@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,173 @@
1
+ // src/components/Gallery/index.tsx
2
+ import {
3
+ Children,
4
+ useEffect,
5
+ useState,
6
+ useRef
7
+ } from "react";
8
+ import { clss } from "../../agnostic/css/clss/index.js";
9
+ import { mergeClassNames } from "../utils/index.js";
10
+ import { gallery as publicClassName } from "../public-classnames.js";
11
+ import { forceActivateSlot } from "./utils.js";
12
+ import cssModule from "./styles.module.css";
13
+ import { jsx, jsxs } from "react/jsx-runtime";
14
+ var Gallery = ({
15
+ paddingLeft,
16
+ paddingRight,
17
+ padding,
18
+ prevButtonContent,
19
+ nextButtonContent,
20
+ paginationContent,
21
+ initActive,
22
+ active,
23
+ noSnap,
24
+ stateHandlers,
25
+ actionHandlers,
26
+ children,
27
+ className
28
+ }) => {
29
+ const scrollerRef = useRef(null);
30
+ const [activeIndex, setActiveIndex] = useState(0);
31
+ const [canGoLeft, setCanGoLeft] = useState(false);
32
+ const [canGoRight, setCanGoRight] = useState(false);
33
+ const childrenCount = Children.count(children);
34
+ useEffect(() => {
35
+ stateHandlers?.slotChanged?.(activeIndex);
36
+ }, [activeIndex]);
37
+ const handlePrevClick = () => {
38
+ actionHandlers?.prevClick?.(activeIndex);
39
+ if (active === void 0) forceActivateSlot(scrollerRef.current, activeIndex - 1);
40
+ };
41
+ const handleNextClick = () => {
42
+ actionHandlers?.nextClick?.(activeIndex);
43
+ if (active === void 0) forceActivateSlot(scrollerRef.current, activeIndex + 1);
44
+ };
45
+ const handlePaginationClick = (pos) => {
46
+ actionHandlers?.paginationClick?.(activeIndex, pos);
47
+ if (active === void 0) forceActivateSlot(scrollerRef.current, pos);
48
+ };
49
+ useEffect(() => {
50
+ const scrollerElt = scrollerRef.current;
51
+ if (scrollerElt === null) return;
52
+ scrollerElt.scrollBy(-1, 0);
53
+ let animationFrame = null;
54
+ const update = () => {
55
+ animationFrame = null;
56
+ const { scrollLeft, clientWidth, scrollWidth } = scrollerElt;
57
+ const center = scrollLeft + clientWidth / 2;
58
+ const children2 = Array.from(scrollerElt.children);
59
+ let closestIndex = 0;
60
+ let closestDistance = Infinity;
61
+ children2.forEach((child, index) => {
62
+ const childCenter = child.offsetLeft + child.offsetWidth / 2;
63
+ const distance = Math.abs(center - childCenter);
64
+ if (distance < closestDistance) {
65
+ closestDistance = distance;
66
+ closestIndex = index;
67
+ }
68
+ });
69
+ setActiveIndex(closestIndex);
70
+ setCanGoLeft(scrollLeft > 0);
71
+ setCanGoRight(scrollLeft + clientWidth < scrollWidth);
72
+ };
73
+ const onScroll = () => {
74
+ if (animationFrame !== null) return;
75
+ animationFrame = requestAnimationFrame(update);
76
+ };
77
+ scrollerElt.addEventListener("scroll", onScroll, { passive: true });
78
+ update();
79
+ return () => {
80
+ scrollerElt.removeEventListener("scroll", onScroll);
81
+ if (animationFrame !== null) cancelAnimationFrame(animationFrame);
82
+ };
83
+ }, []);
84
+ useEffect(() => {
85
+ const toActivate = active ?? initActive;
86
+ if (toActivate === void 0) return;
87
+ const id = setTimeout(() => forceActivateSlot(scrollerRef.current, toActivate, false), 50);
88
+ return () => clearTimeout(id);
89
+ }, []);
90
+ useEffect(() => {
91
+ if (active === void 0) return;
92
+ forceActivateSlot(scrollerRef.current, active);
93
+ }, [active]);
94
+ const c = clss(publicClassName, { cssModule });
95
+ const rootClss = mergeClassNames(
96
+ c(null, {
97
+ "controlled": active !== void 0,
98
+ "no-snap": noSnap === true,
99
+ "at-first": activeIndex === 0,
100
+ "at-last": activeIndex === childrenCount - 1,
101
+ "can-go-left": canGoLeft,
102
+ "can-go-right": canGoRight
103
+ }),
104
+ className
105
+ );
106
+ const scrollerClss = c("scroller");
107
+ const actionsClss = c("actions");
108
+ const prevBtnClss = c("prev");
109
+ const nextBtnClss = c("next");
110
+ const paginationClss = c("pagination");
111
+ const dataAttributes = { "data-active": `${activeIndex}` };
112
+ const actualPaddingLeft = typeof paddingLeft === "number" ? `${paddingLeft}px` : typeof paddingLeft === "string" ? paddingLeft : typeof padding === "number" ? `${padding}px` : padding ?? "0px";
113
+ const actualPaddingRight = typeof paddingRight === "number" ? `${paddingRight}px` : typeof paddingRight === "string" ? paddingRight : typeof padding === "number" ? `${padding}px` : padding ?? "0px";
114
+ return /* @__PURE__ */ jsxs(
115
+ "div",
116
+ {
117
+ className: rootClss,
118
+ ...dataAttributes,
119
+ children: [
120
+ /* @__PURE__ */ jsx("div", { className: scrollerClss, ref: scrollerRef, children: Children.map(children, (child, pos) => {
121
+ const slotClss = c("slot", { active: pos === activeIndex });
122
+ const style = {
123
+ "margin-left": pos === 0 ? actualPaddingLeft : void 0,
124
+ "margin-right": pos === Children.count(children) - 1 ? actualPaddingRight : void 0
125
+ };
126
+ return /* @__PURE__ */ jsx(
127
+ "div",
128
+ {
129
+ className: `${slotClss}`,
130
+ style: { ...style },
131
+ "data-slot": pos,
132
+ children: child
133
+ },
134
+ pos
135
+ );
136
+ }) }),
137
+ /* @__PURE__ */ jsxs("div", { className: actionsClss, children: [
138
+ /* @__PURE__ */ jsx(
139
+ "div",
140
+ {
141
+ className: prevBtnClss,
142
+ onClick: handlePrevClick,
143
+ children: prevButtonContent ?? "prev"
144
+ }
145
+ ),
146
+ /* @__PURE__ */ jsx(
147
+ "div",
148
+ {
149
+ className: nextBtnClss,
150
+ onClick: handleNextClick,
151
+ children: nextButtonContent ?? "next"
152
+ }
153
+ )
154
+ ] }),
155
+ /* @__PURE__ */ jsx("div", { className: paginationClss, children: Children.map(children, (_, pos) => {
156
+ const pageClss = c("page", { active: pos === activeIndex });
157
+ return /* @__PURE__ */ jsx(
158
+ "div",
159
+ {
160
+ className: pageClss,
161
+ "data-page": pos,
162
+ onClick: () => handlePaginationClick(pos),
163
+ children: typeof paginationContent === "string" ? paginationContent : typeof paginationContent === "function" ? paginationContent(pos) : pos
164
+ }
165
+ );
166
+ }) })
167
+ ]
168
+ }
169
+ );
170
+ };
171
+ export {
172
+ Gallery
173
+ };
@@ -0,0 +1,33 @@
1
+ .scroller {
2
+ display: flex;
3
+ overflow-x: auto;
4
+ scroll-snap-type: x mandatory;
5
+ scroll-behavior: smooth;
6
+ -webkit-overflow-scrolling: touch;
7
+ scrollbar-width: none;
8
+ -ms-overflow-style: none;
9
+ scrollbar-color: transparent transparent;
10
+ }
11
+
12
+ .scroller::-webkit-scrollbar {
13
+ display: none;
14
+ height: 0;
15
+ }
16
+
17
+ .scroller::-webkit-scrollbar-track,
18
+ .scroller::-webkit-scrollbar-thumb {
19
+ background: transparent;
20
+ }
21
+
22
+ .root.root--controlled .scroller {
23
+ overflow-x: hidden;
24
+ }
25
+
26
+ .root.root--no-snap .scroller {
27
+ scroll-snap-type: unset;
28
+ scroll-behavior: unset;
29
+ }
30
+
31
+ .slot {
32
+ scroll-snap-align: center;
33
+ }
@@ -0,0 +1 @@
1
+ export declare const forceActivateSlot: (div: HTMLDivElement | null, targetPos: number, smooth?: boolean) => void;
@@ -0,0 +1,60 @@
1
+ import { type FunctionComponent, type ImgHTMLAttributes } from 'react';
2
+ import { type Props as DisclaimerProps } from '../Disclaimer/index.js';
3
+ import { type Props as TheatreProps } from '../Theatre/index.js';
4
+ import type { WithClassName } from '../utils/types.js';
5
+ /**
6
+ * Describes a single responsive image source for use in a `<picture>` element.
7
+ *
8
+ * @property srcSet - One or more image URLs with optional width/density descriptors
9
+ * (e.g. `'img@2x.png 2x'`).
10
+ * @property type - MIME type hint for the source (e.g. `'image/webp'`).
11
+ * @property media - Media condition under which this source is selected
12
+ * (e.g. `'(max-width: 768px)'`).
13
+ * @property sizes - Sizes attribute forwarded to the `<source>` element.
14
+ */
15
+ type SourceData = {
16
+ srcSet?: string;
17
+ type?: string;
18
+ media?: string;
19
+ sizes?: string;
20
+ };
21
+ /**
22
+ * Props for the {@link Image} component.
23
+ *
24
+ * Extends all native `ImgHTMLAttributes<HTMLImageElement>`, so any standard
25
+ * img attribute (`alt`, `loading`, `sizes`, `srcSet`, etc.) can be passed
26
+ * and will be forwarded to the underlying `<img>` element.
27
+ *
28
+ * @property sources - One or more responsive sources rendered as `<source>`
29
+ * elements inside a wrapping `<picture>`. Accepts:
30
+ * - a single srcSet string,
31
+ * - an array of srcSet strings,
32
+ * - an array of {@link SourceData} objects for full `<source>` control.
33
+ * @property disclaimer - Props forwarded to the internal {@link Disclaimer} component.
34
+ * While the disclaimer is active the `<img>` is hidden.
35
+ * @property theatre - Props forwarded to the internal {@link Theatre} component.
36
+ * @property className - Optional additional class name(s) applied to the root element.
37
+ */
38
+ export type Props = WithClassName<{
39
+ sources?: string | string[] | SourceData[];
40
+ disclaimer?: DisclaimerProps;
41
+ theatre?: TheatreProps;
42
+ }> & ImgHTMLAttributes<HTMLImageElement>;
43
+ /**
44
+ * Image component. Wraps a native `<img>` (or `<picture>`) element with
45
+ * optional responsive sources, an optional disclaimer gate, and
46
+ * viewport-driven visibility tracking.
47
+ *
48
+ *
49
+ * ### Child elements
50
+ * - `__picture` — wrapping `<picture>` element, always rendered. Contains the
51
+ * `<source>` elements (if any) and the `<img>`.
52
+ * - `__img` — the native `<img>` element.
53
+ *
54
+ * @param props - Component properties.
55
+ * @see {@link Props}
56
+ * @returns A `<figure>` element containing a `<picture>` with the image,
57
+ * and an optional disclaimer overlay.
58
+ */
59
+ export declare const Image: FunctionComponent<Props>;
60
+ export {};
@@ -0,0 +1,99 @@
1
+ // src/components/Image/index.tsx
2
+ import {
3
+ useState,
4
+ useMemo,
5
+ useCallback
6
+ } from "react";
7
+ import { clss } from "../../agnostic/css/clss/index.js";
8
+ import {
9
+ Disclaimer
10
+ } from "../Disclaimer/index.js";
11
+ import {
12
+ Theatre
13
+ } from "../Theatre/index.js";
14
+ import { mergeClassNames } from "../utils/index.js";
15
+ import { image as publicClassName } from "../public-classnames.js";
16
+ import cssModule from "./styles.module.css";
17
+ import { jsx, jsxs } from "react/jsx-runtime";
18
+ var Image = ({
19
+ sources,
20
+ disclaimer,
21
+ theatre,
22
+ className,
23
+ ...intrinsicImgAttributes
24
+ }) => {
25
+ const [isDisclaimerOn, setIsDisclaimerOn] = useState(
26
+ disclaimer?.isOn === true || disclaimer?.defaultIsOn === true || disclaimer !== void 0 && disclaimer.defaultIsOn === void 0
27
+ );
28
+ let shouldDisclaimerBeOn = isDisclaimerOn;
29
+ if (disclaimer?.isOn === true) shouldDisclaimerBeOn = true;
30
+ if (disclaimer?.isOn === false) shouldDisclaimerBeOn = false;
31
+ const parsedSources = useMemo(() => {
32
+ if (sources === void 0) return [];
33
+ if (typeof sources === "string") return [{ srcSet: sources }];
34
+ if (Array.isArray(sources)) {
35
+ if (sources.length === 0) return [];
36
+ if (typeof sources[0] === "string") return sources.map((srcSet) => ({ srcSet }));
37
+ return sources;
38
+ }
39
+ return [];
40
+ }, [sources]);
41
+ const handleDisclaimerDismissClick = useCallback(() => {
42
+ disclaimer?.actionHandlers?.dismissClick?.(isDisclaimerOn);
43
+ setIsDisclaimerOn(false);
44
+ }, []);
45
+ const c = clss(publicClassName, { cssModule });
46
+ const rootClss = mergeClassNames(c(), className);
47
+ const rootAttributes = {};
48
+ const pictureClss = c("picture");
49
+ const imgClss = c("image");
50
+ const sensitiveContent = /* @__PURE__ */ jsxs("picture", { className: pictureClss, children: [
51
+ parsedSources.map(
52
+ (source, index) => /* @__PURE__ */ jsx(
53
+ "source",
54
+ {
55
+ srcSet: typeof source === "string" ? source : source.srcSet,
56
+ type: typeof source === "string" ? void 0 : source.type,
57
+ media: typeof source === "string" ? void 0 : source.media,
58
+ sizes: typeof source === "string" ? void 0 : source.sizes
59
+ },
60
+ index
61
+ )
62
+ ),
63
+ /* @__PURE__ */ jsx(
64
+ "img",
65
+ {
66
+ className: imgClss,
67
+ ...intrinsicImgAttributes
68
+ }
69
+ )
70
+ ] });
71
+ const theatricalContent = theatre !== void 0 ? /* @__PURE__ */ jsx(
72
+ Theatre,
73
+ {
74
+ defaultIsOn: false,
75
+ ...theatre,
76
+ children: sensitiveContent
77
+ }
78
+ ) : sensitiveContent;
79
+ const disclaimedContent = disclaimer !== void 0 ? /* @__PURE__ */ jsx(
80
+ Disclaimer,
81
+ {
82
+ ...disclaimer,
83
+ isOn: shouldDisclaimerBeOn,
84
+ actionHandlers: { dismissClick: handleDisclaimerDismissClick },
85
+ children: theatricalContent
86
+ }
87
+ ) : theatricalContent;
88
+ return /* @__PURE__ */ jsx(
89
+ "figure",
90
+ {
91
+ className: rootClss,
92
+ ...rootAttributes,
93
+ children: disclaimedContent
94
+ }
95
+ );
96
+ };
97
+ export {
98
+ Image
99
+ };
File without changes
@@ -1,20 +1,57 @@
1
- import { type ReactNode, type JSX } from 'react';
1
+ import { type PropsWithChildren, type FunctionComponent } from 'react';
2
+ import type { WithClassName } from '../utils/types.js';
3
+ /** Alias for the native IntersectionObserver interface. */
2
4
  export type IO = IntersectionObserver;
5
+ /** Alias for the native IntersectionObserverEntry interface. */
3
6
  export type IOE = IntersectionObserverEntry;
4
- type ObserverOptions = {
7
+ /**
8
+ * Configuration options for the underlying IntersectionObserver instance.
9
+ *
10
+ * @property root - The element used as the viewport for checking visibility.
11
+ * If not provided, the browser viewport is used.
12
+ * @property rootMargin - Margin around the root, in CSS margin format
13
+ * (e.g. "10px 20px"). Expands or shrinks the effective root bounds.
14
+ * @property threshold - A single number or an array of numbers indicating
15
+ * at what percentage(s) of the target's visibility the observer callback
16
+ * should be executed.
17
+ */
18
+ export type ObserverOptions = {
5
19
  root?: HTMLElement;
6
20
  rootMargin?: string;
7
21
  threshold?: number[] | number;
8
22
  };
9
- export type Props = {
10
- className?: string;
11
- render?: ReactNode;
12
- onIntersection?: (details: {
23
+ /**
24
+ * Props for the IntersectionObserverComponent.
25
+ *
26
+ * @property onIntersected - Callback invoked whenever an intersection change
27
+ * is reported. Receives an object containing the current
28
+ * {@link IntersectionObserverEntry} (if available) and the active
29
+ * {@link IntersectionObserver} instance.
30
+ *
31
+ * @property root - See {@link ObserverOptions.root}.
32
+ * @property rootMargin - See {@link ObserverOptions.rootMargin}.
33
+ * @property threshold - See {@link ObserverOptions.threshold}.
34
+ * @property className - Optional additional class name(s) applied to the root element.
35
+ * @property children - React children rendered inside the observed element.
36
+ */
37
+ export type Props = PropsWithChildren<WithClassName<{
38
+ onIntersected?: (details: {
13
39
  ioEntry?: IOE | undefined;
14
40
  observer: IO;
15
41
  }) => void;
16
- content?: ReactNode;
17
- children?: ReactNode;
18
- } & ObserverOptions;
19
- export declare const IntersectionObserverComponent: ({ className, render, content, onIntersection, root, rootMargin, threshold, children }: Props) => JSX.Element;
20
- export {};
42
+ } & ObserverOptions>>;
43
+ /**
44
+ * Component that observes its root element using the IntersectionObserver API
45
+ * and notifies consumers about visibility changes.
46
+ *
47
+ * @param props - Component properties.
48
+ * @see {@link Props}
49
+ *
50
+ * @returns A div element wrapping `children`, observed for intersection changes.
51
+ *
52
+ * @remarks
53
+ * - Automatically creates and disconnects the {@link IntersectionObserver} instance.
54
+ * - Re-observes the element shortly after mount to handle late layout changes.
55
+ * - Adds an `is-intersecting` modifier class when the element is intersecting.
56
+ */
57
+ export declare const IntersectionObserverComponent: FunctionComponent<Props>;
@@ -6,18 +6,16 @@ import {
6
6
  useCallback
7
7
  } from "react";
8
8
  import { clss } from "../../agnostic/css/clss/index.js";
9
- import { isNotFalsy } from "../../agnostic/booleans/is-falsy/index.js";
10
- import { intersectionObserver } from "../public-classnames.js";
9
+ import { mergeClassNames } from "../utils/index.js";
10
+ import { intersectionObserver as publicClassName } from "../public-classnames.js";
11
11
  import cssModule from "./styles.module.css";
12
- import { jsxs } from "react/jsx-runtime";
12
+ import { jsx } from "react/jsx-runtime";
13
13
  var IntersectionObserverComponent = ({
14
- className,
15
- render,
16
- content,
17
- onIntersection,
14
+ onIntersected,
18
15
  root,
19
16
  rootMargin,
20
17
  threshold,
18
+ className,
21
19
  children
22
20
  }) => {
23
21
  const [ioEntry, setIoEntry] = useState(null);
@@ -26,12 +24,9 @@ var IntersectionObserverComponent = ({
26
24
  const observation = useCallback((entries, observer) => {
27
25
  const thisEntry = entries[0];
28
26
  if (thisEntry === void 0) return setIoEntry(null);
29
- if (onIntersection !== void 0) onIntersection({
30
- ioEntry: thisEntry,
31
- observer
32
- });
27
+ onIntersected?.({ ioEntry: thisEntry, observer });
33
28
  setIoEntry(thisEntry);
34
- }, [onIntersection]);
29
+ }, [onIntersected]);
35
30
  const forceObservation = useCallback(() => {
36
31
  const rootEl = rootRef.current;
37
32
  const observer = observerRef.current;
@@ -55,22 +50,18 @@ var IntersectionObserverComponent = ({
55
50
  window.clearTimeout(timeout2);
56
51
  };
57
52
  }, [forceObservation]);
58
- const c = clss(intersectionObserver, { cssModule });
53
+ const c = clss(publicClassName, { cssModule });
59
54
  const isIntersecting = ioEntry?.isIntersecting ?? false;
60
- const wrapperClassName = [
55
+ const rootClss = mergeClassNames(
61
56
  c(null, { "is-intersecting": isIntersecting }),
62
57
  className
63
- ].filter(isNotFalsy).join(" ");
64
- return /* @__PURE__ */ jsxs(
58
+ );
59
+ return /* @__PURE__ */ jsx(
65
60
  "div",
66
61
  {
62
+ className: rootClss,
67
63
  ref: rootRef,
68
- className: wrapperClassName,
69
- children: [
70
- render ?? null,
71
- children,
72
- content
73
- ]
64
+ children
74
65
  }
75
66
  );
76
67
  };
@@ -0,0 +1,72 @@
1
+ import { type PropsWithChildren, type FunctionComponent } from 'react';
2
+ import type { WithClassName } from '../utils/types.js';
3
+ /**
4
+ * State associated with a single page slot.
5
+ *
6
+ * @property position - The current position of the page relative to the viewport:
7
+ * - `'prev'` — the page has been scrolled past.
8
+ * - `'curr'` — the page is currently visible in the viewport.
9
+ * - `'next'` — the page has not yet been reached.
10
+ * @property currCount - The number of times this page has entered the `'curr'` position.
11
+ */
12
+ type PageState = {
13
+ position: 'prev' | 'curr' | 'next';
14
+ currCount: number;
15
+ };
16
+ /**
17
+ * Represents the scroll direction state of the paginator.
18
+ * - `'forwards'` — the user is scrolling down.
19
+ * - `'backwards'` — the user is scrolling up.
20
+ * - `null` — no scroll has been detected yet.
21
+ */
22
+ type DirectionState = 'forwards' | 'backwards' | null;
23
+ /**
24
+ * Props for the {@link Paginator} component.
25
+ *
26
+ * @property thresholdOffsetPercent - Optional percentage offset used to compute
27
+ * the {@link IntersectionObserver} root margin. Determines how far into the viewport
28
+ * a page must be before it is considered `'curr'`. Defaults to `0`.
29
+ *
30
+ * @property stateHandlers - Callbacks called after the internal state changed
31
+ * @property stateHandlers.directionChanged - Callback invoked when the scroll direction changes.
32
+ * Receives the new {@link DirectionState}. Only fires when the direction actually
33
+ * changes — repeated scrolls in the same direction do not trigger it again.
34
+ *
35
+ * @property stateHandlers.pageChanged - Callback invoked whenever any page's {@link PageState}
36
+ * changes. Receives a flat array of all pages' states, ordered by position.
37
+ *
38
+ * @property className - Optional additional class name(s) applied to the root element.
39
+ * @property children - Each direct child is treated as an individual page slot.
40
+ */
41
+ export type Props = PropsWithChildren<WithClassName<{
42
+ thresholdOffsetPercent?: number;
43
+ stateHandlers?: {
44
+ directionChanged?: (direction: DirectionState) => void;
45
+ pageChanged?: (pages: PageState[]) => void;
46
+ };
47
+ }>>;
48
+ /**
49
+ * A scroll-driven pagination component that tracks which child page is currently
50
+ * visible in the viewport and the direction of scroll.
51
+ *
52
+ * Each direct child is wrapped in a page slot and observed via
53
+ * {@link IntersectionObserver}. Page slots receive `'prev'`, `'curr'`, or `'next'`
54
+ * positional modifier classes based on their visibility.
55
+ *
56
+ * @param props - Component properties.
57
+ * @see {@link Props}
58
+ *
59
+ * @returns A root div containing a pages wrapper, with each child isolated in
60
+ * its own observed page slot div.
61
+ *
62
+ * @remarks
63
+ * - Scroll direction is throttled to one update per 100ms and only dispatched
64
+ * when the direction actually changes, using an internal ref to avoid stale
65
+ * closure comparisons.
66
+ * - Page visibility is tracked via a single {@link IntersectionObserver} instance
67
+ * that is recreated when `thresholdOffsetPercent` or `children` change.
68
+ * - `currCount` on each {@link PageState} increments each time a page transitions
69
+ * into the `'curr'` position, making it useful as a re-entry counter.
70
+ */
71
+ export declare const Paginator: FunctionComponent<Props>;
72
+ export {};