<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] 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>