@inc2734/unitone-css 0.94.1 → 0.94.3

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/src/library.js CHANGED
@@ -1,3 +1,69 @@
1
+ const layoutAttributeName = 'data-unitone-layout';
2
+
3
+ const getLayoutTokens = (element) =>
4
+ (element.getAttribute(layoutAttributeName) ?? '').split(/\s+/).filter(Boolean);
5
+
6
+ const withoutLayoutTokens = (tokens, removedTokens) =>
7
+ tokens.filter((value) => !removedTokens.includes(value));
8
+
9
+ const setLayoutTokens = (element, tokens) => {
10
+ const nextValue = tokens.filter(Boolean).join(' ');
11
+ if ((element.getAttribute(layoutAttributeName) ?? '') !== nextValue) {
12
+ element.setAttribute(layoutAttributeName, nextValue);
13
+ }
14
+ };
15
+
16
+ const createResizeObserver = (target, callback, { getValue, delay = 250 } = {}) => {
17
+ callback(target);
18
+
19
+ let prevValue;
20
+ let isFirstEntry = true;
21
+
22
+ const observer = new ResizeObserver(
23
+ debounce((entries) => {
24
+ for (const entry of entries) {
25
+ const currentValue = getValue?.(entry);
26
+ if (isFirstEntry) {
27
+ prevValue = currentValue;
28
+ isFirstEntry = false;
29
+ continue;
30
+ }
31
+
32
+ if (undefined === currentValue || currentValue !== prevValue) {
33
+ callback(entry.target, entry);
34
+ prevValue = currentValue;
35
+ }
36
+ }
37
+ }, delay),
38
+ );
39
+
40
+ observer.observe(target);
41
+
42
+ return observer;
43
+ };
44
+
45
+ const createMutationObserver = (target, options, callback) => {
46
+ const observer = new MutationObserver((entries) => {
47
+ requestAnimationFrame(() => {
48
+ if (!target?.isConnected) {
49
+ return;
50
+ }
51
+
52
+ callback(entries);
53
+ });
54
+ });
55
+
56
+ observer.observe(target, options);
57
+
58
+ return observer;
59
+ };
60
+
61
+ const getBorderBoxInlineSize = (entry) => entry.borderBoxSize?.[0].inlineSize;
62
+
63
+ const getContentRectWidth = (entry) => parseInt(entry.contentRect?.width);
64
+
65
+ const hasLayoutBox = (element) => !!element?.isConnected && 0 < element.getClientRects().length;
66
+
1
67
  export function debounce(fn, delay) {
2
68
  let timer;
3
69
 
@@ -11,85 +77,74 @@ export function debounce(fn, delay) {
11
77
  }
12
78
 
13
79
  export const setDividerLinewrap = (target) => {
14
- const firstChild = [].slice.call(target?.children ?? [])?.[0];
80
+ const children = Array.from(target?.children ?? []);
81
+ const firstChild = children[0];
15
82
  if (!firstChild) {
16
83
  return;
17
84
  }
18
85
 
19
- let prevChild;
20
- const baseRect = firstChild.getBoundingClientRect();
86
+ const currentLayoutArray = withoutLayoutTokens(getLayoutTokens(target), [
87
+ 'divider:initialized',
88
+ '-stack',
89
+ ]);
90
+ setLayoutTokens(target, currentLayoutArray);
21
91
 
22
- let currentLayoutArray = (target.getAttribute('data-unitone-layout') ?? '').split(/\s+/);
23
- if (currentLayoutArray.some((value) => ['divider:initialized', '-stack'].includes(value))) {
24
- currentLayoutArray = currentLayoutArray.filter(
25
- (value) => !['divider:initialized', '-stack'].includes(value),
26
- );
27
- target.setAttribute('data-unitone-layout', currentLayoutArray.join(' '));
92
+ const childLayoutMap = new Map();
93
+ children.forEach((child) => {
94
+ const layoutTokens = withoutLayoutTokens(getLayoutTokens(child), ['-bol', '-linewrap']);
95
+ childLayoutMap.set(child, layoutTokens);
96
+ setLayoutTokens(child, layoutTokens);
97
+ });
98
+
99
+ if (!hasLayoutBox(target)) {
100
+ return;
28
101
  }
29
102
 
30
- const targetChildren = [].slice.call(target.children).filter((child) => {
103
+ const baseRect = firstChild.getBoundingClientRect();
104
+ const targetChildren = children.reduce((accumulator, child) => {
31
105
  const position = window.getComputedStyle(child).getPropertyValue('position');
32
106
  const display = window.getComputedStyle(child).getPropertyValue('display');
33
- return 'absolute' !== position && 'fixed' !== position && 'none' !== display;
34
- });
35
-
36
- targetChildren.forEach((child, index) => {
37
- let childCurrentLayoutArray = (child.getAttribute('data-unitone-layout') ?? '').split(/\s+/);
38
- if (childCurrentLayoutArray.some((value) => ['-bol', '-linewrap'].includes(value))) {
39
- childCurrentLayoutArray = childCurrentLayoutArray.filter(
40
- (value) => !['-bol', '-linewrap'].includes(value),
41
- );
42
- child.setAttribute('data-unitone-layout', childCurrentLayoutArray.join(' '));
107
+ if ('absolute' !== position && 'fixed' !== position && 'none' !== display) {
108
+ accumulator.push({
109
+ child,
110
+ layoutTokens: childLayoutMap.get(child) ?? [],
111
+ rect: child.getBoundingClientRect(),
112
+ });
43
113
  }
114
+ return accumulator;
115
+ }, []);
44
116
 
45
- const prevRect = prevChild?.getBoundingClientRect();
46
- const childRect = child.getBoundingClientRect();
117
+ let prevRect;
118
+ const nextChildLayouts = targetChildren.map(({ child, layoutTokens, rect }, index) => {
119
+ const nextLayoutTokens = [...layoutTokens];
47
120
 
48
- let shouldUpdate = false;
49
-
50
- if (0 === index || (prevRect?.top < childRect.top && prevRect?.left >= childRect.left)) {
51
- childCurrentLayoutArray.push('-bol');
52
- shouldUpdate = true;
121
+ if (0 === index || (prevRect?.top < rect.top && prevRect?.left >= rect.left)) {
122
+ nextLayoutTokens.push('-bol');
53
123
  }
54
124
 
55
- if (0 < index && baseRect.top < childRect.top) {
56
- childCurrentLayoutArray.push('-linewrap');
57
- shouldUpdate = true;
125
+ if (0 < index && baseRect.top < rect.top) {
126
+ nextLayoutTokens.push('-linewrap');
58
127
  }
59
128
 
60
- if (shouldUpdate) {
61
- child.setAttribute('data-unitone-layout', childCurrentLayoutArray.join(' '));
62
- }
129
+ prevRect = rect;
130
+ return { child, layoutTokens: nextLayoutTokens };
131
+ });
63
132
 
64
- prevChild = child;
133
+ nextChildLayouts.forEach(({ child, layoutTokens }) => {
134
+ setLayoutTokens(child, layoutTokens);
65
135
  });
66
136
 
67
- const isStack = [].slice
68
- .call(targetChildren)
69
- .every((child) => child.getBoundingClientRect().left === baseRect.left);
137
+ const isStack = targetChildren.every(({ rect }) => rect.left === baseRect.left);
138
+ const nextTargetLayout = [...currentLayoutArray];
70
139
  if (isStack) {
71
- currentLayoutArray.push('-stack');
140
+ nextTargetLayout.push('-stack');
72
141
  }
73
142
 
74
- currentLayoutArray.push('divider:initialized');
75
- target.setAttribute('data-unitone-layout', currentLayoutArray.join(' '));
143
+ nextTargetLayout.push('divider:initialized');
144
+ setLayoutTokens(target, nextTargetLayout);
76
145
  };
77
146
 
78
147
  export const dividersResizeObserver = (target, args = {}) => {
79
- let prevWidth = 0;
80
-
81
- const observer = new ResizeObserver(
82
- debounce((entries) => {
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
- );
92
-
93
148
  const mObserverArgs = {
94
149
  attributes: true,
95
150
  attributeFilter: ['style', 'data-unitone-layout', 'class'],
@@ -98,54 +153,53 @@ export const dividersResizeObserver = (target, args = {}) => {
98
153
  characterData: true,
99
154
  };
100
155
 
101
- const mObserver = new MutationObserver((entries) => {
102
- requestAnimationFrame(() => {
103
- for (const entry of entries) {
104
- if ('attributes' === entry.type && 'data-unitone-layout' === entry.attributeName) {
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 ?? [])];
156
+ const observer = createResizeObserver(target, setDividerLinewrap, {
157
+ getValue: getBorderBoxInlineSize,
158
+ });
125
159
 
126
- const current = (entry.target.getAttribute(entry.attributeName) ?? '')
127
- .split(' ')
128
- .filter((v) => !ignoreClassNames.includes(v))
129
- .join(' ');
160
+ const mObserver = createMutationObserver(target, mObserverArgs, (entries) => {
161
+ for (const entry of entries) {
162
+ if ('attributes' === entry.type && 'data-unitone-layout' === entry.attributeName) {
163
+ const ignoreUnitoneLayouts = [
164
+ ...(args?.ignore?.layout ?? []),
165
+ ...['divider:initialized', '-bol', '-linewrap', '-stack'],
166
+ ];
167
+
168
+ const current = (entry.target.getAttribute(entry.attributeName) ?? '')
169
+ .split(' ')
170
+ .filter((v) => !ignoreUnitoneLayouts.includes(v))
171
+ .join(' ');
172
+
173
+ const old = (entry.oldValue ?? '')
174
+ .split(' ')
175
+ .filter((v) => !ignoreUnitoneLayouts.includes(v))
176
+ .join(' ');
177
+
178
+ if (current !== old) {
179
+ setDividerLinewrap(target);
180
+ }
181
+ } else if ('attributes' === entry.type && 'class' === entry.attributeName) {
182
+ const ignoreClassNames = [...(args?.ignore?.className ?? [])];
130
183
 
131
- const old = (entry.oldValue ?? '')
132
- .split(' ')
133
- .filter((v) => !ignoreClassNames.includes(v))
134
- .join(' ');
184
+ const current = (entry.target.getAttribute(entry.attributeName) ?? '')
185
+ .split(' ')
186
+ .filter((v) => !ignoreClassNames.includes(v))
187
+ .join(' ');
135
188
 
136
- if (current !== old) {
137
- setDividerLinewrap(target);
138
- }
139
- } else if ('attributes' === entry.type && 'style' === entry.attributeName) {
189
+ const old = (entry.oldValue ?? '')
190
+ .split(' ')
191
+ .filter((v) => !ignoreClassNames.includes(v))
192
+ .join(' ');
193
+
194
+ if (current !== old) {
140
195
  setDividerLinewrap(target);
141
196
  }
197
+ } else if ('attributes' === entry.type && 'style' === entry.attributeName) {
198
+ setDividerLinewrap(target);
142
199
  }
143
- });
200
+ }
144
201
  });
145
202
 
146
- observer.observe(target);
147
- mObserver.observe(target, mObserverArgs);
148
-
149
203
  return {
150
204
  resizeObserver: observer,
151
205
  mutationObserver: mObserver,
@@ -153,24 +207,26 @@ export const dividersResizeObserver = (target, args = {}) => {
153
207
  };
154
208
 
155
209
  export const setStairsStep = (target) => {
156
- const children = [].slice.call(target.children);
157
- const firstChild = children?.[0];
210
+ const children = Array.from(target.children);
211
+ const currentLayoutArray = withoutLayoutTokens(getLayoutTokens(target), ['stairs:initialized']);
212
+ setLayoutTokens(target, currentLayoutArray);
213
+
214
+ const firstChild = children[0];
158
215
  if (!firstChild) {
216
+ setLayoutTokens(target, [...currentLayoutArray, 'stairs:initialized']);
159
217
  return;
160
218
  }
161
219
 
162
220
  // Reset
163
221
  target.style.removeProperty('--unitone--stairs-step-overflow-volume');
164
222
  target.style.removeProperty('--unitone--max-stairs-step');
165
- [].slice.call(target?.children ?? []).forEach((child) => {
223
+ children.forEach((child) => {
166
224
  child.style.removeProperty('--unitone--stairs-step');
167
225
  });
168
226
 
169
- const filteredChildren = [];
170
-
171
- let prevChild;
172
- let stairsStep = 0;
173
- let maxStairsStep = stairsStep;
227
+ if (!hasLayoutBox(target)) {
228
+ return;
229
+ }
174
230
 
175
231
  const stairsUp = (target.getAttribute('data-unitone-layout') ?? '')
176
232
  .split(/\s+/)
@@ -180,22 +236,30 @@ export const setStairsStep = (target) => {
180
236
  const isAlternatingStairs = ['up-down', 'down-up'].includes(stairsUp);
181
237
 
182
238
  const direction = window.getComputedStyle(target).getPropertyValue('flex-direction');
183
-
184
- children.forEach((child) => {
239
+ const targetBottom = target.getBoundingClientRect().bottom;
240
+ const filteredChildren = children.reduce((accumulator, child) => {
185
241
  const position = window.getComputedStyle(child).getPropertyValue('position');
186
242
  const display = window.getComputedStyle(child).getPropertyValue('display');
187
243
  if ('absolute' === position || 'fixed' === position || 'none' === display) {
188
- return;
244
+ return accumulator;
189
245
  }
190
246
 
191
- filteredChildren.push(child);
247
+ accumulator.push({
248
+ child,
249
+ rect: child.getBoundingClientRect(),
250
+ });
251
+ return accumulator;
252
+ }, []);
192
253
 
254
+ let prevRect;
255
+ let stairsStep = 0;
256
+ let maxStairsStep = stairsStep;
257
+
258
+ const nextSteps = filteredChildren.map(({ child, rect }, index) => {
193
259
  const isBol =
194
- 'row-reverse' === direction
195
- ? prevChild?.getBoundingClientRect()?.left <= child.getBoundingClientRect().left
196
- : prevChild?.getBoundingClientRect()?.left >= child.getBoundingClientRect().left;
260
+ 'row-reverse' === direction ? prevRect?.left <= rect.left : prevRect?.left >= rect.left;
197
261
 
198
- if (firstChild === child || isBol) {
262
+ if (0 === index || (firstChild === child && !prevRect) || isBol) {
199
263
  stairsStep = 0;
200
264
  } else if (isAlternatingStairs) {
201
265
  stairsStep = 0 === stairsStep ? 1 : 0;
@@ -203,39 +267,35 @@ export const setStairsStep = (target) => {
203
267
  stairsStep++;
204
268
  }
205
269
 
206
- child.style.setProperty('--unitone--stairs-step', stairsStep);
207
-
208
- prevChild = child;
270
+ prevRect = rect;
209
271
 
210
272
  if (stairsStep > maxStairsStep) {
211
273
  maxStairsStep = stairsStep;
212
274
  }
275
+
276
+ return {
277
+ child,
278
+ stairsStep,
279
+ };
280
+ });
281
+
282
+ nextSteps.forEach(({ child, stairsStep }) => {
283
+ child.style.setProperty('--unitone--stairs-step', stairsStep);
213
284
  });
214
285
 
215
286
  target.style.setProperty('--unitone--max-stairs-step', maxStairsStep);
216
287
 
217
- const targetBottom = target.getBoundingClientRect().bottom;
218
- const overflowVolume = filteredChildren.reduce((accumulator, current) => {
219
- const childBottom = current.getBoundingClientRect().bottom;
220
- const overflow = childBottom - targetBottom;
288
+ const overflowVolume = filteredChildren.reduce((accumulator, { child }) => {
289
+ const overflow = child.getBoundingClientRect().bottom - targetBottom;
221
290
  return accumulator > overflow ? accumulator : overflow;
222
291
  }, 0);
223
292
 
224
293
  target.style.setProperty('--unitone--stairs-step-overflow-volume', overflowVolume);
294
+ setLayoutTokens(target, [...currentLayoutArray, 'stairs:initialized']);
225
295
  };
226
296
 
227
297
  export const stairsResizeObserver = (target) => {
228
- const observer = new ResizeObserver(
229
- debounce((entries) => {
230
- for (const entry of entries) {
231
- setStairsStep(entry.target);
232
- }
233
- }, 250),
234
- );
235
-
236
- observer.observe(target);
237
-
238
- return observer;
298
+ return createResizeObserver(target, setStairsStep);
239
299
  };
240
300
 
241
301
  export const setColumnCountForVertical = (target) => {
@@ -420,34 +480,19 @@ export const result1emPxForFireFoxObserver = (target) => {
420
480
  return;
421
481
  }
422
482
 
423
- let prevWidth = 0;
424
-
425
- const observer = new ResizeObserver(
426
- debounce((entries) => {
427
- for (const entry of entries) {
428
- const width = entry.borderBoxSize?.[0].inlineSize;
429
- if (width !== prevWidth) {
430
- setResult1emPxForFireFox(entry.target);
431
- prevWidth = width;
432
- }
433
- }
434
- }, 250),
435
- );
436
-
437
483
  const mObserverArgs = {
438
484
  attributes: true,
439
485
  attributeFilter: ['style', 'data-unitone-layout', 'class'],
440
486
  characterData: true,
441
487
  };
442
488
 
443
- const mObserver = new MutationObserver(() => {
444
- requestAnimationFrame(() => {
445
- setResult1emPxForFireFox(target);
446
- });
489
+ const observer = createResizeObserver(target, setResult1emPxForFireFox, {
490
+ getValue: getBorderBoxInlineSize,
447
491
  });
448
492
 
449
- observer.observe(target);
450
- mObserver.observe(target, mObserverArgs);
493
+ const mObserver = createMutationObserver(target, mObserverArgs, () => {
494
+ setResult1emPxForFireFox(target);
495
+ });
451
496
 
452
497
  return {
453
498
  resizeObserver: observer,
@@ -456,6 +501,8 @@ export const result1emPxForFireFoxObserver = (target) => {
456
501
  };
457
502
 
458
503
  export const setMarquee = (target) => {
504
+ let clonedMarquee;
505
+
459
506
  const addInitializedToken = (element) => {
460
507
  const layout = element.getAttribute('data-unitone-layout') ?? '';
461
508
  if (layout.split(/\s+/).includes('marquee:initialized')) {
@@ -481,7 +528,7 @@ export const setMarquee = (target) => {
481
528
 
482
529
  if (1 === target.childElementCount && 1 === marquees.length) {
483
530
  const marquee = marquees[0];
484
- const clonedMarquee = marquee.cloneNode(true);
531
+ clonedMarquee = marquee.cloneNode(true);
485
532
  clonedMarquee.setAttribute('aria-hidden', 'true');
486
533
  marquee.after(clonedMarquee);
487
534
  }
@@ -499,41 +546,46 @@ export const setMarquee = (target) => {
499
546
  addInitializedToken(marquee);
500
547
  });
501
548
  });
549
+
550
+ return clonedMarquee;
502
551
  };
503
552
 
504
553
  export const marqueeResizeObserver = (target) => {
505
- let prevWidth = 0;
554
+ let clonedMarquee;
506
555
 
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
- );
556
+ const applyMarquee = (element) => {
557
+ clonedMarquee = setMarquee(element);
558
+ };
520
559
 
521
- observer.observe(target);
560
+ const observer = createResizeObserver(target, applyMarquee, {
561
+ getValue: getContentRectWidth,
562
+ });
522
563
 
523
564
  const mObserverArgs = {
524
565
  childList: true,
525
566
  };
526
567
 
527
- const mObserver = new MutationObserver(() => {
528
- requestAnimationFrame(() => {
529
- if (!target?.isConnected) {
530
- return;
531
- }
532
- setMarquee(target);
533
- });
534
- });
568
+ const mObserver = createMutationObserver(target, mObserverArgs, (entries) => {
569
+ if (!target?.isConnected) {
570
+ return;
571
+ }
535
572
 
536
- mObserver.observe(target, mObserverArgs);
573
+ const addedNodes = entries.flatMap((entry) => Array.from(entry.addedNodes ?? []));
574
+ const removedNodes = entries.flatMap((entry) => Array.from(entry.removedNodes ?? []));
575
+
576
+ if (
577
+ clonedMarquee?.isConnected &&
578
+ 1 === addedNodes.length &&
579
+ 0 === removedNodes.length &&
580
+ addedNodes[0] === clonedMarquee
581
+ ) {
582
+ clonedMarquee = null;
583
+ return;
584
+ }
585
+
586
+ clonedMarquee = null;
587
+ applyMarquee(target);
588
+ });
537
589
 
538
590
  return {
539
591
  resizeObserver: observer,