drab 5.0.0 → 5.0.2
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/README.md +0 -5
- package/animate/define.d.ts +1 -2
- package/animate/define.iife.js +1 -1
- package/animate/define.js +2 -1
- package/animate/index.d.ts +8 -5
- package/animate/index.iife.js +1 -1
- package/animate/index.js +138 -1
- package/{index-SyRipepB.d.ts → base/copy/index.d.ts} +4 -5
- package/base/copy/index.js +25 -0
- package/base/define.d.ts +1 -2
- package/base/define.js +2 -1
- package/base/index.d.ts +1 -3
- package/base/index.js +108 -1
- package/breakpoint/define.d.ts +1 -2
- package/breakpoint/define.js +2 -1
- package/breakpoint/index.d.ts +7 -5
- package/breakpoint/index.js +55 -1
- package/contextmenu/define.d.ts +1 -2
- package/contextmenu/define.iife.js +1 -1
- package/contextmenu/define.js +2 -1
- package/contextmenu/index.d.ts +4 -6
- package/contextmenu/index.iife.js +1 -1
- package/contextmenu/index.js +71 -1
- package/copy/define.d.ts +1 -2
- package/copy/define.js +2 -1
- package/copy/index.d.ts +4 -6
- package/copy/index.iife.js +1 -1
- package/copy/index.js +13 -1
- package/define/index.iife.js +9 -9
- package/define.d.ts +1 -0
- package/define.js +4 -0
- package/details/define.d.ts +1 -2
- package/details/define.iife.js +1 -1
- package/details/define.js +2 -1
- package/details/index.d.ts +4 -6
- package/details/index.iife.js +1 -1
- package/details/index.js +46 -1
- package/dialog/define.d.ts +1 -2
- package/dialog/define.iife.js +1 -1
- package/dialog/define.js +2 -1
- package/dialog/index.d.ts +4 -6
- package/dialog/index.iife.js +1 -1
- package/dialog/index.js +44 -1
- package/editor/define.d.ts +1 -2
- package/editor/define.js +2 -1
- package/editor/index.d.ts +5 -6
- package/editor/index.iife.js +7 -7
- package/editor/index.js +480 -1
- package/fullscreen/define.d.ts +1 -2
- package/fullscreen/define.iife.js +1 -1
- package/fullscreen/define.js +2 -1
- package/fullscreen/index.d.ts +4 -5
- package/fullscreen/index.iife.js +1 -1
- package/fullscreen/index.js +45 -1
- package/index.d.ts +14 -14
- package/index.iife.js +9 -9
- package/index.js +14 -1
- package/package.json +6 -5
- package/popover/define.d.ts +1 -2
- package/popover/define.iife.js +1 -1
- package/popover/define.js +2 -1
- package/popover/index.d.ts +4 -6
- package/popover/index.iife.js +1 -1
- package/popover/index.js +74 -1
- package/share/define.d.ts +1 -2
- package/share/define.js +2 -1
- package/share/index.d.ts +5 -7
- package/share/index.js +34 -1
- package/tablesort/define.d.ts +1 -2
- package/tablesort/define.iife.js +1 -1
- package/tablesort/define.js +2 -1
- package/tablesort/index.d.ts +4 -5
- package/tablesort/index.iife.js +1 -1
- package/tablesort/index.js +109 -1
- package/types/index.d.ts +11 -0
- package/types/index.js +1 -0
- package/youtube/define.d.ts +1 -2
- package/youtube/define.js +2 -1
- package/youtube/index.d.ts +4 -5
- package/youtube/index.iife.js +1 -1
- package/youtube/index.js +56 -1
- package/chunk-57VEEUFG.js +0 -1
- package/chunk-5JV4T7GM.js +0 -1
- package/chunk-6HYPZWQ4.js +0 -1
- package/chunk-7F7CQUEG.js +0 -1
- package/chunk-7KU2PRW5.js +0 -1
- package/chunk-7S6DTKGH.js +0 -1
- package/chunk-G5WFHYNX.js +0 -1
- package/chunk-GPET75FT.js +0 -9
- package/chunk-IQJQPZUL.js +0 -1
- package/chunk-JMJUWKN2.js +0 -1
- package/chunk-MXKU7AKV.js +0 -1
- package/chunk-T7RZI3ZL.js +0 -1
- package/chunk-TSTTUEAF.js +0 -1
- package/chunk-VEVFQB5N.js +0 -1
- package/define/index.d.ts +0 -2
- package/define/index.js +0 -1
package/README.md
CHANGED
@@ -1,10 +1,5 @@
|
|
1
1
|
# A Headless Custom Element Library
|
2
2
|
|
3
|
-
- [Docs](https://drab.robino.dev)
|
4
|
-
- [GitHub](https://github.com/rossrobino/drab)
|
5
|
-
- [npm](https://www.npmjs.com/package/drab)
|
6
|
-
- [MIT License](https://github.com/rossrobino/drab/blob/main/LICENSE.md)
|
7
|
-
|
8
3
|
## Features
|
9
4
|
|
10
5
|
**drab** focuses on providing JavaScript functionality where it's most useful. Many of the elements are helpful wrappers around browser APIs. Here are some of the features of the library.
|
package/animate/define.d.ts
CHANGED
@@ -1,2 +1 @@
|
|
1
|
-
|
2
|
-
export { }
|
1
|
+
export {};
|
package/animate/define.iife.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
"use strict";(()=>{var
|
1
|
+
"use strict";(()=>{var l=class extends HTMLElement{#e=new AbortController;constructor(){super()}get event(){return this.getAttribute("event")??"click"}set event(i){this.setAttribute("event",i)}getTrigger(){return this.querySelectorAll(this.getAttribute("trigger")??"[data-trigger]")}getContent(i=HTMLElement){let n=this.querySelector(this.getAttribute("content")??"[data-content]");if(n instanceof i)return n;throw new Error("Content not found")}swapContent(i=!0,n=800){let e=this.querySelector(this.getAttribute("swap")??"[data-swap]");if(e){let t=Array.from(this.getContent().childNodes);e instanceof HTMLTemplateElement?this.getContent().replaceChildren(e.content.cloneNode(!0)):this.getContent().replaceChildren(...e.childNodes),i&&setTimeout(()=>this.getContent().replaceChildren(...t),n)}}safeListener(i,n,e=document.body,t={}){t.signal=this.#e.signal,e.addEventListener(i,n,t)}triggerListener(i,n=this.event){for(let e of this.getTrigger())e.addEventListener(n,i)}mount(){}connectedCallback(){queueMicrotask(()=>this.mount())}disconnectedCallback(){this.#e.abort()}};var f=class extends l{constructor(){super()}get animationOptions(){let i={};for(let n of this.getAttributeNames())if(n.startsWith("animation-option-")){let e=this.getAttribute(n),[,,t]=n.split("-");e&&(t==="duration"||t==="delay"?i[t]=Number(e):t==="easing"&&(i[t]=e))}return i}async animateElement(i={element:this.getContent(),options:{}}){let{element:n=this.getContent(),options:e={}}=i,t=this.keyframes;if(t.length&&!window.matchMedia("(prefers-reduced-motion: reduce)").matches){e=Object.assign(this.animationOptions,e),e.duration||(e.duration=200),e.easing||(e.easing="ease-in-out");let o=t.at(0),s=t.at(-1);if(o&&s){let a=["composite","easing","offset"];for(let m of a)delete o[m],delete s[m]}e.direction?.includes("reverse")&&([o,s]=[s,o]),Object.assign(n.style,o),await n.animate(t,e).finished,Object.assign(n.style,s)}}get keyframes(){let i=[];for(let n of this.getAttributeNames()){let e=this.getAttribute(n),[,,t,...o]=n.split("-");if(n.startsWith("animation-keyframe-")){let s=o.map((r,a)=>a<1?r:r.at(0)?.toUpperCase()+r.slice(1)).join("");if(t&&s){t==="from"?t="0":t==="to"?t="1":t=String(parseInt(t)*.01);let r=Number(t),a=i.find(m=>m.offset===r);a?a[s]=e:i.push({[s]:e,offset:r})}}}return i.sort((n,e)=>Number(n.offset)-Number(e.offset)),i}};customElements.define("drab-animate",f);})();
|
package/animate/define.js
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
import{
|
1
|
+
import { Animate } from "./index.js";
|
2
|
+
customElements.define("drab-animate", Animate);
|
package/animate/index.d.ts
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
-
import { Base } from
|
2
|
-
|
1
|
+
import { Base } from "../base/index.js";
|
2
|
+
type AnimationKeyframe = `animation-keyframe-${"from" | "to" | number}-${string}`;
|
3
|
+
type AnimationOption = `animation-option-${"easing" | "duration" | "delay"}`;
|
4
|
+
export type AnimateAttributes = Partial<{
|
5
|
+
[K in AnimationKeyframe | AnimationOption]: string;
|
6
|
+
}>;
|
3
7
|
/**
|
4
8
|
* The `Animate` base class provides a declarative way to use the
|
5
9
|
* [Web Animations API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API)
|
@@ -26,7 +30,7 @@ import { Base } from '../base/index.js';
|
|
26
30
|
*
|
27
31
|
* `property` can be `duration`, `delay`, or `easing`.
|
28
32
|
*/
|
29
|
-
declare class Animate extends Base {
|
33
|
+
export declare class Animate extends Base {
|
30
34
|
constructor();
|
31
35
|
/**
|
32
36
|
* @returns An object containing the values of each `animation-option` attribute
|
@@ -50,5 +54,4 @@ declare class Animate extends Base {
|
|
50
54
|
}): Promise<void>;
|
51
55
|
get keyframes(): Keyframe[];
|
52
56
|
}
|
53
|
-
|
54
|
-
export { Animate };
|
57
|
+
export {};
|
package/animate/index.iife.js
CHANGED
@@ -1 +1 @@
|
|
1
|
-
"use strict";(()=>{var
|
1
|
+
"use strict";(()=>{var m=class extends HTMLElement{#e=new AbortController;constructor(){super()}get event(){return this.getAttribute("event")??"click"}set event(i){this.setAttribute("event",i)}getTrigger(){return this.querySelectorAll(this.getAttribute("trigger")??"[data-trigger]")}getContent(i=HTMLElement){let n=this.querySelector(this.getAttribute("content")??"[data-content]");if(n instanceof i)return n;throw new Error("Content not found")}swapContent(i=!0,n=800){let e=this.querySelector(this.getAttribute("swap")??"[data-swap]");if(e){let t=Array.from(this.getContent().childNodes);e instanceof HTMLTemplateElement?this.getContent().replaceChildren(e.content.cloneNode(!0)):this.getContent().replaceChildren(...e.childNodes),i&&setTimeout(()=>this.getContent().replaceChildren(...t),n)}}safeListener(i,n,e=document.body,t={}){t.signal=this.#e.signal,e.addEventListener(i,n,t)}triggerListener(i,n=this.event){for(let e of this.getTrigger())e.addEventListener(n,i)}mount(){}connectedCallback(){queueMicrotask(()=>this.mount())}disconnectedCallback(){this.#e.abort()}};var f=class extends m{constructor(){super()}get animationOptions(){let i={};for(let n of this.getAttributeNames())if(n.startsWith("animation-option-")){let e=this.getAttribute(n),[,,t]=n.split("-");e&&(t==="duration"||t==="delay"?i[t]=Number(e):t==="easing"&&(i[t]=e))}return i}async animateElement(i={element:this.getContent(),options:{}}){let{element:n=this.getContent(),options:e={}}=i,t=this.keyframes;if(t.length&&!window.matchMedia("(prefers-reduced-motion: reduce)").matches){e=Object.assign(this.animationOptions,e),e.duration||(e.duration=200),e.easing||(e.easing="ease-in-out");let o=t.at(0),s=t.at(-1);if(o&&s){let a=["composite","easing","offset"];for(let l of a)delete o[l],delete s[l]}e.direction?.includes("reverse")&&([o,s]=[s,o]),Object.assign(n.style,o),await n.animate(t,e).finished,Object.assign(n.style,s)}}get keyframes(){let i=[];for(let n of this.getAttributeNames()){let e=this.getAttribute(n),[,,t,...o]=n.split("-");if(n.startsWith("animation-keyframe-")){let s=o.map((r,a)=>a<1?r:r.at(0)?.toUpperCase()+r.slice(1)).join("");if(t&&s){t==="from"?t="0":t==="to"?t="1":t=String(parseInt(t)*.01);let r=Number(t),a=i.find(l=>l.offset===r);a?a[s]=e:i.push({[s]:e,offset:r})}}}return i.sort((n,e)=>Number(n.offset)-Number(e.offset)),i}};})();
|
package/animate/index.js
CHANGED
@@ -1 +1,138 @@
|
|
1
|
-
import{
|
1
|
+
import { Base } from "../base/index.js";
|
2
|
+
/**
|
3
|
+
* The `Animate` base class provides a declarative way to use the
|
4
|
+
* [Web Animations API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API)
|
5
|
+
* through HTML attributes. The `animateElement` method uses these attributes and
|
6
|
+
* persists the final animation state. Other elements in **drab** extend this class
|
7
|
+
* to provide animations. You can also extend this class to create your own custom
|
8
|
+
* animated element.
|
9
|
+
*
|
10
|
+
* Keyframes can be set via HTML attributes on the element in the form of:
|
11
|
+
*
|
12
|
+
* ```html
|
13
|
+
* <drab-animate animation-keyframe-offset-property="value">
|
14
|
+
* ```
|
15
|
+
*
|
16
|
+
* `offset` can be `to`, `from`, or a `number`.
|
17
|
+
*
|
18
|
+
* `property` can be any animatable CSS property separated by dashes.
|
19
|
+
*
|
20
|
+
* Animations `options` can be set:
|
21
|
+
*
|
22
|
+
* ```html
|
23
|
+
* <drab-animate animation-option-property="value">
|
24
|
+
* ```
|
25
|
+
*
|
26
|
+
* `property` can be `duration`, `delay`, or `easing`.
|
27
|
+
*/
|
28
|
+
export class Animate extends Base {
|
29
|
+
constructor() {
|
30
|
+
super();
|
31
|
+
}
|
32
|
+
/**
|
33
|
+
* @returns An object containing the values of each `animation-option` attribute
|
34
|
+
*/
|
35
|
+
get animationOptions() {
|
36
|
+
const options = {};
|
37
|
+
for (const attributeName of this.getAttributeNames()) {
|
38
|
+
if (attributeName.startsWith("animation-option-")) {
|
39
|
+
const value = this.getAttribute(attributeName);
|
40
|
+
let [, , option] = attributeName.split("-");
|
41
|
+
if (value) {
|
42
|
+
if (option === "duration" || option === "delay") {
|
43
|
+
options[option] = Number(value);
|
44
|
+
}
|
45
|
+
else if (option === "easing") {
|
46
|
+
options[option] = value;
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
}
|
51
|
+
return options;
|
52
|
+
}
|
53
|
+
/**
|
54
|
+
* @description
|
55
|
+
* Animates a particular element using the web animations API.
|
56
|
+
*
|
57
|
+
* - Disables animation if the user prefers reduced motion.
|
58
|
+
* - Sets default options
|
59
|
+
* - Uses the keyframes provided from `this.keyframes`
|
60
|
+
* - Waits for the animation to complete
|
61
|
+
* - Sets the start and end styles based on the first and last keyframe
|
62
|
+
*
|
63
|
+
* @param animateOptions - animates `this.content()` by default
|
64
|
+
*/
|
65
|
+
async animateElement(animateOptions = { element: this.getContent(), options: {} }) {
|
66
|
+
let { element = this.getContent(), options = {} } = animateOptions;
|
67
|
+
const keyframes = this.keyframes;
|
68
|
+
if (keyframes.length &&
|
69
|
+
!window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
70
|
+
// options passed in via JS override the html attributes
|
71
|
+
options = Object.assign(this.animationOptions, options);
|
72
|
+
// defaults
|
73
|
+
if (!options.duration)
|
74
|
+
options.duration = 200;
|
75
|
+
if (!options.easing)
|
76
|
+
options.easing = "ease-in-out";
|
77
|
+
let startStyles = keyframes.at(0);
|
78
|
+
let endStyles = keyframes.at(-1);
|
79
|
+
if (startStyles && endStyles) {
|
80
|
+
// Don't modify the start/end style based on these,
|
81
|
+
// everything else is a CSS property.
|
82
|
+
// This is instead of doing `fill` since it is discouraged:
|
83
|
+
// https://www.w3.org/TR/web-animations-1/#fill-behavior
|
84
|
+
const notStyles = ["composite", "easing", "offset"];
|
85
|
+
for (const key of notStyles) {
|
86
|
+
delete startStyles[key];
|
87
|
+
delete endStyles[key];
|
88
|
+
}
|
89
|
+
}
|
90
|
+
if (options.direction?.includes("reverse")) {
|
91
|
+
// swap the start and ending values
|
92
|
+
[startStyles, endStyles] = [endStyles, startStyles];
|
93
|
+
}
|
94
|
+
Object.assign(element.style, startStyles);
|
95
|
+
const animation = element.animate(keyframes, options);
|
96
|
+
await animation.finished;
|
97
|
+
Object.assign(element.style, endStyles);
|
98
|
+
}
|
99
|
+
}
|
100
|
+
get keyframes() {
|
101
|
+
const keyframes = [];
|
102
|
+
for (const attributeName of this.getAttributeNames()) {
|
103
|
+
/** the css property value, ex: "translate(100%,0)" */
|
104
|
+
const value = this.getAttribute(attributeName);
|
105
|
+
let [, , offset, ...propertyArray] = attributeName.split("-");
|
106
|
+
if (attributeName.startsWith("animation-keyframe-")) {
|
107
|
+
const property = propertyArray
|
108
|
+
.map((v, i) => {
|
109
|
+
if (i < 1)
|
110
|
+
return v;
|
111
|
+
return v.at(0)?.toUpperCase() + v.slice(1);
|
112
|
+
})
|
113
|
+
.join("");
|
114
|
+
if (offset && property) {
|
115
|
+
if (offset === "from")
|
116
|
+
offset = "0";
|
117
|
+
else if (offset === "to")
|
118
|
+
offset = "1";
|
119
|
+
else
|
120
|
+
offset = String(parseInt(offset) * 0.01);
|
121
|
+
const numberOffset = Number(offset);
|
122
|
+
const sameOffsetKeyframe = keyframes.find((v) => v.offset === numberOffset);
|
123
|
+
if (sameOffsetKeyframe) {
|
124
|
+
sameOffsetKeyframe[property] = value;
|
125
|
+
}
|
126
|
+
else {
|
127
|
+
keyframes.push({
|
128
|
+
[property]: value,
|
129
|
+
offset: numberOffset,
|
130
|
+
});
|
131
|
+
}
|
132
|
+
}
|
133
|
+
}
|
134
|
+
}
|
135
|
+
keyframes.sort((a, b) => Number(a.offset) - Number(b.offset));
|
136
|
+
return keyframes;
|
137
|
+
}
|
138
|
+
}
|
@@ -1,8 +1,9 @@
|
|
1
|
-
import { Base } from
|
2
|
-
|
3
|
-
declare class BaseCopy extends Base {
|
1
|
+
import { Base } from "../index.js";
|
2
|
+
export declare class BaseCopy extends Base {
|
4
3
|
constructor();
|
5
4
|
/**
|
5
|
+
* The value to copy or share.
|
6
|
+
*
|
6
7
|
* @default "" the empty string
|
7
8
|
*/
|
8
9
|
get value(): string;
|
@@ -13,5 +14,3 @@ declare class BaseCopy extends Base {
|
|
13
14
|
*/
|
14
15
|
copy(text?: string): Promise<void>;
|
15
16
|
}
|
16
|
-
|
17
|
-
export { BaseCopy as B };
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import { Base } from "../index.js";
|
2
|
+
export class BaseCopy extends Base {
|
3
|
+
constructor() {
|
4
|
+
super();
|
5
|
+
}
|
6
|
+
/**
|
7
|
+
* The value to copy or share.
|
8
|
+
*
|
9
|
+
* @default "" the empty string
|
10
|
+
*/
|
11
|
+
get value() {
|
12
|
+
return this.getAttribute("value") ?? "";
|
13
|
+
}
|
14
|
+
set value(value) {
|
15
|
+
this.setAttribute("value", value);
|
16
|
+
}
|
17
|
+
/**
|
18
|
+
* Copies the `text`.
|
19
|
+
* @param text The `text` to share
|
20
|
+
*/
|
21
|
+
async copy(text = this.value) {
|
22
|
+
await navigator.clipboard.writeText(text);
|
23
|
+
this.swapContent();
|
24
|
+
}
|
25
|
+
}
|
package/base/define.d.ts
CHANGED
@@ -1,2 +1 @@
|
|
1
|
-
|
2
|
-
export { }
|
1
|
+
export {};
|
package/base/define.js
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
import{
|
1
|
+
import { Base } from "./index.js";
|
2
|
+
customElements.define("drab-base", Base);
|
package/base/index.d.ts
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
*
|
11
11
|
* Each element can have multiple `trigger`s, but will only have one `content`.
|
12
12
|
*/
|
13
|
-
declare class Base extends HTMLElement {
|
13
|
+
export declare class Base extends HTMLElement {
|
14
14
|
#private;
|
15
15
|
constructor();
|
16
16
|
/**
|
@@ -65,5 +65,3 @@ declare class Base extends HTMLElement {
|
|
65
65
|
connectedCallback(): void;
|
66
66
|
disconnectedCallback(): void;
|
67
67
|
}
|
68
|
-
|
69
|
-
export { Base };
|
package/base/index.js
CHANGED
@@ -1 +1,108 @@
|
|
1
|
-
|
1
|
+
/**
|
2
|
+
* Each element in the library extends the `Base` class. It provides methods
|
3
|
+
* for selecting elements via HTML attributes along with other helpers.
|
4
|
+
*
|
5
|
+
* By default, `trigger`s and `content` will be selected via the `data-trigger` and
|
6
|
+
* `data-content` attributes. Alternatively, you can set the `trigger` or
|
7
|
+
* `content` attribute to a CSS selector to change the default selector from
|
8
|
+
* `[data-trigger]` or `[data-content]` to a selector of your choosing.
|
9
|
+
* This can be useful if you have multiple elements within one another.
|
10
|
+
*
|
11
|
+
* Each element can have multiple `trigger`s, but will only have one `content`.
|
12
|
+
*/
|
13
|
+
export class Base extends HTMLElement {
|
14
|
+
/**
|
15
|
+
* To clean up event listeners added to `document` when the element is removed.
|
16
|
+
*/
|
17
|
+
#listenerController = new AbortController();
|
18
|
+
constructor() {
|
19
|
+
super();
|
20
|
+
}
|
21
|
+
/**
|
22
|
+
* Event for the `trigger` to execute.
|
23
|
+
*
|
24
|
+
* For example, set to `"mouseover"` to execute the event when the user hovers the mouse over the `trigger`, instead of when they click it.
|
25
|
+
*
|
26
|
+
* @default "click"
|
27
|
+
*/
|
28
|
+
get event() {
|
29
|
+
return (this.getAttribute("event") ?? "click");
|
30
|
+
}
|
31
|
+
set event(value) {
|
32
|
+
this.setAttribute("event", value);
|
33
|
+
}
|
34
|
+
/**
|
35
|
+
* @returns All of the elements that match the `trigger` selector.
|
36
|
+
* @default this.querySelectorAll("[data-trigger]")
|
37
|
+
*/
|
38
|
+
getTrigger() {
|
39
|
+
const triggers = this.querySelectorAll(this.getAttribute("trigger") ?? "[data-trigger]");
|
40
|
+
return triggers;
|
41
|
+
}
|
42
|
+
/**
|
43
|
+
* @param instance The instance of the desired element, ex: `HTMLDialogElement`.
|
44
|
+
* Defaults to `HTMLElement`.
|
45
|
+
* @returns The element that matches the `content` selector.
|
46
|
+
* @default this.querySelector("[data-content]")
|
47
|
+
*/
|
48
|
+
getContent(instance = HTMLElement) {
|
49
|
+
const content = this.querySelector(this.getAttribute("content") ?? "[data-content]");
|
50
|
+
if (content instanceof instance)
|
51
|
+
return content;
|
52
|
+
throw new Error("Content not found");
|
53
|
+
}
|
54
|
+
/**
|
55
|
+
* Finds the `HTMLElement | HTMLTemplateElement` via the `swap` selector and
|
56
|
+
* swaps `this.content()` with the content of the element found.
|
57
|
+
*
|
58
|
+
* @param revert Swap back to old content
|
59
|
+
* @param delay Wait time before swapping back
|
60
|
+
*/
|
61
|
+
swapContent(revert = true, delay = 800) {
|
62
|
+
const swap = this.querySelector(this.getAttribute("swap") ?? "[data-swap]");
|
63
|
+
if (swap) {
|
64
|
+
const original = Array.from(this.getContent().childNodes);
|
65
|
+
if (swap instanceof HTMLTemplateElement) {
|
66
|
+
this.getContent().replaceChildren(swap.content.cloneNode(true));
|
67
|
+
}
|
68
|
+
else {
|
69
|
+
this.getContent().replaceChildren(...swap.childNodes);
|
70
|
+
}
|
71
|
+
if (revert) {
|
72
|
+
setTimeout(() => this.getContent().replaceChildren(...original), delay);
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|
76
|
+
/**
|
77
|
+
* Wrapper around `document.body.addEventListener` that ensures when the
|
78
|
+
* element is removed from the DOM, these event listeners are cleaned up.
|
79
|
+
* @param type
|
80
|
+
* @param listener
|
81
|
+
* @param options
|
82
|
+
*/
|
83
|
+
safeListener(type, listener, element = document.body, options = {}) {
|
84
|
+
options.signal = this.#listenerController.signal;
|
85
|
+
//@ts-ignore - inferred listener type not working...?
|
86
|
+
element.addEventListener(type, listener, options);
|
87
|
+
}
|
88
|
+
/**
|
89
|
+
* @param listener Listener to attach to all of the `trigger` elements.
|
90
|
+
*/
|
91
|
+
triggerListener(listener, type = this.event) {
|
92
|
+
for (const trigger of this.getTrigger()) {
|
93
|
+
trigger.addEventListener(type, listener);
|
94
|
+
}
|
95
|
+
}
|
96
|
+
/**
|
97
|
+
* Placeholder function is passed into `queueMicrotask` in `connectedCallback`. It is overridden in each component that needs to run `connectedCallback`.
|
98
|
+
*
|
99
|
+
* The reason for this is to make these elements work better with frameworks like Svelte. For SSR this isn't necessary, but when client side rendering, the HTML within the custom element isn't available before `connectedCallback` is called. By waiting until the next microtask, the HTML content is available---then for example, listeners can be attached to elements inside.
|
100
|
+
*/
|
101
|
+
mount() { }
|
102
|
+
connectedCallback() {
|
103
|
+
queueMicrotask(() => this.mount());
|
104
|
+
}
|
105
|
+
disconnectedCallback() {
|
106
|
+
this.#listenerController.abort();
|
107
|
+
}
|
108
|
+
}
|
package/breakpoint/define.d.ts
CHANGED
@@ -1,2 +1 @@
|
|
1
|
-
|
2
|
-
export { }
|
1
|
+
export {};
|
package/breakpoint/define.js
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
import{
|
1
|
+
import { Breakpoint } from "./index.js";
|
2
|
+
customElements.define("drab-breakpoint", Breakpoint);
|
package/breakpoint/index.d.ts
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
import { Base } from
|
2
|
-
|
1
|
+
import { Base } from "../base/index.js";
|
2
|
+
import type { Attributes } from "../types/index.js";
|
3
|
+
export type BreakpointAttributes = Attributes<Breakpoint> & Partial<{
|
4
|
+
[attr: `breakpoint-${string}`]: string;
|
5
|
+
}>;
|
3
6
|
type Breakpoints = {
|
4
7
|
name: string;
|
5
8
|
width: number;
|
@@ -13,12 +16,11 @@ type Breakpoints = {
|
|
13
16
|
* <drab-breakpoint breakpoint-name="400">
|
14
17
|
* ```
|
15
18
|
*/
|
16
|
-
declare class Breakpoint extends Base {
|
19
|
+
export declare class Breakpoint extends Base {
|
17
20
|
breakpoints: Breakpoints;
|
18
21
|
constructor();
|
19
22
|
/** finds the current breakpoint */
|
20
23
|
get breakpoint(): string;
|
21
24
|
mount(): void;
|
22
25
|
}
|
23
|
-
|
24
|
-
export { Breakpoint, type Breakpoints };
|
26
|
+
export {};
|
package/breakpoint/index.js
CHANGED
@@ -1 +1,55 @@
|
|
1
|
-
import{
|
1
|
+
import { Base } from "../base/index.js";
|
2
|
+
/**
|
3
|
+
* Displays the current breakpoint and `window.innerWidth`, based on the `breakpoints` provided. Defaults to [TailwindCSS breakpoint sizes](https://tailwindcss.com/docs/responsive-design).
|
4
|
+
*
|
5
|
+
* Provide alternate breakpoints by specifying `breakpoint` attributes:
|
6
|
+
*
|
7
|
+
* ```html
|
8
|
+
* <drab-breakpoint breakpoint-name="400">
|
9
|
+
* ```
|
10
|
+
*/
|
11
|
+
export class Breakpoint extends Base {
|
12
|
+
breakpoints = [
|
13
|
+
{ name: "sm", width: 640 },
|
14
|
+
{ name: "md", width: 768 },
|
15
|
+
{ name: "lg", width: 1024 },
|
16
|
+
{ name: "xl", width: 1280 },
|
17
|
+
{ name: "2xl", width: 1536 },
|
18
|
+
];
|
19
|
+
constructor() {
|
20
|
+
super();
|
21
|
+
const custom = [];
|
22
|
+
for (const attributeName of this.getAttributeNames()) {
|
23
|
+
if (attributeName.startsWith("breakpoint-")) {
|
24
|
+
const [, ...name] = attributeName.split("-");
|
25
|
+
if (name) {
|
26
|
+
custom.push({
|
27
|
+
name: name.join("-"),
|
28
|
+
width: Number(this.getAttribute(attributeName)),
|
29
|
+
});
|
30
|
+
}
|
31
|
+
}
|
32
|
+
}
|
33
|
+
if (custom.length)
|
34
|
+
this.breakpoints = custom;
|
35
|
+
// highest to lowest
|
36
|
+
this.breakpoints.sort((a, b) => b.width - a.width);
|
37
|
+
}
|
38
|
+
/** finds the current breakpoint */
|
39
|
+
get breakpoint() {
|
40
|
+
for (let i = 0; i < this.breakpoints.length; i++) {
|
41
|
+
const breakpoint = this.breakpoints[i];
|
42
|
+
if (breakpoint) {
|
43
|
+
if (window.innerWidth > breakpoint.width) {
|
44
|
+
return breakpoint.name;
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
return "none";
|
49
|
+
}
|
50
|
+
mount() {
|
51
|
+
const render = () => (this.getContent().innerHTML = `${this.breakpoint}:${window.innerWidth}`);
|
52
|
+
render();
|
53
|
+
this.safeListener("resize", render, window);
|
54
|
+
}
|
55
|
+
}
|
package/contextmenu/define.d.ts
CHANGED
@@ -1,2 +1 @@
|
|
1
|
-
|
2
|
-
export { }
|
1
|
+
export {};
|
@@ -1 +1 @@
|
|
1
|
-
"use strict";(()=>{var c=class extends HTMLElement{#t=new AbortController;constructor(){super()}get event(){return this.getAttribute("event")??"click"}set event(t){this.setAttribute("event",t)}getTrigger(){return this.querySelectorAll(this.getAttribute("trigger")??"[data-trigger]")}getContent(t=HTMLElement){let e=this.querySelector(this.getAttribute("content")??"[data-content]");if(e instanceof t)return e;throw new Error("Content not found")}swapContent(t=!0,e=800){let n=this.querySelector(this.getAttribute("swap")??"[data-swap]");if(n){let
|
1
|
+
"use strict";(()=>{var c=class extends HTMLElement{#t=new AbortController;constructor(){super()}get event(){return this.getAttribute("event")??"click"}set event(t){this.setAttribute("event",t)}getTrigger(){return this.querySelectorAll(this.getAttribute("trigger")??"[data-trigger]")}getContent(t=HTMLElement){let e=this.querySelector(this.getAttribute("content")??"[data-content]");if(e instanceof t)return e;throw new Error("Content not found")}swapContent(t=!0,e=800){let n=this.querySelector(this.getAttribute("swap")??"[data-swap]");if(n){let i=Array.from(this.getContent().childNodes);n instanceof HTMLTemplateElement?this.getContent().replaceChildren(n.content.cloneNode(!0)):this.getContent().replaceChildren(...n.childNodes),t&&setTimeout(()=>this.getContent().replaceChildren(...i),e)}}safeListener(t,e,n=document.body,i={}){i.signal=this.#t.signal,n.addEventListener(t,e,i)}triggerListener(t,e=this.event){for(let n of this.getTrigger())n.addEventListener(e,t)}mount(){}connectedCallback(){queueMicrotask(()=>this.mount())}disconnectedCallback(){this.#t.abort()}};var m=class extends c{constructor(){super()}get animationOptions(){let t={};for(let e of this.getAttributeNames())if(e.startsWith("animation-option-")){let n=this.getAttribute(e),[,,i]=e.split("-");n&&(i==="duration"||i==="delay"?t[i]=Number(n):i==="easing"&&(t[i]=n))}return t}async animateElement(t={element:this.getContent(),options:{}}){let{element:e=this.getContent(),options:n={}}=t,i=this.keyframes;if(i.length&&!window.matchMedia("(prefers-reduced-motion: reduce)").matches){n=Object.assign(this.animationOptions,n),n.duration||(n.duration=200),n.easing||(n.easing="ease-in-out");let r=i.at(0),s=i.at(-1);if(r&&s){let a=["composite","easing","offset"];for(let l of a)delete r[l],delete s[l]}n.direction?.includes("reverse")&&([r,s]=[s,r]),Object.assign(e.style,r),await e.animate(i,n).finished,Object.assign(e.style,s)}}get keyframes(){let t=[];for(let e of this.getAttributeNames()){let n=this.getAttribute(e),[,,i,...r]=e.split("-");if(e.startsWith("animation-keyframe-")){let s=r.map((o,a)=>a<1?o:o.at(0)?.toUpperCase()+o.slice(1)).join("");if(i&&s){i==="from"?i="0":i==="to"?i="1":i=String(parseInt(i)*.01);let o=Number(i),a=t.find(l=>l.offset===o);a?a[s]=n:t.push({[s]:n,offset:o})}}}return t.sort((e,n)=>Number(e.offset)-Number(n.offset)),t}};var f=class extends m{#t;constructor(){super()}set#e(t){this.getContent().style.left=`${t.x}px`,this.getContent().style.top=`${t.y}px`}async show(t){let e=window.scrollY,n=window.scrollX,i=t instanceof MouseEvent?t.clientX:t.touches[0]?.clientX??0,r=t instanceof MouseEvent?t.clientY:t.touches[0]?.clientY??0,s=i+n,o=r+e;this.getContent().style.position="absolute",this.getContent().style.display="block";let a=this.getContent().offsetWidth+24,l=this.getContent().offsetHeight+6,u=window.innerWidth,d=window.innerHeight;s+a>n+u&&(s=n+u-a),o+l>e+d&&(o=e+d-l),this.#e={x:s,y:o},await this.animateElement()}async hide(){this.getContent().style.display!=="none"&&(await this.animateElement({options:{direction:"reverse"}}),this.getContent().style.display="none")}mount(){this.triggerListener(e=>{e.preventDefault(),this.show(e)},"contextmenu"),this.safeListener("click",()=>this.hide()),this.triggerListener(e=>{this.#t=setTimeout(()=>{this.show(e)},800)},"touchstart");let t=()=>clearTimeout(this.#t);this.triggerListener(t,"touchend"),this.triggerListener(t,"touchcancel"),this.safeListener("keydown",e=>{e.key==="Escape"&&this.hide()})}};customElements.define("drab-contextmenu",f);})();
|
package/contextmenu/define.js
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
import{
|
1
|
+
import { ContextMenu } from "./index.js";
|
2
|
+
customElements.define("drab-contextmenu", ContextMenu);
|
package/contextmenu/index.d.ts
CHANGED
@@ -1,15 +1,13 @@
|
|
1
|
-
import { Animate } from
|
2
|
-
import
|
3
|
-
|
1
|
+
import { Animate, type AnimateAttributes } from "../animate/index.js";
|
2
|
+
import type { Attributes } from "../types/index.js";
|
3
|
+
export type ContextMenuAttributes = Attributes<ContextMenu> & AnimateAttributes;
|
4
4
|
/**
|
5
5
|
* Displays content when the `trigger` element is right clicked, or long pressed on mobile.
|
6
6
|
*/
|
7
|
-
declare class ContextMenu extends Animate {
|
7
|
+
export declare class ContextMenu extends Animate {
|
8
8
|
#private;
|
9
9
|
constructor();
|
10
10
|
show(e: MouseEvent | TouchEvent): Promise<void>;
|
11
11
|
hide(): Promise<void>;
|
12
12
|
mount(): void;
|
13
13
|
}
|
14
|
-
|
15
|
-
export { ContextMenu };
|
@@ -1 +1 @@
|
|
1
|
-
"use strict";(()=>{var c=class extends HTMLElement{#t=new AbortController;constructor(){super()}get event(){return this.getAttribute("event")??"click"}set event(t){this.setAttribute("event",t)}getTrigger(){return this.querySelectorAll(this.getAttribute("trigger")??"[data-trigger]")}getContent(t=HTMLElement){let e=this.querySelector(this.getAttribute("content")??"[data-content]");if(e instanceof t)return e;throw new Error("Content not found")}swapContent(t=!0,e=800){let n=this.querySelector(this.getAttribute("swap")??"[data-swap]");if(n){let
|
1
|
+
"use strict";(()=>{var c=class extends HTMLElement{#t=new AbortController;constructor(){super()}get event(){return this.getAttribute("event")??"click"}set event(t){this.setAttribute("event",t)}getTrigger(){return this.querySelectorAll(this.getAttribute("trigger")??"[data-trigger]")}getContent(t=HTMLElement){let e=this.querySelector(this.getAttribute("content")??"[data-content]");if(e instanceof t)return e;throw new Error("Content not found")}swapContent(t=!0,e=800){let n=this.querySelector(this.getAttribute("swap")??"[data-swap]");if(n){let i=Array.from(this.getContent().childNodes);n instanceof HTMLTemplateElement?this.getContent().replaceChildren(n.content.cloneNode(!0)):this.getContent().replaceChildren(...n.childNodes),t&&setTimeout(()=>this.getContent().replaceChildren(...i),e)}}safeListener(t,e,n=document.body,i={}){i.signal=this.#t.signal,n.addEventListener(t,e,i)}triggerListener(t,e=this.event){for(let n of this.getTrigger())n.addEventListener(e,t)}mount(){}connectedCallback(){queueMicrotask(()=>this.mount())}disconnectedCallback(){this.#t.abort()}};var m=class extends c{constructor(){super()}get animationOptions(){let t={};for(let e of this.getAttributeNames())if(e.startsWith("animation-option-")){let n=this.getAttribute(e),[,,i]=e.split("-");n&&(i==="duration"||i==="delay"?t[i]=Number(n):i==="easing"&&(t[i]=n))}return t}async animateElement(t={element:this.getContent(),options:{}}){let{element:e=this.getContent(),options:n={}}=t,i=this.keyframes;if(i.length&&!window.matchMedia("(prefers-reduced-motion: reduce)").matches){n=Object.assign(this.animationOptions,n),n.duration||(n.duration=200),n.easing||(n.easing="ease-in-out");let r=i.at(0),s=i.at(-1);if(r&&s){let a=["composite","easing","offset"];for(let l of a)delete r[l],delete s[l]}n.direction?.includes("reverse")&&([r,s]=[s,r]),Object.assign(e.style,r),await e.animate(i,n).finished,Object.assign(e.style,s)}}get keyframes(){let t=[];for(let e of this.getAttributeNames()){let n=this.getAttribute(e),[,,i,...r]=e.split("-");if(e.startsWith("animation-keyframe-")){let s=r.map((o,a)=>a<1?o:o.at(0)?.toUpperCase()+o.slice(1)).join("");if(i&&s){i==="from"?i="0":i==="to"?i="1":i=String(parseInt(i)*.01);let o=Number(i),a=t.find(l=>l.offset===o);a?a[s]=n:t.push({[s]:n,offset:o})}}}return t.sort((e,n)=>Number(e.offset)-Number(n.offset)),t}};var d=class extends m{#t;constructor(){super()}set#e(t){this.getContent().style.left=`${t.x}px`,this.getContent().style.top=`${t.y}px`}async show(t){let e=window.scrollY,n=window.scrollX,i=t instanceof MouseEvent?t.clientX:t.touches[0]?.clientX??0,r=t instanceof MouseEvent?t.clientY:t.touches[0]?.clientY??0,s=i+n,o=r+e;this.getContent().style.position="absolute",this.getContent().style.display="block";let a=this.getContent().offsetWidth+24,l=this.getContent().offsetHeight+6,h=window.innerWidth,u=window.innerHeight;s+a>n+h&&(s=n+h-a),o+l>e+u&&(o=e+u-l),this.#e={x:s,y:o},await this.animateElement()}async hide(){this.getContent().style.display!=="none"&&(await this.animateElement({options:{direction:"reverse"}}),this.getContent().style.display="none")}mount(){this.triggerListener(e=>{e.preventDefault(),this.show(e)},"contextmenu"),this.safeListener("click",()=>this.hide()),this.triggerListener(e=>{this.#t=setTimeout(()=>{this.show(e)},800)},"touchstart");let t=()=>clearTimeout(this.#t);this.triggerListener(t,"touchend"),this.triggerListener(t,"touchcancel"),this.safeListener("keydown",e=>{e.key==="Escape"&&this.hide()})}};})();
|
package/contextmenu/index.js
CHANGED
@@ -1 +1,71 @@
|
|
1
|
-
import{
|
1
|
+
import { Animate } from "../animate/index.js";
|
2
|
+
/**
|
3
|
+
* Displays content when the `trigger` element is right clicked, or long pressed on mobile.
|
4
|
+
*/
|
5
|
+
export class ContextMenu extends Animate {
|
6
|
+
/** Tracks the long press duration on mobile. */
|
7
|
+
#touchTimer;
|
8
|
+
constructor() {
|
9
|
+
super();
|
10
|
+
}
|
11
|
+
/** Sets the context menu's `style.left` and `style.top` position. */
|
12
|
+
set #coordinates(value) {
|
13
|
+
this.getContent().style.left = `${value.x}px`;
|
14
|
+
this.getContent().style.top = `${value.y}px`;
|
15
|
+
}
|
16
|
+
async show(e) {
|
17
|
+
// find coordinates of the click
|
18
|
+
const scrollY = window.scrollY;
|
19
|
+
const scrollX = window.scrollX;
|
20
|
+
const clientX = e instanceof MouseEvent ? e.clientX : e.touches[0]?.clientX ?? 0;
|
21
|
+
const clientY = e instanceof MouseEvent ? e.clientY : e.touches[0]?.clientY ?? 0;
|
22
|
+
let x = clientX + scrollX;
|
23
|
+
let y = clientY + scrollY;
|
24
|
+
this.getContent().style.position = "absolute";
|
25
|
+
this.getContent().style.display = "block";
|
26
|
+
const offsetWidth = this.getContent().offsetWidth + 24;
|
27
|
+
const offsetHeight = this.getContent().offsetHeight + 6;
|
28
|
+
const innerWidth = window.innerWidth;
|
29
|
+
const innerHeight = window.innerHeight;
|
30
|
+
// ensure menu is within view
|
31
|
+
if (x + offsetWidth > scrollX + innerWidth) {
|
32
|
+
x = scrollX + innerWidth - offsetWidth;
|
33
|
+
}
|
34
|
+
if (y + offsetHeight > scrollY + innerHeight) {
|
35
|
+
y = scrollY + innerHeight - offsetHeight;
|
36
|
+
}
|
37
|
+
this.#coordinates = { x, y };
|
38
|
+
await this.animateElement();
|
39
|
+
}
|
40
|
+
async hide() {
|
41
|
+
if (this.getContent().style.display !== "none") {
|
42
|
+
await this.animateElement({
|
43
|
+
options: { direction: "reverse" },
|
44
|
+
});
|
45
|
+
this.getContent().style.display = "none";
|
46
|
+
}
|
47
|
+
}
|
48
|
+
mount() {
|
49
|
+
// mouse
|
50
|
+
this.triggerListener((e) => {
|
51
|
+
e.preventDefault();
|
52
|
+
this.show(e);
|
53
|
+
}, "contextmenu");
|
54
|
+
this.safeListener("click", () => this.hide());
|
55
|
+
// touch
|
56
|
+
this.triggerListener((e) => {
|
57
|
+
this.#touchTimer = setTimeout(() => {
|
58
|
+
this.show(e);
|
59
|
+
}, 800);
|
60
|
+
}, "touchstart");
|
61
|
+
const resetTimer = () => clearTimeout(this.#touchTimer);
|
62
|
+
this.triggerListener(resetTimer, "touchend");
|
63
|
+
this.triggerListener(resetTimer, "touchcancel");
|
64
|
+
// keyboard
|
65
|
+
this.safeListener("keydown", (e) => {
|
66
|
+
if (e.key === "Escape") {
|
67
|
+
this.hide();
|
68
|
+
}
|
69
|
+
});
|
70
|
+
}
|
71
|
+
}
|
package/copy/define.d.ts
CHANGED
@@ -1,2 +1 @@
|
|
1
|
-
|
2
|
-
export { }
|
1
|
+
export {};
|