@design-edito/tools 0.5.19 → 0.5.21

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 (40) hide show
  1. package/agnostic/colors/convert/index.js +0 -1
  2. package/agnostic/css/index.d.ts +1 -1
  3. package/agnostic/css/index.js +1 -1
  4. package/agnostic/html/hyper-json/smart-tags/coalesced/index.d.ts +6 -6
  5. package/agnostic/html/hyper-json/smart-tags/coalesced/index.js +6 -6
  6. package/agnostic/html/hyper-json/smart-tags/isolated/index.d.ts +4 -4
  7. package/agnostic/html/hyper-json/smart-tags/isolated/index.js +4 -4
  8. package/agnostic/html/hyper-json/utils/index.js +1 -1
  9. package/agnostic/html/index.d.ts +2 -2
  10. package/agnostic/html/index.js +2 -2
  11. package/agnostic/index.d.ts +1 -1
  12. package/agnostic/index.js +1 -1
  13. package/agnostic/misc/index.d.ts +3 -3
  14. package/agnostic/misc/index.js +3 -3
  15. package/agnostic/misc/logs/index.d.ts +1 -1
  16. package/agnostic/misc/logs/index.js +1 -1
  17. package/agnostic/objects/index.d.ts +1 -1
  18. package/agnostic/objects/index.js +1 -1
  19. package/agnostic/random/index.d.ts +1 -1
  20. package/agnostic/random/index.js +1 -1
  21. package/agnostic/strings/index.d.ts +1 -1
  22. package/agnostic/strings/index.js +1 -1
  23. package/agnostic/time/index.d.ts +1 -1
  24. package/agnostic/time/index.js +1 -1
  25. package/components/Sequencer/index.d.ts +62 -81
  26. package/components/Sequencer/index.js +150 -98
  27. package/components/Video/index.d.ts +1 -1
  28. package/components/utils/viewport-behaviours/index.d.ts +25 -1
  29. package/components/utils/viewport-behaviours/index.js +35 -0
  30. package/components/utils/viewport-behaviours/utils.d.ts +0 -21
  31. package/components/utils/viewport-behaviours/utils.js +0 -31
  32. package/node/@aws-s3/storage/file/index.d.ts +1 -1
  33. package/node/@aws-s3/storage/file/index.js +1 -1
  34. package/node/@aws-s3/storage/index.d.ts +1 -1
  35. package/node/@aws-s3/storage/index.js +1 -1
  36. package/node/cloud-storage/operations/index.d.ts +1 -1
  37. package/node/cloud-storage/operations/index.js +1 -1
  38. package/package.json +1 -1
  39. package/components/Sequencer/index.controlled.d.ts +0 -68
  40. package/components/Sequencer/index.controlled.js +0 -70
@@ -1,5 +1,4 @@
1
1
  /* eslint-disable @typescript-eslint/no-unsafe-type-assertion -- TransformedColor<C> can't be narrowed from a generic type param at compile time; every assertion here is guarded by an isX(...) runtime check immediately prior */
2
- /* eslint-disable max-lines */
3
2
  import { absoluteModulo } from '../../numbers/absolute-modulo/index.js';
4
3
  import { tidy } from '../tidy/index.js';
5
4
  import { cssColors } from '../cssColorsMap.js';
@@ -1,6 +1,6 @@
1
1
  export * as bem from './bem/index.js'
2
2
  export * as clss from './clss/index.js'
3
3
  export * as generateNiceColor from './generate-nice-color/index.js'
4
- export * as isValidCssClassName from './is-valid-css-class-name/index.js'
5
4
  export * as scale from './scale/index.js'
5
+ export * as isValidCssClassName from './is-valid-css-class-name/index.js'
6
6
  export * as stylesSet from './styles-set/index.js'
@@ -1,6 +1,6 @@
1
1
  export * as bem from './bem/index.js'
2
2
  export * as clss from './clss/index.js'
3
3
  export * as generateNiceColor from './generate-nice-color/index.js'
4
- export * as isValidCssClassName from './is-valid-css-class-name/index.js'
5
4
  export * as scale from './scale/index.js'
5
+ export * as isValidCssClassName from './is-valid-css-class-name/index.js'
6
6
  export * as stylesSet from './styles-set/index.js'
@@ -4,24 +4,24 @@ export * as and from './and/index.js'
4
4
  export * as append from './append/index.js'
5
5
  export * as at from './at/index.js'
6
6
  export * as call from './call/index.js'
7
+ export * as clone from './clone/index.js'
7
8
  export * as deleteproperties from './deleteproperties/index.js'
8
9
  export * as equals from './equals/index.js'
9
- export * as clone from './clone/index.js'
10
10
  export * as getattribute from './getattribute/index.js'
11
11
  export * as getproperties from './getproperties/index.js'
12
12
  export * as getproperty from './getproperty/index.js'
13
13
  export * as hjparse from './hjparse/index.js'
14
14
  export * as hjstringify from './hjstringify/index.js'
15
15
  export * as if from './if/index.js'
16
- export * as length from './length/index.js'
17
16
  export * as initialize from './initialize/index.js'
18
- export * as join from './join/index.js'
17
+ export * as length from './length/index.js'
19
18
  export * as map from './map/index.js'
20
19
  export * as negate from './negate/index.js'
21
20
  export * as notrailing from './notrailing/index.js'
22
21
  export * as or from './or/index.js'
23
22
  export * as pickrandom from './pickrandom/index.js'
24
23
  export * as populate from './populate/index.js'
24
+ export * as join from './join/index.js'
25
25
  export * as print from './print/index.js'
26
26
  export * as push from './push/index.js'
27
27
  export * as pusheach from './pusheach/index.js'
@@ -37,16 +37,16 @@ export * as setproperty from './setproperty/index.js'
37
37
  export * as sorton from './sorton/index.js'
38
38
  export * as split from './split/index.js'
39
39
  export * as spread from './spread/index.js'
40
- export * as toboolean from './toboolean/index.js'
41
40
  export * as toarray from './toarray/index.js'
41
+ export * as toboolean from './toboolean/index.js'
42
42
  export * as toelement from './toelement/index.js'
43
43
  export * as toggleclass from './toggleclass/index.js'
44
+ export * as tonodelist from './tonodelist/index.js'
44
45
  export * as tonull from './tonull/index.js'
45
46
  export * as tonumber from './tonumber/index.js'
46
- export * as tonodelist from './tonodelist/index.js'
47
47
  export * as torecord from './torecord/index.js'
48
48
  export * as toref from './toref/index.js'
49
49
  export * as tostring from './tostring/index.js'
50
+ export * as totext from './totext/index.js'
50
51
  export * as transformselected from './transformselected/index.js'
51
52
  export * as trim from './trim/index.js'
52
- export * as totext from './totext/index.js'
@@ -4,24 +4,24 @@ export * as and from './and/index.js'
4
4
  export * as append from './append/index.js'
5
5
  export * as at from './at/index.js'
6
6
  export * as call from './call/index.js'
7
+ export * as clone from './clone/index.js'
7
8
  export * as deleteproperties from './deleteproperties/index.js'
8
9
  export * as equals from './equals/index.js'
9
- export * as clone from './clone/index.js'
10
10
  export * as getattribute from './getattribute/index.js'
11
11
  export * as getproperties from './getproperties/index.js'
12
12
  export * as getproperty from './getproperty/index.js'
13
13
  export * as hjparse from './hjparse/index.js'
14
14
  export * as hjstringify from './hjstringify/index.js'
15
15
  export * as if from './if/index.js'
16
- export * as length from './length/index.js'
17
16
  export * as initialize from './initialize/index.js'
18
- export * as join from './join/index.js'
17
+ export * as length from './length/index.js'
19
18
  export * as map from './map/index.js'
20
19
  export * as negate from './negate/index.js'
21
20
  export * as notrailing from './notrailing/index.js'
22
21
  export * as or from './or/index.js'
23
22
  export * as pickrandom from './pickrandom/index.js'
24
23
  export * as populate from './populate/index.js'
24
+ export * as join from './join/index.js'
25
25
  export * as print from './print/index.js'
26
26
  export * as push from './push/index.js'
27
27
  export * as pusheach from './pusheach/index.js'
@@ -37,16 +37,16 @@ export * as setproperty from './setproperty/index.js'
37
37
  export * as sorton from './sorton/index.js'
38
38
  export * as split from './split/index.js'
39
39
  export * as spread from './spread/index.js'
40
- export * as toboolean from './toboolean/index.js'
41
40
  export * as toarray from './toarray/index.js'
41
+ export * as toboolean from './toboolean/index.js'
42
42
  export * as toelement from './toelement/index.js'
43
43
  export * as toggleclass from './toggleclass/index.js'
44
+ export * as tonodelist from './tonodelist/index.js'
44
45
  export * as tonull from './tonull/index.js'
45
46
  export * as tonumber from './tonumber/index.js'
46
- export * as tonodelist from './tonodelist/index.js'
47
47
  export * as torecord from './torecord/index.js'
48
48
  export * as toref from './toref/index.js'
49
49
  export * as tostring from './tostring/index.js'
50
+ export * as totext from './totext/index.js'
50
51
  export * as transformselected from './transformselected/index.js'
51
52
  export * as trim from './trim/index.js'
52
- export * as totext from './totext/index.js'
@@ -1,13 +1,13 @@
1
- export * as array from './array/index.js'
2
1
  export * as any from './any/index.js'
2
+ export * as array from './array/index.js'
3
+ export * as boolean from './boolean/index.js'
3
4
  export * as element from './element/index.js'
4
5
  export * as get from './get/index.js'
5
- export * as nodelist from './nodelist/index.js'
6
6
  export * as guess from './guess/index.js'
7
+ export * as nodelist from './nodelist/index.js'
7
8
  export * as null from './null/index.js'
8
- export * as boolean from './boolean/index.js'
9
9
  export * as number from './number/index.js'
10
10
  export * as record from './record/index.js'
11
- export * as string from './string/index.js'
12
11
  export * as ref from './ref/index.js'
12
+ export * as string from './string/index.js'
13
13
  export * as text from './text/index.js'
@@ -1,13 +1,13 @@
1
- export * as array from './array/index.js'
2
1
  export * as any from './any/index.js'
2
+ export * as array from './array/index.js'
3
+ export * as boolean from './boolean/index.js'
3
4
  export * as element from './element/index.js'
4
5
  export * as get from './get/index.js'
5
- export * as nodelist from './nodelist/index.js'
6
6
  export * as guess from './guess/index.js'
7
+ export * as nodelist from './nodelist/index.js'
7
8
  export * as null from './null/index.js'
8
- export * as boolean from './boolean/index.js'
9
9
  export * as number from './number/index.js'
10
10
  export * as record from './record/index.js'
11
- export * as string from './string/index.js'
12
11
  export * as ref from './ref/index.js'
12
+ export * as string from './string/index.js'
13
13
  export * as text from './text/index.js'
@@ -1,4 +1,4 @@
1
- /* eslint-disable max-lines, @typescript-eslint/no-unsafe-type-assertion -- generic type params (T/K/Value) and DOM NodeList casts here can't be narrowed from a generic/runtime check at compile time; guarded by runtime typeCheck(s)/instanceof checks immediately prior */
1
+ /* eslint-disable @typescript-eslint/no-unsafe-type-assertion -- generic type params (T/K/Value) and DOM NodeList casts here can't be narrowed from a generic/runtime check at compile time; guarded by runtime typeCheck(s)/instanceof checks immediately prior */
2
2
  import * as Window from '../../../misc/crossenv/window/index.js';
3
3
  import { isRecord } from '../../../objects/is-record/index.js';
4
4
  import { recordMap } from '../../../objects/record-map/index.js';
@@ -1,10 +1,10 @@
1
1
  export * as deepSelect from './deep-select/index.js'
2
2
  export * as getNodeAncestors from './get-node-ancestors/index.js'
3
3
  export * as getPositionInsideParent from './get-position-inside-parent/index.js'
4
- export * as insertNode from './insert-node/index.js'
5
4
  export * as hyperJson from './hyper-json/index.js'
5
+ export * as insertNode from './insert-node/index.js'
6
+ export * as placeholders from './placeholders/index.js'
6
7
  export * as replaceInElement from './replace-in-element/index.js'
7
8
  export * as selectorToElement from './selector-to-element/index.js'
8
- export * as placeholders from './placeholders/index.js'
9
9
  export * as stringToNodes from './string-to-nodes/index.js'
10
10
  export * as watchSelection from './watch-selection/index.js'
@@ -1,10 +1,10 @@
1
1
  export * as deepSelect from './deep-select/index.js'
2
2
  export * as getNodeAncestors from './get-node-ancestors/index.js'
3
3
  export * as getPositionInsideParent from './get-position-inside-parent/index.js'
4
- export * as insertNode from './insert-node/index.js'
5
4
  export * as hyperJson from './hyper-json/index.js'
5
+ export * as insertNode from './insert-node/index.js'
6
+ export * as placeholders from './placeholders/index.js'
6
7
  export * as replaceInElement from './replace-in-element/index.js'
7
8
  export * as selectorToElement from './selector-to-element/index.js'
8
- export * as placeholders from './placeholders/index.js'
9
9
  export * as stringToNodes from './string-to-nodes/index.js'
10
10
  export * as watchSelection from './watch-selection/index.js'
@@ -1,6 +1,6 @@
1
1
  export * as arrays from './arrays/index.js'
2
- export * as booleans from './booleans/index.js'
3
2
  export * as colors from './colors/index.js'
3
+ export * as booleans from './booleans/index.js'
4
4
  export * as css from './css/index.js'
5
5
  export * as errors from './errors/index.js'
6
6
  export * as html from './html/index.js'
package/agnostic/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export * as arrays from './arrays/index.js'
2
- export * as booleans from './booleans/index.js'
3
2
  export * as colors from './colors/index.js'
3
+ export * as booleans from './booleans/index.js'
4
4
  export * as css from './css/index.js'
5
5
  export * as errors from './errors/index.js'
6
6
  export * as html from './html/index.js'
@@ -1,12 +1,12 @@
1
1
  export * as assert from './assert/index.js'
2
2
  export * as cast from './cast/index.js'
3
+ export * as connection from './connection/index.js'
3
4
  export * as crawler from './crawler/index.js'
4
5
  export * as crossenv from './crossenv/index.js'
5
- export * as connection from './connection/index.js'
6
- export * as isConstructorFunction from './is-constructor-function/index.js'
7
6
  export * as dataSize from './data-size/index.js'
7
+ export * as isConstructorFunction from './is-constructor-function/index.js'
8
8
  export * as isNullish from './is-nullish/index.js'
9
- export * as logs from './logs/index.js'
10
9
  export * as loremIpsum from './lorem-ipsum/index.js'
10
+ export * as logs from './logs/index.js'
11
11
  export * as normalizeExtension from './normalize-extension/index.js'
12
12
  export * as outcome from './outcome/index.js'
@@ -1,12 +1,12 @@
1
1
  export * as assert from './assert/index.js'
2
2
  export * as cast from './cast/index.js'
3
+ export * as connection from './connection/index.js'
3
4
  export * as crawler from './crawler/index.js'
4
5
  export * as crossenv from './crossenv/index.js'
5
- export * as connection from './connection/index.js'
6
- export * as isConstructorFunction from './is-constructor-function/index.js'
7
6
  export * as dataSize from './data-size/index.js'
7
+ export * as isConstructorFunction from './is-constructor-function/index.js'
8
8
  export * as isNullish from './is-nullish/index.js'
9
- export * as logs from './logs/index.js'
10
9
  export * as loremIpsum from './lorem-ipsum/index.js'
10
+ export * as logs from './logs/index.js'
11
11
  export * as normalizeExtension from './normalize-extension/index.js'
12
12
  export * as outcome from './outcome/index.js'
@@ -1,3 +1,3 @@
1
- export * as makeTextBlock from './make-text-block/index.js'
2
1
  export * as logger from './logger/index.js'
2
+ export * as makeTextBlock from './make-text-block/index.js'
3
3
  export * as styles from './styles/index.js'
@@ -1,3 +1,3 @@
1
- export * as makeTextBlock from './make-text-block/index.js'
2
1
  export * as logger from './logger/index.js'
2
+ export * as makeTextBlock from './make-text-block/index.js'
3
3
  export * as styles from './styles/index.js'
@@ -1,9 +1,9 @@
1
1
  export * as deepGetProperty from './deep-get-property/index.js'
2
2
  export * as enums from './enums/index.js'
3
3
  export * as flattenGetters from './flatten-getters/index.js'
4
+ export * as isObject from './is-object/index.js'
4
5
  export * as isRecord from './is-record/index.js'
5
6
  export * as recordFormat from './record-format/index.js'
6
- export * as isObject from './is-object/index.js'
7
7
  export * as recordMap from './record-map/index.js'
8
8
  export * as sortKeys from './sort-keys/index.js'
9
9
  export * as validation from './validation/index.js'
@@ -1,9 +1,9 @@
1
1
  export * as deepGetProperty from './deep-get-property/index.js'
2
2
  export * as enums from './enums/index.js'
3
3
  export * as flattenGetters from './flatten-getters/index.js'
4
+ export * as isObject from './is-object/index.js'
4
5
  export * as isRecord from './is-record/index.js'
5
6
  export * as recordFormat from './record-format/index.js'
6
- export * as isObject from './is-object/index.js'
7
7
  export * as recordMap from './record-map/index.js'
8
8
  export * as sortKeys from './sort-keys/index.js'
9
9
  export * as validation from './validation/index.js'
@@ -1,3 +1,3 @@
1
- export * as hexChar from './hex-char/index.js'
2
1
  export * as random from './random/index.js'
3
2
  export * as uuid from './uuid/index.js'
3
+ export * as hexChar from './hex-char/index.js'
@@ -1,3 +1,3 @@
1
- export * as hexChar from './hex-char/index.js'
2
1
  export * as random from './random/index.js'
3
2
  export * as uuid from './uuid/index.js'
3
+ export * as hexChar from './hex-char/index.js'
@@ -1,7 +1,7 @@
1
1
  export * as charCodes from './char-codes/index.js'
2
+ export * as matches from './matches/index.js'
2
3
  export * as normalizeIndent from './normalize-indent/index.js'
3
4
  export * as parseTable from './parse-table/index.js'
4
- export * as matches from './matches/index.js'
5
5
  export * as replaceAll from './replace-all/index.js'
6
6
  export * as splitTrim from './split-trim/index.js'
7
7
  export * as toAlphanum from './to-alphanum/index.js'
@@ -1,7 +1,7 @@
1
1
  export * as charCodes from './char-codes/index.js'
2
+ export * as matches from './matches/index.js'
2
3
  export * as normalizeIndent from './normalize-indent/index.js'
3
4
  export * as parseTable from './parse-table/index.js'
4
- export * as matches from './matches/index.js'
5
5
  export * as replaceAll from './replace-all/index.js'
6
6
  export * as splitTrim from './split-trim/index.js'
7
7
  export * as toAlphanum from './to-alphanum/index.js'
@@ -1,5 +1,5 @@
1
+ export * as dates from './dates/index.js'
1
2
  export * as duration from './duration/index.js'
2
3
  export * as timeout from './timeout/index.js'
3
- export * as dates from './dates/index.js'
4
4
  export * as transitions from './transitions/index.js'
5
5
  export * as wait from './wait/index.js'
@@ -1,5 +1,5 @@
1
+ export * as dates from './dates/index.js'
1
2
  export * as duration from './duration/index.js'
2
3
  export * as timeout from './timeout/index.js'
3
- export * as dates from './dates/index.js'
4
4
  export * as transitions from './transitions/index.js'
5
5
  export * as wait from './wait/index.js'
@@ -1,97 +1,78 @@
1
- import { type FunctionComponent } from 'react';
2
- import { type Props as IOCompProps } from '../IntersectionObserver/index.js';
3
- import type { ViewportObserverOptions } from '../utils/types.js';
4
- import { type Props as ControlledProps } from './index.controlled.js';
1
+ import { type FunctionComponent, type PropsWithChildren } from 'react';
2
+ import type { WithClassName } from '../utils/types.js';
5
3
  /**
6
- * Props for the {@link Sequencer} component.
7
- *
8
- * Extends {@link ControlledProps} — minus `isPlaying` and `tempo`, which this
9
- * component derives itself — with uncontrolled playback and viewport-driven
10
- * behaviour.
11
- *
12
- * @property defaultStep - Initial step index when running in uncontrolled mode.
13
- * Ignored if `step` is provided. Defaults to `0`.
14
- * @property tempo - Playback speed in beats per minute. The interval between
15
- * steps is derived as `1000 / (tempo / 60)` ms. Clamped to a minimum of `1`.
16
- * Defaults to `60`.
17
- * @property play - Controlled play state. When provided, overrides the internal
18
- * play state. The auto-advance interval only runs when this is `true`.
19
- * @property loop - When `true`, the step wraps around using absolute modulo so
20
- * it never exceeds the number of steps.
21
- * @property clampFirst - When `true` (and `loop` is `false`), clamps the step
22
- * to `0` at the lower bound, preventing negative step values.
23
- * @property clampLast - When `true` (and `loop` is `false`), clamps the step
24
- * to `stepsCount - 1` at the upper bound, preventing overflow.
25
- * @property resetOnVisible - When `true`, resets the internal step to `0` each
26
- * time the component enters the viewport. No-op when `step` or `play` is controlled.
27
- * @property resetOnHidden - When `true`, resets the internal step to `0` each
28
- * time the component leaves the viewport. No-op when `step` or `play` is controlled.
29
- * @property playOnVisible - When `true`, starts internal playback when the
30
- * component enters the viewport. No-op when `play` is controlled.
31
- * @property pauseOnHidden - When `true`, pauses internal playback when the
32
- * component leaves the viewport. No-op when `play` is controlled.
33
- * @property threshold - How much of the component has to be in view before it
34
- * counts as visible, forwarded to the internal {@link IntersectionObserver}.
35
- * @property root - The observer's root. Defaults to the viewport.
36
- * @property rootMargin - Grows or shrinks that root before measuring.
37
- * @property onVisibilityChanged - Called on every crossing with the new value.
38
- * `onIntersected` is the raw form of the same event, carrying the entry and the
39
- * observer; this one carries the answer.
40
- * @property onIntersected - Forwarded verbatim to the internal
41
- * {@link IntersectionObserverComponent}, and called on every intersection
42
- * change whichever mode the sequencer runs in.
43
- * @property onIsPlayingChanged - Called after the effective play state changed,
44
- * with the new value.
45
- * @property onStepChanged - Called after the forwarded step changed, with the
46
- * new value.
47
- * @property onLooped - Called when the step wraps around (either direction),
48
- * only while `loop` is `true`.
49
- * @property onReachedFirstStep - Called when the forwarded step becomes `0`.
50
- * @property onReachedLastStep - Called when the forwarded step becomes the last
51
- * one.
4
+ * @property totalSteps - How many steps the sequence has. Falls back to the number of
5
+ * **element** children: text and whitespace never count, which matters more than it
6
+ * looks — an article writes its children across several lines, and a text node counting
7
+ * as a step would put every index out by one.
8
+ * @property stepMap - Rewrites what a position means, **by position and nothing else**:
9
+ * at position `i` the active step is `stepMap[i]` when there is one, and `i` otherwise.
10
+ * `[2, 6, 7]` over seven steps plays `2, 6, 7, 3, 4, 5, 6` — a step may be skipped, may
11
+ * come back, and may not correspond to any child at all, in which case nothing lights up
12
+ * and that is a legal thing to write.
13
+ * @property step - The position, taken over by the consumer. Provided, the tempo stops
14
+ * advancing anything: the counter is theirs.
15
+ * @property defaultStep - The position to start from. Ignored when `step` is provided.
16
+ * @property play - Whether the sequence advances. **There are no controls in this
17
+ * component** — nothing to click, so nothing to surrender to — and this is the only way
18
+ * it moves on its own.
19
+ * @property tempo - Speed in beats per minute: one step every `60000 / tempo` ms, so `60`
20
+ * is a step per second. Clamped to a minimum of `1`.
21
+ * @property loop - Whether the sequence wraps round. **`false` by default**, so a
22
+ * sequence ends the way a video does rather than running forever.
23
+ * @property onStepChanged - Called when the position changes, with the position and the
24
+ * active step it resolves to. Never on mount.
25
+ * @property onIsPlayingChanged - Called once the effective play state changed — which
26
+ * includes it dropping to `false` on its own at the end.
27
+ * @property onIsEndedChanged - Called when the sequence reaches its last step, or leaves
28
+ * it. A looping sequence never ends, so never emits.
29
+ * @property onLooped - Called on each wrap, only while `loop` is `true`.
30
+ * @property onReachedFirstStep - Called when the position becomes `0`.
31
+ * @property onReachedLastStep - Called when the position becomes the last one.
52
32
  */
53
- export type Props = ViewportObserverOptions & Omit<ControlledProps, 'isPlaying' | 'tempo'> & {
33
+ export type Props = PropsWithChildren<WithClassName<{
34
+ totalSteps?: number;
35
+ stepMap?: number[];
36
+ step?: number;
54
37
  defaultStep?: number;
55
- tempo?: number;
56
38
  play?: boolean;
39
+ tempo?: number;
57
40
  loop?: boolean;
58
- clampFirst?: boolean;
59
- clampLast?: boolean;
60
- resetOnVisible?: boolean;
61
- resetOnHidden?: boolean;
62
- playOnVisible?: boolean;
63
- pauseOnHidden?: boolean;
64
- onIntersected?: IOCompProps['onIntersected'];
65
- onVisibilityChanged?: (isVisible: boolean) => void;
41
+ onStepChanged?: (step: number, activeStep: number) => void;
66
42
  onIsPlayingChanged?: (isPlaying: boolean) => void;
67
- onStepChanged?: (step: number) => void;
43
+ onIsEndedChanged?: (isEnded: boolean) => void;
68
44
  onLooped?: () => void;
69
45
  onReachedFirstStep?: () => void;
70
46
  onReachedLastStep?: () => void;
71
- };
47
+ }>>;
72
48
  /**
73
- * Uncontrolled, self-advancing sequencer component. Drives a
74
- * {@link ControlledSequencer} instance with an internal tempo-based interval,
75
- * optional loop/clamp boundary behaviour, and viewport-driven play/reset triggers
76
- * via an {@link IntersectionObserverComponent}.
49
+ * A sequencer over its children, which it classifies and never moves.
50
+ *
51
+ * It renders no box of its own around anything: **the modifiers go straight onto the
52
+ * children**, by `cloneElement`. A wrapper would take the place a consumer lays out —
53
+ * it would be the grid or flex item, leaving the element they actually wrote one level
54
+ * down — and a sequencer exists precisely so that a stylesheet can animate that element.
55
+ * The cost is the contract: **a child has to honour `className`**, which every DOM
56
+ * element does, and every component here does by convention. One that ignores it gets no
57
+ * modifiers, silently.
77
58
  *
78
- * Supports mixed controlled/uncontrolled usage: passing `step` disables the
79
- * internal interval while still applying loop/clamp arithmetic before forwarding
80
- * to the controlled layer. Passing `play` disables internal play state management
81
- * while still allowing viewport handlers to fire `onIntersected`.
59
+ * Children that are not elements — text, whitespace — are rendered untouched and take no
60
+ * part: they carry no class, so they simply stay visible throughout.
82
61
  *
83
- * ### Forwarded to {@link ControlledSequencer}
84
- * - `step` — the effective step, after loop/clamp arithmetic.
85
- * - `isPlaying` — the effective play state, controlled or internal.
86
- * - `tempo` — the current tempo, which the controlled layer exposes as `data-tempo`.
62
+ * ### On the root
63
+ * `--playing`, `--at-start`, `--at-end`, `--ended`, plus `data-step`, `data-active-step`,
64
+ * `data-total-steps` and `data-tempo`.
87
65
  *
88
- * The `--at-start` and `--at-end` modifiers are derived by the controlled layer
89
- * from `step` and the children count.
66
+ * ### On each element child
67
+ * The `__child` element class, then three pairs, one of each always present:
68
+ * - `--on` / `--off` — whether it belongs to the active step.
69
+ * - `--is-first` / `--not-first` — whether its first turn in this lap is still to come.
70
+ * - `--is-last` / `--not-last` — whether it has another turn left in this lap.
90
71
  *
91
- * @param props - Component properties.
92
- * @see {@link Props}
93
- * @see {@link ControlledSequencer}
94
- * @returns An {@link IntersectionObserverComponent} wrapping a
95
- * {@link ControlledSequencer} with the computed step and modifiers applied.
72
+ * **The three pairs are derived, not remembered.** The order of the whole lap is known
73
+ * from `stepMap` alone, so « has it been shown yet » is a question about positions before
74
+ * this one, not about what happened since mount. A child active at positions 1 and 6 is
75
+ * therefore `--is-first` at 1 and `--not-first` at 6, and the answer is the same on every
76
+ * lap without anything being reset.
96
77
  */
97
78
  export declare const Sequencer: FunctionComponent<Props>;
@@ -1,116 +1,168 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useState, useEffect, useCallback, useRef, Children } from 'react';
2
+ import { Children, cloneElement, isValidElement, useEffect, useState } from 'react';
3
3
  import { absoluteModulo } from '../../agnostic/numbers/absolute-modulo/index.js';
4
4
  import { clamp } from '../../agnostic/numbers/clamp/index.js';
5
- import { IntersectionObserverComponent } from '../IntersectionObserver/index.js';
6
- import { useChangeDispatch } from '../utils/index.js';
7
- import { ControlledSequencer } from './index.controlled.js';
5
+ import { clss } from '../../agnostic/css/clss/index.js';
6
+ import { mergeClassNames, useChangeDispatch } from '../utils/index.js';
7
+ import { sequencer as publicClassName } from '../public-classnames.js';
8
+ import cssModule from './styles.module.css';
8
9
  /**
9
- * Uncontrolled, self-advancing sequencer component. Drives a
10
- * {@link ControlledSequencer} instance with an internal tempo-based interval,
11
- * optional loop/clamp boundary behaviour, and viewport-driven play/reset triggers
12
- * via an {@link IntersectionObserverComponent}.
10
+ * The attribute a child uses to say which steps it belongs to.
13
11
  *
14
- * Supports mixed controlled/uncontrolled usage: passing `step` disables the
15
- * internal interval while still applying loop/clamp arithmetic before forwarding
16
- * to the controlled layer. Passing `play` disables internal play state management
17
- * while still allowing viewport handlers to fire `onIntersected`.
12
+ * **Written on the child rather than gathered in a prop**, and that is the whole
13
+ * difference with the `activateOnStep` this component used to take. A parallel array and
14
+ * a children list derive from one another: inserting a child in the middle means
15
+ * reindexing the array, and nothing signals the one time it is forgotten. Carried by the
16
+ * child, the attachment travels with it.
18
17
  *
19
- * ### Forwarded to {@link ControlledSequencer}
20
- * - `step` — the effective step, after loop/clamp arithmetic.
21
- * - `isPlaying` — the effective play state, controlled or internal.
22
- * - `tempo` — the current tempo, which the controlled layer exposes as `data-tempo`.
18
+ * It stays in the DOM once read — it costs nothing, it is worth having while debugging,
19
+ * and a stylesheet can select on it.
20
+ */
21
+ const STEPS_ATTRIBUTE = 'data-steps';
22
+ /**
23
+ * `data-steps="2, 6"` into the steps it names.
24
+ *
25
+ * Anything unreadable gives back `null`, which the caller reads as « the attribute was
26
+ * not written » and falls back to the child's position. That is lm-link's rule applied
27
+ * here — a typo costs the default behaviour, never a broken one.
28
+ */
29
+ function parseSteps(raw) {
30
+ if (typeof raw !== 'string')
31
+ return null;
32
+ const parts = raw.split(',').map(part => part.trim()).filter(part => part !== '');
33
+ if (parts.length === 0)
34
+ return null;
35
+ const steps = parts.map(Number);
36
+ if (steps.some(step => !Number.isInteger(step)))
37
+ return null;
38
+ return steps;
39
+ }
40
+ /** The steps a child answers to: what it declares, or where it sits. */
41
+ function stepsOf(child, elementIndex) {
42
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- a React element's props are a record, whatever else they are
43
+ const declared = parseSteps(child.props[STEPS_ATTRIBUTE]);
44
+ return declared ?? [elementIndex];
45
+ }
46
+ /**
47
+ * A sequencer over its children, which it classifies and never moves.
48
+ *
49
+ * It renders no box of its own around anything: **the modifiers go straight onto the
50
+ * children**, by `cloneElement`. A wrapper would take the place a consumer lays out —
51
+ * it would be the grid or flex item, leaving the element they actually wrote one level
52
+ * down — and a sequencer exists precisely so that a stylesheet can animate that element.
53
+ * The cost is the contract: **a child has to honour `className`**, which every DOM
54
+ * element does, and every component here does by convention. One that ignores it gets no
55
+ * modifiers, silently.
56
+ *
57
+ * Children that are not elements — text, whitespace — are rendered untouched and take no
58
+ * part: they carry no class, so they simply stay visible throughout.
23
59
  *
24
- * The `--at-start` and `--at-end` modifiers are derived by the controlled layer
25
- * from `step` and the children count.
60
+ * ### On the root
61
+ * `--playing`, `--at-start`, `--at-end`, `--ended`, plus `data-step`, `data-active-step`,
62
+ * `data-total-steps` and `data-tempo`.
26
63
  *
27
- * @param props - Component properties.
28
- * @see {@link Props}
29
- * @see {@link ControlledSequencer}
30
- * @returns An {@link IntersectionObserverComponent} wrapping a
31
- * {@link ControlledSequencer} with the computed step and modifiers applied.
64
+ * ### On each element child
65
+ * The `__child` element class, then three pairs, one of each always present:
66
+ * - `--on` / `--off` — whether it belongs to the active step.
67
+ * - `--is-first` / `--not-first` — whether its first turn in this lap is still to come.
68
+ * - `--is-last` / `--not-last` — whether it has another turn left in this lap.
69
+ *
70
+ * **The three pairs are derived, not remembered.** The order of the whole lap is known
71
+ * from `stepMap` alone, so « has it been shown yet » is a question about positions before
72
+ * this one, not about what happened since mount. A child active at positions 1 and 6 is
73
+ * therefore `--is-first` at 1 and `--not-first` at 6, and the answer is the same on every
74
+ * lap without anything being reset.
32
75
  */
33
- export const Sequencer = ({ defaultStep, tempo = 60, play, loop, clampFirst, clampLast, resetOnVisible, resetOnHidden, playOnVisible, pauseOnHidden, onIntersected, onVisibilityChanged, threshold, root, rootMargin, onIsPlayingChanged, onStepChanged, onLooped, onReachedFirstStep, onReachedLastStep, ...controlledProps }) => {
34
- // State
35
- const { step, activateOnStep, children } = controlledProps;
36
- const [internalPlay, setInternalPlay] = useState(play ?? false);
76
+ export const Sequencer = ({ totalSteps, stepMap, step, defaultStep, play, tempo = 60, loop, onStepChanged, onIsPlayingChanged, onIsEndedChanged, onLooped, onReachedFirstStep, onReachedLastStep, className, children }) => {
37
77
  const [internalStep, setInternalStep] = useState(step ?? defaultStep ?? 0);
38
- const actualPlay = play ?? internalPlay;
39
- const actualStep = step ?? internalStep;
40
- // Effects
78
+ const [hasLapped, setHasLapped] = useState(0);
79
+ // Children, split once: what takes part and what merely renders.
80
+ const childrenArr = Children.toArray(children);
81
+ const elements = childrenArr.filter(isValidElement);
82
+ const stepsCount = Math.max(totalSteps ?? elements.length, 0);
83
+ // The two numbers
84
+ const rawStep = step ?? internalStep;
85
+ const position = stepsCount > 0
86
+ ? (loop === true
87
+ ? absoluteModulo(rawStep, stepsCount)
88
+ : clamp(rawStep, 0, stepsCount - 1))
89
+ : 0;
90
+ const activeStep = stepMap?.[position] ?? position;
91
+ // A sequence that does not loop stops on its last step, and says so rather than
92
+ // claiming to still be playing. Derived rather than held: nothing has to be unset when
93
+ // `loop` or `step` changes under it.
94
+ const isEnded = loop !== true && stepsCount > 0 && position >= stepsCount - 1;
95
+ const isPlaying = (play ?? false) && !isEnded;
41
96
  useEffect(() => {
42
- const clampedTempo = Math.max(tempo, 1);
43
- if (!actualPlay || step !== undefined)
97
+ if (!isPlaying || step !== undefined || stepsCount <= 0)
44
98
  return;
45
- const interval = window.setInterval(() => {
46
- setInternalStep(s => s + 1);
47
- }, 1000 / (clampedTempo / 60));
99
+ const interval = window.setInterval(() => setInternalStep(current => current + 1), 60000 / Math.max(tempo, 1));
48
100
  return () => window.clearInterval(interval);
49
- }, [actualPlay, tempo, step]);
50
- // Forwarded step calculation
51
- const stepsCount = activateOnStep !== undefined
52
- ? activateOnStep.length
53
- : Children.toArray(children).length;
54
- let forwardedStep;
55
- if (loop === true) {
56
- forwardedStep = absoluteModulo(actualStep, stepsCount);
57
- }
58
- else {
59
- const leftClamp = clampFirst === true ? 0 : -Infinity;
60
- const rightClamp = clampLast === true ? stepsCount - 1 : Infinity;
61
- forwardedStep = clamp(actualStep, leftClamp, rightClamp);
62
- }
63
- // State dispatch
64
- useChangeDispatch(actualPlay, onIsPlayingChanged);
65
- useChangeDispatch(forwardedStep, onStepChanged);
66
- // Fx. dep. `forwardedStep` - loop / boundary events
67
- const previousStepsRef = useRef(null);
101
+ }, [isPlaying, step, stepsCount, tempo]);
102
+ // A lap counter rather than a comparison of steps: `onLooped` has to fire on the wrap
103
+ // itself, and the position on either side of it says nothing about having crossed.
104
+ const lap = stepsCount > 0 ? Math.floor(rawStep / stepsCount) : 0;
68
105
  useEffect(() => {
69
- if (stepsCount <= 0)
106
+ if (loop !== true)
70
107
  return;
71
- const previous = previousStepsRef.current;
72
- previousStepsRef.current = { forwarded: forwardedStep, actual: actualStep };
73
- if (previous === null)
108
+ if (lap === hasLapped)
74
109
  return;
75
- const lapChanged = Math.floor(actualStep / stepsCount) !== Math.floor(previous.actual / stepsCount);
76
- if (loop === true && lapChanged)
77
- onLooped?.();
78
- if (forwardedStep === 0 && previous.forwarded !== 0)
79
- onReachedFirstStep?.();
80
- if (forwardedStep === stepsCount - 1 && previous.forwarded !== stepsCount - 1)
81
- onReachedLastStep?.();
82
- }, [forwardedStep, actualStep, stepsCount, loop]);
83
- // Action handlers
84
- const handleIntersection = useCallback(({ ioEntry, observer }) => {
85
- onIntersected?.({ ioEntry, observer });
86
- const { isIntersecting } = ioEntry ?? {};
87
- if (isIntersecting !== undefined)
88
- onVisibilityChanged?.(isIntersecting);
89
- // Both handlers fire whatever the mode: a controlled sequencer still wants to
90
- // hear about the viewport, it just doesn't let it drive the step.
91
- if (play === true || step !== undefined)
92
- return;
93
- if (isIntersecting === true) {
94
- if (resetOnVisible === true)
95
- setInternalStep(0);
96
- if (playOnVisible === true)
97
- setInternalPlay(true);
98
- }
99
- else {
100
- if (resetOnHidden === true)
101
- setInternalStep(0);
102
- if (pauseOnHidden === true)
103
- setInternalPlay(false);
104
- }
105
- }, [
106
- resetOnVisible,
107
- playOnVisible,
108
- resetOnHidden,
109
- pauseOnHidden,
110
- play,
111
- step,
112
- onIntersected
113
- ]);
110
+ setHasLapped(lap);
111
+ onLooped?.();
112
+ }, [lap, loop]);
113
+ // State dispatch
114
+ useChangeDispatch(position, () => onStepChanged?.(position, activeStep));
115
+ useChangeDispatch(isPlaying, onIsPlayingChanged);
116
+ useChangeDispatch(isEnded, onIsEndedChanged);
117
+ useChangeDispatch(position === 0, atStart => { if (atStart)
118
+ onReachedFirstStep?.(); });
119
+ useChangeDispatch(stepsCount > 0 && position === stepsCount - 1, atEnd => { if (atEnd)
120
+ onReachedLastStep?.(); });
114
121
  // Rendering
115
- return _jsx(IntersectionObserverComponent, { threshold: threshold, root: root, rootMargin: rootMargin, onIntersected: handleIntersection, children: _jsx(ControlledSequencer, { ...controlledProps, step: forwardedStep, isPlaying: actualPlay, tempo: tempo }) });
122
+ const c = clss(publicClassName, { cssModule });
123
+ const rootClss = mergeClassNames(c(null, {
124
+ playing: isPlaying,
125
+ 'at-start': position === 0,
126
+ 'at-end': stepsCount > 0 && position === stepsCount - 1,
127
+ ended: isEnded
128
+ }), className);
129
+ // Walked once per render: for each element, the positions of this lap at which it is
130
+ // active. Cheap, and it is what makes the three pairs answerable without memory.
131
+ const positionsOf = (steps) => {
132
+ const positions = [];
133
+ for (let i = 0; i < stepsCount; i += 1) {
134
+ if (steps.includes(stepMap?.[i] ?? i))
135
+ positions.push(i);
136
+ }
137
+ return positions;
138
+ };
139
+ let elementIndex = -1;
140
+ return _jsx("div", { className: rootClss, "data-step": position, "data-active-step": activeStep, "data-total-steps": stepsCount, "data-tempo": tempo, children: childrenArr.map((child, childPos) => {
141
+ if (!isValidElement(child))
142
+ return child;
143
+ elementIndex += 1;
144
+ const steps = stepsOf(child, elementIndex);
145
+ const positions = positionsOf(steps);
146
+ const isOn = steps.includes(activeStep);
147
+ const hasEarlier = positions.some(p => p < position);
148
+ const hasLater = positions.some(p => p > position);
149
+ const childClss = c('child', {
150
+ on: isOn,
151
+ off: !isOn,
152
+ 'is-first': !hasEarlier,
153
+ 'not-first': hasEarlier,
154
+ 'is-last': !hasLater,
155
+ 'not-last': hasLater
156
+ });
157
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- a React element's props are a record, whatever else they are
158
+ const ownClassName = child.props.className;
159
+ // `className` is the whole of what a child is asked to honour, and the only prop
160
+ // touched here — its own is kept, and comes second so a consumer's class wins a
161
+ // shorthand conflict.
162
+ const nextProps = {
163
+ key: childPos,
164
+ className: mergeClassNames(childClss, typeof ownClassName === 'string' ? ownClassName : undefined)
165
+ };
166
+ return cloneElement(child, nextProps);
167
+ }) });
116
168
  };
@@ -1,5 +1,5 @@
1
1
  import { type FunctionComponent } from 'react';
2
- import { type ViewportBehaviours } from '../utils/viewport-behaviours/index.js';
2
+ import type { ViewportBehaviours } from '../utils/viewport-behaviours/types.js';
3
3
  import type { VideoAction } from './types.js';
4
4
  import { type Props as ControlledProps } from './index.controlled.js';
5
5
  /**
@@ -1,5 +1,30 @@
1
1
  import { type RefObject } from 'react';
2
2
  import type { ActionTable, ViewportBehaviours, VisibilityOptions } from './types.js';
3
+ /**
4
+ * Whether a string is an instruction this vocabulary answers to.
5
+ *
6
+ * **It lives in the entry point and not next to `parseInstruction`**, which is where it
7
+ * belongs by subject, because the publish step only maps `index.js` and `types.js` into
8
+ * the package's exports — a `utils.js` is unreachable from outside, whatever it holds.
9
+ *
10
+ * **Exported for the consumers that receive their props as text.** lm-link reads an
11
+ * article's XML, where nothing is typed, and has to reject `'jump-to:banana'` before it
12
+ * reaches a component. Written on its side, the rule would exist twice and drift in
13
+ * silence — a verb added here would be rejected there for no visible reason.
14
+ *
15
+ * Returns a plain boolean rather than a type predicate, and that is not an oversight: a
16
+ * verb taking an argument is written `'jump-to:500'`, which is not a member of the verb
17
+ * list this checks against. Narrowing to `Instruction<A>` would therefore claim more than
18
+ * the list can support, and the caller that needs the narrow type is better off asserting
19
+ * it once, where it can say why.
20
+ *
21
+ * @param value - What the consumer wrote.
22
+ * @param verbs - The vocabulary, bare — `VIDEO_VERBS` and its like.
23
+ * @param withArgument - Those of them that take one, and what a valid one looks like. A
24
+ * verb given an argument it does not take, or denied one it needs, fails either way:
25
+ * both are a consumer meaning something the component cannot do.
26
+ */
27
+ export declare function isInstruction(value: unknown, verbs: readonly string[], withArgument?: Readonly<Partial<Record<string, (arg: string) => boolean>>>): boolean;
3
28
  /**
4
29
  * Whether the element counts as visible — **as a state, not as a crossing**.
5
30
  *
@@ -51,4 +76,3 @@ export type ViewportBehavioursResult = {
51
76
  * and yielding is the silent one.
52
77
  */
53
78
  export declare function useViewportBehaviours<A extends string>(targetRef: RefObject<Element | null>, props: ViewportBehaviours<A>, table: ActionTable<A>, suspended?: boolean): ViewportBehavioursResult;
54
- export type { ActionSpec, ActionTable, Instruction, Modifier, ParsedInstruction, ViewportBehaviours, VisibilityOptions } from './types.js';
@@ -2,6 +2,41 @@ import { useCallback, useEffect, useRef, useState } from 'react';
2
2
  import { useIntersectionObserver } from '../../IntersectionObserver/index.js';
3
3
  import { useChangeDispatch } from '../index.js';
4
4
  import { parseInstruction, toInstructionList } from './utils.js';
5
+ /**
6
+ * Whether a string is an instruction this vocabulary answers to.
7
+ *
8
+ * **It lives in the entry point and not next to `parseInstruction`**, which is where it
9
+ * belongs by subject, because the publish step only maps `index.js` and `types.js` into
10
+ * the package's exports — a `utils.js` is unreachable from outside, whatever it holds.
11
+ *
12
+ * **Exported for the consumers that receive their props as text.** lm-link reads an
13
+ * article's XML, where nothing is typed, and has to reject `'jump-to:banana'` before it
14
+ * reaches a component. Written on its side, the rule would exist twice and drift in
15
+ * silence — a verb added here would be rejected there for no visible reason.
16
+ *
17
+ * Returns a plain boolean rather than a type predicate, and that is not an oversight: a
18
+ * verb taking an argument is written `'jump-to:500'`, which is not a member of the verb
19
+ * list this checks against. Narrowing to `Instruction<A>` would therefore claim more than
20
+ * the list can support, and the caller that needs the narrow type is better off asserting
21
+ * it once, where it can say why.
22
+ *
23
+ * @param value - What the consumer wrote.
24
+ * @param verbs - The vocabulary, bare — `VIDEO_VERBS` and its like.
25
+ * @param withArgument - Those of them that take one, and what a valid one looks like. A
26
+ * verb given an argument it does not take, or denied one it needs, fails either way:
27
+ * both are a consumer meaning something the component cannot do.
28
+ */
29
+ export function isInstruction(value, verbs, withArgument = {}) {
30
+ if (typeof value !== 'string')
31
+ return false;
32
+ const { verb, arg } = parseInstruction(value);
33
+ if (!verbs.includes(verb))
34
+ return false;
35
+ const validate = withArgument[verb];
36
+ if (validate === undefined)
37
+ return arg === undefined;
38
+ return arg !== undefined && validate(arg);
39
+ }
5
40
  /**
6
41
  * Whether the element counts as visible — **as a state, not as a crossing**.
7
42
  *
@@ -24,24 +24,3 @@ export declare function parseInstruction(instruction: string): ParsedInstruction
24
24
  * @returns The instructions in the order they were given, which is the order they run.
25
25
  */
26
26
  export declare function toInstructionList<A extends string>(given: Instruction<A> | Array<Instruction<A>> | undefined): Array<Instruction<A>>;
27
- /**
28
- * Whether a string is an instruction this vocabulary answers to.
29
- *
30
- * **Exported for the consumers that receive their props as text.** lm-link reads an
31
- * article's XML, where nothing is typed, and has to reject `'jump-to:banana'` before it
32
- * reaches a component. Written on its side, the rule would exist twice and drift in
33
- * silence — a verb added here would be rejected there for no visible reason.
34
- *
35
- * Returns a plain boolean rather than a type predicate, and that is not an oversight: a
36
- * verb taking an argument is written `'jump-to:500'`, which is not a member of the verb
37
- * list this checks against. Narrowing to `Instruction<A>` would therefore claim more than
38
- * the list can support, and the caller that needs the narrow type is better off asserting
39
- * it once, where it can say why.
40
- *
41
- * @param value - What the consumer wrote.
42
- * @param verbs - The vocabulary, bare — `VIDEO_VERBS` and its like.
43
- * @param withArgument - Those of them that take one, and what a valid one looks like. A
44
- * verb given an argument it does not take, or denied one it needs, fails either way:
45
- * both are a consumer meaning something the component cannot do.
46
- */
47
- export declare function isInstruction(value: unknown, verbs: readonly string[], withArgument?: Readonly<Partial<Record<string, (arg: string) => boolean>>>): boolean;
@@ -53,34 +53,3 @@ export function toInstructionList(given) {
53
53
  return given;
54
54
  return [given];
55
55
  }
56
- /**
57
- * Whether a string is an instruction this vocabulary answers to.
58
- *
59
- * **Exported for the consumers that receive their props as text.** lm-link reads an
60
- * article's XML, where nothing is typed, and has to reject `'jump-to:banana'` before it
61
- * reaches a component. Written on its side, the rule would exist twice and drift in
62
- * silence — a verb added here would be rejected there for no visible reason.
63
- *
64
- * Returns a plain boolean rather than a type predicate, and that is not an oversight: a
65
- * verb taking an argument is written `'jump-to:500'`, which is not a member of the verb
66
- * list this checks against. Narrowing to `Instruction<A>` would therefore claim more than
67
- * the list can support, and the caller that needs the narrow type is better off asserting
68
- * it once, where it can say why.
69
- *
70
- * @param value - What the consumer wrote.
71
- * @param verbs - The vocabulary, bare — `VIDEO_VERBS` and its like.
72
- * @param withArgument - Those of them that take one, and what a valid one looks like. A
73
- * verb given an argument it does not take, or denied one it needs, fails either way:
74
- * both are a consumer meaning something the component cannot do.
75
- */
76
- export function isInstruction(value, verbs, withArgument = {}) {
77
- if (typeof value !== 'string')
78
- return false;
79
- const { verb, arg } = parseInstruction(value);
80
- if (!verbs.includes(verb))
81
- return false;
82
- const validate = withArgument[verb];
83
- if (validate === undefined)
84
- return arg === undefined;
85
- return arg !== undefined && validate(arg);
86
- }
@@ -1,6 +1,6 @@
1
1
  export * as copy from './copy/index.js'
2
- export * as exists from './exists/index.js'
3
2
  export * as download from './download/index.js'
3
+ export * as exists from './exists/index.js'
4
4
  export * as move from './move/index.js'
5
5
  export * as remove from './remove/index.js'
6
6
  export * as stat from './stat/index.js'
@@ -1,6 +1,6 @@
1
1
  export * as copy from './copy/index.js'
2
- export * as exists from './exists/index.js'
3
2
  export * as download from './download/index.js'
3
+ export * as exists from './exists/index.js'
4
4
  export * as move from './move/index.js'
5
5
  export * as remove from './remove/index.js'
6
6
  export * as stat from './stat/index.js'
@@ -1,2 +1,2 @@
1
- export * as directory from './directory/index.js'
2
1
  export * as file from './file/index.js'
2
+ export * as directory from './directory/index.js'
@@ -1,2 +1,2 @@
1
- export * as directory from './directory/index.js'
2
1
  export * as file from './file/index.js'
2
+ export * as directory from './directory/index.js'
@@ -1,6 +1,6 @@
1
1
  export * as copyDir from './copy-dir/index.js'
2
- export * as downloadFile from './download-file/index.js'
3
2
  export * as copyFile from './copy-file/index.js'
3
+ export * as downloadFile from './download-file/index.js'
4
4
  export * as existsFile from './exists-file/index.js'
5
5
  export * as listDir from './list-dir/index.js'
6
6
  export * as moveDir from './move-dir/index.js'
@@ -1,6 +1,6 @@
1
1
  export * as copyDir from './copy-dir/index.js'
2
- export * as downloadFile from './download-file/index.js'
3
2
  export * as copyFile from './copy-file/index.js'
3
+ export * as downloadFile from './download-file/index.js'
4
4
  export * as existsFile from './exists-file/index.js'
5
5
  export * as listDir from './list-dir/index.js'
6
6
  export * as moveDir from './move-dir/index.js'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@design-edito/tools",
3
- "version": "0.5.19",
3
+ "version": "0.5.21",
4
4
  "description": "",
5
5
  "author": "Maxime Fabas",
6
6
  "license": "ISC",
@@ -1,68 +0,0 @@
1
- import { type FunctionComponent, type PropsWithChildren } from 'react';
2
- import type { WithClassName } from '../utils/types.js';
3
- /**
4
- * Props for the {@link ControlledSequencer} 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 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.
25
- * @property className - Optional additional class name(s) applied to the root element.
26
- * @property children - The items to sequence. Each child is wrapped in a
27
- * classifier `<div>` and receives one of the `--active`, `--previous`, or
28
- * `--next` modifiers depending on its position relative to the current `step`.
29
- */
30
- export type Props = PropsWithChildren<WithClassName<{
31
- step?: number;
32
- activateOnStep?: number[][];
33
- isPlaying?: boolean;
34
- tempo?: number;
35
- }>>;
36
- /**
37
- * Controlled sequencer component. Renders each child inside a classifier
38
- * wrapper and assigns step-relative modifiers based on `step` and the optional
39
- * `activateOnStep` override map.
40
- *
41
- * This component is purely presentational and holds no internal state.
42
- * It is designed to be driven by the uncontrolled wrapper, which handles
43
- * timing, playback events, and derives the props passed here.
44
- *
45
- * ### Root element modifiers
46
- * The root `<div>` receives the public class name defined by `sequencer` and
47
- * the following BEM-style modifier classes:
48
- * - `--playing` — when `isPlaying` is `true`.
49
- * - `--at-start` — when the current step is the first step.
50
- * - `--at-end` — when the current step is the last step.
51
- *
52
- * ### Data attributes on the root element
53
- * - `data-step` — the current step index.
54
- * - `data-tempo` — current playback tempo, when `tempo` is provided.
55
- *
56
- * ### Child wrapper elements
57
- * Each child is wrapped in a `<div>` with the `__child` element class and
58
- * exactly one of the following mutually exclusive modifiers:
59
- * - `--active` — this child corresponds to the current step (or is included
60
- * in `activateOnStep[step]`).
61
- * - `--previous` — this child was active in a prior step.
62
- * - `--next` — this child has not yet been reached.
63
- *
64
- * @param props - Component properties.
65
- * @see {@link Props}
66
- * @returns A root `<div>` containing one classifier wrapper per child.
67
- */
68
- export declare const ControlledSequencer: FunctionComponent<Props>;
@@ -1,70 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Children } from 'react';
3
- import { clss } from '../../agnostic/css/clss/index.js';
4
- import { mergeClassNames } from '../utils/index.js';
5
- import { sequencer as publicClassName } from '../public-classnames.js';
6
- import cssModule from './styles.module.css';
7
- /**
8
- * Controlled sequencer component. Renders each child inside a classifier
9
- * wrapper and assigns step-relative modifiers based on `step` and the optional
10
- * `activateOnStep` override map.
11
- *
12
- * This component is purely presentational and holds no internal state.
13
- * It is designed to be driven by the uncontrolled wrapper, which handles
14
- * timing, playback events, and derives the props passed here.
15
- *
16
- * ### Root element modifiers
17
- * The root `<div>` receives the public class name defined by `sequencer` and
18
- * the following BEM-style modifier classes:
19
- * - `--playing` — when `isPlaying` is `true`.
20
- * - `--at-start` — when the current step is the first step.
21
- * - `--at-end` — when the current step is the last step.
22
- *
23
- * ### Data attributes on the root element
24
- * - `data-step` — the current step index.
25
- * - `data-tempo` — current playback tempo, when `tempo` is provided.
26
- *
27
- * ### Child wrapper elements
28
- * Each child is wrapped in a `<div>` with the `__child` element class and
29
- * exactly one of the following mutually exclusive modifiers:
30
- * - `--active` — this child corresponds to the current step (or is included
31
- * in `activateOnStep[step]`).
32
- * - `--previous` — this child was active in a prior step.
33
- * - `--next` — this child has not yet been reached.
34
- *
35
- * @param props - Component properties.
36
- * @see {@link Props}
37
- * @returns A root `<div>` containing one classifier wrapper per child.
38
- */
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;
42
- // Rendering
43
- const c = clss(publicClassName, { cssModule });
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
50
- .map((child, childPos) => {
51
- const thisStepActivateOnStep = activateOnStep?.[step];
52
- const isPrevious = activateOnStep === undefined
53
- ? childPos < step
54
- : [...activateOnStep]
55
- .slice(0, step)
56
- .some(list => list.includes(childPos));
57
- const isCurrent = activateOnStep === undefined
58
- ? childPos === step
59
- : Array.isArray(thisStepActivateOnStep)
60
- ? thisStepActivateOnStep.includes(childPos)
61
- : false;
62
- const isNext = !isPrevious && !isCurrent;
63
- const childClss = c('child', {
64
- current: isCurrent,
65
- prev: isPrevious,
66
- next: isNext
67
- });
68
- return _jsx("div", { className: childClss, children: child }, childPos);
69
- }) });
70
- };