@design-edito/tools 0.5.2 → 0.5.4
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/html/deep-select/index.js +5 -3
- package/agnostic/html/hyper-json/smart-tags/coalesced/index.d.ts +9 -9
- package/agnostic/html/hyper-json/smart-tags/coalesced/index.js +9 -9
- package/agnostic/html/hyper-json/smart-tags/isolated/index.d.ts +3 -3
- package/agnostic/html/hyper-json/smart-tags/isolated/index.js +3 -3
- package/agnostic/html/index.d.ts +1 -1
- package/agnostic/html/index.js +1 -1
- package/agnostic/index.d.ts +2 -2
- package/agnostic/index.js +2 -2
- package/agnostic/misc/index.d.ts +3 -3
- package/agnostic/misc/index.js +3 -3
- package/agnostic/misc/logs/index.d.ts +1 -1
- package/agnostic/misc/logs/index.js +1 -1
- package/agnostic/numbers/index.d.ts +1 -1
- package/agnostic/numbers/index.js +1 -1
- package/agnostic/optim/index.d.ts +1 -1
- package/agnostic/optim/index.js +1 -1
- package/agnostic/strings/index.d.ts +2 -2
- package/agnostic/strings/index.js +2 -2
- package/agnostic/time/dates/format-date/index.d.ts +4 -2
- package/agnostic/time/dates/format-date/index.js +43 -94
- package/agnostic/time/dates/format-date/index.test.js +154 -0
- package/agnostic/time/dates/format-date/parts.d.ts +28 -0
- package/agnostic/time/dates/format-date/parts.js +136 -0
- package/agnostic/time/dates/format-date/types.d.ts +84 -0
- package/agnostic/time/dates/format-date/types.js +1 -0
- package/agnostic/time/duration/format-duration/index.d.ts +42 -0
- package/agnostic/time/duration/format-duration/index.js +94 -0
- package/agnostic/time/duration/format-duration/index.test.d.ts +1 -0
- package/agnostic/time/duration/format-duration/index.test.js +148 -0
- package/agnostic/time/duration/format-duration/parts.d.ts +32 -0
- package/agnostic/time/duration/format-duration/parts.js +107 -0
- package/agnostic/time/duration/format-duration/types.d.ts +93 -0
- package/agnostic/time/duration/format-duration/types.js +1 -0
- package/agnostic/time/duration/index.d.ts +6 -1
- package/agnostic/time/duration/index.js +2 -2
- package/agnostic/time/duration/index.test.d.ts +1 -0
- package/agnostic/time/duration/index.test.js +168 -0
- package/agnostic/time/index.d.ts +1 -1
- package/agnostic/time/index.js +1 -1
- package/components/BeforeAfter/index.controlled.d.ts +25 -25
- package/components/BeforeAfter/index.controlled.js +47 -53
- package/components/BeforeAfter/index.d.ts +20 -23
- package/components/BeforeAfter/index.js +48 -40
- package/components/Button/index.js +1 -1
- package/components/Clippable/index.d.ts +17 -26
- package/components/Clippable/index.js +21 -23
- package/components/Disclaimer/index.d.ts +32 -24
- package/components/Disclaimer/index.js +29 -24
- package/components/Drawer/index.d.ts +45 -25
- package/components/Drawer/index.js +61 -52
- package/components/EventListener/index.d.ts +4 -5
- package/components/Gallery/index.d.ts +14 -16
- package/components/Gallery/index.js +25 -30
- package/components/Iframe/index.js +2 -2
- package/components/Image/index.d.ts +5 -15
- package/components/Image/index.js +9 -34
- package/components/Input/index.d.ts +23 -19
- package/components/Input/index.js +30 -20
- package/components/JsonEditor/index.d.ts +177 -9
- package/components/JsonEditor/index.js +133 -77
- package/components/JsonEditor/types.d.ts +11 -0
- package/components/JsonEditor/types.js +1 -0
- package/components/JsonEditor/utils.d.ts +8 -0
- package/components/JsonEditor/utils.js +23 -0
- package/components/ListLoader/index.controlled.d.ts +5 -5
- package/components/ListLoader/index.controlled.js +4 -4
- package/components/ListLoader/index.d.ts +11 -11
- package/components/ListLoader/index.js +12 -12
- package/components/Overlayer/index.d.ts +4 -4
- package/components/Overlayer/index.js +6 -6
- package/components/Paginator/index.d.ts +7 -12
- package/components/Paginator/index.js +7 -11
- package/components/ResizeObserver/index.d.ts +14 -3
- package/components/ResizeObserver/index.js +49 -24
- package/components/Scrllgngn/index.d.ts +17 -13
- package/components/Scrllgngn/index.js +25 -84
- package/components/Scrllgngn/utils.d.ts +52 -0
- package/components/Scrllgngn/utils.js +102 -0
- package/components/ScrollListener/index.d.ts +43 -28
- package/components/ScrollListener/index.js +59 -79
- package/components/ScrollListener/utils.d.ts +37 -16
- package/components/ScrollListener/utils.js +141 -28
- package/components/Select/index.d.ts +24 -20
- package/components/Select/index.js +30 -20
- package/components/Sequencer/index.controlled.d.ts +16 -26
- package/components/Sequencer/index.controlled.js +15 -12
- package/components/Sequencer/index.d.ts +25 -30
- package/components/Sequencer/index.js +20 -34
- package/components/Subtitles/index.d.ts +29 -5
- package/components/Subtitles/index.js +90 -12
- package/components/Textarea/index.d.ts +26 -20
- package/components/Textarea/index.js +40 -21
- package/components/Theatre/index.d.ts +35 -39
- package/components/Theatre/index.js +46 -58
- package/components/UIModule/index.d.ts +25 -29
- package/components/UIModule/index.js +81 -110
- package/components/Video/index.controlled.d.ts +51 -51
- package/components/Video/index.controlled.js +75 -72
- package/components/Video/index.d.ts +12 -14
- package/components/Video/index.js +41 -86
- package/components/Video/utils.d.ts +35 -3
- package/components/Video/utils.js +37 -14
- package/components/index.d.ts +6 -6
- package/components/index.js +6 -6
- package/components/utils/index.d.ts +22 -0
- package/components/utils/index.js +31 -0
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/node/@aws-s3/storage/directory/index.d.ts +2 -2
- package/node/@aws-s3/storage/directory/index.js +2 -2
- package/node/@aws-s3/storage/file/index.d.ts +1 -1
- package/node/@aws-s3/storage/file/index.js +1 -1
- package/node/@aws-s3/storage/index.d.ts +1 -1
- package/node/@aws-s3/storage/index.js +1 -1
- package/node/@google-cloud/storage/directory/index.d.ts +2 -2
- package/node/@google-cloud/storage/directory/index.js +2 -2
- package/node/@google-cloud/storage/file/index.d.ts +2 -2
- package/node/@google-cloud/storage/file/index.js +2 -2
- package/node/@google-cloud/storage/index.d.ts +1 -1
- package/node/@google-cloud/storage/index.js +1 -1
- package/node/cloud-storage/operations/index.d.ts +2 -2
- package/node/cloud-storage/operations/index.js +2 -2
- package/node/ftps/file/index.d.ts +2 -2
- package/node/ftps/file/index.js +2 -2
- package/node/images/index.d.ts +1 -1
- package/node/images/index.js +1 -1
- package/node/images/transform/operations/index.d.ts +2 -2
- package/node/images/transform/operations/index.js +2 -2
- package/node/index.d.ts +1 -1
- package/node/index.js +1 -1
- package/node/process/index.d.ts +1 -1
- package/node/process/index.js +1 -1
- package/node/sftp/file/index.d.ts +2 -2
- package/node/sftp/file/index.js +2 -2
- package/node/sftp/index.d.ts +1 -1
- package/node/sftp/index.js +1 -1
- package/package.json +20 -5
- package/TODO.md +0 -269
- package/components/BeforeAfter/utils.d.ts +0 -4
- package/components/BeforeAfter/utils.js +0 -9
- package/components/Input/index.controlled.d.ts +0 -42
- package/components/Input/index.controlled.js +0 -54
- package/components/Select/index.controlled.d.ts +0 -43
- package/components/Select/index.controlled.js +0 -54
- package/components/Subtitles/types.d.ts +0 -24
- package/components/Subtitles/utils.d.ts +0 -32
- package/components/Subtitles/utils.js +0 -107
- package/components/Textarea/index.controlled.d.ts +0 -50
- package/components/Textarea/index.controlled.js +0 -75
- /package/{components/Subtitles/types.js → agnostic/time/dates/format-date/index.test.d.ts} +0 -0
- /package/components/ResizeObserver/{style.module.css → styles.module.css} +0 -0
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
/* * * * * * * * * * * * * * * * *
|
|
2
|
+
*
|
|
3
|
+
* Measuring
|
|
4
|
+
*
|
|
5
|
+
* * * * * * * * * * * * * * * * */
|
|
6
|
+
function measureGlobal() {
|
|
2
7
|
const win = {
|
|
3
8
|
height: window.innerHeight,
|
|
4
9
|
width: window.innerWidth
|
|
@@ -18,34 +23,142 @@ export const globalDocumentScrollListener = () => {
|
|
|
18
23
|
maxY: scroll.y + win.height
|
|
19
24
|
};
|
|
20
25
|
return { win, html, scroll, viewport };
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const offsetX = div.offsetLeft;
|
|
24
|
-
const offsetY = div.offsetTop;
|
|
25
|
-
const width = div.offsetWidth;
|
|
26
|
-
const height = div.offsetHeight;
|
|
26
|
+
}
|
|
27
|
+
function measureLocal(element) {
|
|
27
28
|
return {
|
|
28
|
-
offsetX,
|
|
29
|
-
offsetY,
|
|
30
|
-
width,
|
|
31
|
-
height
|
|
29
|
+
offsetX: element.offsetLeft,
|
|
30
|
+
offsetY: element.offsetTop,
|
|
31
|
+
width: element.offsetWidth,
|
|
32
|
+
height: element.offsetHeight
|
|
32
33
|
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
}
|
|
35
|
+
/* * * * * * * * * * * * * * * * *
|
|
36
|
+
*
|
|
37
|
+
* The shared listener
|
|
38
|
+
*
|
|
39
|
+
* * * * * * * * * * * * * * * * */
|
|
40
|
+
/**
|
|
41
|
+
* Every mounted {@link ScrollListener} shares one pair of `scroll` / `resize`
|
|
42
|
+
* listeners and one measurement pass per animation frame, however many
|
|
43
|
+
* components are on the page: the document is measured once, and only each
|
|
44
|
+
* subscriber's own element is measured individually.
|
|
45
|
+
*
|
|
46
|
+
* The listeners are attached on the first subscription and detached on the
|
|
47
|
+
* last, so importing this module has no effect on its own — which is what keeps
|
|
48
|
+
* it importable outside a browser.
|
|
49
|
+
*/
|
|
50
|
+
const subscribers = new Map();
|
|
51
|
+
let scheduledFrame = null;
|
|
52
|
+
function notifySubscribers() {
|
|
53
|
+
scheduledFrame = null;
|
|
54
|
+
const global = measureGlobal();
|
|
55
|
+
subscribers.forEach(({ rootRef, onScrollStateChange }) => {
|
|
56
|
+
const element = rootRef.current;
|
|
57
|
+
if (element === null)
|
|
39
58
|
return;
|
|
40
|
-
|
|
41
|
-
setData({ global, local });
|
|
59
|
+
onScrollStateChange({ global, local: measureLocal(element) });
|
|
42
60
|
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
61
|
+
}
|
|
62
|
+
/** Coalesces every event of a frame into a single measurement pass. */
|
|
63
|
+
function scheduleNotification() {
|
|
64
|
+
if (scheduledFrame !== null)
|
|
65
|
+
return;
|
|
66
|
+
scheduledFrame = window.requestAnimationFrame(notifySubscribers);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Starts tracking one element, attaching the shared listeners if this is the
|
|
70
|
+
* first subscriber. A measurement is scheduled right away, so a subscriber gets
|
|
71
|
+
* its initial state without waiting for a first scroll.
|
|
72
|
+
*
|
|
73
|
+
* @param id - Identifies the subscriber for {@link unsubscribe}.
|
|
74
|
+
* @param subscriber - The element to track and where to report.
|
|
75
|
+
*/
|
|
76
|
+
export function subscribe(id, subscriber) {
|
|
77
|
+
const isFirstSubscriber = subscribers.size === 0;
|
|
78
|
+
subscribers.set(id, subscriber);
|
|
79
|
+
if (isFirstSubscriber) {
|
|
80
|
+
window.addEventListener('scroll', scheduleNotification, { passive: true });
|
|
81
|
+
window.addEventListener('resize', scheduleNotification, { passive: true });
|
|
82
|
+
}
|
|
83
|
+
scheduleNotification();
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Stops tracking one element, detaching the shared listeners once no subscriber
|
|
87
|
+
* is left. Unsubscribing an unknown id is a no-op.
|
|
88
|
+
*
|
|
89
|
+
* @param id - The id passed to {@link subscribe}.
|
|
90
|
+
*/
|
|
91
|
+
export function unsubscribe(id) {
|
|
92
|
+
subscribers.delete(id);
|
|
93
|
+
if (subscribers.size > 0)
|
|
94
|
+
return;
|
|
95
|
+
window.removeEventListener('scroll', scheduleNotification);
|
|
96
|
+
window.removeEventListener('resize', scheduleNotification);
|
|
97
|
+
if (scheduledFrame === null)
|
|
98
|
+
return;
|
|
99
|
+
window.cancelAnimationFrame(scheduledFrame);
|
|
100
|
+
scheduledFrame = null;
|
|
101
|
+
}
|
|
102
|
+
/* * * * * * * * * * * * * * * * *
|
|
103
|
+
*
|
|
104
|
+
* CSS custom properties
|
|
105
|
+
*
|
|
106
|
+
* * * * * * * * * * * * * * * * */
|
|
107
|
+
/** Spans the scroll positions where the element is fully inside the viewport. */
|
|
108
|
+
function innerRange(offset, size, winSize) {
|
|
109
|
+
return [offset + size - winSize, offset];
|
|
110
|
+
}
|
|
111
|
+
/** Spans the scroll positions where the element overlaps the viewport at all. */
|
|
112
|
+
function outerRange(offset, size, winSize) {
|
|
113
|
+
return [offset - winSize, offset + size];
|
|
114
|
+
}
|
|
115
|
+
/** Where `scroll` sits in `range`, as a `0`–`1` ratio. */
|
|
116
|
+
function progressIn(scroll, [from, to]) {
|
|
117
|
+
return (scroll - from) / Math.max(to - from, 1);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Builds the CSS custom properties exposed on a {@link ScrollListener} root.
|
|
121
|
+
*
|
|
122
|
+
* Measurements are lengths and come with a `-raw` twin carrying the plain number
|
|
123
|
+
* for `calc()`; ratios are unitless and have no twin.
|
|
124
|
+
*
|
|
125
|
+
* @param scrollState - The state to expose.
|
|
126
|
+
* @returns The custom properties, keyed by their full name.
|
|
127
|
+
*/
|
|
128
|
+
export function toScrollCssProps(scrollState) {
|
|
129
|
+
const { global: { win, html, scroll }, local } = scrollState;
|
|
130
|
+
const windowScrolledXRatio = scroll.x / Math.max(html.width - win.width, 1);
|
|
131
|
+
const windowScrolledYRatio = scroll.y / Math.max(html.height - win.height, 1);
|
|
132
|
+
const selfInnerScrolledXRatio = progressIn(scroll.x, innerRange(local.offsetX, local.width, win.width));
|
|
133
|
+
const selfOuterScrolledXRatio = progressIn(scroll.x, outerRange(local.offsetX, local.width, win.width));
|
|
134
|
+
const selfInnerScrolledYRatio = progressIn(scroll.y, innerRange(local.offsetY, local.height, win.height));
|
|
135
|
+
const selfOuterScrolledYRatio = progressIn(scroll.y, outerRange(local.offsetY, local.height, win.height));
|
|
136
|
+
return {
|
|
137
|
+
'--lm-scroll-listener-window-width': `${win.width}px`,
|
|
138
|
+
'--lm-scroll-listener-window-width-raw': `${win.width}`,
|
|
139
|
+
'--lm-scroll-listener-window-height': `${win.height}px`,
|
|
140
|
+
'--lm-scroll-listener-window-height-raw': `${win.height}`,
|
|
141
|
+
'--lm-scroll-listener-html-width': `${html.width}px`,
|
|
142
|
+
'--lm-scroll-listener-html-width-raw': `${html.width}`,
|
|
143
|
+
'--lm-scroll-listener-html-height': `${html.height}px`,
|
|
144
|
+
'--lm-scroll-listener-html-height-raw': `${html.height}`,
|
|
145
|
+
'--lm-scroll-listener-scroll-x': `${scroll.x}px`,
|
|
146
|
+
'--lm-scroll-listener-scroll-x-raw': `${scroll.x}`,
|
|
147
|
+
'--lm-scroll-listener-scroll-y': `${scroll.y}px`,
|
|
148
|
+
'--lm-scroll-listener-scroll-y-raw': `${scroll.y}`,
|
|
149
|
+
'--lm-scroll-listener-width': `${local.width}px`,
|
|
150
|
+
'--lm-scroll-listener-width-raw': `${local.width}`,
|
|
151
|
+
'--lm-scroll-listener-height': `${local.height}px`,
|
|
152
|
+
'--lm-scroll-listener-height-raw': `${local.height}`,
|
|
153
|
+
'--lm-scroll-listener-offset-x': `${local.offsetX}px`,
|
|
154
|
+
'--lm-scroll-listener-offset-x-raw': `${local.offsetX}`,
|
|
155
|
+
'--lm-scroll-listener-offset-y': `${local.offsetY}px`,
|
|
156
|
+
'--lm-scroll-listener-offset-y-raw': `${local.offsetY}`,
|
|
157
|
+
'--lm-scroll-listener-window-scrolled-x-ratio': `${windowScrolledXRatio}`,
|
|
158
|
+
'--lm-scroll-listener-window-scrolled-y-ratio': `${windowScrolledYRatio}`,
|
|
159
|
+
'--lm-scroll-listener-self-inner-scrolled-x-ratio': `${selfInnerScrolledXRatio}`,
|
|
160
|
+
'--lm-scroll-listener-self-outer-scrolled-x-ratio': `${selfOuterScrolledXRatio}`,
|
|
161
|
+
'--lm-scroll-listener-self-inner-scrolled-y-ratio': `${selfInnerScrolledYRatio}`,
|
|
162
|
+
'--lm-scroll-listener-self-outer-scrolled-y-ratio': `${selfOuterScrolledYRatio}`
|
|
163
|
+
};
|
|
164
|
+
}
|
|
@@ -1,20 +1,28 @@
|
|
|
1
|
-
import { type FunctionComponent } from 'react';
|
|
2
|
-
import
|
|
1
|
+
import { type FunctionComponent, type PropsWithChildren, type ReactNode, type SelectHTMLAttributes } from 'react';
|
|
2
|
+
import type { WithClassName } from '../utils/types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Props for the {@link Select} component.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* Extends all native {@link SelectHTMLAttributes} and {@link WithClassName}
|
|
7
|
+
* with optional label, error content, and option children.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* @property label - Content rendered as an associated `<label>`. When omitted, no label is rendered.
|
|
10
|
+
* @property error - Content rendered as an error message below the select. When omitted, no error is rendered.
|
|
11
|
+
* @property className - Additional class name(s) applied to the select element.
|
|
12
|
+
* @property children - `<option>` or `<optgroup>` elements rendered inside the select.
|
|
11
13
|
*/
|
|
12
|
-
export type Props =
|
|
14
|
+
export type Props = SelectHTMLAttributes<HTMLSelectElement> & PropsWithChildren<WithClassName<{
|
|
15
|
+
label?: ReactNode;
|
|
16
|
+
error?: ReactNode;
|
|
17
|
+
}>>;
|
|
13
18
|
/**
|
|
14
|
-
* Select field
|
|
19
|
+
* Select field supporting controlled and uncontrolled usage.
|
|
15
20
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
21
|
+
* Renders a native `<select>` with optional label and error feedback. All
|
|
22
|
+
* standard select attributes are forwarded to the underlying element.
|
|
23
|
+
*
|
|
24
|
+
* A stable auto-generated `id` is created on mount and used to associate the
|
|
25
|
+
* rendered label through the `htmlFor` attribute.
|
|
18
26
|
*
|
|
19
27
|
* ### CSS elements
|
|
20
28
|
* - `label`
|
|
@@ -22,17 +30,13 @@ export type Props = ControlledProps;
|
|
|
22
30
|
*
|
|
23
31
|
* @param props - Component properties.
|
|
24
32
|
* @see {@link Props}
|
|
25
|
-
*
|
|
26
|
-
* @returns A labelled select with optional internal selection management.
|
|
33
|
+
* @returns A labelled select with optional error feedback.
|
|
27
34
|
*
|
|
28
35
|
* @remarks
|
|
29
|
-
* - In controlled mode (`value` defined), the
|
|
30
|
-
*
|
|
31
|
-
* - In
|
|
32
|
-
*
|
|
33
|
-
* -
|
|
34
|
-
* `onChange` callback.
|
|
35
|
-
* - `defaultValue` is only used to initialize internal state and is not
|
|
36
|
-
* forwarded to the underlying controlled component.
|
|
36
|
+
* - In controlled mode (`value` defined), the selection is fully driven by the
|
|
37
|
+
* parent and internal state is never updated.
|
|
38
|
+
* - In uncontrolled mode, internal state is initialized from `defaultValue` and
|
|
39
|
+
* updated before `onChange` is forwarded.
|
|
40
|
+
* - `onChange` fires in both modes.
|
|
37
41
|
*/
|
|
38
42
|
export declare const Select: FunctionComponent<Props>;
|
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { clss } from '../../agnostic/css/clss/index.js';
|
|
4
|
+
import { isNotFalsy } from '../../agnostic/booleans/is-falsy/index.js';
|
|
5
|
+
import { randomHash } from '../../agnostic/random/uuid/index.js';
|
|
6
|
+
import { mergeClassNames } from '../utils/index.js';
|
|
7
|
+
import { select as publicClassName } from '../public-classnames.js';
|
|
8
|
+
import cssModule from './styles.module.css';
|
|
4
9
|
/**
|
|
5
|
-
* Select field
|
|
10
|
+
* Select field supporting controlled and uncontrolled usage.
|
|
6
11
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
12
|
+
* Renders a native `<select>` with optional label and error feedback. All
|
|
13
|
+
* standard select attributes are forwarded to the underlying element.
|
|
14
|
+
*
|
|
15
|
+
* A stable auto-generated `id` is created on mount and used to associate the
|
|
16
|
+
* rendered label through the `htmlFor` attribute.
|
|
9
17
|
*
|
|
10
18
|
* ### CSS elements
|
|
11
19
|
* - `label`
|
|
@@ -13,27 +21,29 @@ import { ControlledSelect } from './index.controlled.js';
|
|
|
13
21
|
*
|
|
14
22
|
* @param props - Component properties.
|
|
15
23
|
* @see {@link Props}
|
|
16
|
-
*
|
|
17
|
-
* @returns A labelled select with optional internal selection management.
|
|
24
|
+
* @returns A labelled select with optional error feedback.
|
|
18
25
|
*
|
|
19
26
|
* @remarks
|
|
20
|
-
* - In controlled mode (`value` defined), the
|
|
21
|
-
*
|
|
22
|
-
* - In
|
|
23
|
-
*
|
|
24
|
-
* -
|
|
25
|
-
* `onChange` callback.
|
|
26
|
-
* - `defaultValue` is only used to initialize internal state and is not
|
|
27
|
-
* forwarded to the underlying controlled component.
|
|
27
|
+
* - In controlled mode (`value` defined), the selection is fully driven by the
|
|
28
|
+
* parent and internal state is never updated.
|
|
29
|
+
* - In uncontrolled mode, internal state is initialized from `defaultValue` and
|
|
30
|
+
* updated before `onChange` is forwarded.
|
|
31
|
+
* - `onChange` fires in both modes.
|
|
28
32
|
*/
|
|
29
|
-
export const Select = ({
|
|
33
|
+
export const Select = ({ label, error, value, defaultValue, onChange, className, children, ...rest }) => {
|
|
34
|
+
// State
|
|
35
|
+
const [id] = useState(`_${randomHash(12)}`);
|
|
36
|
+
const [internalValue, setInternalValue] = useState(defaultValue ?? '');
|
|
30
37
|
const isControlled = value !== undefined;
|
|
31
|
-
const
|
|
32
|
-
|
|
38
|
+
const currentValue = isControlled ? value : internalValue;
|
|
39
|
+
// User action handlers
|
|
33
40
|
const handleChange = (e) => {
|
|
34
41
|
if (!isControlled)
|
|
35
|
-
|
|
42
|
+
setInternalValue(e.target.value);
|
|
36
43
|
onChange?.(e);
|
|
37
44
|
};
|
|
38
|
-
|
|
45
|
+
// Rendering
|
|
46
|
+
const c = clss(publicClassName, { cssModule });
|
|
47
|
+
const rootClss = mergeClassNames(c(), className);
|
|
48
|
+
return _jsxs(_Fragment, { children: [isNotFalsy(label) && _jsx("label", { className: c('label'), htmlFor: id, children: label }), _jsx("select", { ...rest, id: id, className: rootClss, value: currentValue, onChange: handleChange, children: children }), isNotFalsy(error) && _jsx("span", { className: c('error'), children: error })] });
|
|
39
49
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type FunctionComponent, type PropsWithChildren } from 'react';
|
|
2
2
|
import type { WithClassName } from '../utils/types.js';
|
|
3
3
|
/**
|
|
4
|
-
* Props for the {@link
|
|
4
|
+
* Props for the {@link ControlledSequencer} component.
|
|
5
5
|
*
|
|
6
6
|
* This is the low-level controlled interface. All state is driven externally —
|
|
7
7
|
* the component holds no internal state of its own. For the uncontrolled
|
|
@@ -16,32 +16,22 @@ import type { WithClassName } from '../utils/types.js';
|
|
|
16
16
|
* child indices that should be active when `step === i`, allowing multiple
|
|
17
17
|
* children to be simultaneously active on a given step. When omitted, exactly
|
|
18
18
|
* one child is active at a time (the child at index `step`).
|
|
19
|
-
* @property
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* -
|
|
23
|
-
*
|
|
24
|
-
*
|
|
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.
|
|
19
|
+
* @property isPlaying - Whether the sequence is currently progressing. The
|
|
20
|
+
* controlled layer never advances on its own; this only drives the `--playing`
|
|
21
|
+
* modifier. Defaults to `false`.
|
|
22
|
+
* @property tempo - Playback speed in beats per minute, exposed as `data-tempo`
|
|
23
|
+
* for styling and scripting. Purely informational here — the interval itself
|
|
24
|
+
* lives in the uncontrolled wrapper. When omitted, no attribute is rendered.
|
|
29
25
|
* @property className - Optional additional class name(s) applied to the root element.
|
|
30
26
|
* @property children - The items to sequence. Each child is wrapped in a
|
|
31
27
|
* classifier `<div>` and receives one of the `--active`, `--previous`, or
|
|
32
28
|
* `--next` modifiers depending on its position relative to the current `step`.
|
|
33
29
|
*/
|
|
34
|
-
export type
|
|
30
|
+
export type Props = PropsWithChildren<WithClassName<{
|
|
35
31
|
step?: number;
|
|
36
32
|
activateOnStep?: number[][];
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
'at-start'?: boolean;
|
|
40
|
-
'at-end'?: boolean;
|
|
41
|
-
};
|
|
42
|
-
_dataAttributes?: {
|
|
43
|
-
tempo?: number;
|
|
44
|
-
};
|
|
33
|
+
isPlaying?: boolean;
|
|
34
|
+
tempo?: number;
|
|
45
35
|
}>>;
|
|
46
36
|
/**
|
|
47
37
|
* Controlled sequencer component. Renders each child inside a classifier
|
|
@@ -54,14 +44,14 @@ export type ControlledProps = PropsWithChildren<WithClassName<{
|
|
|
54
44
|
*
|
|
55
45
|
* ### Root element modifiers
|
|
56
46
|
* The root `<div>` receives the public class name defined by `sequencer` and
|
|
57
|
-
* the following BEM-style modifier classes
|
|
58
|
-
* - `--playing` — when
|
|
47
|
+
* the following BEM-style modifier classes:
|
|
48
|
+
* - `--playing` — when `isPlaying` is `true`.
|
|
59
49
|
* - `--at-start` — when the current step is the first step.
|
|
60
50
|
* - `--at-end` — when the current step is the last step.
|
|
61
51
|
*
|
|
62
52
|
* ### Data attributes on the root element
|
|
63
|
-
*
|
|
64
|
-
* - `data-tempo` — current playback tempo, when provided.
|
|
53
|
+
* - `data-step` — the current step index.
|
|
54
|
+
* - `data-tempo` — current playback tempo, when `tempo` is provided.
|
|
65
55
|
*
|
|
66
56
|
* ### Child wrapper elements
|
|
67
57
|
* Each child is wrapped in a `<div>` with the `__child` element class and
|
|
@@ -72,7 +62,7 @@ export type ControlledProps = PropsWithChildren<WithClassName<{
|
|
|
72
62
|
* - `--next` — this child has not yet been reached.
|
|
73
63
|
*
|
|
74
64
|
* @param props - Component properties.
|
|
75
|
-
* @see {@link
|
|
65
|
+
* @see {@link Props}
|
|
76
66
|
* @returns A root `<div>` containing one classifier wrapper per child.
|
|
77
67
|
*/
|
|
78
|
-
export declare const
|
|
68
|
+
export declare const ControlledSequencer: FunctionComponent<Props>;
|
|
@@ -15,14 +15,14 @@ import cssModule from './styles.module.css';
|
|
|
15
15
|
*
|
|
16
16
|
* ### Root element modifiers
|
|
17
17
|
* The root `<div>` receives the public class name defined by `sequencer` and
|
|
18
|
-
* the following BEM-style modifier classes
|
|
19
|
-
* - `--playing` — when
|
|
18
|
+
* the following BEM-style modifier classes:
|
|
19
|
+
* - `--playing` — when `isPlaying` is `true`.
|
|
20
20
|
* - `--at-start` — when the current step is the first step.
|
|
21
21
|
* - `--at-end` — when the current step is the last step.
|
|
22
22
|
*
|
|
23
23
|
* ### Data attributes on the root element
|
|
24
|
-
*
|
|
25
|
-
* - `data-tempo` — current playback tempo, when provided.
|
|
24
|
+
* - `data-step` — the current step index.
|
|
25
|
+
* - `data-tempo` — current playback tempo, when `tempo` is provided.
|
|
26
26
|
*
|
|
27
27
|
* ### Child wrapper elements
|
|
28
28
|
* Each child is wrapped in a `<div>` with the `__child` element class and
|
|
@@ -33,17 +33,20 @@ import cssModule from './styles.module.css';
|
|
|
33
33
|
* - `--next` — this child has not yet been reached.
|
|
34
34
|
*
|
|
35
35
|
* @param props - Component properties.
|
|
36
|
-
* @see {@link
|
|
36
|
+
* @see {@link Props}
|
|
37
37
|
* @returns A root `<div>` containing one classifier wrapper per child.
|
|
38
38
|
*/
|
|
39
|
-
export const
|
|
39
|
+
export const ControlledSequencer = ({ step = 0, activateOnStep, isPlaying = false, tempo, className, children }) => {
|
|
40
|
+
const childrenArr = Children.toArray(children);
|
|
41
|
+
const stepsCount = activateOnStep?.length ?? childrenArr.length;
|
|
40
42
|
// Rendering
|
|
41
43
|
const c = clss(publicClassName, { cssModule });
|
|
42
|
-
const rootClss = mergeClassNames(c(null,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
const rootClss = mergeClassNames(c(null, {
|
|
45
|
+
playing: isPlaying,
|
|
46
|
+
'at-start': step === 0,
|
|
47
|
+
'at-end': step === stepsCount - 1
|
|
48
|
+
}), className);
|
|
49
|
+
return _jsx("div", { className: rootClss, "data-step": step, "data-tempo": tempo, children: childrenArr
|
|
47
50
|
.map((child, childPos) => {
|
|
48
51
|
const thisStepActivateOnStep = activateOnStep?.[step];
|
|
49
52
|
const isPrevious = activateOnStep === undefined
|
|
@@ -62,6 +65,6 @@ export const SequencerControlled = ({ step = 0, activateOnStep, _modifiers, _dat
|
|
|
62
65
|
prev: isPrevious,
|
|
63
66
|
next: isNext
|
|
64
67
|
});
|
|
65
|
-
return _jsx("div", { className: childClss, children: child });
|
|
68
|
+
return _jsx("div", { className: childClss, children: child }, childPos);
|
|
66
69
|
}) });
|
|
67
70
|
};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { type FunctionComponent } from 'react';
|
|
2
2
|
import { type Props as IOCompProps } from '../IntersectionObserver/index.js';
|
|
3
|
-
import { type ControlledProps } from './index.controlled.js';
|
|
3
|
+
import { type Props as ControlledProps } from './index.controlled.js';
|
|
4
4
|
/**
|
|
5
5
|
* Props for the {@link Sequencer} component.
|
|
6
6
|
*
|
|
7
|
-
* Extends {@link ControlledProps}
|
|
8
|
-
*
|
|
7
|
+
* Extends {@link ControlledProps} — minus `isPlaying` and `tempo`, which this
|
|
8
|
+
* component derives itself — with uncontrolled playback and viewport-driven
|
|
9
|
+
* behaviour.
|
|
9
10
|
*
|
|
10
11
|
* @property defaultStep - Initial step index when running in uncontrolled mode.
|
|
11
12
|
* Ignored if `step` is provided. Defaults to `0`.
|
|
@@ -28,16 +29,15 @@ import { type ControlledProps } from './index.controlled.js';
|
|
|
28
29
|
* component enters the viewport. No-op when `play` is controlled.
|
|
29
30
|
* @property pauseOnHidden - When `true`, pauses internal playback when the
|
|
30
31
|
* component leaves the viewport. No-op when `play` is controlled.
|
|
31
|
-
* @property
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* @property
|
|
37
|
-
*
|
|
38
|
-
* - `stepChanged` — called with the new forwarded step index whenever it changes.
|
|
32
|
+
* @property onIntersected - Forwarded verbatim to the internal
|
|
33
|
+
* {@link IntersectionObserverComponent}, and called on every intersection
|
|
34
|
+
* change whichever mode the sequencer runs in.
|
|
35
|
+
* @property onIsPlayingChanged - Called after the effective play state changed,
|
|
36
|
+
* with the new value.
|
|
37
|
+
* @property onStepChanged - Called after the forwarded step changed, with the
|
|
38
|
+
* new value.
|
|
39
39
|
*/
|
|
40
|
-
export type Props = Omit<ControlledProps, '
|
|
40
|
+
export type Props = Omit<ControlledProps, 'isPlaying' | 'tempo'> & {
|
|
41
41
|
defaultStep?: number;
|
|
42
42
|
tempo?: number;
|
|
43
43
|
play?: boolean;
|
|
@@ -48,38 +48,33 @@ export type Props = Omit<ControlledProps, '_modifiers'> & {
|
|
|
48
48
|
resetOnHidden?: boolean;
|
|
49
49
|
playOnVisible?: boolean;
|
|
50
50
|
pauseOnHidden?: boolean;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
stateHandlers?: {
|
|
55
|
-
isPlaying?: (isPlaying: boolean) => void;
|
|
56
|
-
stepChanged?: (step: number) => void;
|
|
57
|
-
};
|
|
51
|
+
onIntersected?: IOCompProps['onIntersected'];
|
|
52
|
+
onIsPlayingChanged?: (isPlaying: boolean) => void;
|
|
53
|
+
onStepChanged?: (step: number) => void;
|
|
58
54
|
};
|
|
59
55
|
/**
|
|
60
56
|
* Uncontrolled, self-advancing sequencer component. Drives a
|
|
61
|
-
* {@link
|
|
57
|
+
* {@link ControlledSequencer} instance with an internal tempo-based interval,
|
|
62
58
|
* optional loop/clamp boundary behaviour, and viewport-driven play/reset triggers
|
|
63
59
|
* via an {@link IntersectionObserverComponent}.
|
|
64
60
|
*
|
|
65
61
|
* Supports mixed controlled/uncontrolled usage: passing `step` disables the
|
|
66
62
|
* internal interval while still applying loop/clamp arithmetic before forwarding
|
|
67
63
|
* to the controlled layer. Passing `play` disables internal play state management
|
|
68
|
-
* while still allowing viewport handlers to fire `
|
|
64
|
+
* while still allowing viewport handlers to fire `onIntersected`.
|
|
69
65
|
*
|
|
70
|
-
* ### Forwarded
|
|
71
|
-
*
|
|
72
|
-
* - `
|
|
73
|
-
* - `
|
|
74
|
-
* - `at-end` — `true` when the forwarded step equals `stepsCount - 1`.
|
|
66
|
+
* ### Forwarded to {@link ControlledSequencer}
|
|
67
|
+
* - `step` — the effective step, after loop/clamp arithmetic.
|
|
68
|
+
* - `isPlaying` — the effective play state, controlled or internal.
|
|
69
|
+
* - `tempo` — the current tempo, which the controlled layer exposes as `data-tempo`.
|
|
75
70
|
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
71
|
+
* The `--at-start` and `--at-end` modifiers are derived by the controlled layer
|
|
72
|
+
* from `step` and the children count.
|
|
78
73
|
*
|
|
79
74
|
* @param props - Component properties.
|
|
80
75
|
* @see {@link Props}
|
|
81
|
-
* @see {@link
|
|
76
|
+
* @see {@link ControlledSequencer}
|
|
82
77
|
* @returns An {@link IntersectionObserverComponent} wrapping a
|
|
83
|
-
* {@link
|
|
78
|
+
* {@link ControlledSequencer} with the computed step and modifiers applied.
|
|
84
79
|
*/
|
|
85
80
|
export declare const Sequencer: FunctionComponent<Props>;
|