@design-edito/tools 0.3.10 → 0.3.11
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 +1 -1
- package/agnostic/arrays/index.js +1 -1
- package/agnostic/colors/index.d.ts +2 -2
- package/agnostic/colors/index.js +2 -2
- package/agnostic/css/clss/index.d.ts +53 -1
- package/agnostic/css/clss/index.js +1 -1
- package/agnostic/css/index.d.ts +2 -2
- package/agnostic/css/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 +20 -20
- package/agnostic/html/hyper-json/smart-tags/coalesced/index.js +20 -20
- package/agnostic/html/hyper-json/smart-tags/isolated/index.d.ts +4 -4
- package/agnostic/html/hyper-json/smart-tags/isolated/index.js +4 -4
- package/agnostic/index.d.ts +4 -4
- package/agnostic/index.js +4 -4
- package/agnostic/misc/assert/index.d.ts +3 -0
- package/agnostic/misc/crossenv/index.d.ts +1 -1
- package/agnostic/misc/crossenv/index.js +1 -1
- package/agnostic/misc/index.d.ts +5 -5
- package/agnostic/misc/index.js +5 -5
- package/agnostic/misc/logs/index.d.ts +1 -1
- package/agnostic/misc/logs/index.js +1 -1
- package/agnostic/misc/logs/logger/index.d.ts +10 -0
- package/agnostic/misc/logs/logger/index.js +40 -10
- package/agnostic/misc/logs/styles/index.d.ts +1 -0
- package/agnostic/misc/logs/styles/index.js +27 -9
- package/agnostic/numbers/index.d.ts +2 -2
- package/agnostic/numbers/index.js +2 -2
- package/agnostic/objects/index.d.ts +4 -4
- package/agnostic/objects/index.js +4 -4
- package/agnostic/sanitization/index.d.ts +1 -1
- package/agnostic/sanitization/index.js +1 -1
- package/agnostic/strings/index.d.ts +1 -1
- package/agnostic/strings/index.js +1 -1
- package/agnostic/time/index.d.ts +2 -2
- package/agnostic/time/index.js +2 -2
- package/agnostic/time/transitions/index.d.ts +3 -3
- package/agnostic/time/transitions/index.js +4 -4
- package/components/Disclaimer/index.d.ts +45 -0
- package/components/Disclaimer/index.js +70 -0
- package/components/Drawer/index.d.ts +45 -0
- package/components/Drawer/index.js +82 -0
- package/components/Drawer/styles.module.css +0 -0
- package/components/EventListener/index.d.ts +20 -3
- package/components/EventListener/index.js +15 -22
- package/components/Gallery/index.d.ts +67 -0
- package/components/Gallery/index.js +173 -0
- package/components/Gallery/styles.module.css +33 -0
- package/components/Gallery/utils.d.ts +1 -0
- package/components/Image/index.d.ts +60 -0
- package/components/Image/index.js +99 -0
- package/components/Image/styles.module.css +0 -0
- package/components/IntersectionObserver/index.d.ts +48 -11
- package/components/IntersectionObserver/index.js +13 -22
- package/components/Paginator/index.d.ts +72 -0
- package/components/Paginator/index.js +116 -0
- package/components/Paginator/styles.module.css +9 -0
- package/components/ResizeObserver/index.d.ts +27 -0
- package/components/ResizeObserver/index.js +81 -0
- package/components/Scrllgngn/index.d.ts +123 -0
- package/components/Scrllgngn/index.js +175 -0
- package/components/Scrllgngn/styles.module.css +74 -0
- package/components/Sequencer/index.controlled.d.ts +78 -0
- package/components/Sequencer/index.d.ts +85 -0
- package/components/Sequencer/index.js +109 -0
- package/components/Sequencer/styles.module.css +0 -0
- package/components/ShadowRoot/index.d.ts +35 -0
- package/components/ShadowRoot/index.js +56 -0
- package/components/ShadowRoot/styles.module.css +0 -0
- package/components/Subtitles/index.d.ts +58 -0
- package/components/Subtitles/index.js +111 -0
- package/components/Subtitles/styles.module.css +0 -0
- package/components/Subtitles/types.d.ts +10 -0
- package/components/Subtitles/types.js +0 -0
- package/components/Subtitles/utils.d.ts +28 -0
- package/components/Theatre/index.d.ts +64 -0
- package/components/Theatre/index.js +97 -0
- package/components/Theatre/styles.module.css +0 -0
- package/components/Video/index.d.ts +119 -0
- package/components/Video/index.js +358 -0
- package/components/Video/styles.module.css +0 -0
- package/components/Video/utils.d.ts +10 -0
- package/components/_WIP_AudioQuote/index.d.ts +1 -0
- package/components/_WIP_AudioQuote/index.js +0 -0
- package/components/_WIP_Icon/index.d.ts +1 -0
- package/components/_WIP_Icon/index.js +0 -0
- package/components/index.d.ts +15 -1
- package/components/index.js +15 -1
- package/components/public-classnames.d.ts +14 -3
- package/components/utils/index.d.ts +1 -0
- package/components/utils/index.js +12 -0
- package/components/utils/types.d.ts +3 -0
- package/components/utils/types.js +0 -0
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/node/@aws-s3/index.test.d.ts +1 -0
- 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 +3 -3
- package/node/@aws-s3/storage/file/index.js +3 -3
- 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 +4 -4
- package/node/@google-cloud/storage/file/index.js +4 -4
- package/node/cloud-storage/operations/index.d.ts +3 -3
- package/node/cloud-storage/operations/index.js +3 -3
- package/node/encryption/index.d.ts +1 -1
- package/node/encryption/index.js +1 -1
- package/node/files/index.d.ts +1 -1
- package/node/files/index.js +1 -1
- package/node/ftps/directory/index.d.ts +2 -2
- package/node/ftps/directory/index.js +2 -2
- package/node/ftps/file/index.d.ts +1 -1
- package/node/ftps/file/index.js +1 -1
- package/node/images/index.d.ts +3 -3
- package/node/images/index.js +3 -3
- package/node/images/transform/operations/index.d.ts +6 -6
- package/node/images/transform/operations/index.js +6 -6
- package/node/index.d.ts +4 -4
- package/node/index.js +4 -4
- package/node/process/spawner/index.d.ts +61 -2
- package/node/process/spawner/index.js +6 -6
- package/node/sftp/file/index.d.ts +3 -3
- package/node/sftp/file/index.js +3 -3
- package/package.json +1030 -13
- package/components/Input/index.d.ts +0 -7
- package/components/Input/index.js +0 -29
- /package/components/{Input → Disclaimer}/styles.module.css +0 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// src/components/Paginator/index.tsx
|
|
2
|
+
import {
|
|
3
|
+
useEffect,
|
|
4
|
+
useRef,
|
|
5
|
+
Children,
|
|
6
|
+
useState
|
|
7
|
+
} from "react";
|
|
8
|
+
import { clss } from "../../agnostic/css/clss/index.js";
|
|
9
|
+
import { mergeClassNames } from "../utils/index.js";
|
|
10
|
+
import { paginator as publicClassName } from "../public-classnames.js";
|
|
11
|
+
import cssModule from "./styles.module.css";
|
|
12
|
+
import { jsx } from "react/jsx-runtime";
|
|
13
|
+
var Paginator = ({
|
|
14
|
+
thresholdOffsetPercent,
|
|
15
|
+
stateHandlers,
|
|
16
|
+
className,
|
|
17
|
+
children
|
|
18
|
+
}) => {
|
|
19
|
+
const [pagesState, setPagesState] = useState(/* @__PURE__ */ new Map());
|
|
20
|
+
const [directionState, setDirectionState] = useState(null);
|
|
21
|
+
const pagesRef = useRef(null);
|
|
22
|
+
const directionRef = useRef(null);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
stateHandlers?.pageChanged?.(Array.from(pagesState).map(([, state]) => state));
|
|
25
|
+
}, [pagesState]);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
stateHandlers?.directionChanged?.(directionState);
|
|
28
|
+
}, [directionState]);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
let lastCall = 0;
|
|
31
|
+
let lastScrollY = window.scrollY;
|
|
32
|
+
const handleScroll = (e) => {
|
|
33
|
+
const now = Date.now();
|
|
34
|
+
if (now - lastCall < 100) return;
|
|
35
|
+
lastCall = now;
|
|
36
|
+
const currentScrollY = window.scrollY;
|
|
37
|
+
const direction = currentScrollY > lastScrollY ? "forwards" : "backwards";
|
|
38
|
+
lastScrollY = currentScrollY;
|
|
39
|
+
if (direction !== directionRef.current) {
|
|
40
|
+
directionRef.current = direction;
|
|
41
|
+
setDirectionState(direction);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
window.addEventListener("scroll", handleScroll);
|
|
45
|
+
window.addEventListener("resize", handleScroll);
|
|
46
|
+
return () => {
|
|
47
|
+
window.removeEventListener("scroll", handleScroll);
|
|
48
|
+
window.removeEventListener("resize", handleScroll);
|
|
49
|
+
};
|
|
50
|
+
}, []);
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (pagesRef.current === null) return;
|
|
53
|
+
const pages = Array.from(pagesRef.current.children);
|
|
54
|
+
const observerRootMargin = `-${thresholdOffsetPercent ?? 0}% 0px -${100 - (thresholdOffsetPercent ?? 0)}% 0px`;
|
|
55
|
+
const observer = new IntersectionObserver((entries) => {
|
|
56
|
+
setPagesState((prevState) => {
|
|
57
|
+
const nextState = new Map(prevState);
|
|
58
|
+
entries.forEach((entry) => {
|
|
59
|
+
const index = pageIndexMap.get(entry.target);
|
|
60
|
+
if (index === void 0) return;
|
|
61
|
+
const prev = prevState.get(index);
|
|
62
|
+
const position = entry.isIntersecting ? "curr" : entry.boundingClientRect.bottom < (entry.rootBounds?.top ?? 0) ? "prev" : "next";
|
|
63
|
+
const currCount = position === "curr" && prev?.position !== "curr" ? (prev?.currCount ?? 0) + 1 : prev?.currCount ?? 0;
|
|
64
|
+
nextState.set(index, { position, currCount });
|
|
65
|
+
});
|
|
66
|
+
return nextState;
|
|
67
|
+
});
|
|
68
|
+
}, { rootMargin: observerRootMargin });
|
|
69
|
+
const pageIndexMap = /* @__PURE__ */ new Map();
|
|
70
|
+
pages.forEach((page, index) => {
|
|
71
|
+
pageIndexMap.set(page, index);
|
|
72
|
+
observer.observe(page);
|
|
73
|
+
});
|
|
74
|
+
return () => observer.disconnect();
|
|
75
|
+
}, [thresholdOffsetPercent, children]);
|
|
76
|
+
const c = clss(publicClassName, { cssModule });
|
|
77
|
+
const rootClss = mergeClassNames(
|
|
78
|
+
c(null, {
|
|
79
|
+
forwards: directionState === "forwards",
|
|
80
|
+
backwards: directionState === "backwards"
|
|
81
|
+
}),
|
|
82
|
+
className
|
|
83
|
+
);
|
|
84
|
+
const pagesClss = c("pages");
|
|
85
|
+
const childrenArr = Children.toArray(children);
|
|
86
|
+
return /* @__PURE__ */ jsx("div", { className: rootClss, children: /* @__PURE__ */ jsx(
|
|
87
|
+
"div",
|
|
88
|
+
{
|
|
89
|
+
className: pagesClss,
|
|
90
|
+
ref: pagesRef,
|
|
91
|
+
children: childrenArr.map((child, pos) => {
|
|
92
|
+
const state = pagesState.get(pos);
|
|
93
|
+
const pageClss = c("page", {
|
|
94
|
+
first: pos === 0,
|
|
95
|
+
last: pos === childrenArr.length - 1,
|
|
96
|
+
prev: state?.position === "prev",
|
|
97
|
+
curr: state?.position === "curr",
|
|
98
|
+
next: state?.position === "next"
|
|
99
|
+
});
|
|
100
|
+
return /* @__PURE__ */ jsx(
|
|
101
|
+
"div",
|
|
102
|
+
{
|
|
103
|
+
className: pageClss,
|
|
104
|
+
"data-page": pos,
|
|
105
|
+
"data-curr-count": state?.currCount ?? 0,
|
|
106
|
+
children: child
|
|
107
|
+
},
|
|
108
|
+
pos
|
|
109
|
+
);
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
) });
|
|
113
|
+
};
|
|
114
|
+
export {
|
|
115
|
+
Paginator
|
|
116
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type PropsWithChildren, type FunctionComponent } from 'react';
|
|
2
|
+
import type { WithClassName } from '../utils/types.js';
|
|
3
|
+
type ROEntryWithBoundingRect = {
|
|
4
|
+
entry: ResizeObserverEntry;
|
|
5
|
+
boundingClientRect: DOMRect;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Props for the ResizeObserverComponent.
|
|
9
|
+
*
|
|
10
|
+
* @property className - Optional additional class name(s) applied to the root element.
|
|
11
|
+
* @property onResized - Callback invoked when the element is resized.
|
|
12
|
+
* Receives the `ResizeObserverEntry` from the observed element, or `undefined` if none.
|
|
13
|
+
* @property children - React children rendered inside the root element. Only the root element is observed
|
|
14
|
+
*/
|
|
15
|
+
export type Props = PropsWithChildren<WithClassName<{
|
|
16
|
+
onResized?: (entry: ROEntryWithBoundingRect) => void;
|
|
17
|
+
}>>;
|
|
18
|
+
/**
|
|
19
|
+
* Component that observes its own size changes and exposes the dimensions.
|
|
20
|
+
* Updates are exposed both via data attributes (e.g., `data-width`) and CSS custom properties
|
|
21
|
+
* (e.g., `--<prefix>-width`, `--<prefix>-width-px`) for styling or scripting purposes.
|
|
22
|
+
* @param props - Component properties
|
|
23
|
+
* @see {@link Props}
|
|
24
|
+
* @returns A div wrapping `children`, with resize observation applied.
|
|
25
|
+
*/
|
|
26
|
+
export declare const ResizeObserverComponent: FunctionComponent<Props>;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// src/components/ResizeObserver/index.tsx
|
|
2
|
+
import {
|
|
3
|
+
useRef,
|
|
4
|
+
useEffect,
|
|
5
|
+
useState
|
|
6
|
+
} from "react";
|
|
7
|
+
import { clss } from "../../agnostic/css/clss/index.js";
|
|
8
|
+
import { mergeClassNames } from "../utils/index.js";
|
|
9
|
+
import { resizeObserver as publicClassName } from "../public-classnames.js";
|
|
10
|
+
import cssModule from "./style.module.css";
|
|
11
|
+
import { jsx } from "react/jsx-runtime";
|
|
12
|
+
var ResizeObserverComponent = ({
|
|
13
|
+
className,
|
|
14
|
+
onResized,
|
|
15
|
+
children
|
|
16
|
+
}) => {
|
|
17
|
+
const [roEntry, setRoEntry] = useState();
|
|
18
|
+
const rootRef = useRef(null);
|
|
19
|
+
const observerRef = useRef(null);
|
|
20
|
+
const createObserver = () => {
|
|
21
|
+
const root = rootRef.current;
|
|
22
|
+
observerRef.current?.disconnect();
|
|
23
|
+
if (root === null) return;
|
|
24
|
+
observerRef.current = new ResizeObserver((entries) => {
|
|
25
|
+
const firstEntry = entries[0];
|
|
26
|
+
if (firstEntry !== void 0) {
|
|
27
|
+
const boundingClientRect = firstEntry.target.getBoundingClientRect();
|
|
28
|
+
const fullEntry = {
|
|
29
|
+
entry: firstEntry,
|
|
30
|
+
boundingClientRect
|
|
31
|
+
};
|
|
32
|
+
setRoEntry(fullEntry);
|
|
33
|
+
if (onResized === void 0) return;
|
|
34
|
+
onResized(fullEntry);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
observerRef.current.observe(root);
|
|
38
|
+
};
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
createObserver();
|
|
41
|
+
return () => observerRef.current?.disconnect();
|
|
42
|
+
}, [onResized]);
|
|
43
|
+
const {
|
|
44
|
+
x,
|
|
45
|
+
y,
|
|
46
|
+
top,
|
|
47
|
+
left,
|
|
48
|
+
bottom,
|
|
49
|
+
right,
|
|
50
|
+
width,
|
|
51
|
+
height
|
|
52
|
+
} = roEntry?.entry.contentRect ?? {};
|
|
53
|
+
const contentRect = { x, y, top, left, bottom, right, width, height };
|
|
54
|
+
const dataAttributes = Object.entries(contentRect).reduce((acc, [key, val]) => {
|
|
55
|
+
if (val === void 0) return acc;
|
|
56
|
+
return { ...acc, [`data-${key}`]: val.toString() };
|
|
57
|
+
}, {});
|
|
58
|
+
const cssCustomProps = Object.entries(contentRect).reduce((acc, [key, val]) => {
|
|
59
|
+
if (val === void 0) return acc;
|
|
60
|
+
return {
|
|
61
|
+
...acc,
|
|
62
|
+
[`--${publicClassName}-${key}`]: val.toString(),
|
|
63
|
+
[`--${publicClassName}-${key}-px`]: `${val.toString()}px`
|
|
64
|
+
};
|
|
65
|
+
}, {});
|
|
66
|
+
const c = clss(publicClassName, { cssModule });
|
|
67
|
+
const rootClss = mergeClassNames(c(null), className);
|
|
68
|
+
return /* @__PURE__ */ jsx(
|
|
69
|
+
"div",
|
|
70
|
+
{
|
|
71
|
+
...dataAttributes,
|
|
72
|
+
className: rootClss,
|
|
73
|
+
ref: rootRef,
|
|
74
|
+
style: { ...cssCustomProps },
|
|
75
|
+
children
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
};
|
|
79
|
+
export {
|
|
80
|
+
ResizeObserverComponent
|
|
81
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { type PropsWithChildren, type FunctionComponent } from 'react';
|
|
2
|
+
import type { WithClassName } from '../utils/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Common properties shared by all block types.
|
|
5
|
+
*
|
|
6
|
+
* @property id - Optional stable identifier for the block. Used to consolidate
|
|
7
|
+
* blocks with the same id across multiple pages into a single sticky block
|
|
8
|
+
* displayed across those pages.
|
|
9
|
+
* @property trackScroll - Whether scroll tracking is enabled for this block.
|
|
10
|
+
* @property children - Content rendered inside the block.
|
|
11
|
+
*/
|
|
12
|
+
type PropsCommonBlock = PropsWithChildren<{
|
|
13
|
+
id?: string;
|
|
14
|
+
trackScroll?: boolean;
|
|
15
|
+
}>;
|
|
16
|
+
/**
|
|
17
|
+
* A block that scrolls with the page content.
|
|
18
|
+
*
|
|
19
|
+
* @property depth - When set to `'scroll'` or omitted, the block is rendered
|
|
20
|
+
* inline in the scrolling content layer.
|
|
21
|
+
*/
|
|
22
|
+
type PropsScrollBlock = PropsCommonBlock & {
|
|
23
|
+
depth?: 'scroll';
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* A block that sticks to the viewport, rendered outside the scroll flow.
|
|
27
|
+
*
|
|
28
|
+
* @property depth - `'back'` renders the block behind the scrolling content;
|
|
29
|
+
* `'front'` renders it in front.
|
|
30
|
+
* @property zIndex - Optional explicit z-index. Sticky blocks are otherwise
|
|
31
|
+
* stacked in the order they appear across pages (ascending).
|
|
32
|
+
*/
|
|
33
|
+
type PropsStickyBlock = PropsCommonBlock & {
|
|
34
|
+
depth: 'back' | 'front';
|
|
35
|
+
zIndex?: number;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Union of all block variants accepted by a {@link PropsPage}.
|
|
39
|
+
*
|
|
40
|
+
* @see {@link PropsScrollBlock}
|
|
41
|
+
* @see {@link PropsStickyBlock}
|
|
42
|
+
*/
|
|
43
|
+
type PropsBlock = PropsScrollBlock | PropsStickyBlock;
|
|
44
|
+
/**
|
|
45
|
+
* Describes a single page in the scrollytelling sequence.
|
|
46
|
+
*
|
|
47
|
+
* @property id - Optional identifier for the page.
|
|
48
|
+
* @property blocks - Ordered list of blocks belonging to this page.
|
|
49
|
+
* Scroll blocks are rendered inline; sticky blocks (`'back'` / `'front'`) are
|
|
50
|
+
* lifted into their respective layer and consolidated with same-id blocks from
|
|
51
|
+
* other pages.
|
|
52
|
+
*/
|
|
53
|
+
type PropsPage = {
|
|
54
|
+
id?: string;
|
|
55
|
+
blocks?: PropsBlock[];
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Props for the {@link Scrllgngn} component.
|
|
59
|
+
*
|
|
60
|
+
* @property pages - Ordered list of pages that compose the scrollytelling
|
|
61
|
+
* sequence. Each page may contain any mix of {@link PropsBlock} variants.
|
|
62
|
+
* @property thresholdOffsetPercent - Forwarded to the internal
|
|
63
|
+
* {@link Paginator}. Defines the viewport offset percentage used to determine
|
|
64
|
+
* which page is considered current.
|
|
65
|
+
* @property stickyBlocksLazyLoadDistance - Number of pages around the current
|
|
66
|
+
* page within which sticky blocks are mounted. Blocks outside this window are
|
|
67
|
+
* unmounted to save resources. Defaults to `2`.
|
|
68
|
+
* @property forceStickBlocks - Controls which out-of-range sticky blocks are
|
|
69
|
+
* forced into a stuck state regardless of the current page:
|
|
70
|
+
* - `'before'` — forces blocks from pages before the current one.
|
|
71
|
+
* - `'after'` — forces blocks from pages after the current one.
|
|
72
|
+
* - `'both'` — forces blocks on both sides.
|
|
73
|
+
* - `'none'` — no forcing (default behaviour).
|
|
74
|
+
* @property className - Optional additional class name(s) applied to the root element.
|
|
75
|
+
*/
|
|
76
|
+
export type Props = WithClassName<{
|
|
77
|
+
pages?: PropsPage[];
|
|
78
|
+
thresholdOffsetPercent?: number;
|
|
79
|
+
stickyBlocksLazyLoadDistance?: number;
|
|
80
|
+
forceStickBlocks?: 'before' | 'after' | 'both' | 'none';
|
|
81
|
+
}>;
|
|
82
|
+
/**
|
|
83
|
+
* Scrollytelling engine component. Orchestrates layered sticky blocks (`back`
|
|
84
|
+
* and `front`) over a paginated scrolling content area, with lazy loading,
|
|
85
|
+
* viewport bound detection, and dimension tracking.
|
|
86
|
+
*
|
|
87
|
+
* ### Root element modifiers
|
|
88
|
+
* The root `<div>` receives the public class name defined by `scrllgngn` and
|
|
89
|
+
* the following BEM-style modifier classes when active:
|
|
90
|
+
* - `--top-visible` — the top boundary sentinel is intersecting the viewport.
|
|
91
|
+
* - `--content-visible` — the scrolling content area is intersecting the viewport.
|
|
92
|
+
* - `--bottom-visible` — the bottom boundary sentinel is intersecting the viewport.
|
|
93
|
+
* - `--force-stick-blocks-before` — when `forceStickBlocks === 'before'`.
|
|
94
|
+
* - `--force-stick-blocks-after` — when `forceStickBlocks === 'after'`.
|
|
95
|
+
* - `--force-stick-blocks-both` — when `forceStickBlocks === 'both'`.
|
|
96
|
+
*
|
|
97
|
+
* ### Data attributes
|
|
98
|
+
* - `data-current-page-pos` — zero-based index of the page currently in view,
|
|
99
|
+
* updated on every page change reported by the internal {@link Paginator}.
|
|
100
|
+
*
|
|
101
|
+
* ### CSS custom properties
|
|
102
|
+
* Exposed on the root element and updated on resize via the internal
|
|
103
|
+
* {@link ResizeObserverComponent}:
|
|
104
|
+
* - `--scrllgngn-screen-left` / `--PRIVATE-left` — left edge of the bounding rect (px).
|
|
105
|
+
* - `--scrllgngn-screen-right` / `--PRIVATE-right` — right edge (px).
|
|
106
|
+
* - `--scrllgngn-screen-width` / `--PRIVATE-width` — total width (px).
|
|
107
|
+
* - `--scrllgngn-screen-height` / `--PRIVATE-height` — total height (px).
|
|
108
|
+
*
|
|
109
|
+
* ### Sticky block elements
|
|
110
|
+
* Each lazy-loaded sticky block receives:
|
|
111
|
+
* - `--active` modifier when the block's page range includes the current page.
|
|
112
|
+
* - `--lazy-loaded` modifier when the block is mounted but not on the current page.
|
|
113
|
+
* - An inline `z-index` derived from the block's position in the sorted stack
|
|
114
|
+
* (overridden by {@link PropsStickyBlock.zIndex} if provided).
|
|
115
|
+
*
|
|
116
|
+
* @param props - Component properties.
|
|
117
|
+
* @see {@link Props}
|
|
118
|
+
* @returns A div wrapping the full scrollytelling structure: top-bound sentinel,
|
|
119
|
+
* back-blocks layer, front-blocks layer, paginated scrolling content, and
|
|
120
|
+
* bottom-bound sentinel.
|
|
121
|
+
*/
|
|
122
|
+
export declare const Scrllgngn: FunctionComponent<Props>;
|
|
123
|
+
export {};
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// src/components/Scrllgngn/index.tsx
|
|
2
|
+
import {
|
|
3
|
+
useState,
|
|
4
|
+
useEffect
|
|
5
|
+
} from "react";
|
|
6
|
+
import { clss } from "../../agnostic/css/clss/index.js";
|
|
7
|
+
import { randomHash } from "../../agnostic/random/uuid/index.js";
|
|
8
|
+
import {
|
|
9
|
+
IntersectionObserverComponent
|
|
10
|
+
} from "../IntersectionObserver/index.js";
|
|
11
|
+
import {
|
|
12
|
+
Paginator
|
|
13
|
+
} from "../Paginator/index.js";
|
|
14
|
+
import {
|
|
15
|
+
ResizeObserverComponent
|
|
16
|
+
} from "../ResizeObserver/index.js";
|
|
17
|
+
import { mergeClassNames } from "../utils/index.js";
|
|
18
|
+
import { scrllgngn as publicClassName } from "../public-classnames.js";
|
|
19
|
+
import cssModule from "./styles.module.css";
|
|
20
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
21
|
+
var Scrllgngn = ({
|
|
22
|
+
pages,
|
|
23
|
+
thresholdOffsetPercent,
|
|
24
|
+
stickyBlocksLazyLoadDistance = 2,
|
|
25
|
+
forceStickBlocks,
|
|
26
|
+
className
|
|
27
|
+
}) => {
|
|
28
|
+
const [topVisible, setTopVis] = useState(false);
|
|
29
|
+
const [contentVisible, setCntVis] = useState(false);
|
|
30
|
+
const [bottomVisible, setBtmVis] = useState(false);
|
|
31
|
+
const [currentPagePos, setCurrentPagePos] = useState(0);
|
|
32
|
+
const [stickyBlocks, setStickyBlocks] = useState(/* @__PURE__ */ new Map());
|
|
33
|
+
const [partialBoundingRect, setPartialBoundingRect] = useState();
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const consolidatedBlocks = /* @__PURE__ */ new Map();
|
|
36
|
+
for (const page of pages ?? []) {
|
|
37
|
+
const pageIndex = pages?.indexOf(page) ?? -1;
|
|
38
|
+
for (const block of page.blocks ?? []) {
|
|
39
|
+
const blockId = block.id ?? randomHash(12);
|
|
40
|
+
const found = consolidatedBlocks.get(blockId);
|
|
41
|
+
if (found !== void 0) consolidatedBlocks.set(blockId, {
|
|
42
|
+
...found,
|
|
43
|
+
...block,
|
|
44
|
+
displayOnPages: [
|
|
45
|
+
...found.displayOnPages,
|
|
46
|
+
pageIndex
|
|
47
|
+
]
|
|
48
|
+
});
|
|
49
|
+
else consolidatedBlocks.set(blockId, {
|
|
50
|
+
...block,
|
|
51
|
+
displayOnPages: [pageIndex]
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const consolidatedStickyBlocks = new Map(Array.from(consolidatedBlocks).filter((e) => {
|
|
56
|
+
const block = e[1];
|
|
57
|
+
return block.depth === "back" || block.depth === "front";
|
|
58
|
+
}));
|
|
59
|
+
setStickyBlocks(consolidatedStickyBlocks);
|
|
60
|
+
}, [pages]);
|
|
61
|
+
const lazyLoadedBackBlocks = Array.from(stickyBlocks).filter(
|
|
62
|
+
([, block]) => block.depth === "back" && block.displayOnPages.some((dispPage) => {
|
|
63
|
+
const absDiff = Math.abs(dispPage - currentPagePos);
|
|
64
|
+
return absDiff <= stickyBlocksLazyLoadDistance;
|
|
65
|
+
})
|
|
66
|
+
).map(([, block]) => block).sort((a, b) => {
|
|
67
|
+
return (a.zIndex ?? -Infinity) - (b.zIndex ?? -Infinity);
|
|
68
|
+
});
|
|
69
|
+
const lazyLoadedFrontBlocks = Array.from(stickyBlocks).filter(
|
|
70
|
+
([, block]) => block.depth === "front" && block.displayOnPages.some((dispPage) => {
|
|
71
|
+
const absDiff = Math.abs(dispPage - currentPagePos);
|
|
72
|
+
return absDiff <= stickyBlocksLazyLoadDistance;
|
|
73
|
+
})
|
|
74
|
+
).map(([, block]) => block).sort((a, b) => {
|
|
75
|
+
return (a.zIndex ?? -Infinity) - (b.zIndex ?? -Infinity);
|
|
76
|
+
});
|
|
77
|
+
const handleTopBoundDetect = (e) => setTopVis(e.ioEntry?.isIntersecting ?? false);
|
|
78
|
+
const handleCntDetect = (e) => setCntVis(e.ioEntry?.isIntersecting ?? false);
|
|
79
|
+
const handleBtmBoundDetect = (e) => setBtmVis(e.ioEntry?.isIntersecting ?? false);
|
|
80
|
+
const handlePageChange = (pages2) => {
|
|
81
|
+
const curPagePos = pages2.findIndex((page) => page.position === "curr");
|
|
82
|
+
if (curPagePos === -1) return;
|
|
83
|
+
return setCurrentPagePos(curPagePos);
|
|
84
|
+
};
|
|
85
|
+
const handleResize = ({ boundingClientRect }) => {
|
|
86
|
+
if (partialBoundingRect === void 0 || boundingClientRect.left !== partialBoundingRect.left || boundingClientRect.right !== partialBoundingRect.right || boundingClientRect.height !== partialBoundingRect.height || boundingClientRect.width !== partialBoundingRect.width) setPartialBoundingRect(boundingClientRect);
|
|
87
|
+
};
|
|
88
|
+
const c = clss(publicClassName, { cssModule });
|
|
89
|
+
const rootClss = mergeClassNames(
|
|
90
|
+
c(null, {
|
|
91
|
+
"top-visible": topVisible,
|
|
92
|
+
"content-visible": contentVisible,
|
|
93
|
+
"bottom-visible": bottomVisible,
|
|
94
|
+
"force-stick-blocks-before": forceStickBlocks === "before",
|
|
95
|
+
"force-stick-blocks-after": forceStickBlocks === "after",
|
|
96
|
+
"force-stick-blocks-both": forceStickBlocks === "both"
|
|
97
|
+
}),
|
|
98
|
+
className
|
|
99
|
+
);
|
|
100
|
+
const customCssProps = {};
|
|
101
|
+
if (partialBoundingRect?.left !== void 0) {
|
|
102
|
+
customCssProps[`--${publicClassName}-screen-left`] = `${partialBoundingRect.left}px`;
|
|
103
|
+
customCssProps["--PRIVATE-left"] = `${partialBoundingRect.left}px`;
|
|
104
|
+
}
|
|
105
|
+
if (partialBoundingRect?.right !== void 0) {
|
|
106
|
+
customCssProps[`--${publicClassName}-screen-right`] = `${partialBoundingRect.right}px`;
|
|
107
|
+
customCssProps["--PRIVATE-right"] = `${partialBoundingRect.right}px`;
|
|
108
|
+
}
|
|
109
|
+
if (partialBoundingRect?.width !== void 0) {
|
|
110
|
+
customCssProps[`--${publicClassName}-screen-width`] = `${partialBoundingRect.width}px`;
|
|
111
|
+
customCssProps["--PRIVATE-width"] = `${partialBoundingRect.width}px`;
|
|
112
|
+
}
|
|
113
|
+
if (partialBoundingRect?.height !== void 0) {
|
|
114
|
+
customCssProps[`--${publicClassName}-screen-height`] = `${partialBoundingRect.height}px`;
|
|
115
|
+
customCssProps["--PRIVATE-height"] = `${partialBoundingRect.height}px`;
|
|
116
|
+
}
|
|
117
|
+
return /* @__PURE__ */ jsx(
|
|
118
|
+
"div",
|
|
119
|
+
{
|
|
120
|
+
className: rootClss,
|
|
121
|
+
"data-current-page-pos": currentPagePos,
|
|
122
|
+
style: { ...customCssProps },
|
|
123
|
+
children: /* @__PURE__ */ jsxs(ResizeObserverComponent, { onResized: handleResize, children: [
|
|
124
|
+
/* @__PURE__ */ jsx("div", { className: c("top-bound"), children: /* @__PURE__ */ jsx(IntersectionObserverComponent, { onIntersected: handleTopBoundDetect }) }),
|
|
125
|
+
/* @__PURE__ */ jsx("div", { className: c("back-blocks"), children: lazyLoadedBackBlocks.map((block, blockPos) => {
|
|
126
|
+
const isActive = block.displayOnPages.includes(currentPagePos);
|
|
127
|
+
const blockClss = c("back-block", {
|
|
128
|
+
active: isActive,
|
|
129
|
+
"lazy-loaded": !isActive
|
|
130
|
+
});
|
|
131
|
+
return /* @__PURE__ */ jsx(
|
|
132
|
+
"div",
|
|
133
|
+
{
|
|
134
|
+
className: blockClss,
|
|
135
|
+
style: { zIndex: blockPos },
|
|
136
|
+
children: block.children
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
}) }),
|
|
140
|
+
/* @__PURE__ */ jsx("div", { className: c("front-blocks"), children: lazyLoadedFrontBlocks.map((block, blockPos) => {
|
|
141
|
+
const isActive = block.displayOnPages.includes(currentPagePos);
|
|
142
|
+
const blockClss = c("front-block", {
|
|
143
|
+
active: isActive,
|
|
144
|
+
"lazy-loaded": !isActive
|
|
145
|
+
});
|
|
146
|
+
return /* @__PURE__ */ jsx(
|
|
147
|
+
"div",
|
|
148
|
+
{
|
|
149
|
+
className: blockClss,
|
|
150
|
+
style: { zIndex: blockPos },
|
|
151
|
+
children: block.children
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
}) }),
|
|
155
|
+
/* @__PURE__ */ jsx("div", { className: c("scrolling-content"), children: /* @__PURE__ */ jsx(IntersectionObserverComponent, { onIntersected: handleCntDetect, children: /* @__PURE__ */ jsx(
|
|
156
|
+
Paginator,
|
|
157
|
+
{
|
|
158
|
+
thresholdOffsetPercent,
|
|
159
|
+
stateHandlers: {
|
|
160
|
+
pageChanged: handlePageChange
|
|
161
|
+
},
|
|
162
|
+
children: pages?.map((page) => {
|
|
163
|
+
const scrollBlocks = page.blocks?.filter((b) => b.depth === "scroll" || b.depth === void 0) ?? [];
|
|
164
|
+
return /* @__PURE__ */ jsx(Fragment, { children: scrollBlocks.map((b) => b.children) });
|
|
165
|
+
})
|
|
166
|
+
}
|
|
167
|
+
) }) }),
|
|
168
|
+
/* @__PURE__ */ jsx("div", { className: c("bottom-bound"), children: /* @__PURE__ */ jsx(IntersectionObserverComponent, { onIntersected: handleBtmBoundDetect }) })
|
|
169
|
+
] })
|
|
170
|
+
}
|
|
171
|
+
);
|
|
172
|
+
};
|
|
173
|
+
export {
|
|
174
|
+
Scrllgngn
|
|
175
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
.root {
|
|
2
|
+
position: relative;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
/* MAIN LAYOUTS (back / front blocks, scrolling content) */
|
|
6
|
+
|
|
7
|
+
.back-blocks {
|
|
8
|
+
z-index: 1;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.scrolling-content {
|
|
12
|
+
z-index: 2;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.front-blocks {
|
|
16
|
+
z-index: 3;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* Scrllgngn entering the viewport, blocks not fixed yet */
|
|
20
|
+
.back-blocks,
|
|
21
|
+
.front-blocks,
|
|
22
|
+
.root.root--top-visible.root--content-visible .back-blocks,
|
|
23
|
+
.root.root--top-visible.root--content-visible .front-blocks {
|
|
24
|
+
position: absolute;
|
|
25
|
+
top: 0;
|
|
26
|
+
left: 0;
|
|
27
|
+
right: 0;
|
|
28
|
+
height: 100vh;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/*
|
|
32
|
+
Scrllgngn well in the viewport, fixed blocks
|
|
33
|
+
OR
|
|
34
|
+
Force stick blocks option
|
|
35
|
+
*/
|
|
36
|
+
.root.root--content-visible .back-blocks,
|
|
37
|
+
.root.root--content-visible .front-blocks,
|
|
38
|
+
|
|
39
|
+
.root.root--top-visible.root--content-visible.root--force-stick-blocks-before .back-blocks,
|
|
40
|
+
.root.root--top-visible.root--content-visible.root--force-stick-blocks-before .front-blocks,
|
|
41
|
+
.root.root--top-visible.root--content-visible.root--force-stick-blocks-both .back-blocks,
|
|
42
|
+
.root.root--top-visible.root--content-visible.root--force-stick-blocks-both .front-blocks,
|
|
43
|
+
|
|
44
|
+
.root.root--bottom-visible.root--content-visible.root--force-stick-blocks-after .back-blocks,
|
|
45
|
+
.root.root--bottom-visible.root--content-visible.root--force-stick-blocks-after .front-blocks,
|
|
46
|
+
.root.root--bottom-visible.root--content-visible.root--force-stick-blocks-both .back-blocks,
|
|
47
|
+
.root.root--bottom-visible.root--content-visible.root--force-stick-blocks-both .front-blocks {
|
|
48
|
+
position: fixed;
|
|
49
|
+
top: 0;
|
|
50
|
+
left: var(--PRIVATE-left);
|
|
51
|
+
width: var(--PRIVATE-width);
|
|
52
|
+
height: 100vh;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Scrllgngn leaving the viewport, blocks not fixed but have offset */
|
|
56
|
+
.root.root--bottom-visible.root--content-visible .back-blocks,
|
|
57
|
+
.root.root--bottom-visible.root--content-visible .front-blocks {
|
|
58
|
+
position: absolute;
|
|
59
|
+
top: calc(var(--PRIVATE-height) - 100vh);
|
|
60
|
+
left: 0;
|
|
61
|
+
right: 0;
|
|
62
|
+
height: 100vh;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* INDIVIDUAL FIXED BLOCKS */
|
|
66
|
+
|
|
67
|
+
.back-block,
|
|
68
|
+
.front-block {
|
|
69
|
+
position: absolute;
|
|
70
|
+
top: 0;
|
|
71
|
+
bottom: 0;
|
|
72
|
+
left: 0;
|
|
73
|
+
right: 0;
|
|
74
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { type FunctionComponent, type PropsWithChildren } from 'react';
|
|
2
|
+
import type { WithClassName } from '../utils/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Props for the {@link SequencerControlled} component.
|
|
5
|
+
*
|
|
6
|
+
* This is the low-level controlled interface. All state is driven externally —
|
|
7
|
+
* the component holds no internal state of its own. For the uncontrolled
|
|
8
|
+
* version that reacts to external events and derives these props automatically,
|
|
9
|
+
* see the default export of the parent module.
|
|
10
|
+
*
|
|
11
|
+
* @property step - Zero-based index of the currently active step. Determines
|
|
12
|
+
* which child is marked active, which are previous, and which are next.
|
|
13
|
+
* Defaults to `0`.
|
|
14
|
+
* @property activateOnStep - Optional 2D array that overrides the default
|
|
15
|
+
* one-child-per-step activation logic. Each entry at index `i` is the list of
|
|
16
|
+
* child indices that should be active when `step === i`, allowing multiple
|
|
17
|
+
* children to be simultaneously active on a given step. When omitted, exactly
|
|
18
|
+
* one child is active at a time (the child at index `step`).
|
|
19
|
+
* @property _modifiers - Internal modifier flags forwarded directly to the
|
|
20
|
+
* BEM class builder on the root element. Intended to be injected by the
|
|
21
|
+
* uncontrolled wrapper; avoid setting manually in consumer code.
|
|
22
|
+
* - `playing` — sequence is currently progressing.
|
|
23
|
+
* - `at-start` — current step is the first step.
|
|
24
|
+
* - `at-end` — current step is the last step.
|
|
25
|
+
* @property _dataAttributes - Internal data attribute values forwarded to the
|
|
26
|
+
* root element as `data-<key>`. Intended to be injected by the uncontrolled
|
|
27
|
+
* wrapper; avoid setting manually in consumer code.
|
|
28
|
+
* - `tempo` — exposed as `data-tempo`, reflects the current playback tempo.
|
|
29
|
+
* @property className - Optional additional class name(s) applied to the root element.
|
|
30
|
+
* @property children - The items to sequence. Each child is wrapped in a
|
|
31
|
+
* classifier `<div>` and receives one of the `--active`, `--previous`, or
|
|
32
|
+
* `--next` modifiers depending on its position relative to the current `step`.
|
|
33
|
+
*/
|
|
34
|
+
export type ControlledProps = PropsWithChildren<WithClassName<{
|
|
35
|
+
step?: number;
|
|
36
|
+
activateOnStep?: number[][];
|
|
37
|
+
_modifiers?: {
|
|
38
|
+
playing?: boolean;
|
|
39
|
+
'at-start'?: boolean;
|
|
40
|
+
'at-end'?: boolean;
|
|
41
|
+
};
|
|
42
|
+
_dataAttributes?: {
|
|
43
|
+
tempo?: number;
|
|
44
|
+
};
|
|
45
|
+
}>>;
|
|
46
|
+
/**
|
|
47
|
+
* Controlled sequencer component. Renders each child inside a classifier
|
|
48
|
+
* wrapper and assigns step-relative modifiers based on `step` and the optional
|
|
49
|
+
* `activateOnStep` override map.
|
|
50
|
+
*
|
|
51
|
+
* This component is purely presentational and holds no internal state.
|
|
52
|
+
* It is designed to be driven by the uncontrolled wrapper, which handles
|
|
53
|
+
* timing, playback events, and derives the props passed here.
|
|
54
|
+
*
|
|
55
|
+
* ### Root element modifiers
|
|
56
|
+
* The root `<div>` receives the public class name defined by `sequencer` and
|
|
57
|
+
* the following BEM-style modifier classes, sourced from `_modifiers`:
|
|
58
|
+
* - `--playing` — when the sequence is actively progressing.
|
|
59
|
+
* - `--at-start` — when the current step is the first step.
|
|
60
|
+
* - `--at-end` — when the current step is the last step.
|
|
61
|
+
*
|
|
62
|
+
* ### Data attributes on the root element
|
|
63
|
+
* Derived from `_dataAttributes`, each key is prefixed with `data-`:
|
|
64
|
+
* - `data-tempo` — current playback tempo, when provided.
|
|
65
|
+
*
|
|
66
|
+
* ### Child wrapper elements
|
|
67
|
+
* Each child is wrapped in a `<div>` with the `__child` element class and
|
|
68
|
+
* exactly one of the following mutually exclusive modifiers:
|
|
69
|
+
* - `--active` — this child corresponds to the current step (or is included
|
|
70
|
+
* in `activateOnStep[step]`).
|
|
71
|
+
* - `--previous` — this child was active in a prior step.
|
|
72
|
+
* - `--next` — this child has not yet been reached.
|
|
73
|
+
*
|
|
74
|
+
* @param props - Component properties.
|
|
75
|
+
* @see {@link ControlledProps}
|
|
76
|
+
* @returns A root `<div>` containing one classifier wrapper per child.
|
|
77
|
+
*/
|
|
78
|
+
export declare const SequencerControlled: FunctionComponent<ControlledProps>;
|