@design-edito/tools 0.5.38 → 0.5.39
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/@react/effects/index.d.ts +1 -0
- package/agnostic/@react/effects/index.js +1 -0
- package/agnostic/@react/effects/use-state-change-dispatch/index.d.ts +22 -0
- package/agnostic/@react/effects/use-state-change-dispatch/index.js +31 -0
- package/agnostic/@react/index.d.ts +1 -0
- package/agnostic/@react/index.js +1 -0
- package/agnostic/arrays/index.d.ts +2 -1
- package/agnostic/arrays/index.js +2 -1
- package/agnostic/arrays/integers-between/index.d.ts +8 -0
- package/agnostic/arrays/integers-between/index.js +11 -0
- package/agnostic/arrays/integers-between/index.test.d.ts +1 -0
- package/agnostic/arrays/integers-between/index.test.js +16 -0
- package/agnostic/colors/index.d.ts +1 -1
- package/agnostic/colors/index.js +1 -1
- package/agnostic/css/index.d.ts +1 -0
- package/agnostic/css/index.js +1 -0
- package/agnostic/css/merge-class-names/index.d.ts +8 -0
- package/agnostic/css/merge-class-names/index.js +23 -0
- package/agnostic/css/merge-class-names/index.test.d.ts +1 -0
- package/agnostic/css/merge-class-names/index.test.js +52 -0
- 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 +1 -1
- package/agnostic/html/hyper-json/smart-tags/isolated/index.js +1 -1
- package/agnostic/html/index.d.ts +2 -2
- package/agnostic/html/index.js +2 -2
- package/agnostic/index.d.ts +1 -0
- package/agnostic/index.js +1 -0
- package/agnostic/misc/index.d.ts +1 -1
- package/agnostic/misc/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 +1 -1
- package/agnostic/objects/index.js +1 -1
- package/agnostic/time/index.d.ts +1 -1
- package/agnostic/time/index.js +1 -1
- package/components/BeforeAfter/index.controlled.js +1 -1
- package/components/BeforeAfter/index.js +2 -2
- package/components/Button/index.js +1 -1
- package/components/Clippable/index.js +1 -1
- package/components/Disclaimer/index.d.ts +2 -2
- package/components/Disclaimer/index.js +4 -3
- package/components/Drawer/index.js +4 -3
- package/components/EventListener/index.js +1 -1
- package/components/Gallery/index.js +5 -4
- package/components/Iframe/index.js +1 -1
- package/components/Image/index.d.ts +1 -1
- package/components/Image/index.js +3 -2
- package/components/Input/index.js +1 -1
- package/components/IntersectionObserver/index.js +1 -1
- package/components/JsonEditor/index.js +1 -1
- package/components/JsonEditor/types.d.ts +0 -6
- package/components/JsonEditor/utils.d.ts +0 -5
- package/components/JsonEditor/utils.js +0 -5
- package/components/Lightbox/index.d.ts +12 -12
- package/components/Lightbox/index.js +37 -35
- package/components/Lightbox/store.d.ts +9 -43
- package/components/Lightbox/store.js +51 -73
- package/components/Lightbox/types.d.ts +16 -0
- package/components/Lightbox/types.js +1 -0
- package/components/ListLoader/index.controlled.js +19 -13
- package/components/ListLoader/index.js +19 -3
- package/components/Overlayer/index.js +1 -1
- package/components/Paginator/index.js +4 -3
- package/components/ResizeObserver/index.js +1 -1
- package/components/Scrllgngn/index.d.ts +4 -4
- package/components/Scrllgngn/index.js +4 -3
- package/components/Scrllgngn/types.d.ts +55 -0
- package/components/Scrllgngn/types.js +24 -0
- package/components/Scrllgngn/utils.d.ts +2 -38
- package/components/ScrollListener/index.d.ts +1 -6
- package/components/ScrollListener/index.js +10 -9
- package/components/ScrollListener/types.d.ts +0 -3
- package/components/Select/index.js +1 -1
- package/components/Sequencer/index.d.ts +2 -9
- package/components/Sequencer/index.js +26 -128
- package/components/Sequencer/types.d.ts +30 -0
- package/components/Sequencer/types.js +30 -0
- package/components/Sequencer/utils.d.ts +4 -0
- package/components/Sequencer/utils.js +25 -0
- package/components/ShadowRoot/index.js +1 -1
- package/components/Subtitles/index.js +3 -5
- package/components/Subtitles/utils.d.ts +0 -51
- package/components/Subtitles/utils.js +0 -41
- package/components/Textarea/index.js +1 -1
- package/components/UIModule/index.js +5 -4
- package/components/Video/index.controlled.d.ts +1 -7
- package/components/Video/index.controlled.js +13 -12
- package/components/Video/index.d.ts +0 -8
- package/components/Video/index.js +11 -15
- package/components/Video/types.d.ts +33 -0
- package/components/Video/utils.d.ts +1 -67
- package/components/Video/utils.js +25 -44
- package/components/index.d.ts +2 -2
- package/components/index.js +2 -2
- package/components/utils/index.d.ts +1 -44
- package/components/utils/index.js +1 -83
- package/components/utils/viewport-behaviours/index.d.ts +8 -14
- package/components/utils/viewport-behaviours/index.js +15 -37
- package/components/utils/viewport-behaviours/types.d.ts +3 -9
- package/components/utils/viewport-behaviours/types.js +3 -4
- 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/cloud-storage/operations/index.d.ts +1 -1
- package/node/cloud-storage/operations/index.js +1 -1
- package/node/encryption/@aes-256-gcm/index.d.ts +1 -1
- package/node/encryption/@aes-256-gcm/index.js +1 -1
- package/node/ftps/directory/index.d.ts +1 -1
- package/node/ftps/directory/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 +3 -3
- package/node/images/transform/operations/index.js +3 -3
- package/node/index.d.ts +2 -2
- package/node/index.js +2 -2
- package/package.json +44 -1
- package/components/ListLoader/utils.d.ts +0 -8
- package/components/ListLoader/utils.js +0 -10
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { integersBetween } from '../../agnostic/arrays/integers-between/index.js';
|
|
2
3
|
import { clss } from '../../agnostic/css/clss/index.js';
|
|
4
|
+
import { mergeClassNames } from '../../agnostic/css/merge-class-names/index.js';
|
|
3
5
|
import { IntersectionObserverComponent } from '../IntersectionObserver/index.js';
|
|
4
|
-
import { mergeClassNames } from '../utils/index.js';
|
|
5
6
|
import { listLoader as publicClassName } from '../public-classnames.js';
|
|
6
|
-
import { rangeBetween } from './utils.js';
|
|
7
7
|
import cssModule from './styles.module.css';
|
|
8
8
|
/**
|
|
9
9
|
* Presentational layer of the paginated list. Renders already-loaded pages as a
|
|
@@ -65,21 +65,27 @@ export const ControlledListLoader = ({ className, pages, firstPagePos, lastPageP
|
|
|
65
65
|
const prevPage = loadedPages[pos - 1];
|
|
66
66
|
if (prevPage === undefined)
|
|
67
67
|
return [];
|
|
68
|
-
return
|
|
68
|
+
return integersBetween(prevPage + 1, pagePos - 1);
|
|
69
69
|
});
|
|
70
|
-
const slots = [
|
|
71
|
-
...firstLoadedPage !== undefined && firstLoadedPage > firstPagePos
|
|
72
|
-
? [{ pagePos: firstLoadedPage - 1, kind: 'prev' }]
|
|
73
|
-
: [],
|
|
74
|
-
...loadedPages.map(pagePos => ({ pagePos, kind: 'items' })),
|
|
75
|
-
...gapPages.map(pagePos => ({ pagePos, kind: 'gap' })),
|
|
76
|
-
...lastLoadedPage !== undefined && lastLoadedPage < lastPagePos
|
|
77
|
-
? [{ pagePos: lastLoadedPage + 1, kind: 'next' }]
|
|
78
|
-
: []
|
|
79
|
-
].sort((a, b) => a.pagePos - b.pagePos);
|
|
80
70
|
const sortedLoadingPages = [...loadingPages ?? []]
|
|
81
71
|
.filter(pagePos => inBoundsPages.includes(pagePos))
|
|
82
72
|
.sort((a, b) => a - b);
|
|
73
|
+
const hasNothing = loadedPages.length === 0 && sortedLoadingPages.length === 0;
|
|
74
|
+
const slots = hasNothing
|
|
75
|
+
? [{
|
|
76
|
+
pagePos: [...inBoundsPages].sort((a, b) => a - b)[0] ?? firstPagePos,
|
|
77
|
+
kind: 'gap'
|
|
78
|
+
}]
|
|
79
|
+
: [
|
|
80
|
+
...firstLoadedPage !== undefined && firstLoadedPage > firstPagePos
|
|
81
|
+
? [{ pagePos: firstLoadedPage - 1, kind: 'prev' }]
|
|
82
|
+
: [],
|
|
83
|
+
...loadedPages.map(pagePos => ({ pagePos, kind: 'items' })),
|
|
84
|
+
...gapPages.map(pagePos => ({ pagePos, kind: 'gap' })),
|
|
85
|
+
...lastLoadedPage !== undefined && lastLoadedPage < lastPagePos
|
|
86
|
+
? [{ pagePos: lastLoadedPage + 1, kind: 'next' }]
|
|
87
|
+
: []
|
|
88
|
+
].sort((a, b) => a.pagePos - b.pagePos);
|
|
83
89
|
const c = clss(publicClassName, { cssModule });
|
|
84
90
|
const rootClss = mergeClassNames(c(null, { loading: sortedLoadingPages.length > 0 }), className);
|
|
85
91
|
const renderLoadButton = (pagePos, kind) => {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { integersBetween } from '../../agnostic/arrays/integers-between/index.js';
|
|
3
4
|
import { toError } from '../../agnostic/misc/cast/index.js';
|
|
4
5
|
import { clamp } from '../../agnostic/numbers/clamp/index.js';
|
|
5
6
|
import { ControlledListLoader } from './index.controlled.js';
|
|
6
|
-
import { rangeBetween } from './utils.js';
|
|
7
7
|
/**
|
|
8
8
|
* Paginated list abstraction. Fetches the pages it is asked for, keeps them
|
|
9
9
|
* fresh, and drives a {@link ControlledListLoader} with the result.
|
|
@@ -32,6 +32,7 @@ export const ListLoader = ({ className, pages, defaultPage, fillGaps = true, dro
|
|
|
32
32
|
clamp(defaultPage ?? firstPagePos, firstPagePos, lastPagePos)
|
|
33
33
|
]);
|
|
34
34
|
const requestedPages = useRef(new Set());
|
|
35
|
+
const retryTimeouts = useRef(new Set());
|
|
35
36
|
// Single source of truth for what should be loaded. Memoized so the effects
|
|
36
37
|
// below keep a stable dependency when the page set has not actually changed.
|
|
37
38
|
const currentPages = useMemo(() => (pages ?? internalPages).filter(page => page >= firstPagePos && page <= lastPagePos), [pages, internalPages, firstPagePos, lastPagePos]);
|
|
@@ -45,6 +46,11 @@ export const ListLoader = ({ className, pages, defaultPage, fillGaps = true, dro
|
|
|
45
46
|
return kept.length === next.length ? next : kept;
|
|
46
47
|
});
|
|
47
48
|
}
|
|
49
|
+
const gaveUp = requestedPages.current.has(pagePos)
|
|
50
|
+
&& !itemsPages.has(pagePos)
|
|
51
|
+
&& !loadingPages.has(pagePos);
|
|
52
|
+
if (gaveUp)
|
|
53
|
+
loadPage(pagePos);
|
|
48
54
|
onLoadPageClicked?.(pagePos);
|
|
49
55
|
};
|
|
50
56
|
const storePage = (page, items) => setItemsPages(curr => {
|
|
@@ -85,9 +91,19 @@ export const ListLoader = ({ className, pages, defaultPage, fillGaps = true, dro
|
|
|
85
91
|
reportFetchError(page, err);
|
|
86
92
|
if (retriesLeft <= 0)
|
|
87
93
|
return setPageLoading(page, false);
|
|
88
|
-
window.setTimeout(() =>
|
|
94
|
+
const timeout = window.setTimeout(() => {
|
|
95
|
+
retryTimeouts.current.delete(timeout);
|
|
96
|
+
loadPage(page, retriesLeft - 1);
|
|
97
|
+
}, fetchRetriesDelayMs);
|
|
98
|
+
retryTimeouts.current.add(timeout);
|
|
89
99
|
});
|
|
90
100
|
};
|
|
101
|
+
// Fx. dep. none - Emptying `requestedPages` is what stops a fetch already in flight.
|
|
102
|
+
useEffect(() => () => {
|
|
103
|
+
retryTimeouts.current.forEach(timeout => { window.clearTimeout(timeout); });
|
|
104
|
+
retryTimeouts.current.clear();
|
|
105
|
+
requestedPages.current.clear();
|
|
106
|
+
}, []);
|
|
91
107
|
// Fx. dep. `currentPages` - Drops everything held for pages no longer wanted.
|
|
92
108
|
// The updaters return `curr` untouched when nothing was removed, so an unstable
|
|
93
109
|
// `pages` prop cannot spin the render loop.
|
|
@@ -112,7 +128,7 @@ export const ListLoader = ({ className, pages, defaultPage, fillGaps = true, dro
|
|
|
112
128
|
setInternalPages(curr => {
|
|
113
129
|
if (curr.length === 0)
|
|
114
130
|
return curr;
|
|
115
|
-
const missing =
|
|
131
|
+
const missing = integersBetween(Math.min(...curr), Math.max(...curr))
|
|
116
132
|
.filter(page => !curr.includes(page));
|
|
117
133
|
if (missing.length === 0)
|
|
118
134
|
return curr;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { clss } from '../../agnostic/css/clss/index.js';
|
|
3
3
|
import { isFalsy } from '../../agnostic/booleans/is-falsy/index.js';
|
|
4
|
-
import { mergeClassNames } from '
|
|
4
|
+
import { mergeClassNames } from '../../agnostic/css/merge-class-names/index.js';
|
|
5
5
|
import { overlayer as publicClassName } from '../public-classnames.js';
|
|
6
6
|
import cssModule from './styles.module.css';
|
|
7
7
|
/**
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, Children, useState } from 'react';
|
|
3
|
+
import { useStateChangeDispatch } from '../../agnostic/@react/effects/use-state-change-dispatch/index.js';
|
|
3
4
|
import { clss } from '../../agnostic/css/clss/index.js';
|
|
4
|
-
import { mergeClassNames
|
|
5
|
+
import { mergeClassNames } from '../../agnostic/css/merge-class-names/index.js';
|
|
5
6
|
import { paginator as publicClassName } from '../public-classnames.js';
|
|
6
7
|
import cssModule from './styles.module.css';
|
|
7
8
|
/**
|
|
@@ -63,10 +64,10 @@ export const Paginator = ({ thresholdOffsetPercent, onDirectionChanged, onPagesC
|
|
|
63
64
|
console.warn('Paginator: thresholdOffsetPercent must be a number between 0 and 100, received', thresholdOffsetPercent, `— clamped to ${thresholdPercent}.`);
|
|
64
65
|
}, [thresholdOffsetPercent, thresholdPercent]);
|
|
65
66
|
// State dispatch
|
|
66
|
-
|
|
67
|
+
useStateChangeDispatch(pagesState, pages => onPagesChanged?.(Array
|
|
67
68
|
.from(pages)
|
|
68
69
|
.map(([, pageState]) => pageState)));
|
|
69
|
-
|
|
70
|
+
useStateChangeDispatch(directionState, onDirectionChanged);
|
|
70
71
|
// Catch scroll direction listening on scroll events
|
|
71
72
|
useEffect(() => {
|
|
72
73
|
let lastCall = 0;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useRef, useEffect, useState } from 'react';
|
|
3
3
|
import { clss } from '../../agnostic/css/clss/index.js';
|
|
4
|
-
import { mergeClassNames } from '
|
|
4
|
+
import { mergeClassNames } from '../../agnostic/css/merge-class-names/index.js';
|
|
5
5
|
import { resizeObserver as publicClassName } from '../public-classnames.js';
|
|
6
6
|
import cssModule from './styles.module.css';
|
|
7
7
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type FunctionComponent, type PropsWithChildren } from 'react';
|
|
2
2
|
import type { WithClassName } from '../utils/types.js';
|
|
3
|
-
import {
|
|
3
|
+
import type { ForceStickMode, StickyDepth, TrackedBlockContext, ViewportOffset } from './types.js';
|
|
4
4
|
/**
|
|
5
5
|
* Common properties shared by all block types.
|
|
6
6
|
*
|
|
@@ -34,7 +34,7 @@ export type PropsScrollBlock = PropsCommonBlock & {
|
|
|
34
34
|
* stacked in the order they appear across pages (ascending).
|
|
35
35
|
*/
|
|
36
36
|
export type PropsStickyBlock = PropsCommonBlock & {
|
|
37
|
-
depth:
|
|
37
|
+
depth: StickyDepth;
|
|
38
38
|
zIndex?: number;
|
|
39
39
|
};
|
|
40
40
|
/**
|
|
@@ -98,7 +98,7 @@ export type Props = WithClassName<{
|
|
|
98
98
|
viewportOffsetBottom?: ViewportOffset;
|
|
99
99
|
viewportOffsetLeft?: ViewportOffset;
|
|
100
100
|
stickyBlocksLazyLoadDistance?: number;
|
|
101
|
-
forceStickBlocks?:
|
|
101
|
+
forceStickBlocks?: ForceStickMode;
|
|
102
102
|
onPageChanged?: (payload: {
|
|
103
103
|
currentPagePos: number;
|
|
104
104
|
pageData?: PropsPage;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { useStateChangeDispatch } from '../../agnostic/@react/effects/use-state-change-dispatch/index.js';
|
|
3
4
|
import { clss } from '../../agnostic/css/clss/index.js';
|
|
4
5
|
import { randomHash } from '../../agnostic/random/uuid/index.js';
|
|
6
|
+
import { mergeClassNames } from '../../agnostic/css/merge-class-names/index.js';
|
|
5
7
|
import { IntersectionObserverComponent } from '../IntersectionObserver/index.js';
|
|
6
8
|
import { Paginator } from '../Paginator/index.js';
|
|
7
9
|
import { ResizeObserverComponent } from '../ResizeObserver/index.js';
|
|
8
10
|
import { subscribe, unsubscribe } from '../ScrollListener/utils.js';
|
|
9
|
-
import { mergeClassNames, useChangeDispatch } from '../utils/index.js';
|
|
10
11
|
import { scrllgngn as publicClassName } from '../public-classnames.js';
|
|
11
12
|
import { blockDisplayZones, consolidateStickyBlocks, contextsAreEqual, lazyLoadedBlocks, measurePages, sameDiscretePart, scrollBlocksOf, scrollKey, stickyKey, toPaginatorThresholdPercent, toScreenCssProps, toThresholdY, toTrackedBlockContext, toTrackedBlockCssProps, toTrackedBlockDataAttributes, toViewportOffsetCssProps, toVisibleZoneRect, toVisibleZoneRootMargin, visibleZonePollInterval, visibleZonesAreEqual } from './utils.js';
|
|
12
13
|
import cssModule from './styles.module.css';
|
|
@@ -254,11 +255,11 @@ export const Scrllgngn = ({ pages, thresholdOffsetPercent, viewportOffsetTop, vi
|
|
|
254
255
|
return () => window.clearInterval(interval);
|
|
255
256
|
}, [isOnScreen, readVisibleZone]);
|
|
256
257
|
// Handlers
|
|
257
|
-
|
|
258
|
+
useStateChangeDispatch(currentPagePos, pagePos => onPageChanged?.({
|
|
258
259
|
currentPagePos: pagePos,
|
|
259
260
|
pageData: pages?.[pagePos]
|
|
260
261
|
}));
|
|
261
|
-
|
|
262
|
+
useStateChangeDispatch(contentVisible, onContentVisibilityChanged);
|
|
262
263
|
const handleTopBoundDetect = e => setTopVis(e.ioEntry?.isIntersecting ?? false);
|
|
263
264
|
const handleCntDetect = e => setCntVis(e.ioEntry?.isIntersecting ?? false);
|
|
264
265
|
const handleBtmBoundDetect = e => setBtmVis(e.ioEntry?.isIntersecting ?? false);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The depths that lift a block out of the scroll flow.
|
|
3
|
+
*
|
|
4
|
+
* **Exported because a consumer has to validate what it is handed**, and a type gives a
|
|
5
|
+
* runtime validator nothing to compare against. A block declaring one of these is
|
|
6
|
+
* consolidated across the pages it appears on and rendered in that layer, not inline.
|
|
7
|
+
*/
|
|
8
|
+
export declare const SCRLLGNGN_STICKY_DEPTHS: readonly ["back", "front"];
|
|
9
|
+
export type StickyDepth = typeof SCRLLGNGN_STICKY_DEPTHS[number];
|
|
10
|
+
/**
|
|
11
|
+
* Which out-of-range sticky blocks are forced into a stuck state.
|
|
12
|
+
*
|
|
13
|
+
* Exported for the same reason as {@link SCRLLGNGN_STICKY_DEPTHS}: the list is what a
|
|
14
|
+
* validator needs, and the day a mode is added is the day a caller writing it would
|
|
15
|
+
* otherwise silently get nothing.
|
|
16
|
+
*/
|
|
17
|
+
export declare const SCRLLGNGN_FORCE_STICK_MODES: readonly ["before", "after", "both", "none"];
|
|
18
|
+
export type ForceStickMode = typeof SCRLLGNGN_FORCE_STICK_MODES[number];
|
|
19
|
+
/** A length as authored: a bare number means pixels, a string is CSS's problem. */
|
|
20
|
+
export type ViewportOffset = number | string;
|
|
21
|
+
/**
|
|
22
|
+
* Where one tracked block stands in the scroll, as handed to its `onScrolled`.
|
|
23
|
+
*
|
|
24
|
+
* Every progression is a `0`–`1` ratio measured against the threshold line, and
|
|
25
|
+
* weighted **by pixel height**: a zone of unequal pages is not the same as
|
|
26
|
+
* `(indexOfCurrentPage + currentPageProgression) / displayZone.length`.
|
|
27
|
+
*
|
|
28
|
+
* @property currentPage - Zero-based position of the page in view.
|
|
29
|
+
* @property currentPageProgression - `0` when the current page's top meets the
|
|
30
|
+
* threshold, `1` when its bottom does.
|
|
31
|
+
* @property displayZone - Every page the block's `id` appears on. A block without
|
|
32
|
+
* an `id` shows on one page, so its zone holds that page alone and its three
|
|
33
|
+
* progressions are equal.
|
|
34
|
+
* @property indexOfCurrentPageInDisplayZone - Where `currentPage` sits in
|
|
35
|
+
* `displayZone`.
|
|
36
|
+
* @property displayZoneProgression - Progression across the whole zone. On a zone
|
|
37
|
+
* like `[2, 3, 5]` it runs 0 → ~0.66 over pages 2–3, pauses while page 4 shows the
|
|
38
|
+
* block nowhere, then resumes to 1 on page 5.
|
|
39
|
+
* @property contiguousDisplayZone - The unbroken run of `displayZone` containing
|
|
40
|
+
* `currentPage` — `[2, 3]` for the zone above while on page 2 or 3.
|
|
41
|
+
* @property indexOfCurrentPageInContiguousDisplayZone - Where `currentPage` sits in
|
|
42
|
+
* `contiguousDisplayZone`.
|
|
43
|
+
* @property contiguousDisplayZoneProgression - Progression across that run alone,
|
|
44
|
+
* so it always spans a full 0 → 1.
|
|
45
|
+
*/
|
|
46
|
+
export type TrackedBlockContext = {
|
|
47
|
+
currentPage: number;
|
|
48
|
+
currentPageProgression: number;
|
|
49
|
+
displayZone: number[];
|
|
50
|
+
indexOfCurrentPageInDisplayZone: number;
|
|
51
|
+
displayZoneProgression: number;
|
|
52
|
+
contiguousDisplayZone: number[];
|
|
53
|
+
indexOfCurrentPageInContiguousDisplayZone: number;
|
|
54
|
+
contiguousDisplayZoneProgression: number;
|
|
55
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The depths that lift a block out of the scroll flow.
|
|
3
|
+
*
|
|
4
|
+
* **Exported because a consumer has to validate what it is handed**, and a type gives a
|
|
5
|
+
* runtime validator nothing to compare against. A block declaring one of these is
|
|
6
|
+
* consolidated across the pages it appears on and rendered in that layer, not inline.
|
|
7
|
+
*/
|
|
8
|
+
export const SCRLLGNGN_STICKY_DEPTHS = [
|
|
9
|
+
'back',
|
|
10
|
+
'front'
|
|
11
|
+
];
|
|
12
|
+
/**
|
|
13
|
+
* Which out-of-range sticky blocks are forced into a stuck state.
|
|
14
|
+
*
|
|
15
|
+
* Exported for the same reason as {@link SCRLLGNGN_STICKY_DEPTHS}: the list is what a
|
|
16
|
+
* validator needs, and the day a mode is added is the day a caller writing it would
|
|
17
|
+
* otherwise silently get nothing.
|
|
18
|
+
*/
|
|
19
|
+
export const SCRLLGNGN_FORCE_STICK_MODES = [
|
|
20
|
+
'before',
|
|
21
|
+
'after',
|
|
22
|
+
'both',
|
|
23
|
+
'none'
|
|
24
|
+
];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PropsBlock, PropsPage, PropsStickyBlock } from './index.js';
|
|
2
|
+
import type { StickyDepth, TrackedBlockContext, ViewportOffset } from './types.js';
|
|
2
3
|
/** The subset of a bounding rect a {@link Scrllgngn} tracks and exposes. */
|
|
3
4
|
export type ScreenRect = {
|
|
4
5
|
left: number;
|
|
@@ -37,7 +38,7 @@ export declare function consolidateStickyBlocks(pages?: PropsPage[]): Map<string
|
|
|
37
38
|
* by ascending `zIndex`. The id comes along because it is what identifies the block
|
|
38
39
|
* across renders — its own `id` is optional, a consolidation key never is.
|
|
39
40
|
*/
|
|
40
|
-
export declare function lazyLoadedBlocks(blocks: Map<string, ConsolidatedStickyBlock>, depth:
|
|
41
|
+
export declare function lazyLoadedBlocks(blocks: Map<string, ConsolidatedStickyBlock>, depth: StickyDepth, currentPagePos: number, lazyLoadDistance: number): Array<[string, ConsolidatedStickyBlock]>;
|
|
41
42
|
/**
|
|
42
43
|
* Builds the CSS custom properties exposed on a {@link Scrllgngn} root.
|
|
43
44
|
*
|
|
@@ -58,41 +59,6 @@ export declare function lazyLoadedBlocks(blocks: Map<string, ConsolidatedStickyB
|
|
|
58
59
|
* @returns The custom properties, keyed by their full name.
|
|
59
60
|
*/
|
|
60
61
|
export declare function toScreenCssProps(rect?: ScreenRect, zone?: VisibleZoneRect): Record<string, string>;
|
|
61
|
-
/**
|
|
62
|
-
* Where one tracked block stands in the scroll, as handed to its `onScrolled`.
|
|
63
|
-
*
|
|
64
|
-
* Every progression is a `0`–`1` ratio measured against the threshold line, and
|
|
65
|
-
* weighted **by pixel height**: a zone of unequal pages is not the same as
|
|
66
|
-
* `(indexOfCurrentPage + currentPageProgression) / displayZone.length`.
|
|
67
|
-
*
|
|
68
|
-
* @property currentPage - Zero-based position of the page in view.
|
|
69
|
-
* @property currentPageProgression - `0` when the current page's top meets the
|
|
70
|
-
* threshold, `1` when its bottom does.
|
|
71
|
-
* @property displayZone - Every page the block's `id` appears on. A block without
|
|
72
|
-
* an `id` shows on one page, so its zone holds that page alone and its three
|
|
73
|
-
* progressions are equal.
|
|
74
|
-
* @property indexOfCurrentPageInDisplayZone - Where `currentPage` sits in
|
|
75
|
-
* `displayZone`.
|
|
76
|
-
* @property displayZoneProgression - Progression across the whole zone. On a zone
|
|
77
|
-
* like `[2, 3, 5]` it runs 0 → ~0.66 over pages 2–3, pauses while page 4 shows the
|
|
78
|
-
* block nowhere, then resumes to 1 on page 5.
|
|
79
|
-
* @property contiguousDisplayZone - The unbroken run of `displayZone` containing
|
|
80
|
-
* `currentPage` — `[2, 3]` for the zone above while on page 2 or 3.
|
|
81
|
-
* @property indexOfCurrentPageInContiguousDisplayZone - Where `currentPage` sits in
|
|
82
|
-
* `contiguousDisplayZone`.
|
|
83
|
-
* @property contiguousDisplayZoneProgression - Progression across that run alone,
|
|
84
|
-
* so it always spans a full 0 → 1.
|
|
85
|
-
*/
|
|
86
|
-
export type TrackedBlockContext = {
|
|
87
|
-
currentPage: number;
|
|
88
|
-
currentPageProgression: number;
|
|
89
|
-
displayZone: number[];
|
|
90
|
-
indexOfCurrentPageInDisplayZone: number;
|
|
91
|
-
displayZoneProgression: number;
|
|
92
|
-
contiguousDisplayZone: number[];
|
|
93
|
-
indexOfCurrentPageInContiguousDisplayZone: number;
|
|
94
|
-
contiguousDisplayZoneProgression: number;
|
|
95
|
-
};
|
|
96
62
|
/** How far the threshold line has travelled through one page, in pixels. */
|
|
97
63
|
export type PageScrollMetrics = {
|
|
98
64
|
scrolled: number;
|
|
@@ -200,8 +166,6 @@ export type VisibleZoneRect = {
|
|
|
200
166
|
width: number;
|
|
201
167
|
height: number;
|
|
202
168
|
};
|
|
203
|
-
/** A length as authored: a bare number means pixels, a string is CSS's problem. */
|
|
204
|
-
export type ViewportOffset = number | string;
|
|
205
169
|
/** The four edges, as authored on the component. */
|
|
206
170
|
export type ViewportOffsets = {
|
|
207
171
|
top?: ViewportOffset;
|
|
@@ -12,9 +12,6 @@ import type { ScrollListenerAction } from './types.js';
|
|
|
12
12
|
* which is what this component has always done — a starting point and not a setting,
|
|
13
13
|
* hence `default…`. `defaultTracking={false}` with `whenVisible='track'` is the old
|
|
14
14
|
* `startOnVisible`, written in the shared grammar.
|
|
15
|
-
* @property behavioursSuspended - Holds back the instructions that **start** something,
|
|
16
|
-
* and lets through those that stop it. A gate reaches `'track'`: measuring a subtree
|
|
17
|
-
* nobody is being shown is work done for no one.
|
|
18
15
|
* @property onIsTrackingChanged - Called once the effective tracking state changed.
|
|
19
16
|
* @property onScrollStateChanged - Called after the measured {@link ScrollState}
|
|
20
17
|
* changed. Receives `undefined` until the first measurement lands.
|
|
@@ -42,9 +39,7 @@ export type Props = PropsWithChildren<WithClassName<{
|
|
|
42
39
|
onScrollStateChanged?: (scrollState?: ScrollState) => void;
|
|
43
40
|
onScrollProgressChanged?: (progress: number) => void;
|
|
44
41
|
onScrollDirectionChanged?: (direction: 'up' | 'down') => void;
|
|
45
|
-
}>> & ViewportBehaviours<ScrollListenerAction
|
|
46
|
-
behavioursSuspended?: boolean;
|
|
47
|
-
};
|
|
42
|
+
}>> & ViewportBehaviours<ScrollListenerAction>;
|
|
48
43
|
/**
|
|
49
44
|
* Exposes scroll metrics — both the document's and its own — as CSS custom
|
|
50
45
|
* properties on its root element, so scroll-driven styling needs no JavaScript
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { useStateChangeDispatch } from '../../agnostic/@react/effects/use-state-change-dispatch/index.js';
|
|
3
4
|
import { clss } from '../../agnostic/css/clss/index.js';
|
|
4
5
|
import { randomHash } from '../../agnostic/random/uuid/index.js';
|
|
5
|
-
import { mergeClassNames
|
|
6
|
+
import { mergeClassNames } from '../../agnostic/css/merge-class-names/index.js';
|
|
6
7
|
import { useViewportBehaviours } from '../utils/viewport-behaviours/index.js';
|
|
7
8
|
import { scrollListener as publicClassName } from '../public-classnames.js';
|
|
8
9
|
import { getScrollProgress, subscribe, toScrollCssProps, unsubscribe } from './utils.js';
|
|
@@ -64,7 +65,7 @@ import cssModule from './styles.module.css';
|
|
|
64
65
|
* for everyone, each element only for itself. The listeners exist only while at
|
|
65
66
|
* least one instance is tracking.
|
|
66
67
|
*/
|
|
67
|
-
export const ScrollListener = ({ tracking, defaultTracking,
|
|
68
|
+
export const ScrollListener = ({ tracking, defaultTracking, visibilityThreshold, visibilityRoot, visibilityRootMargin, visibilityOnAfterMs, visibilityOffAfterMs, whenVisible, whenHidden, onIsTrackingChanged, onScrollStateChanged, onVisibilityChanged, onScrollProgressChanged, onScrollDirectionChanged, className, children }) => {
|
|
68
69
|
// State & refs
|
|
69
70
|
const [subscriberId] = useState(() => randomHash(6));
|
|
70
71
|
const [internalTracking, setInternalTracking] = useState(defaultTracking ?? true);
|
|
@@ -82,8 +83,8 @@ export const ScrollListener = ({ tracking, defaultTracking, behavioursSuspended,
|
|
|
82
83
|
// `tracking`: the effective state reads the prop first, so the internal one they write
|
|
83
84
|
// is simply never consulted.
|
|
84
85
|
const actions = {
|
|
85
|
-
track: {
|
|
86
|
-
untrack: {
|
|
86
|
+
track: { domain: 'tracking', run: () => setInternalTracking(true) },
|
|
87
|
+
untrack: { domain: 'tracking', run: () => setInternalTracking(false) }
|
|
87
88
|
};
|
|
88
89
|
// The surrender half of the result is dropped: this component has no control a reader
|
|
89
90
|
// could take over. The observer runs on the root itself — there is no inner box any
|
|
@@ -98,13 +99,13 @@ export const ScrollListener = ({ tracking, defaultTracking, behavioursSuspended,
|
|
|
98
99
|
whenVisible,
|
|
99
100
|
whenHidden,
|
|
100
101
|
onVisibilityChanged
|
|
101
|
-
}, actions
|
|
102
|
+
}, actions);
|
|
102
103
|
// State dispatch
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
useStateChangeDispatch(isTracking, onIsTrackingChanged);
|
|
105
|
+
useStateChangeDispatch(scrollState, onScrollStateChanged);
|
|
106
|
+
useStateChangeDispatch(scrollProgress, progress => { if (progress !== undefined)
|
|
106
107
|
onScrollProgressChanged?.(progress); });
|
|
107
|
-
|
|
108
|
+
useStateChangeDispatch(scrollDirection, direction => { if (direction !== null)
|
|
108
109
|
onScrollDirectionChanged?.(direction); });
|
|
109
110
|
// Fx. dep. `scrollState` - derive the document scroll direction
|
|
110
111
|
useEffect(() => {
|
|
@@ -24,8 +24,5 @@ export type ScrollListenerAction = ScrollListenerVerb;
|
|
|
24
24
|
* and nothing that ever surrenders. The field is part of `ActionSpec` and is declared
|
|
25
25
|
* honestly rather than split into domains no hand could ever tell apart. The consequence
|
|
26
26
|
* is that `':force'` has no trigger here.
|
|
27
|
-
*
|
|
28
|
-
* A gate is the other brake, and it does reach `track`: measuring a subtree nobody is
|
|
29
|
-
* being shown is work done for no one.
|
|
30
27
|
*/
|
|
31
28
|
export type ScrollListenerDomain = 'tracking';
|
|
@@ -3,7 +3,7 @@ import { useState } from 'react';
|
|
|
3
3
|
import { clss } from '../../agnostic/css/clss/index.js';
|
|
4
4
|
import { isNotFalsy } from '../../agnostic/booleans/is-falsy/index.js';
|
|
5
5
|
import { randomHash } from '../../agnostic/random/uuid/index.js';
|
|
6
|
-
import { mergeClassNames } from '
|
|
6
|
+
import { mergeClassNames } from '../../agnostic/css/merge-class-names/index.js';
|
|
7
7
|
import { select as publicClassName } from '../public-classnames.js';
|
|
8
8
|
import cssModule from './styles.module.css';
|
|
9
9
|
/**
|
|
@@ -21,11 +21,6 @@ import type { SequencerAction } from './types.js';
|
|
|
21
21
|
* the counter over from the tempo.
|
|
22
22
|
* @property defaultPlay - Whether it starts advancing. `false` by default, and the
|
|
23
23
|
* starting point a `'play'` instruction moves from — not a setting, hence `default…`.
|
|
24
|
-
* @property behavioursSuspended - Holds back the instructions that **start** something,
|
|
25
|
-
* and lets through those that stop it. For a consumer withholding the sequence behind
|
|
26
|
-
* something — a warning to accept, typically. What was held back is replayed the moment
|
|
27
|
-
* this goes false, so lifting the veil on an already-visible sequence does what was
|
|
28
|
-
* asked. `':force'` does not override it.
|
|
29
24
|
* @property tempo - Speed in beats per minute: one step every `60000 / tempo` ms, so `60`
|
|
30
25
|
* is a step per second. Clamped to a minimum of `1`.
|
|
31
26
|
* @property loop - Whether the sequence wraps round. **`false` by default**, so a
|
|
@@ -62,9 +57,7 @@ export type Props = PropsWithChildren<WithClassName<{
|
|
|
62
57
|
onLooped?: () => void;
|
|
63
58
|
onReachedFirstStep?: () => void;
|
|
64
59
|
onReachedLastStep?: () => void;
|
|
65
|
-
}>> & ViewportBehaviours<SequencerAction
|
|
66
|
-
behavioursSuspended?: boolean;
|
|
67
|
-
};
|
|
60
|
+
}>> & ViewportBehaviours<SequencerAction>;
|
|
68
61
|
/**
|
|
69
62
|
* A sequencer over its children, which it classifies and never moves.
|
|
70
63
|
*
|
|
@@ -108,7 +101,7 @@ export type Props = PropsWithChildren<WithClassName<{
|
|
|
108
101
|
* **Nothing surrenders here.** The layer lets a reader's hand switch an instruction off
|
|
109
102
|
* for the rest of the mount, and this component has no control for a hand to touch — so
|
|
110
103
|
* `':force'` has nothing to override, and the single `'playback'` domain is a formality.
|
|
111
|
-
*
|
|
104
|
+
* @see {@link SequencerDomain}
|
|
112
105
|
*
|
|
113
106
|
* ### On the root
|
|
114
107
|
* `--playing`, `--at-start`, `--at-end`, `--ended`, plus `data-step`, `data-active-step`,
|