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 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
@@ -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 };
@@ -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 };
@@ -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
- var import_gw_result = require("gw-result");
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/text_editor.tsx
158
- var import_jsx_runtime = require("react/jsx-runtime");
159
- function TextEditor({
160
+ // src/editor/use_text_editor_binding.tsx
161
+ function useTextEditorBinding({
160
162
  store,
161
163
  name,
162
- defaultValue,
163
- ref,
164
- ...props
164
+ ref
165
165
  }) {
166
166
  const controllerRef = (0, import_react2.useRef)(null);
167
- (0, import_react2.useImperativeHandle)(ref, () => controllerRef.current, []);
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 { commit, initialValue } = useStoreBinding(store, binding, {
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
@@ -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"]}
@@ -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
- import { ok } from "gw-result";
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/text_editor.tsx
134
- import { jsx } from "react/jsx-runtime";
135
- function TextEditor({
136
+ // src/editor/use_text_editor_binding.tsx
137
+ function useTextEditorBinding({
136
138
  store,
137
139
  name,
138
- defaultValue,
139
- ref,
140
- ...props
140
+ ref
141
141
  }) {
142
142
  const controllerRef = useRef2(null);
143
- useImperativeHandle(ref, () => controllerRef.current, []);
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 { commit, initialValue } = useStoreBinding(store, binding, {
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
@@ -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":"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","names":["useRef","useRef"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-store-input",
3
- "version": "0.6.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.1.4",
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.1.4",
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
  },