@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.
Files changed (129) hide show
  1. package/agnostic/arrays/index.d.ts +1 -1
  2. package/agnostic/arrays/index.js +1 -1
  3. package/agnostic/colors/index.d.ts +2 -2
  4. package/agnostic/colors/index.js +2 -2
  5. package/agnostic/css/clss/index.d.ts +53 -1
  6. package/agnostic/css/clss/index.js +1 -1
  7. package/agnostic/css/index.d.ts +2 -2
  8. package/agnostic/css/index.js +2 -2
  9. package/agnostic/errors/index.d.ts +1 -1
  10. package/agnostic/errors/index.js +1 -1
  11. package/agnostic/html/hyper-json/smart-tags/coalesced/index.d.ts +20 -20
  12. package/agnostic/html/hyper-json/smart-tags/coalesced/index.js +20 -20
  13. package/agnostic/html/hyper-json/smart-tags/isolated/index.d.ts +4 -4
  14. package/agnostic/html/hyper-json/smart-tags/isolated/index.js +4 -4
  15. package/agnostic/index.d.ts +4 -4
  16. package/agnostic/index.js +4 -4
  17. package/agnostic/misc/assert/index.d.ts +3 -0
  18. package/agnostic/misc/crossenv/index.d.ts +1 -1
  19. package/agnostic/misc/crossenv/index.js +1 -1
  20. package/agnostic/misc/index.d.ts +5 -5
  21. package/agnostic/misc/index.js +5 -5
  22. package/agnostic/misc/logs/index.d.ts +1 -1
  23. package/agnostic/misc/logs/index.js +1 -1
  24. package/agnostic/misc/logs/logger/index.d.ts +10 -0
  25. package/agnostic/misc/logs/logger/index.js +40 -10
  26. package/agnostic/misc/logs/styles/index.d.ts +1 -0
  27. package/agnostic/misc/logs/styles/index.js +27 -9
  28. package/agnostic/numbers/index.d.ts +2 -2
  29. package/agnostic/numbers/index.js +2 -2
  30. package/agnostic/objects/index.d.ts +4 -4
  31. package/agnostic/objects/index.js +4 -4
  32. package/agnostic/sanitization/index.d.ts +1 -1
  33. package/agnostic/sanitization/index.js +1 -1
  34. package/agnostic/strings/index.d.ts +1 -1
  35. package/agnostic/strings/index.js +1 -1
  36. package/agnostic/time/index.d.ts +2 -2
  37. package/agnostic/time/index.js +2 -2
  38. package/agnostic/time/transitions/index.d.ts +3 -3
  39. package/agnostic/time/transitions/index.js +4 -4
  40. package/components/Disclaimer/index.d.ts +45 -0
  41. package/components/Disclaimer/index.js +70 -0
  42. package/components/Drawer/index.d.ts +45 -0
  43. package/components/Drawer/index.js +82 -0
  44. package/components/Drawer/styles.module.css +0 -0
  45. package/components/EventListener/index.d.ts +20 -3
  46. package/components/EventListener/index.js +15 -22
  47. package/components/Gallery/index.d.ts +67 -0
  48. package/components/Gallery/index.js +173 -0
  49. package/components/Gallery/styles.module.css +33 -0
  50. package/components/Gallery/utils.d.ts +1 -0
  51. package/components/Image/index.d.ts +60 -0
  52. package/components/Image/index.js +99 -0
  53. package/components/Image/styles.module.css +0 -0
  54. package/components/IntersectionObserver/index.d.ts +48 -11
  55. package/components/IntersectionObserver/index.js +13 -22
  56. package/components/Paginator/index.d.ts +72 -0
  57. package/components/Paginator/index.js +116 -0
  58. package/components/Paginator/styles.module.css +9 -0
  59. package/components/ResizeObserver/index.d.ts +27 -0
  60. package/components/ResizeObserver/index.js +81 -0
  61. package/components/Scrllgngn/index.d.ts +123 -0
  62. package/components/Scrllgngn/index.js +175 -0
  63. package/components/Scrllgngn/styles.module.css +74 -0
  64. package/components/Sequencer/index.controlled.d.ts +78 -0
  65. package/components/Sequencer/index.d.ts +85 -0
  66. package/components/Sequencer/index.js +109 -0
  67. package/components/Sequencer/styles.module.css +0 -0
  68. package/components/ShadowRoot/index.d.ts +35 -0
  69. package/components/ShadowRoot/index.js +56 -0
  70. package/components/ShadowRoot/styles.module.css +0 -0
  71. package/components/Subtitles/index.d.ts +58 -0
  72. package/components/Subtitles/index.js +111 -0
  73. package/components/Subtitles/styles.module.css +0 -0
  74. package/components/Subtitles/types.d.ts +10 -0
  75. package/components/Subtitles/types.js +0 -0
  76. package/components/Subtitles/utils.d.ts +28 -0
  77. package/components/Theatre/index.d.ts +64 -0
  78. package/components/Theatre/index.js +97 -0
  79. package/components/Theatre/styles.module.css +0 -0
  80. package/components/Video/index.d.ts +119 -0
  81. package/components/Video/index.js +358 -0
  82. package/components/Video/styles.module.css +0 -0
  83. package/components/Video/utils.d.ts +10 -0
  84. package/components/_WIP_AudioQuote/index.d.ts +1 -0
  85. package/components/_WIP_AudioQuote/index.js +0 -0
  86. package/components/_WIP_Icon/index.d.ts +1 -0
  87. package/components/_WIP_Icon/index.js +0 -0
  88. package/components/index.d.ts +15 -1
  89. package/components/index.js +15 -1
  90. package/components/public-classnames.d.ts +14 -3
  91. package/components/utils/index.d.ts +1 -0
  92. package/components/utils/index.js +12 -0
  93. package/components/utils/types.d.ts +3 -0
  94. package/components/utils/types.js +0 -0
  95. package/index.d.ts +1 -1
  96. package/index.js +1 -1
  97. package/node/@aws-s3/index.test.d.ts +1 -0
  98. package/node/@aws-s3/storage/directory/index.d.ts +1 -1
  99. package/node/@aws-s3/storage/directory/index.js +1 -1
  100. package/node/@aws-s3/storage/file/index.d.ts +3 -3
  101. package/node/@aws-s3/storage/file/index.js +3 -3
  102. package/node/@google-cloud/storage/directory/index.d.ts +2 -2
  103. package/node/@google-cloud/storage/directory/index.js +2 -2
  104. package/node/@google-cloud/storage/file/index.d.ts +4 -4
  105. package/node/@google-cloud/storage/file/index.js +4 -4
  106. package/node/cloud-storage/operations/index.d.ts +3 -3
  107. package/node/cloud-storage/operations/index.js +3 -3
  108. package/node/encryption/index.d.ts +1 -1
  109. package/node/encryption/index.js +1 -1
  110. package/node/files/index.d.ts +1 -1
  111. package/node/files/index.js +1 -1
  112. package/node/ftps/directory/index.d.ts +2 -2
  113. package/node/ftps/directory/index.js +2 -2
  114. package/node/ftps/file/index.d.ts +1 -1
  115. package/node/ftps/file/index.js +1 -1
  116. package/node/images/index.d.ts +3 -3
  117. package/node/images/index.js +3 -3
  118. package/node/images/transform/operations/index.d.ts +6 -6
  119. package/node/images/transform/operations/index.js +6 -6
  120. package/node/index.d.ts +4 -4
  121. package/node/index.js +4 -4
  122. package/node/process/spawner/index.d.ts +61 -2
  123. package/node/process/spawner/index.js +6 -6
  124. package/node/sftp/file/index.d.ts +3 -3
  125. package/node/sftp/file/index.js +3 -3
  126. package/package.json +1030 -13
  127. package/components/Input/index.d.ts +0 -7
  128. package/components/Input/index.js +0 -29
  129. /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,9 @@
1
+ .threshold {
2
+ position: fixed;
3
+ left: 0px;
4
+ right: 0px;
5
+ height: 0px;
6
+ user-select: none;
7
+ pointer-events: none;
8
+ z-index: -9999;
9
+ }
@@ -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>;