433 lines
12 KiB
HTML
433 lines
12 KiB
HTML
|
|
<html>
|
|
<head>
|
|
<title>Pattern</title>
|
|
</head>
|
|
<body data-prismjs-copy-timeout="1500">
|
|
<h2>What is it</h2>
|
|
<p>Sticky notes provide a way to attach information. They are a DS2 core pattern for making notes. </p>
|
|
<h2>When to use it</h2>
|
|
<p>Use a sticky when you want to make a note without adding it to the content. </p>
|
|
<h2>How to use it</h2>
|
|
<p>Uses absolute positioning.
|
|
<sticky-note class="blue" float="right">This <strong>is</strong> a sample sticky. You can drag it out of the way if you need to see the content under it.</sticky-note>
|
|
</p>
|
|
<p>If you wish to create a custom element, that extends another HTML element, the native element has to be extended in customElements.define(). Custom elements that inherit native elements are also known as "type extension custom elements". </p>
|
|
<sticky-note>You will notice when you hover over the sticky, it shows a dot in the colour of the sticky in the position where the sticky note refers to (assuming you haven't dragged it and scrolled that location off the screen).</sticky-note>
|
|
<tabset id="sticky-note">
|
|
<pre class="language-html" tab="html">
|
|
<sticky-note class="blue" float="right">This <strong>is</strong> a sample sticky. You can drag it out of the way if you need to see the content under it.</sticky-note></pre>
|
|
<pre class="language-pug" tab="pug">sticky-note(float="right").blue This <strong>is</strong> a sample sticky.
|
|
| You can drag it out of the way if you need to see the content under it.</pre>
|
|
<pre class="language-css" data-tab="css"> @import url("https://fonts.googleapis.com/css2?family=Kalam:wght@300;400;700&display=swap");
|
|
sticky-note-wrapper {
|
|
-webkit-box-sizing: border-box;
|
|
box-sizing: border-box;
|
|
border: 1px solid transparent;
|
|
border-radius: 50%;
|
|
display: inline-block;
|
|
height: 0.5rem;
|
|
position: relative;
|
|
width: 0.5rem;
|
|
}
|
|
sticky-note-wrapper * {
|
|
-webkit-box-sizing: border-box;
|
|
box-sizing: border-box;
|
|
}
|
|
sticky-note-wrapper sticky-note {
|
|
cursor: -webkit-grab;
|
|
cursor: grab;
|
|
display: -ms-grid;
|
|
display: grid;
|
|
float: left;
|
|
font-size: 1rem;
|
|
height: 13rem;
|
|
left: 0;
|
|
place-items: stretch;
|
|
position: absolute;
|
|
top: 0;
|
|
width: 13rem;
|
|
z-index: 100;
|
|
}
|
|
sticky-note-wrapper sticky-note:active {
|
|
cursor: -webkit-grabbing;
|
|
cursor: grabbing;
|
|
-webkit-user-select: none;
|
|
-moz-user-select: none;
|
|
-ms-user-select: none;
|
|
user-select: none;
|
|
}
|
|
sticky-note-wrapper sticky-note.right {
|
|
float: right;
|
|
}
|
|
sticky-note-wrapper sticky-note > div {
|
|
-ms-grid-row: 1;
|
|
grid-row: 1;
|
|
-ms-grid-column: 1;
|
|
grid-column: 1;
|
|
}
|
|
sticky-note-wrapper sticky-note > div:nth-child(1) {
|
|
background-color: rgba(0, 0, 0, 0.25);
|
|
-webkit-box-shadow: -2px 2px 15px 0 rgba(0, 0, 0, 0.5);
|
|
box-shadow: -2px 2px 15px 0 rgba(0, 0, 0, 0.5);
|
|
display: -ms-grid;
|
|
display: grid;
|
|
margin: 2rem 1rem 0.25rem 0.36rem;
|
|
width: calc(100% - 1rem);
|
|
}
|
|
sticky-note-wrapper sticky-note > div:nth-child(2) {
|
|
clip-path: url(#stickyClip);
|
|
display: -ms-grid;
|
|
display: grid;
|
|
font-family: "Kalam", cursive;
|
|
font-style: 1rem;
|
|
font-weight: 300;
|
|
line-height: 1.25rem;
|
|
padding: 1rem;
|
|
place-items: center;
|
|
text-align: center;
|
|
}
|
|
sticky-note-wrapper sticky-note > div:nth-child(2) > * {
|
|
font-family: "Kalam", cursive !important;
|
|
font-style: normal !important;
|
|
font-weight: 300 !important;
|
|
}
|
|
@media screen and (max-width: 1024px) {
|
|
sticky-note-wrapper sticky-note > div:nth-child(2) {
|
|
max-width: 13rem;
|
|
min-width: 13rem;
|
|
width: 1rem;
|
|
}
|
|
}
|
|
@media screen and (max-width: 768px) {
|
|
sticky-note-wrapper sticky-note > div:nth-child(2) {
|
|
font-size: 1.75rem;
|
|
max-width: 21rem;
|
|
min-height: 21rem;
|
|
}
|
|
}
|
|
@media screen and (max-width: 640px) {
|
|
sticky-note-wrapper sticky-note > div:nth-child(2) {
|
|
font-size: 2.5rem;
|
|
max-width: 26rem;
|
|
min-height: 26rem;
|
|
}
|
|
}
|
|
sticky-note-wrapper sticky-note > div:nth-child(2) {
|
|
background: -webkit-gradient(linear, left top, left bottom, from(#ffffdd), color-stop(2%, #ffffd3), color-stop(12%, #ffffd3), color-stop(75%, #ffffc9), to(#ffffba));
|
|
background: linear-gradient(180deg, #ffffdd 0%, #ffffd3 2%, #ffffd3 12%, #ffffc9 75%, #ffffba 100%);
|
|
}
|
|
sticky-note-wrapper sticky-note.blue > div:nth-child(2) {
|
|
background: -webkit-gradient(linear, left top, left bottom, from(#9dddec), color-stop(2%, #94daea), color-stop(12%, #94daea), color-stop(75%, #8bd7e8), to(#7fd3e6));
|
|
background: linear-gradient(180deg, #9dddec 0%, #94daea 2%, #94daea 12%, #8bd7e8 75%, #7fd3e6 100%);
|
|
}
|
|
sticky-note-wrapper sticky-note.pink > div:nth-child(2) {
|
|
background: -webkit-gradient(linear, left top, left bottom, from(#fa7c93), color-stop(2%, #fa728b), color-stop(12%, #fa728b), color-stop(75%, #fa6883), to(#f95977));
|
|
background: linear-gradient(180deg, #fa7c93 0%, #fa728b 2%, #fa728b 12%, #fa6883 75%, #f95977 100%);
|
|
}
|
|
sticky-note-wrapper sticky-note.green > div:nth-child(2) {
|
|
background: -webkit-gradient(linear, left top, left bottom, from(#c5fcc9), color-stop(2%, #bbfbc0), color-stop(12%, #bbfbc0), color-stop(75%, #b1fab7), to(#a3faaa));
|
|
background: linear-gradient(180deg, #c5fcc9 0%, #bbfbc0 2%, #bbfbc0 12%, #b1fab7 75%, #a3faaa 100%);
|
|
}
|
|
sticky-note-wrapper:has(sticky-note:hover) {
|
|
background-color: #ffffd3;
|
|
border: 1px solid black;
|
|
}
|
|
sticky-note-wrapper:has(sticky-note.yellow:hover) {
|
|
background-color: #ffffd3;
|
|
}
|
|
sticky-note-wrapper:has(sticky-note.blue:hover) {
|
|
background-color: #94daea;
|
|
}
|
|
sticky-note-wrapper:has(sticky-note.pink:hover) {
|
|
background-color: #fa728b;
|
|
}
|
|
sticky-note-wrapper:has(sticky-note.green:hover) {
|
|
background-color: #bbfbc0;
|
|
}</pre>
|
|
<pre class="language-css" data-tab="scss"> //- DS2 core (c) 2024 Alexander McIlwraith
|
|
//- Licensed under CC BY-SA 4.0
|
|
|
|
@use 'sass:color';
|
|
@use "sass:map";
|
|
@use 'sass:list';
|
|
@import url('https://fonts.googleapis.com/css2?family=Kalam:wght@300;400;700&display=swap');
|
|
|
|
$sticky-colors: (
|
|
"yellow": #ffffd3,
|
|
"blue": #94daea,
|
|
"pink": #fa728b,
|
|
"green": #bbfbc0 ) !default;
|
|
|
|
@mixin sticky-note {
|
|
sticky-note-wrapper {
|
|
box-sizing: border-box;
|
|
|
|
border: 1px solid transparent;
|
|
border-radius: 50%;
|
|
display: inline-block;
|
|
height: .5rem;
|
|
position: relative;
|
|
width: .5rem;
|
|
& * {
|
|
box-sizing: border-box;
|
|
}
|
|
sticky-note {
|
|
cursor: grab;
|
|
display: grid;
|
|
float: left;
|
|
font-size: 1rem;
|
|
height: 13rem;
|
|
left: 0;
|
|
place-items: stretch;
|
|
position: absolute;
|
|
top: 0;
|
|
width: 13rem;
|
|
z-index: 100;
|
|
|
|
&:active {
|
|
cursor: grabbing;
|
|
user-select: none;
|
|
}
|
|
|
|
|
|
|
|
@media screen and (max-width: 1024px) {
|
|
//width: 15rem;
|
|
// background-color: green;
|
|
}
|
|
@media screen and (max-width: 768px) {
|
|
//width: 20rem;
|
|
// background-color: blue;
|
|
}
|
|
@media screen and (max-width: 640px) {
|
|
//width: 30rem;
|
|
// background-color: yellow;
|
|
}
|
|
&.right {
|
|
float: right;
|
|
}
|
|
@content;
|
|
|
|
> div {
|
|
grid-row: 1;
|
|
grid-column: 1;
|
|
&:nth-child(1) {
|
|
background-color: rgba(0, 0, 0, 0.25);
|
|
box-shadow: -2px 2px 15px 0 rgba(0, 0, 0, 0.5);
|
|
display: grid;
|
|
margin: 2rem 1rem .25rem .36rem;
|
|
width: calc(100% - 1rem);
|
|
|
|
}
|
|
&:nth-child(2) {
|
|
clip-path: url(#stickyClip);
|
|
display: grid;
|
|
font-family: "Kalam", cursive;
|
|
font-style: 1rem;
|
|
font-weight: 300;
|
|
line-height: 1.25rem;
|
|
padding: 1rem;
|
|
place-items: center;
|
|
text-align: center;
|
|
// not adding this because maybe we want the sticky note text to be able to be selected and copied.
|
|
// user-select: none;
|
|
> * {
|
|
font-family: "Kalam", cursive !important;
|
|
font-style: normal !important;
|
|
font-weight: 300 !important;
|
|
}
|
|
|
|
|
|
@media screen and (max-width: 1024px) {
|
|
max-width: 13rem;
|
|
min-width: 13rem;
|
|
width: 1rem;
|
|
}
|
|
@media screen and (max-width: 768px) {
|
|
font-size: 1.75rem;
|
|
max-width: 21rem;
|
|
min-height: 21rem;
|
|
}
|
|
@media screen and (max-width: 640px) {
|
|
font-size: 2.5rem;
|
|
max-width: 26rem;
|
|
min-height: 26rem;
|
|
}
|
|
}
|
|
}
|
|
|
|
// default colour
|
|
> div:nth-child(2) {
|
|
$sticky-color: nth(map.values($sticky-colors), 1);
|
|
background: linear-gradient(
|
|
180deg,
|
|
lighten($sticky-color, 2%) 0%,
|
|
$sticky-color 2%,
|
|
$sticky-color 12%,
|
|
darken($sticky-color, 2%) 75%,
|
|
darken($sticky-color, 5%) 100%
|
|
);
|
|
}
|
|
// all class colors
|
|
@each $class, $sticky-color in $sticky-colors {
|
|
&.#{$class} > div:nth-child(2) {
|
|
@if $class != nth(map.keys($sticky-colors), 1) {
|
|
background: linear-gradient(
|
|
180deg,
|
|
lighten($sticky-color, 2%) 0%,
|
|
$sticky-color 2%,
|
|
$sticky-color 12%,
|
|
darken($sticky-color, 2%) 75%,
|
|
darken($sticky-color, 5%) 100%
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
&:has(sticky-note:hover) {
|
|
background-color: #{nth(map.values($sticky-colors), 1)};
|
|
border: 1px solid black;
|
|
|
|
}
|
|
@each $class, $sticky-color in $sticky-colors {
|
|
&:has(sticky-note.#{$class}:hover) {
|
|
background-color: $sticky-color;
|
|
}
|
|
}
|
|
}
|
|
}</pre>
|
|
<pre class="language-js" data-tab="js"> //- DS2 core (c) 2024 Alexander McIlwraith
|
|
//- Licensed under CC BY-SA 4.0
|
|
|
|
const font = {
|
|
size: 0
|
|
}
|
|
|
|
const pos = {
|
|
x: 0,
|
|
y: 0
|
|
}
|
|
|
|
function drag(sticky) {
|
|
sticky.onmousedown = event => {
|
|
// get the position within the sticky
|
|
pos.x = (event.clientX - sticky.offsetLeft);
|
|
pos.y = (event.clientY - sticky.offsetTop);
|
|
|
|
// on mouse move, move the sticky to the position, minus the offset, of the mouse
|
|
document.onmousemove = documentEvent => {
|
|
sticky.style.top = (documentEvent.clientY - pos.y) + 'px';
|
|
sticky.style.left = (documentEvent.clientX - pos.x) + 'px';
|
|
sticky.setAttribute("moved", "true");
|
|
}
|
|
// when done, remove mouse move and mouse up handlers.
|
|
document.onmouseup = () => {
|
|
document.onmouseup = null;
|
|
document.onmousemove = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
function waitForElement(selector) {
|
|
return new Promise(resolve => {
|
|
if (document.querySelector(selector)) {
|
|
return resolve(document.querySelector(selector));
|
|
}
|
|
|
|
const observer = new MutationObserver(mutations => {
|
|
if (document.querySelector(selector)) {
|
|
observer.disconnect();
|
|
resolve(document.querySelector(selector));
|
|
}
|
|
});
|
|
|
|
// If you get "parameter 1 is not of type 'Node'" error, see https://stackoverflow.com/a/77855838/492336
|
|
observer.observe(document.body, {
|
|
childList: true,
|
|
subtree: true
|
|
});
|
|
});
|
|
}
|
|
|
|
const checkBottom = (attach) => {
|
|
// check if top or bottom
|
|
if (attach.y < 0) {
|
|
attach.ys = "bottom";
|
|
attach.y = attach.y * -1;
|
|
}
|
|
}
|
|
|
|
const setStickyPostions = (s, attach) => {
|
|
// set
|
|
s.style[attach.ys] = `${attach.y}px`;
|
|
s.style[attach.xs] = `${attach.x}px`;
|
|
s.style.display = "grid";
|
|
}
|
|
|
|
const css = (el, attr) => {
|
|
let st = ""
|
|
Object.entries(attr).forEach(val => {
|
|
const [key, value] = val;
|
|
st += `${key}: ${value}; `;
|
|
})
|
|
el.setAttribute("style",st.trim());
|
|
}
|
|
|
|
|
|
const calculateStickyPosition = (s) => {
|
|
let float = s.getAttribute("float");
|
|
let p = s.parentNode.getBoundingClientRect()
|
|
switch (float) {
|
|
case "left":
|
|
css(s, {left: (p.left * -1) + "px"})
|
|
break;
|
|
case "right":
|
|
css(s, { left: (Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0) - p.left - s.offsetWidth - (font.size * 2)) + "px" });
|
|
break;
|
|
}
|
|
|
|
let offset = s.getAttribute("offset");
|
|
if (offset !== null) {
|
|
offset = offset.trim().split(" ");
|
|
css(s, {top: offset[0], left: offset[1] })
|
|
}
|
|
}
|
|
|
|
export function init(p = document){
|
|
font.size = parseFloat(getComputedStyle(document.documentElement).fontSize.replace("px",""));
|
|
|
|
p.querySelectorAll("sticky-note").forEach((s) => {
|
|
if (s.querySelectorAll("svg").length == 0) {
|
|
|
|
let wrapper = document.createElement("sticky-note-wrapper");
|
|
|
|
s.parentNode.insertBefore(wrapper, s);
|
|
wrapper.appendChild(s);
|
|
|
|
s.setAttribute("content", s.innerHTML.replace(/"/g, "\""));
|
|
s.innerHTML = `<div><svg width='0' height='0'><defs><clipPath id='stickyClip' clipPathUnits='objectBoundingBox'><path d='M 0 0 Q 0 0.69, 0.03 0.96 0.03 0.96, 1 0.96 Q 0.96 0.69, 0.96 0 0.96 0, 0 0' stroke-linejoin='round' stroke-linecap='square' /></clipPath></defs></svg></div><div><div>${s.innerHTML}</div></div>`;
|
|
}
|
|
calculateStickyPosition(s);
|
|
drag(s);
|
|
s.ondblclick = (e) => {
|
|
if (e.ctrlKey) {
|
|
calculateStickyPosition(s);
|
|
} else {
|
|
// add one click select
|
|
}
|
|
}
|
|
})
|
|
|
|
|
|
window.onresize = () => {
|
|
font.size = parseFloat(getComputedStyle(document.documentElement).fontSize.replace("px",""));
|
|
let stickies = p.querySelectorAll("sticky-note");
|
|
stickies.forEach((s) => {
|
|
calculateStickyPosition(s);
|
|
});
|
|
}
|
|
}</pre>
|
|
</tabset>
|
|
</body>
|
|
</html> |