@design-edito/tools 0.5.2 → 0.5.3
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 +8 -8
- package/agnostic/html/hyper-json/smart-tags/coalesced/index.js +8 -8
- package/agnostic/html/hyper-json/smart-tags/isolated/index.d.ts +2 -2
- package/agnostic/html/hyper-json/smart-tags/isolated/index.js +2 -2
- package/agnostic/html/index.d.ts +1 -1
- package/agnostic/html/index.js +1 -1
- package/agnostic/misc/index.d.ts +3 -3
- package/agnostic/misc/index.js +3 -3
- 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 +1 -1
- package/agnostic/strings/index.js +1 -1
- 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 +29 -22
- 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 -12
- package/components/Image/index.js +11 -32
- 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.js +4 -4
- 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 +1 -1
- package/components/index.js +1 -1
- 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/file/index.d.ts +1 -1
- package/node/@aws-s3/storage/file/index.js +1 -1
- package/node/@google-cloud/storage/index.d.ts +1 -1
- package/node/@google-cloud/storage/index.js +1 -1
- package/node/images/index.d.ts +1 -1
- package/node/images/index.js +1 -1
- package/node/images/transform/operations/index.d.ts +1 -1
- package/node/images/transform/operations/index.js +1 -1
- package/node/index.d.ts +1 -1
- package/node/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,20 +1,30 @@
|
|
|
1
|
-
import { type FunctionComponent } from 'react';
|
|
2
|
-
import
|
|
1
|
+
import { type FunctionComponent, type ReactNode, type TextareaHTMLAttributes } from 'react';
|
|
2
|
+
import type { WithClassName } from '../utils/types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Props for the {@link Textarea} component.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* Extends all native {@link TextareaHTMLAttributes} and {@link WithClassName}
|
|
7
|
+
* with optional label, error content, and automatic height adjustment.
|
|
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 textarea. When omitted, no error is rendered.
|
|
11
|
+
* @property autoHeight - When `true`, grows and shrinks the textarea to fit its
|
|
12
|
+
* content on every value change. Defaults to `false`.
|
|
13
|
+
* @property className - Additional class name(s) applied to the textarea element.
|
|
11
14
|
*/
|
|
12
|
-
export type Props =
|
|
15
|
+
export type Props = TextareaHTMLAttributes<HTMLTextAreaElement> & WithClassName<{
|
|
16
|
+
label?: ReactNode;
|
|
17
|
+
error?: ReactNode;
|
|
18
|
+
autoHeight?: boolean;
|
|
19
|
+
}>;
|
|
13
20
|
/**
|
|
14
|
-
* Textarea field
|
|
21
|
+
* Textarea field supporting controlled and uncontrolled usage.
|
|
15
22
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
23
|
+
* Renders a native `<textarea>` with optional label and error feedback. All
|
|
24
|
+
* standard textarea attributes are forwarded to the underlying element.
|
|
25
|
+
*
|
|
26
|
+
* A stable auto-generated `id` is created on mount and used to associate the
|
|
27
|
+
* rendered label through the `htmlFor` attribute.
|
|
18
28
|
*
|
|
19
29
|
* ### CSS elements
|
|
20
30
|
* - `label`
|
|
@@ -22,17 +32,13 @@ export type Props = ControlledProps;
|
|
|
22
32
|
*
|
|
23
33
|
* @param props - Component properties.
|
|
24
34
|
* @see {@link Props}
|
|
25
|
-
*
|
|
26
|
-
* @returns A labelled textarea with optional internal value management.
|
|
35
|
+
* @returns A labelled textarea with optional error feedback.
|
|
27
36
|
*
|
|
28
37
|
* @remarks
|
|
29
|
-
* - In controlled mode (`value` defined), the
|
|
30
|
-
* by the parent
|
|
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.
|
|
38
|
+
* - In controlled mode (`value` defined), the displayed value is fully driven
|
|
39
|
+
* by the parent and internal state is never updated.
|
|
40
|
+
* - In uncontrolled mode, internal state is initialized from `defaultValue` and
|
|
41
|
+
* updated before `onChange` is forwarded.
|
|
42
|
+
* - `onChange` fires in both modes.
|
|
37
43
|
*/
|
|
38
44
|
export declare const Textarea: FunctionComponent<Props>;
|
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from 'react';
|
|
3
|
-
import {
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useLayoutEffect, useRef, useState } from 'react';
|
|
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 { textarea as publicClassName } from '../public-classnames.js';
|
|
8
|
+
import cssModule from './styles.module.css';
|
|
4
9
|
/**
|
|
5
|
-
* Textarea field
|
|
10
|
+
* Textarea field supporting controlled and uncontrolled usage.
|
|
6
11
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
12
|
+
* Renders a native `<textarea>` with optional label and error feedback. All
|
|
13
|
+
* standard textarea 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,38 @@ import { ControlledTextarea } from './index.controlled.js';
|
|
|
13
21
|
*
|
|
14
22
|
* @param props - Component properties.
|
|
15
23
|
* @see {@link Props}
|
|
16
|
-
*
|
|
17
|
-
* @returns A labelled textarea with optional internal value management.
|
|
24
|
+
* @returns A labelled textarea with optional error feedback.
|
|
18
25
|
*
|
|
19
26
|
* @remarks
|
|
20
|
-
* - In controlled mode (`value` defined), the
|
|
21
|
-
* by the parent
|
|
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 displayed value is fully driven
|
|
28
|
+
* by the 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 Textarea = ({
|
|
33
|
+
export const Textarea = ({ label, error, autoHeight = false, value, defaultValue, onChange, className, ...rest }) => {
|
|
34
|
+
// State & refs
|
|
35
|
+
const [id] = useState(`_${randomHash(12)}`);
|
|
36
|
+
const [internalValue, setInternalValue] = useState(defaultValue ?? '');
|
|
37
|
+
const textareaRef = useRef(null);
|
|
30
38
|
const isControlled = value !== undefined;
|
|
31
|
-
const
|
|
32
|
-
|
|
39
|
+
const currentValue = isControlled ? value : internalValue;
|
|
40
|
+
// Fx. dep. `autoHeight`, `currentValue` - fit the height to the content
|
|
41
|
+
useLayoutEffect(() => {
|
|
42
|
+
const textarea = textareaRef.current;
|
|
43
|
+
if (!autoHeight || textarea === null)
|
|
44
|
+
return;
|
|
45
|
+
textarea.style.height = 'auto';
|
|
46
|
+
textarea.style.height = `${textarea.scrollHeight}px`;
|
|
47
|
+
}, [autoHeight, currentValue]);
|
|
48
|
+
// User action handlers
|
|
33
49
|
const handleChange = (e) => {
|
|
34
50
|
if (!isControlled)
|
|
35
|
-
|
|
51
|
+
setInternalValue(e.target.value);
|
|
36
52
|
onChange?.(e);
|
|
37
53
|
};
|
|
38
|
-
|
|
54
|
+
// Rendering
|
|
55
|
+
const c = clss(publicClassName, { cssModule });
|
|
56
|
+
const rootClss = mergeClassNames(c(), className);
|
|
57
|
+
return _jsxs(_Fragment, { children: [isNotFalsy(label) && _jsx("label", { className: c('label'), htmlFor: id, children: label }), _jsx("textarea", { ...rest, ref: textareaRef, id: id, className: rootClss, value: currentValue, onChange: handleChange }), isNotFalsy(error) && _jsx("span", { className: c('error'), children: error })] });
|
|
39
58
|
};
|
|
@@ -3,23 +3,24 @@ import type { WithClassName } from '../utils/types.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* Props for the {@link Theatre} component.
|
|
5
5
|
*
|
|
6
|
-
* @property closeBtnContent -
|
|
7
|
-
* @property openBtnContent -
|
|
8
|
-
* @property isOn - Controlled theatre mode state. When
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* @property exitOnEscape
|
|
13
|
-
*
|
|
14
|
-
* @property
|
|
15
|
-
*
|
|
16
|
-
* @property
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
6
|
+
* @property closeBtnContent - Content rendered inside the close/exit button.
|
|
7
|
+
* @property openBtnContent - Content rendered inside the open/enter button.
|
|
8
|
+
* @property isOn - Controlled theatre mode state. When defined, the component
|
|
9
|
+
* behaves as a controlled component and internal state is never updated.
|
|
10
|
+
* @property defaultIsOn - Initial theatre mode state in uncontrolled mode.
|
|
11
|
+
* Ignored when `isOn` is provided. Defaults to `false`.
|
|
12
|
+
* @property exitOnEscape - When `true`, pressing `Escape` while the stage is
|
|
13
|
+
* open counts as a toggle.
|
|
14
|
+
* @property exitOnBgClick - When `true`, clicking the stage background — and
|
|
15
|
+
* not its content — counts as a toggle.
|
|
16
|
+
* @property onToggleClicked - Called whenever a toggle is requested: the open
|
|
17
|
+
* or close button, the `Escape` key, or the stage background. Fires before the
|
|
18
|
+
* theatre reacts, with the state as it was.
|
|
19
|
+
* @property onIsOnChanged - Called after the theatre mode changed, with the new
|
|
20
|
+
* value.
|
|
20
21
|
* @property className - Optional additional class name(s) applied to the root element.
|
|
21
|
-
* @property children - Content rendered both in the default slot and, when
|
|
22
|
-
* mode is active, duplicated inside the stage element.
|
|
22
|
+
* @property children - Content rendered both in the default slot and, when
|
|
23
|
+
* theatre mode is active, duplicated inside the stage element.
|
|
23
24
|
*/
|
|
24
25
|
export type Props = PropsWithChildren<WithClassName<{
|
|
25
26
|
closeBtnContent?: ReactNode;
|
|
@@ -28,37 +29,32 @@ export type Props = PropsWithChildren<WithClassName<{
|
|
|
28
29
|
defaultIsOn?: boolean;
|
|
29
30
|
exitOnEscape?: boolean;
|
|
30
31
|
exitOnBgClick?: boolean;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
};
|
|
34
|
-
actionHandlers?: {
|
|
35
|
-
toggleClick?: (prevIsOn: boolean) => void;
|
|
36
|
-
};
|
|
32
|
+
onToggleClicked?: (isOn: boolean) => void;
|
|
33
|
+
onIsOnChanged?: (isOn: boolean) => void;
|
|
37
34
|
}>>;
|
|
38
35
|
/**
|
|
39
36
|
* Theatre mode component. Wraps content in a toggleable fullscreen-like "stage"
|
|
40
|
-
* overlay.
|
|
37
|
+
* overlay.
|
|
41
38
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
39
|
+
* ### CSS modifiers
|
|
40
|
+
* - `on` — theatre mode is active.
|
|
41
|
+
* - `off` — theatre mode is inactive.
|
|
45
42
|
*
|
|
46
|
-
* ###
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* -
|
|
50
|
-
* - `--off` — when theatre mode is inactive.
|
|
51
|
-
*
|
|
52
|
-
* ### Child elements
|
|
53
|
-
* - `__stage` — container rendered inside the root that holds the duplicated
|
|
54
|
-
* `children` when theatre mode is active. Only mounted when `isOn` is `true`.
|
|
55
|
-
* - `__open-btn` — clickable element that activates theatre mode.
|
|
56
|
-
* - `__close-btn` — clickable element that deactivates theatre mode.
|
|
43
|
+
* ### CSS elements
|
|
44
|
+
* - `stage` — holds the duplicated `children`, mounted only when `isOn`.
|
|
45
|
+
* - `open-btn`
|
|
46
|
+
* - `close-btn`
|
|
57
47
|
*
|
|
58
48
|
* @param props - Component properties.
|
|
59
49
|
* @see {@link Props}
|
|
60
50
|
* @returns A root `<div>` containing the children in their original position,
|
|
61
|
-
* a stage overlay with the duplicated children
|
|
62
|
-
*
|
|
51
|
+
* a stage overlay with the duplicated children when active, and both toggles.
|
|
52
|
+
*
|
|
53
|
+
* @remarks
|
|
54
|
+
* - In controlled mode (`isOn` defined), the state is fully driven by the
|
|
55
|
+
* parent and internal state is never updated.
|
|
56
|
+
* - `onToggleClicked` fires in both modes, for all four toggle sources — a
|
|
57
|
+
* controlled parent needs it to know a toggle was requested at all.
|
|
58
|
+
* - `onIsOnChanged` fires in both modes too, and never on mount.
|
|
63
59
|
*/
|
|
64
60
|
export declare const Theatre: FunctionComponent<Props>;
|
|
@@ -1,90 +1,78 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState, useRef } from 'react';
|
|
3
3
|
import { clss } from '../../agnostic/css/clss/index.js';
|
|
4
|
-
import { mergeClassNames } from '../utils/index.js';
|
|
4
|
+
import { mergeClassNames, useChangeDispatch } from '../utils/index.js';
|
|
5
5
|
import { theatre as publicClassName } from '../public-classnames.js';
|
|
6
6
|
import cssModule from './styles.module.css';
|
|
7
7
|
/**
|
|
8
8
|
* Theatre mode component. Wraps content in a toggleable fullscreen-like "stage"
|
|
9
|
-
* overlay.
|
|
9
|
+
* overlay.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* ### CSS modifiers
|
|
12
|
+
* - `on` — theatre mode is active.
|
|
13
|
+
* - `off` — theatre mode is inactive.
|
|
14
14
|
*
|
|
15
|
-
* ###
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* -
|
|
19
|
-
* - `--off` — when theatre mode is inactive.
|
|
20
|
-
*
|
|
21
|
-
* ### Child elements
|
|
22
|
-
* - `__stage` — container rendered inside the root that holds the duplicated
|
|
23
|
-
* `children` when theatre mode is active. Only mounted when `isOn` is `true`.
|
|
24
|
-
* - `__open-btn` — clickable element that activates theatre mode.
|
|
25
|
-
* - `__close-btn` — clickable element that deactivates theatre mode.
|
|
15
|
+
* ### CSS elements
|
|
16
|
+
* - `stage` — holds the duplicated `children`, mounted only when `isOn`.
|
|
17
|
+
* - `open-btn`
|
|
18
|
+
* - `close-btn`
|
|
26
19
|
*
|
|
27
20
|
* @param props - Component properties.
|
|
28
21
|
* @see {@link Props}
|
|
29
22
|
* @returns A root `<div>` containing the children in their original position,
|
|
30
|
-
* a stage overlay with the duplicated children
|
|
31
|
-
*
|
|
23
|
+
* a stage overlay with the duplicated children when active, and both toggles.
|
|
24
|
+
*
|
|
25
|
+
* @remarks
|
|
26
|
+
* - In controlled mode (`isOn` defined), the state is fully driven by the
|
|
27
|
+
* parent and internal state is never updated.
|
|
28
|
+
* - `onToggleClicked` fires in both modes, for all four toggle sources — a
|
|
29
|
+
* controlled parent needs it to know a toggle was requested at all.
|
|
30
|
+
* - `onIsOnChanged` fires in both modes too, and never on mount.
|
|
32
31
|
*/
|
|
33
|
-
export const Theatre = ({ closeBtnContent, openBtnContent, isOn, defaultIsOn, exitOnEscape, exitOnBgClick,
|
|
32
|
+
export const Theatre = ({ closeBtnContent, openBtnContent, isOn: isOnProp, defaultIsOn = false, exitOnEscape, exitOnBgClick, onToggleClicked, onIsOnChanged, children, className }) => {
|
|
34
33
|
// State & refs
|
|
35
|
-
const [internalIsOn, setInternalIsOn] = useState(defaultIsOn
|
|
34
|
+
const [internalIsOn, setInternalIsOn] = useState(defaultIsOn);
|
|
36
35
|
const stageRef = useRef(null);
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (isOn === undefined)
|
|
48
|
-
setInternalIsOn(true);
|
|
36
|
+
const isControlled = isOnProp !== undefined;
|
|
37
|
+
const isOn = isOnProp ?? internalIsOn;
|
|
38
|
+
// State dispatch
|
|
39
|
+
useChangeDispatch(isOn, onIsOnChanged);
|
|
40
|
+
// User action handlers
|
|
41
|
+
const requestToggle = (targetIsOn) => {
|
|
42
|
+
onToggleClicked?.(isOn);
|
|
43
|
+
if (isControlled)
|
|
44
|
+
return;
|
|
45
|
+
setInternalIsOn(targetIsOn);
|
|
49
46
|
};
|
|
47
|
+
const handleOpenBtnClick = () => requestToggle(true);
|
|
48
|
+
const handleCloseBtnClick = () => requestToggle(false);
|
|
50
49
|
const handleStageBgClick = e => {
|
|
51
50
|
if (exitOnBgClick !== true)
|
|
52
51
|
return;
|
|
53
52
|
if (e.target !== stageRef.current)
|
|
54
53
|
return;
|
|
55
|
-
|
|
56
|
-
if (isOn === undefined)
|
|
57
|
-
setInternalIsOn(false);
|
|
54
|
+
requestToggle(false);
|
|
58
55
|
};
|
|
59
|
-
//
|
|
60
|
-
useEffect(() => {
|
|
61
|
-
if (prevIsTheatreOnRef.current !== isTheatreOn) {
|
|
62
|
-
stateHandlers?.toggled?.(isTheatreOn);
|
|
63
|
-
prevIsTheatreOnRef.current = isTheatreOn;
|
|
64
|
-
}
|
|
65
|
-
}, [isTheatreOn, stateHandlers]);
|
|
56
|
+
// Fx. dep. `exitOnEscape`, `isOn` - close the stage on the Escape key
|
|
66
57
|
useEffect(() => {
|
|
67
|
-
if (exitOnEscape
|
|
68
|
-
|| !isTheatreOn
|
|
69
|
-
|| isOn !== undefined)
|
|
58
|
+
if (exitOnEscape !== true || !isOn)
|
|
70
59
|
return;
|
|
71
|
-
const
|
|
60
|
+
const handleKeyDown = (e) => {
|
|
72
61
|
if (e.key !== 'Escape')
|
|
73
62
|
return;
|
|
74
|
-
|
|
75
|
-
setInternalIsOn(false);
|
|
63
|
+
requestToggle(false);
|
|
76
64
|
};
|
|
77
|
-
window.addEventListener('keydown',
|
|
78
|
-
return () => window.removeEventListener('keydown',
|
|
79
|
-
}, [exitOnEscape,
|
|
80
|
-
//
|
|
65
|
+
window.addEventListener('keydown', handleKeyDown);
|
|
66
|
+
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
67
|
+
}, [exitOnEscape, isOn, isControlled]);
|
|
68
|
+
// Rendering
|
|
81
69
|
const c = clss(publicClassName, { cssModule });
|
|
82
70
|
const rootClss = mergeClassNames(c(null, {
|
|
83
|
-
'on':
|
|
84
|
-
'off': !
|
|
71
|
+
'on': isOn,
|
|
72
|
+
'off': !isOn
|
|
85
73
|
}), className);
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
return _jsxs("div", { className: rootClss, children: [children, _jsx("div", { className:
|
|
74
|
+
const stageClss = c('stage');
|
|
75
|
+
const openBtnClss = c('open-btn');
|
|
76
|
+
const closeBtnClss = c('close-btn');
|
|
77
|
+
return _jsxs("div", { className: rootClss, children: [children, _jsx("div", { className: stageClss, onClick: handleStageBgClick, ref: stageRef, children: isOn && children }), _jsx("div", { className: closeBtnClss, onClick: handleCloseBtnClick, children: closeBtnContent }), _jsx("div", { className: openBtnClss, onClick: handleOpenBtnClick, children: openBtnContent })] });
|
|
90
78
|
};
|
|
@@ -31,49 +31,45 @@ type ModuleData = {
|
|
|
31
31
|
* component stays in the `--no-module` state.
|
|
32
32
|
* @property props - Arbitrary key-value object forwarded verbatim to the
|
|
33
33
|
* module's `init` call and, on subsequent changes, to `update` (if exported).
|
|
34
|
-
* @property
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* after
|
|
39
|
-
*
|
|
40
|
-
*
|
|
34
|
+
* @property onIdGenerated - Called once on mount with the instance's generated
|
|
35
|
+
* `id`. The id never changes afterwards, so this fires exactly once.
|
|
36
|
+
* @property onIsLoadingChanged - Called after the loading state changed, with
|
|
37
|
+
* the new value.
|
|
38
|
+
* @property onLoadedModuleChanged - Called after the loaded module changed, with
|
|
39
|
+
* the new value: the validated {@link ModuleData}, an `Error`, or `null`.
|
|
40
|
+
* @property onModuleTargetChanged - Called after the hosted element changed,
|
|
41
|
+
* with the `Element` returned by `init`, or `null` once unloaded or errored.
|
|
41
42
|
* @property className - Optional additional class name(s) applied to the root element.
|
|
42
43
|
*/
|
|
43
44
|
export type Props = WithClassName<{
|
|
44
45
|
src?: string;
|
|
45
46
|
props?: Record<string, unknown>;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
moduleTargetChanged?: (moduleTarget: Element | null) => void;
|
|
51
|
-
};
|
|
47
|
+
onIdGenerated?: (id: string) => void;
|
|
48
|
+
onIsLoadingChanged?: (isLoading: boolean) => void;
|
|
49
|
+
onLoadedModuleChanged?: (loadedModule: ModuleData | Error | null) => void;
|
|
50
|
+
onModuleTargetChanged?: (moduleTarget: Element | null) => void;
|
|
52
51
|
}>;
|
|
53
52
|
/**
|
|
54
|
-
* Dynamic UI module host
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* error, and teardown states automatically.
|
|
53
|
+
* Dynamic UI module host. Asynchronously imports an ES module by URL, validates
|
|
54
|
+
* its exported interface, calls its `init` lifecycle to obtain a DOM `Element`,
|
|
55
|
+
* and appends that element to its own root `<div>`.
|
|
58
56
|
*
|
|
59
57
|
* The imported module is expected to conform to the {@link ModuleData} interface.
|
|
60
58
|
* Any violation (missing exports, wrong types, `init` not returning an `Element`)
|
|
61
59
|
* transitions the component into the `--error` state and logs to `console.error`.
|
|
62
60
|
*
|
|
63
|
-
* ###
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* -
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* -
|
|
70
|
-
* -
|
|
71
|
-
* - `--initialized` — the `Element` returned by `init` has been appended to the
|
|
72
|
-
* host `<div>`.
|
|
61
|
+
* ### CSS modifiers
|
|
62
|
+
* Reflecting the current load lifecycle:
|
|
63
|
+
* - `loading` — the module fetch is in progress.
|
|
64
|
+
* - `no-module` — nothing has been loaded yet (`src` is undefined, or the
|
|
65
|
+
* effect has not run).
|
|
66
|
+
* - `error` — the import, the validation, or the `init` call failed.
|
|
67
|
+
* - `loaded` — the module passed validation and `init` returned successfully.
|
|
68
|
+
* - `initialized` — the `Element` returned by `init` has been appended.
|
|
73
69
|
*
|
|
74
70
|
* ### Root element attributes
|
|
75
|
-
* - `id` — a stable
|
|
76
|
-
*
|
|
71
|
+
* - `id` — a stable generated id, assigned once on mount and used to scope the
|
|
72
|
+
* module's `css` entries to this specific instance.
|
|
77
73
|
*
|
|
78
74
|
* @param props - Component properties.
|
|
79
75
|
* @see {@link Props}
|