@design-edito/tools 0.5.30 → 0.5.32
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/agnostic/arrays/index.d.ts +2 -2
- package/agnostic/arrays/index.js +2 -2
- package/agnostic/colors/index.d.ts +2 -2
- package/agnostic/colors/index.js +2 -2
- package/agnostic/errors/index.d.ts +1 -1
- package/agnostic/errors/index.js +1 -1
- package/agnostic/html/hyper-json/smart-tags/coalesced/index.d.ts +9 -9
- package/agnostic/html/hyper-json/smart-tags/coalesced/index.js +9 -9
- package/agnostic/html/hyper-json/smart-tags/isolated/index.d.ts +2 -2
- package/agnostic/html/hyper-json/smart-tags/isolated/index.js +2 -2
- package/agnostic/html/index.d.ts +2 -2
- package/agnostic/html/index.js +2 -2
- package/agnostic/index.d.ts +3 -3
- package/agnostic/index.js +3 -3
- package/agnostic/misc/index.d.ts +3 -3
- package/agnostic/misc/index.js +3 -3
- package/agnostic/misc/logs/index.d.ts +1 -1
- package/agnostic/misc/logs/index.js +1 -1
- package/agnostic/numbers/index.d.ts +1 -1
- package/agnostic/numbers/index.js +1 -1
- package/agnostic/objects/index.d.ts +2 -2
- package/agnostic/objects/index.js +2 -2
- package/agnostic/optim/index.d.ts +1 -1
- package/agnostic/optim/index.js +1 -1
- package/agnostic/random/index.d.ts +1 -1
- package/agnostic/random/index.js +1 -1
- package/agnostic/strings/index.d.ts +2 -2
- package/agnostic/strings/index.js +2 -2
- package/agnostic/time/dates/index.d.ts +1 -1
- package/agnostic/time/dates/index.js +1 -1
- package/agnostic/time/index.d.ts +1 -1
- package/agnostic/time/index.js +1 -1
- package/components/Gallery/index.d.ts +45 -41
- package/components/Gallery/index.js +89 -50
- package/components/Gallery/utils.d.ts +36 -1
- package/components/Gallery/utils.js +57 -15
- package/components/Sequencer/index.js +2 -25
- package/components/index.d.ts +6 -6
- package/components/index.js +6 -6
- package/node/@aws-s3/storage/directory/index.d.ts +1 -1
- package/node/@aws-s3/storage/directory/index.js +1 -1
- package/node/@aws-s3/storage/file/index.d.ts +1 -1
- package/node/@aws-s3/storage/file/index.js +1 -1
- package/node/@google-cloud/storage/directory/index.d.ts +2 -2
- package/node/@google-cloud/storage/directory/index.js +2 -2
- package/node/@google-cloud/storage/file/index.d.ts +2 -2
- package/node/@google-cloud/storage/file/index.js +2 -2
- package/node/cloud-storage/operations/index.d.ts +3 -3
- package/node/cloud-storage/operations/index.js +3 -3
- package/node/files/index.d.ts +1 -1
- package/node/files/index.js +1 -1
- package/node/ftps/file/index.d.ts +1 -1
- package/node/ftps/file/index.js +1 -1
- package/node/images/index.d.ts +1 -1
- package/node/images/index.js +1 -1
- package/node/images/transform/operations/index.d.ts +5 -5
- package/node/images/transform/operations/index.js +5 -5
- package/node/index.d.ts +3 -3
- package/node/index.js +3 -3
- package/node/sftp/index.d.ts +1 -1
- package/node/sftp/index.js +1 -1
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Children,
|
|
2
|
+
import { Children, useCallback, useEffect, useRef, useState } from 'react';
|
|
3
3
|
import { clss } from '../../agnostic/css/clss/index.js';
|
|
4
4
|
import { mergeClassNames, useChangeDispatch } from '../utils/index.js';
|
|
5
5
|
import { gallery as publicClassName } from '../public-classnames.js';
|
|
6
|
-
import {
|
|
6
|
+
import { EMPTY_GEOMETRY, measureGeometry, readActiveIndex, readReach } from './utils.js';
|
|
7
7
|
import cssModule from './styles.module.css';
|
|
8
8
|
/** Resolves one side's padding to a CSS length, falling back to the shorthand then to `0px`. */
|
|
9
9
|
function resolvePadding(side, shorthand) {
|
|
@@ -11,108 +11,149 @@ function resolvePadding(side, shorthand) {
|
|
|
11
11
|
return typeof value === 'number' ? `${value}px` : value;
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
|
-
* Horizontally scrollable gallery
|
|
14
|
+
* Horizontally scrollable gallery, with navigation controls and pagination.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
* and
|
|
16
|
+
* Geometry is measured **once per layout change** into a table of scroll positions, one
|
|
17
|
+
* per slot, and the boundaries between them. A scroll event then only has to place a
|
|
18
|
+
* number between two others — no element is measured while the reader scrolls.
|
|
19
|
+
*
|
|
20
|
+
* The same table is what the controls scroll to, so reading the active slot and moving
|
|
21
|
+
* to one cannot disagree, with or without snapping.
|
|
22
|
+
*
|
|
23
|
+
* ### CSS elements
|
|
24
|
+
* - `scroller`
|
|
25
|
+
* - `slot`, `slot--active`
|
|
26
|
+
* - `actions`, `prev`, `next`
|
|
27
|
+
* - `pagination`, `page`, `page--active`
|
|
28
|
+
*
|
|
29
|
+
* Root modifiers: `controlled`, `no-snap`, `measured`, `at-first`, `at-last`,
|
|
30
|
+
* `can-go-left`, `can-go-right`.
|
|
18
31
|
*
|
|
19
32
|
* @param props - Component properties.
|
|
20
33
|
* @see {@link Props}
|
|
21
|
-
* @returns A container
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
34
|
+
* @returns A container wrapping the scroller, the two controls and the pagination.
|
|
35
|
+
*
|
|
36
|
+
* @remarks
|
|
37
|
+
* Nothing is measured while the reader scrolls: the table is rebuilt only when a box
|
|
38
|
+
* changes size or a slot is added, watched through a `ResizeObserver`. Until the first
|
|
39
|
+
* measurement lands, the root carries no `measured` modifier and every slot position
|
|
40
|
+
* reads as `0`.
|
|
25
41
|
*/
|
|
26
42
|
export const Gallery = ({ paddingLeft, paddingRight, padding, prevButtonContent, nextButtonContent, paginationContent, defaultActive, active, noSnap, onPrevClicked, onNextClicked, onPaginationClicked, onActiveSlotChanged, onCanGoLeftChanged, onCanGoRightChanged, children, className }) => {
|
|
27
43
|
// State & refs
|
|
28
44
|
const scrollerRef = useRef(null);
|
|
29
|
-
const
|
|
45
|
+
const geometryRef = useRef(EMPTY_GEOMETRY);
|
|
46
|
+
const [scrolledIndex, setScrolledIndex] = useState(0);
|
|
47
|
+
const [isMeasured, setIsMeasured] = useState(false);
|
|
30
48
|
const [canGoLeft, setCanGoLeft] = useState(false);
|
|
31
49
|
const [canGoRight, setCanGoRight] = useState(false);
|
|
32
50
|
const childrenCount = Children.count(children);
|
|
33
51
|
const isControlled = active !== undefined;
|
|
52
|
+
// Controlled, the prop is the answer: the class names and `data-active` say where the
|
|
53
|
+
// gallery was told to be, rather than where a smooth scroll has got to so far.
|
|
54
|
+
const activeIndex = active ?? scrolledIndex;
|
|
34
55
|
// State dispatch
|
|
35
56
|
useChangeDispatch(activeIndex, onActiveSlotChanged);
|
|
36
57
|
useChangeDispatch(canGoLeft, onCanGoLeftChanged);
|
|
37
58
|
useChangeDispatch(canGoRight, onCanGoRightChanged);
|
|
59
|
+
const readScroll = useCallback(() => {
|
|
60
|
+
const scrollerElt = scrollerRef.current;
|
|
61
|
+
if (scrollerElt === null)
|
|
62
|
+
return;
|
|
63
|
+
const { scrollLeft } = scrollerElt;
|
|
64
|
+
const geometry = geometryRef.current;
|
|
65
|
+
setScrolledIndex(readActiveIndex(geometry, scrollLeft));
|
|
66
|
+
const { canGoLeft, canGoRight } = readReach(geometry, scrollLeft);
|
|
67
|
+
setCanGoLeft(canGoLeft);
|
|
68
|
+
setCanGoRight(canGoRight);
|
|
69
|
+
}, []);
|
|
70
|
+
const measure = useCallback(() => {
|
|
71
|
+
const scrollerElt = scrollerRef.current;
|
|
72
|
+
if (scrollerElt === null)
|
|
73
|
+
return;
|
|
74
|
+
geometryRef.current = measureGeometry(scrollerElt);
|
|
75
|
+
setIsMeasured(true);
|
|
76
|
+
readScroll();
|
|
77
|
+
}, [readScroll]);
|
|
78
|
+
const scrollToSlot = useCallback((pos, smooth = true) => {
|
|
79
|
+
const scrollerElt = scrollerRef.current;
|
|
80
|
+
const offset = geometryRef.current.offsets[pos];
|
|
81
|
+
if (scrollerElt === null || offset === undefined)
|
|
82
|
+
return;
|
|
83
|
+
scrollerElt.scrollTo({ left: offset, behavior: smooth ? 'smooth' : 'instant' });
|
|
84
|
+
}, []);
|
|
38
85
|
// User actions handlers
|
|
39
86
|
const handlePrevClick = () => {
|
|
40
87
|
onPrevClicked?.(activeIndex);
|
|
41
88
|
if (isControlled)
|
|
42
89
|
return;
|
|
43
|
-
|
|
90
|
+
scrollToSlot(activeIndex - 1);
|
|
44
91
|
};
|
|
45
92
|
const handleNextClick = () => {
|
|
46
93
|
onNextClicked?.(activeIndex);
|
|
47
94
|
if (isControlled)
|
|
48
95
|
return;
|
|
49
|
-
|
|
96
|
+
scrollToSlot(activeIndex + 1);
|
|
50
97
|
};
|
|
51
98
|
const handlePaginationClick = (pos) => {
|
|
52
99
|
onPaginationClicked?.(activeIndex, pos);
|
|
53
100
|
if (isControlled)
|
|
54
101
|
return;
|
|
55
|
-
|
|
102
|
+
scrollToSlot(pos);
|
|
56
103
|
};
|
|
57
|
-
//
|
|
104
|
+
// Fx. dep. childrenCount - Measure, and measure again whenever a box changes size.
|
|
105
|
+
// Watching the slots and not only the scroller is what makes a late image or font
|
|
106
|
+
// land correctly, where a delay after mount could only guess.
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
const scrollerElt = scrollerRef.current;
|
|
109
|
+
if (scrollerElt === null)
|
|
110
|
+
return;
|
|
111
|
+
measure();
|
|
112
|
+
const observer = new ResizeObserver(measure);
|
|
113
|
+
observer.observe(scrollerElt);
|
|
114
|
+
Array.from(scrollerElt.children).forEach(child => observer.observe(child));
|
|
115
|
+
return () => observer.disconnect();
|
|
116
|
+
}, [childrenCount, measure]);
|
|
117
|
+
// Fx. no dep. - One read per frame while scrolling, and it touches no element.
|
|
58
118
|
useEffect(() => {
|
|
59
119
|
const scrollerElt = scrollerRef.current;
|
|
60
120
|
if (scrollerElt === null)
|
|
61
121
|
return;
|
|
62
|
-
scrollerElt.scrollBy(-1, 0);
|
|
63
122
|
let animationFrame = null;
|
|
64
|
-
const update = () => {
|
|
65
|
-
animationFrame = null;
|
|
66
|
-
const { scrollLeft, clientWidth, scrollWidth } = scrollerElt;
|
|
67
|
-
const center = scrollLeft + clientWidth / 2;
|
|
68
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- scrollerElt.children (HTMLCollection) always contains Element/HTMLElement nodes
|
|
69
|
-
const children = Array.from(scrollerElt.children);
|
|
70
|
-
let closestIndex = 0;
|
|
71
|
-
let closestDistance = Infinity;
|
|
72
|
-
children.forEach((child, index) => {
|
|
73
|
-
const childCenter = child.offsetLeft + child.offsetWidth / 2;
|
|
74
|
-
const distance = Math.abs(center - childCenter);
|
|
75
|
-
if (distance < closestDistance) {
|
|
76
|
-
closestDistance = distance;
|
|
77
|
-
closestIndex = index;
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
setActiveIndex(closestIndex);
|
|
81
|
-
setCanGoLeft(scrollLeft > 0);
|
|
82
|
-
setCanGoRight(scrollLeft + clientWidth < scrollWidth);
|
|
83
|
-
};
|
|
84
123
|
const onScroll = () => {
|
|
85
124
|
if (animationFrame !== null)
|
|
86
125
|
return;
|
|
87
|
-
animationFrame = requestAnimationFrame(
|
|
126
|
+
animationFrame = requestAnimationFrame(() => {
|
|
127
|
+
animationFrame = null;
|
|
128
|
+
readScroll();
|
|
129
|
+
});
|
|
88
130
|
};
|
|
89
131
|
scrollerElt.addEventListener('scroll', onScroll, { passive: true });
|
|
90
|
-
update();
|
|
91
132
|
return () => {
|
|
92
133
|
scrollerElt.removeEventListener('scroll', onScroll);
|
|
93
134
|
if (animationFrame !== null)
|
|
94
135
|
cancelAnimationFrame(animationFrame);
|
|
95
136
|
};
|
|
96
|
-
}, []);
|
|
97
|
-
//
|
|
137
|
+
}, [readScroll]);
|
|
138
|
+
// Fx. no dep. - Place the gallery where it starts, once the table exists.
|
|
98
139
|
useEffect(() => {
|
|
99
140
|
const toActivate = active ?? defaultActive;
|
|
100
141
|
if (toActivate === undefined)
|
|
101
142
|
return;
|
|
102
|
-
|
|
103
|
-
return () => clearTimeout(id);
|
|
143
|
+
scrollToSlot(toActivate, false);
|
|
104
144
|
}, []);
|
|
105
|
-
//
|
|
145
|
+
// Fx. dep. active - Follow the controlled index.
|
|
106
146
|
useEffect(() => {
|
|
107
147
|
if (active === undefined)
|
|
108
148
|
return;
|
|
109
|
-
|
|
149
|
+
scrollToSlot(active);
|
|
110
150
|
}, [active]);
|
|
111
151
|
// Rendering
|
|
112
152
|
const c = clss(publicClassName, { cssModule });
|
|
113
153
|
const rootClss = mergeClassNames(c(null, {
|
|
114
|
-
'controlled':
|
|
154
|
+
'controlled': isControlled,
|
|
115
155
|
'no-snap': noSnap === true,
|
|
156
|
+
'measured': isMeasured,
|
|
116
157
|
'at-first': activeIndex === 0,
|
|
117
158
|
'at-last': activeIndex === childrenCount - 1,
|
|
118
159
|
'can-go-left': canGoLeft,
|
|
@@ -132,17 +173,15 @@ export const Gallery = ({ paddingLeft, paddingRight, padding, prevButtonContent,
|
|
|
132
173
|
'margin-left': pos === 0
|
|
133
174
|
? actualPaddingLeft
|
|
134
175
|
: undefined,
|
|
135
|
-
'margin-right': pos ===
|
|
176
|
+
'margin-right': pos === childrenCount - 1
|
|
136
177
|
? actualPaddingRight
|
|
137
178
|
: undefined
|
|
138
179
|
};
|
|
139
180
|
return _jsx("div", { className: slotClss, style: { ...style }, "data-slot": pos, children: child }, pos);
|
|
140
181
|
}) }), _jsxs("div", { className: actionsClss, children: [_jsx("div", { className: prevBtnClss, onClick: handlePrevClick, children: prevButtonContent ?? 'prev' }), _jsx("div", { className: nextBtnClss, onClick: handleNextClick, children: nextButtonContent ?? 'next' })] }), _jsx("div", { className: paginationClss, children: Children.map(children, (_, pos) => {
|
|
141
182
|
const pageClss = c('page', { active: pos === activeIndex });
|
|
142
|
-
return _jsx("div", { className: pageClss, "data-page": pos, onClick: () => handlePaginationClick(pos), children: typeof paginationContent === '
|
|
143
|
-
? paginationContent
|
|
144
|
-
:
|
|
145
|
-
? paginationContent(pos)
|
|
146
|
-
: pos });
|
|
183
|
+
return _jsx("div", { className: pageClss, "data-page": pos, onClick: () => handlePaginationClick(pos), children: typeof paginationContent === 'function'
|
|
184
|
+
? paginationContent(pos)
|
|
185
|
+
: paginationContent ?? pos });
|
|
147
186
|
}) })] });
|
|
148
187
|
};
|
|
@@ -1 +1,36 @@
|
|
|
1
|
-
|
|
1
|
+
/** The layout, measured once, so that reading the active slot touches no element. */
|
|
2
|
+
export type Geometry = {
|
|
3
|
+
/** Scroll positions that bring each slot to the middle, clamped to the track. */
|
|
4
|
+
offsets: number[];
|
|
5
|
+
/** Midpoints between consecutive slot centres, in content coordinates. */
|
|
6
|
+
boundaries: number[];
|
|
7
|
+
/** Half the viewport, kept here rather than read again on every scroll. */
|
|
8
|
+
halfViewport: number;
|
|
9
|
+
/** The far end of the track. */
|
|
10
|
+
maxScroll: number;
|
|
11
|
+
};
|
|
12
|
+
export declare const EMPTY_GEOMETRY: Geometry;
|
|
13
|
+
/**
|
|
14
|
+
* Everything the gallery needs to know about its own layout.
|
|
15
|
+
*
|
|
16
|
+
* Positions are taken against the scroller's own box rather than `offsetLeft`, which is
|
|
17
|
+
* relative to whichever ancestor happens to be positioned — so a gallery nested anywhere
|
|
18
|
+
* measures the same.
|
|
19
|
+
*
|
|
20
|
+
* **Boundaries come from slot centres, and scroll positions are clamped.** Keeping them
|
|
21
|
+
* apart is what avoids ties: two slots at the same end of the track share a scroll
|
|
22
|
+
* position, but never a centre, so every slot owns a range of its own.
|
|
23
|
+
*/
|
|
24
|
+
export declare function measureGeometry(scroller: HTMLElement): Geometry;
|
|
25
|
+
/**
|
|
26
|
+
* Which slot the middle of the viewport is nearest — numbers only, whatever the count.
|
|
27
|
+
*
|
|
28
|
+
* Same answer as walking the slots and keeping the closest centre, which is what the
|
|
29
|
+
* table was built from.
|
|
30
|
+
*/
|
|
31
|
+
export declare function readActiveIndex(geometry: Geometry, scrollLeft: number): number;
|
|
32
|
+
/** Whether there is track left on each side, within {@link EDGE_TOLERANCE}. */
|
|
33
|
+
export declare function readReach(geometry: Geometry, scrollLeft: number): {
|
|
34
|
+
canGoLeft: boolean;
|
|
35
|
+
canGoRight: boolean;
|
|
36
|
+
};
|
|
@@ -1,16 +1,58 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const target = children[targetPos];
|
|
9
|
-
const halfClientWidth = div.clientWidth / 2;
|
|
10
|
-
const halfOffsetWidth = div.offsetWidth / 2;
|
|
11
|
-
const offset = target.offsetLeft - (halfClientWidth - halfOffsetWidth);
|
|
12
|
-
div.scrollTo({
|
|
13
|
-
left: offset,
|
|
14
|
-
behavior: smooth ? 'smooth' : 'instant'
|
|
15
|
-
});
|
|
1
|
+
/** A tolerance in px, so a sub-pixel scroll position does not read as « can still go ». */
|
|
2
|
+
const EDGE_TOLERANCE = 1;
|
|
3
|
+
export const EMPTY_GEOMETRY = {
|
|
4
|
+
offsets: [],
|
|
5
|
+
boundaries: [],
|
|
6
|
+
halfViewport: 0,
|
|
7
|
+
maxScroll: 0
|
|
16
8
|
};
|
|
9
|
+
/**
|
|
10
|
+
* Everything the gallery needs to know about its own layout.
|
|
11
|
+
*
|
|
12
|
+
* Positions are taken against the scroller's own box rather than `offsetLeft`, which is
|
|
13
|
+
* relative to whichever ancestor happens to be positioned — so a gallery nested anywhere
|
|
14
|
+
* measures the same.
|
|
15
|
+
*
|
|
16
|
+
* **Boundaries come from slot centres, and scroll positions are clamped.** Keeping them
|
|
17
|
+
* apart is what avoids ties: two slots at the same end of the track share a scroll
|
|
18
|
+
* position, but never a centre, so every slot owns a range of its own.
|
|
19
|
+
*/
|
|
20
|
+
export function measureGeometry(scroller) {
|
|
21
|
+
const { clientWidth, scrollWidth, scrollLeft } = scroller;
|
|
22
|
+
const halfViewport = clientWidth / 2;
|
|
23
|
+
const maxScroll = Math.max(0, scrollWidth - clientWidth);
|
|
24
|
+
const scrollerLeft = scroller.getBoundingClientRect().left;
|
|
25
|
+
const centers = Array.from(scroller.children).map(child => {
|
|
26
|
+
const rect = child.getBoundingClientRect();
|
|
27
|
+
return rect.left - scrollerLeft + scrollLeft + rect.width / 2;
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
offsets: centers.map(center => Math.min(Math.max(center - halfViewport, 0), maxScroll)),
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- i is in range by construction, centers[i] sits before the one being mapped
|
|
32
|
+
boundaries: centers.slice(1).map((center, i) => (centers[i] + center) / 2),
|
|
33
|
+
halfViewport,
|
|
34
|
+
maxScroll
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Which slot the middle of the viewport is nearest — numbers only, whatever the count.
|
|
39
|
+
*
|
|
40
|
+
* Same answer as walking the slots and keeping the closest centre, which is what the
|
|
41
|
+
* table was built from.
|
|
42
|
+
*/
|
|
43
|
+
export function readActiveIndex(geometry, scrollLeft) {
|
|
44
|
+
const { boundaries, halfViewport } = geometry;
|
|
45
|
+
const viewportCenter = scrollLeft + halfViewport;
|
|
46
|
+
let index = 0;
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- index is bounded by the loop condition
|
|
48
|
+
while (index < boundaries.length && viewportCenter > boundaries[index])
|
|
49
|
+
index += 1;
|
|
50
|
+
return index;
|
|
51
|
+
}
|
|
52
|
+
/** Whether there is track left on each side, within {@link EDGE_TOLERANCE}. */
|
|
53
|
+
export function readReach(geometry, scrollLeft) {
|
|
54
|
+
return {
|
|
55
|
+
canGoLeft: scrollLeft > EDGE_TOLERANCE,
|
|
56
|
+
canGoRight: scrollLeft < geometry.maxScroll - EDGE_TOLERANCE
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Children, cloneElement,
|
|
2
|
+
import { Children, cloneElement, isValidElement, useEffect, useRef, useState } from 'react';
|
|
3
3
|
import { absoluteModulo } from '../../agnostic/numbers/absolute-modulo/index.js';
|
|
4
4
|
import { clamp } from '../../agnostic/numbers/clamp/index.js';
|
|
5
5
|
import { clss } from '../../agnostic/css/clss/index.js';
|
|
@@ -55,29 +55,6 @@ function parseSteps(raw) {
|
|
|
55
55
|
return null;
|
|
56
56
|
return steps;
|
|
57
57
|
}
|
|
58
|
-
/**
|
|
59
|
-
* The children, with fragments opened.
|
|
60
|
-
*
|
|
61
|
-
* **`Children.toArray` does not go through a `<>…</>`**: a fragment is one element to it,
|
|
62
|
-
* and one that cannot be classed either — `cloneElement` on it would hand `className` to
|
|
63
|
-
* `React.Fragment`, which warns and drops it. So a consumer whose children arrive wrapped
|
|
64
|
-
* in one gets a sequence of exactly one step, stuck on position `0`.
|
|
65
|
-
*
|
|
66
|
-
* That consumer is not hypothetical: lm-link renders an article's `<nodelist>` through
|
|
67
|
-
* `lm-html`, which returns `<>{children}</>` — every sequence written in hyper-json
|
|
68
|
-
* therefore arrives wrapped. Opening them here rather than asking each consumer to
|
|
69
|
-
* flatten is the only place the fix belongs: a fragment is a grouping with no rendered
|
|
70
|
-
* element of its own, so it has no business consuming a step.
|
|
71
|
-
*/
|
|
72
|
-
function flattenChildren(nodes) {
|
|
73
|
-
return Children.toArray(nodes).flatMap((child) => {
|
|
74
|
-
if (!isValidElement(child) || child.type !== Fragment)
|
|
75
|
-
return [child];
|
|
76
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- a fragment's props are its children and its key, whatever else they are
|
|
77
|
-
const { children } = child.props;
|
|
78
|
-
return flattenChildren(children);
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
58
|
/**
|
|
82
59
|
* Whether a child carries {@link LAST_STEP_ATTRIBUTE}.
|
|
83
60
|
*
|
|
@@ -177,7 +154,7 @@ export const Sequencer = ({ totalSteps, stepMap, step, defaultStep, play, defaul
|
|
|
177
154
|
const [hasLapped, setHasLapped] = useState(0);
|
|
178
155
|
const rootRef = useRef(null);
|
|
179
156
|
// Children, split once: what takes part and what merely renders.
|
|
180
|
-
const childrenArr =
|
|
157
|
+
const childrenArr = Children.toArray(children);
|
|
181
158
|
const elements = childrenArr.filter(isValidElement);
|
|
182
159
|
// Three answers to « how long is the sequence », in decreasing order of explicitness:
|
|
183
160
|
// the prop, then the child that says the sequence ends on it, then the count of
|
package/components/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export * as beforeAfter from './BeforeAfter/index.js'
|
|
2
1
|
export * as button from './Button/index.js'
|
|
3
2
|
export * as clippable from './Clippable/index.js'
|
|
3
|
+
export * as beforeAfter from './BeforeAfter/index.js'
|
|
4
4
|
export * as disclaimer from './Disclaimer/index.js'
|
|
5
5
|
export * as drawer from './Drawer/index.js'
|
|
6
6
|
export * as eventListener from './EventListener/index.js'
|
|
@@ -9,21 +9,21 @@ export * as iframe from './Iframe/index.js'
|
|
|
9
9
|
export * as image from './Image/index.js'
|
|
10
10
|
export * as input from './Input/index.js'
|
|
11
11
|
export * as intersectionObserver from './IntersectionObserver/index.js'
|
|
12
|
-
export * as jsonEditor from './JsonEditor/index.js'
|
|
13
12
|
export * as lightbox from './Lightbox/index.js'
|
|
13
|
+
export * as jsonEditor from './JsonEditor/index.js'
|
|
14
14
|
export * as listLoader from './ListLoader/index.js'
|
|
15
15
|
export * as overlayer from './Overlayer/index.js'
|
|
16
|
-
export * as paginator from './Paginator/index.js'
|
|
17
|
-
export * as resizeObserver from './ResizeObserver/index.js'
|
|
18
16
|
export * as scrllgngn from './Scrllgngn/index.js'
|
|
19
|
-
export * as select from './Select/index.js'
|
|
20
17
|
export * as scrollListener from './ScrollListener/index.js'
|
|
18
|
+
export * as resizeObserver from './ResizeObserver/index.js'
|
|
19
|
+
export * as paginator from './Paginator/index.js'
|
|
20
|
+
export * as select from './Select/index.js'
|
|
21
21
|
export * as sequencer from './Sequencer/index.js'
|
|
22
22
|
export * as shadowRoot from './ShadowRoot/index.js'
|
|
23
23
|
export * as subtitles from './Subtitles/index.js'
|
|
24
|
-
export * as textarea from './Textarea/index.js'
|
|
25
24
|
export * as uiModule from './UIModule/index.js'
|
|
26
25
|
export * as video from './Video/index.js'
|
|
26
|
+
export * as textarea from './Textarea/index.js'
|
|
27
27
|
export * as wipAudioQuote from './_WIP_AudioQuote/index.js'
|
|
28
28
|
export * as wipIcon from './_WIP_Icon/index.js'
|
|
29
29
|
export * as utils from './utils/index.js'
|
package/components/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export * as beforeAfter from './BeforeAfter/index.js'
|
|
2
1
|
export * as button from './Button/index.js'
|
|
3
2
|
export * as clippable from './Clippable/index.js'
|
|
3
|
+
export * as beforeAfter from './BeforeAfter/index.js'
|
|
4
4
|
export * as disclaimer from './Disclaimer/index.js'
|
|
5
5
|
export * as drawer from './Drawer/index.js'
|
|
6
6
|
export * as eventListener from './EventListener/index.js'
|
|
@@ -9,21 +9,21 @@ export * as iframe from './Iframe/index.js'
|
|
|
9
9
|
export * as image from './Image/index.js'
|
|
10
10
|
export * as input from './Input/index.js'
|
|
11
11
|
export * as intersectionObserver from './IntersectionObserver/index.js'
|
|
12
|
-
export * as jsonEditor from './JsonEditor/index.js'
|
|
13
12
|
export * as lightbox from './Lightbox/index.js'
|
|
13
|
+
export * as jsonEditor from './JsonEditor/index.js'
|
|
14
14
|
export * as listLoader from './ListLoader/index.js'
|
|
15
15
|
export * as overlayer from './Overlayer/index.js'
|
|
16
|
-
export * as paginator from './Paginator/index.js'
|
|
17
|
-
export * as resizeObserver from './ResizeObserver/index.js'
|
|
18
16
|
export * as scrllgngn from './Scrllgngn/index.js'
|
|
19
|
-
export * as select from './Select/index.js'
|
|
20
17
|
export * as scrollListener from './ScrollListener/index.js'
|
|
18
|
+
export * as resizeObserver from './ResizeObserver/index.js'
|
|
19
|
+
export * as paginator from './Paginator/index.js'
|
|
20
|
+
export * as select from './Select/index.js'
|
|
21
21
|
export * as sequencer from './Sequencer/index.js'
|
|
22
22
|
export * as shadowRoot from './ShadowRoot/index.js'
|
|
23
23
|
export * as subtitles from './Subtitles/index.js'
|
|
24
|
-
export * as textarea from './Textarea/index.js'
|
|
25
24
|
export * as uiModule from './UIModule/index.js'
|
|
26
25
|
export * as video from './Video/index.js'
|
|
26
|
+
export * as textarea from './Textarea/index.js'
|
|
27
27
|
export * as wipAudioQuote from './_WIP_AudioQuote/index.js'
|
|
28
28
|
export * as wipIcon from './_WIP_Icon/index.js'
|
|
29
29
|
export * as utils from './utils/index.js'
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * as copy from './copy/index.js'
|
|
2
2
|
export * as download from './download/index.js'
|
|
3
3
|
export * as exists from './exists/index.js'
|
|
4
|
-
export * as move from './move/index.js'
|
|
5
4
|
export * as remove from './remove/index.js'
|
|
6
5
|
export * as stat from './stat/index.js'
|
|
6
|
+
export * as move from './move/index.js'
|
|
7
7
|
export * as upload from './upload/index.js'
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * as copy from './copy/index.js'
|
|
2
2
|
export * as download from './download/index.js'
|
|
3
3
|
export * as exists from './exists/index.js'
|
|
4
|
-
export * as move from './move/index.js'
|
|
5
4
|
export * as remove from './remove/index.js'
|
|
6
5
|
export * as stat from './stat/index.js'
|
|
6
|
+
export * as move from './move/index.js'
|
|
7
7
|
export * as upload from './upload/index.js'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
+
export * as list from './list/index.js'
|
|
2
|
+
export * as copyDir from './copy-dir/index.js'
|
|
1
3
|
export * as moveDir from './move-dir/index.js'
|
|
2
4
|
export * as removeDir from './remove-dir/index.js'
|
|
3
|
-
export * as copyDir from './copy-dir/index.js'
|
|
4
|
-
export * as list from './list/index.js'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
+
export * as list from './list/index.js'
|
|
2
|
+
export * as copyDir from './copy-dir/index.js'
|
|
1
3
|
export * as moveDir from './move-dir/index.js'
|
|
2
4
|
export * as removeDir from './remove-dir/index.js'
|
|
3
|
-
export * as copyDir from './copy-dir/index.js'
|
|
4
|
-
export * as list from './list/index.js'
|
|
@@ -2,10 +2,10 @@ export * as copy from './copy/index.js'
|
|
|
2
2
|
export * as download from './download/index.js'
|
|
3
3
|
export * as exists from './exists/index.js'
|
|
4
4
|
export * as generateSignedUrl from './generate-signed-url/index.js'
|
|
5
|
-
export * as getMetadata from './get-metadata/index.js'
|
|
6
5
|
export * as getPermissions from './get-permissions/index.js'
|
|
7
|
-
export * as
|
|
6
|
+
export * as getMetadata from './get-metadata/index.js'
|
|
8
7
|
export * as remove from './remove/index.js'
|
|
8
|
+
export * as move from './move/index.js'
|
|
9
9
|
export * as revokeSignedUrls from './revoke-signed-urls/index.js'
|
|
10
10
|
export * as stat from './stat/index.js'
|
|
11
11
|
export * as updateMetadata from './update-metadata/index.js'
|
|
@@ -2,10 +2,10 @@ export * as copy from './copy/index.js'
|
|
|
2
2
|
export * as download from './download/index.js'
|
|
3
3
|
export * as exists from './exists/index.js'
|
|
4
4
|
export * as generateSignedUrl from './generate-signed-url/index.js'
|
|
5
|
-
export * as getMetadata from './get-metadata/index.js'
|
|
6
5
|
export * as getPermissions from './get-permissions/index.js'
|
|
7
|
-
export * as
|
|
6
|
+
export * as getMetadata from './get-metadata/index.js'
|
|
8
7
|
export * as remove from './remove/index.js'
|
|
8
|
+
export * as move from './move/index.js'
|
|
9
9
|
export * as revokeSignedUrls from './revoke-signed-urls/index.js'
|
|
10
10
|
export * as stat from './stat/index.js'
|
|
11
11
|
export * as updateMetadata from './update-metadata/index.js'
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export * as copyDir from './copy-dir/index.js'
|
|
2
1
|
export * as copyFile from './copy-file/index.js'
|
|
2
|
+
export * as copyDir from './copy-dir/index.js'
|
|
3
3
|
export * as downloadFile from './download-file/index.js'
|
|
4
4
|
export * as existsFile from './exists-file/index.js'
|
|
5
5
|
export * as listDir from './list-dir/index.js'
|
|
6
6
|
export * as moveDir from './move-dir/index.js'
|
|
7
|
-
export * as moveFile from './move-file/index.js'
|
|
8
7
|
export * as removeDir from './remove-dir/index.js'
|
|
9
8
|
export * as removeFile from './remove-file/index.js'
|
|
10
|
-
export * as uploadFile from './upload-file/index.js'
|
|
11
9
|
export * as statFile from './stat-file/index.js'
|
|
10
|
+
export * as uploadFile from './upload-file/index.js'
|
|
11
|
+
export * as moveFile from './move-file/index.js'
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export * as copyDir from './copy-dir/index.js'
|
|
2
1
|
export * as copyFile from './copy-file/index.js'
|
|
2
|
+
export * as copyDir from './copy-dir/index.js'
|
|
3
3
|
export * as downloadFile from './download-file/index.js'
|
|
4
4
|
export * as existsFile from './exists-file/index.js'
|
|
5
5
|
export * as listDir from './list-dir/index.js'
|
|
6
6
|
export * as moveDir from './move-dir/index.js'
|
|
7
|
-
export * as moveFile from './move-file/index.js'
|
|
8
7
|
export * as removeDir from './remove-dir/index.js'
|
|
9
8
|
export * as removeFile from './remove-file/index.js'
|
|
10
|
-
export * as uploadFile from './upload-file/index.js'
|
|
11
9
|
export * as statFile from './stat-file/index.js'
|
|
10
|
+
export * as uploadFile from './upload-file/index.js'
|
|
11
|
+
export * as moveFile from './move-file/index.js'
|
package/node/files/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export * as getSize from './get-size/index.js'
|
|
2
|
-
export * as isInDirectory from './is-in-directory/index.js'
|
|
3
2
|
export * as readWrite from './read-write/index.js'
|
|
3
|
+
export * as isInDirectory from './is-in-directory/index.js'
|
|
4
4
|
export * as subpaths from './subpaths/index.js'
|
package/node/files/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export * as getSize from './get-size/index.js'
|
|
2
|
-
export * as isInDirectory from './is-in-directory/index.js'
|
|
3
2
|
export * as readWrite from './read-write/index.js'
|
|
3
|
+
export * as isInDirectory from './is-in-directory/index.js'
|
|
4
4
|
export * as subpaths from './subpaths/index.js'
|
package/node/ftps/file/index.js
CHANGED
package/node/images/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export * as create from './create/index.js'
|
|
2
2
|
export * as format from './format/index.js'
|
|
3
|
+
export * as metadata from './metadata/index.js'
|
|
3
4
|
export * as transform from './transform/index.js'
|
|
4
5
|
export * as utils from './utils/index.js'
|
|
5
|
-
export * as metadata from './metadata/index.js'
|
package/node/images/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export * as create from './create/index.js'
|
|
2
2
|
export * as format from './format/index.js'
|
|
3
|
+
export * as metadata from './metadata/index.js'
|
|
3
4
|
export * as transform from './transform/index.js'
|
|
4
5
|
export * as utils from './utils/index.js'
|
|
5
|
-
export * as metadata from './metadata/index.js'
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
+
export * as extend from './extend/index.js'
|
|
1
2
|
export * as blur from './blur/index.js'
|
|
2
|
-
export * as brighten from './brighten/index.js'
|
|
3
3
|
export * as extract from './extract/index.js'
|
|
4
4
|
export * as flatten from './flatten/index.js'
|
|
5
5
|
export * as flip from './flip/index.js'
|
|
6
|
-
export * as hue from './hue/index.js'
|
|
7
6
|
export * as flop from './flop/index.js'
|
|
8
|
-
export * as
|
|
7
|
+
export * as hue from './hue/index.js'
|
|
8
|
+
export * as brighten from './brighten/index.js'
|
|
9
9
|
export * as lighten from './lighten/index.js'
|
|
10
|
-
export * as extend from './extend/index.js'
|
|
11
10
|
export * as normalize from './normalize/index.js'
|
|
12
|
-
export * as
|
|
11
|
+
export * as level from './level/index.js'
|
|
13
12
|
export * as resize from './resize/index.js'
|
|
13
|
+
export * as overlay from './overlay/index.js'
|
|
14
14
|
export * as rotate from './rotate/index.js'
|
|
15
15
|
export * as saturate from './saturate/index.js'
|