overflowed 0.0.0-experimental-20221120013127 → 0.0.0-experimental-20260129225612

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.
@@ -0,0 +1,15 @@
1
+ :host {
2
+ visibility: hidden;
3
+ max-inline-size: 100%;
4
+ max-block-size: 100%;
5
+ }
6
+
7
+ :host([box-sizing="content-box"]) {
8
+ inline-size: var(--content-box-inline-size);
9
+ block-size: var(--content-box-block-size);
10
+ }
11
+
12
+ :host([box-sizing="border-box"]) {
13
+ inline-size: var(--border-box-inline-size);
14
+ block-size: var(--border-box-block-size);
15
+ }
@@ -0,0 +1,99 @@
1
+ import styles from "./HTMLBoxMirror.css?raw" with { type: "text" };
2
+
3
+ export class HTMLBoxMirror extends HTMLElement {
4
+ private readonly rootStyleSheet = new CSSStyleSheet();
5
+ private readonly variableStyleSheet = new CSSStyleSheet();
6
+
7
+ private readonly resizeObserver = new ResizeObserver(this.handleResize.bind(this));
8
+
9
+ public connectedCallback() {
10
+ this.rootStyleSheet.replaceSync(styles);
11
+
12
+ const shadowRoot = this.attachShadow({ mode: "open" });
13
+ shadowRoot.adoptedStyleSheets = [this.rootStyleSheet, this.variableStyleSheet];
14
+
15
+ shadowRoot.appendChild(this.ownerDocument.createElement("slot"));
16
+ }
17
+
18
+ public disconnectedCallback() {
19
+ this.resizeObserver.disconnect();
20
+ }
21
+
22
+ private handleResize(entries: ResizeObserverEntry[]) {
23
+ const [entry, ...remainingEntries] = entries;
24
+ if (!entry) throw 1;
25
+ if (remainingEntries.length > 0) throw 1;
26
+
27
+ const [borderBox, ...borderBoxRest] = entry.borderBoxSize;
28
+ if (!borderBox || borderBoxRest.length > 0) throw 1;
29
+
30
+ const [contentBox, ...contentBoxRest] = entry.contentBoxSize;
31
+ if (!contentBox || contentBoxRest.length > 0) throw 1;
32
+
33
+ if (
34
+ this.behavior === "ignore-hidden" &&
35
+ entry.target instanceof HTMLElement &&
36
+ entry.target.hidden &&
37
+ borderBox.blockSize === 0 && borderBox.inlineSize === 0 &&
38
+ contentBox.blockSize === 0 && contentBox.inlineSize === 0
39
+ ) {
40
+ return;
41
+ }
42
+
43
+ this.variableStyleSheet.replaceSync(`
44
+ :host {
45
+ --border-box-block-size: ${borderBox.blockSize}px;
46
+ --border-box-inline-size: ${borderBox.inlineSize}px;
47
+ --content-box-block-size: ${contentBox.blockSize}px;
48
+ --content-box-inline-size: ${contentBox.inlineSize}px;
49
+ }
50
+ `);
51
+ }
52
+
53
+ #target?: Element | null;
54
+ get target() {
55
+ return this.#target ?? null;
56
+ }
57
+ set target(value) {
58
+ this.resizeObserver.disconnect();
59
+ if (value) this.resizeObserver.observe(value);
60
+
61
+ this.#target = value;
62
+ }
63
+
64
+ static #BOX_SIZING_ATTRIBUTE = "box-sizing" as const;
65
+ get boxSizing() {
66
+ return this.getAttribute(HTMLBoxMirror.#BOX_SIZING_ATTRIBUTE) as "border-box" | "content-box" | null;
67
+ }
68
+ set boxSizing(value) {
69
+ if (value) {
70
+ this.setAttribute(HTMLBoxMirror.#BOX_SIZING_ATTRIBUTE, value);
71
+ } else {
72
+ this.removeAttribute(HTMLBoxMirror.#BOX_SIZING_ATTRIBUTE);
73
+ }
74
+ }
75
+
76
+ static #BEHAVIOR_ATTRIBUTE = "behavior" as const;
77
+ get behavior() {
78
+ return this.getAttribute(HTMLBoxMirror.#BEHAVIOR_ATTRIBUTE) as "ignore-hidden" | null;
79
+ }
80
+ set behavior(value) {
81
+ if (value) {
82
+ this.setAttribute(HTMLBoxMirror.#BEHAVIOR_ATTRIBUTE, value);
83
+ } else {
84
+ this.removeAttribute(HTMLBoxMirror.#BEHAVIOR_ATTRIBUTE);
85
+ }
86
+ }
87
+
88
+ static isHTMLBoxMirror(value: unknown) {
89
+ return value instanceof HTMLBoxMirror;
90
+ }
91
+ }
92
+
93
+ declare global {
94
+ interface HTMLElementTagNameMap {
95
+ "box-mirror": HTMLBoxMirror;
96
+ }
97
+ }
98
+
99
+ globalThis.customElements.define("box-mirror", HTMLBoxMirror);
@@ -0,0 +1,3 @@
1
+ :host {
2
+ display: contents;
3
+ }
@@ -0,0 +1,43 @@
1
+ import styles from "./HTMLContentMirror.css?raw" with { type: "text" };
2
+
3
+ export class HTMLContentMirror extends HTMLElement {
4
+ private readonly styleSheet = new CSSStyleSheet();
5
+
6
+ readonly #mutationObserver = new MutationObserver(this.#cloneTargetToShadowRoot.bind(this));
7
+
8
+ connectedCallback() {
9
+ this.styleSheet.replaceSync(styles);
10
+
11
+ const shadowRoot = this.attachShadow({ mode: "open" });
12
+ shadowRoot.adoptedStyleSheets = [this.styleSheet];
13
+ }
14
+
15
+ #target?: Element;
16
+ get target() {
17
+ return this.#target;
18
+ }
19
+ set target(value: Element | undefined) {
20
+ this.#target = value;
21
+
22
+ this.#mutationObserver.disconnect();
23
+ if (value) {
24
+ this.#mutationObserver.observe(value, {
25
+ childList: true,
26
+ attributes: true,
27
+ characterData: true,
28
+ subtree: true,
29
+ });
30
+ }
31
+
32
+ this.#cloneTargetToShadowRoot();
33
+ }
34
+
35
+ #cloneTargetToShadowRoot() {
36
+ if (!this.shadowRoot) throw 0;
37
+ if (!this.#target) throw 1;
38
+
39
+ this.shadowRoot.replaceChildren(this.#target.cloneNode(true));
40
+ }
41
+ }
42
+
43
+ globalThis.customElements.define("content-mirror", HTMLContentMirror);
package/dist/mod.d.ts ADDED
@@ -0,0 +1,54 @@
1
+ //#region box-mirror/HTMLBoxMirror.d.ts
2
+ declare class HTMLBoxMirror extends HTMLElement {
3
+ #private;
4
+ private readonly rootStyleSheet;
5
+ private readonly variableStyleSheet;
6
+ private readonly resizeObserver;
7
+ connectedCallback(): void;
8
+ disconnectedCallback(): void;
9
+ private handleResize;
10
+ get target(): Element;
11
+ set target(value: Element);
12
+ get boxSizing(): "border-box" | "content-box" | null;
13
+ set boxSizing(value: "border-box" | "content-box" | null);
14
+ get behavior(): "ignore-hidden" | null;
15
+ set behavior(value: "ignore-hidden" | null);
16
+ static isHTMLBoxMirror(value: unknown): value is HTMLBoxMirror;
17
+ }
18
+ declare global {
19
+ interface HTMLElementTagNameMap {
20
+ "box-mirror": HTMLBoxMirror;
21
+ }
22
+ } //# sourceMappingURL=HTMLBoxMirror.d.ts.map
23
+ //#endregion
24
+ //#region overflow-container/HTMLOverflowContainerElement.d.ts
25
+ declare class HTMLOverflowContainerElement extends HTMLElement {
26
+ private readonly rootStyleSheet;
27
+ private readonly variableStyleSheet;
28
+ private readonly overflowStyleSheet;
29
+ private readonly childrenSlot;
30
+ private readonly overflowSlot;
31
+ private readonly containerMirror;
32
+ private readonly overflowResizeObserver;
33
+ private readonly elementIsIntersectingMap;
34
+ private readonly intersectionObserver;
35
+ constructor();
36
+ connectedCallback(): void;
37
+ disconnectedCallback(): void;
38
+ private registerItemElement;
39
+ private registerOverflowElement;
40
+ private handleIntersection;
41
+ private updateInlineGap;
42
+ private handleResize;
43
+ private getItemElements;
44
+ private getOverflowElements;
45
+ private static isHTMLElement;
46
+ }
47
+ declare global {
48
+ interface HTMLElementTagNameMap {
49
+ "overflow-container": HTMLOverflowContainerElement;
50
+ }
51
+ } //# sourceMappingURL=HTMLOverflowContainerElement.d.ts.map
52
+ //#endregion
53
+ export { HTMLBoxMirror, HTMLOverflowContainerElement };
54
+ //# sourceMappingURL=mod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.d.ts","names":[],"sources":["../box-mirror/HTMLBoxMirror.ts","../overflow-container/HTMLOverflowContainerElement.ts"],"mappings":";cAEa,aAAA,SAAsB,WAAA;EAAA;mBACjB,cAAA;EAAA,iBACA,kBAAA;EAAA,iBAEA,cAAA;EAEV,iBAAA,CAAA;EASA,oBAAA,CAAA;EAAA,QAIC,YAAA;EAAA,IAgCJ,MAAA,CAAA,GAAM,OAAA;EAAA,IAGN,MAAA,CAAO,KAAA,EAAK,OAAA;EAAA,IAQZ,SAAA,CAAA;EAAA,IAGA,SAAA,CAAU,KAAA;EAAA,IASV,QAAA,CAAA;EAAA,IAGA,QAAA,CAAS,KAAA;EAAA,OAQN,eAAA,CAAgB,KAAA,YAAc,KAAA,IAAA,aAAA;AAAA;AAAA,QAK9B,MAAA;EAAA,UACG,qBAAA;IACT,YAAA,EAAc,aAAA;EAAA;AAAA;;;cCzFH,4BAAA,SAAqC,WAAA;EAAA,iBAChC,cAAA;EAAA,iBACA,kBAAA;EAAA,iBACA,kBAAA;EAAA,iBAEA,YAAA;EAAA,iBACA,YAAA;EAAA,iBAEA,eAAA;EAAA,iBAEA,sBAAA;EAAA,iBAEA,wBAAA;EAAA,iBACA,oBAAA;;EAuBjB,iBAAA,CAAA;EAaO,oBAAA,CAAA;EAAA,QAKC,mBAAA;EAAA,QAaA,uBAAA;EAAA,QAYA,kBAAA;EAAA,QAgCA,eAAA;EAAA,QAYA,YAAA;EAAA,QAmBA,eAAA;EAAA,QAMA,mBAAA;EAAA,eAMO,aAAA;AAAA;AAAA,QAKR,MAAA;EAAA,UACG,qBAAA;IACT,oBAAA,EAAsB,4BAAA;EAAA;AAAA"}
package/dist/mod.js ADDED
@@ -0,0 +1,189 @@
1
+ //#region box-mirror/HTMLBoxMirror.css?raw
2
+ var HTMLBoxMirror_default = ":host {\n visibility: hidden;\n max-inline-size: 100%;\n max-block-size: 100%;\n}\n\n:host([box-sizing=\"content-box\"]) {\n inline-size: var(--content-box-inline-size);\n block-size: var(--content-box-block-size);\n}\n\n:host([box-sizing=\"border-box\"]) {\n inline-size: var(--border-box-inline-size);\n block-size: var(--border-box-block-size);\n}\n";
3
+
4
+ //#endregion
5
+ //#region box-mirror/HTMLBoxMirror.ts
6
+ var HTMLBoxMirror = class HTMLBoxMirror extends HTMLElement {
7
+ constructor(..._args) {
8
+ super(..._args);
9
+ this.rootStyleSheet = new CSSStyleSheet();
10
+ this.variableStyleSheet = new CSSStyleSheet();
11
+ this.resizeObserver = new ResizeObserver(this.handleResize.bind(this));
12
+ }
13
+ connectedCallback() {
14
+ this.rootStyleSheet.replaceSync(HTMLBoxMirror_default);
15
+ const shadowRoot = this.attachShadow({ mode: "open" });
16
+ shadowRoot.adoptedStyleSheets = [this.rootStyleSheet, this.variableStyleSheet];
17
+ shadowRoot.appendChild(this.ownerDocument.createElement("slot"));
18
+ }
19
+ disconnectedCallback() {
20
+ this.resizeObserver.disconnect();
21
+ }
22
+ handleResize(entries) {
23
+ const [entry, ...remainingEntries] = entries;
24
+ if (!entry) throw 1;
25
+ if (remainingEntries.length > 0) throw 1;
26
+ const [borderBox, ...borderBoxRest] = entry.borderBoxSize;
27
+ if (!borderBox || borderBoxRest.length > 0) throw 1;
28
+ const [contentBox, ...contentBoxRest] = entry.contentBoxSize;
29
+ if (!contentBox || contentBoxRest.length > 0) throw 1;
30
+ if (this.behavior === "ignore-hidden" && entry.target instanceof HTMLElement && entry.target.hidden && borderBox.blockSize === 0 && borderBox.inlineSize === 0 && contentBox.blockSize === 0 && contentBox.inlineSize === 0) return;
31
+ this.variableStyleSheet.replaceSync(`
32
+ :host {
33
+ --border-box-block-size: ${borderBox.blockSize}px;
34
+ --border-box-inline-size: ${borderBox.inlineSize}px;
35
+ --content-box-block-size: ${contentBox.blockSize}px;
36
+ --content-box-inline-size: ${contentBox.inlineSize}px;
37
+ }
38
+ `);
39
+ }
40
+ #target;
41
+ get target() {
42
+ return this.#target ?? null;
43
+ }
44
+ set target(value) {
45
+ this.resizeObserver.disconnect();
46
+ if (value) this.resizeObserver.observe(value);
47
+ this.#target = value;
48
+ }
49
+ static #BOX_SIZING_ATTRIBUTE = "box-sizing";
50
+ get boxSizing() {
51
+ return this.getAttribute(HTMLBoxMirror.#BOX_SIZING_ATTRIBUTE);
52
+ }
53
+ set boxSizing(value) {
54
+ if (value) this.setAttribute(HTMLBoxMirror.#BOX_SIZING_ATTRIBUTE, value);
55
+ else this.removeAttribute(HTMLBoxMirror.#BOX_SIZING_ATTRIBUTE);
56
+ }
57
+ static #BEHAVIOR_ATTRIBUTE = "behavior";
58
+ get behavior() {
59
+ return this.getAttribute(HTMLBoxMirror.#BEHAVIOR_ATTRIBUTE);
60
+ }
61
+ set behavior(value) {
62
+ if (value) this.setAttribute(HTMLBoxMirror.#BEHAVIOR_ATTRIBUTE, value);
63
+ else this.removeAttribute(HTMLBoxMirror.#BEHAVIOR_ATTRIBUTE);
64
+ }
65
+ static isHTMLBoxMirror(value) {
66
+ return value instanceof HTMLBoxMirror;
67
+ }
68
+ };
69
+ globalThis.customElements.define("box-mirror", HTMLBoxMirror);
70
+
71
+ //#endregion
72
+ //#region overflow-container/HTMLOverflowContainerElement.css?raw
73
+ var HTMLOverflowContainerElement_default = ":host {\n display: flex;\n position: relative;\n box-sizing: border-box;\n overflow: clip;\n\n & > *::slotted(*) {\n flex-shrink: 0;\n }\n\n & > slot[name=\"overflow\"]::slotted([hidden]) {\n display: block;\n visibility: hidden;\n position: absolute;\n }\n}\n\n#container-mirror {\n position: absolute;\n display: flex;\n flex-direction: inherit;\n gap: inherit;\n box-sizing: border-box;\n\n & > * {\n flex-shrink: 0;\n }\n\n & > *:last-of-type {\n position: relative;\n inset-inline-end: clamp(\n 0%,\n calc(var(--overflow-inline-size) + var(--inline-gap)),\n calc(100% - var(--border-box-inline-size))\n );\n inset-block-end: clamp(\n 0%,\n calc(var(--overflow-block-size) + var(--block-gap)),\n calc(100% - var(--border-box-block-size))\n );\n }\n}\n";
74
+
75
+ //#endregion
76
+ //#region overflow-container/HTMLOverflowContainerElement.ts
77
+ var HTMLOverflowContainerElement = class HTMLOverflowContainerElement extends HTMLElement {
78
+ constructor() {
79
+ super();
80
+ this.rootStyleSheet = new CSSStyleSheet();
81
+ this.variableStyleSheet = new CSSStyleSheet();
82
+ this.overflowStyleSheet = new CSSStyleSheet();
83
+ this.childrenSlot = this.ownerDocument.createElement("slot");
84
+ this.overflowSlot = this.ownerDocument.createElement("slot");
85
+ this.containerMirror = this.ownerDocument.createElement("box-mirror");
86
+ this.overflowResizeObserver = new ResizeObserver(this.handleResize.bind(this));
87
+ this.elementIsIntersectingMap = /* @__PURE__ */ new WeakMap();
88
+ this.intersectionObserver = new IntersectionObserver(this.handleIntersection.bind(this), {
89
+ root: this.containerMirror,
90
+ threshold: 1
91
+ });
92
+ this.overflowSlot.name = "overflow";
93
+ this.containerMirror.id = "container-mirror";
94
+ this.containerMirror.boxSizing = "content-box";
95
+ this.containerMirror.target = this;
96
+ const shadowRoot = this.attachShadow({ mode: "open" });
97
+ shadowRoot.adoptedStyleSheets = [
98
+ this.rootStyleSheet,
99
+ this.variableStyleSheet,
100
+ this.overflowStyleSheet
101
+ ];
102
+ shadowRoot.appendChild(this.childrenSlot);
103
+ shadowRoot.appendChild(this.overflowSlot);
104
+ shadowRoot.appendChild(this.containerMirror);
105
+ }
106
+ connectedCallback() {
107
+ this.rootStyleSheet.replaceSync(HTMLOverflowContainerElement_default);
108
+ this.updateInlineGap();
109
+ for (const overflowElement of this.getOverflowElements()) this.registerOverflowElement(overflowElement);
110
+ for (const itemElement of this.getItemElements()) this.registerItemElement(itemElement);
111
+ }
112
+ disconnectedCallback() {
113
+ this.intersectionObserver.disconnect();
114
+ this.overflowResizeObserver.disconnect();
115
+ }
116
+ registerItemElement(element) {
117
+ if (!this.contains(element)) throw new Error("Container must contain item element", { cause: element });
118
+ const mirrorElement = globalThis.document.createElement("box-mirror");
119
+ mirrorElement.boxSizing = "border-box";
120
+ mirrorElement.behavior = "ignore-hidden";
121
+ this.containerMirror.appendChild(mirrorElement);
122
+ this.intersectionObserver.observe(mirrorElement);
123
+ mirrorElement.target = element;
124
+ }
125
+ registerOverflowElement(element) {
126
+ this.overflowResizeObserver.observe(element);
127
+ const mirrorElement = globalThis.document.createElement("box-mirror");
128
+ mirrorElement.boxSizing = "border-box";
129
+ this.containerMirror.appendChild(mirrorElement);
130
+ this.intersectionObserver.observe(mirrorElement);
131
+ mirrorElement.target = element;
132
+ }
133
+ handleIntersection(entries) {
134
+ this.updateInlineGap();
135
+ for (const entry of entries) {
136
+ if (!HTMLBoxMirror.isHTMLBoxMirror(entry.target)) throw 78324;
137
+ const mirrorElement = entry.target.target;
138
+ if (!mirrorElement) throw 1129342;
139
+ this.elementIsIntersectingMap.set(mirrorElement, entry.isIntersecting);
140
+ }
141
+ const itemElements = this.getItemElements();
142
+ const lastItemElement = itemElements.at(-1);
143
+ const isOverflowing = lastItemElement ? !this.elementIsIntersectingMap.get(lastItemElement) : true;
144
+ for (const overflowElement of this.getOverflowElements()) overflowElement.hidden = !isOverflowing;
145
+ for (const itemElement of itemElements) if (isOverflowing) itemElement.hidden = !this.elementIsIntersectingMap.get(itemElement);
146
+ else itemElement.hidden = false;
147
+ this.setAttribute("overflowing", String(isOverflowing));
148
+ }
149
+ updateInlineGap() {
150
+ const blockGap = globalThis.getComputedStyle(this).rowGap;
151
+ const inlineGap = globalThis.getComputedStyle(this).columnGap;
152
+ this.variableStyleSheet.replaceSync(`
153
+ :host {
154
+ --block-gap: ${isNaN(parseFloat(blockGap)) ? "0px" : blockGap};
155
+ --inline-gap: ${isNaN(parseFloat(inlineGap)) ? "0px" : inlineGap};
156
+ }
157
+ `);
158
+ }
159
+ handleResize(entries) {
160
+ for (const entry of entries) {
161
+ const [borderBoxSize, ...rest] = entry.borderBoxSize;
162
+ if (!borderBoxSize) throw 1;
163
+ if (rest.length > 0) throw 1;
164
+ if (entry.target.slot === "overflow") {
165
+ if (borderBoxSize.blockSize === 0 || borderBoxSize.inlineSize === 0) continue;
166
+ this.overflowStyleSheet.replaceSync(`
167
+ :host {
168
+ --overflow-inline-size: ${borderBoxSize.inlineSize}px;
169
+ --overflow-block-size: ${borderBoxSize.blockSize}px;
170
+ }
171
+ `);
172
+ }
173
+ }
174
+ }
175
+ getItemElements() {
176
+ return this.childrenSlot.assignedElements().filter(HTMLOverflowContainerElement.isHTMLElement);
177
+ }
178
+ getOverflowElements() {
179
+ return this.overflowSlot.assignedElements().filter(HTMLOverflowContainerElement.isHTMLElement);
180
+ }
181
+ static isHTMLElement(element) {
182
+ return element instanceof HTMLElement;
183
+ }
184
+ };
185
+ globalThis.customElements.define("overflow-container", HTMLOverflowContainerElement);
186
+
187
+ //#endregion
188
+ export { HTMLBoxMirror, HTMLOverflowContainerElement };
189
+ //# sourceMappingURL=mod.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.js","names":["styles","#target","#BOX_SIZING_ATTRIBUTE","#BEHAVIOR_ATTRIBUTE","styles"],"sources":["../box-mirror/HTMLBoxMirror.css?raw","../box-mirror/HTMLBoxMirror.ts","../overflow-container/HTMLOverflowContainerElement.css?raw","../overflow-container/HTMLOverflowContainerElement.ts"],"sourcesContent":["export default \":host {\\n\\tvisibility: hidden;\\n\\tmax-inline-size: 100%;\\n\\tmax-block-size: 100%;\\n}\\n\\n:host([box-sizing=\\\"content-box\\\"]) {\\n\\tinline-size: var(--content-box-inline-size);\\n\\tblock-size: var(--content-box-block-size);\\n}\\n\\n:host([box-sizing=\\\"border-box\\\"]) {\\n\\tinline-size: var(--border-box-inline-size);\\n\\tblock-size: var(--border-box-block-size);\\n}\\n\"","import styles from \"./HTMLBoxMirror.css?raw\" with { type: \"text\" };\n\nexport class HTMLBoxMirror extends HTMLElement {\n\tprivate readonly rootStyleSheet = new CSSStyleSheet();\n\tprivate readonly variableStyleSheet = new CSSStyleSheet();\n\n\tprivate readonly resizeObserver = new ResizeObserver(this.handleResize.bind(this));\n\n\tpublic connectedCallback() {\n\t\tthis.rootStyleSheet.replaceSync(styles);\n\n\t\tconst shadowRoot = this.attachShadow({ mode: \"open\" });\n\t\tshadowRoot.adoptedStyleSheets = [this.rootStyleSheet, this.variableStyleSheet];\n\n\t\tshadowRoot.appendChild(this.ownerDocument.createElement(\"slot\"));\n\t}\n\n\tpublic disconnectedCallback() {\n\t\tthis.resizeObserver.disconnect();\n\t}\n\n\tprivate handleResize(entries: ResizeObserverEntry[]) {\n\t\tconst [entry, ...remainingEntries] = entries;\n\t\tif (!entry) throw 1;\n\t\tif (remainingEntries.length > 0) throw 1;\n\n\t\tconst [borderBox, ...borderBoxRest] = entry.borderBoxSize;\n\t\tif (!borderBox || borderBoxRest.length > 0) throw 1;\n\n\t\tconst [contentBox, ...contentBoxRest] = entry.contentBoxSize;\n\t\tif (!contentBox || contentBoxRest.length > 0) throw 1;\n\n\t\tif (\n\t\t\tthis.behavior === \"ignore-hidden\" &&\n\t\t\tentry.target instanceof HTMLElement &&\n\t\t\tentry.target.hidden &&\n\t\t\tborderBox.blockSize === 0 && borderBox.inlineSize === 0 &&\n\t\t\tcontentBox.blockSize === 0 && contentBox.inlineSize === 0\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.variableStyleSheet.replaceSync(`\n\t\t\t:host {\n\t\t\t\t--border-box-block-size: ${borderBox.blockSize}px;\n\t\t\t\t--border-box-inline-size: ${borderBox.inlineSize}px;\n\t\t\t\t--content-box-block-size: ${contentBox.blockSize}px;\n\t\t\t\t--content-box-inline-size: ${contentBox.inlineSize}px;\n\t\t\t}\n\t\t`);\n\t}\n\n\t#target?: Element | null;\n\tget target() {\n\t\treturn this.#target ?? null;\n\t}\n\tset target(value) {\n\t\tthis.resizeObserver.disconnect();\n\t\tif (value) this.resizeObserver.observe(value);\n\n\t\tthis.#target = value;\n\t}\n\n\tstatic #BOX_SIZING_ATTRIBUTE = \"box-sizing\" as const;\n\tget boxSizing() {\n\t\treturn this.getAttribute(HTMLBoxMirror.#BOX_SIZING_ATTRIBUTE) as \"border-box\" | \"content-box\" | null;\n\t}\n\tset boxSizing(value) {\n\t\tif (value) {\n\t\t\tthis.setAttribute(HTMLBoxMirror.#BOX_SIZING_ATTRIBUTE, value);\n\t\t} else {\n\t\t\tthis.removeAttribute(HTMLBoxMirror.#BOX_SIZING_ATTRIBUTE);\n\t\t}\n\t}\n\n\tstatic #BEHAVIOR_ATTRIBUTE = \"behavior\" as const;\n\tget behavior() {\n\t\treturn this.getAttribute(HTMLBoxMirror.#BEHAVIOR_ATTRIBUTE) as \"ignore-hidden\" | null;\n\t}\n\tset behavior(value) {\n\t\tif (value) {\n\t\t\tthis.setAttribute(HTMLBoxMirror.#BEHAVIOR_ATTRIBUTE, value);\n\t\t} else {\n\t\t\tthis.removeAttribute(HTMLBoxMirror.#BEHAVIOR_ATTRIBUTE);\n\t\t}\n\t}\n\n\tstatic isHTMLBoxMirror(value: unknown) {\n\t\treturn value instanceof HTMLBoxMirror;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t\"box-mirror\": HTMLBoxMirror;\n\t}\n}\n\nglobalThis.customElements.define(\"box-mirror\", HTMLBoxMirror);\n","export default \":host {\\n\\tdisplay: flex;\\n\\tposition: relative;\\n\\tbox-sizing: border-box;\\n\\toverflow: clip;\\n\\n\\t& > *::slotted(*) {\\n\\t\\tflex-shrink: 0;\\n\\t}\\n\\n\\t& > slot[name=\\\"overflow\\\"]::slotted([hidden]) {\\n\\t\\tdisplay: block;\\n\\t\\tvisibility: hidden;\\n\\t\\tposition: absolute;\\n\\t}\\n}\\n\\n#container-mirror {\\n\\tposition: absolute;\\n\\tdisplay: flex;\\n\\tflex-direction: inherit;\\n\\tgap: inherit;\\n\\tbox-sizing: border-box;\\n\\n\\t& > * {\\n\\t\\tflex-shrink: 0;\\n\\t}\\n\\n\\t& > *:last-of-type {\\n\\t\\tposition: relative;\\n\\t\\tinset-inline-end: clamp(\\n\\t\\t\\t0%,\\n\\t\\t\\tcalc(var(--overflow-inline-size) + var(--inline-gap)),\\n\\t\\t\\tcalc(100% - var(--border-box-inline-size))\\n\\t\\t);\\n\\t\\tinset-block-end: clamp(\\n\\t\\t\\t0%,\\n\\t\\t\\tcalc(var(--overflow-block-size) + var(--block-gap)),\\n\\t\\t\\tcalc(100% - var(--border-box-block-size))\\n\\t\\t);\\n\\t}\\n}\\n\"","import \"../box-mirror/HTMLBoxMirror.ts\";\nimport { HTMLBoxMirror } from \"../box-mirror/HTMLBoxMirror.ts\";\n\nimport styles from \"./HTMLOverflowContainerElement.css?raw\" with { type: \"text\" };\n\nexport class HTMLOverflowContainerElement extends HTMLElement {\n\tprivate readonly rootStyleSheet = new CSSStyleSheet();\n\tprivate readonly variableStyleSheet = new CSSStyleSheet();\n\tprivate readonly overflowStyleSheet = new CSSStyleSheet();\n\n\tprivate readonly childrenSlot = this.ownerDocument.createElement(\"slot\");\n\tprivate readonly overflowSlot = this.ownerDocument.createElement(\"slot\");\n\n\tprivate readonly containerMirror = this.ownerDocument.createElement(\"box-mirror\");\n\n\tprivate readonly overflowResizeObserver = new ResizeObserver(this.handleResize.bind(this));\n\n\tprivate readonly elementIsIntersectingMap = new WeakMap<Element, boolean>();\n\tprivate readonly intersectionObserver = new IntersectionObserver(this.handleIntersection.bind(this), {\n\t\troot: this.containerMirror,\n\t\tthreshold: 1,\n\t});\n\n\tconstructor() {\n\t\tsuper();\n\n\t\tthis.overflowSlot.name = \"overflow\";\n\n\t\tthis.containerMirror.id = \"container-mirror\";\n\t\tthis.containerMirror.boxSizing = \"content-box\";\n\t\tthis.containerMirror.target = this;\n\n\t\tconst shadowRoot = this.attachShadow({ mode: \"open\" });\n\t\tshadowRoot.adoptedStyleSheets = [this.rootStyleSheet, this.variableStyleSheet, this.overflowStyleSheet];\n\n\t\tshadowRoot.appendChild(this.childrenSlot);\n\t\tshadowRoot.appendChild(this.overflowSlot);\n\n\t\tshadowRoot.appendChild(this.containerMirror);\n\t}\n\n\tconnectedCallback() {\n\t\tthis.rootStyleSheet.replaceSync(styles);\n\t\tthis.updateInlineGap();\n\n\t\tfor (const overflowElement of this.getOverflowElements()) {\n\t\t\tthis.registerOverflowElement(overflowElement);\n\t\t}\n\n\t\tfor (const itemElement of this.getItemElements()) {\n\t\t\tthis.registerItemElement(itemElement);\n\t\t}\n\t}\n\n\tpublic disconnectedCallback() {\n\t\tthis.intersectionObserver.disconnect();\n\t\tthis.overflowResizeObserver.disconnect();\n\t}\n\n\tprivate registerItemElement(element: HTMLElement) {\n\t\tif (!this.contains(element)) throw new Error(\"Container must contain item element\", { cause: element });\n\n\t\tconst mirrorElement = globalThis.document.createElement(\"box-mirror\");\n\t\tmirrorElement.boxSizing = \"border-box\";\n\t\tmirrorElement.behavior = \"ignore-hidden\";\n\n\t\tthis.containerMirror.appendChild(mirrorElement);\n\n\t\tthis.intersectionObserver.observe(mirrorElement);\n\t\tmirrorElement.target = element;\n\t}\n\n\tprivate registerOverflowElement(element: HTMLElement) {\n\t\tthis.overflowResizeObserver.observe(element);\n\n\t\tconst mirrorElement = globalThis.document.createElement(\"box-mirror\");\n\t\tmirrorElement.boxSizing = \"border-box\";\n\n\t\tthis.containerMirror.appendChild(mirrorElement);\n\n\t\tthis.intersectionObserver.observe(mirrorElement);\n\t\tmirrorElement.target = element;\n\t}\n\n\tprivate handleIntersection(entries: IntersectionObserverEntry[]) {\n\t\tthis.updateInlineGap();\n\n\t\tfor (const entry of entries) {\n\t\t\tif (!HTMLBoxMirror.isHTMLBoxMirror(entry.target)) throw 78324;\n\n\t\t\tconst mirrorElement = entry.target.target;\n\t\t\tif (!mirrorElement) throw 1129342;\n\n\t\t\tthis.elementIsIntersectingMap.set(mirrorElement, entry.isIntersecting);\n\t\t}\n\n\t\tconst itemElements = this.getItemElements();\n\t\tconst lastItemElement = itemElements.at(-1);\n\n\t\tconst isOverflowing = lastItemElement ? !this.elementIsIntersectingMap.get(lastItemElement) : true;\n\n\t\tfor (const overflowElement of this.getOverflowElements()) {\n\t\t\toverflowElement.hidden = !isOverflowing;\n\t\t}\n\n\t\tfor (const itemElement of itemElements) {\n\t\t\tif (isOverflowing) {\n\t\t\t\titemElement.hidden = !this.elementIsIntersectingMap.get(itemElement);\n\t\t\t} else {\n\t\t\t\titemElement.hidden = false;\n\t\t\t}\n\t\t}\n\n\t\tthis.setAttribute(\"overflowing\", String(isOverflowing));\n\t}\n\n\tprivate updateInlineGap() {\n\t\tconst blockGap = globalThis.getComputedStyle(this).rowGap;\n\t\tconst inlineGap = globalThis.getComputedStyle(this).columnGap;\n\n\t\tthis.variableStyleSheet.replaceSync(`\n\t\t\t:host {\n\t\t\t\t--block-gap: ${isNaN(parseFloat(blockGap)) ? \"0px\" : blockGap};\n\t\t\t\t--inline-gap: ${isNaN(parseFloat(inlineGap)) ? \"0px\" : inlineGap};\n\t\t\t}\n\t\t`);\n\t}\n\n\tprivate handleResize(entries: ResizeObserverEntry[]) {\n\t\tfor (const entry of entries) {\n\t\t\tconst [borderBoxSize, ...rest] = entry.borderBoxSize;\n\t\t\tif (!borderBoxSize) throw 1;\n\t\t\tif (rest.length > 0) throw 1;\n\n\t\t\tif (entry.target.slot === \"overflow\") {\n\t\t\t\tif (borderBoxSize.blockSize === 0 || borderBoxSize.inlineSize === 0) continue;\n\n\t\t\t\tthis.overflowStyleSheet.replaceSync(`\n\t\t\t\t\t:host {\n\t\t\t\t\t\t--overflow-inline-size: ${borderBoxSize.inlineSize}px;\n\t\t\t\t\t\t--overflow-block-size: ${borderBoxSize.blockSize}px;\n\t\t\t\t\t}\n\t\t\t\t`);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate getItemElements() {\n\t\treturn this.childrenSlot\n\t\t\t.assignedElements()\n\t\t\t.filter(HTMLOverflowContainerElement.isHTMLElement);\n\t}\n\n\tprivate getOverflowElements() {\n\t\treturn this.overflowSlot\n\t\t\t.assignedElements()\n\t\t\t.filter(HTMLOverflowContainerElement.isHTMLElement);\n\t}\n\n\tprivate static isHTMLElement(element: unknown) {\n\t\treturn element instanceof HTMLElement;\n\t}\n}\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t\"overflow-container\": HTMLOverflowContainerElement;\n\t}\n}\n\nglobalThis.customElements.define(\"overflow-container\", HTMLOverflowContainerElement);\n"],"mappings":"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"}
package/mod.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./box-mirror/HTMLBoxMirror.ts";
2
+ export * from "./overflow-container/HTMLOverflowContainerElement.ts";
@@ -0,0 +1,42 @@
1
+ :host {
2
+ display: flex;
3
+ position: relative;
4
+ box-sizing: border-box;
5
+ overflow: clip;
6
+
7
+ & > *::slotted(*) {
8
+ flex-shrink: 0;
9
+ }
10
+
11
+ & > slot[name="overflow"]::slotted([hidden]) {
12
+ display: block;
13
+ visibility: hidden;
14
+ position: absolute;
15
+ }
16
+ }
17
+
18
+ #container-mirror {
19
+ position: absolute;
20
+ display: flex;
21
+ flex-direction: inherit;
22
+ gap: inherit;
23
+ box-sizing: border-box;
24
+
25
+ & > * {
26
+ flex-shrink: 0;
27
+ }
28
+
29
+ & > *:last-of-type {
30
+ position: relative;
31
+ inset-inline-end: clamp(
32
+ 0%,
33
+ calc(var(--overflow-inline-size) + var(--inline-gap)),
34
+ calc(100% - var(--border-box-inline-size))
35
+ );
36
+ inset-block-end: clamp(
37
+ 0%,
38
+ calc(var(--overflow-block-size) + var(--block-gap)),
39
+ calc(100% - var(--border-box-block-size))
40
+ );
41
+ }
42
+ }
@@ -0,0 +1,171 @@
1
+ import "../box-mirror/HTMLBoxMirror.ts";
2
+ import { HTMLBoxMirror } from "../box-mirror/HTMLBoxMirror.ts";
3
+
4
+ import styles from "./HTMLOverflowContainerElement.css?raw" with { type: "text" };
5
+
6
+ export class HTMLOverflowContainerElement extends HTMLElement {
7
+ private readonly rootStyleSheet = new CSSStyleSheet();
8
+ private readonly variableStyleSheet = new CSSStyleSheet();
9
+ private readonly overflowStyleSheet = new CSSStyleSheet();
10
+
11
+ private readonly childrenSlot = this.ownerDocument.createElement("slot");
12
+ private readonly overflowSlot = this.ownerDocument.createElement("slot");
13
+
14
+ private readonly containerMirror = this.ownerDocument.createElement("box-mirror");
15
+
16
+ private readonly overflowResizeObserver = new ResizeObserver(this.handleResize.bind(this));
17
+
18
+ private readonly elementIsIntersectingMap = new WeakMap<Element, boolean>();
19
+ private readonly intersectionObserver = new IntersectionObserver(this.handleIntersection.bind(this), {
20
+ root: this.containerMirror,
21
+ threshold: 1,
22
+ });
23
+
24
+ constructor() {
25
+ super();
26
+
27
+ this.overflowSlot.name = "overflow";
28
+
29
+ this.containerMirror.id = "container-mirror";
30
+ this.containerMirror.boxSizing = "content-box";
31
+ this.containerMirror.target = this;
32
+
33
+ const shadowRoot = this.attachShadow({ mode: "open" });
34
+ shadowRoot.adoptedStyleSheets = [this.rootStyleSheet, this.variableStyleSheet, this.overflowStyleSheet];
35
+
36
+ shadowRoot.appendChild(this.childrenSlot);
37
+ shadowRoot.appendChild(this.overflowSlot);
38
+
39
+ shadowRoot.appendChild(this.containerMirror);
40
+ }
41
+
42
+ connectedCallback() {
43
+ this.rootStyleSheet.replaceSync(styles);
44
+ this.updateInlineGap();
45
+
46
+ for (const overflowElement of this.getOverflowElements()) {
47
+ this.registerOverflowElement(overflowElement);
48
+ }
49
+
50
+ for (const itemElement of this.getItemElements()) {
51
+ this.registerItemElement(itemElement);
52
+ }
53
+ }
54
+
55
+ public disconnectedCallback() {
56
+ this.intersectionObserver.disconnect();
57
+ this.overflowResizeObserver.disconnect();
58
+ }
59
+
60
+ private registerItemElement(element: HTMLElement) {
61
+ if (!this.contains(element)) throw new Error("Container must contain item element", { cause: element });
62
+
63
+ const mirrorElement = globalThis.document.createElement("box-mirror");
64
+ mirrorElement.boxSizing = "border-box";
65
+ mirrorElement.behavior = "ignore-hidden";
66
+
67
+ this.containerMirror.appendChild(mirrorElement);
68
+
69
+ this.intersectionObserver.observe(mirrorElement);
70
+ mirrorElement.target = element;
71
+ }
72
+
73
+ private registerOverflowElement(element: HTMLElement) {
74
+ this.overflowResizeObserver.observe(element);
75
+
76
+ const mirrorElement = globalThis.document.createElement("box-mirror");
77
+ mirrorElement.boxSizing = "border-box";
78
+
79
+ this.containerMirror.appendChild(mirrorElement);
80
+
81
+ this.intersectionObserver.observe(mirrorElement);
82
+ mirrorElement.target = element;
83
+ }
84
+
85
+ private handleIntersection(entries: IntersectionObserverEntry[]) {
86
+ this.updateInlineGap();
87
+
88
+ for (const entry of entries) {
89
+ if (!HTMLBoxMirror.isHTMLBoxMirror(entry.target)) throw 78324;
90
+
91
+ const mirrorElement = entry.target.target;
92
+ if (!mirrorElement) throw 1129342;
93
+
94
+ this.elementIsIntersectingMap.set(mirrorElement, entry.isIntersecting);
95
+ }
96
+
97
+ const itemElements = this.getItemElements();
98
+ const lastItemElement = itemElements.at(-1);
99
+
100
+ const isOverflowing = lastItemElement ? !this.elementIsIntersectingMap.get(lastItemElement) : true;
101
+
102
+ for (const overflowElement of this.getOverflowElements()) {
103
+ overflowElement.hidden = !isOverflowing;
104
+ }
105
+
106
+ for (const itemElement of itemElements) {
107
+ if (isOverflowing) {
108
+ itemElement.hidden = !this.elementIsIntersectingMap.get(itemElement);
109
+ } else {
110
+ itemElement.hidden = false;
111
+ }
112
+ }
113
+
114
+ this.setAttribute("overflowing", String(isOverflowing));
115
+ }
116
+
117
+ private updateInlineGap() {
118
+ const blockGap = globalThis.getComputedStyle(this).rowGap;
119
+ const inlineGap = globalThis.getComputedStyle(this).columnGap;
120
+
121
+ this.variableStyleSheet.replaceSync(`
122
+ :host {
123
+ --block-gap: ${isNaN(parseFloat(blockGap)) ? "0px" : blockGap};
124
+ --inline-gap: ${isNaN(parseFloat(inlineGap)) ? "0px" : inlineGap};
125
+ }
126
+ `);
127
+ }
128
+
129
+ private handleResize(entries: ResizeObserverEntry[]) {
130
+ for (const entry of entries) {
131
+ const [borderBoxSize, ...rest] = entry.borderBoxSize;
132
+ if (!borderBoxSize) throw 1;
133
+ if (rest.length > 0) throw 1;
134
+
135
+ if (entry.target.slot === "overflow") {
136
+ if (borderBoxSize.blockSize === 0 || borderBoxSize.inlineSize === 0) continue;
137
+
138
+ this.overflowStyleSheet.replaceSync(`
139
+ :host {
140
+ --overflow-inline-size: ${borderBoxSize.inlineSize}px;
141
+ --overflow-block-size: ${borderBoxSize.blockSize}px;
142
+ }
143
+ `);
144
+ }
145
+ }
146
+ }
147
+
148
+ private getItemElements() {
149
+ return this.childrenSlot
150
+ .assignedElements()
151
+ .filter(HTMLOverflowContainerElement.isHTMLElement);
152
+ }
153
+
154
+ private getOverflowElements() {
155
+ return this.overflowSlot
156
+ .assignedElements()
157
+ .filter(HTMLOverflowContainerElement.isHTMLElement);
158
+ }
159
+
160
+ private static isHTMLElement(element: unknown) {
161
+ return element instanceof HTMLElement;
162
+ }
163
+ }
164
+
165
+ declare global {
166
+ interface HTMLElementTagNameMap {
167
+ "overflow-container": HTMLOverflowContainerElement;
168
+ }
169
+ }
170
+
171
+ globalThis.customElements.define("overflow-container", HTMLOverflowContainerElement);
@@ -0,0 +1,11 @@
1
+ export class HTMLOverflowContainerEvent<Type extends string, Detail> extends CustomEvent<Detail> {
2
+ constructor(type: Type, eventInitDict: CustomEventInit<Detail>) {
3
+ super(type, eventInitDict);
4
+ }
5
+ }
6
+
7
+ export class HTMLOverflowContainerStateEvent extends HTMLOverflowContainerEvent<"state", { overflowingAt?: number }> {
8
+ private constructor(overflowingAt?: number) {
9
+ super("state", { detail: { overflowingAt } });
10
+ }
11
+ }