@inc2734/unitone-css 0.94.2 → 0.95.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/dist/app.css +1 -1
- package/dist/app.js +1 -1
- package/dist/behaviors/dividers.js +1 -0
- package/dist/behaviors/index.js +1 -0
- package/dist/behaviors/stairs.js +1 -0
- package/dist/compatibility/fluid-typography.js +1 -0
- package/dist/compatibility/index.js +1 -0
- package/dist/layout-primitives/both-sides/react.js +1 -0
- package/dist/layout-primitives/center/react.js +1 -0
- package/dist/layout-primitives/cluster/react.js +1 -0
- package/dist/layout-primitives/container/react.js +1 -0
- package/dist/layout-primitives/cover/react.js +1 -0
- package/dist/layout-primitives/decorator/react.js +1 -0
- package/dist/layout-primitives/float/react.js +1 -0
- package/dist/layout-primitives/frame/react.js +1 -0
- package/dist/layout-primitives/gutters/react.js +1 -0
- package/dist/layout-primitives/index.js +1 -0
- package/dist/layout-primitives/layers/react.js +1 -0
- package/dist/layout-primitives/marquee/behavior.js +1 -0
- package/dist/layout-primitives/marquee/react.js +1 -0
- package/dist/layout-primitives/masonry/react.js +1 -0
- package/dist/layout-primitives/reel/react.js +1 -0
- package/dist/layout-primitives/responsive-grid/react.js +1 -0
- package/dist/layout-primitives/stack/react.js +1 -0
- package/dist/layout-primitives/switcher/react.js +1 -0
- package/dist/layout-primitives/text/react.js +1 -0
- package/dist/layout-primitives/texture/react.js +1 -0
- package/dist/layout-primitives/vertical-writing/behavior.js +1 -0
- package/dist/layout-primitives/vertical-writing/react.js +1 -0
- package/dist/layout-primitives/with-sidebar/react.js +1 -0
- package/dist/library.js +1 -1
- package/package.json +24 -19
- package/src/app.js +3 -123
- package/src/app.scss +4 -4
- package/src/behaviors/_index.scss +43 -0
- package/src/{helper → behaviors}/_typography.scss +1 -1
- package/src/behaviors/dividers.js +8 -0
- package/src/behaviors/index.js +2 -0
- package/src/behaviors/stairs.js +8 -0
- package/src/compatibility/fluid-typography.js +18 -0
- package/src/compatibility/index.js +1 -0
- package/src/foundation/_foundation-core.scss +107 -0
- package/src/foundation/_foundation.scss +2 -106
- package/src/foundation/_index.scss +1 -0
- package/src/helper/_helper.scss +2 -42
- package/src/layout-primitives/_index.scss +1 -0
- package/src/layout-primitives/_layout-primitives-core.scss +41 -0
- package/src/layout-primitives/_layout-primitives.scss +2 -42
- package/src/layout-primitives/both-sides/_both-sides-core.scss +31 -0
- package/src/layout-primitives/both-sides/_both-sides.scss +2 -30
- package/src/layout-primitives/both-sides/_index.scss +1 -0
- package/src/layout-primitives/both-sides/react.jsx +1 -0
- package/src/layout-primitives/center/_center-core.scss +22 -0
- package/src/layout-primitives/center/_center.scss +2 -21
- package/src/layout-primitives/center/_index.scss +1 -0
- package/src/layout-primitives/center/react.jsx +1 -0
- package/src/layout-primitives/cluster/_cluster-core.scss +126 -0
- package/src/layout-primitives/cluster/_cluster.scss +2 -125
- package/src/layout-primitives/cluster/_index.scss +1 -0
- package/src/layout-primitives/cluster/react.jsx +3 -0
- package/src/layout-primitives/container/_container-core.scss +18 -0
- package/src/layout-primitives/container/_container.scss +2 -17
- package/src/layout-primitives/container/_index.scss +1 -0
- package/src/layout-primitives/container/react.jsx +1 -0
- package/src/layout-primitives/cover/_cover-core.scss +80 -0
- package/src/layout-primitives/cover/_cover.scss +2 -79
- package/src/layout-primitives/cover/_index.scss +1 -0
- package/src/layout-primitives/cover/react.jsx +1 -0
- package/src/layout-primitives/decorator/_decorator-core.scss +104 -0
- package/src/layout-primitives/decorator/_decorator.scss +2 -103
- package/src/layout-primitives/decorator/_index.scss +1 -0
- package/src/layout-primitives/decorator/react.jsx +1 -0
- package/src/layout-primitives/float/_float-core.scss +29 -0
- package/src/layout-primitives/float/_float.scss +2 -28
- package/src/layout-primitives/float/_index.scss +1 -0
- package/src/layout-primitives/float/react.jsx +1 -0
- package/src/layout-primitives/frame/_frame-core.scss +36 -0
- package/src/layout-primitives/frame/_frame.scss +2 -35
- package/src/layout-primitives/frame/_index.scss +1 -0
- package/src/layout-primitives/frame/react.jsx +1 -0
- package/src/layout-primitives/gutters/_gutters-core.scss +12 -0
- package/src/layout-primitives/gutters/_gutters.scss +2 -11
- package/src/layout-primitives/gutters/_index.scss +1 -0
- package/src/layout-primitives/gutters/react.jsx +1 -0
- package/src/layout-primitives/index.js +2 -20
- package/src/layout-primitives/layers/_index.scss +1 -0
- package/src/layout-primitives/layers/_layers-core.scss +139 -0
- package/src/layout-primitives/layers/_layers.scss +2 -138
- package/src/layout-primitives/layers/react.jsx +1 -0
- package/src/layout-primitives/marquee/_index.scss +1 -0
- package/src/layout-primitives/marquee/_marquee-core.scss +73 -0
- package/src/layout-primitives/marquee/_marquee.scss +2 -72
- package/src/layout-primitives/marquee/behavior.js +8 -0
- package/src/layout-primitives/marquee/react.jsx +3 -0
- package/src/layout-primitives/masonry/_index.scss +1 -0
- package/src/layout-primitives/masonry/_masonry-core.scss +26 -0
- package/src/layout-primitives/masonry/_masonry.scss +2 -25
- package/src/layout-primitives/masonry/react.jsx +1 -0
- package/src/layout-primitives/reel/_index.scss +1 -0
- package/src/layout-primitives/reel/_reel-core.scss +55 -0
- package/src/layout-primitives/reel/_reel.scss +2 -54
- package/src/layout-primitives/reel/react.jsx +1 -0
- package/src/layout-primitives/responsive-grid/_index.scss +1 -0
- package/src/layout-primitives/responsive-grid/_responsive-grid-core.scss +249 -0
- package/src/layout-primitives/responsive-grid/_responsive-grid.scss +2 -248
- package/src/layout-primitives/responsive-grid/react.jsx +4 -0
- package/src/layout-primitives/stack/_index.scss +1 -0
- package/src/layout-primitives/stack/_stack-core.scss +201 -0
- package/src/layout-primitives/stack/_stack.scss +2 -200
- package/src/layout-primitives/stack/react.jsx +3 -0
- package/src/layout-primitives/switcher/_index.scss +1 -0
- package/src/layout-primitives/switcher/_switcher-core.scss +70 -0
- package/src/layout-primitives/switcher/_switcher.scss +2 -69
- package/src/layout-primitives/switcher/react.jsx +3 -0
- package/src/layout-primitives/text/_index.scss +1 -0
- package/src/layout-primitives/text/_text-core.scss +169 -0
- package/src/layout-primitives/text/_text.scss +2 -168
- package/src/layout-primitives/text/react.jsx +1 -0
- package/src/layout-primitives/texture/_index.scss +1 -0
- package/src/layout-primitives/texture/_texture-core.scss +235 -0
- package/src/layout-primitives/texture/_texture.scss +2 -234
- package/src/layout-primitives/texture/react.jsx +1 -0
- package/src/layout-primitives/vertical-writing/_index.scss +1 -0
- package/src/layout-primitives/vertical-writing/_vertical-writing-core.scss +118 -0
- package/src/layout-primitives/vertical-writing/_vertical-writing.scss +2 -117
- package/src/layout-primitives/vertical-writing/behavior.js +8 -0
- package/src/layout-primitives/vertical-writing/react.jsx +3 -0
- package/src/layout-primitives/with-sidebar/_index.scss +1 -0
- package/src/layout-primitives/with-sidebar/_with-sidebar-core.scss +337 -0
- package/src/layout-primitives/with-sidebar/_with-sidebar.scss +2 -336
- package/src/layout-primitives/with-sidebar/react.jsx +3 -0
- package/src/library.js +807 -321
- package/src/register-layout-initializer.js +132 -0
- package/src/settings/_html.scss +1 -1
- package/src/settings/_index.scss +1 -0
- package/src/settings/_root.scss +1 -1
- package/src/settings/_settings-core.scss +3 -0
- package/src/settings/_settings.scss +3 -3
- package/src/variables/_index.scss +1 -0
- package/src/variables/_variables-core.scss +78 -0
- package/src/variables/_variables.scss +2 -77
- package/dist/index.js +0 -1
- package/src/index.js +0 -1
- /package/src/{helper → behaviors}/_align-content.scss +0 -0
- /package/src/{helper → behaviors}/_align-items.scss +0 -0
- /package/src/{helper → behaviors}/_align-self.scss +0 -0
- /package/src/{helper → behaviors}/_align.scss +0 -0
- /package/src/{helper → behaviors}/_auto-phrase.scss +0 -0
- /package/src/{helper → behaviors}/_auto-repeat.scss +0 -0
- /package/src/{helper → behaviors}/_background-clip.scss +0 -0
- /package/src/{helper → behaviors}/_gap.scss +0 -0
- /package/src/{helper → behaviors}/_gutters.scss +0 -0
- /package/src/{helper → behaviors}/_justify-content.scss +0 -0
- /package/src/{helper → behaviors}/_justify-items.scss +0 -0
- /package/src/{helper → behaviors}/_justify-self.scss +0 -0
- /package/src/{helper → behaviors}/_link-decoration.scss +0 -0
- /package/src/{helper → behaviors}/_mix-blend-mode.scss +0 -0
- /package/src/{helper → behaviors}/_negative-gap.scss +0 -0
- /package/src/{helper → behaviors}/_overflow.scss +0 -0
- /package/src/{helper → behaviors}/_padding.scss +0 -0
- /package/src/{helper → behaviors}/_position.scss +0 -0
- /package/src/{helper → behaviors}/_stairs.scss +0 -0
- /package/src/{helper → behaviors}/_text-orientation.scss +0 -0
package/src/library.js
CHANGED
|
@@ -1,3 +1,459 @@
|
|
|
1
|
+
const layoutAttributeName = 'data-unitone-layout';
|
|
2
|
+
const layoutIntersectionMargin = 200;
|
|
3
|
+
const layoutIntersectionRootMargin = `${layoutIntersectionMargin}px 0px`;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Returns layout tokens from the target element.
|
|
7
|
+
*
|
|
8
|
+
* @param {Element | null | undefined} element Target element.
|
|
9
|
+
* @returns {string[]} Layout tokens.
|
|
10
|
+
*/
|
|
11
|
+
const getLayoutTokens = (element) =>
|
|
12
|
+
(element.getAttribute(layoutAttributeName) ?? '').split(/\s+/).filter(Boolean);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Returns tokens with the specified values removed.
|
|
16
|
+
*
|
|
17
|
+
* @param {string[]} tokens Source tokens.
|
|
18
|
+
* @param {string[]} removedTokens Tokens to remove.
|
|
19
|
+
* @returns {string[]} Filtered tokens.
|
|
20
|
+
*/
|
|
21
|
+
const withoutLayoutTokens = (tokens, removedTokens) =>
|
|
22
|
+
tokens.filter((value) => !removedTokens.includes(value));
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Updates the layout token attribute on the element.
|
|
26
|
+
*
|
|
27
|
+
* @param {Element} element Target element.
|
|
28
|
+
* @param {string[]} tokens Tokens to set.
|
|
29
|
+
* @returns {void}
|
|
30
|
+
*/
|
|
31
|
+
const setLayoutTokens = (element, tokens) => {
|
|
32
|
+
const nextValue = tokens.filter(Boolean).join(' ');
|
|
33
|
+
if ((element.getAttribute(layoutAttributeName) ?? '') !== nextValue) {
|
|
34
|
+
element.setAttribute(layoutAttributeName, nextValue);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Observes target resizes and invokes the callback when a relevant change is detected.
|
|
40
|
+
*
|
|
41
|
+
* @param {Element} target Target element.
|
|
42
|
+
* @param {(target: Element, entry?: ResizeObserverEntry) => void} callback Callback to run.
|
|
43
|
+
* @param {{ getValue?: (entry: ResizeObserverEntry) => unknown, delay?: number }} [options]
|
|
44
|
+
* @returns {ResizeObserver} ResizeObserver instance.
|
|
45
|
+
*/
|
|
46
|
+
const createResizeObserver = (target, callback, { getValue, delay = 250 } = {}) => {
|
|
47
|
+
let prevValue;
|
|
48
|
+
let isFirstEntry = true;
|
|
49
|
+
|
|
50
|
+
const observer = new ResizeObserver(
|
|
51
|
+
debounce((entries) => {
|
|
52
|
+
for (const entry of entries) {
|
|
53
|
+
const currentValue = getValue?.(entry);
|
|
54
|
+
if (isFirstEntry) {
|
|
55
|
+
prevValue = currentValue;
|
|
56
|
+
isFirstEntry = false;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (undefined === currentValue || currentValue !== prevValue) {
|
|
61
|
+
callback(entry.target, entry);
|
|
62
|
+
prevValue = currentValue;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}, delay),
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
observer.observe(target);
|
|
69
|
+
|
|
70
|
+
return observer;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Creates a MutationObserver for the target node.
|
|
75
|
+
*
|
|
76
|
+
* @param {Node} target Target node.
|
|
77
|
+
* @param {MutationObserverInit} options Observer options.
|
|
78
|
+
* @param {(entries: MutationRecord[]) => void} callback Callback to run.
|
|
79
|
+
* @returns {MutationObserver} MutationObserver instance.
|
|
80
|
+
*/
|
|
81
|
+
const createMutationObserver = (target, options, callback) => {
|
|
82
|
+
const observer = new MutationObserver((entries) => {
|
|
83
|
+
requestAnimationFrame(() => {
|
|
84
|
+
if (!target?.isConnected) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
callback(entries);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
observer.observe(target, options);
|
|
93
|
+
|
|
94
|
+
return observer;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Creates an IntersectionObserver for the target element.
|
|
99
|
+
*
|
|
100
|
+
* @param {Element} target Target element.
|
|
101
|
+
* @param {(entry: IntersectionObserverEntry) => void} callback Callback to run.
|
|
102
|
+
* @returns {IntersectionObserver} IntersectionObserver instance.
|
|
103
|
+
*/
|
|
104
|
+
const createIntersectionObserver = (target, callback) => {
|
|
105
|
+
const observer = new IntersectionObserver(
|
|
106
|
+
([entry]) => {
|
|
107
|
+
if (entry) {
|
|
108
|
+
callback(entry);
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
{ rootMargin: layoutIntersectionRootMargin },
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
observer.observe(target);
|
|
115
|
+
|
|
116
|
+
return observer;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Returns a scheduler that coalesces re-application work into a single frame.
|
|
121
|
+
*
|
|
122
|
+
* @param {Element} target Target element.
|
|
123
|
+
* @param {(target: Element) => void} callback Callback to run.
|
|
124
|
+
* @returns {() => void} Schedule function.
|
|
125
|
+
*/
|
|
126
|
+
const createScheduledTargetCallback = (target, callback) => {
|
|
127
|
+
let rafId = 0;
|
|
128
|
+
let defaultView;
|
|
129
|
+
|
|
130
|
+
return () => {
|
|
131
|
+
defaultView = target?.ownerDocument?.defaultView;
|
|
132
|
+
if (!defaultView?.requestAnimationFrame) {
|
|
133
|
+
callback(target);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (rafId) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
rafId = defaultView.requestAnimationFrame(() => {
|
|
142
|
+
rafId = 0;
|
|
143
|
+
defaultView = null;
|
|
144
|
+
|
|
145
|
+
if (target?.isConnected) {
|
|
146
|
+
callback(target);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Observes resizes on the target and its direct children.
|
|
154
|
+
*
|
|
155
|
+
* @param {Element} target Target element.
|
|
156
|
+
* @param {(target: Element) => void} callback Callback to run.
|
|
157
|
+
* @param {{ getValue?: (entry: ResizeObserverEntry) => unknown, delay?: number, onChildList?: (entries: MutationRecord[]) => void }} [options]
|
|
158
|
+
* @returns {{ resizeObserver: ResizeObserver, mutationObserver: MutationObserver }}
|
|
159
|
+
*/
|
|
160
|
+
const createDirectChildrenResizeObserver = (
|
|
161
|
+
target,
|
|
162
|
+
callback,
|
|
163
|
+
{ getValue, delay = 250, onChildList } = {},
|
|
164
|
+
) => {
|
|
165
|
+
const prevValues = new WeakMap();
|
|
166
|
+
const observedChildren = new Set();
|
|
167
|
+
|
|
168
|
+
const observer = new ResizeObserver(
|
|
169
|
+
debounce((entries) => {
|
|
170
|
+
let shouldApply = false;
|
|
171
|
+
|
|
172
|
+
for (const entry of entries) {
|
|
173
|
+
const currentValue = getValue?.(entry);
|
|
174
|
+
if (!prevValues.has(entry.target)) {
|
|
175
|
+
prevValues.set(entry.target, currentValue);
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (undefined === currentValue || currentValue !== prevValues.get(entry.target)) {
|
|
180
|
+
shouldApply = true;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
prevValues.set(entry.target, currentValue);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (shouldApply) {
|
|
187
|
+
callback(target);
|
|
188
|
+
}
|
|
189
|
+
}, delay),
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
const syncObservedChildren = () => {
|
|
193
|
+
Array.from(observedChildren).forEach((child) => {
|
|
194
|
+
if (child.parentElement !== target) {
|
|
195
|
+
observer.unobserve(child);
|
|
196
|
+
observedChildren.delete(child);
|
|
197
|
+
prevValues.delete(child);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
Array.from(target?.children ?? []).forEach((child) => {
|
|
202
|
+
if (observedChildren.has(child)) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
observer.observe(child);
|
|
207
|
+
observedChildren.add(child);
|
|
208
|
+
});
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
observer.observe(target);
|
|
212
|
+
syncObservedChildren();
|
|
213
|
+
|
|
214
|
+
const mutationObserver = createMutationObserver(target, { childList: true }, (entries) => {
|
|
215
|
+
if (!entries.some((entry) => 'childList' === entry.type)) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
syncObservedChildren();
|
|
220
|
+
onChildList?.(entries);
|
|
221
|
+
callback(target);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
resizeObserver: observer,
|
|
226
|
+
mutationObserver,
|
|
227
|
+
};
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Observes attribute changes on direct children.
|
|
232
|
+
*
|
|
233
|
+
* @param {Element} target Target element.
|
|
234
|
+
* @param {(target: Element) => void} callback Callback to run.
|
|
235
|
+
* @param {{ attributeFilter: string[], shouldApply: (entry: MutationRecord) => boolean, attributeOldValue?: boolean }} options
|
|
236
|
+
* @returns {{ observer: MutationObserver, syncObservedChildren: () => void }}
|
|
237
|
+
*/
|
|
238
|
+
const createDirectChildrenAttributeObserver = (
|
|
239
|
+
target,
|
|
240
|
+
callback,
|
|
241
|
+
{ attributeFilter, shouldApply, attributeOldValue = true } = {},
|
|
242
|
+
) => {
|
|
243
|
+
const observer = new MutationObserver((entries) => {
|
|
244
|
+
requestAnimationFrame(() => {
|
|
245
|
+
if (!target?.isConnected) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (
|
|
250
|
+
entries.some(
|
|
251
|
+
(entry) =>
|
|
252
|
+
'attributes' === entry.type &&
|
|
253
|
+
entry.target.parentElement === target &&
|
|
254
|
+
shouldApply(entry),
|
|
255
|
+
)
|
|
256
|
+
) {
|
|
257
|
+
callback(target);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
const syncObservedChildren = () => {
|
|
263
|
+
observer.disconnect();
|
|
264
|
+
Array.from(target?.children ?? []).forEach((child) => {
|
|
265
|
+
observer.observe(child, {
|
|
266
|
+
attributes: true,
|
|
267
|
+
attributeFilter,
|
|
268
|
+
attributeOldValue,
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
syncObservedChildren();
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
observer,
|
|
277
|
+
syncObservedChildren,
|
|
278
|
+
};
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Creates a bundled observer setup for layout re-application.
|
|
283
|
+
*
|
|
284
|
+
* @param {Element} target Target element.
|
|
285
|
+
* @param {(target: Element) => void} apply Apply function.
|
|
286
|
+
* @param {{
|
|
287
|
+
* getResizeValue?: (entry: ResizeObserverEntry) => unknown,
|
|
288
|
+
* delay?: number,
|
|
289
|
+
* observeResize?: boolean,
|
|
290
|
+
* observeIntersection?: boolean,
|
|
291
|
+
* observeDirectChildrenResize?: boolean,
|
|
292
|
+
* targetMutation?: { options: MutationObserverInit, shouldApply?: (entries: MutationRecord[]) => boolean },
|
|
293
|
+
* directChildMutation?: { attributeFilter: string[], shouldApply: (entry: MutationRecord) => boolean, attributeOldValue?: boolean }
|
|
294
|
+
* }} [options]
|
|
295
|
+
* @returns {void}
|
|
296
|
+
*/
|
|
297
|
+
const createLayoutObserver = (
|
|
298
|
+
target,
|
|
299
|
+
apply,
|
|
300
|
+
{
|
|
301
|
+
getResizeValue,
|
|
302
|
+
delay = 250,
|
|
303
|
+
observeResize = true,
|
|
304
|
+
observeIntersection = false,
|
|
305
|
+
observeDirectChildrenResize = false,
|
|
306
|
+
targetMutation,
|
|
307
|
+
directChildMutation,
|
|
308
|
+
} = {},
|
|
309
|
+
) => {
|
|
310
|
+
const shouldObserveIntersection =
|
|
311
|
+
observeIntersection && 'undefined' !== typeof IntersectionObserver;
|
|
312
|
+
let isIntersecting = !shouldObserveIntersection || isNearViewport(target);
|
|
313
|
+
let needsApply = shouldObserveIntersection && !isIntersecting;
|
|
314
|
+
|
|
315
|
+
const runApply = (element = target) => {
|
|
316
|
+
if (!element?.isConnected) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (shouldObserveIntersection && !isIntersecting) {
|
|
321
|
+
needsApply = true;
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
needsApply = false;
|
|
326
|
+
apply(element);
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
const schedule = createScheduledTargetCallback(target, runApply);
|
|
330
|
+
const scheduleApply = () => {
|
|
331
|
+
if (!target?.isConnected) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (shouldObserveIntersection && !isIntersecting) {
|
|
336
|
+
needsApply = true;
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
needsApply = true;
|
|
341
|
+
schedule();
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
let syncDirectChildAttributes = () => {};
|
|
345
|
+
|
|
346
|
+
const resizeBundle = !observeResize
|
|
347
|
+
? {
|
|
348
|
+
resizeObserver: null,
|
|
349
|
+
mutationObserver: null,
|
|
350
|
+
}
|
|
351
|
+
: observeDirectChildrenResize
|
|
352
|
+
? createDirectChildrenResizeObserver(target, scheduleApply, {
|
|
353
|
+
getValue: getResizeValue,
|
|
354
|
+
delay,
|
|
355
|
+
onChildList: () => {
|
|
356
|
+
syncDirectChildAttributes();
|
|
357
|
+
},
|
|
358
|
+
})
|
|
359
|
+
: {
|
|
360
|
+
resizeObserver: createResizeObserver(target, scheduleApply, {
|
|
361
|
+
getValue: getResizeValue,
|
|
362
|
+
delay,
|
|
363
|
+
}),
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
if (shouldObserveIntersection) {
|
|
367
|
+
createIntersectionObserver(target, (entry) => {
|
|
368
|
+
isIntersecting = entry.isIntersecting;
|
|
369
|
+
if (isIntersecting && needsApply) {
|
|
370
|
+
scheduleApply();
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (targetMutation?.options) {
|
|
376
|
+
createMutationObserver(target, targetMutation.options, (entries) => {
|
|
377
|
+
if (targetMutation.shouldApply?.(entries) ?? 0 < entries.length) {
|
|
378
|
+
scheduleApply();
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const directChildAttributeBundle =
|
|
384
|
+
directChildMutation?.attributeFilter && directChildMutation.shouldApply
|
|
385
|
+
? createDirectChildrenAttributeObserver(target, scheduleApply, {
|
|
386
|
+
attributeFilter: directChildMutation.attributeFilter,
|
|
387
|
+
shouldApply: directChildMutation.shouldApply,
|
|
388
|
+
attributeOldValue: directChildMutation.attributeOldValue,
|
|
389
|
+
})
|
|
390
|
+
: null;
|
|
391
|
+
|
|
392
|
+
if (directChildAttributeBundle) {
|
|
393
|
+
syncDirectChildAttributes = directChildAttributeBundle.syncObservedChildren;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (!resizeBundle.mutationObserver && directChildAttributeBundle) {
|
|
397
|
+
createMutationObserver(target, { childList: true }, (entries) => {
|
|
398
|
+
if (!entries.some((entry) => 'childList' === entry.type)) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
syncDirectChildAttributes();
|
|
403
|
+
scheduleApply();
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (!shouldObserveIntersection || isIntersecting) {
|
|
408
|
+
runApply(target);
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
const getBorderBoxInlineSize = (entry) => entry.borderBoxSize?.[0].inlineSize;
|
|
413
|
+
|
|
414
|
+
const getContentRectWidth = (entry) => parseInt(entry.contentRect?.width);
|
|
415
|
+
|
|
416
|
+
const hasLayoutBox = (element) => !!element?.isConnected && 0 < element.getClientRects().length;
|
|
417
|
+
|
|
418
|
+
const isNearViewport = (element) => {
|
|
419
|
+
if (!hasLayoutBox(element)) {
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const defaultView = element?.ownerDocument?.defaultView;
|
|
424
|
+
if (!defaultView) {
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const rect = element.getBoundingClientRect();
|
|
429
|
+
const viewportWidth = defaultView.innerWidth;
|
|
430
|
+
const viewportHeight = defaultView.innerHeight;
|
|
431
|
+
|
|
432
|
+
return (
|
|
433
|
+
rect.bottom > -layoutIntersectionMargin &&
|
|
434
|
+
rect.right > 0 &&
|
|
435
|
+
rect.top < viewportHeight + layoutIntersectionMargin &&
|
|
436
|
+
rect.left < viewportWidth
|
|
437
|
+
);
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Returns whether the mutation list contains a matching attributes record.
|
|
442
|
+
*
|
|
443
|
+
* @param {MutationRecord[]} entries Mutation records.
|
|
444
|
+
* @param {(entry: MutationRecord) => boolean} predicate Match predicate.
|
|
445
|
+
* @returns {boolean} Whether a matching attributes mutation exists.
|
|
446
|
+
*/
|
|
447
|
+
const hasAttributeMutation = (entries, predicate) =>
|
|
448
|
+
entries.some((entry) => 'attributes' === entry.type && predicate(entry));
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Coalesces repeated calls into the final call within the delay window.
|
|
452
|
+
*
|
|
453
|
+
* @param {Function} fn Function to wrap.
|
|
454
|
+
* @param {number} delay Delay in milliseconds.
|
|
455
|
+
* @returns {Function} Debounced function.
|
|
456
|
+
*/
|
|
1
457
|
export function debounce(fn, delay) {
|
|
2
458
|
let timer;
|
|
3
459
|
|
|
@@ -10,167 +466,178 @@ export function debounce(fn, delay) {
|
|
|
10
466
|
};
|
|
11
467
|
}
|
|
12
468
|
|
|
469
|
+
/**
|
|
470
|
+
* Recalculates line wrapping state for divider layouts.
|
|
471
|
+
*
|
|
472
|
+
* @param {Element} target Target element.
|
|
473
|
+
* @returns {void}
|
|
474
|
+
*/
|
|
13
475
|
export const setDividerLinewrap = (target) => {
|
|
14
|
-
const
|
|
476
|
+
const children = Array.from(target?.children ?? []);
|
|
477
|
+
const firstChild = children[0];
|
|
15
478
|
if (!firstChild) {
|
|
16
479
|
return;
|
|
17
480
|
}
|
|
18
481
|
|
|
19
|
-
|
|
20
|
-
|
|
482
|
+
const currentLayoutArray = withoutLayoutTokens(getLayoutTokens(target), [
|
|
483
|
+
'divider:initialized',
|
|
484
|
+
'-stack',
|
|
485
|
+
]);
|
|
486
|
+
setLayoutTokens(target, currentLayoutArray);
|
|
21
487
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
);
|
|
27
|
-
|
|
488
|
+
const childLayoutMap = new Map();
|
|
489
|
+
children.forEach((child) => {
|
|
490
|
+
const layoutTokens = withoutLayoutTokens(getLayoutTokens(child), ['-bol', '-linewrap']);
|
|
491
|
+
childLayoutMap.set(child, layoutTokens);
|
|
492
|
+
setLayoutTokens(child, layoutTokens);
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
if (!hasLayoutBox(target)) {
|
|
496
|
+
return;
|
|
28
497
|
}
|
|
29
498
|
|
|
30
|
-
const
|
|
499
|
+
const baseRect = firstChild.getBoundingClientRect();
|
|
500
|
+
const targetChildren = children.reduce((accumulator, child) => {
|
|
31
501
|
const position = window.getComputedStyle(child).getPropertyValue('position');
|
|
32
502
|
const display = window.getComputedStyle(child).getPropertyValue('display');
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
childCurrentLayoutArray = childCurrentLayoutArray.filter(
|
|
40
|
-
(value) => !['-bol', '-linewrap'].includes(value),
|
|
41
|
-
);
|
|
42
|
-
child.setAttribute('data-unitone-layout', childCurrentLayoutArray.join(' '));
|
|
503
|
+
if ('absolute' !== position && 'fixed' !== position && 'none' !== display) {
|
|
504
|
+
accumulator.push({
|
|
505
|
+
child,
|
|
506
|
+
layoutTokens: childLayoutMap.get(child) ?? [],
|
|
507
|
+
rect: child.getBoundingClientRect(),
|
|
508
|
+
});
|
|
43
509
|
}
|
|
510
|
+
return accumulator;
|
|
511
|
+
}, []);
|
|
44
512
|
|
|
45
|
-
|
|
46
|
-
|
|
513
|
+
let prevRect;
|
|
514
|
+
const nextChildLayouts = targetChildren.map(({ child, layoutTokens, rect }, index) => {
|
|
515
|
+
const nextLayoutTokens = [...layoutTokens];
|
|
47
516
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (0 === index || (prevRect?.top < childRect.top && prevRect?.left >= childRect.left)) {
|
|
51
|
-
childCurrentLayoutArray.push('-bol');
|
|
52
|
-
shouldUpdate = true;
|
|
517
|
+
if (0 === index || (prevRect?.top < rect.top && prevRect?.left >= rect.left)) {
|
|
518
|
+
nextLayoutTokens.push('-bol');
|
|
53
519
|
}
|
|
54
520
|
|
|
55
|
-
if (0 < index && baseRect.top <
|
|
56
|
-
|
|
57
|
-
shouldUpdate = true;
|
|
521
|
+
if (0 < index && baseRect.top < rect.top) {
|
|
522
|
+
nextLayoutTokens.push('-linewrap');
|
|
58
523
|
}
|
|
59
524
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
525
|
+
prevRect = rect;
|
|
526
|
+
return { child, layoutTokens: nextLayoutTokens };
|
|
527
|
+
});
|
|
63
528
|
|
|
64
|
-
|
|
529
|
+
nextChildLayouts.forEach(({ child, layoutTokens }) => {
|
|
530
|
+
setLayoutTokens(child, layoutTokens);
|
|
65
531
|
});
|
|
66
532
|
|
|
67
|
-
const isStack =
|
|
68
|
-
|
|
69
|
-
.every((child) => child.getBoundingClientRect().left === baseRect.left);
|
|
533
|
+
const isStack = targetChildren.every(({ rect }) => rect.left === baseRect.left);
|
|
534
|
+
const nextTargetLayout = [...currentLayoutArray];
|
|
70
535
|
if (isStack) {
|
|
71
|
-
|
|
536
|
+
nextTargetLayout.push('-stack');
|
|
72
537
|
}
|
|
73
538
|
|
|
74
|
-
|
|
75
|
-
target
|
|
539
|
+
nextTargetLayout.push('divider:initialized');
|
|
540
|
+
setLayoutTokens(target, nextTargetLayout);
|
|
76
541
|
};
|
|
77
542
|
|
|
543
|
+
/**
|
|
544
|
+
* Creates the observer bundle for divider layouts.
|
|
545
|
+
*
|
|
546
|
+
* @param {Element} target Target element.
|
|
547
|
+
* @param {{ ignore?: { layout?: string[], className?: string[] } }} [args]
|
|
548
|
+
* @returns {void}
|
|
549
|
+
*/
|
|
78
550
|
export const dividersResizeObserver = (target, args = {}) => {
|
|
79
|
-
|
|
551
|
+
const shouldRecalculateByAttributeMutation = (entry) => {
|
|
552
|
+
if ('data-unitone-layout' === entry.attributeName) {
|
|
553
|
+
const ignoreUnitoneLayouts = [
|
|
554
|
+
...(args?.ignore?.layout ?? []),
|
|
555
|
+
...['divider:initialized', '-bol', '-linewrap', '-stack'],
|
|
556
|
+
];
|
|
557
|
+
|
|
558
|
+
const current = (entry.target.getAttribute(entry.attributeName) ?? '')
|
|
559
|
+
.split(' ')
|
|
560
|
+
.filter((v) => !ignoreUnitoneLayouts.includes(v))
|
|
561
|
+
.join(' ');
|
|
562
|
+
|
|
563
|
+
const old = (entry.oldValue ?? '')
|
|
564
|
+
.split(' ')
|
|
565
|
+
.filter((v) => !ignoreUnitoneLayouts.includes(v))
|
|
566
|
+
.join(' ');
|
|
567
|
+
|
|
568
|
+
return current !== old;
|
|
569
|
+
}
|
|
80
570
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
for (const entry of entries) {
|
|
84
|
-
const width = entry.borderBoxSize?.[0].inlineSize;
|
|
85
|
-
if (width !== prevWidth) {
|
|
86
|
-
setDividerLinewrap(entry.target);
|
|
87
|
-
prevWidth = width;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}, 250),
|
|
91
|
-
);
|
|
571
|
+
if ('class' === entry.attributeName) {
|
|
572
|
+
const ignoreClassNames = [...(args?.ignore?.className ?? [])];
|
|
92
573
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
subtree: true,
|
|
98
|
-
characterData: true,
|
|
99
|
-
};
|
|
574
|
+
const current = (entry.target.getAttribute(entry.attributeName) ?? '')
|
|
575
|
+
.split(' ')
|
|
576
|
+
.filter((v) => !ignoreClassNames.includes(v))
|
|
577
|
+
.join(' ');
|
|
100
578
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const ignoreUnitoneLayouts = [
|
|
106
|
-
...(args?.ignore?.layout ?? []),
|
|
107
|
-
...['divider:initialized', '-bol', '-linewrap', '-stack'],
|
|
108
|
-
];
|
|
109
|
-
|
|
110
|
-
const current = (entry.target.getAttribute(entry.attributeName) ?? '')
|
|
111
|
-
.split(' ')
|
|
112
|
-
.filter((v) => !ignoreUnitoneLayouts.includes(v))
|
|
113
|
-
.join(' ');
|
|
114
|
-
|
|
115
|
-
const old = (entry.oldValue ?? '')
|
|
116
|
-
.split(' ')
|
|
117
|
-
.filter((v) => !ignoreUnitoneLayouts.includes(v))
|
|
118
|
-
.join(' ');
|
|
119
|
-
|
|
120
|
-
if (current !== old) {
|
|
121
|
-
setDividerLinewrap(target);
|
|
122
|
-
}
|
|
123
|
-
} else if ('attributes' === entry.type && 'class' === entry.attributeName) {
|
|
124
|
-
const ignoreClassNames = [...(args?.ignore?.className ?? [])];
|
|
125
|
-
|
|
126
|
-
const current = (entry.target.getAttribute(entry.attributeName) ?? '')
|
|
127
|
-
.split(' ')
|
|
128
|
-
.filter((v) => !ignoreClassNames.includes(v))
|
|
129
|
-
.join(' ');
|
|
130
|
-
|
|
131
|
-
const old = (entry.oldValue ?? '')
|
|
132
|
-
.split(' ')
|
|
133
|
-
.filter((v) => !ignoreClassNames.includes(v))
|
|
134
|
-
.join(' ');
|
|
135
|
-
|
|
136
|
-
if (current !== old) {
|
|
137
|
-
setDividerLinewrap(target);
|
|
138
|
-
}
|
|
139
|
-
} else if ('attributes' === entry.type && 'style' === entry.attributeName) {
|
|
140
|
-
setDividerLinewrap(target);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
});
|
|
579
|
+
const old = (entry.oldValue ?? '')
|
|
580
|
+
.split(' ')
|
|
581
|
+
.filter((v) => !ignoreClassNames.includes(v))
|
|
582
|
+
.join(' ');
|
|
145
583
|
|
|
146
|
-
|
|
147
|
-
|
|
584
|
+
return current !== old;
|
|
585
|
+
}
|
|
148
586
|
|
|
149
|
-
|
|
150
|
-
resizeObserver: observer,
|
|
151
|
-
mutationObserver: mObserver,
|
|
587
|
+
return 'style' === entry.attributeName;
|
|
152
588
|
};
|
|
589
|
+
|
|
590
|
+
createLayoutObserver(target, setDividerLinewrap, {
|
|
591
|
+
getResizeValue: getBorderBoxInlineSize,
|
|
592
|
+
observeIntersection: true,
|
|
593
|
+
observeDirectChildrenResize: true,
|
|
594
|
+
targetMutation: {
|
|
595
|
+
options: {
|
|
596
|
+
attributes: true,
|
|
597
|
+
attributeFilter: ['style', 'data-unitone-layout', 'class'],
|
|
598
|
+
attributeOldValue: true,
|
|
599
|
+
},
|
|
600
|
+
shouldApply: (entries) =>
|
|
601
|
+
hasAttributeMutation(
|
|
602
|
+
entries,
|
|
603
|
+
(entry) => entry.target === target && shouldRecalculateByAttributeMutation(entry),
|
|
604
|
+
),
|
|
605
|
+
},
|
|
606
|
+
directChildMutation: {
|
|
607
|
+
attributeFilter: ['style', 'data-unitone-layout', 'class'],
|
|
608
|
+
attributeOldValue: true,
|
|
609
|
+
shouldApply: shouldRecalculateByAttributeMutation,
|
|
610
|
+
},
|
|
611
|
+
});
|
|
153
612
|
};
|
|
154
613
|
|
|
614
|
+
/**
|
|
615
|
+
* Recalculates step values for stairs layouts.
|
|
616
|
+
*
|
|
617
|
+
* @param {Element} target Target element.
|
|
618
|
+
* @returns {void}
|
|
619
|
+
*/
|
|
155
620
|
export const setStairsStep = (target) => {
|
|
156
|
-
const children =
|
|
157
|
-
const
|
|
621
|
+
const children = Array.from(target.children);
|
|
622
|
+
const currentLayoutArray = withoutLayoutTokens(getLayoutTokens(target), ['stairs:initialized']);
|
|
623
|
+
setLayoutTokens(target, currentLayoutArray);
|
|
624
|
+
|
|
625
|
+
const firstChild = children[0];
|
|
158
626
|
if (!firstChild) {
|
|
627
|
+
setLayoutTokens(target, [...currentLayoutArray, 'stairs:initialized']);
|
|
159
628
|
return;
|
|
160
629
|
}
|
|
161
630
|
|
|
162
631
|
// Reset
|
|
163
632
|
target.style.removeProperty('--unitone--stairs-step-overflow-volume');
|
|
164
633
|
target.style.removeProperty('--unitone--max-stairs-step');
|
|
165
|
-
|
|
634
|
+
children.forEach((child) => {
|
|
166
635
|
child.style.removeProperty('--unitone--stairs-step');
|
|
167
636
|
});
|
|
168
637
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
let stairsStep = 0;
|
|
173
|
-
let maxStairsStep = stairsStep;
|
|
638
|
+
if (!hasLayoutBox(target)) {
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
174
641
|
|
|
175
642
|
const stairsUp = (target.getAttribute('data-unitone-layout') ?? '')
|
|
176
643
|
.split(/\s+/)
|
|
@@ -180,22 +647,30 @@ export const setStairsStep = (target) => {
|
|
|
180
647
|
const isAlternatingStairs = ['up-down', 'down-up'].includes(stairsUp);
|
|
181
648
|
|
|
182
649
|
const direction = window.getComputedStyle(target).getPropertyValue('flex-direction');
|
|
183
|
-
|
|
184
|
-
children.
|
|
650
|
+
const targetBottom = target.getBoundingClientRect().bottom;
|
|
651
|
+
const filteredChildren = children.reduce((accumulator, child) => {
|
|
185
652
|
const position = window.getComputedStyle(child).getPropertyValue('position');
|
|
186
653
|
const display = window.getComputedStyle(child).getPropertyValue('display');
|
|
187
654
|
if ('absolute' === position || 'fixed' === position || 'none' === display) {
|
|
188
|
-
return;
|
|
655
|
+
return accumulator;
|
|
189
656
|
}
|
|
190
657
|
|
|
191
|
-
|
|
658
|
+
accumulator.push({
|
|
659
|
+
child,
|
|
660
|
+
rect: child.getBoundingClientRect(),
|
|
661
|
+
});
|
|
662
|
+
return accumulator;
|
|
663
|
+
}, []);
|
|
664
|
+
|
|
665
|
+
let prevRect;
|
|
666
|
+
let stairsStep = 0;
|
|
667
|
+
let maxStairsStep = stairsStep;
|
|
192
668
|
|
|
669
|
+
const nextSteps = filteredChildren.map(({ child, rect }, index) => {
|
|
193
670
|
const isBol =
|
|
194
|
-
'row-reverse' === direction
|
|
195
|
-
? prevChild?.getBoundingClientRect()?.left <= child.getBoundingClientRect().left
|
|
196
|
-
: prevChild?.getBoundingClientRect()?.left >= child.getBoundingClientRect().left;
|
|
671
|
+
'row-reverse' === direction ? prevRect?.left <= rect.left : prevRect?.left >= rect.left;
|
|
197
672
|
|
|
198
|
-
if (firstChild === child || isBol) {
|
|
673
|
+
if (0 === index || (firstChild === child && !prevRect) || isBol) {
|
|
199
674
|
stairsStep = 0;
|
|
200
675
|
} else if (isAlternatingStairs) {
|
|
201
676
|
stairsStep = 0 === stairsStep ? 1 : 0;
|
|
@@ -203,64 +678,106 @@ export const setStairsStep = (target) => {
|
|
|
203
678
|
stairsStep++;
|
|
204
679
|
}
|
|
205
680
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
prevChild = child;
|
|
681
|
+
prevRect = rect;
|
|
209
682
|
|
|
210
683
|
if (stairsStep > maxStairsStep) {
|
|
211
684
|
maxStairsStep = stairsStep;
|
|
212
685
|
}
|
|
686
|
+
|
|
687
|
+
return {
|
|
688
|
+
child,
|
|
689
|
+
stairsStep,
|
|
690
|
+
};
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
nextSteps.forEach(({ child, stairsStep }) => {
|
|
694
|
+
child.style.setProperty('--unitone--stairs-step', stairsStep);
|
|
213
695
|
});
|
|
214
696
|
|
|
215
697
|
target.style.setProperty('--unitone--max-stairs-step', maxStairsStep);
|
|
216
698
|
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
const childBottom = current.getBoundingClientRect().bottom;
|
|
220
|
-
const overflow = childBottom - targetBottom;
|
|
699
|
+
const overflowVolume = filteredChildren.reduce((accumulator, { child }) => {
|
|
700
|
+
const overflow = child.getBoundingClientRect().bottom - targetBottom;
|
|
221
701
|
return accumulator > overflow ? accumulator : overflow;
|
|
222
702
|
}, 0);
|
|
223
703
|
|
|
224
704
|
target.style.setProperty('--unitone--stairs-step-overflow-volume', overflowVolume);
|
|
705
|
+
setLayoutTokens(target, [...currentLayoutArray, 'stairs:initialized']);
|
|
225
706
|
};
|
|
226
707
|
|
|
708
|
+
/**
|
|
709
|
+
* Creates the observer bundle for stairs layouts.
|
|
710
|
+
*
|
|
711
|
+
* @param {Element} target Target element.
|
|
712
|
+
* @returns {void}
|
|
713
|
+
*/
|
|
227
714
|
export const stairsResizeObserver = (target) => {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}, 250),
|
|
234
|
-
);
|
|
715
|
+
createLayoutObserver(target, setStairsStep, {
|
|
716
|
+
observeIntersection: true,
|
|
717
|
+
observeDirectChildrenResize: true,
|
|
718
|
+
});
|
|
719
|
+
};
|
|
235
720
|
|
|
236
|
-
|
|
721
|
+
/**
|
|
722
|
+
* Returns whether the node should be ignored by vertical-writing mutation handling.
|
|
723
|
+
*
|
|
724
|
+
* @param {Node | null | undefined} node Target node.
|
|
725
|
+
* @returns {boolean} Whether the node should be ignored.
|
|
726
|
+
*/
|
|
727
|
+
const isIgnoredVerticalWritingMutationNode = (node) =>
|
|
728
|
+
node?.nodeType === Node.ELEMENT_NODE &&
|
|
729
|
+
[
|
|
730
|
+
'vertical-writing__thresholder',
|
|
731
|
+
'vertical-writing:initialized',
|
|
732
|
+
'vertical-writing:safari',
|
|
733
|
+
].includes(node.getAttribute('data-unitone-layout'));
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* Returns whether vertical-writing mutations require re-application.
|
|
737
|
+
*
|
|
738
|
+
* @param {MutationRecord[]} entries Mutation records.
|
|
739
|
+
* @returns {boolean} Whether re-application is required.
|
|
740
|
+
*/
|
|
741
|
+
const shouldApplyVerticalWritingMutation = (entries) =>
|
|
742
|
+
entries.some((entry) => {
|
|
743
|
+
if ('attributes' === entry.type) {
|
|
744
|
+
return true;
|
|
745
|
+
}
|
|
237
746
|
|
|
238
|
-
|
|
239
|
-
|
|
747
|
+
if ('childList' !== entry.type) {
|
|
748
|
+
return false;
|
|
749
|
+
}
|
|
240
750
|
|
|
751
|
+
return [...entry.addedNodes, ...entry.removedNodes].some(
|
|
752
|
+
(node) => !isIgnoredVerticalWritingMutationNode(node),
|
|
753
|
+
);
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* Recalculates column count and height for vertical-writing layouts.
|
|
758
|
+
*
|
|
759
|
+
* @param {Element} target Target element.
|
|
760
|
+
* @returns {void}
|
|
761
|
+
*/
|
|
241
762
|
export const setColumnCountForVertical = (target) => {
|
|
242
763
|
if (!target) {
|
|
243
764
|
return;
|
|
244
765
|
}
|
|
245
766
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
);
|
|
258
|
-
target.setAttribute('data-unitone-layout', currentLayoutArray.join(' '));
|
|
259
|
-
}
|
|
767
|
+
const currentLayoutTokens = getLayoutTokens(target);
|
|
768
|
+
const baseLayoutTokens = withoutLayoutTokens(currentLayoutTokens, [
|
|
769
|
+
'vertical-writing:safari',
|
|
770
|
+
'-force-switch',
|
|
771
|
+
]);
|
|
772
|
+
const nextLayoutTokens = withoutLayoutTokens(currentLayoutTokens, [
|
|
773
|
+
'vertical-writing:initialized',
|
|
774
|
+
'vertical-writing:safari',
|
|
775
|
+
'-force-switch',
|
|
776
|
+
]);
|
|
777
|
+
setLayoutTokens(target, baseLayoutTokens);
|
|
260
778
|
|
|
261
779
|
let lastChild;
|
|
262
|
-
|
|
263
|
-
.call(target.children)
|
|
780
|
+
Array.from(target.children)
|
|
264
781
|
.reverse()
|
|
265
782
|
.some((child) => {
|
|
266
783
|
if (
|
|
@@ -273,130 +790,92 @@ export const setColumnCountForVertical = (target) => {
|
|
|
273
790
|
});
|
|
274
791
|
|
|
275
792
|
if (!lastChild) {
|
|
276
|
-
|
|
277
|
-
target.setAttribute('data-unitone-layout', currentLayoutArray.join(' '));
|
|
793
|
+
setLayoutTokens(target, [...nextLayoutTokens, 'vertical-writing:initialized']);
|
|
278
794
|
return;
|
|
279
795
|
}
|
|
280
796
|
|
|
281
|
-
// Process of the threshold
|
|
282
797
|
const computedStyle = getComputedStyle(target);
|
|
283
798
|
const threshold = String(computedStyle.getPropertyValue('--unitone--threshold')).trim();
|
|
284
799
|
let forceSwitch = false;
|
|
285
800
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
801
|
+
if (threshold) {
|
|
802
|
+
const thresholder = target.ownerDocument.createElement('div');
|
|
803
|
+
thresholder.setAttribute(layoutAttributeName, 'vertical-writing__thresholder');
|
|
804
|
+
target.appendChild(thresholder);
|
|
805
|
+
forceSwitch = thresholder.offsetWidth >= target.offsetWidth;
|
|
806
|
+
thresholder.remove();
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
if (forceSwitch) {
|
|
810
|
+
nextLayoutTokens.push('-force-switch');
|
|
811
|
+
} else {
|
|
812
|
+
const maybeSafari =
|
|
813
|
+
target.getBoundingClientRect().left > lastChild.getBoundingClientRect().left;
|
|
814
|
+
if (maybeSafari) {
|
|
815
|
+
nextLayoutTokens.push('vertical-writing:safari');
|
|
289
816
|
}
|
|
817
|
+
}
|
|
290
818
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
if (thresholder.parentNode) {
|
|
297
|
-
thresholder.parentNode.removeChild(thresholder);
|
|
298
|
-
}
|
|
819
|
+
setLayoutTokens(target, [...nextLayoutTokens, 'vertical-writing:initialized']);
|
|
820
|
+
|
|
821
|
+
requestAnimationFrame(() => {
|
|
822
|
+
if (!target?.isConnected) {
|
|
823
|
+
return;
|
|
299
824
|
}
|
|
300
825
|
|
|
301
|
-
if (
|
|
302
|
-
|
|
303
|
-
if (target.parentNode?.style) {
|
|
826
|
+
if (target.parentNode?.style) {
|
|
827
|
+
if (forceSwitch) {
|
|
304
828
|
target.parentNode.style.height = '';
|
|
305
|
-
|
|
306
|
-
} else {
|
|
307
|
-
// For Safari
|
|
308
|
-
const maybeSafari =
|
|
309
|
-
target.getBoundingClientRect().left > lastChild.getBoundingClientRect().left;
|
|
310
|
-
if (maybeSafari) {
|
|
311
|
-
currentLayoutArray.push('vertical-writing:safari');
|
|
829
|
+
return;
|
|
312
830
|
}
|
|
313
831
|
|
|
314
|
-
target.
|
|
832
|
+
const targetRect = target.getBoundingClientRect();
|
|
833
|
+
const lastChildRect = lastChild.getBoundingClientRect();
|
|
315
834
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
const targetRect = target.getBoundingClientRect();
|
|
322
|
-
const lastChildRect = lastChild.getBoundingClientRect();
|
|
323
|
-
|
|
324
|
-
const targetY = targetRect.top + targetRect.height;
|
|
325
|
-
const lastChildY = lastChildRect.top + lastChildRect.height;
|
|
326
|
-
if (targetY !== lastChildY) {
|
|
327
|
-
if (target.parentNode?.style) {
|
|
328
|
-
target.parentNode.style.height = `${Math.ceil(lastChildY - targetRect.top)}px`;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
});
|
|
835
|
+
const targetY = targetRect.top + targetRect.height;
|
|
836
|
+
const lastChildY = lastChildRect.top + lastChildRect.height;
|
|
837
|
+
target.parentNode.style.height =
|
|
838
|
+
targetY !== lastChildY ? `${Math.ceil(lastChildY - targetRect.top)}px` : '';
|
|
332
839
|
}
|
|
333
|
-
|
|
334
|
-
currentLayoutArray.push('vertical-writing:initialized');
|
|
335
|
-
target.setAttribute('data-unitone-layout', currentLayoutArray.join(' '));
|
|
336
|
-
}, 250);
|
|
840
|
+
});
|
|
337
841
|
};
|
|
338
842
|
|
|
843
|
+
/**
|
|
844
|
+
* Creates the observer bundle for vertical-writing layouts.
|
|
845
|
+
*
|
|
846
|
+
* @param {Element} target Target element.
|
|
847
|
+
* @returns {void}
|
|
848
|
+
*/
|
|
339
849
|
export const verticalsResizeObserver = (target) => {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
for (const entry of entries) {
|
|
345
|
-
const width = entry.contentRect?.width;
|
|
346
|
-
if (parseInt(width) !== parseInt(prevWidth)) {
|
|
347
|
-
prevWidth = width;
|
|
348
|
-
entry.target.parentNode.style.height = '';
|
|
349
|
-
setColumnCountForVertical(entry.target);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
}, 250),
|
|
353
|
-
);
|
|
354
|
-
|
|
355
|
-
observer.observe(target);
|
|
850
|
+
const applyVerticalColumns = (element) => {
|
|
851
|
+
if (element.parentNode?.style) {
|
|
852
|
+
element.parentNode.style.height = '';
|
|
853
|
+
}
|
|
356
854
|
|
|
357
|
-
|
|
358
|
-
attributes: true,
|
|
359
|
-
attributeFilter: ['style'],
|
|
360
|
-
childList: true,
|
|
361
|
-
subtree: true,
|
|
855
|
+
setColumnCountForVertical(element);
|
|
362
856
|
};
|
|
363
857
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
'vertical-writing:initialized' === addedNode.getAttribute('data-unitone-layout')) ||
|
|
377
|
-
(removedNode?.nodeType === Node.ELEMENT_NODE &&
|
|
378
|
-
'vertical-writing:initialized' === removedNode.getAttribute('data-unitone-layout')) ||
|
|
379
|
-
(addedNode?.nodeType === Node.ELEMENT_NODE &&
|
|
380
|
-
'vertical-writing:safari' === addedNode.getAttribute('data-unitone-layout')) ||
|
|
381
|
-
(removedNode?.nodeType === Node.ELEMENT_NODE &&
|
|
382
|
-
'vertical-writing:safari' === removedNode.getAttribute('data-unitone-layout'))
|
|
383
|
-
) {
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
setColumnCountForVertical(target);
|
|
388
|
-
}
|
|
389
|
-
});
|
|
858
|
+
createLayoutObserver(target, applyVerticalColumns, {
|
|
859
|
+
getResizeValue: getContentRectWidth,
|
|
860
|
+
observeIntersection: true,
|
|
861
|
+
targetMutation: {
|
|
862
|
+
options: {
|
|
863
|
+
attributes: true,
|
|
864
|
+
attributeFilter: ['style'],
|
|
865
|
+
childList: true,
|
|
866
|
+
subtree: true,
|
|
867
|
+
},
|
|
868
|
+
shouldApply: shouldApplyVerticalWritingMutation,
|
|
869
|
+
},
|
|
390
870
|
});
|
|
391
|
-
|
|
392
|
-
mObserver.observe(target, mObserverArgs);
|
|
393
|
-
|
|
394
|
-
return {
|
|
395
|
-
resizeObserver: observer,
|
|
396
|
-
mutationObserver: mObserver,
|
|
397
|
-
};
|
|
398
871
|
};
|
|
399
872
|
|
|
873
|
+
/**
|
|
874
|
+
* Writes the computed 1em value to a CSS custom property for Firefox.
|
|
875
|
+
*
|
|
876
|
+
* @param {HTMLElement} target Target element.
|
|
877
|
+
* @returns {void}
|
|
878
|
+
*/
|
|
400
879
|
export const setResult1emPxForFireFox = (target) => {
|
|
401
880
|
const ownerDocument = target.ownerDocument;
|
|
402
881
|
const defaultView = ownerDocument.defaultView;
|
|
@@ -411,6 +890,12 @@ export const setResult1emPxForFireFox = (target) => {
|
|
|
411
890
|
target.style.setProperty('--unitone--result--1em-px', fontSize);
|
|
412
891
|
};
|
|
413
892
|
|
|
893
|
+
/**
|
|
894
|
+
* Creates the observer bundle for the Firefox 1em measurement workaround.
|
|
895
|
+
*
|
|
896
|
+
* @param {HTMLElement} target Target element.
|
|
897
|
+
* @returns {void}
|
|
898
|
+
*/
|
|
414
899
|
export const result1emPxForFireFoxObserver = (target) => {
|
|
415
900
|
const ownerDocument = target.ownerDocument;
|
|
416
901
|
const defaultView = ownerDocument.defaultView;
|
|
@@ -420,42 +905,28 @@ export const result1emPxForFireFoxObserver = (target) => {
|
|
|
420
905
|
return;
|
|
421
906
|
}
|
|
422
907
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
}
|
|
434
|
-
}, 250),
|
|
435
|
-
);
|
|
436
|
-
|
|
437
|
-
const mObserverArgs = {
|
|
438
|
-
attributes: true,
|
|
439
|
-
attributeFilter: ['style', 'data-unitone-layout', 'class'],
|
|
440
|
-
characterData: true,
|
|
441
|
-
};
|
|
442
|
-
|
|
443
|
-
const mObserver = new MutationObserver(() => {
|
|
444
|
-
requestAnimationFrame(() => {
|
|
445
|
-
setResult1emPxForFireFox(target);
|
|
446
|
-
});
|
|
908
|
+
createLayoutObserver(target, setResult1emPxForFireFox, {
|
|
909
|
+
getResizeValue: getBorderBoxInlineSize,
|
|
910
|
+
targetMutation: {
|
|
911
|
+
options: {
|
|
912
|
+
attributes: true,
|
|
913
|
+
attributeFilter: ['style', 'data-unitone-layout', 'class'],
|
|
914
|
+
characterData: true,
|
|
915
|
+
},
|
|
916
|
+
shouldApply: () => true,
|
|
917
|
+
},
|
|
447
918
|
});
|
|
448
|
-
|
|
449
|
-
observer.observe(target);
|
|
450
|
-
mObserver.observe(target, mObserverArgs);
|
|
451
|
-
|
|
452
|
-
return {
|
|
453
|
-
resizeObserver: observer,
|
|
454
|
-
mutationObserver: mObserver,
|
|
455
|
-
};
|
|
456
919
|
};
|
|
457
920
|
|
|
921
|
+
/**
|
|
922
|
+
* Creates duplicated marquee content and refreshes initialization markers.
|
|
923
|
+
*
|
|
924
|
+
* @param {Element} target Target element.
|
|
925
|
+
* @returns {Element | undefined} The duplicated marquee element, if created.
|
|
926
|
+
*/
|
|
458
927
|
export const setMarquee = (target) => {
|
|
928
|
+
let clonedMarquee;
|
|
929
|
+
|
|
459
930
|
const addInitializedToken = (element) => {
|
|
460
931
|
const layout = element.getAttribute('data-unitone-layout') ?? '';
|
|
461
932
|
if (layout.split(/\s+/).includes('marquee:initialized')) {
|
|
@@ -479,14 +950,26 @@ export const setMarquee = (target) => {
|
|
|
479
950
|
return;
|
|
480
951
|
}
|
|
481
952
|
|
|
953
|
+
const shouldRestartAnimation = Array.from(marquees).some((marquee) =>
|
|
954
|
+
(marquee.getAttribute(layoutAttributeName) ?? '').split(/\s+/).includes('marquee:initialized'),
|
|
955
|
+
);
|
|
956
|
+
|
|
482
957
|
if (1 === target.childElementCount && 1 === marquees.length) {
|
|
483
958
|
const marquee = marquees[0];
|
|
484
|
-
|
|
959
|
+
clonedMarquee = marquee.cloneNode(true);
|
|
485
960
|
clonedMarquee.setAttribute('aria-hidden', 'true');
|
|
486
961
|
marquee.after(clonedMarquee);
|
|
487
962
|
}
|
|
488
963
|
|
|
489
964
|
marquees = getMarquees();
|
|
965
|
+
|
|
966
|
+
if (!shouldRestartAnimation) {
|
|
967
|
+
marquees.forEach((marquee) => {
|
|
968
|
+
addInitializedToken(marquee);
|
|
969
|
+
});
|
|
970
|
+
return clonedMarquee;
|
|
971
|
+
}
|
|
972
|
+
|
|
490
973
|
marquees.forEach((marquee) => {
|
|
491
974
|
removeInitializedToken(marquee);
|
|
492
975
|
});
|
|
@@ -499,44 +982,47 @@ export const setMarquee = (target) => {
|
|
|
499
982
|
addInitializedToken(marquee);
|
|
500
983
|
});
|
|
501
984
|
});
|
|
985
|
+
|
|
986
|
+
return clonedMarquee;
|
|
502
987
|
};
|
|
503
988
|
|
|
989
|
+
/**
|
|
990
|
+
* Creates the observer bundle for marquee layouts.
|
|
991
|
+
*
|
|
992
|
+
* @param {Element} target Target element.
|
|
993
|
+
* @returns {void}
|
|
994
|
+
*/
|
|
504
995
|
export const marqueeResizeObserver = (target) => {
|
|
505
|
-
let
|
|
506
|
-
|
|
507
|
-
setMarquee(target);
|
|
508
|
-
|
|
509
|
-
const observer = new ResizeObserver(
|
|
510
|
-
debounce((entries) => {
|
|
511
|
-
for (const entry of entries) {
|
|
512
|
-
const width = entry.contentRect?.width;
|
|
513
|
-
if (parseInt(width) !== parseInt(prevWidth)) {
|
|
514
|
-
prevWidth = width;
|
|
515
|
-
setMarquee(entry.target);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
}, 250),
|
|
519
|
-
);
|
|
520
|
-
|
|
521
|
-
observer.observe(target);
|
|
996
|
+
let clonedMarquee;
|
|
522
997
|
|
|
523
|
-
const
|
|
524
|
-
|
|
998
|
+
const applyMarquee = (element) => {
|
|
999
|
+
clonedMarquee = setMarquee(element);
|
|
525
1000
|
};
|
|
526
1001
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
1002
|
+
createLayoutObserver(target, applyMarquee, {
|
|
1003
|
+
observeResize: false,
|
|
1004
|
+
observeIntersection: true,
|
|
1005
|
+
targetMutation: {
|
|
1006
|
+
options: {
|
|
1007
|
+
childList: true,
|
|
1008
|
+
},
|
|
1009
|
+
shouldApply: (entries) => {
|
|
1010
|
+
const addedNodes = entries.flatMap((entry) => Array.from(entry.addedNodes ?? []));
|
|
1011
|
+
const removedNodes = entries.flatMap((entry) => Array.from(entry.removedNodes ?? []));
|
|
535
1012
|
|
|
536
|
-
|
|
1013
|
+
if (
|
|
1014
|
+
clonedMarquee?.isConnected &&
|
|
1015
|
+
1 === addedNodes.length &&
|
|
1016
|
+
0 === removedNodes.length &&
|
|
1017
|
+
addedNodes[0] === clonedMarquee
|
|
1018
|
+
) {
|
|
1019
|
+
clonedMarquee = null;
|
|
1020
|
+
return false;
|
|
1021
|
+
}
|
|
537
1022
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
1023
|
+
clonedMarquee = null;
|
|
1024
|
+
return true;
|
|
1025
|
+
},
|
|
1026
|
+
},
|
|
1027
|
+
});
|
|
542
1028
|
};
|