cladux-html 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cl.css +2243 -2
- package/cl.js +866 -2
- package/cl.min.css +2 -0
- package/cl.min.js +2 -0
- package/package.json +6 -2
package/cl.js
CHANGED
|
@@ -1,2 +1,866 @@
|
|
|
1
|
-
/*! cladux-html v0.
|
|
2
|
-
"use strict";(()=>{function T(e){let t=document.getElementById(e);t instanceof HTMLDialogElement&&!t.open&&t.showModal()}function O(e,t){let o=t.closest("[data-dialog-open]");if(o){T(o.dataset.dialogOpen);return}let r=t.closest("[data-dialog-close]");if(r){r.closest("dialog")?.close();return}if(t instanceof HTMLDialogElement&&t.matches(".cl-dialog")){let i=t.getBoundingClientRect();e.clientX>=i.left&&e.clientX<=i.right&&e.clientY>=i.top&&e.clientY<=i.bottom||t.close()}}var P={top:"bottom",bottom:"top",left:"right",right:"left"};function C(e,t,o={}){let{align:r="center",offset:i=4,padding:s=8}=o,n=o.side??"bottom",a=t.getBoundingClientRect();e.style.setProperty("--cl-anchor-width",`${a.width}px`);let l={width:e.offsetWidth,height:e.offsetHeight},g=document.documentElement.clientWidth,c=document.documentElement.clientHeight,u={top:a.top-i-s,bottom:c-a.bottom-i-s,left:a.left-i-s,right:g-a.right-i-s},h=n==="top"||n==="bottom"?l.height:l.width;u[n]<h&&u[P[n]]>u[n]&&(n=P[n]);let m,f;n==="top"||n==="bottom"?(m=n==="bottom"?a.bottom+i:a.top-i-l.height,f=r==="start"?a.left:r==="end"?a.right-l.width:a.left+(a.width-l.width)/2,f=Math.min(Math.max(f,s),g-l.width-s),e.style.setProperty("--cl-available-height",`${u[n]}px`)):(f=n==="right"?a.right+i:a.left-i-l.width,m=r==="start"?a.top:r==="end"?a.bottom-l.height:a.top+(a.height-l.height)/2,m=Math.min(Math.max(m,s),c-l.height-s)),e.style.top=`${m}px`,e.style.left=`${f}px`,e.dataset.placed=n;let J=n==="left"?"right":n==="right"?"left":`${a.left+a.width/2-f}px`,Q=n==="top"?"bottom":n==="bottom"?"top":`${a.top+a.height/2-m}px`;e.style.transformOrigin=`${J} ${Q}`}var L=".cl-popover, .cl-menu",D=10,E='.cl-menu-item:not(:disabled):not([aria-disabled="true"])';function x(e){return e.dataset.anchor&&document.getElementById(e.dataset.anchor)||document.querySelector(`[popovertarget="${CSS.escape(e.id)}"]`)}function M(e,t,o,r){C(e,t,{side:e.dataset.side||o,align:e.dataset.align||(e.matches(".cl-menu")?"start":"center"),offset:r})}var v=!1;function I(e){let t=e.target;!(t instanceof HTMLElement)||!t.matches(L)||e.newState==="open"&&(t.style.visibility="hidden",t.dataset.placed=t.dataset.side||"bottom")}function N(e){let t=e.target;if(!(t instanceof HTMLElement)||!t.matches(L))return;let o=e.newState==="open",r=x(t);if(r?.setAttribute("aria-expanded",String(o)),!!o){if(r&&M(t,r,"bottom",4),t.style.visibility="",t.matches(".cl-menu")){t.hasAttribute("tabindex")||(t.tabIndex=-1);let i=t.querySelector(E);(v&&i?i:t).focus({preventScroll:!0})}v=!1}}function w(){for(let e of document.querySelectorAll(`${L}, .cl-tooltip`)){if(!e.matches(":popover-open"))continue;let t=e.matches(".cl-tooltip")?document.querySelector(`[aria-describedby~="${CSS.escape(e.id)}"]`):x(e);t&&M(e,t,e.matches(".cl-tooltip")?"top":"bottom",e.matches(".cl-tooltip")?D:4)}}function $(e,t){if(t.matches("[popovertarget]")&&["Enter"," ","ArrowDown"].includes(e.key)){v=!0,e.key==="ArrowDown"&&(e.preventDefault(),t.click());return}let o=t.closest(".cl-menu");if(!o)return;let r=[...o.querySelectorAll(E)],i=r.indexOf(document.activeElement),s=n=>{e.preventDefault(),r[(n+r.length)%r.length]?.focus()};switch(e.key){case"ArrowDown":return s(i+1);case"ArrowUp":return s(i<0?-1:i-1);case"Home":return s(0);case"End":return s(-1);case"Tab":o.hidePopover();return;default:if(e.key.length===1&&/\S/.test(e.key)){let n=e.key.toLowerCase();[...r.slice(i+1),...r.slice(0,i+1)].find(l=>l.textContent?.trim().toLowerCase().startsWith(n))?.focus()}}}function q(e){let t=e.closest(E);t&&t.closest(".cl-menu")&&document.activeElement!==t&&t.focus({preventScroll:!0})}function F(e){let t=e.target;t instanceof HTMLElement&&t.matches(".cl-menu")&&t.contains(document.activeElement)&&t.focus({preventScroll:!0})}function B(e){let t=e.closest(E),o=t?.closest(".cl-menu");if(!t||!o)return;let r=t.getAttribute("role");if(r==="menuitemcheckbox")t.setAttribute("aria-checked",String(t.getAttribute("aria-checked")!=="true"));else if(r==="menuitemradio"){let i=t.closest('[role="group"]')??o;for(let s of i.querySelectorAll('[role="menuitemradio"]'))s.setAttribute("aria-checked",String(s===t))}t.hasAttribute("data-keep-open")||(o.hidePopover(),x(o)?.focus())}function K(e){let t=e.closest("[aria-describedby]");if(!t)return null;for(let o of t.getAttribute("aria-describedby").split(/\s+/)){let r=document.getElementById(o);if(r?.matches(".cl-tooltip"))return{owner:t,tip:r}}return null}function Z(e,t){t.matches(":popover-open")||(t.dataset.placed=t.dataset.side||"top",t.showPopover()),M(t,e,"top",D)}function R(e){e.matches(":popover-open")&&e.hidePopover()}function A(e,t){if(e instanceof PointerEvent&&e.pointerType==="touch"||e instanceof FocusEvent&&!t.matches(":focus-visible"))return;let o=K(t);o&&Z(o.owner,o.tip)}function H(e,t){let o=K(t);if(!o)return;let r=e.relatedTarget;r&&o.owner.contains(r)||R(o.tip)}function W(){for(let e of document.querySelectorAll(".cl-tooltip:popover-open"))R(e)}function z(e){let t=e.closest('[role="tablist"]');if(!(!t||e.matches(":disabled")))for(let o of t.querySelectorAll('[role="tab"]')){let r=o===e;o.setAttribute("aria-selected",String(r)),o.tabIndex=r?0:-1;let i=document.getElementById(o.getAttribute("aria-controls")??"");i&&(i.hidden=!r)}}function X(e){let t=e.closest('.cl-tabs [role="tab"]');t&&z(t)}function Y(e,t){let o=t.closest('.cl-tabs [role="tab"]'),r=o?.closest('[role="tablist"]');if(!o||!r)return;let i=r.getAttribute("aria-orientation")==="vertical",s=[...r.querySelectorAll('[role="tab"]:not(:disabled)')],n=s.indexOf(o),a=i?{ArrowDown:n+1,ArrowUp:n-1,Home:0,End:s.length-1}:{ArrowRight:n+1,ArrowLeft:n-1,Home:0,End:s.length-1};if(!(e.key in a))return;e.preventDefault();let l=s[(a[e.key]+s.length)%s.length];l.focus(),z(l)}function G(e=document){for(let t of e.querySelectorAll('.cl-tabs [role="tab"]'))t.tabIndex=t.getAttribute("aria-selected")==="true"?0:-1}var U={success:'<circle cx="12" cy="12" r="10"/><path d="m16 9-5.5 5.5L8 12"/>',info:'<circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/>',warning:'<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/>',error:'<path d="m15 9-6 6"/><path d="M2.586 16.726A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2h6.624a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586z"/><path d="m9 9 6 6"/>',loading:'<path d="M21 12a9 9 0 1 1-6.219-8.56"/>'},tt=e=>`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${e}</svg>`,et=3,S=14;function ot(){let e=document.querySelector("ol.cl-toaster");return e||(e=document.createElement("ol"),e.className="cl-toaster",e.setAttribute("aria-label","Notifications"),e.setAttribute("aria-live","polite"),e.addEventListener("pointerenter",()=>{e.dataset.expanded="",b(e)}),e.addEventListener("pointerleave",()=>{delete e.dataset.expanded,b(e)}),document.body.append(e)),e}function b(e){let t=[...e.querySelectorAll(".cl-toast:not([data-removed])")].reverse(),o=e.dataset.expanded!==void 0,r=t[0];e.style.setProperty("--front-height",`${r?Number(r.dataset.height):0}px`);let i=0;t.forEach((s,n)=>{s.toggleAttribute("data-front",n===0),s.toggleAttribute("data-hidden",n>=et),s.style.zIndex=String(t.length-n);let a=o?-i:-n*S;s.style.setProperty("--y",`${a}px`),s.style.setProperty("--scale",o?"1":String(1-n*.05)),i+=Number(s.dataset.height)+S}),e.style.setProperty("--expanded-height",`${Math.max(0,i-S)}px`)}function y(e){if(e.dataset.removed!==void 0)return;e.dataset.removed="";let t=e.closest(".cl-toaster");t&&b(t),setTimeout(()=>{e.remove(),t&&!t.querySelector(".cl-toast")&&delete t.dataset.expanded},200)}function d(e,t={}){let{description:o,type:r="default",action:i}=t,s=t.duration??(r==="loading"?1/0:4e3),n=document.createElement("li");if(n.className="cl-toast",n.dataset.type=r,n.setAttribute("role",r==="error"?"alert":"status"),U[r]){let c=document.createElement("div");c.className="cl-toast-icon",c.innerHTML=tt(U[r]),n.append(c)}let a=document.createElement("div");a.className="cl-toast-content";let l=document.createElement("div");if(l.className="cl-toast-title",l.textContent=e,a.append(l),o){let c=document.createElement("div");c.className="cl-toast-description",c.textContent=o,a.append(c)}if(n.append(a),i){let c=document.createElement("button");c.type="button",c.className="cl-toast-action",c.textContent=i.label,c.addEventListener("click",u=>{i.onClick?.(u),y(n)}),n.append(c)}let g=ot();if(n.toggleAttribute("data-front",!0),g.append(n),n.dataset.height=String(n.offsetHeight),b(g),Number.isFinite(s)){let c=s,u=Date.now(),h=setTimeout(()=>y(n),c);n.addEventListener("pointerenter",()=>{clearTimeout(h),c-=Date.now()-u}),n.addEventListener("pointerleave",()=>{u=Date.now(),h=setTimeout(()=>y(n),Math.max(c,1e3))})}return()=>y(n)}d.success=(e,t)=>d(e,{...t,type:"success"});d.info=(e,t)=>d(e,{...t,type:"info"});d.warning=(e,t)=>d(e,{...t,type:"warning"});d.error=(e,t)=>d(e,{...t,type:"error"});function V(e){let t=e.closest("[data-toast]");if(!t)return;let{toast:o,toastDescription:r,toastType:i}=t.dataset;d(o,{description:r,type:i||"default"})}var j=!1,p=e=>e instanceof Element?e:null;function k(){if(j||typeof document>"u")return;j=!0,document.addEventListener("click",t=>{let o=p(t.target);o&&(O(t,o),B(o),X(o),V(o))}),document.addEventListener("keydown",t=>{let o=p(t.target);t.key==="Escape"&&W(),o&&($(t,o),Y(t,o))}),document.addEventListener("beforetoggle",I,!0),document.addEventListener("toggle",N,!0),document.addEventListener("pointerleave",F,!0),document.addEventListener("pointermove",t=>{let o=p(t.target);o&&q(o)}),document.addEventListener("pointerover",t=>{let o=p(t.target);o&&A(t,o)}),document.addEventListener("pointerout",t=>{let o=p(t.target);o&&H(t,o)}),document.addEventListener("focusin",t=>{let o=p(t.target);o&&A(t,o)}),document.addEventListener("focusout",t=>{let o=p(t.target);o&&H(t,o)}),window.addEventListener("scroll",w,{capture:!0,passive:!0}),window.addEventListener("resize",w,{passive:!0});let e=()=>G();document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e()}var _={start:k,toast:d,openDialog:T};window.cl=_;k();})();
|
|
1
|
+
/*! cladux-html v0.2.0 | Apache-2.0 | https://cladux.vercel.app/docs/html */
|
|
2
|
+
"use strict";
|
|
3
|
+
(() => {
|
|
4
|
+
var closing = new WeakMap();
|
|
5
|
+
var reducedMotion = () => matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
6
|
+
function close(item) {
|
|
7
|
+
const content = item.querySelector(":scope > .cl-accordion-content");
|
|
8
|
+
if (!content || reducedMotion()) {
|
|
9
|
+
item.open = false;
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const name = item.getAttribute("name");
|
|
13
|
+
item.removeAttribute("name");
|
|
14
|
+
item.dataset.closing = "";
|
|
15
|
+
const animation = content.animate([{ height: `${content.offsetHeight}px` }, { height: "0px" }], {
|
|
16
|
+
duration: 200,
|
|
17
|
+
easing: "ease-out",
|
|
18
|
+
fill: "forwards"
|
|
19
|
+
});
|
|
20
|
+
closing.set(item, { animation, name });
|
|
21
|
+
animation.onfinish = () => {
|
|
22
|
+
item.open = false;
|
|
23
|
+
animation.cancel();
|
|
24
|
+
finish(item);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function finish(item) {
|
|
28
|
+
const state = closing.get(item);
|
|
29
|
+
if (!state) return;
|
|
30
|
+
closing.delete(item);
|
|
31
|
+
if (state.name !== null) item.setAttribute("name", state.name);
|
|
32
|
+
requestAnimationFrame(() => delete item.dataset.closing);
|
|
33
|
+
}
|
|
34
|
+
function onAccordionClick(event, target) {
|
|
35
|
+
const trigger = target.closest(".cl-accordion-trigger");
|
|
36
|
+
const item = trigger?.parentElement;
|
|
37
|
+
if (!(item instanceof HTMLDetailsElement) || !item.matches(".cl-accordion-item")) return;
|
|
38
|
+
if (trigger !== item.querySelector(":scope > summary")) return;
|
|
39
|
+
const state = closing.get(item);
|
|
40
|
+
if (state) {
|
|
41
|
+
event.preventDefault();
|
|
42
|
+
state.animation.cancel();
|
|
43
|
+
finish(item);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (item.open) {
|
|
47
|
+
event.preventDefault();
|
|
48
|
+
close(item);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const name = item.getAttribute("name");
|
|
52
|
+
if (!name) return;
|
|
53
|
+
for (const other of document.querySelectorAll(
|
|
54
|
+
"details.cl-accordion-item[open]"
|
|
55
|
+
)) {
|
|
56
|
+
if (other !== item && other.getAttribute("name") === name) close(other);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function partsOf(el) {
|
|
60
|
+
const root = el?.closest(".cl-carousel");
|
|
61
|
+
const content = root?.querySelector(".cl-carousel-content");
|
|
62
|
+
if (!root || !content) return null;
|
|
63
|
+
const items2 = [...content.querySelectorAll(":scope > .cl-carousel-item")];
|
|
64
|
+
return { root, content, items: items2, vertical: root.dataset.orientation === "vertical" };
|
|
65
|
+
}
|
|
66
|
+
var reducedMotion2 = () => matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
67
|
+
var sign = ({ content, vertical }) => !vertical && getComputedStyle(content).direction === "rtl" ? -1 : 1;
|
|
68
|
+
var position = (parts) => Math.abs(parts.vertical ? parts.content.scrollTop : parts.content.scrollLeft);
|
|
69
|
+
var end = ({ content, vertical }) => vertical ? content.scrollHeight - content.clientHeight : content.scrollWidth - content.clientWidth;
|
|
70
|
+
function step({ content, items: items2, vertical }) {
|
|
71
|
+
const first = items2[0];
|
|
72
|
+
if (!first) return 0;
|
|
73
|
+
const style = getComputedStyle(content);
|
|
74
|
+
const gap = parseFloat(vertical ? style.rowGap : style.columnGap) || 0;
|
|
75
|
+
return (vertical ? first.offsetHeight : first.offsetWidth) + gap;
|
|
76
|
+
}
|
|
77
|
+
function scrollToOffset(parts, offset) {
|
|
78
|
+
const to = Math.min(Math.max(offset, 0), end(parts));
|
|
79
|
+
parts.content.scrollTo({
|
|
80
|
+
[parts.vertical ? "top" : "left"]: to * sign(parts),
|
|
81
|
+
behavior: reducedMotion2() ? "instant" : "smooth"
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
var targets = new WeakMap();
|
|
85
|
+
function go(parts, by) {
|
|
86
|
+
const size = step(parts);
|
|
87
|
+
if (!size) return;
|
|
88
|
+
const last = Math.round(end(parts) / size);
|
|
89
|
+
const from = targets.get(parts.content)?.index ?? Math.round(position(parts) / size);
|
|
90
|
+
const index = Math.min(Math.max(from + by, 0), last);
|
|
91
|
+
clearTimeout(targets.get(parts.content)?.timer);
|
|
92
|
+
const timer = window.setTimeout(() => targets.delete(parts.content), 700);
|
|
93
|
+
targets.set(parts.content, { index, timer });
|
|
94
|
+
scrollToOffset(parts, index * size);
|
|
95
|
+
}
|
|
96
|
+
function update(parts) {
|
|
97
|
+
const at = position(parts);
|
|
98
|
+
const previous = parts.root.querySelector(".cl-carousel-previous");
|
|
99
|
+
const next = parts.root.querySelector(".cl-carousel-next");
|
|
100
|
+
if (previous) previous.disabled = at <= 1;
|
|
101
|
+
if (next) next.disabled = at >= end(parts) - 1;
|
|
102
|
+
}
|
|
103
|
+
function setupCarousels(root = document) {
|
|
104
|
+
const roots = [...root.querySelectorAll(".cl-carousel")];
|
|
105
|
+
if (root instanceof HTMLElement && root.matches(".cl-carousel")) roots.push(root);
|
|
106
|
+
for (const el of roots) {
|
|
107
|
+
const parts = partsOf(el);
|
|
108
|
+
if (!parts) continue;
|
|
109
|
+
if (!parts.content.hasAttribute("tabindex")) parts.content.tabIndex = 0;
|
|
110
|
+
parts.items.forEach((item, i) => {
|
|
111
|
+
item.setAttribute("role", "group");
|
|
112
|
+
item.setAttribute("aria-roledescription", "slide");
|
|
113
|
+
if (!item.hasAttribute("aria-label")) {
|
|
114
|
+
item.setAttribute("aria-label", `${i + 1} of ${parts.items.length}`);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
update(parts);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function onCarouselClick(target) {
|
|
121
|
+
const button = target.closest(".cl-carousel-previous, .cl-carousel-next");
|
|
122
|
+
const parts = partsOf(button);
|
|
123
|
+
if (button && parts) go(parts, button.matches(".cl-carousel-next") ? 1 : -1);
|
|
124
|
+
}
|
|
125
|
+
function onCarouselKeyDown(event, target) {
|
|
126
|
+
if (target.closest("input, textarea, select, [contenteditable]")) return;
|
|
127
|
+
const parts = partsOf(target);
|
|
128
|
+
if (!parts || !["ArrowLeft", "ArrowRight"].includes(event.key)) return;
|
|
129
|
+
const rtl = getComputedStyle(parts.root).direction === "rtl";
|
|
130
|
+
const forward = event.key === "ArrowRight" !== rtl;
|
|
131
|
+
event.preventDefault();
|
|
132
|
+
go(parts, forward ? 1 : -1);
|
|
133
|
+
}
|
|
134
|
+
var pending = new WeakSet();
|
|
135
|
+
function onCarouselScroll(event) {
|
|
136
|
+
const content = event.target;
|
|
137
|
+
if (!(content instanceof HTMLElement) || !content.matches(".cl-carousel-content")) return;
|
|
138
|
+
if (pending.has(content)) return;
|
|
139
|
+
pending.add(content);
|
|
140
|
+
requestAnimationFrame(() => {
|
|
141
|
+
pending.delete(content);
|
|
142
|
+
const parts = partsOf(content);
|
|
143
|
+
if (parts) update(parts);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
function onCarouselPointerDown(event, target) {
|
|
147
|
+
if (event.pointerType !== "mouse" || event.button !== 0) return;
|
|
148
|
+
const content = target.closest(".cl-carousel-content");
|
|
149
|
+
const parts = partsOf(content);
|
|
150
|
+
if (!content || !parts || target.closest("a, button, input, textarea, select, label")) return;
|
|
151
|
+
const axis = parts.vertical ? "scrollTop" : "scrollLeft";
|
|
152
|
+
const start2 = content[axis];
|
|
153
|
+
const from = parts.vertical ? event.clientY : event.clientX;
|
|
154
|
+
const startIndex = Math.round(position(parts) / (step(parts) || 1));
|
|
155
|
+
let moved = 0;
|
|
156
|
+
event.preventDefault();
|
|
157
|
+
const stop = (e) => e.stopPropagation();
|
|
158
|
+
const move = (e) => {
|
|
159
|
+
moved = (parts.vertical ? e.clientY : e.clientX) - from;
|
|
160
|
+
if (Math.abs(moved) > 3) content.dataset.dragging = "";
|
|
161
|
+
content[axis] = start2 - moved;
|
|
162
|
+
};
|
|
163
|
+
const up = () => {
|
|
164
|
+
document.removeEventListener("pointermove", move);
|
|
165
|
+
document.removeEventListener("pointerup", up);
|
|
166
|
+
document.removeEventListener("pointercancel", up);
|
|
167
|
+
if (!content.hasAttribute("data-dragging")) return;
|
|
168
|
+
document.addEventListener("click", stop, { capture: true, once: true });
|
|
169
|
+
setTimeout(() => document.removeEventListener("click", stop, true));
|
|
170
|
+
const size = step(parts);
|
|
171
|
+
const pulled = -moved * sign(parts);
|
|
172
|
+
const by = Math.abs(pulled) > size / 5 ? Math.sign(pulled) : 0;
|
|
173
|
+
scrollToOffset(parts, (startIndex + by) * size);
|
|
174
|
+
const done = () => delete content.dataset.dragging;
|
|
175
|
+
content.addEventListener("scrollend", done, { once: true });
|
|
176
|
+
setTimeout(done, 600);
|
|
177
|
+
};
|
|
178
|
+
document.addEventListener("pointermove", move);
|
|
179
|
+
document.addEventListener("pointerup", up);
|
|
180
|
+
document.addEventListener("pointercancel", up);
|
|
181
|
+
}
|
|
182
|
+
var OPPOSITE = { top: "bottom", bottom: "top", left: "right", right: "left" };
|
|
183
|
+
function place(floating, anchor, options = {}) {
|
|
184
|
+
const { align = "center", offset = 4, padding = 8 } = options;
|
|
185
|
+
let side = options.side ?? "bottom";
|
|
186
|
+
const a = anchor.getBoundingClientRect();
|
|
187
|
+
floating.style.setProperty("--cl-anchor-width", `${a.width}px`);
|
|
188
|
+
const f = { width: floating.offsetWidth, height: floating.offsetHeight };
|
|
189
|
+
const vw = document.documentElement.clientWidth;
|
|
190
|
+
const vh = document.documentElement.clientHeight;
|
|
191
|
+
const space = {
|
|
192
|
+
top: a.top - offset - padding,
|
|
193
|
+
bottom: vh - a.bottom - offset - padding,
|
|
194
|
+
left: a.left - offset - padding,
|
|
195
|
+
right: vw - a.right - offset - padding
|
|
196
|
+
};
|
|
197
|
+
const needed = side === "top" || side === "bottom" ? f.height : f.width;
|
|
198
|
+
if (space[side] < needed && space[OPPOSITE[side]] > space[side]) side = OPPOSITE[side];
|
|
199
|
+
let top;
|
|
200
|
+
let left;
|
|
201
|
+
if (side === "top" || side === "bottom") {
|
|
202
|
+
top = side === "bottom" ? a.bottom + offset : a.top - offset - f.height;
|
|
203
|
+
left = align === "start" ? a.left : align === "end" ? a.right - f.width : a.left + (a.width - f.width) / 2;
|
|
204
|
+
left = Math.min(Math.max(left, padding), vw - f.width - padding);
|
|
205
|
+
floating.style.setProperty("--cl-available-height", `${space[side]}px`);
|
|
206
|
+
} else {
|
|
207
|
+
left = side === "right" ? a.right + offset : a.left - offset - f.width;
|
|
208
|
+
top = align === "start" ? a.top : align === "end" ? a.bottom - f.height : a.top + (a.height - f.height) / 2;
|
|
209
|
+
top = Math.min(Math.max(top, padding), vh - f.height - padding);
|
|
210
|
+
}
|
|
211
|
+
floating.style.top = `${top}px`;
|
|
212
|
+
floating.style.left = `${left}px`;
|
|
213
|
+
floating.dataset.placed = side;
|
|
214
|
+
const originX = side === "left" ? "right" : side === "right" ? "left" : `${a.left + a.width / 2 - left}px`;
|
|
215
|
+
const originY = side === "top" ? "bottom" : side === "bottom" ? "top" : `${a.top + a.height / 2 - top}px`;
|
|
216
|
+
floating.style.transformOrigin = `${originX} ${originY}`;
|
|
217
|
+
}
|
|
218
|
+
var ITEM = '.cl-combobox-item:not([aria-disabled="true"])';
|
|
219
|
+
var ids = 0;
|
|
220
|
+
var idFor = (el) => el.id || (el.id = `cl-combobox-${++ids}`);
|
|
221
|
+
function partsOf2(el) {
|
|
222
|
+
const root = el?.closest(".cl-combobox");
|
|
223
|
+
const input = root?.querySelector(".cl-combobox-input");
|
|
224
|
+
const content = root?.querySelector(".cl-combobox-content");
|
|
225
|
+
const list = content?.querySelector(".cl-combobox-list");
|
|
226
|
+
return root && input && content && list ? { root, input, content, list } : null;
|
|
227
|
+
}
|
|
228
|
+
var isOpen = ({ content }) => content.matches(":popover-open");
|
|
229
|
+
var items = ({ list }) => [...list.querySelectorAll(".cl-combobox-item")];
|
|
230
|
+
var visible = (parts) => items(parts).filter((item) => !item.hidden && item.matches(ITEM));
|
|
231
|
+
var label = (item) => item.textContent?.trim() ?? "";
|
|
232
|
+
var fold = (text) => text.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase();
|
|
233
|
+
function setupComboboxes(root = document) {
|
|
234
|
+
const roots = [...root.querySelectorAll(".cl-combobox")];
|
|
235
|
+
if (root instanceof HTMLElement && root.matches(".cl-combobox")) roots.push(root);
|
|
236
|
+
for (const el of roots) {
|
|
237
|
+
const parts = partsOf2(el);
|
|
238
|
+
if (!parts) continue;
|
|
239
|
+
const { input, list } = parts;
|
|
240
|
+
input.setAttribute("role", "combobox");
|
|
241
|
+
input.setAttribute("aria-autocomplete", "list");
|
|
242
|
+
input.setAttribute("aria-haspopup", "listbox");
|
|
243
|
+
input.setAttribute("aria-expanded", String(isOpen(parts)));
|
|
244
|
+
input.setAttribute("aria-controls", idFor(list));
|
|
245
|
+
input.autocomplete = "off";
|
|
246
|
+
list.setAttribute("role", "listbox");
|
|
247
|
+
for (const item of items(parts)) {
|
|
248
|
+
item.setAttribute("role", "option");
|
|
249
|
+
idFor(item);
|
|
250
|
+
if (!item.hasAttribute("aria-selected")) item.setAttribute("aria-selected", "false");
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
function position2({ input, content }) {
|
|
255
|
+
place(content, input, { side: "bottom", align: "start", offset: 6 });
|
|
256
|
+
}
|
|
257
|
+
function highlight(parts, item, scroll = true) {
|
|
258
|
+
for (const other of items(parts)) other.toggleAttribute("data-highlighted", other === item);
|
|
259
|
+
if (item) {
|
|
260
|
+
parts.input.setAttribute("aria-activedescendant", item.id);
|
|
261
|
+
if (scroll) item.scrollIntoView({ block: "nearest" });
|
|
262
|
+
} else {
|
|
263
|
+
parts.input.removeAttribute("aria-activedescendant");
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function filter(parts, query) {
|
|
267
|
+
const q = fold(query.trim());
|
|
268
|
+
for (const item of items(parts)) item.hidden = !!q && !fold(label(item)).includes(q);
|
|
269
|
+
for (const group of parts.list.querySelectorAll('[role="group"]')) {
|
|
270
|
+
group.hidden = !group.querySelector(".cl-combobox-item:not([hidden])");
|
|
271
|
+
}
|
|
272
|
+
for (const separator of parts.list.querySelectorAll(".cl-combobox-separator")) {
|
|
273
|
+
separator.hidden = !!q;
|
|
274
|
+
}
|
|
275
|
+
parts.content.toggleAttribute("data-empty", visible(parts).length === 0);
|
|
276
|
+
}
|
|
277
|
+
function open(parts) {
|
|
278
|
+
if (isOpen(parts)) return;
|
|
279
|
+
setupComboboxes(parts.root);
|
|
280
|
+
filter(parts, "");
|
|
281
|
+
parts.content.showPopover();
|
|
282
|
+
parts.input.setAttribute("aria-expanded", "true");
|
|
283
|
+
position2(parts);
|
|
284
|
+
highlight(
|
|
285
|
+
parts,
|
|
286
|
+
items(parts).find((item) => item.getAttribute("aria-selected") === "true") ?? null
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
function close2(parts) {
|
|
290
|
+
if (!isOpen(parts)) return;
|
|
291
|
+
parts.content.hidePopover();
|
|
292
|
+
parts.input.setAttribute("aria-expanded", "false");
|
|
293
|
+
highlight(parts, null);
|
|
294
|
+
const picked = items(parts).find((item) => item.getAttribute("aria-selected") === "true");
|
|
295
|
+
parts.input.value = picked ? label(picked) : "";
|
|
296
|
+
}
|
|
297
|
+
function pick(parts, item) {
|
|
298
|
+
for (const other of items(parts)) other.setAttribute("aria-selected", String(other === item));
|
|
299
|
+
const hidden = parts.root.querySelector('input[type="hidden"]');
|
|
300
|
+
if (hidden) hidden.value = item.dataset.value ?? label(item);
|
|
301
|
+
close2(parts);
|
|
302
|
+
parts.input.dispatchEvent(new Event("change", { bubbles: true }));
|
|
303
|
+
}
|
|
304
|
+
function onComboboxClick(target) {
|
|
305
|
+
const parts = partsOf2(target);
|
|
306
|
+
if (!parts) return;
|
|
307
|
+
const item = target.closest(ITEM);
|
|
308
|
+
if (item && parts.list.contains(item)) return pick(parts, item);
|
|
309
|
+
if (target.closest(".cl-combobox-trigger")) {
|
|
310
|
+
parts.input.focus();
|
|
311
|
+
return isOpen(parts) ? close2(parts) : open(parts);
|
|
312
|
+
}
|
|
313
|
+
if (target === parts.input && !parts.input.disabled) open(parts);
|
|
314
|
+
}
|
|
315
|
+
function onComboboxInput(target) {
|
|
316
|
+
const parts = partsOf2(target);
|
|
317
|
+
if (!parts || target !== parts.input) return;
|
|
318
|
+
open(parts);
|
|
319
|
+
filter(parts, parts.input.value);
|
|
320
|
+
highlight(parts, null);
|
|
321
|
+
}
|
|
322
|
+
function onComboboxKeyDown(event, target) {
|
|
323
|
+
const parts = partsOf2(target);
|
|
324
|
+
if (!parts || target !== parts.input) return;
|
|
325
|
+
const list = visible(parts);
|
|
326
|
+
const current = list.findIndex((item) => item.hasAttribute("data-highlighted"));
|
|
327
|
+
const move = (to) => {
|
|
328
|
+
event.preventDefault();
|
|
329
|
+
if (!isOpen(parts)) return open(parts);
|
|
330
|
+
highlight(parts, list[(to + list.length) % list.length] ?? null);
|
|
331
|
+
};
|
|
332
|
+
switch (event.key) {
|
|
333
|
+
case "ArrowDown":
|
|
334
|
+
return move(current + 1);
|
|
335
|
+
case "ArrowUp":
|
|
336
|
+
return move(current < 0 ? -1 : current - 1);
|
|
337
|
+
case "Enter":
|
|
338
|
+
if (isOpen(parts) && current >= 0) {
|
|
339
|
+
event.preventDefault();
|
|
340
|
+
pick(parts, list[current]);
|
|
341
|
+
}
|
|
342
|
+
return;
|
|
343
|
+
case "Escape":
|
|
344
|
+
if (isOpen(parts)) {
|
|
345
|
+
event.preventDefault();
|
|
346
|
+
close2(parts);
|
|
347
|
+
}
|
|
348
|
+
return;
|
|
349
|
+
case "Tab":
|
|
350
|
+
return close2(parts);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
function onComboboxPointerMove(target) {
|
|
354
|
+
const item = target.closest(ITEM);
|
|
355
|
+
const parts = partsOf2(item);
|
|
356
|
+
if (item && parts && !item.hasAttribute("data-highlighted")) highlight(parts, item, false);
|
|
357
|
+
}
|
|
358
|
+
function onComboboxPointerDown(event, target) {
|
|
359
|
+
if (target.closest(".cl-combobox-content, .cl-combobox-trigger")) event.preventDefault();
|
|
360
|
+
}
|
|
361
|
+
function onComboboxFocusOut(event, target) {
|
|
362
|
+
const parts = partsOf2(target);
|
|
363
|
+
if (!parts || target !== parts.input) return;
|
|
364
|
+
const next = event.relatedTarget;
|
|
365
|
+
if (next && parts.root.contains(next)) return;
|
|
366
|
+
close2(parts);
|
|
367
|
+
}
|
|
368
|
+
function repositionComboboxes() {
|
|
369
|
+
for (const content of document.querySelectorAll(
|
|
370
|
+
".cl-combobox-content:popover-open"
|
|
371
|
+
)) {
|
|
372
|
+
const parts = partsOf2(content);
|
|
373
|
+
if (parts) position2(parts);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function openDialog(id) {
|
|
377
|
+
const dialog = document.getElementById(id);
|
|
378
|
+
if (dialog instanceof HTMLDialogElement && !dialog.open) dialog.showModal();
|
|
379
|
+
}
|
|
380
|
+
function onDialogClick(event, target) {
|
|
381
|
+
const opener = target.closest("[data-dialog-open]");
|
|
382
|
+
if (opener) {
|
|
383
|
+
openDialog(opener.dataset.dialogOpen);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
const closer = target.closest("[data-dialog-close]");
|
|
387
|
+
if (closer) {
|
|
388
|
+
closer.closest("dialog")?.close();
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
if (target instanceof HTMLDialogElement && target.matches(".cl-dialog, .cl-sheet")) {
|
|
392
|
+
const r = target.getBoundingClientRect();
|
|
393
|
+
const inside = event.clientX >= r.left && event.clientX <= r.right && event.clientY >= r.top && event.clientY <= r.bottom;
|
|
394
|
+
if (!inside) target.close();
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
var FLOATING = ".cl-popover, .cl-menu";
|
|
398
|
+
var TOOLTIP_OFFSET = 10;
|
|
399
|
+
var ITEM2 = '.cl-menu-item:not(:disabled):not([aria-disabled="true"])';
|
|
400
|
+
function anchorOf(el) {
|
|
401
|
+
const custom = el.dataset.anchor && document.getElementById(el.dataset.anchor);
|
|
402
|
+
return custom || document.querySelector(`[popovertarget="${CSS.escape(el.id)}"]`);
|
|
403
|
+
}
|
|
404
|
+
function placeNextTo(el, anchor, side, offset) {
|
|
405
|
+
place(el, anchor, {
|
|
406
|
+
side: el.dataset.side || side,
|
|
407
|
+
align: el.dataset.align || (el.matches(".cl-menu") ? "start" : "center"),
|
|
408
|
+
offset
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
var openedWithKeyboard = false;
|
|
412
|
+
function onFloatingBeforeToggle(event) {
|
|
413
|
+
const el = event.target;
|
|
414
|
+
if (!(el instanceof HTMLElement) || !el.matches(FLOATING)) return;
|
|
415
|
+
if (event.newState === "open") {
|
|
416
|
+
el.style.visibility = "hidden";
|
|
417
|
+
el.dataset.placed = el.dataset.side || "bottom";
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
function onFloatingToggle(event) {
|
|
421
|
+
const el = event.target;
|
|
422
|
+
if (!(el instanceof HTMLElement) || !el.matches(FLOATING)) return;
|
|
423
|
+
const open2 = event.newState === "open";
|
|
424
|
+
const anchor = anchorOf(el);
|
|
425
|
+
anchor?.setAttribute("aria-expanded", String(open2));
|
|
426
|
+
if (!open2) return;
|
|
427
|
+
if (anchor) placeNextTo(el, anchor, "bottom", 4);
|
|
428
|
+
el.style.visibility = "";
|
|
429
|
+
if (el.matches(".cl-menu")) {
|
|
430
|
+
if (!el.hasAttribute("tabindex")) el.tabIndex = -1;
|
|
431
|
+
const first = el.querySelector(ITEM2);
|
|
432
|
+
(openedWithKeyboard && first ? first : el).focus({ preventScroll: true });
|
|
433
|
+
}
|
|
434
|
+
openedWithKeyboard = false;
|
|
435
|
+
}
|
|
436
|
+
function repositionOpen() {
|
|
437
|
+
for (const el of document.querySelectorAll(`${FLOATING}, .cl-tooltip`)) {
|
|
438
|
+
if (!el.matches(":popover-open")) continue;
|
|
439
|
+
const anchor = el.matches(".cl-tooltip") ? document.querySelector(`[aria-describedby~="${CSS.escape(el.id)}"]`) : anchorOf(el);
|
|
440
|
+
if (anchor)
|
|
441
|
+
placeNextTo(
|
|
442
|
+
el,
|
|
443
|
+
anchor,
|
|
444
|
+
el.matches(".cl-tooltip") ? "top" : "bottom",
|
|
445
|
+
el.matches(".cl-tooltip") ? TOOLTIP_OFFSET : 4
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
function onMenuKeyDown(event, target) {
|
|
450
|
+
if (target.matches("[popovertarget]") && ["Enter", " ", "ArrowDown"].includes(event.key)) {
|
|
451
|
+
openedWithKeyboard = true;
|
|
452
|
+
if (event.key === "ArrowDown") {
|
|
453
|
+
event.preventDefault();
|
|
454
|
+
target.click();
|
|
455
|
+
}
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
const menu = target.closest(".cl-menu");
|
|
459
|
+
if (!menu) return;
|
|
460
|
+
const items2 = [...menu.querySelectorAll(ITEM2)];
|
|
461
|
+
const index = items2.indexOf(document.activeElement);
|
|
462
|
+
const move = (to) => {
|
|
463
|
+
event.preventDefault();
|
|
464
|
+
items2[(to + items2.length) % items2.length]?.focus();
|
|
465
|
+
};
|
|
466
|
+
switch (event.key) {
|
|
467
|
+
case "ArrowDown":
|
|
468
|
+
return move(index + 1);
|
|
469
|
+
case "ArrowUp":
|
|
470
|
+
return move(index < 0 ? -1 : index - 1);
|
|
471
|
+
case "Home":
|
|
472
|
+
return move(0);
|
|
473
|
+
case "End":
|
|
474
|
+
return move(-1);
|
|
475
|
+
case "Tab":
|
|
476
|
+
menu.hidePopover();
|
|
477
|
+
return;
|
|
478
|
+
default:
|
|
479
|
+
if (event.key.length === 1 && /\S/.test(event.key)) {
|
|
480
|
+
const key = event.key.toLowerCase();
|
|
481
|
+
const order = [...items2.slice(index + 1), ...items2.slice(0, index + 1)];
|
|
482
|
+
order.find((item) => item.textContent?.trim().toLowerCase().startsWith(key))?.focus();
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
function onMenuPointerMove(target) {
|
|
487
|
+
const item = target.closest(ITEM2);
|
|
488
|
+
if (item && item.closest(".cl-menu") && document.activeElement !== item) {
|
|
489
|
+
item.focus({ preventScroll: true });
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
function onMenuPointerLeave(event) {
|
|
493
|
+
const menu = event.target;
|
|
494
|
+
if (menu instanceof HTMLElement && menu.matches(".cl-menu") && menu.contains(document.activeElement)) {
|
|
495
|
+
menu.focus({ preventScroll: true });
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
function onMenuClick(target) {
|
|
499
|
+
const item = target.closest(ITEM2);
|
|
500
|
+
const menu = item?.closest(".cl-menu");
|
|
501
|
+
if (!item || !menu) return;
|
|
502
|
+
const role = item.getAttribute("role");
|
|
503
|
+
if (role === "menuitemcheckbox") {
|
|
504
|
+
item.setAttribute("aria-checked", String(item.getAttribute("aria-checked") !== "true"));
|
|
505
|
+
} else if (role === "menuitemradio") {
|
|
506
|
+
const group = item.closest('[role="group"]') ?? menu;
|
|
507
|
+
for (const radio of group.querySelectorAll('[role="menuitemradio"]')) {
|
|
508
|
+
radio.setAttribute("aria-checked", String(radio === item));
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
if (!item.hasAttribute("data-keep-open")) {
|
|
512
|
+
menu.hidePopover();
|
|
513
|
+
anchorOf(menu)?.focus();
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
function tooltipFor(target) {
|
|
517
|
+
const owner = target.closest("[aria-describedby]");
|
|
518
|
+
if (!owner) return null;
|
|
519
|
+
for (const id of owner.getAttribute("aria-describedby").split(/\s+/)) {
|
|
520
|
+
const tip = document.getElementById(id);
|
|
521
|
+
if (tip?.matches(".cl-tooltip")) return { owner, tip };
|
|
522
|
+
}
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
function showTooltip(owner, tip) {
|
|
526
|
+
if (!tip.matches(":popover-open")) {
|
|
527
|
+
tip.dataset.placed = tip.dataset.side || "top";
|
|
528
|
+
tip.showPopover();
|
|
529
|
+
}
|
|
530
|
+
placeNextTo(tip, owner, "top", TOOLTIP_OFFSET);
|
|
531
|
+
}
|
|
532
|
+
function hideTooltip(tip) {
|
|
533
|
+
if (tip.matches(":popover-open")) tip.hidePopover();
|
|
534
|
+
}
|
|
535
|
+
function onTooltipEnter(event, target) {
|
|
536
|
+
if (event instanceof PointerEvent && event.pointerType === "touch") return;
|
|
537
|
+
if (event instanceof FocusEvent && !target.matches(":focus-visible")) return;
|
|
538
|
+
const found = tooltipFor(target);
|
|
539
|
+
if (found) showTooltip(found.owner, found.tip);
|
|
540
|
+
}
|
|
541
|
+
function onTooltipLeave(event, target) {
|
|
542
|
+
const found = tooltipFor(target);
|
|
543
|
+
if (!found) return;
|
|
544
|
+
const next = event.relatedTarget;
|
|
545
|
+
if (next && found.owner.contains(next)) return;
|
|
546
|
+
hideTooltip(found.tip);
|
|
547
|
+
}
|
|
548
|
+
function hideAllTooltips() {
|
|
549
|
+
for (const tip of document.querySelectorAll(".cl-tooltip:popover-open"))
|
|
550
|
+
hideTooltip(tip);
|
|
551
|
+
}
|
|
552
|
+
var THUMB = 12;
|
|
553
|
+
var bounds = (input) => {
|
|
554
|
+
const min = input.min === "" ? 0 : Number(input.min);
|
|
555
|
+
const max = input.max === "" ? 100 : Number(input.max);
|
|
556
|
+
return { min, max };
|
|
557
|
+
};
|
|
558
|
+
function fill(input) {
|
|
559
|
+
const { min, max } = bounds(input);
|
|
560
|
+
const percent = max > min ? (Number(input.value) - min) / (max - min) * 100 : 0;
|
|
561
|
+
input.style.setProperty("--cl-slider-fill", `${percent}%`);
|
|
562
|
+
}
|
|
563
|
+
function onSliderInput(target) {
|
|
564
|
+
if (target instanceof HTMLInputElement && target.matches(".cl-slider")) fill(target);
|
|
565
|
+
}
|
|
566
|
+
function setupSliders(root = document) {
|
|
567
|
+
if (root instanceof HTMLInputElement && root.matches(".cl-slider")) fill(root);
|
|
568
|
+
for (const input of root.querySelectorAll("input.cl-slider")) fill(input);
|
|
569
|
+
}
|
|
570
|
+
function onFormReset(target) {
|
|
571
|
+
if (target instanceof HTMLFormElement) setTimeout(() => setupSliders(target));
|
|
572
|
+
}
|
|
573
|
+
function moveTo(input, x) {
|
|
574
|
+
const rect = input.getBoundingClientRect();
|
|
575
|
+
let ratio = Math.min(1, Math.max(0, (x - rect.left - THUMB / 2) / (rect.width - THUMB)));
|
|
576
|
+
if (getComputedStyle(input).direction === "rtl") ratio = 1 - ratio;
|
|
577
|
+
const { min, max } = bounds(input);
|
|
578
|
+
const step2 = input.step === "any" ? 0 : Number(input.step || 1);
|
|
579
|
+
let value = min + ratio * (max - min);
|
|
580
|
+
if (step2 > 0) value = min + Math.round((value - min) / step2) * step2;
|
|
581
|
+
const before = input.value;
|
|
582
|
+
input.value = String(value);
|
|
583
|
+
if (input.value !== before) input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
584
|
+
}
|
|
585
|
+
function onSliderPointerDown(event, target) {
|
|
586
|
+
if (event.pointerType === "mouse") return;
|
|
587
|
+
if (!(target instanceof HTMLInputElement) || !target.matches(".cl-slider") || target.disabled) {
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
const input = target;
|
|
591
|
+
const start2 = input.value;
|
|
592
|
+
input.focus({ preventScroll: true });
|
|
593
|
+
try {
|
|
594
|
+
input.setPointerCapture(event.pointerId);
|
|
595
|
+
} catch {
|
|
596
|
+
}
|
|
597
|
+
moveTo(input, event.clientX);
|
|
598
|
+
const move = (e) => {
|
|
599
|
+
if (e.pointerId === event.pointerId) moveTo(input, e.clientX);
|
|
600
|
+
};
|
|
601
|
+
const end2 = (e) => {
|
|
602
|
+
if (e.pointerId !== event.pointerId) return;
|
|
603
|
+
input.removeEventListener("pointermove", move);
|
|
604
|
+
input.removeEventListener("pointerup", end2);
|
|
605
|
+
input.removeEventListener("pointercancel", end2);
|
|
606
|
+
if (input.value !== start2) input.dispatchEvent(new Event("change", { bubbles: true }));
|
|
607
|
+
};
|
|
608
|
+
input.addEventListener("pointermove", move);
|
|
609
|
+
input.addEventListener("pointerup", end2);
|
|
610
|
+
input.addEventListener("pointercancel", end2);
|
|
611
|
+
}
|
|
612
|
+
function select(tab) {
|
|
613
|
+
const list = tab.closest('[role="tablist"]');
|
|
614
|
+
if (!list || tab.matches(":disabled")) return;
|
|
615
|
+
for (const other of list.querySelectorAll('[role="tab"]')) {
|
|
616
|
+
const selected = other === tab;
|
|
617
|
+
other.setAttribute("aria-selected", String(selected));
|
|
618
|
+
other.tabIndex = selected ? 0 : -1;
|
|
619
|
+
const panel = document.getElementById(other.getAttribute("aria-controls") ?? "");
|
|
620
|
+
if (panel) panel.hidden = !selected;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
function onTabClick(target) {
|
|
624
|
+
const tab = target.closest('.cl-tabs [role="tab"]');
|
|
625
|
+
if (tab) select(tab);
|
|
626
|
+
}
|
|
627
|
+
function onTabKeyDown(event, target) {
|
|
628
|
+
const tab = target.closest('.cl-tabs [role="tab"]');
|
|
629
|
+
const list = tab?.closest('[role="tablist"]');
|
|
630
|
+
if (!tab || !list) return;
|
|
631
|
+
const vertical = list.getAttribute("aria-orientation") === "vertical";
|
|
632
|
+
const tabs = [...list.querySelectorAll('[role="tab"]:not(:disabled)')];
|
|
633
|
+
const index = tabs.indexOf(tab);
|
|
634
|
+
const keys = vertical ? { ArrowDown: index + 1, ArrowUp: index - 1, Home: 0, End: tabs.length - 1 } : { ArrowRight: index + 1, ArrowLeft: index - 1, Home: 0, End: tabs.length - 1 };
|
|
635
|
+
if (!(event.key in keys)) return;
|
|
636
|
+
event.preventDefault();
|
|
637
|
+
const next = tabs[(keys[event.key] + tabs.length) % tabs.length];
|
|
638
|
+
next.focus();
|
|
639
|
+
select(next);
|
|
640
|
+
}
|
|
641
|
+
function setupTabs(root = document) {
|
|
642
|
+
for (const tab of root.querySelectorAll('.cl-tabs [role="tab"]')) {
|
|
643
|
+
tab.tabIndex = tab.getAttribute("aria-selected") === "true" ? 0 : -1;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
var ICONS = {
|
|
647
|
+
success: '<circle cx="12" cy="12" r="10"/><path d="m16 9-5.5 5.5L8 12"/>',
|
|
648
|
+
info: '<circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/>',
|
|
649
|
+
warning: '<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/>',
|
|
650
|
+
error: '<path d="m15 9-6 6"/><path d="M2.586 16.726A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2h6.624a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586z"/><path d="m9 9 6 6"/>',
|
|
651
|
+
loading: '<path d="M21 12a9 9 0 1 1-6.219-8.56"/>'
|
|
652
|
+
};
|
|
653
|
+
var svg = (paths) => `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${paths}</svg>`;
|
|
654
|
+
var VISIBLE = 3;
|
|
655
|
+
var GAP = 14;
|
|
656
|
+
function toaster() {
|
|
657
|
+
let list = document.querySelector("ol.cl-toaster");
|
|
658
|
+
if (!list) {
|
|
659
|
+
list = document.createElement("ol");
|
|
660
|
+
list.className = "cl-toaster";
|
|
661
|
+
list.setAttribute("aria-label", "Notifications");
|
|
662
|
+
list.setAttribute("aria-live", "polite");
|
|
663
|
+
list.addEventListener("pointerenter", () => {
|
|
664
|
+
list.dataset.expanded = "";
|
|
665
|
+
layout(list);
|
|
666
|
+
});
|
|
667
|
+
list.addEventListener("pointerleave", () => {
|
|
668
|
+
delete list.dataset.expanded;
|
|
669
|
+
layout(list);
|
|
670
|
+
});
|
|
671
|
+
document.body.append(list);
|
|
672
|
+
}
|
|
673
|
+
return list;
|
|
674
|
+
}
|
|
675
|
+
function layout(list) {
|
|
676
|
+
const toasts = [...list.querySelectorAll(".cl-toast:not([data-removed])")].reverse();
|
|
677
|
+
const expanded = list.dataset.expanded !== void 0;
|
|
678
|
+
const front = toasts[0];
|
|
679
|
+
list.style.setProperty("--front-height", `${front ? Number(front.dataset.height) : 0}px`);
|
|
680
|
+
let offset = 0;
|
|
681
|
+
toasts.forEach((el, i) => {
|
|
682
|
+
el.toggleAttribute("data-front", i === 0);
|
|
683
|
+
el.toggleAttribute("data-hidden", i >= VISIBLE);
|
|
684
|
+
el.style.zIndex = String(toasts.length - i);
|
|
685
|
+
const y = expanded ? -offset : -i * GAP;
|
|
686
|
+
el.style.setProperty("--y", `${y}px`);
|
|
687
|
+
el.style.setProperty("--scale", expanded ? "1" : String(1 - i * 0.05));
|
|
688
|
+
offset += Number(el.dataset.height) + GAP;
|
|
689
|
+
});
|
|
690
|
+
list.style.setProperty("--expanded-height", `${Math.max(0, offset - GAP)}px`);
|
|
691
|
+
}
|
|
692
|
+
function dismiss(el) {
|
|
693
|
+
if (el.dataset.removed !== void 0) return;
|
|
694
|
+
el.dataset.removed = "";
|
|
695
|
+
const list = el.closest(".cl-toaster");
|
|
696
|
+
if (list) layout(list);
|
|
697
|
+
setTimeout(() => {
|
|
698
|
+
el.remove();
|
|
699
|
+
if (list && !list.querySelector(".cl-toast")) delete list.dataset.expanded;
|
|
700
|
+
}, 200);
|
|
701
|
+
}
|
|
702
|
+
function toast(title, options = {}) {
|
|
703
|
+
const { description, type = "default", action } = options;
|
|
704
|
+
const duration = options.duration ?? (type === "loading" ? Infinity : 4e3);
|
|
705
|
+
const el = document.createElement("li");
|
|
706
|
+
el.className = "cl-toast";
|
|
707
|
+
el.dataset.type = type;
|
|
708
|
+
el.setAttribute("role", type === "error" ? "alert" : "status");
|
|
709
|
+
if (ICONS[type]) {
|
|
710
|
+
const icon = document.createElement("div");
|
|
711
|
+
icon.className = "cl-toast-icon";
|
|
712
|
+
icon.innerHTML = svg(ICONS[type]);
|
|
713
|
+
el.append(icon);
|
|
714
|
+
}
|
|
715
|
+
const content = document.createElement("div");
|
|
716
|
+
content.className = "cl-toast-content";
|
|
717
|
+
const titleEl = document.createElement("div");
|
|
718
|
+
titleEl.className = "cl-toast-title";
|
|
719
|
+
titleEl.textContent = title;
|
|
720
|
+
content.append(titleEl);
|
|
721
|
+
if (description) {
|
|
722
|
+
const desc = document.createElement("div");
|
|
723
|
+
desc.className = "cl-toast-description";
|
|
724
|
+
desc.textContent = description;
|
|
725
|
+
content.append(desc);
|
|
726
|
+
}
|
|
727
|
+
el.append(content);
|
|
728
|
+
if (action) {
|
|
729
|
+
const button = document.createElement("button");
|
|
730
|
+
button.type = "button";
|
|
731
|
+
button.className = "cl-toast-action";
|
|
732
|
+
button.textContent = action.label;
|
|
733
|
+
button.addEventListener("click", (event) => {
|
|
734
|
+
action.onClick?.(event);
|
|
735
|
+
dismiss(el);
|
|
736
|
+
});
|
|
737
|
+
el.append(button);
|
|
738
|
+
}
|
|
739
|
+
const list = toaster();
|
|
740
|
+
el.toggleAttribute("data-front", true);
|
|
741
|
+
list.append(el);
|
|
742
|
+
el.dataset.height = String(el.offsetHeight);
|
|
743
|
+
layout(list);
|
|
744
|
+
if (Number.isFinite(duration)) {
|
|
745
|
+
let remaining = duration;
|
|
746
|
+
let started2 = Date.now();
|
|
747
|
+
let timer = setTimeout(() => dismiss(el), remaining);
|
|
748
|
+
el.addEventListener("pointerenter", () => {
|
|
749
|
+
clearTimeout(timer);
|
|
750
|
+
remaining -= Date.now() - started2;
|
|
751
|
+
});
|
|
752
|
+
el.addEventListener("pointerleave", () => {
|
|
753
|
+
started2 = Date.now();
|
|
754
|
+
timer = setTimeout(() => dismiss(el), Math.max(remaining, 1e3));
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
return () => dismiss(el);
|
|
758
|
+
}
|
|
759
|
+
toast.success = (title, options) => toast(title, { ...options, type: "success" });
|
|
760
|
+
toast.info = (title, options) => toast(title, { ...options, type: "info" });
|
|
761
|
+
toast.warning = (title, options) => toast(title, { ...options, type: "warning" });
|
|
762
|
+
toast.error = (title, options) => toast(title, { ...options, type: "error" });
|
|
763
|
+
function onToastClick(target) {
|
|
764
|
+
const trigger = target.closest("[data-toast]");
|
|
765
|
+
if (!trigger) return;
|
|
766
|
+
const { toast: title, toastDescription, toastType } = trigger.dataset;
|
|
767
|
+
toast(title, { description: toastDescription, type: toastType || "default" });
|
|
768
|
+
}
|
|
769
|
+
var started = false;
|
|
770
|
+
var asElement = (target) => target instanceof Element ? target : null;
|
|
771
|
+
function start() {
|
|
772
|
+
if (started || typeof document === "undefined") return;
|
|
773
|
+
started = true;
|
|
774
|
+
document.addEventListener("click", (event) => {
|
|
775
|
+
const target = asElement(event.target);
|
|
776
|
+
if (!target) return;
|
|
777
|
+
onAccordionClick(event, target);
|
|
778
|
+
onCarouselClick(target);
|
|
779
|
+
onComboboxClick(target);
|
|
780
|
+
onDialogClick(event, target);
|
|
781
|
+
onMenuClick(target);
|
|
782
|
+
onTabClick(target);
|
|
783
|
+
onToastClick(target);
|
|
784
|
+
});
|
|
785
|
+
document.addEventListener("keydown", (event) => {
|
|
786
|
+
const target = asElement(event.target);
|
|
787
|
+
if (event.key === "Escape") hideAllTooltips();
|
|
788
|
+
if (!target) return;
|
|
789
|
+
onCarouselKeyDown(event, target);
|
|
790
|
+
onComboboxKeyDown(event, target);
|
|
791
|
+
onMenuKeyDown(event, target);
|
|
792
|
+
onTabKeyDown(event, target);
|
|
793
|
+
});
|
|
794
|
+
document.addEventListener("input", (event) => {
|
|
795
|
+
const target = asElement(event.target);
|
|
796
|
+
if (!target) return;
|
|
797
|
+
onComboboxInput(target);
|
|
798
|
+
onSliderInput(target);
|
|
799
|
+
});
|
|
800
|
+
document.addEventListener("reset", (event) => {
|
|
801
|
+
const target = asElement(event.target);
|
|
802
|
+
if (target) onFormReset(target);
|
|
803
|
+
});
|
|
804
|
+
document.addEventListener("pointerdown", (event) => {
|
|
805
|
+
const target = asElement(event.target);
|
|
806
|
+
if (!target) return;
|
|
807
|
+
onCarouselPointerDown(event, target);
|
|
808
|
+
onComboboxPointerDown(event, target);
|
|
809
|
+
onSliderPointerDown(event, target);
|
|
810
|
+
});
|
|
811
|
+
document.addEventListener("beforetoggle", onFloatingBeforeToggle, true);
|
|
812
|
+
document.addEventListener("toggle", onFloatingToggle, true);
|
|
813
|
+
document.addEventListener("pointerleave", onMenuPointerLeave, true);
|
|
814
|
+
document.addEventListener("pointermove", (event) => {
|
|
815
|
+
const target = asElement(event.target);
|
|
816
|
+
if (!target) return;
|
|
817
|
+
onComboboxPointerMove(target);
|
|
818
|
+
onMenuPointerMove(target);
|
|
819
|
+
});
|
|
820
|
+
document.addEventListener("pointerover", (event) => {
|
|
821
|
+
const target = asElement(event.target);
|
|
822
|
+
if (target) onTooltipEnter(event, target);
|
|
823
|
+
});
|
|
824
|
+
document.addEventListener("pointerout", (event) => {
|
|
825
|
+
const target = asElement(event.target);
|
|
826
|
+
if (target) onTooltipLeave(event, target);
|
|
827
|
+
});
|
|
828
|
+
document.addEventListener("focusin", (event) => {
|
|
829
|
+
const target = asElement(event.target);
|
|
830
|
+
if (target) onTooltipEnter(event, target);
|
|
831
|
+
});
|
|
832
|
+
document.addEventListener("focusout", (event) => {
|
|
833
|
+
const target = asElement(event.target);
|
|
834
|
+
if (!target) return;
|
|
835
|
+
onComboboxFocusOut(event, target);
|
|
836
|
+
onTooltipLeave(event, target);
|
|
837
|
+
});
|
|
838
|
+
const reposition = () => {
|
|
839
|
+
repositionOpen();
|
|
840
|
+
repositionComboboxes();
|
|
841
|
+
};
|
|
842
|
+
window.addEventListener("scroll", reposition, { capture: true, passive: true });
|
|
843
|
+
window.addEventListener("resize", reposition, { passive: true });
|
|
844
|
+
document.addEventListener("scroll", onCarouselScroll, { capture: true, passive: true });
|
|
845
|
+
window.addEventListener("resize", () => setupCarousels(), { passive: true });
|
|
846
|
+
const setup = (root) => {
|
|
847
|
+
setupTabs(root);
|
|
848
|
+
setupSliders(root);
|
|
849
|
+
setupComboboxes(root);
|
|
850
|
+
setupCarousels(root);
|
|
851
|
+
};
|
|
852
|
+
const ready = () => {
|
|
853
|
+
setup(document);
|
|
854
|
+
new MutationObserver((records) => {
|
|
855
|
+
for (const record of records) {
|
|
856
|
+
for (const node of record.addedNodes) if (node instanceof Element) setup(node);
|
|
857
|
+
}
|
|
858
|
+
}).observe(document.body, { childList: true, subtree: true });
|
|
859
|
+
};
|
|
860
|
+
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", ready);
|
|
861
|
+
else ready();
|
|
862
|
+
}
|
|
863
|
+
var cl = { start, toast, openDialog };
|
|
864
|
+
window.cl = cl;
|
|
865
|
+
start();
|
|
866
|
+
})();
|