react-store-input 0.6.0 → 0.7.0
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/CHANGELOG.md +12 -0
- package/README.md +5 -1
- package/dist/text-editor.d.mts +7 -2
- package/dist/text-editor.d.ts +7 -2
- package/dist/text-editor.js +57 -9
- package/dist/text-editor.js.map +1 -1
- package/dist/text-editor.mjs +57 -9
- package/dist/text-editor.mjs.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,18 @@ All notable changes to this project are documented in this file.
|
|
|
5
5
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project uses [Semantic Versioning](https://semver.org/).
|
|
7
7
|
|
|
8
|
+
## [0.7.0] - 2026-08-24
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Added a store-bound `RichTextEditor` export alongside the minimal
|
|
13
|
+
`TextEditor` in the optional `react-store-input/text-editor` entry point.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Updated the optional editor peer and binding types for
|
|
18
|
+
`gw-rich-text-editor@^0.2.0`.
|
|
19
|
+
|
|
8
20
|
## [0.6.0] - 2026-08-10
|
|
9
21
|
|
|
10
22
|
### Changed
|
package/README.md
CHANGED
|
@@ -263,11 +263,15 @@ npm install gw-rich-text-editor
|
|
|
263
263
|
```
|
|
264
264
|
|
|
265
265
|
```tsx
|
|
266
|
-
import { TextEditor } from "react-store-input/text-editor";
|
|
266
|
+
import { RichTextEditor, TextEditor } from "react-store-input/text-editor";
|
|
267
267
|
|
|
268
268
|
<TextEditor store={store} name="content" />;
|
|
269
|
+
<RichTextEditor store={store} name="content" />;
|
|
269
270
|
```
|
|
270
271
|
|
|
272
|
+
`TextEditor` binds the minimal paragraph editor. `RichTextEditor` binds the
|
|
273
|
+
built-in rich schema, history, keymap, and file-attachment preset.
|
|
274
|
+
|
|
271
275
|
## Development
|
|
272
276
|
|
|
273
277
|
Release history is tracked in [CHANGELOG.md](./CHANGELOG.md). The manual
|
package/dist/text-editor.d.mts
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { TextEditorProps as TextEditorProps$1 } from 'gw-rich-text-editor';
|
|
2
|
+
import { RichTextEditorProps as RichTextEditorProps$1, TextEditorProps as TextEditorProps$1 } from 'gw-rich-text-editor';
|
|
3
3
|
import { Store } from 'gw-store';
|
|
4
4
|
|
|
5
5
|
type TextEditorProps<TState extends object> = Omit<TextEditorProps$1, "name"> & {
|
|
6
6
|
store: Store<TState>;
|
|
7
7
|
name: keyof TState;
|
|
8
8
|
};
|
|
9
|
+
type RichTextEditorProps<TState extends object> = Omit<RichTextEditorProps$1, "name"> & {
|
|
10
|
+
store: Store<TState>;
|
|
11
|
+
name: keyof TState;
|
|
12
|
+
};
|
|
9
13
|
declare function TextEditor<TState extends object>({ store, name, defaultValue, ref, ...props }: TextEditorProps<TState>): react_jsx_runtime.JSX.Element;
|
|
14
|
+
declare function RichTextEditor<TState extends object>({ store, name, defaultValue, ref, ...props }: RichTextEditorProps<TState>): react_jsx_runtime.JSX.Element;
|
|
10
15
|
|
|
11
|
-
export { TextEditor, type TextEditorProps };
|
|
16
|
+
export { RichTextEditor, type RichTextEditorProps, TextEditor, type TextEditorProps };
|
package/dist/text-editor.d.ts
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { TextEditorProps as TextEditorProps$1 } from 'gw-rich-text-editor';
|
|
2
|
+
import { RichTextEditorProps as RichTextEditorProps$1, TextEditorProps as TextEditorProps$1 } from 'gw-rich-text-editor';
|
|
3
3
|
import { Store } from 'gw-store';
|
|
4
4
|
|
|
5
5
|
type TextEditorProps<TState extends object> = Omit<TextEditorProps$1, "name"> & {
|
|
6
6
|
store: Store<TState>;
|
|
7
7
|
name: keyof TState;
|
|
8
8
|
};
|
|
9
|
+
type RichTextEditorProps<TState extends object> = Omit<RichTextEditorProps$1, "name"> & {
|
|
10
|
+
store: Store<TState>;
|
|
11
|
+
name: keyof TState;
|
|
12
|
+
};
|
|
9
13
|
declare function TextEditor<TState extends object>({ store, name, defaultValue, ref, ...props }: TextEditorProps<TState>): react_jsx_runtime.JSX.Element;
|
|
14
|
+
declare function RichTextEditor<TState extends object>({ store, name, defaultValue, ref, ...props }: RichTextEditorProps<TState>): react_jsx_runtime.JSX.Element;
|
|
10
15
|
|
|
11
|
-
export { TextEditor, type TextEditorProps };
|
|
16
|
+
export { RichTextEditor, type RichTextEditorProps, TextEditor, type TextEditorProps };
|
package/dist/text-editor.js
CHANGED
|
@@ -20,14 +20,17 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/text_editor.tsx
|
|
21
21
|
var text_editor_exports = {};
|
|
22
22
|
__export(text_editor_exports, {
|
|
23
|
+
RichTextEditor: () => RichTextEditor,
|
|
23
24
|
TextEditor: () => TextEditor
|
|
24
25
|
});
|
|
25
26
|
module.exports = __toCommonJS(text_editor_exports);
|
|
26
27
|
|
|
27
28
|
// src/editor/text_editor.tsx
|
|
28
29
|
var import_gw_rich_text_editor = require("gw-rich-text-editor");
|
|
29
|
-
|
|
30
|
+
|
|
31
|
+
// src/editor/use_text_editor_binding.tsx
|
|
30
32
|
var import_react2 = require("react");
|
|
33
|
+
var import_gw_result = require("gw-result");
|
|
31
34
|
|
|
32
35
|
// src/binding/binding.ts
|
|
33
36
|
function defineBinding(binding) {
|
|
@@ -154,17 +157,18 @@ function useStoreBinding(store, binding, options = {}) {
|
|
|
154
157
|
};
|
|
155
158
|
}
|
|
156
159
|
|
|
157
|
-
// src/editor/
|
|
158
|
-
|
|
159
|
-
function TextEditor({
|
|
160
|
+
// src/editor/use_text_editor_binding.tsx
|
|
161
|
+
function useTextEditorBinding({
|
|
160
162
|
store,
|
|
161
163
|
name,
|
|
162
|
-
|
|
163
|
-
ref,
|
|
164
|
-
...props
|
|
164
|
+
ref
|
|
165
165
|
}) {
|
|
166
166
|
const controllerRef = (0, import_react2.useRef)(null);
|
|
167
|
-
(0, import_react2.useImperativeHandle)(
|
|
167
|
+
(0, import_react2.useImperativeHandle)(
|
|
168
|
+
ref,
|
|
169
|
+
() => controllerRef.current,
|
|
170
|
+
[]
|
|
171
|
+
);
|
|
168
172
|
const binding = (0, import_react2.useMemo)(() => {
|
|
169
173
|
return defineBinding({
|
|
170
174
|
lens: stateLens().prop(name),
|
|
@@ -174,7 +178,7 @@ function TextEditor({
|
|
|
174
178
|
})
|
|
175
179
|
});
|
|
176
180
|
}, [name]);
|
|
177
|
-
const
|
|
181
|
+
const storeBinding = useStoreBinding(store, binding, {
|
|
178
182
|
onStoreChange: (value) => {
|
|
179
183
|
const controller = controllerRef.current;
|
|
180
184
|
if (controller && controller.value !== value) {
|
|
@@ -182,6 +186,23 @@ function TextEditor({
|
|
|
182
186
|
}
|
|
183
187
|
}
|
|
184
188
|
});
|
|
189
|
+
return { controllerRef, ...storeBinding };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// src/editor/text_editor.tsx
|
|
193
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
194
|
+
function TextEditor({
|
|
195
|
+
store,
|
|
196
|
+
name,
|
|
197
|
+
defaultValue,
|
|
198
|
+
ref,
|
|
199
|
+
...props
|
|
200
|
+
}) {
|
|
201
|
+
const { controllerRef, initialValue, commit } = useTextEditorBinding({
|
|
202
|
+
store,
|
|
203
|
+
name,
|
|
204
|
+
ref
|
|
205
|
+
});
|
|
185
206
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
186
207
|
import_gw_rich_text_editor.TextEditor,
|
|
187
208
|
{
|
|
@@ -196,8 +217,35 @@ function TextEditor({
|
|
|
196
217
|
}
|
|
197
218
|
);
|
|
198
219
|
}
|
|
220
|
+
function RichTextEditor({
|
|
221
|
+
store,
|
|
222
|
+
name,
|
|
223
|
+
defaultValue,
|
|
224
|
+
ref,
|
|
225
|
+
...props
|
|
226
|
+
}) {
|
|
227
|
+
const { controllerRef, initialValue, commit } = useTextEditorBinding({
|
|
228
|
+
store,
|
|
229
|
+
name,
|
|
230
|
+
ref
|
|
231
|
+
});
|
|
232
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
233
|
+
import_gw_rich_text_editor.RichTextEditor,
|
|
234
|
+
{
|
|
235
|
+
...props,
|
|
236
|
+
ref: controllerRef,
|
|
237
|
+
name: String(name),
|
|
238
|
+
defaultValue: defaultValue ?? initialValue,
|
|
239
|
+
onChange: (value) => {
|
|
240
|
+
commit(value);
|
|
241
|
+
props.onChange?.(value);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
);
|
|
245
|
+
}
|
|
199
246
|
// Annotate the CommonJS export names for ESM import in node:
|
|
200
247
|
0 && (module.exports = {
|
|
248
|
+
RichTextEditor,
|
|
201
249
|
TextEditor
|
|
202
250
|
});
|
|
203
251
|
//# sourceMappingURL=text-editor.js.map
|
package/dist/text-editor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/text_editor.tsx","../src/editor/text_editor.tsx","../src/binding/binding.ts","../src/binding/codec.ts","../src/binding/lens.ts","../src/binding/use_store_binding.tsx"],"sourcesContent":["export * from \"./editor/text_editor\";\n","import {\n TextEditor as GwTextEditor,\n TextEditorController,\n type TextEditorProps as GwTextEditorProps,\n} from \"gw-rich-text-editor\";\nimport { ok } from \"gw-result\";\nimport { useImperativeHandle, useMemo, useRef } from \"react\";\nimport type { Store } from \"gw-store\";\nimport { defineBinding } from \"../binding/binding\";\nimport { defineCodec } from \"../binding/codec\";\nimport { stateLens } from \"../binding/lens\";\nimport { useStoreBinding } from \"../binding/use_store_binding\";\n\nexport type TextEditorProps<TState extends object> = Omit<GwTextEditorProps, \"name\"> & {\n store: Store<TState>;\n name: keyof TState;\n};\n\nexport function TextEditor<TState extends object>({\n store,\n name,\n defaultValue,\n ref,\n ...props\n}: TextEditorProps<TState>) {\n const controllerRef = useRef<TextEditorController>(null);\n\n useImperativeHandle(ref, () => controllerRef.current as TextEditorController, []);\n\n const binding = useMemo(() => {\n type TValue = TState[typeof name];\n return defineBinding({\n lens: stateLens<TState>().prop(name),\n codec: defineCodec<TValue, string>({\n format: (value) => (typeof value === \"string\" ? value : \"\"),\n parse: (value) => ok(value as TValue),\n }),\n });\n }, [name]);\n\n const { commit, initialValue } = useStoreBinding(store, binding, {\n onStoreChange: (value) => {\n const controller = controllerRef.current;\n\n if (controller && controller.value !== value) {\n controller.value = value;\n }\n },\n });\n\n return (\n <GwTextEditor\n {...props}\n ref={controllerRef}\n name={String(name)}\n defaultValue={defaultValue ?? initialValue}\n onChange={(value) => {\n commit(value);\n props.onChange?.(value);\n }}\n />\n );\n}\n","import type { Codec } from \"./codec\";\nimport type { Lens } from \"./lens\";\n\nexport type InputBinding<TState extends object, TValue, TInput, TError = never> = {\n lens: Lens<TState, TValue>;\n codec: Codec<TValue, TInput, TError>;\n};\n\nexport function defineBinding<TState extends object, TValue, TInput, TError = never>(\n binding: InputBinding<TState, TValue, TInput, TError>,\n) {\n return binding;\n}\n","import type { Result } from \"gw-result\";\n\nexport type Codec<TValue, TInput, TError = never> = {\n format: (value: TValue) => TInput;\n parse: (input: TInput) => Result<TValue, TError>;\n equals?: (previous: TValue, next: TValue) => boolean;\n};\n\nexport function defineCodec<TValue, TInput, TError = never>(codec: Codec<TValue, TInput, TError>) {\n return codec;\n}\n","import type { Draft, Immutable } from \"immer\";\n\nexport type Lens<TState extends object, TValue> = {\n get: (state: Immutable<TState>) => TValue;\n set: (state: Draft<TState>, value: TValue) => void;\n};\n\nexport type LensBuilder<TState extends object, TValue> = Lens<TState, TValue> &\n (TValue extends object\n ? {\n prop<TKey extends keyof TValue>(key: TKey): LensBuilder<TState, TValue[TKey]>;\n }\n : object);\n\nexport type StateLens<TState extends object> = {\n prop<TKey extends keyof TState>(key: TKey): LensBuilder<TState, TState[TKey]>;\n};\n\nexport function defineLens<TState extends object, TValue>(lens: Lens<TState, TValue>) {\n return lens;\n}\n\nfunction readPath(value: unknown, path: readonly PropertyKey[]) {\n return path.reduce<unknown>((current, key) => (current as Record<PropertyKey, unknown>)[key], value);\n}\n\nfunction writePath(state: unknown, path: readonly PropertyKey[], value: unknown) {\n const parent = path\n .slice(0, -1)\n .reduce<\n Record<PropertyKey, unknown>\n >((current, key) => current[key] as Record<PropertyKey, unknown>, state as Record<PropertyKey, unknown>);\n parent[path.at(-1) as PropertyKey] = value;\n}\n\nfunction createPathLens<TState extends object, TValue>(path: readonly PropertyKey[]): LensBuilder<TState, TValue> {\n return {\n get: (state: Immutable<TState>) => readPath(state, path) as TValue,\n set: (state: Draft<TState>, value: TValue) => writePath(state, path, value),\n prop: (key: PropertyKey) => createPathLens([...path, key]),\n } as unknown as LensBuilder<TState, TValue>;\n}\n\nexport function stateLens<TState extends object>(): StateLens<TState> {\n return {\n prop: (key) => createPathLens<TState, TState[typeof key]>([key]),\n };\n}\n","import { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { Immutable } from \"immer\";\nimport type { Result } from \"gw-result\";\nimport type { Store } from \"gw-store\";\nimport type { InputBinding } from \"./binding\";\n\nexport type StoreBindingMeta<TError> = { valid: true; error?: never } | { valid: false; error: TError };\n\nexport type StoreBindingOptions<TValue, TInput> = {\n onStoreChange?: (input: TInput, value: TValue) => void;\n};\n\nexport type StoreBindingResult<TValue, TInput, TError> = {\n initialValue: TInput;\n meta: StoreBindingMeta<TError>;\n commit: (input: TInput) => Result<TValue, TError>;\n reset: (value: TValue) => void;\n};\n\nfunction equalStoreValue(previous: unknown, next: unknown) {\n if (Object.is(previous, next)) {\n return true;\n }\n\n if (previous instanceof Date && next instanceof Date) {\n return Object.is(previous.getTime(), next.getTime());\n }\n\n if (Array.isArray(previous) && Array.isArray(next)) {\n return previous.length === next.length && previous.every((value, index) => Object.is(value, next[index]));\n }\n\n return false;\n}\n\nexport function useStoreBinding<TState extends object, TValue, TInput, TError>(\n store: Store<TState>,\n binding: InputBinding<TState, TValue, TInput, TError>,\n options: StoreBindingOptions<TValue, TInput> = {},\n): StoreBindingResult<TValue, TInput, TError> {\n const dispatchKey = useId();\n const [initialValue] = useState(() => binding.codec.format(binding.lens.get(store.state)));\n const [meta, setMeta] = useState<StoreBindingMeta<TError>>({ valid: true });\n const metaRef = useRef<StoreBindingMeta<TError>>(meta);\n const optionsRef = useRef(options);\n const subscribedStoreRef = useRef<Store<TState> | null>(null);\n const subscribedBindingRef = useRef(binding);\n\n useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n useEffect(() => {\n const isReplacement =\n subscribedStoreRef.current !== null &&\n (subscribedStoreRef.current !== store || subscribedBindingRef.current !== binding);\n\n subscribedStoreRef.current = store;\n subscribedBindingRef.current = binding;\n\n const sync = (state: Immutable<TState>) => {\n if (!metaRef.current.valid) {\n return;\n }\n\n const value = binding.lens.get(state);\n optionsRef.current.onStoreChange?.(binding.codec.format(value), value);\n };\n\n const unsubscribe = store.subscribe((state, key) => {\n if (key !== dispatchKey) {\n sync(state);\n }\n });\n\n if (isReplacement) {\n sync(store.state);\n }\n\n return unsubscribe;\n }, [binding, dispatchKey, store]);\n\n const setValue = useCallback(\n (value: TValue) => {\n const currentValue = binding.lens.get(store.state);\n const equals = binding.codec.equals ?? equalStoreValue;\n\n if (equals(currentValue, value)) {\n return;\n }\n\n store.dispatch((state) => binding.lens.set(state, value), { key: dispatchKey });\n },\n [binding, dispatchKey, store],\n );\n\n const commit = useCallback(\n (input: TInput) => {\n const result = binding.codec.parse(input);\n\n if (result.isErr) {\n const invalidMeta: StoreBindingMeta<TError> = {\n valid: false,\n error: result.error,\n };\n metaRef.current = invalidMeta;\n setMeta(invalidMeta);\n return result;\n }\n\n const validMeta: StoreBindingMeta<TError> = { valid: true };\n metaRef.current = validMeta;\n setMeta((current) => (current.valid ? current : validMeta));\n setValue(result.value);\n return result;\n },\n [binding, setValue],\n );\n\n const reset = useCallback(\n (value: TValue) => {\n const validMeta: StoreBindingMeta<TError> = { valid: true };\n metaRef.current = validMeta;\n setMeta((current) => (current.valid ? current : validMeta));\n setValue(value);\n },\n [setValue],\n );\n\n return {\n initialValue,\n meta,\n commit,\n reset,\n };\n}\n"],"mappings":"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","names":["import_react","GwTextEditor"]}
|
|
1
|
+
{"version":3,"sources":["../src/text_editor.tsx","../src/editor/text_editor.tsx","../src/editor/use_text_editor_binding.tsx","../src/binding/binding.ts","../src/binding/codec.ts","../src/binding/lens.ts","../src/binding/use_store_binding.tsx"],"sourcesContent":["export * from \"./editor/text_editor\";\n","import {\n RichTextEditor as GwRichTextEditor,\n TextEditor as GwTextEditor,\n type RichTextEditorProps as GwRichTextEditorProps,\n type TextEditorProps as GwTextEditorProps,\n} from \"gw-rich-text-editor\";\nimport type { Store } from \"gw-store\";\nimport { useTextEditorBinding } from \"./use_text_editor_binding\";\n\nexport type TextEditorProps<TState extends object> = Omit<\n GwTextEditorProps,\n \"name\"\n> & {\n store: Store<TState>;\n name: keyof TState;\n};\n\nexport type RichTextEditorProps<TState extends object> = Omit<\n GwRichTextEditorProps,\n \"name\"\n> & {\n store: Store<TState>;\n name: keyof TState;\n};\n\nexport function TextEditor<TState extends object>({\n store,\n name,\n defaultValue,\n ref,\n ...props\n}: TextEditorProps<TState>) {\n const { controllerRef, initialValue, commit } = useTextEditorBinding({\n store,\n name,\n ref,\n });\n\n return (\n <GwTextEditor\n {...props}\n ref={controllerRef}\n name={String(name)}\n defaultValue={defaultValue ?? initialValue}\n onChange={(value) => {\n commit(value);\n props.onChange?.(value);\n }}\n />\n );\n}\n\nexport function RichTextEditor<TState extends object>({\n store,\n name,\n defaultValue,\n ref,\n ...props\n}: RichTextEditorProps<TState>) {\n const { controllerRef, initialValue, commit } = useTextEditorBinding({\n store,\n name,\n ref,\n });\n\n return (\n <GwRichTextEditor\n {...props}\n ref={controllerRef}\n name={String(name)}\n defaultValue={defaultValue ?? initialValue}\n onChange={(value) => {\n commit(value);\n props.onChange?.(value);\n }}\n />\n );\n}\n","import type { Ref } from \"react\";\nimport { useImperativeHandle, useMemo, useRef } from \"react\";\nimport type { TextEditorController } from \"gw-rich-text-editor\";\nimport { ok } from \"gw-result\";\nimport type { Store } from \"gw-store\";\nimport { defineBinding } from \"../binding/binding\";\nimport { defineCodec } from \"../binding/codec\";\nimport { stateLens } from \"../binding/lens\";\nimport { useStoreBinding } from \"../binding/use_store_binding\";\n\nexport function useTextEditorBinding<TState extends object>({\n store,\n name,\n ref,\n}: {\n store: Store<TState>;\n name: keyof TState;\n ref?: Ref<TextEditorController>;\n}) {\n const controllerRef = useRef<TextEditorController>(null);\n\n useImperativeHandle(\n ref,\n () => controllerRef.current as TextEditorController,\n [],\n );\n\n const binding = useMemo(() => {\n type TValue = TState[typeof name];\n\n return defineBinding({\n lens: stateLens<TState>().prop(name),\n codec: defineCodec<TValue, string>({\n format: (value) => (typeof value === \"string\" ? value : \"\"),\n parse: (value) => ok(value as TValue),\n }),\n });\n }, [name]);\n\n const storeBinding = useStoreBinding(store, binding, {\n onStoreChange: (value) => {\n const controller = controllerRef.current;\n\n if (controller && controller.value !== value) {\n controller.value = value;\n }\n },\n });\n\n return { controllerRef, ...storeBinding };\n}\n","import type { Codec } from \"./codec\";\nimport type { Lens } from \"./lens\";\n\nexport type InputBinding<TState extends object, TValue, TInput, TError = never> = {\n lens: Lens<TState, TValue>;\n codec: Codec<TValue, TInput, TError>;\n};\n\nexport function defineBinding<TState extends object, TValue, TInput, TError = never>(\n binding: InputBinding<TState, TValue, TInput, TError>,\n) {\n return binding;\n}\n","import type { Result } from \"gw-result\";\n\nexport type Codec<TValue, TInput, TError = never> = {\n format: (value: TValue) => TInput;\n parse: (input: TInput) => Result<TValue, TError>;\n equals?: (previous: TValue, next: TValue) => boolean;\n};\n\nexport function defineCodec<TValue, TInput, TError = never>(codec: Codec<TValue, TInput, TError>) {\n return codec;\n}\n","import type { Draft, Immutable } from \"immer\";\n\nexport type Lens<TState extends object, TValue> = {\n get: (state: Immutable<TState>) => TValue;\n set: (state: Draft<TState>, value: TValue) => void;\n};\n\nexport type LensBuilder<TState extends object, TValue> = Lens<TState, TValue> &\n (TValue extends object\n ? {\n prop<TKey extends keyof TValue>(key: TKey): LensBuilder<TState, TValue[TKey]>;\n }\n : object);\n\nexport type StateLens<TState extends object> = {\n prop<TKey extends keyof TState>(key: TKey): LensBuilder<TState, TState[TKey]>;\n};\n\nexport function defineLens<TState extends object, TValue>(lens: Lens<TState, TValue>) {\n return lens;\n}\n\nfunction readPath(value: unknown, path: readonly PropertyKey[]) {\n return path.reduce<unknown>((current, key) => (current as Record<PropertyKey, unknown>)[key], value);\n}\n\nfunction writePath(state: unknown, path: readonly PropertyKey[], value: unknown) {\n const parent = path\n .slice(0, -1)\n .reduce<\n Record<PropertyKey, unknown>\n >((current, key) => current[key] as Record<PropertyKey, unknown>, state as Record<PropertyKey, unknown>);\n parent[path.at(-1) as PropertyKey] = value;\n}\n\nfunction createPathLens<TState extends object, TValue>(path: readonly PropertyKey[]): LensBuilder<TState, TValue> {\n return {\n get: (state: Immutable<TState>) => readPath(state, path) as TValue,\n set: (state: Draft<TState>, value: TValue) => writePath(state, path, value),\n prop: (key: PropertyKey) => createPathLens([...path, key]),\n } as unknown as LensBuilder<TState, TValue>;\n}\n\nexport function stateLens<TState extends object>(): StateLens<TState> {\n return {\n prop: (key) => createPathLens<TState, TState[typeof key]>([key]),\n };\n}\n","import { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { Immutable } from \"immer\";\nimport type { Result } from \"gw-result\";\nimport type { Store } from \"gw-store\";\nimport type { InputBinding } from \"./binding\";\n\nexport type StoreBindingMeta<TError> = { valid: true; error?: never } | { valid: false; error: TError };\n\nexport type StoreBindingOptions<TValue, TInput> = {\n onStoreChange?: (input: TInput, value: TValue) => void;\n};\n\nexport type StoreBindingResult<TValue, TInput, TError> = {\n initialValue: TInput;\n meta: StoreBindingMeta<TError>;\n commit: (input: TInput) => Result<TValue, TError>;\n reset: (value: TValue) => void;\n};\n\nfunction equalStoreValue(previous: unknown, next: unknown) {\n if (Object.is(previous, next)) {\n return true;\n }\n\n if (previous instanceof Date && next instanceof Date) {\n return Object.is(previous.getTime(), next.getTime());\n }\n\n if (Array.isArray(previous) && Array.isArray(next)) {\n return previous.length === next.length && previous.every((value, index) => Object.is(value, next[index]));\n }\n\n return false;\n}\n\nexport function useStoreBinding<TState extends object, TValue, TInput, TError>(\n store: Store<TState>,\n binding: InputBinding<TState, TValue, TInput, TError>,\n options: StoreBindingOptions<TValue, TInput> = {},\n): StoreBindingResult<TValue, TInput, TError> {\n const dispatchKey = useId();\n const [initialValue] = useState(() => binding.codec.format(binding.lens.get(store.state)));\n const [meta, setMeta] = useState<StoreBindingMeta<TError>>({ valid: true });\n const metaRef = useRef<StoreBindingMeta<TError>>(meta);\n const optionsRef = useRef(options);\n const subscribedStoreRef = useRef<Store<TState> | null>(null);\n const subscribedBindingRef = useRef(binding);\n\n useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n useEffect(() => {\n const isReplacement =\n subscribedStoreRef.current !== null &&\n (subscribedStoreRef.current !== store || subscribedBindingRef.current !== binding);\n\n subscribedStoreRef.current = store;\n subscribedBindingRef.current = binding;\n\n const sync = (state: Immutable<TState>) => {\n if (!metaRef.current.valid) {\n return;\n }\n\n const value = binding.lens.get(state);\n optionsRef.current.onStoreChange?.(binding.codec.format(value), value);\n };\n\n const unsubscribe = store.subscribe((state, key) => {\n if (key !== dispatchKey) {\n sync(state);\n }\n });\n\n if (isReplacement) {\n sync(store.state);\n }\n\n return unsubscribe;\n }, [binding, dispatchKey, store]);\n\n const setValue = useCallback(\n (value: TValue) => {\n const currentValue = binding.lens.get(store.state);\n const equals = binding.codec.equals ?? equalStoreValue;\n\n if (equals(currentValue, value)) {\n return;\n }\n\n store.dispatch((state) => binding.lens.set(state, value), { key: dispatchKey });\n },\n [binding, dispatchKey, store],\n );\n\n const commit = useCallback(\n (input: TInput) => {\n const result = binding.codec.parse(input);\n\n if (result.isErr) {\n const invalidMeta: StoreBindingMeta<TError> = {\n valid: false,\n error: result.error,\n };\n metaRef.current = invalidMeta;\n setMeta(invalidMeta);\n return result;\n }\n\n const validMeta: StoreBindingMeta<TError> = { valid: true };\n metaRef.current = validMeta;\n setMeta((current) => (current.valid ? current : validMeta));\n setValue(result.value);\n return result;\n },\n [binding, setValue],\n );\n\n const reset = useCallback(\n (value: TValue) => {\n const validMeta: StoreBindingMeta<TError> = { valid: true };\n metaRef.current = validMeta;\n setMeta((current) => (current.valid ? current : validMeta));\n setValue(value);\n },\n [setValue],\n );\n\n return {\n initialValue,\n meta,\n commit,\n reset,\n };\n}\n"],"mappings":"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","names":["import_react","GwTextEditor","GwRichTextEditor"]}
|
package/dist/text-editor.mjs
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
// src/editor/text_editor.tsx
|
|
2
2
|
import {
|
|
3
|
+
RichTextEditor as GwRichTextEditor,
|
|
3
4
|
TextEditor as GwTextEditor
|
|
4
5
|
} from "gw-rich-text-editor";
|
|
5
|
-
|
|
6
|
+
|
|
7
|
+
// src/editor/use_text_editor_binding.tsx
|
|
6
8
|
import { useImperativeHandle, useMemo, useRef as useRef2 } from "react";
|
|
9
|
+
import { ok } from "gw-result";
|
|
7
10
|
|
|
8
11
|
// src/binding/binding.ts
|
|
9
12
|
function defineBinding(binding) {
|
|
@@ -130,17 +133,18 @@ function useStoreBinding(store, binding, options = {}) {
|
|
|
130
133
|
};
|
|
131
134
|
}
|
|
132
135
|
|
|
133
|
-
// src/editor/
|
|
134
|
-
|
|
135
|
-
function TextEditor({
|
|
136
|
+
// src/editor/use_text_editor_binding.tsx
|
|
137
|
+
function useTextEditorBinding({
|
|
136
138
|
store,
|
|
137
139
|
name,
|
|
138
|
-
|
|
139
|
-
ref,
|
|
140
|
-
...props
|
|
140
|
+
ref
|
|
141
141
|
}) {
|
|
142
142
|
const controllerRef = useRef2(null);
|
|
143
|
-
useImperativeHandle(
|
|
143
|
+
useImperativeHandle(
|
|
144
|
+
ref,
|
|
145
|
+
() => controllerRef.current,
|
|
146
|
+
[]
|
|
147
|
+
);
|
|
144
148
|
const binding = useMemo(() => {
|
|
145
149
|
return defineBinding({
|
|
146
150
|
lens: stateLens().prop(name),
|
|
@@ -150,7 +154,7 @@ function TextEditor({
|
|
|
150
154
|
})
|
|
151
155
|
});
|
|
152
156
|
}, [name]);
|
|
153
|
-
const
|
|
157
|
+
const storeBinding = useStoreBinding(store, binding, {
|
|
154
158
|
onStoreChange: (value) => {
|
|
155
159
|
const controller = controllerRef.current;
|
|
156
160
|
if (controller && controller.value !== value) {
|
|
@@ -158,6 +162,23 @@ function TextEditor({
|
|
|
158
162
|
}
|
|
159
163
|
}
|
|
160
164
|
});
|
|
165
|
+
return { controllerRef, ...storeBinding };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/editor/text_editor.tsx
|
|
169
|
+
import { jsx } from "react/jsx-runtime";
|
|
170
|
+
function TextEditor({
|
|
171
|
+
store,
|
|
172
|
+
name,
|
|
173
|
+
defaultValue,
|
|
174
|
+
ref,
|
|
175
|
+
...props
|
|
176
|
+
}) {
|
|
177
|
+
const { controllerRef, initialValue, commit } = useTextEditorBinding({
|
|
178
|
+
store,
|
|
179
|
+
name,
|
|
180
|
+
ref
|
|
181
|
+
});
|
|
161
182
|
return /* @__PURE__ */ jsx(
|
|
162
183
|
GwTextEditor,
|
|
163
184
|
{
|
|
@@ -172,7 +193,34 @@ function TextEditor({
|
|
|
172
193
|
}
|
|
173
194
|
);
|
|
174
195
|
}
|
|
196
|
+
function RichTextEditor({
|
|
197
|
+
store,
|
|
198
|
+
name,
|
|
199
|
+
defaultValue,
|
|
200
|
+
ref,
|
|
201
|
+
...props
|
|
202
|
+
}) {
|
|
203
|
+
const { controllerRef, initialValue, commit } = useTextEditorBinding({
|
|
204
|
+
store,
|
|
205
|
+
name,
|
|
206
|
+
ref
|
|
207
|
+
});
|
|
208
|
+
return /* @__PURE__ */ jsx(
|
|
209
|
+
GwRichTextEditor,
|
|
210
|
+
{
|
|
211
|
+
...props,
|
|
212
|
+
ref: controllerRef,
|
|
213
|
+
name: String(name),
|
|
214
|
+
defaultValue: defaultValue ?? initialValue,
|
|
215
|
+
onChange: (value) => {
|
|
216
|
+
commit(value);
|
|
217
|
+
props.onChange?.(value);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
);
|
|
221
|
+
}
|
|
175
222
|
export {
|
|
223
|
+
RichTextEditor,
|
|
176
224
|
TextEditor
|
|
177
225
|
};
|
|
178
226
|
//# sourceMappingURL=text-editor.mjs.map
|
package/dist/text-editor.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/editor/text_editor.tsx","../src/binding/binding.ts","../src/binding/codec.ts","../src/binding/lens.ts","../src/binding/use_store_binding.tsx"],"sourcesContent":["import {\n TextEditor as GwTextEditor,\n TextEditorController,\n type TextEditorProps as GwTextEditorProps,\n} from \"gw-rich-text-editor\";\nimport { ok } from \"gw-result\";\nimport { useImperativeHandle, useMemo, useRef } from \"react\";\nimport type { Store } from \"gw-store\";\nimport { defineBinding } from \"../binding/binding\";\nimport { defineCodec } from \"../binding/codec\";\nimport { stateLens } from \"../binding/lens\";\nimport { useStoreBinding } from \"../binding/use_store_binding\";\n\nexport type TextEditorProps<TState extends object> = Omit<GwTextEditorProps, \"name\"> & {\n store: Store<TState>;\n name: keyof TState;\n};\n\nexport function TextEditor<TState extends object>({\n store,\n name,\n defaultValue,\n ref,\n ...props\n}: TextEditorProps<TState>) {\n const controllerRef = useRef<TextEditorController>(null);\n\n useImperativeHandle(ref, () => controllerRef.current as TextEditorController, []);\n\n const binding = useMemo(() => {\n type TValue = TState[typeof name];\n return defineBinding({\n lens: stateLens<TState>().prop(name),\n codec: defineCodec<TValue, string>({\n format: (value) => (typeof value === \"string\" ? value : \"\"),\n parse: (value) => ok(value as TValue),\n }),\n });\n }, [name]);\n\n const { commit, initialValue } = useStoreBinding(store, binding, {\n onStoreChange: (value) => {\n const controller = controllerRef.current;\n\n if (controller && controller.value !== value) {\n controller.value = value;\n }\n },\n });\n\n return (\n <GwTextEditor\n {...props}\n ref={controllerRef}\n name={String(name)}\n defaultValue={defaultValue ?? initialValue}\n onChange={(value) => {\n commit(value);\n props.onChange?.(value);\n }}\n />\n );\n}\n","import type { Codec } from \"./codec\";\nimport type { Lens } from \"./lens\";\n\nexport type InputBinding<TState extends object, TValue, TInput, TError = never> = {\n lens: Lens<TState, TValue>;\n codec: Codec<TValue, TInput, TError>;\n};\n\nexport function defineBinding<TState extends object, TValue, TInput, TError = never>(\n binding: InputBinding<TState, TValue, TInput, TError>,\n) {\n return binding;\n}\n","import type { Result } from \"gw-result\";\n\nexport type Codec<TValue, TInput, TError = never> = {\n format: (value: TValue) => TInput;\n parse: (input: TInput) => Result<TValue, TError>;\n equals?: (previous: TValue, next: TValue) => boolean;\n};\n\nexport function defineCodec<TValue, TInput, TError = never>(codec: Codec<TValue, TInput, TError>) {\n return codec;\n}\n","import type { Draft, Immutable } from \"immer\";\n\nexport type Lens<TState extends object, TValue> = {\n get: (state: Immutable<TState>) => TValue;\n set: (state: Draft<TState>, value: TValue) => void;\n};\n\nexport type LensBuilder<TState extends object, TValue> = Lens<TState, TValue> &\n (TValue extends object\n ? {\n prop<TKey extends keyof TValue>(key: TKey): LensBuilder<TState, TValue[TKey]>;\n }\n : object);\n\nexport type StateLens<TState extends object> = {\n prop<TKey extends keyof TState>(key: TKey): LensBuilder<TState, TState[TKey]>;\n};\n\nexport function defineLens<TState extends object, TValue>(lens: Lens<TState, TValue>) {\n return lens;\n}\n\nfunction readPath(value: unknown, path: readonly PropertyKey[]) {\n return path.reduce<unknown>((current, key) => (current as Record<PropertyKey, unknown>)[key], value);\n}\n\nfunction writePath(state: unknown, path: readonly PropertyKey[], value: unknown) {\n const parent = path\n .slice(0, -1)\n .reduce<\n Record<PropertyKey, unknown>\n >((current, key) => current[key] as Record<PropertyKey, unknown>, state as Record<PropertyKey, unknown>);\n parent[path.at(-1) as PropertyKey] = value;\n}\n\nfunction createPathLens<TState extends object, TValue>(path: readonly PropertyKey[]): LensBuilder<TState, TValue> {\n return {\n get: (state: Immutable<TState>) => readPath(state, path) as TValue,\n set: (state: Draft<TState>, value: TValue) => writePath(state, path, value),\n prop: (key: PropertyKey) => createPathLens([...path, key]),\n } as unknown as LensBuilder<TState, TValue>;\n}\n\nexport function stateLens<TState extends object>(): StateLens<TState> {\n return {\n prop: (key) => createPathLens<TState, TState[typeof key]>([key]),\n };\n}\n","import { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { Immutable } from \"immer\";\nimport type { Result } from \"gw-result\";\nimport type { Store } from \"gw-store\";\nimport type { InputBinding } from \"./binding\";\n\nexport type StoreBindingMeta<TError> = { valid: true; error?: never } | { valid: false; error: TError };\n\nexport type StoreBindingOptions<TValue, TInput> = {\n onStoreChange?: (input: TInput, value: TValue) => void;\n};\n\nexport type StoreBindingResult<TValue, TInput, TError> = {\n initialValue: TInput;\n meta: StoreBindingMeta<TError>;\n commit: (input: TInput) => Result<TValue, TError>;\n reset: (value: TValue) => void;\n};\n\nfunction equalStoreValue(previous: unknown, next: unknown) {\n if (Object.is(previous, next)) {\n return true;\n }\n\n if (previous instanceof Date && next instanceof Date) {\n return Object.is(previous.getTime(), next.getTime());\n }\n\n if (Array.isArray(previous) && Array.isArray(next)) {\n return previous.length === next.length && previous.every((value, index) => Object.is(value, next[index]));\n }\n\n return false;\n}\n\nexport function useStoreBinding<TState extends object, TValue, TInput, TError>(\n store: Store<TState>,\n binding: InputBinding<TState, TValue, TInput, TError>,\n options: StoreBindingOptions<TValue, TInput> = {},\n): StoreBindingResult<TValue, TInput, TError> {\n const dispatchKey = useId();\n const [initialValue] = useState(() => binding.codec.format(binding.lens.get(store.state)));\n const [meta, setMeta] = useState<StoreBindingMeta<TError>>({ valid: true });\n const metaRef = useRef<StoreBindingMeta<TError>>(meta);\n const optionsRef = useRef(options);\n const subscribedStoreRef = useRef<Store<TState> | null>(null);\n const subscribedBindingRef = useRef(binding);\n\n useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n useEffect(() => {\n const isReplacement =\n subscribedStoreRef.current !== null &&\n (subscribedStoreRef.current !== store || subscribedBindingRef.current !== binding);\n\n subscribedStoreRef.current = store;\n subscribedBindingRef.current = binding;\n\n const sync = (state: Immutable<TState>) => {\n if (!metaRef.current.valid) {\n return;\n }\n\n const value = binding.lens.get(state);\n optionsRef.current.onStoreChange?.(binding.codec.format(value), value);\n };\n\n const unsubscribe = store.subscribe((state, key) => {\n if (key !== dispatchKey) {\n sync(state);\n }\n });\n\n if (isReplacement) {\n sync(store.state);\n }\n\n return unsubscribe;\n }, [binding, dispatchKey, store]);\n\n const setValue = useCallback(\n (value: TValue) => {\n const currentValue = binding.lens.get(store.state);\n const equals = binding.codec.equals ?? equalStoreValue;\n\n if (equals(currentValue, value)) {\n return;\n }\n\n store.dispatch((state) => binding.lens.set(state, value), { key: dispatchKey });\n },\n [binding, dispatchKey, store],\n );\n\n const commit = useCallback(\n (input: TInput) => {\n const result = binding.codec.parse(input);\n\n if (result.isErr) {\n const invalidMeta: StoreBindingMeta<TError> = {\n valid: false,\n error: result.error,\n };\n metaRef.current = invalidMeta;\n setMeta(invalidMeta);\n return result;\n }\n\n const validMeta: StoreBindingMeta<TError> = { valid: true };\n metaRef.current = validMeta;\n setMeta((current) => (current.valid ? current : validMeta));\n setValue(result.value);\n return result;\n },\n [binding, setValue],\n );\n\n const reset = useCallback(\n (value: TValue) => {\n const validMeta: StoreBindingMeta<TError> = { valid: true };\n metaRef.current = validMeta;\n setMeta((current) => (current.valid ? current : validMeta));\n setValue(value);\n },\n [setValue],\n );\n\n return {\n initialValue,\n meta,\n commit,\n reset,\n };\n}\n"],"mappings":"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","names":["useRef","useRef"]}
|
|
1
|
+
{"version":3,"sources":["../src/editor/text_editor.tsx","../src/editor/use_text_editor_binding.tsx","../src/binding/binding.ts","../src/binding/codec.ts","../src/binding/lens.ts","../src/binding/use_store_binding.tsx"],"sourcesContent":["import {\n RichTextEditor as GwRichTextEditor,\n TextEditor as GwTextEditor,\n type RichTextEditorProps as GwRichTextEditorProps,\n type TextEditorProps as GwTextEditorProps,\n} from \"gw-rich-text-editor\";\nimport type { Store } from \"gw-store\";\nimport { useTextEditorBinding } from \"./use_text_editor_binding\";\n\nexport type TextEditorProps<TState extends object> = Omit<\n GwTextEditorProps,\n \"name\"\n> & {\n store: Store<TState>;\n name: keyof TState;\n};\n\nexport type RichTextEditorProps<TState extends object> = Omit<\n GwRichTextEditorProps,\n \"name\"\n> & {\n store: Store<TState>;\n name: keyof TState;\n};\n\nexport function TextEditor<TState extends object>({\n store,\n name,\n defaultValue,\n ref,\n ...props\n}: TextEditorProps<TState>) {\n const { controllerRef, initialValue, commit } = useTextEditorBinding({\n store,\n name,\n ref,\n });\n\n return (\n <GwTextEditor\n {...props}\n ref={controllerRef}\n name={String(name)}\n defaultValue={defaultValue ?? initialValue}\n onChange={(value) => {\n commit(value);\n props.onChange?.(value);\n }}\n />\n );\n}\n\nexport function RichTextEditor<TState extends object>({\n store,\n name,\n defaultValue,\n ref,\n ...props\n}: RichTextEditorProps<TState>) {\n const { controllerRef, initialValue, commit } = useTextEditorBinding({\n store,\n name,\n ref,\n });\n\n return (\n <GwRichTextEditor\n {...props}\n ref={controllerRef}\n name={String(name)}\n defaultValue={defaultValue ?? initialValue}\n onChange={(value) => {\n commit(value);\n props.onChange?.(value);\n }}\n />\n );\n}\n","import type { Ref } from \"react\";\nimport { useImperativeHandle, useMemo, useRef } from \"react\";\nimport type { TextEditorController } from \"gw-rich-text-editor\";\nimport { ok } from \"gw-result\";\nimport type { Store } from \"gw-store\";\nimport { defineBinding } from \"../binding/binding\";\nimport { defineCodec } from \"../binding/codec\";\nimport { stateLens } from \"../binding/lens\";\nimport { useStoreBinding } from \"../binding/use_store_binding\";\n\nexport function useTextEditorBinding<TState extends object>({\n store,\n name,\n ref,\n}: {\n store: Store<TState>;\n name: keyof TState;\n ref?: Ref<TextEditorController>;\n}) {\n const controllerRef = useRef<TextEditorController>(null);\n\n useImperativeHandle(\n ref,\n () => controllerRef.current as TextEditorController,\n [],\n );\n\n const binding = useMemo(() => {\n type TValue = TState[typeof name];\n\n return defineBinding({\n lens: stateLens<TState>().prop(name),\n codec: defineCodec<TValue, string>({\n format: (value) => (typeof value === \"string\" ? value : \"\"),\n parse: (value) => ok(value as TValue),\n }),\n });\n }, [name]);\n\n const storeBinding = useStoreBinding(store, binding, {\n onStoreChange: (value) => {\n const controller = controllerRef.current;\n\n if (controller && controller.value !== value) {\n controller.value = value;\n }\n },\n });\n\n return { controllerRef, ...storeBinding };\n}\n","import type { Codec } from \"./codec\";\nimport type { Lens } from \"./lens\";\n\nexport type InputBinding<TState extends object, TValue, TInput, TError = never> = {\n lens: Lens<TState, TValue>;\n codec: Codec<TValue, TInput, TError>;\n};\n\nexport function defineBinding<TState extends object, TValue, TInput, TError = never>(\n binding: InputBinding<TState, TValue, TInput, TError>,\n) {\n return binding;\n}\n","import type { Result } from \"gw-result\";\n\nexport type Codec<TValue, TInput, TError = never> = {\n format: (value: TValue) => TInput;\n parse: (input: TInput) => Result<TValue, TError>;\n equals?: (previous: TValue, next: TValue) => boolean;\n};\n\nexport function defineCodec<TValue, TInput, TError = never>(codec: Codec<TValue, TInput, TError>) {\n return codec;\n}\n","import type { Draft, Immutable } from \"immer\";\n\nexport type Lens<TState extends object, TValue> = {\n get: (state: Immutable<TState>) => TValue;\n set: (state: Draft<TState>, value: TValue) => void;\n};\n\nexport type LensBuilder<TState extends object, TValue> = Lens<TState, TValue> &\n (TValue extends object\n ? {\n prop<TKey extends keyof TValue>(key: TKey): LensBuilder<TState, TValue[TKey]>;\n }\n : object);\n\nexport type StateLens<TState extends object> = {\n prop<TKey extends keyof TState>(key: TKey): LensBuilder<TState, TState[TKey]>;\n};\n\nexport function defineLens<TState extends object, TValue>(lens: Lens<TState, TValue>) {\n return lens;\n}\n\nfunction readPath(value: unknown, path: readonly PropertyKey[]) {\n return path.reduce<unknown>((current, key) => (current as Record<PropertyKey, unknown>)[key], value);\n}\n\nfunction writePath(state: unknown, path: readonly PropertyKey[], value: unknown) {\n const parent = path\n .slice(0, -1)\n .reduce<\n Record<PropertyKey, unknown>\n >((current, key) => current[key] as Record<PropertyKey, unknown>, state as Record<PropertyKey, unknown>);\n parent[path.at(-1) as PropertyKey] = value;\n}\n\nfunction createPathLens<TState extends object, TValue>(path: readonly PropertyKey[]): LensBuilder<TState, TValue> {\n return {\n get: (state: Immutable<TState>) => readPath(state, path) as TValue,\n set: (state: Draft<TState>, value: TValue) => writePath(state, path, value),\n prop: (key: PropertyKey) => createPathLens([...path, key]),\n } as unknown as LensBuilder<TState, TValue>;\n}\n\nexport function stateLens<TState extends object>(): StateLens<TState> {\n return {\n prop: (key) => createPathLens<TState, TState[typeof key]>([key]),\n };\n}\n","import { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { Immutable } from \"immer\";\nimport type { Result } from \"gw-result\";\nimport type { Store } from \"gw-store\";\nimport type { InputBinding } from \"./binding\";\n\nexport type StoreBindingMeta<TError> = { valid: true; error?: never } | { valid: false; error: TError };\n\nexport type StoreBindingOptions<TValue, TInput> = {\n onStoreChange?: (input: TInput, value: TValue) => void;\n};\n\nexport type StoreBindingResult<TValue, TInput, TError> = {\n initialValue: TInput;\n meta: StoreBindingMeta<TError>;\n commit: (input: TInput) => Result<TValue, TError>;\n reset: (value: TValue) => void;\n};\n\nfunction equalStoreValue(previous: unknown, next: unknown) {\n if (Object.is(previous, next)) {\n return true;\n }\n\n if (previous instanceof Date && next instanceof Date) {\n return Object.is(previous.getTime(), next.getTime());\n }\n\n if (Array.isArray(previous) && Array.isArray(next)) {\n return previous.length === next.length && previous.every((value, index) => Object.is(value, next[index]));\n }\n\n return false;\n}\n\nexport function useStoreBinding<TState extends object, TValue, TInput, TError>(\n store: Store<TState>,\n binding: InputBinding<TState, TValue, TInput, TError>,\n options: StoreBindingOptions<TValue, TInput> = {},\n): StoreBindingResult<TValue, TInput, TError> {\n const dispatchKey = useId();\n const [initialValue] = useState(() => binding.codec.format(binding.lens.get(store.state)));\n const [meta, setMeta] = useState<StoreBindingMeta<TError>>({ valid: true });\n const metaRef = useRef<StoreBindingMeta<TError>>(meta);\n const optionsRef = useRef(options);\n const subscribedStoreRef = useRef<Store<TState> | null>(null);\n const subscribedBindingRef = useRef(binding);\n\n useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n useEffect(() => {\n const isReplacement =\n subscribedStoreRef.current !== null &&\n (subscribedStoreRef.current !== store || subscribedBindingRef.current !== binding);\n\n subscribedStoreRef.current = store;\n subscribedBindingRef.current = binding;\n\n const sync = (state: Immutable<TState>) => {\n if (!metaRef.current.valid) {\n return;\n }\n\n const value = binding.lens.get(state);\n optionsRef.current.onStoreChange?.(binding.codec.format(value), value);\n };\n\n const unsubscribe = store.subscribe((state, key) => {\n if (key !== dispatchKey) {\n sync(state);\n }\n });\n\n if (isReplacement) {\n sync(store.state);\n }\n\n return unsubscribe;\n }, [binding, dispatchKey, store]);\n\n const setValue = useCallback(\n (value: TValue) => {\n const currentValue = binding.lens.get(store.state);\n const equals = binding.codec.equals ?? equalStoreValue;\n\n if (equals(currentValue, value)) {\n return;\n }\n\n store.dispatch((state) => binding.lens.set(state, value), { key: dispatchKey });\n },\n [binding, dispatchKey, store],\n );\n\n const commit = useCallback(\n (input: TInput) => {\n const result = binding.codec.parse(input);\n\n if (result.isErr) {\n const invalidMeta: StoreBindingMeta<TError> = {\n valid: false,\n error: result.error,\n };\n metaRef.current = invalidMeta;\n setMeta(invalidMeta);\n return result;\n }\n\n const validMeta: StoreBindingMeta<TError> = { valid: true };\n metaRef.current = validMeta;\n setMeta((current) => (current.valid ? current : validMeta));\n setValue(result.value);\n return result;\n },\n [binding, setValue],\n );\n\n const reset = useCallback(\n (value: TValue) => {\n const validMeta: StoreBindingMeta<TError> = { valid: true };\n metaRef.current = validMeta;\n setMeta((current) => (current.valid ? current : validMeta));\n setValue(value);\n },\n [setValue],\n );\n\n return {\n initialValue,\n meta,\n commit,\n reset,\n };\n}\n"],"mappings":";AAAA;AAAA,EACE,kBAAkB;AAAA,EAClB,cAAc;AAAA,OAGT;;;ACJP,SAAS,qBAAqB,SAAS,UAAAA,eAAc;AAErD,SAAS,UAAU;;;ACKZ,SAAS,cACd,SACA;AACA,SAAO;AACT;;;ACJO,SAAS,YAA4C,OAAsC;AAChG,SAAO;AACT;;;ACYA,SAAS,SAAS,OAAgB,MAA8B;AAC9D,SAAO,KAAK,OAAgB,CAAC,SAAS,QAAS,QAAyC,GAAG,GAAG,KAAK;AACrG;AAEA,SAAS,UAAU,OAAgB,MAA8B,OAAgB;AAC/E,QAAM,SAAS,KACZ,MAAM,GAAG,EAAE,EACX,OAEC,CAAC,SAAS,QAAQ,QAAQ,GAAG,GAAmC,KAAqC;AACzG,SAAO,KAAK,GAAG,EAAE,CAAgB,IAAI;AACvC;AAEA,SAAS,eAA8C,MAA2D;AAChH,SAAO;AAAA,IACL,KAAK,CAAC,UAA6B,SAAS,OAAO,IAAI;AAAA,IACvD,KAAK,CAAC,OAAsB,UAAkB,UAAU,OAAO,MAAM,KAAK;AAAA,IAC1E,MAAM,CAAC,QAAqB,eAAe,CAAC,GAAG,MAAM,GAAG,CAAC;AAAA,EAC3D;AACF;AAEO,SAAS,YAAsD;AACpE,SAAO;AAAA,IACL,MAAM,CAAC,QAAQ,eAA2C,CAAC,GAAG,CAAC;AAAA,EACjE;AACF;;;AC/CA,SAAS,aAAa,WAAW,OAAO,QAAQ,gBAAgB;AAmBhE,SAAS,gBAAgB,UAAmB,MAAe;AACzD,MAAI,OAAO,GAAG,UAAU,IAAI,GAAG;AAC7B,WAAO;AAAA,EACT;AAEA,MAAI,oBAAoB,QAAQ,gBAAgB,MAAM;AACpD,WAAO,OAAO,GAAG,SAAS,QAAQ,GAAG,KAAK,QAAQ,CAAC;AAAA,EACrD;AAEA,MAAI,MAAM,QAAQ,QAAQ,KAAK,MAAM,QAAQ,IAAI,GAAG;AAClD,WAAO,SAAS,WAAW,KAAK,UAAU,SAAS,MAAM,CAAC,OAAO,UAAU,OAAO,GAAG,OAAO,KAAK,KAAK,CAAC,CAAC;AAAA,EAC1G;AAEA,SAAO;AACT;AAEO,SAAS,gBACd,OACA,SACA,UAA+C,CAAC,GACJ;AAC5C,QAAM,cAAc,MAAM;AAC1B,QAAM,CAAC,YAAY,IAAI,SAAS,MAAM,QAAQ,MAAM,OAAO,QAAQ,KAAK,IAAI,MAAM,KAAK,CAAC,CAAC;AACzF,QAAM,CAAC,MAAM,OAAO,IAAI,SAAmC,EAAE,OAAO,KAAK,CAAC;AAC1E,QAAM,UAAU,OAAiC,IAAI;AACrD,QAAM,aAAa,OAAO,OAAO;AACjC,QAAM,qBAAqB,OAA6B,IAAI;AAC5D,QAAM,uBAAuB,OAAO,OAAO;AAE3C,YAAU,MAAM;AACd,eAAW,UAAU;AAAA,EACvB,GAAG,CAAC,OAAO,CAAC;AAEZ,YAAU,MAAM;AACd,UAAM,gBACJ,mBAAmB,YAAY,SAC9B,mBAAmB,YAAY,SAAS,qBAAqB,YAAY;AAE5E,uBAAmB,UAAU;AAC7B,yBAAqB,UAAU;AAE/B,UAAM,OAAO,CAAC,UAA6B;AACzC,UAAI,CAAC,QAAQ,QAAQ,OAAO;AAC1B;AAAA,MACF;AAEA,YAAM,QAAQ,QAAQ,KAAK,IAAI,KAAK;AACpC,iBAAW,QAAQ,gBAAgB,QAAQ,MAAM,OAAO,KAAK,GAAG,KAAK;AAAA,IACvE;AAEA,UAAM,cAAc,MAAM,UAAU,CAAC,OAAO,QAAQ;AAClD,UAAI,QAAQ,aAAa;AACvB,aAAK,KAAK;AAAA,MACZ;AAAA,IACF,CAAC;AAED,QAAI,eAAe;AACjB,WAAK,MAAM,KAAK;AAAA,IAClB;AAEA,WAAO;AAAA,EACT,GAAG,CAAC,SAAS,aAAa,KAAK,CAAC;AAEhC,QAAM,WAAW;AAAA,IACf,CAAC,UAAkB;AACjB,YAAM,eAAe,QAAQ,KAAK,IAAI,MAAM,KAAK;AACjD,YAAM,SAAS,QAAQ,MAAM,UAAU;AAEvC,UAAI,OAAO,cAAc,KAAK,GAAG;AAC/B;AAAA,MACF;AAEA,YAAM,SAAS,CAAC,UAAU,QAAQ,KAAK,IAAI,OAAO,KAAK,GAAG,EAAE,KAAK,YAAY,CAAC;AAAA,IAChF;AAAA,IACA,CAAC,SAAS,aAAa,KAAK;AAAA,EAC9B;AAEA,QAAM,SAAS;AAAA,IACb,CAAC,UAAkB;AACjB,YAAM,SAAS,QAAQ,MAAM,MAAM,KAAK;AAExC,UAAI,OAAO,OAAO;AAChB,cAAM,cAAwC;AAAA,UAC5C,OAAO;AAAA,UACP,OAAO,OAAO;AAAA,QAChB;AACA,gBAAQ,UAAU;AAClB,gBAAQ,WAAW;AACnB,eAAO;AAAA,MACT;AAEA,YAAM,YAAsC,EAAE,OAAO,KAAK;AAC1D,cAAQ,UAAU;AAClB,cAAQ,CAAC,YAAa,QAAQ,QAAQ,UAAU,SAAU;AAC1D,eAAS,OAAO,KAAK;AACrB,aAAO;AAAA,IACT;AAAA,IACA,CAAC,SAAS,QAAQ;AAAA,EACpB;AAEA,QAAM,QAAQ;AAAA,IACZ,CAAC,UAAkB;AACjB,YAAM,YAAsC,EAAE,OAAO,KAAK;AAC1D,cAAQ,UAAU;AAClB,cAAQ,CAAC,YAAa,QAAQ,QAAQ,UAAU,SAAU;AAC1D,eAAS,KAAK;AAAA,IAChB;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AJ7HO,SAAS,qBAA4C;AAAA,EAC1D;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,gBAAgBC,QAA6B,IAAI;AAEvD;AAAA,IACE;AAAA,IACA,MAAM,cAAc;AAAA,IACpB,CAAC;AAAA,EACH;AAEA,QAAM,UAAU,QAAQ,MAAM;AAG5B,WAAO,cAAc;AAAA,MACnB,MAAM,UAAkB,EAAE,KAAK,IAAI;AAAA,MACnC,OAAO,YAA4B;AAAA,QACjC,QAAQ,CAAC,UAAW,OAAO,UAAU,WAAW,QAAQ;AAAA,QACxD,OAAO,CAAC,UAAU,GAAG,KAAe;AAAA,MACtC,CAAC;AAAA,IACH,CAAC;AAAA,EACH,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,eAAe,gBAAgB,OAAO,SAAS;AAAA,IACnD,eAAe,CAAC,UAAU;AACxB,YAAM,aAAa,cAAc;AAEjC,UAAI,cAAc,WAAW,UAAU,OAAO;AAC5C,mBAAW,QAAQ;AAAA,MACrB;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO,EAAE,eAAe,GAAG,aAAa;AAC1C;;;ADXI;AAdG,SAAS,WAAkC;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAA4B;AAC1B,QAAM,EAAE,eAAe,cAAc,OAAO,IAAI,qBAAqB;AAAA,IACnE;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,SACE;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ,KAAK;AAAA,MACL,MAAM,OAAO,IAAI;AAAA,MACjB,cAAc,gBAAgB;AAAA,MAC9B,UAAU,CAAC,UAAU;AACnB,eAAO,KAAK;AACZ,cAAM,WAAW,KAAK;AAAA,MACxB;AAAA;AAAA,EACF;AAEJ;AAEO,SAAS,eAAsC;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAgC;AAC9B,QAAM,EAAE,eAAe,cAAc,OAAO,IAAI,qBAAqB;AAAA,IACnE;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,SACE;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ,KAAK;AAAA,MACL,MAAM,OAAO,IAAI;AAAA,MACjB,cAAc,gBAAgB;AAAA,MAC9B,UAAU,CAAC,UAAU;AACnB,eAAO,KAAK;AACZ,cAAM,WAAW,KAAK;AAAA,MACxB;AAAA;AAAA,EACF;AAEJ;","names":["useRef","useRef"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-store-input",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Typed React form inputs backed by a small immutable store",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"@types/react-dom": "^18.3.1 || ^19.2.1",
|
|
74
74
|
"eslint": "^9.39.5",
|
|
75
75
|
"eslint-plugin-react-hooks": "^7.1.1",
|
|
76
|
-
"gw-rich-text-editor": "^0.
|
|
76
|
+
"gw-rich-text-editor": "^0.2.0",
|
|
77
77
|
"jsdom": "^24.1.3",
|
|
78
78
|
"react": "^19.2.8",
|
|
79
79
|
"react-dom": "^19.2.8",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"typescript-eslint": "^8.65.0"
|
|
83
83
|
},
|
|
84
84
|
"peerDependencies": {
|
|
85
|
-
"gw-rich-text-editor": "^0.
|
|
85
|
+
"gw-rich-text-editor": "^0.2.0",
|
|
86
86
|
"react": ">=18.0.0 <20.0.0",
|
|
87
87
|
"react-dom": ">=18.0.0 <20.0.0"
|
|
88
88
|
},
|