@design-edito/tools 0.5.1 → 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/arrays/index.d.ts +1 -1
- package/agnostic/arrays/index.js +1 -1
- package/agnostic/colors/index.d.ts +1 -1
- package/agnostic/colors/index.js +1 -1
- package/agnostic/css/index.d.ts +1 -1
- package/agnostic/css/index.js +1 -1
- 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 +1 -1
- package/agnostic/html/hyper-json/smart-tags/isolated/index.js +1 -1
- package/agnostic/html/index.d.ts +3 -3
- package/agnostic/html/index.js +3 -3
- package/agnostic/index.d.ts +3 -3
- package/agnostic/index.js +3 -3
- package/agnostic/misc/index.d.ts +1 -1
- package/agnostic/misc/index.js +1 -1
- package/agnostic/misc/logs/index.d.ts +1 -1
- package/agnostic/misc/logs/index.js +1 -1
- package/agnostic/numbers/index.d.ts +3 -3
- package/agnostic/numbers/index.js +3 -3
- package/agnostic/objects/index.d.ts +3 -3
- package/agnostic/objects/index.js +3 -3
- package/agnostic/random/index.d.ts +1 -1
- package/agnostic/random/index.js +1 -1
- package/agnostic/strings/index.d.ts +3 -3
- package/agnostic/strings/index.js +3 -3
- 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/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 -75
- 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 +78 -0
- package/components/ListLoader/index.controlled.js +99 -0
- package/components/ListLoader/index.d.ts +69 -0
- package/components/ListLoader/index.js +146 -0
- package/components/ListLoader/utils.d.ts +8 -0
- package/components/ListLoader/utils.js +10 -0
- 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/ResizeObserver/styles.module.css +0 -0
- 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 +5 -4
- package/components/index.js +5 -4
- package/components/public-classnames.d.ts +1 -0
- package/components/public-classnames.js +1 -0
- package/components/utils/index.d.ts +22 -0
- package/components/utils/index.js +31 -0
- 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/directory/index.d.ts +1 -1
- package/node/ftps/directory/index.js +1 -1
- 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 +3 -3
- package/node/images/transform/operations/index.js +3 -3
- package/node/index.d.ts +2 -2
- package/node/index.js +2 -2
- package/node/process/index.d.ts +1 -1
- package/node/process/index.js +1 -1
- package/node/sftp/directory/index.d.ts +1 -1
- package/node/sftp/directory/index.js +1 -1
- package/node/sftp/file/index.d.ts +1 -1
- package/node/sftp/file/index.js +1 -1
- package/package.json +27 -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 → ListLoader/styles.module.css} +0 -0
|
@@ -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 { input as publicClassName } from '../public-classnames.js';
|
|
8
|
+
import cssModule from './styles.module.css';
|
|
4
9
|
/**
|
|
5
|
-
* Input field
|
|
10
|
+
* Input field supporting controlled and uncontrolled usage.
|
|
6
11
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
12
|
+
* Renders a native `<input>` with optional label and error feedback. All
|
|
13
|
+
* standard input 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 { ControlledInput } from './index.controlled.js';
|
|
|
13
21
|
*
|
|
14
22
|
* @param props - Component properties.
|
|
15
23
|
* @see {@link Props}
|
|
16
|
-
*
|
|
17
|
-
* @returns A labelled input with optional internal value management.
|
|
24
|
+
* @returns A labelled input with optional error feedback.
|
|
18
25
|
*
|
|
19
26
|
* @remarks
|
|
20
|
-
* -
|
|
21
|
-
*
|
|
22
|
-
* -
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* - In hybrid mode, the internal value is updated before forwarding
|
|
26
|
-
* the `onChange` callback.
|
|
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.
|
|
27
32
|
*/
|
|
28
|
-
export const Input = (
|
|
29
|
-
|
|
33
|
+
export const Input = ({ label, error, value, defaultValue, onChange, className, ...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("input", { ...rest, id: id, className: rootClss, value: currentValue, onChange: handleChange }), isNotFalsy(error) && _jsx("span", { className: c('error'), children: error })] });
|
|
39
49
|
};
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import { type FunctionComponent } from 'react';
|
|
1
|
+
import { type FunctionComponent, type EventHandler, type ChangeEvent } from 'react';
|
|
2
2
|
import { type ZodType, type core } from 'zod';
|
|
3
3
|
import type { WithClassName } from '../utils/types.js';
|
|
4
|
-
type
|
|
5
|
-
type JsonValue = JsonPrimitive | JsonValue[] | {
|
|
6
|
-
[k: string]: JsonValue;
|
|
7
|
-
};
|
|
4
|
+
import type { JsonValue } from './types.js';
|
|
8
5
|
/**
|
|
9
6
|
* Props for the {@link JsonEditor} component.
|
|
10
7
|
*
|
|
@@ -14,6 +11,9 @@ type JsonValue = JsonPrimitive | JsonValue[] | {
|
|
|
14
11
|
* with the list of Zod issues. Not called when no `schema` is provided.
|
|
15
12
|
* @property schema - Optional Zod schema used to validate the value after each change.
|
|
16
13
|
* When provided, validation runs after `onChange` is dispatched.
|
|
14
|
+
* @property preventSameNameKeys - Refuses a record key rename that would collide
|
|
15
|
+
* with a sibling key, instead of letting the renamed entry replace it. Applies to
|
|
16
|
+
* every record in the tree. Defaults to `false`.
|
|
17
17
|
* @property className - Additional class name(s) applied to the root element.
|
|
18
18
|
*/
|
|
19
19
|
export type Props = WithClassName<{
|
|
@@ -21,6 +21,7 @@ export type Props = WithClassName<{
|
|
|
21
21
|
onChange?: (val: JsonValue) => void;
|
|
22
22
|
onValidationError?: (issues: core.$ZodIssue[]) => void;
|
|
23
23
|
schema?: ZodType<JsonValue>;
|
|
24
|
+
preventSameNameKeys?: boolean;
|
|
24
25
|
}>;
|
|
25
26
|
/**
|
|
26
27
|
* Interactive JSON editor component.
|
|
@@ -38,22 +39,189 @@ export type Props = WithClassName<{
|
|
|
38
39
|
* matching the current value type (`string`, `number`, `boolean`, `null`, `array`, `record`).
|
|
39
40
|
* - `type` — the type selector control.
|
|
40
41
|
* - `string`, `number`, `boolean`, `null` — primitive value inputs.
|
|
41
|
-
* - `record` — record entry list.
|
|
42
|
-
* - `array` — array
|
|
42
|
+
* - `record` — record entry list; `record-item` — one entry of it.
|
|
43
|
+
* - `array` — array item list; `array-item` — one item of it.
|
|
43
44
|
* - `prop-name` — key input for record entries.
|
|
44
45
|
* - `create-prop` — button to append a new entry to a record or array.
|
|
45
46
|
* - `delete-prop` — button to remove an existing entry.
|
|
46
47
|
* - `lift-prop` — button to move an array entry up.
|
|
47
48
|
* - `drop-prop` — button to move an array entry down.
|
|
49
|
+
* - `preview` — the serialized JSON preview.
|
|
48
50
|
*
|
|
49
51
|
* @param props - Component properties.
|
|
50
52
|
* @see {@link Props}
|
|
51
53
|
* @returns A recursive JSON value editor with a live serialized preview.
|
|
54
|
+
*
|
|
55
|
+
* @remarks
|
|
56
|
+
* **The editor is uncontrolled, at every level of the tree, and there is no
|
|
57
|
+
* controlled variant.** Each node seeds its state from its `defaultValue` on
|
|
58
|
+
* mount and never reads it again, so handing a mounted editor a new
|
|
59
|
+
* `defaultValue` changes nothing on screen — remount it with a different `key`
|
|
60
|
+
* to load another document. A controlled mode would mean rebuilding the state
|
|
61
|
+
* model so the whole tree reads from a single owner, which is why none of these
|
|
62
|
+
* components accepts a `value` prop.
|
|
52
63
|
*/
|
|
53
64
|
export declare const JsonEditor: FunctionComponent<Props>;
|
|
54
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Props for the {@link ValueEditor} component.
|
|
67
|
+
*
|
|
68
|
+
* @property defaultValue - Initial value of this node. Read on mount only.
|
|
69
|
+
* @property onChange - Called after the node's value changed, with the new value.
|
|
70
|
+
* @property path - Keys and indices leading to this node from the document root,
|
|
71
|
+
* exposed on the node as `data-path` and passed down to its children.
|
|
72
|
+
* @property preventSameNameKeys - Forwarded to every record below this node.
|
|
73
|
+
* @property className - Additional class name(s) applied to the root element.
|
|
74
|
+
*/
|
|
75
|
+
export type ValueEditorProps = WithClassName<{
|
|
55
76
|
defaultValue?: JsonValue;
|
|
56
77
|
onChange?: (newValue: JsonValue) => void;
|
|
57
78
|
path?: Array<string | number>;
|
|
79
|
+
preventSameNameKeys?: boolean;
|
|
80
|
+
}>;
|
|
81
|
+
/**
|
|
82
|
+
* One node of the editor tree: a type selector, plus the editor matching the
|
|
83
|
+
* value's current type. Switching type replaces the value with an empty one of
|
|
84
|
+
* the new type.
|
|
85
|
+
*
|
|
86
|
+
* @param props - Component properties.
|
|
87
|
+
* @see {@link ValueEditorProps}
|
|
88
|
+
* @returns A span holding the type selector and the type-appropriate editor.
|
|
89
|
+
*/
|
|
90
|
+
export declare const ValueEditor: FunctionComponent<ValueEditorProps>;
|
|
91
|
+
/**
|
|
92
|
+
* Props for the {@link StringEditor} component.
|
|
93
|
+
*
|
|
94
|
+
* @property type - Which control to render. Defaults to a single-line `input`.
|
|
95
|
+
* @property defaultValue - Initial text. Read on mount only.
|
|
96
|
+
* @property onChange - Native change handler, forwarded to the control.
|
|
97
|
+
* @property className - Additional class name(s) applied to the control.
|
|
98
|
+
*/
|
|
99
|
+
export type StringEditorProps = WithClassName<{
|
|
100
|
+
type?: 'input' | 'textarea';
|
|
101
|
+
defaultValue?: string;
|
|
102
|
+
onChange?: EventHandler<ChangeEvent<HTMLTextAreaElement | HTMLInputElement>>;
|
|
103
|
+
}>;
|
|
104
|
+
/**
|
|
105
|
+
* The editor for a string value.
|
|
106
|
+
*
|
|
107
|
+
* @param props - Component properties.
|
|
108
|
+
* @see {@link StringEditorProps}
|
|
109
|
+
* @returns A textarea or a text input, depending on `type`.
|
|
110
|
+
*/
|
|
111
|
+
export declare const StringEditor: FunctionComponent<StringEditorProps>;
|
|
112
|
+
/**
|
|
113
|
+
* Props for the {@link NumberEditor} component.
|
|
114
|
+
*
|
|
115
|
+
* @property defaultValue - Initial number. Read on mount only.
|
|
116
|
+
* @property onChange - Native change handler, forwarded to the input.
|
|
117
|
+
* @property className - Additional class name(s) applied to the input.
|
|
118
|
+
*/
|
|
119
|
+
export type NumberEditorProps = WithClassName<{
|
|
120
|
+
defaultValue?: number;
|
|
121
|
+
onChange?: EventHandler<ChangeEvent<HTMLInputElement>>;
|
|
122
|
+
}>;
|
|
123
|
+
/**
|
|
124
|
+
* The editor for a number value.
|
|
125
|
+
*
|
|
126
|
+
* @param props - Component properties.
|
|
127
|
+
* @see {@link NumberEditorProps}
|
|
128
|
+
* @returns A number input.
|
|
129
|
+
*/
|
|
130
|
+
export declare const NumberEditor: FunctionComponent<NumberEditorProps>;
|
|
131
|
+
/**
|
|
132
|
+
* Props for the {@link BooleanEditor} component.
|
|
133
|
+
*
|
|
134
|
+
* @property defaultValue - Initial checked state. Read on mount only.
|
|
135
|
+
* @property onChange - Native change handler, forwarded to the input.
|
|
136
|
+
* @property className - Additional class name(s) applied to the input.
|
|
137
|
+
*/
|
|
138
|
+
export type BooleanEditorProps = WithClassName<{
|
|
139
|
+
defaultValue?: boolean;
|
|
140
|
+
onChange?: EventHandler<ChangeEvent<HTMLInputElement>>;
|
|
58
141
|
}>;
|
|
59
|
-
|
|
142
|
+
/**
|
|
143
|
+
* The editor for a boolean value.
|
|
144
|
+
*
|
|
145
|
+
* @param props - Component properties.
|
|
146
|
+
* @see {@link BooleanEditorProps}
|
|
147
|
+
* @returns A checkbox.
|
|
148
|
+
*/
|
|
149
|
+
export declare const BooleanEditor: FunctionComponent<BooleanEditorProps>;
|
|
150
|
+
/**
|
|
151
|
+
* Props for the {@link NullEditor} component.
|
|
152
|
+
*
|
|
153
|
+
* @property className - Additional class name(s) applied to the root element.
|
|
154
|
+
*/
|
|
155
|
+
export type NullEditorProps = WithClassName<Record<string, never>>;
|
|
156
|
+
/**
|
|
157
|
+
* The editor for a null value — there is nothing to edit, so it only states the
|
|
158
|
+
* type.
|
|
159
|
+
*
|
|
160
|
+
* @param props - Component properties.
|
|
161
|
+
* @see {@link NullEditorProps}
|
|
162
|
+
* @returns A span reading `null`.
|
|
163
|
+
*/
|
|
164
|
+
export declare const NullEditor: FunctionComponent<NullEditorProps>;
|
|
165
|
+
/**
|
|
166
|
+
* Props for the {@link RecordEditor} component.
|
|
167
|
+
*
|
|
168
|
+
* @property defaultValue - Initial entries. Read on mount only.
|
|
169
|
+
* @property onChange - Called after any entry is added, removed, renamed or
|
|
170
|
+
* edited, with the rebuilt record.
|
|
171
|
+
* @property path - Keys and indices leading to this record from the document
|
|
172
|
+
* root, extended with each entry's key before being passed down.
|
|
173
|
+
* @property preventSameNameKeys - Refuses a rename that would collide with a
|
|
174
|
+
* sibling key, instead of letting the renamed entry replace it. Defaults to
|
|
175
|
+
* `false`. Forwarded to every record nested below.
|
|
176
|
+
* @property className - Additional class name(s) applied to the root element.
|
|
177
|
+
*/
|
|
178
|
+
export type RecordEditorProps = WithClassName<{
|
|
179
|
+
defaultValue?: Record<string, JsonValue>;
|
|
180
|
+
onChange?: (newValue: Record<string, JsonValue>) => void;
|
|
181
|
+
path?: Array<string | number>;
|
|
182
|
+
preventSameNameKeys?: boolean;
|
|
183
|
+
}>;
|
|
184
|
+
/**
|
|
185
|
+
* The editor for a record: one renamable, removable entry per key, each holding
|
|
186
|
+
* a nested {@link ValueEditor}.
|
|
187
|
+
*
|
|
188
|
+
* @param props - Component properties.
|
|
189
|
+
* @see {@link RecordEditorProps}
|
|
190
|
+
* @returns A list of entries, followed by the button that appends one.
|
|
191
|
+
*
|
|
192
|
+
* @remarks
|
|
193
|
+
* Entries are held in a `Map` keyed by property name, each carrying a stable id
|
|
194
|
+
* used as the React key — so renaming a property doesn't remount its editor and
|
|
195
|
+
* lose the subtree's state.
|
|
196
|
+
*/
|
|
197
|
+
export declare const RecordEditor: FunctionComponent<RecordEditorProps>;
|
|
198
|
+
/**
|
|
199
|
+
* Props for the {@link ArrayEditor} component.
|
|
200
|
+
*
|
|
201
|
+
* @property defaultValue - Initial items. Read on mount only.
|
|
202
|
+
* @property onChange - Called after any item is added, removed, moved or edited,
|
|
203
|
+
* with the rebuilt array.
|
|
204
|
+
* @property path - Keys and indices leading to this array from the document
|
|
205
|
+
* root, extended with each item's index before being passed down.
|
|
206
|
+
* @property preventSameNameKeys - Forwarded to every record nested below.
|
|
207
|
+
* @property className - Additional class name(s) applied to the root element.
|
|
208
|
+
*/
|
|
209
|
+
export type ArrayEditorProps = WithClassName<{
|
|
210
|
+
defaultValue?: JsonValue[];
|
|
211
|
+
onChange?: (newValue: JsonValue[]) => void;
|
|
212
|
+
path?: Array<string | number>;
|
|
213
|
+
preventSameNameKeys?: boolean;
|
|
214
|
+
}>;
|
|
215
|
+
/**
|
|
216
|
+
* The editor for an array: one removable, movable item per index, each holding a
|
|
217
|
+
* nested {@link ValueEditor}.
|
|
218
|
+
*
|
|
219
|
+
* @param props - Component properties.
|
|
220
|
+
* @see {@link ArrayEditorProps}
|
|
221
|
+
* @returns An ordered list of items, followed by the button that appends one.
|
|
222
|
+
*
|
|
223
|
+
* @remarks
|
|
224
|
+
* Items are held in a `Map` keyed by a stable id used as the React key, so
|
|
225
|
+
* reordering moves the editors rather than remounting them.
|
|
226
|
+
*/
|
|
227
|
+
export declare const ArrayEditor: FunctionComponent<ArrayEditorProps>;
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { ZodError } from 'zod';
|
|
4
|
-
import { clss } from '../../agnostic/css/clss/index.js';
|
|
5
|
-
import { isNonNullObject } from '../../agnostic/objects/is-object/index.js';
|
|
6
4
|
import { randomHash } from '../../agnostic/random/uuid/index.js';
|
|
5
|
+
import { isNonNullObject } from '../../agnostic/objects/is-object/index.js';
|
|
7
6
|
import { Input } from '../Input/index.js';
|
|
8
7
|
import { Select } from '../Select/index.js';
|
|
9
8
|
import { Textarea } from '../Textarea/index.js';
|
|
10
9
|
import { mergeClassNames } from '../utils/index.js';
|
|
11
|
-
import {
|
|
12
|
-
import cssModule from './styles.module.css';
|
|
13
|
-
/* Shared across all sub components */
|
|
14
|
-
const c = clss(publicClassName, { cssModule });
|
|
10
|
+
import { c, getValueType } from './utils.js';
|
|
15
11
|
/**
|
|
16
12
|
* Interactive JSON editor component.
|
|
17
13
|
*
|
|
@@ -28,19 +24,29 @@ const c = clss(publicClassName, { cssModule });
|
|
|
28
24
|
* matching the current value type (`string`, `number`, `boolean`, `null`, `array`, `record`).
|
|
29
25
|
* - `type` — the type selector control.
|
|
30
26
|
* - `string`, `number`, `boolean`, `null` — primitive value inputs.
|
|
31
|
-
* - `record` — record entry list.
|
|
32
|
-
* - `array` — array
|
|
27
|
+
* - `record` — record entry list; `record-item` — one entry of it.
|
|
28
|
+
* - `array` — array item list; `array-item` — one item of it.
|
|
33
29
|
* - `prop-name` — key input for record entries.
|
|
34
30
|
* - `create-prop` — button to append a new entry to a record or array.
|
|
35
31
|
* - `delete-prop` — button to remove an existing entry.
|
|
36
32
|
* - `lift-prop` — button to move an array entry up.
|
|
37
33
|
* - `drop-prop` — button to move an array entry down.
|
|
34
|
+
* - `preview` — the serialized JSON preview.
|
|
38
35
|
*
|
|
39
36
|
* @param props - Component properties.
|
|
40
37
|
* @see {@link Props}
|
|
41
38
|
* @returns A recursive JSON value editor with a live serialized preview.
|
|
39
|
+
*
|
|
40
|
+
* @remarks
|
|
41
|
+
* **The editor is uncontrolled, at every level of the tree, and there is no
|
|
42
|
+
* controlled variant.** Each node seeds its state from its `defaultValue` on
|
|
43
|
+
* mount and never reads it again, so handing a mounted editor a new
|
|
44
|
+
* `defaultValue` changes nothing on screen — remount it with a different `key`
|
|
45
|
+
* to load another document. A controlled mode would mean rebuilding the state
|
|
46
|
+
* model so the whole tree reads from a single owner, which is why none of these
|
|
47
|
+
* components accepts a `value` prop.
|
|
42
48
|
*/
|
|
43
|
-
export const JsonEditor = ({ defaultValue = null, onChange, onValidationError, schema, className }) => {
|
|
49
|
+
export const JsonEditor = ({ defaultValue = null, onChange, onValidationError, schema, preventSameNameKeys, className }) => {
|
|
44
50
|
const [value, setValue] = useState(defaultValue);
|
|
45
51
|
const setValueAndDispatch = (val) => {
|
|
46
52
|
setValue(val);
|
|
@@ -57,15 +63,19 @@ export const JsonEditor = ({ defaultValue = null, onChange, onValidationError, s
|
|
|
57
63
|
throw err;
|
|
58
64
|
}
|
|
59
65
|
};
|
|
60
|
-
const rootClss = mergeClassNames(c(
|
|
61
|
-
return _jsxs("div", { className: rootClss, children: [_jsx(ValueEditor, { defaultValue: defaultValue, onChange: val => setValueAndDispatch(val), path: [] }), _jsx("pre", { children: JSON.stringify(value, null, 2) })] });
|
|
66
|
+
const rootClss = mergeClassNames(c(), className);
|
|
67
|
+
return _jsxs("div", { className: rootClss, children: [_jsx(ValueEditor, { defaultValue: defaultValue, onChange: val => setValueAndDispatch(val), preventSameNameKeys: preventSameNameKeys, path: [] }), _jsx("pre", { className: c('preview'), children: JSON.stringify(value, null, 2) })] });
|
|
62
68
|
};
|
|
63
|
-
|
|
64
|
-
*
|
|
65
|
-
*
|
|
69
|
+
/**
|
|
70
|
+
* One node of the editor tree: a type selector, plus the editor matching the
|
|
71
|
+
* value's current type. Switching type replaces the value with an empty one of
|
|
72
|
+
* the new type.
|
|
66
73
|
*
|
|
67
|
-
*
|
|
68
|
-
|
|
74
|
+
* @param props - Component properties.
|
|
75
|
+
* @see {@link ValueEditorProps}
|
|
76
|
+
* @returns A span holding the type selector and the type-appropriate editor.
|
|
77
|
+
*/
|
|
78
|
+
export const ValueEditor = ({ defaultValue = null, onChange, path = [], preventSameNameKeys, className }) => {
|
|
69
79
|
// State
|
|
70
80
|
const [value, setValue] = useState(defaultValue);
|
|
71
81
|
const setValueAndDispatch = (newValue) => {
|
|
@@ -84,7 +94,7 @@ export const ValueEditor = ({ defaultValue = {}, onChange, path = [] }) => {
|
|
|
84
94
|
if (newType === 'null')
|
|
85
95
|
return setValueAndDispatch(null);
|
|
86
96
|
if (newType === 'record')
|
|
87
|
-
return setValueAndDispatch({
|
|
97
|
+
return setValueAndDispatch({});
|
|
88
98
|
if (newType === 'array')
|
|
89
99
|
return setValueAndDispatch([]);
|
|
90
100
|
};
|
|
@@ -94,55 +104,67 @@ export const ValueEditor = ({ defaultValue = {}, onChange, path = [] }) => {
|
|
|
94
104
|
const handleRecordValueChange = e => setValueAndDispatch(e);
|
|
95
105
|
const handleArrayValueChange = e => setValueAndDispatch(e);
|
|
96
106
|
// Rendering
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
number: typeof value === 'number',
|
|
100
|
-
boolean: typeof value === 'boolean',
|
|
101
|
-
null: value === null,
|
|
102
|
-
array: Array.isArray(value),
|
|
103
|
-
record: !Array.isArray(value) && isNonNullObject(value)
|
|
104
|
-
});
|
|
107
|
+
const valueType = getValueType(value);
|
|
108
|
+
const valueEditorClss = mergeClassNames(c('value', valueType), className);
|
|
105
109
|
const pathStringDataAttr = path.map(e => e.toString()).join('.');
|
|
106
|
-
return _jsxs("span", { className: valueEditorClss, "data-path": pathStringDataAttr, children: [_jsxs(Select, { className: c('type'), onChange: handleTypeSelectChange, children: [_jsx("option", { value: 'string',
|
|
110
|
+
return _jsxs("span", { className: valueEditorClss, "data-path": pathStringDataAttr, children: [_jsxs(Select, { className: c('type'), value: valueType, onChange: handleTypeSelectChange, children: [_jsx("option", { value: 'string', children: "string" }), _jsx("option", { value: 'number', children: "number" }), _jsx("option", { value: 'boolean', children: "boolean" }), _jsx("option", { value: 'null', children: "null" }), _jsx("option", { value: 'record', children: "record" }), _jsx("option", { value: 'array', children: "array" })] }), typeof value === 'string' && _jsx(StringEditor, { type: 'textarea', defaultValue: value, onChange: handleStringValueChange }), typeof value === 'number' && _jsx(NumberEditor, { defaultValue: value, onChange: handleNumberValueChange }), typeof value === 'boolean' && _jsx(BooleanEditor, { defaultValue: value, onChange: handleBooleanValueChange }), value === null && _jsx(NullEditor, {}), isNonNullObject(value) && !Array.isArray(value) && _jsx(RecordEditor, { defaultValue: value, onChange: handleRecordValueChange, preventSameNameKeys: preventSameNameKeys, path: path }), Array.isArray(value) && _jsx(ArrayEditor, { defaultValue: value, onChange: handleArrayValueChange, preventSameNameKeys: preventSameNameKeys, path: path })] });
|
|
107
111
|
};
|
|
108
|
-
|
|
109
|
-
*
|
|
110
|
-
* String
|
|
111
|
-
*
|
|
112
|
-
* * * * * * * * * * * * * * * * */
|
|
113
|
-
const StringEditor = (props) => props.type === 'textarea'
|
|
114
|
-
? _jsx(Textarea, { className: c('string'), defaultValue: props.defaultValue ?? '', onChange: props.onChange })
|
|
115
|
-
: _jsx(Input, { type: 'text', className: c('string'), defaultValue: props.defaultValue ?? '', onChange: props.onChange });
|
|
116
|
-
/* * * * * * * * * * * * * * * * *
|
|
117
|
-
*
|
|
118
|
-
* Number
|
|
119
|
-
*
|
|
120
|
-
* * * * * * * * * * * * * * * * */
|
|
121
|
-
const NumberEditor = (props) => _jsx(Input, { className: c('number'), type: 'number', defaultValue: props.defaultValue ?? 0, onChange: props.onChange });
|
|
122
|
-
/* * * * * * * * * * * * * * * * *
|
|
112
|
+
/**
|
|
113
|
+
* The editor for a string value.
|
|
123
114
|
*
|
|
124
|
-
*
|
|
115
|
+
* @param props - Component properties.
|
|
116
|
+
* @see {@link StringEditorProps}
|
|
117
|
+
* @returns A textarea or a text input, depending on `type`.
|
|
118
|
+
*/
|
|
119
|
+
export const StringEditor = ({ type, defaultValue, onChange, className }) => type === 'textarea'
|
|
120
|
+
? _jsx(Textarea, { className: mergeClassNames(c('string'), className), defaultValue: defaultValue ?? '', onChange: onChange })
|
|
121
|
+
: _jsx(Input, { type: 'text', className: mergeClassNames(c('string'), className), defaultValue: defaultValue ?? '', onChange: onChange });
|
|
122
|
+
/**
|
|
123
|
+
* The editor for a number value.
|
|
125
124
|
*
|
|
126
|
-
*
|
|
127
|
-
|
|
128
|
-
|
|
125
|
+
* @param props - Component properties.
|
|
126
|
+
* @see {@link NumberEditorProps}
|
|
127
|
+
* @returns A number input.
|
|
128
|
+
*/
|
|
129
|
+
export const NumberEditor = ({ defaultValue, onChange, className }) => _jsx(Input, { className: mergeClassNames(c('number'), className), type: 'number', defaultValue: defaultValue ?? 0, onChange: onChange });
|
|
130
|
+
/**
|
|
131
|
+
* The editor for a boolean value.
|
|
129
132
|
*
|
|
130
|
-
*
|
|
133
|
+
* @param props - Component properties.
|
|
134
|
+
* @see {@link BooleanEditorProps}
|
|
135
|
+
* @returns A checkbox.
|
|
136
|
+
*/
|
|
137
|
+
export const BooleanEditor = ({ defaultValue, onChange, className }) => _jsx(Input, { className: mergeClassNames(c('boolean'), className), type: 'checkbox', defaultChecked: defaultValue ?? false, onChange: onChange });
|
|
138
|
+
/**
|
|
139
|
+
* The editor for a null value — there is nothing to edit, so it only states the
|
|
140
|
+
* type.
|
|
131
141
|
*
|
|
132
|
-
*
|
|
133
|
-
|
|
134
|
-
|
|
142
|
+
* @param props - Component properties.
|
|
143
|
+
* @see {@link NullEditorProps}
|
|
144
|
+
* @returns A span reading `null`.
|
|
145
|
+
*/
|
|
146
|
+
export const NullEditor = ({ className }) => _jsx("span", { className: mergeClassNames(c('null'), className), children: "null" });
|
|
147
|
+
/**
|
|
148
|
+
* The editor for a record: one renamable, removable entry per key, each holding
|
|
149
|
+
* a nested {@link ValueEditor}.
|
|
135
150
|
*
|
|
136
|
-
*
|
|
151
|
+
* @param props - Component properties.
|
|
152
|
+
* @see {@link RecordEditorProps}
|
|
153
|
+
* @returns A list of entries, followed by the button that appends one.
|
|
137
154
|
*
|
|
138
|
-
*
|
|
139
|
-
|
|
155
|
+
* @remarks
|
|
156
|
+
* Entries are held in a `Map` keyed by property name, each carrying a stable id
|
|
157
|
+
* used as the React key — so renaming a property doesn't remount its editor and
|
|
158
|
+
* lose the subtree's state.
|
|
159
|
+
*/
|
|
160
|
+
export const RecordEditor = ({ defaultValue = {}, onChange, path, preventSameNameKeys = false, className }) => {
|
|
140
161
|
const [value, setValue] = useState(new Map(Object
|
|
141
162
|
.entries(defaultValue)
|
|
142
163
|
.map(([key, val]) => [key, {
|
|
143
164
|
id: randomHash(8),
|
|
144
165
|
val
|
|
145
166
|
}])));
|
|
167
|
+
const [refusedRenames, setRefusedRenames] = useState(0);
|
|
146
168
|
const setValueAndDispatch = (newValue) => {
|
|
147
169
|
setValue(newValue);
|
|
148
170
|
onChange?.(Array.from(newValue).reduce((acc, [key, { val }]) => ({
|
|
@@ -156,7 +178,7 @@ const RecordEditor = ({ defaultValue = {}, onChange, path }) => {
|
|
|
156
178
|
setValueAndDispatch(newValue);
|
|
157
179
|
};
|
|
158
180
|
const handleCreateProp = () => {
|
|
159
|
-
let propName =
|
|
181
|
+
let propName = randomHash(4);
|
|
160
182
|
while (value.get(propName) !== undefined) {
|
|
161
183
|
propName = randomHash(4);
|
|
162
184
|
}
|
|
@@ -166,12 +188,24 @@ const RecordEditor = ({ defaultValue = {}, onChange, path }) => {
|
|
|
166
188
|
};
|
|
167
189
|
const handleRenameProp = (oldName) => e => {
|
|
168
190
|
const newName = e.target.value;
|
|
191
|
+
if (newName === oldName)
|
|
192
|
+
return;
|
|
193
|
+
if (preventSameNameKeys && value.has(newName)) {
|
|
194
|
+
// Refusing leaves the state untouched, and the name input is uncontrolled —
|
|
195
|
+
// it would go on showing what was typed. Bumping this counter is what puts
|
|
196
|
+
// the field back: it feeds the input's key, so the input remounts and reads
|
|
197
|
+
// its `defaultValue` again.
|
|
198
|
+
setRefusedRenames(count => count + 1);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
// Dropping the colliding entry first is what makes the outcome predictable:
|
|
202
|
+
// the renamed entry wins, and keeps its own position in the record. Rebuilding
|
|
203
|
+
// the Map without this leaves two entries under the same key, and `new Map`
|
|
204
|
+
// silently keeps the last one — losing the value being renamed.
|
|
169
205
|
const newValue = new Map(Array
|
|
170
206
|
.from(value)
|
|
171
|
-
.
|
|
172
|
-
key === oldName ? newName : key,
|
|
173
|
-
{ id, val }
|
|
174
|
-
]));
|
|
207
|
+
.filter(([key]) => key !== newName)
|
|
208
|
+
.map(([key, entry]) => [key === oldName ? newName : key, entry]));
|
|
175
209
|
setValueAndDispatch(newValue);
|
|
176
210
|
};
|
|
177
211
|
const handleChangeProp = (changedKey) => (newValue) => {
|
|
@@ -181,14 +215,21 @@ const RecordEditor = ({ defaultValue = {}, onChange, path }) => {
|
|
|
181
215
|
}]));
|
|
182
216
|
setValueAndDispatch(newVal);
|
|
183
217
|
};
|
|
184
|
-
return _jsxs("ul", { className: c('record'), children: [Array.from(value).map(([key, { id, val }]) => _jsxs("li", { "data-key": key, "data-path": [...(path ?? []), key].join('.'), children: [_jsx("button", { className: c('delete-prop'), onClick: () => handleDeleteProp(key), children: "x" }), _jsx("span", { className: c('prop-name'), children: _jsx(StringEditor, { type: 'input', defaultValue: key, onChange: handleRenameProp(key) }) }), _jsx(ValueEditor, { defaultValue: val, onChange: handleChangeProp(key), path: [...(path ?? []), key] })] }, id)), _jsx("button", { className: c('create-prop'), onClick: handleCreateProp, children: "+" })] });
|
|
218
|
+
return _jsxs("ul", { className: mergeClassNames(c('record'), className), children: [Array.from(value).map(([key, { id, val }]) => _jsxs("li", { className: c('record-item'), "data-key": key, "data-path": [...(path ?? []), key].join('.'), children: [_jsx("button", { className: c('delete-prop'), onClick: () => handleDeleteProp(key), children: "x" }), _jsx("span", { className: c('prop-name'), children: _jsx(StringEditor, { type: 'input', defaultValue: key, onChange: handleRenameProp(key) }, `${id}-${refusedRenames}`) }), _jsx(ValueEditor, { defaultValue: val, onChange: handleChangeProp(key), preventSameNameKeys: preventSameNameKeys, path: [...(path ?? []), key] })] }, id)), _jsx("button", { className: c('create-prop'), onClick: handleCreateProp, children: "+" })] });
|
|
185
219
|
};
|
|
186
|
-
|
|
220
|
+
/**
|
|
221
|
+
* The editor for an array: one removable, movable item per index, each holding a
|
|
222
|
+
* nested {@link ValueEditor}.
|
|
187
223
|
*
|
|
188
|
-
*
|
|
224
|
+
* @param props - Component properties.
|
|
225
|
+
* @see {@link ArrayEditorProps}
|
|
226
|
+
* @returns An ordered list of items, followed by the button that appends one.
|
|
189
227
|
*
|
|
190
|
-
*
|
|
191
|
-
|
|
228
|
+
* @remarks
|
|
229
|
+
* Items are held in a `Map` keyed by a stable id used as the React key, so
|
|
230
|
+
* reordering moves the editors rather than remounting them.
|
|
231
|
+
*/
|
|
232
|
+
export const ArrayEditor = ({ defaultValue = [], onChange, path, preventSameNameKeys, className }) => {
|
|
192
233
|
const [value, setValue] = useState(new Map(defaultValue.map(val => [randomHash(8), val])));
|
|
193
234
|
const setValueAndDispatch = (newValue) => {
|
|
194
235
|
setValue(newValue);
|
|
@@ -207,17 +248,34 @@ const ArrayEditor = ({ defaultValue = [], onChange, path }) => {
|
|
|
207
248
|
[Array.from(value)[pos]?.[0] ?? randomHash(8), newValue],
|
|
208
249
|
...Array.from(value).slice(pos + 1)
|
|
209
250
|
]));
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
251
|
+
// Swapping with the entry before, so position 0 has nothing to swap with. The
|
|
252
|
+
// guard is what keeps `pos - 1` from reaching back into the array as `-1`.
|
|
253
|
+
const handleLiftProp = (pos) => {
|
|
254
|
+
if (pos <= 0)
|
|
255
|
+
return;
|
|
256
|
+
const entries = Array.from(value);
|
|
257
|
+
setValueAndDispatch(new Map([
|
|
258
|
+
...entries.slice(0, pos - 1),
|
|
259
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
260
|
+
entries[pos],
|
|
261
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
262
|
+
entries[pos - 1],
|
|
263
|
+
...entries.slice(pos + 1)
|
|
264
|
+
]));
|
|
265
|
+
};
|
|
266
|
+
// Same, mirrored: the last entry has nothing to swap with.
|
|
267
|
+
const handleDropProp = (pos) => {
|
|
268
|
+
const entries = Array.from(value);
|
|
269
|
+
if (pos < 0 || pos >= entries.length - 1)
|
|
270
|
+
return;
|
|
271
|
+
setValueAndDispatch(new Map([
|
|
272
|
+
...entries.slice(0, pos),
|
|
273
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
274
|
+
entries[pos + 1],
|
|
275
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
276
|
+
entries[pos],
|
|
277
|
+
...entries.slice(pos + 2)
|
|
278
|
+
]));
|
|
279
|
+
};
|
|
280
|
+
return _jsxs("ol", { className: mergeClassNames(c('array'), className), children: [Array.from(value).map(([id, val], pos) => _jsxs("li", { className: c('array-item'), "data-path": [...(path ?? []), pos].join('.'), children: [_jsx("button", { className: c('delete-prop'), onClick: () => handleDeleteProp(pos), children: "x" }), _jsx("button", { className: c('lift-prop'), disabled: pos === 0, onClick: () => handleLiftProp(pos), children: "\u2191" }), _jsx("button", { className: c('drop-prop'), disabled: pos === value.size - 1, onClick: () => handleDropProp(pos), children: "\u2193" }), _jsx(ValueEditor, { defaultValue: val, onChange: handleChangeProp(pos), preventSameNameKeys: preventSameNameKeys, path: [...(path ?? []), pos] })] }, id)), _jsx("button", { className: c('create-prop'), onClick: handleCreateProp, children: "+" })] });
|
|
223
281
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** A JSON value that contains no other value. */
|
|
2
|
+
export type JsonPrimitive = string | number | boolean | null;
|
|
3
|
+
/** Any value `JSON.parse` can produce, and the type the editor reads and emits. */
|
|
4
|
+
export type JsonValue = JsonPrimitive | JsonValue[] | {
|
|
5
|
+
[k: string]: JsonValue;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* The editor's name for a value's kind. It is what the type selector offers, and
|
|
9
|
+
* what modifies the `value` class name on each node.
|
|
10
|
+
*/
|
|
11
|
+
export type ValueType = 'string' | 'number' | 'boolean' | 'null' | 'array' | 'record';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { JsonValue, ValueType } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Shared by every editor in the tree — they all emit class names under the same
|
|
4
|
+
* public prefix, so a single instance is what keeps them consistent.
|
|
5
|
+
*/
|
|
6
|
+
export declare const c: import("../../agnostic/css/clss/index.js").ClssMaker;
|
|
7
|
+
/** Which {@link ValueType} a value belongs to. */
|
|
8
|
+
export declare function getValueType(value: JsonValue): ValueType;
|