react-store-input 0.2.6 → 0.4.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/README.md +179 -201
  3. package/dist/index.d.mts +127 -10
  4. package/dist/index.d.ts +127 -10
  5. package/dist/index.js +554 -249
  6. package/dist/index.js.map +1 -0
  7. package/dist/index.mjs +549 -247
  8. package/dist/index.mjs.map +1 -0
  9. package/dist/text-editor.d.mts +11 -0
  10. package/dist/text-editor.d.ts +11 -0
  11. package/dist/{text_editor.js → text-editor.js} +31 -51
  12. package/dist/text-editor.js.map +1 -0
  13. package/dist/text-editor.mjs +90 -0
  14. package/dist/text-editor.mjs.map +1 -0
  15. package/package.json +78 -18
  16. package/dist/create_render.d.mts +0 -9
  17. package/dist/create_render.d.ts +0 -9
  18. package/dist/create_render.js +0 -51
  19. package/dist/create_render.mjs +0 -25
  20. package/dist/text_editor.d.mts +0 -13
  21. package/dist/text_editor.d.ts +0 -13
  22. package/dist/text_editor.mjs +0 -112
  23. package/dist/use_form_store.d.mts +0 -16
  24. package/dist/use_form_store.d.ts +0 -16
  25. package/dist/use_form_store.js +0 -365
  26. package/dist/use_form_store.mjs +0 -340
  27. package/dist/use_store_component.d.mts +0 -23
  28. package/dist/use_store_component.d.ts +0 -23
  29. package/dist/use_store_component.js +0 -337
  30. package/dist/use_store_component.mjs +0 -311
  31. package/dist/use_store_controller.d.mts +0 -11
  32. package/dist/use_store_controller.d.ts +0 -11
  33. package/dist/use_store_controller.js +0 -57
  34. package/dist/use_store_controller.mjs +0 -32
  35. package/dist/use_store_input.d.mts +0 -22
  36. package/dist/use_store_input.d.ts +0 -22
  37. package/dist/use_store_input.js +0 -151
  38. package/dist/use_store_input.mjs +0 -126
  39. package/dist/use_store_input_with_name.d.mts +0 -17
  40. package/dist/use_store_input_with_name.d.ts +0 -17
  41. package/dist/use_store_input_with_name.js +0 -177
  42. package/dist/use_store_input_with_name.mjs +0 -150
  43. package/tsup.config.ts +0 -9
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/store/use_store_controller.tsx","../src/store/create_render.tsx","../src/binding/lens.ts","../src/binding/codec.ts","../src/binding/binding.ts","../src/binding/laws.ts","../src/input/use_store_input.tsx","../src/input/dom_value.ts","../src/input/reset_coordinator.ts","../src/input/value_conversion.ts","../src/input/use_store_input_with_name.tsx","../src/form/store_components.tsx","../src/form/use_form_store.tsx","../src/index.tsx"],"sourcesContent":["import { useCallback, useEffect, useId, useRef } from \"react\";\nimport type { Draft, Immutable } from \"immer\";\nimport type { Store } from \"gw-store\";\n\nexport type StoreControllerProps<TState extends object> = {\n onSubscribe: (state: Immutable<TState>) => void;\n onDispatch: (state: Draft<TState>) => void;\n};\n\nexport function useStoreController<TState extends object>(\n store: Store<TState>,\n props: StoreControllerProps<TState>,\n) {\n const dispatchKey = useId();\n const propsRef = useRef(props);\n const subscribedStoreRef = useRef<Store<TState> | null>(null);\n\n useEffect(() => {\n propsRef.current = props;\n }, [props]);\n\n useEffect(() => {\n const isReplacement =\n subscribedStoreRef.current !== null && subscribedStoreRef.current !== store;\n subscribedStoreRef.current = store;\n\n const unsubscribe = store.subscribe((state, key) => {\n if (key !== dispatchKey) {\n propsRef.current.onSubscribe(state);\n }\n });\n\n if (isReplacement) {\n propsRef.current.onSubscribe(store.state);\n }\n\n return unsubscribe;\n }, [dispatchKey, store]);\n\n const dispatch = useCallback(() => {\n store.dispatch(\n (state) => propsRef.current.onDispatch(state),\n { key: dispatchKey },\n );\n }, [dispatchKey, store]);\n\n return { dispatch };\n}\n","import type { Immutable } from \"immer\";\nimport { useSelector, type EqualityFn, type Store } from \"gw-store\";\nimport type { ReactNode } from \"react\";\n\nexport type CreateRender<TState extends object> = (\n selector: (state: Immutable<TState>) => ReactNode,\n compare?: EqualityFn<Immutable<TState>>,\n) => ReactNode;\n\nexport function createRender<TState extends object>(\n store: Store<TState>,\n selector: (state: Immutable<TState>) => ReactNode,\n compare?: EqualityFn<Immutable<TState>>,\n) {\n function Component() {\n const result = useSelector(store, (state) => state, compare);\n return <>{selector(result)}</>;\n }\n\n return <Component />;\n}\n\nexport function createRenderWithStore<TState extends object>(\n store: Store<TState>,\n): CreateRender<TState> {\n return function createRender(selector, compare) {\n function Component() {\n const result = useSelector(store, (state) => state, compare);\n return <>{selector(result)}</>;\n }\n\n return <Component />;\n };\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<\n TState,\n TValue\n> &\n (TValue extends object\n ? {\n prop<TKey extends keyof TValue>(\n key: TKey,\n ): LensBuilder<TState, TValue[TKey]>;\n }\n : object);\n\nexport type StateLens<TState extends object> = {\n prop<TKey extends keyof TState>(\n key: TKey,\n ): LensBuilder<TState, TState[TKey]>;\n};\n\nexport function defineLens<TState extends object, TValue>(\n lens: Lens<TState, TValue>,\n) {\n return lens;\n}\n\nfunction readPath(value: unknown, path: readonly PropertyKey[]) {\n return path.reduce<unknown>(\n (current, key) => (current as Record<PropertyKey, unknown>)[key],\n value,\n );\n}\n\nfunction writePath(\n state: unknown,\n path: readonly PropertyKey[],\n value: unknown,\n) {\n const parent = path\n .slice(0, -1)\n .reduce<Record<PropertyKey, unknown>>(\n (current, key) => current[key] as Record<PropertyKey, unknown>,\n state as Record<PropertyKey, unknown>,\n );\n parent[path.at(-1) as PropertyKey] = value;\n}\n\nfunction createPathLens<TState extends object, TValue>(\n path: readonly PropertyKey[],\n): 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 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>(\n codec: Codec<TValue, TInput, TError>,\n) {\n return codec;\n}\n","import type { Codec } from \"./codec\";\nimport type { Lens } from \"./lens\";\n\nexport type InputBinding<\n TState extends object,\n TValue,\n TInput,\n TError = never,\n> = {\n lens: Lens<TState, TValue>;\n codec: Codec<TValue, TInput, TError>;\n};\n\nexport function defineBinding<\n TState extends object,\n TValue,\n TInput,\n TError = never,\n>(binding: InputBinding<TState, TValue, TInput, TError>) {\n return binding;\n}\n","import { produce, type Immutable } from \"immer\";\nimport type { Codec } from \"./codec\";\nimport type { Lens } from \"./lens\";\n\ntype Equality<TValue> = (previous: TValue, next: TValue) => boolean;\n\nfunction structuralEqual(previous: unknown, next: unknown): boolean {\n if (Object.is(previous, next)) {\n return true;\n }\n\n if (previous instanceof Date && next instanceof Date) {\n return previous.getTime() === next.getTime();\n }\n\n if (Array.isArray(previous) && Array.isArray(next)) {\n return (\n previous.length === next.length &&\n previous.every((value, index) => structuralEqual(value, next[index]))\n );\n }\n\n if (\n previous !== null &&\n next !== null &&\n typeof previous === \"object\" &&\n typeof next === \"object\"\n ) {\n const previousRecord = previous as Record<PropertyKey, unknown>;\n const nextRecord = next as Record<PropertyKey, unknown>;\n const previousKeys = Reflect.ownKeys(previousRecord);\n const nextKeys = Reflect.ownKeys(nextRecord);\n\n return (\n previousKeys.length === nextKeys.length &&\n previousKeys.every(\n (key) =>\n Object.prototype.hasOwnProperty.call(nextRecord, key) &&\n structuralEqual(previousRecord[key], nextRecord[key]),\n )\n );\n }\n\n return false;\n}\n\nexport function assertLensLaws<TState extends object, TValue>(\n lens: Lens<TState, TValue>,\n options: {\n state: TState;\n values: readonly TValue[];\n equalsState?: Equality<TState>;\n equalsValue?: Equality<TValue>;\n },\n) {\n const equalsState = options.equalsState ?? structuralEqual;\n const equalsValue = options.equalsValue ?? structuralEqual;\n const set = (state: TState, value: TValue) =>\n produce(state, (draft) => lens.set(draft, value)) as TState;\n\n if (\n !equalsState(\n set(options.state, lens.get(options.state as Immutable<TState>)),\n options.state,\n )\n ) {\n throw new Error(\"Lens law failed: setting the current value changed state.\");\n }\n\n for (const value of options.values) {\n const updated = set(options.state, value);\n\n if (!equalsValue(lens.get(updated as Immutable<TState>), value)) {\n throw new Error(\"Lens law failed: get(set(state, value)) !== value.\");\n }\n }\n\n for (const previous of options.values) {\n for (const next of options.values) {\n if (\n !equalsState(\n set(set(options.state, previous), next),\n set(options.state, next),\n )\n ) {\n throw new Error(\"Lens law failed: the last set did not win.\");\n }\n }\n }\n}\n\nexport function assertCodecLaws<TValue, TInput, TError>(\n codec: Codec<TValue, TInput, TError>,\n options: {\n values: readonly TValue[];\n inputs?: readonly TInput[];\n equals?: Equality<TValue>;\n equalsInput?: Equality<TInput>;\n },\n) {\n const equals = options.equals ?? codec.equals ?? structuralEqual;\n const equalsInput = options.equalsInput ?? structuralEqual;\n\n for (const value of options.values) {\n const result = codec.parse(codec.format(value));\n\n if (result.isErr) {\n throw new Error(\"Codec law failed: parse(format(value)) returned Err.\");\n }\n\n if (!equals(result.value, value)) {\n throw new Error(\"Codec law failed: parse(format(value)) !== value.\");\n }\n }\n\n for (const input of options.inputs ?? []) {\n const result = codec.parse(input);\n\n if (result.isOk && !equalsInput(codec.format(result.value), input)) {\n throw new Error(\n \"Codec law failed: format(parse(input).value) !== input.\",\n );\n }\n }\n}\n","import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type RefObject,\n} from \"react\";\nimport type { Draft } from \"immer\";\nimport type { Store } from \"gw-store\";\nimport type { InputBinding } from \"../binding/binding\";\nimport { useStoreController } from \"../store/use_store_controller\";\nimport {\n equalStateValue,\n readElementValue,\n writeElementValue,\n} from \"./dom_value\";\nimport { isFormResetting, registerResetBinding } from \"./reset_coordinator\";\nimport type {\n BindingInputResult,\n InputControlValue,\n InputDisplayValue,\n StoreInputDomProps,\n StoreInputBindingOptions,\n StoreInputMeta,\n StoreInputOptions,\n} from \"./types\";\nimport { resolveDefaultChecked, resolveDefaultValue } from \"./value_conversion\";\n\nexport type {\n BindingInputResult,\n InputControlValue,\n InputDisplayValue,\n InputStateValue,\n StoreInputBindingOptions,\n StoreInputDomProps,\n StoreInputMeta,\n StoreInputOptions,\n} from \"./types\";\n\ntype StoreElement =\n | HTMLInputElement\n | HTMLTextAreaElement\n | HTMLSelectElement;\n\nfunction isDisplayValue(value: InputControlValue): value is InputDisplayValue {\n return (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n (Array.isArray(value) && value.every((item) => typeof item === \"string\"))\n );\n}\n\nfunction readControlValue(\n element: StoreElement,\n type: StoreInputOptions<StoreElement>[\"type\"],\n radioValue: InputDisplayValue | undefined,\n): InputControlValue | undefined {\n if (\"files\" in element && type === \"file\") {\n return element.files?.length ? element.files : null;\n }\n\n if (\"checked\" in element && type === \"checkbox\") {\n return element.checked;\n }\n\n if (\"checked\" in element && type === \"radio\") {\n return element.checked ? (radioValue ?? element.value) : undefined;\n }\n\n return readElementValue(element);\n}\n\nexport function useStoreInput<\n TInputElement extends StoreElement,\n TState extends object,\n TValue,\n TInput extends InputControlValue,\n TError,\n>(\n ref: RefObject<TInputElement | null>,\n store: Store<TState>,\n binding: InputBinding<TState, TValue, TInput, TError>,\n options: StoreInputBindingOptions<TInputElement, TValue> = {},\n): BindingInputResult<TInputElement, TError> {\n const props: StoreInputOptions<TInputElement> = options;\n const getStateValue = binding.lens.get;\n const setStoreValue = binding.lens.set;\n const [meta, setMeta] = useState<StoreInputMeta<TError>>({ valid: true });\n const metaRef = useRef<StoreInputMeta<TError>>(meta);\n\n const formatValue = useCallback(\n (value: TValue): InputControlValue => binding.codec.format(value),\n [binding],\n );\n\n const parseValue = useCallback(\n (value: InputControlValue) => binding.codec.parse(value as TInput),\n [binding],\n );\n\n const toChecked = useCallback(\n (value: TValue) =>\n props.type === \"radio\"\n ? Object.is(formatValue(value), props.value)\n : Boolean(formatValue(value)),\n [formatValue, props.type, props.value],\n );\n\n const initialStateValue = getStateValue(store.state);\n const resetValueRef = useRef(\n Object.prototype.hasOwnProperty.call(options, \"resetValue\")\n ? (options.resetValue as TValue)\n : initialStateValue,\n );\n\n const setStateValue = useCallback(\n (state: Draft<TState>, value: TValue) => {\n const equals = binding.codec.equals ?? equalStateValue;\n\n if (equals(getStateValue(store.state), value)) {\n return;\n }\n\n setStoreValue(state, value);\n },\n [binding, getStateValue, setStoreValue, store],\n );\n\n const commitValue = useCallback(\n (state: Draft<TState>, controlValue: InputControlValue) => {\n const result = parseValue(controlValue);\n\n if (result.isErr) {\n const invalidMeta: StoreInputMeta<TError> = {\n valid: false,\n error: result.error,\n };\n metaRef.current = invalidMeta;\n setMeta(invalidMeta);\n return;\n }\n\n const validMeta: StoreInputMeta<TError> = { valid: true };\n metaRef.current = validMeta;\n setMeta((current) => (current.valid ? current : validMeta));\n setStateValue(state, result.value);\n },\n [parseValue, setStateValue],\n );\n\n const writeValue = useCallback(\n (element: TInputElement, value: TValue) => {\n if (\n \"checked\" in element &&\n (props.type === \"checkbox\" || props.type === \"radio\")\n ) {\n element.checked = toChecked(value);\n return;\n }\n\n if (\"files\" in element && props.type === \"file\") {\n return;\n }\n\n const formatted = formatValue(value);\n\n if (isDisplayValue(formatted)) {\n writeElementValue(element, formatted);\n }\n },\n [formatValue, props.type, toChecked],\n );\n\n const { dispatch } = useStoreController(store, {\n onSubscribe: (state) => {\n const element = ref.current;\n\n if (\n !element ||\n !metaRef.current.valid ||\n (element.form && isFormResetting(element.form)) ||\n (props.type !== \"radio\" && props.value !== undefined)\n ) {\n return;\n }\n\n if (\n \"checked\" in element &&\n (props.type === \"checkbox\" || props.type === \"radio\") &&\n props.checked !== undefined\n ) {\n return;\n }\n\n writeValue(element, getStateValue(state));\n },\n onDispatch: (state) => {\n const element = ref.current;\n\n if (!element) {\n return;\n }\n\n const controlValue = readControlValue(element, props.type, props.value);\n\n if (controlValue !== undefined) {\n commitValue(state, controlValue);\n }\n },\n });\n\n useEffect(() => {\n const element = ref.current;\n const form = element?.form;\n\n if (!element || !form) {\n return;\n }\n\n return registerResetBinding(form, store.batch, () => {\n if (!element.isConnected) {\n return;\n }\n\n const resetValue = resetValueRef.current;\n const writeResetValue = () => writeValue(element, resetValue);\n\n writeResetValue();\n const validMeta: StoreInputMeta<TError> = { valid: true };\n metaRef.current = validMeta;\n setMeta((current) => (current.valid ? current : validMeta));\n store.dispatch((state) => setStateValue(state, resetValue));\n\n setTimeout(() => {\n if (element.isConnected) {\n writeResetValue();\n }\n }, 0);\n });\n }, [ref, setStateValue, store, store.batch, writeValue]);\n\n const inputProps: StoreInputDomProps<TInputElement> = {\n defaultValue: resolveDefaultValue(props, initialStateValue, (value) => {\n const formatted = formatValue(value);\n return isDisplayValue(formatted) ? formatted : \"\";\n }),\n defaultChecked: resolveDefaultChecked(\n props,\n initialStateValue,\n toChecked,\n ),\n onChange: (event) => {\n dispatch();\n props.onChange?.(event);\n },\n };\n\n return { inputProps, meta };\n}\n","import type { InputDisplayValue, InputStateValue } from \"./types\";\n\ntype StoreElement =\n | HTMLInputElement\n | HTMLTextAreaElement\n | HTMLSelectElement;\n\nfunction pad(value: number) {\n return String(value).padStart(2, \"0\");\n}\n\nexport function formatDateTimeLocal(value: unknown) {\n const date = new Date(value as string | number | Date);\n\n if (Number.isNaN(date.getTime())) {\n return \"\";\n }\n\n return [\n date.getFullYear(),\n \"-\",\n pad(date.getMonth() + 1),\n \"-\",\n pad(date.getDate()),\n \"T\",\n pad(date.getHours()),\n \":\",\n pad(date.getMinutes()),\n \":\",\n pad(date.getSeconds()),\n ].join(\"\");\n}\n\nexport function readElementValue(element: StoreElement): InputStateValue {\n if (\"options\" in element && element.multiple) {\n return Array.from(element.selectedOptions, (option) => option.value);\n }\n\n return element.value;\n}\n\nexport function writeElementValue(\n element: StoreElement,\n value: InputDisplayValue,\n) {\n if (\"files\" in element && element.type === \"file\") {\n return;\n }\n\n if (\"options\" in element && element.multiple) {\n const selectedValues = new Set(\n (Array.isArray(value) ? value : [value]).map(String),\n );\n\n for (const option of element.options) {\n const selected = selectedValues.has(option.value);\n\n if (option.selected !== selected) {\n option.selected = selected;\n }\n }\n\n return;\n }\n\n const nextValue = String(value);\n\n if (element.value !== nextValue) {\n element.value = nextValue;\n }\n}\n\nexport function equalStateValue(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 (\n previous.length === next.length &&\n previous.every((value, index) => Object.is(value, next[index]))\n );\n }\n\n return false;\n}\n","type ResetBatch = (callback: () => void) => unknown;\n\ntype ResetCoordinator = {\n groups: Map<ResetBatch, Set<() => void>>;\n handleReset: () => void;\n};\n\nconst resettingForms = new WeakSet<HTMLFormElement>();\nconst resetCoordinators = new WeakMap<HTMLFormElement, ResetCoordinator>();\n\nexport function isFormResetting(form: HTMLFormElement) {\n return resettingForms.has(form);\n}\n\nexport function registerResetBinding(\n form: HTMLFormElement,\n batch: ResetBatch,\n reset: () => void,\n) {\n let coordinator = resetCoordinators.get(form);\n\n if (!coordinator) {\n const groups = new Map<ResetBatch, Set<() => void>>();\n const handleReset = () => {\n resettingForms.add(form);\n\n // React reset handlers may render and re-register bindings before the\n // browser completes the native reset default action.\n const groupSnapshots = Array.from(groups, ([groupBatch, bindings]) => [\n groupBatch,\n [...bindings],\n ] as const);\n\n setTimeout(() => {\n try {\n for (const [groupBatch, bindings] of groupSnapshots) {\n groupBatch(() => {\n for (const binding of bindings) {\n binding();\n }\n });\n }\n } finally {\n setTimeout(() => resettingForms.delete(form), 0);\n }\n }, 0);\n };\n\n coordinator = { groups, handleReset };\n resetCoordinators.set(form, coordinator);\n form.addEventListener(\"reset\", handleReset);\n }\n\n let bindings = coordinator.groups.get(batch);\n\n if (!bindings) {\n bindings = new Set();\n coordinator.groups.set(batch, bindings);\n }\n\n bindings.add(reset);\n\n return () => {\n const currentCoordinator = resetCoordinators.get(form);\n\n if (!currentCoordinator) {\n return;\n }\n\n const currentBindings = currentCoordinator.groups.get(batch);\n currentBindings?.delete(reset);\n\n if (currentBindings?.size === 0) {\n currentCoordinator.groups.delete(batch);\n }\n\n if (currentCoordinator.groups.size === 0) {\n form.removeEventListener(\"reset\", currentCoordinator.handleReset);\n resetCoordinators.delete(form);\n }\n };\n}\n","import type { HTMLInputTypeAttribute } from \"react\";\nimport { formatDateTimeLocal } from \"./dom_value\";\nimport type {\n InputDisplayValue,\n InputStateValue,\n StoreInputOptions,\n} from \"./types\";\n\nexport function convertToInputValue<TValue>(\n value: TValue,\n type?: HTMLInputTypeAttribute,\n): InputDisplayValue {\n if (value === undefined || value === null) {\n return \"\";\n }\n\n if (type === \"datetime-local\") {\n return formatDateTimeLocal(value);\n }\n\n return Array.isArray(value) ? value.map(String) : String(value);\n}\n\nexport function convertToChecked(\n value: unknown,\n type?: HTMLInputTypeAttribute,\n inputValue?: InputDisplayValue,\n) {\n return type === \"radio\"\n ? value !== undefined && Object.is(value, inputValue)\n : Boolean(value);\n}\n\nexport function convertToStateValue(\n value: InputStateValue,\n type?: HTMLInputTypeAttribute,\n): unknown {\n if (Array.isArray(value)) {\n return value;\n }\n\n if (type === \"number\" || type === \"range\") {\n return value === \"\" ? undefined : Number(value);\n }\n\n if (type === \"datetime-local\") {\n if (value === \"\") {\n return undefined;\n }\n\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? undefined : date;\n }\n\n return value;\n}\n\nexport function resolveDefaultValue<TInputElement, TValue>(\n props: StoreInputOptions<TInputElement>,\n stateValue: TValue,\n format: (value: TValue) => InputDisplayValue,\n) {\n if (props.type !== \"radio\" && props.value !== undefined) {\n return undefined;\n }\n\n if (props.defaultValue !== undefined) {\n return props.defaultValue;\n }\n\n if (\n props.type === \"checkbox\" ||\n props.type === \"radio\" ||\n props.type === \"file\"\n ) {\n return undefined;\n }\n\n return format(stateValue);\n}\n\nexport function resolveDefaultChecked<\n TInputElement,\n TValue,\n>(\n props: StoreInputOptions<TInputElement>,\n stateValue: TValue,\n toChecked: (value: TValue) => boolean,\n) {\n if (props.checked !== undefined) {\n return undefined;\n }\n\n if (props.defaultChecked !== undefined) {\n return props.defaultChecked;\n }\n\n return props.type === \"checkbox\" || props.type === \"radio\"\n ? toChecked(stateValue)\n : undefined;\n}\n\nexport function resolveResetStateValue<\n TInputElement,\n TValue,\n>(\n props: StoreInputOptions<TInputElement>,\n stateValue: TValue,\n parse: (value: InputStateValue) => TValue,\n): TValue {\n if (props.type === \"checkbox\" && props.defaultChecked !== undefined) {\n return props.defaultChecked as TValue;\n }\n\n if (props.type === \"radio\" && props.defaultChecked) {\n if (props.value !== undefined) {\n return props.value as TValue;\n }\n\n return convertToStateValue(\n String(props.defaultValue ?? \"\"),\n props.type,\n ) as TValue;\n }\n\n if (\n props.type !== \"file\" &&\n props.type !== \"checkbox\" &&\n props.type !== \"radio\" &&\n props.defaultValue !== undefined\n ) {\n const defaultValue = Array.isArray(props.defaultValue)\n ? props.defaultValue.map(String)\n : String(props.defaultValue);\n return parse(defaultValue);\n }\n\n return stateValue;\n}\n","import { useMemo, type RefObject } from \"react\";\nimport { ok } from \"gw-result\";\nimport type { Store } from \"gw-store\";\nimport type { InputBinding } from \"../binding/binding\";\nimport { useStoreInput } from \"./use_store_input\";\nimport type {\n InputControlValue,\n InputStateValue,\n StoreInputOptions,\n} from \"./types\";\nimport {\n convertToInputValue,\n convertToStateValue,\n resolveResetStateValue,\n} from \"./value_conversion\";\n\ntype StoreElement =\n | HTMLInputElement\n | HTMLTextAreaElement\n | HTMLSelectElement;\n\nexport type StoreInputWithNameProps<\n TInputElement,\n TState extends object,\n TName extends keyof TState,\n> = StoreInputOptions<TInputElement> & {\n name: TName;\n};\n\nfunction formatControlValue<TValue>(\n value: TValue,\n type: StoreInputOptions<StoreElement>[\"type\"],\n): InputControlValue {\n if (type === \"checkbox\") {\n return Boolean(value);\n }\n\n if (type === \"radio\" || type === \"file\") {\n return value as InputControlValue;\n }\n\n return convertToInputValue(value, type);\n}\n\nfunction parseControlValue<TValue>(\n value: InputControlValue,\n type: StoreInputOptions<StoreElement>[\"type\"],\n): TValue {\n if (typeof value === \"string\" || Array.isArray(value)) {\n const stateInputValue: InputStateValue = Array.isArray(value)\n ? [...value]\n : value;\n return convertToStateValue(stateInputValue, type) as TValue;\n }\n\n return value as TValue;\n}\n\nexport function useStoreInputWithName<\n TInputElement extends StoreElement,\n TState extends object,\n TName extends keyof TState,\n>(\n ref: RefObject<TInputElement | null>,\n store: Store<TState>,\n props: StoreInputWithNameProps<TInputElement, TState, TName>,\n) {\n type TValue = TState[TName];\n\n const binding = useMemo<\n InputBinding<TState, TValue, InputControlValue, never>\n >(\n () => ({\n lens: {\n get: (state) =>\n (state as Readonly<Record<PropertyKey, unknown>>)[\n props.name as PropertyKey\n ] as TValue,\n set: (state, value) => {\n (state as unknown as Record<PropertyKey, unknown>)[\n props.name as PropertyKey\n ] = value;\n },\n },\n codec: {\n format: (value) => formatControlValue(value, props.type),\n parse: (value) => ok(parseControlValue<TValue>(value, props.type)),\n },\n }),\n [props.name, props.type],\n );\n const initialStateValue = binding.lens.get(store.state);\n const resetValue = resolveResetStateValue(\n props,\n initialStateValue,\n (value) => parseControlValue<TValue>(value, props.type),\n );\n const { inputProps } = useStoreInput(ref, store, binding, {\n ...props,\n resetValue,\n });\n\n return {\n ...inputProps,\n name: String(props.name),\n };\n}\n","import {\n useCallback,\n useRef,\n type InputHTMLAttributes,\n type SelectHTMLAttributes,\n type TextareaHTMLAttributes,\n} from \"react\";\nimport type { Store } from \"gw-store\";\nimport {\n useStoreInputWithName,\n type StoreInputWithNameProps,\n} from \"../input/use_store_input_with_name\";\n\ntype StoreElement =\n | HTMLInputElement\n | HTMLSelectElement\n | HTMLTextAreaElement;\n\ntype ElementAttributes<TElement extends StoreElement> =\n TElement extends HTMLInputElement\n ? InputHTMLAttributes<HTMLInputElement>\n : TElement extends HTMLSelectElement\n ? SelectHTMLAttributes<HTMLSelectElement>\n : TextareaHTMLAttributes<HTMLTextAreaElement>;\n\nexport type StoreInputWithNameComponentProps<\n TElement extends StoreElement,\n TState extends object,\n TName extends keyof TState,\n> = StoreInputWithNameProps<TElement, TState, TName> &\n Omit<\n ElementAttributes<TElement>,\n keyof StoreInputWithNameProps<TElement, TState, TName>\n >;\n\nexport type StoreComponentPropsWithStore<\n TElement extends StoreElement,\n TState extends object,\n TName extends keyof TState,\n> = StoreInputWithNameComponentProps<TElement, TState, TName> & {\n store: Store<TState>;\n};\n\nexport function Input<TState extends object, TName extends keyof TState>({\n store,\n ...props\n}: StoreComponentPropsWithStore<HTMLInputElement, TState, TName>) {\n const ref = useRef<HTMLInputElement | null>(null);\n const storeProps = useStoreInputWithName(ref, store, props);\n\n return <input ref={ref} {...props} {...storeProps} />;\n}\n\nexport function Select<TState extends object, TName extends keyof TState>({\n store,\n ...props\n}: StoreComponentPropsWithStore<HTMLSelectElement, TState, TName>) {\n const ref = useRef<HTMLSelectElement | null>(null);\n const storeProps = useStoreInputWithName(ref, store, props);\n\n return <select ref={ref} {...props} {...storeProps} />;\n}\n\nexport function Textarea<TState extends object, TName extends keyof TState>({\n store,\n ...props\n}: StoreComponentPropsWithStore<HTMLTextAreaElement, TState, TName>) {\n const ref = useRef<HTMLTextAreaElement | null>(null);\n const storeProps = useStoreInputWithName(ref, store, props);\n\n return <textarea ref={ref} {...props} {...storeProps} />;\n}\n\nexport function useStoreComponent<TState extends object>(store: Store<TState>) {\n const input = useCallback(\n function Component<TName extends keyof TState>(\n props: StoreInputWithNameComponentProps<\n HTMLInputElement,\n TState,\n TName\n >,\n ) {\n return <Input store={store} {...props} />;\n },\n [store],\n );\n\n const select = useCallback(\n function Component<TName extends keyof TState>(\n props: StoreInputWithNameComponentProps<\n HTMLSelectElement,\n TState,\n TName\n >,\n ) {\n return <Select store={store} {...props} />;\n },\n [store],\n );\n\n const textarea = useCallback(\n function Component<TName extends keyof TState>(\n props: StoreInputWithNameComponentProps<\n HTMLTextAreaElement,\n TState,\n TName\n >,\n ) {\n return <Textarea store={store} {...props} />;\n },\n [store],\n );\n\n return { input, select, textarea };\n}\n","import { useStore, type Store } from \"gw-store\";\nimport {\n createRenderWithStore,\n type CreateRender,\n} from \"../store/create_render\";\nimport { useStoreComponent } from \"./store_components\";\n\nexport type FormStore<TState extends object> = Store<TState> &\n ReturnType<typeof useStoreComponent<TState>> & {\n render: CreateRender<TState>;\n };\n\nexport function useFormStore<TState extends object>(\n initialState: TState,\n): FormStore<TState> {\n const store = useStore<TState>(initialState);\n const storeComponent = useStoreComponent<TState>(store);\n\n return {\n get state() {\n return store.state;\n },\n dispatch: store.dispatch,\n batch: store.batch,\n subscribe: store.subscribe,\n render: createRenderWithStore<TState>(store),\n ...storeComponent,\n };\n}\n","export * from \"./store/use_store_controller\";\nexport * from \"./store/create_render\";\nexport * from \"./binding/lens\";\nexport * from \"./binding/codec\";\nexport * from \"./binding/binding\";\nexport * from \"./binding/laws\";\nexport * from \"./input/use_store_input\";\nexport * from \"./input/use_store_input_with_name\";\nexport * from \"./form/store_components\";\nexport * from \"./form/use_form_store\";\nexport { err, ok, type Result } from \"gw-result\";\nexport * from \"gw-store\";\n"],"mappings":"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","names":["createRender","useCallback","useEffect","useRef","bindings","useRef","useCallback","useEffect","useCallback","useRef","jsx","useRef","useCallback","ok"]}
@@ -0,0 +1,11 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { TextEditorProps as TextEditorProps$1 } from 'gw-react-text-editor';
3
+ import { Store } from 'gw-store';
4
+
5
+ type TextEditorProps<TState extends object> = Omit<TextEditorProps$1, "name"> & {
6
+ store: Store<TState>;
7
+ name: keyof TState;
8
+ };
9
+ declare function TextEditor<TState extends object>({ store, name, defaultValue, ref, ...props }: TextEditorProps<TState>): react_jsx_runtime.JSX.Element;
10
+
11
+ export { TextEditor, type TextEditorProps };
@@ -0,0 +1,11 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { TextEditorProps as TextEditorProps$1 } from 'gw-react-text-editor';
3
+ import { Store } from 'gw-store';
4
+
5
+ type TextEditorProps<TState extends object> = Omit<TextEditorProps$1, "name"> & {
6
+ store: Store<TState>;
7
+ name: keyof TState;
8
+ };
9
+ declare function TextEditor<TState extends object>({ store, name, defaultValue, ref, ...props }: TextEditorProps<TState>): react_jsx_runtime.JSX.Element;
10
+
11
+ export { TextEditor, type TextEditorProps };
@@ -23,46 +23,47 @@ __export(text_editor_exports, {
23
23
  TextEditor: () => TextEditor
24
24
  });
25
25
  module.exports = __toCommonJS(text_editor_exports);
26
+
27
+ // src/editor/text_editor.tsx
26
28
  var import_gw_react_text_editor = require("gw-react-text-editor");
27
29
  var import_react2 = require("react");
28
30
 
29
- // src/use_store_controller.tsx
31
+ // src/store/use_store_controller.tsx
30
32
  var import_react = require("react");
31
33
  function useStoreController(store, props) {
32
34
  const dispatchKey = (0, import_react.useId)();
35
+ const propsRef = (0, import_react.useRef)(props);
36
+ const subscribedStoreRef = (0, import_react.useRef)(null);
33
37
  (0, import_react.useEffect)(() => {
34
- const unsub = store.subscribe((state, key) => {
35
- if (key === dispatchKey) {
36
- return;
38
+ propsRef.current = props;
39
+ }, [props]);
40
+ (0, import_react.useEffect)(() => {
41
+ const isReplacement = subscribedStoreRef.current !== null && subscribedStoreRef.current !== store;
42
+ subscribedStoreRef.current = store;
43
+ const unsubscribe = store.subscribe((state, key) => {
44
+ if (key !== dispatchKey) {
45
+ propsRef.current.onSubscribe(state);
37
46
  }
38
- props.onSubscribe(state);
39
47
  });
40
- return () => {
41
- unsub();
42
- };
43
- }, []);
44
- const dispatch = () => {
48
+ if (isReplacement) {
49
+ propsRef.current.onSubscribe(store.state);
50
+ }
51
+ return unsubscribe;
52
+ }, [dispatchKey, store]);
53
+ const dispatch = (0, import_react.useCallback)(() => {
45
54
  store.dispatch(
46
- (state) => {
47
- props.onDispatch(state);
48
- },
49
- {
50
- key: dispatchKey
51
- }
55
+ (state) => propsRef.current.onDispatch(state),
56
+ { key: dispatchKey }
52
57
  );
53
- };
54
- return {
55
- dispatch
56
- };
58
+ }, [dispatchKey, store]);
59
+ return { dispatch };
57
60
  }
58
61
 
59
- // src/text_editor.tsx
62
+ // src/editor/text_editor.tsx
60
63
  var import_jsx_runtime = require("react/jsx-runtime");
61
64
  function TextEditor({
62
65
  store,
63
66
  name,
64
- getter,
65
- setter,
66
67
  defaultValue,
67
68
  ref,
68
69
  ...props
@@ -79,16 +80,7 @@ function TextEditor({
79
80
  if (!controller) {
80
81
  return;
81
82
  }
82
- const getResult = () => {
83
- if (getter) {
84
- return getter(state) || "";
85
- }
86
- if (name) {
87
- return state[name] || "";
88
- }
89
- return "";
90
- };
91
- const result = getResult();
83
+ const result = state[name] || "";
92
84
  if (controller.value !== result) {
93
85
  controller.value = result;
94
86
  }
@@ -98,33 +90,20 @@ function TextEditor({
98
90
  if (!controller) {
99
91
  return;
100
92
  }
101
- if (setter) {
102
- setter(state, controller.value);
103
- return;
104
- }
105
- if (name) {
106
- state[name] = controller.value;
107
- }
93
+ state[name] = controller.value;
108
94
  }
109
95
  });
110
- const getDefaultValue = () => {
111
- if (getter) {
112
- return getter(store.state);
113
- }
114
- if (name) {
115
- return store.state[name];
116
- }
117
- return void 0;
118
- };
96
+ const getDefaultValue = () => store.state[name];
119
97
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
120
98
  import_gw_react_text_editor.TextEditor,
121
99
  {
122
100
  ...props,
123
101
  ref: controllerRef,
102
+ name: String(name),
124
103
  defaultValue: defaultValue ?? getDefaultValue(),
125
- onChange: (e) => {
104
+ onChange: (event) => {
126
105
  dispatch();
127
- props.onChange?.(e);
106
+ props.onChange?.(event);
128
107
  }
129
108
  }
130
109
  );
@@ -133,3 +112,4 @@ function TextEditor({
133
112
  0 && (module.exports = {
134
113
  TextEditor
135
114
  });
115
+ //# sourceMappingURL=text-editor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/text_editor.tsx","../src/editor/text_editor.tsx","../src/store/use_store_controller.tsx"],"sourcesContent":["export * from \"./editor/text_editor\";\n","import {\n TextEditor as GwTextEditor,\n TextEditorController,\n type TextEditorProps as GwTextEditorProps,\n} from \"gw-react-text-editor\";\nimport { useImperativeHandle, useRef } from \"react\";\nimport type { Store } from \"gw-store\";\nimport { useStoreController } from \"../store/use_store_controller\";\n\nexport type TextEditorProps<TState extends object> = Omit<\n GwTextEditorProps,\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 = useRef<TextEditorController>(null);\n\n useImperativeHandle(\n ref,\n () => controllerRef.current as TextEditorController,\n [],\n );\n\n const { dispatch } = useStoreController<TState>(store, {\n onSubscribe: (state) => {\n const controller = controllerRef.current;\n\n if (!controller) {\n return;\n }\n\n const result = (state[name as never] as unknown as string) || \"\";\n\n if (controller.value !== result) {\n controller.value = result;\n }\n },\n onDispatch: (state) => {\n const controller = controllerRef.current;\n\n if (!controller) {\n return;\n }\n\n state[name as never] = controller.value as unknown as never;\n },\n });\n\n const getDefaultValue = () =>\n store.state[name as never] as unknown as string;\n\n return (\n <GwTextEditor\n {...props}\n ref={controllerRef}\n name={String(name)}\n defaultValue={defaultValue ?? getDefaultValue()}\n onChange={(event) => {\n dispatch();\n props.onChange?.(event);\n }}\n />\n );\n}\n","import { useCallback, useEffect, useId, useRef } from \"react\";\nimport type { Draft, Immutable } from \"immer\";\nimport type { Store } from \"gw-store\";\n\nexport type StoreControllerProps<TState extends object> = {\n onSubscribe: (state: Immutable<TState>) => void;\n onDispatch: (state: Draft<TState>) => void;\n};\n\nexport function useStoreController<TState extends object>(\n store: Store<TState>,\n props: StoreControllerProps<TState>,\n) {\n const dispatchKey = useId();\n const propsRef = useRef(props);\n const subscribedStoreRef = useRef<Store<TState> | null>(null);\n\n useEffect(() => {\n propsRef.current = props;\n }, [props]);\n\n useEffect(() => {\n const isReplacement =\n subscribedStoreRef.current !== null && subscribedStoreRef.current !== store;\n subscribedStoreRef.current = store;\n\n const unsubscribe = store.subscribe((state, key) => {\n if (key !== dispatchKey) {\n propsRef.current.onSubscribe(state);\n }\n });\n\n if (isReplacement) {\n propsRef.current.onSubscribe(store.state);\n }\n\n return unsubscribe;\n }, [dispatchKey, store]);\n\n const dispatch = useCallback(() => {\n store.dispatch(\n (state) => propsRef.current.onDispatch(state),\n { key: dispatchKey },\n );\n }, [dispatchKey, store]);\n\n return { dispatch };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kCAIO;AACP,IAAAA,gBAA4C;;;ACL5C,mBAAsD;AAS/C,SAAS,mBACd,OACA,OACA;AACA,QAAM,kBAAc,oBAAM;AAC1B,QAAM,eAAW,qBAAO,KAAK;AAC7B,QAAM,yBAAqB,qBAA6B,IAAI;AAE5D,8BAAU,MAAM;AACd,aAAS,UAAU;AAAA,EACrB,GAAG,CAAC,KAAK,CAAC;AAEV,8BAAU,MAAM;AACd,UAAM,gBACJ,mBAAmB,YAAY,QAAQ,mBAAmB,YAAY;AACxE,uBAAmB,UAAU;AAE7B,UAAM,cAAc,MAAM,UAAU,CAAC,OAAO,QAAQ;AAClD,UAAI,QAAQ,aAAa;AACvB,iBAAS,QAAQ,YAAY,KAAK;AAAA,MACpC;AAAA,IACF,CAAC;AAED,QAAI,eAAe;AACjB,eAAS,QAAQ,YAAY,MAAM,KAAK;AAAA,IAC1C;AAEA,WAAO;AAAA,EACT,GAAG,CAAC,aAAa,KAAK,CAAC;AAEvB,QAAM,eAAW,0BAAY,MAAM;AACjC,UAAM;AAAA,MACJ,CAAC,UAAU,SAAS,QAAQ,WAAW,KAAK;AAAA,MAC5C,EAAE,KAAK,YAAY;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,aAAa,KAAK,CAAC;AAEvB,SAAO,EAAE,SAAS;AACpB;;;ADcI;AA5CG,SAAS,WAAkC;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAA4B;AAC1B,QAAM,oBAAgB,sBAA6B,IAAI;AAEvD;AAAA,IACE;AAAA,IACA,MAAM,cAAc;AAAA,IACpB,CAAC;AAAA,EACH;AAEA,QAAM,EAAE,SAAS,IAAI,mBAA2B,OAAO;AAAA,IACrD,aAAa,CAAC,UAAU;AACtB,YAAM,aAAa,cAAc;AAEjC,UAAI,CAAC,YAAY;AACf;AAAA,MACF;AAEA,YAAM,SAAU,MAAM,IAAa,KAA2B;AAE9D,UAAI,WAAW,UAAU,QAAQ;AAC/B,mBAAW,QAAQ;AAAA,MACrB;AAAA,IACF;AAAA,IACA,YAAY,CAAC,UAAU;AACrB,YAAM,aAAa,cAAc;AAEjC,UAAI,CAAC,YAAY;AACf;AAAA,MACF;AAEA,YAAM,IAAa,IAAI,WAAW;AAAA,IACpC;AAAA,EACF,CAAC;AAED,QAAM,kBAAkB,MACtB,MAAM,MAAM,IAAa;AAE3B,SACE;AAAA,IAAC,4BAAAC;AAAA,IAAA;AAAA,MACE,GAAG;AAAA,MACJ,KAAK;AAAA,MACL,MAAM,OAAO,IAAI;AAAA,MACjB,cAAc,gBAAgB,gBAAgB;AAAA,MAC9C,UAAU,CAAC,UAAU;AACnB,iBAAS;AACT,cAAM,WAAW,KAAK;AAAA,MACxB;AAAA;AAAA,EACF;AAEJ;","names":["import_react","GwTextEditor"]}
@@ -0,0 +1,90 @@
1
+ // src/editor/text_editor.tsx
2
+ import {
3
+ TextEditor as GwTextEditor
4
+ } from "gw-react-text-editor";
5
+ import { useImperativeHandle, useRef as useRef2 } from "react";
6
+
7
+ // src/store/use_store_controller.tsx
8
+ import { useCallback, useEffect, useId, useRef } from "react";
9
+ function useStoreController(store, props) {
10
+ const dispatchKey = useId();
11
+ const propsRef = useRef(props);
12
+ const subscribedStoreRef = useRef(null);
13
+ useEffect(() => {
14
+ propsRef.current = props;
15
+ }, [props]);
16
+ useEffect(() => {
17
+ const isReplacement = subscribedStoreRef.current !== null && subscribedStoreRef.current !== store;
18
+ subscribedStoreRef.current = store;
19
+ const unsubscribe = store.subscribe((state, key) => {
20
+ if (key !== dispatchKey) {
21
+ propsRef.current.onSubscribe(state);
22
+ }
23
+ });
24
+ if (isReplacement) {
25
+ propsRef.current.onSubscribe(store.state);
26
+ }
27
+ return unsubscribe;
28
+ }, [dispatchKey, store]);
29
+ const dispatch = useCallback(() => {
30
+ store.dispatch(
31
+ (state) => propsRef.current.onDispatch(state),
32
+ { key: dispatchKey }
33
+ );
34
+ }, [dispatchKey, store]);
35
+ return { dispatch };
36
+ }
37
+
38
+ // src/editor/text_editor.tsx
39
+ import { jsx } from "react/jsx-runtime";
40
+ function TextEditor({
41
+ store,
42
+ name,
43
+ defaultValue,
44
+ ref,
45
+ ...props
46
+ }) {
47
+ const controllerRef = useRef2(null);
48
+ useImperativeHandle(
49
+ ref,
50
+ () => controllerRef.current,
51
+ []
52
+ );
53
+ const { dispatch } = useStoreController(store, {
54
+ onSubscribe: (state) => {
55
+ const controller = controllerRef.current;
56
+ if (!controller) {
57
+ return;
58
+ }
59
+ const result = state[name] || "";
60
+ if (controller.value !== result) {
61
+ controller.value = result;
62
+ }
63
+ },
64
+ onDispatch: (state) => {
65
+ const controller = controllerRef.current;
66
+ if (!controller) {
67
+ return;
68
+ }
69
+ state[name] = controller.value;
70
+ }
71
+ });
72
+ const getDefaultValue = () => store.state[name];
73
+ return /* @__PURE__ */ jsx(
74
+ GwTextEditor,
75
+ {
76
+ ...props,
77
+ ref: controllerRef,
78
+ name: String(name),
79
+ defaultValue: defaultValue ?? getDefaultValue(),
80
+ onChange: (event) => {
81
+ dispatch();
82
+ props.onChange?.(event);
83
+ }
84
+ }
85
+ );
86
+ }
87
+ export {
88
+ TextEditor
89
+ };
90
+ //# sourceMappingURL=text-editor.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/editor/text_editor.tsx","../src/store/use_store_controller.tsx"],"sourcesContent":["import {\n TextEditor as GwTextEditor,\n TextEditorController,\n type TextEditorProps as GwTextEditorProps,\n} from \"gw-react-text-editor\";\nimport { useImperativeHandle, useRef } from \"react\";\nimport type { Store } from \"gw-store\";\nimport { useStoreController } from \"../store/use_store_controller\";\n\nexport type TextEditorProps<TState extends object> = Omit<\n GwTextEditorProps,\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 = useRef<TextEditorController>(null);\n\n useImperativeHandle(\n ref,\n () => controllerRef.current as TextEditorController,\n [],\n );\n\n const { dispatch } = useStoreController<TState>(store, {\n onSubscribe: (state) => {\n const controller = controllerRef.current;\n\n if (!controller) {\n return;\n }\n\n const result = (state[name as never] as unknown as string) || \"\";\n\n if (controller.value !== result) {\n controller.value = result;\n }\n },\n onDispatch: (state) => {\n const controller = controllerRef.current;\n\n if (!controller) {\n return;\n }\n\n state[name as never] = controller.value as unknown as never;\n },\n });\n\n const getDefaultValue = () =>\n store.state[name as never] as unknown as string;\n\n return (\n <GwTextEditor\n {...props}\n ref={controllerRef}\n name={String(name)}\n defaultValue={defaultValue ?? getDefaultValue()}\n onChange={(event) => {\n dispatch();\n props.onChange?.(event);\n }}\n />\n );\n}\n","import { useCallback, useEffect, useId, useRef } from \"react\";\nimport type { Draft, Immutable } from \"immer\";\nimport type { Store } from \"gw-store\";\n\nexport type StoreControllerProps<TState extends object> = {\n onSubscribe: (state: Immutable<TState>) => void;\n onDispatch: (state: Draft<TState>) => void;\n};\n\nexport function useStoreController<TState extends object>(\n store: Store<TState>,\n props: StoreControllerProps<TState>,\n) {\n const dispatchKey = useId();\n const propsRef = useRef(props);\n const subscribedStoreRef = useRef<Store<TState> | null>(null);\n\n useEffect(() => {\n propsRef.current = props;\n }, [props]);\n\n useEffect(() => {\n const isReplacement =\n subscribedStoreRef.current !== null && subscribedStoreRef.current !== store;\n subscribedStoreRef.current = store;\n\n const unsubscribe = store.subscribe((state, key) => {\n if (key !== dispatchKey) {\n propsRef.current.onSubscribe(state);\n }\n });\n\n if (isReplacement) {\n propsRef.current.onSubscribe(store.state);\n }\n\n return unsubscribe;\n }, [dispatchKey, store]);\n\n const dispatch = useCallback(() => {\n store.dispatch(\n (state) => propsRef.current.onDispatch(state),\n { key: dispatchKey },\n );\n }, [dispatchKey, store]);\n\n return { dispatch };\n}\n"],"mappings":";AAAA;AAAA,EACE,cAAc;AAAA,OAGT;AACP,SAAS,qBAAqB,UAAAA,eAAc;;;ACL5C,SAAS,aAAa,WAAW,OAAO,cAAc;AAS/C,SAAS,mBACd,OACA,OACA;AACA,QAAM,cAAc,MAAM;AAC1B,QAAM,WAAW,OAAO,KAAK;AAC7B,QAAM,qBAAqB,OAA6B,IAAI;AAE5D,YAAU,MAAM;AACd,aAAS,UAAU;AAAA,EACrB,GAAG,CAAC,KAAK,CAAC;AAEV,YAAU,MAAM;AACd,UAAM,gBACJ,mBAAmB,YAAY,QAAQ,mBAAmB,YAAY;AACxE,uBAAmB,UAAU;AAE7B,UAAM,cAAc,MAAM,UAAU,CAAC,OAAO,QAAQ;AAClD,UAAI,QAAQ,aAAa;AACvB,iBAAS,QAAQ,YAAY,KAAK;AAAA,MACpC;AAAA,IACF,CAAC;AAED,QAAI,eAAe;AACjB,eAAS,QAAQ,YAAY,MAAM,KAAK;AAAA,IAC1C;AAEA,WAAO;AAAA,EACT,GAAG,CAAC,aAAa,KAAK,CAAC;AAEvB,QAAM,WAAW,YAAY,MAAM;AACjC,UAAM;AAAA,MACJ,CAAC,UAAU,SAAS,QAAQ,WAAW,KAAK;AAAA,MAC5C,EAAE,KAAK,YAAY;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,aAAa,KAAK,CAAC;AAEvB,SAAO,EAAE,SAAS;AACpB;;;ADcI;AA5CG,SAAS,WAAkC;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAA4B;AAC1B,QAAM,gBAAgBC,QAA6B,IAAI;AAEvD;AAAA,IACE;AAAA,IACA,MAAM,cAAc;AAAA,IACpB,CAAC;AAAA,EACH;AAEA,QAAM,EAAE,SAAS,IAAI,mBAA2B,OAAO;AAAA,IACrD,aAAa,CAAC,UAAU;AACtB,YAAM,aAAa,cAAc;AAEjC,UAAI,CAAC,YAAY;AACf;AAAA,MACF;AAEA,YAAM,SAAU,MAAM,IAAa,KAA2B;AAE9D,UAAI,WAAW,UAAU,QAAQ;AAC/B,mBAAW,QAAQ;AAAA,MACrB;AAAA,IACF;AAAA,IACA,YAAY,CAAC,UAAU;AACrB,YAAM,aAAa,cAAc;AAEjC,UAAI,CAAC,YAAY;AACf;AAAA,MACF;AAEA,YAAM,IAAa,IAAI,WAAW;AAAA,IACpC;AAAA,EACF,CAAC;AAED,QAAM,kBAAkB,MACtB,MAAM,MAAM,IAAa;AAE3B,SACE;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ,KAAK;AAAA,MACL,MAAM,OAAO,IAAI;AAAA,MACjB,cAAc,gBAAgB,gBAAgB;AAAA,MAC9C,UAAU,CAAC,UAAU;AACnB,iBAAS;AACT,cAAM,WAAW,KAAK;AAAA,MACxB;AAAA;AAAA,EACF;AAEJ;","names":["useRef","useRef"]}
package/package.json CHANGED
@@ -1,47 +1,107 @@
1
1
  {
2
2
  "name": "react-store-input",
3
- "version": "0.2.6",
3
+ "version": "0.4.0",
4
+ "description": "Typed React form inputs backed by a small immutable store",
5
+ "keywords": [
6
+ "react",
7
+ "form",
8
+ "input",
9
+ "state-management",
10
+ "typescript"
11
+ ],
12
+ "author": "gawonsoft",
13
+ "license": "MIT",
14
+ "type": "commonjs",
15
+ "files": [
16
+ "dist",
17
+ "CHANGELOG.md",
18
+ "LICENSE",
19
+ "README.md"
20
+ ],
4
21
  "types": "./dist/index.d.ts",
5
- "main": "./dist/index.mjs",
6
- "module": "./dist/index.js",
22
+ "main": "./dist/index.js",
23
+ "module": "./dist/index.mjs",
7
24
  "sideEffects": false,
8
25
  "exports": {
9
26
  ".": {
10
- "types": "./dist/index.d.ts",
11
- "import": "./dist/index.mjs",
12
- "require": "./dist/index.js"
27
+ "import": {
28
+ "types": "./dist/index.d.mts",
29
+ "default": "./dist/index.mjs"
30
+ },
31
+ "require": {
32
+ "types": "./dist/index.d.ts",
33
+ "default": "./dist/index.js"
34
+ }
35
+ },
36
+ "./text-editor": {
37
+ "import": {
38
+ "types": "./dist/text-editor.d.mts",
39
+ "default": "./dist/text-editor.mjs"
40
+ },
41
+ "require": {
42
+ "types": "./dist/text-editor.d.ts",
43
+ "default": "./dist/text-editor.js"
44
+ }
13
45
  }
14
46
  },
15
47
  "scripts": {
16
- "build": "NODE_OPTIONS='--max-old-space-size=16384' tsup",
17
- "dev": "tsup --watch"
48
+ "build": "tsup",
49
+ "dev": "tsup --watch",
50
+ "lint": "eslint src",
51
+ "typecheck": "tsc --noEmit",
52
+ "test:types": "tsc -p tests/tsconfig.json",
53
+ "test:runtime": "node --test tests/runtime/binding.test.cjs tests/runtime/input.test.cjs tests/runtime/reset.test.cjs",
54
+ "test:package": "node scripts/package-smoke.cjs",
55
+ "test:example": "npm --prefix example run lint && npm --prefix example run build",
56
+ "check": "npm run lint && npm run typecheck && npm run build && npm run test:types && npm run test:runtime && npm run test:package && npm run test:example",
57
+ "test": "npm run check",
58
+ "prepack": "npm run build",
59
+ "prepublishOnly": "npm run check"
18
60
  },
19
61
  "repository": {
20
62
  "type": "git",
21
63
  "url": "git+https://github.com/gawonsoft/react-store-input.git"
22
64
  },
23
- "author": "",
24
- "license": "MIT",
25
65
  "bugs": {
26
66
  "url": "https://github.com/gawonsoft/react-store-input/issues"
27
67
  },
28
68
  "homepage": "https://github.com/gawonsoft/react-store-input#readme",
29
- "description": "",
30
69
  "devDependencies": {
70
+ "@eslint/js": "^9.39.5",
31
71
  "@types/node": "^24.10.1",
32
72
  "@types/react": "^18.3.1 || ^19.2.1",
33
73
  "@types/react-dom": "^18.3.1 || ^19.2.1",
74
+ "eslint": "^9.39.5",
75
+ "eslint-plugin-react-hooks": "^7.1.1",
76
+ "gw-react-text-editor": "^0.1.6",
77
+ "jsdom": "^24.1.3",
78
+ "react": "^19.2.8",
79
+ "react-dom": "^19.2.8",
34
80
  "tsup": "^8.5.1",
35
- "typescript": "^5.7.3"
81
+ "typescript": "^5.7.3",
82
+ "typescript-eslint": "^8.65.0"
36
83
  },
37
84
  "peerDependencies": {
38
- "react": "^18.3.1 || ^19.2.1",
39
- "react-dom": "^18.3.1 || ^19.2.1"
85
+ "gw-react-text-editor": "^0.1.6",
86
+ "react": ">=18.0.0 <20.0.0",
87
+ "react-dom": ">=18.0.0 <20.0.0"
88
+ },
89
+ "peerDependenciesMeta": {
90
+ "gw-react-text-editor": {
91
+ "optional": true
92
+ }
40
93
  },
41
94
  "dependencies": {
42
- "date-fns": "^4.1.0",
43
- "gw-react-text-editor": "^0.1.4",
44
- "gw-store": "^0.1.1",
45
- "immer": "^11.1.3"
95
+ "gw-result": "0.3.0",
96
+ "gw-store": "0.2.0",
97
+ "immer": "^11.1.4"
98
+ },
99
+ "engines": {
100
+ "node": ">=18.0.0"
101
+ },
102
+ "publishConfig": {
103
+ "access": "public",
104
+ "provenance": true,
105
+ "registry": "https://registry.npmjs.org/"
46
106
  }
47
107
  }
@@ -1,9 +0,0 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { Store } from 'gw-store';
3
- import { ReactNode } from 'react';
4
-
5
- type CreateRender<TState> = (selector: (state: TState) => ReactNode, compare?: (a: TState, b: TState) => boolean) => ReactNode;
6
- declare function createRender<TState>(store: Store<TState>, selector: (state: TState) => ReactNode, compare?: (a: TState, b: TState) => boolean): react_jsx_runtime.JSX.Element;
7
- declare function createRenderWithStore<TState>(store: Store<TState>): CreateRender<TState>;
8
-
9
- export { type CreateRender, createRender, createRenderWithStore };
@@ -1,9 +0,0 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { Store } from 'gw-store';
3
- import { ReactNode } from 'react';
4
-
5
- type CreateRender<TState> = (selector: (state: TState) => ReactNode, compare?: (a: TState, b: TState) => boolean) => ReactNode;
6
- declare function createRender<TState>(store: Store<TState>, selector: (state: TState) => ReactNode, compare?: (a: TState, b: TState) => boolean): react_jsx_runtime.JSX.Element;
7
- declare function createRenderWithStore<TState>(store: Store<TState>): CreateRender<TState>;
8
-
9
- export { type CreateRender, createRender, createRenderWithStore };
@@ -1,51 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/create_render.tsx
21
- var create_render_exports = {};
22
- __export(create_render_exports, {
23
- createRender: () => createRender,
24
- createRenderWithStore: () => createRenderWithStore
25
- });
26
- module.exports = __toCommonJS(create_render_exports);
27
- var import_gw_store = require("gw-store");
28
- var import_jsx_runtime = require("react/jsx-runtime");
29
- function createRender(store, selector, compare) {
30
- function Component() {
31
- const result = (0, import_gw_store.useSelector)(store, (state) => state, compare);
32
- const content = selector(result);
33
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: content });
34
- }
35
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, {});
36
- }
37
- function createRenderWithStore(store) {
38
- return function createRender2(selector, compare) {
39
- function Component() {
40
- const result = (0, import_gw_store.useSelector)(store, (state) => state, compare);
41
- const content = selector(result);
42
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: content });
43
- }
44
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, {});
45
- };
46
- }
47
- // Annotate the CommonJS export names for ESM import in node:
48
- 0 && (module.exports = {
49
- createRender,
50
- createRenderWithStore
51
- });
@@ -1,25 +0,0 @@
1
- // src/create_render.tsx
2
- import { useSelector } from "gw-store";
3
- import { Fragment, jsx } from "react/jsx-runtime";
4
- function createRender(store, selector, compare) {
5
- function Component() {
6
- const result = useSelector(store, (state) => state, compare);
7
- const content = selector(result);
8
- return /* @__PURE__ */ jsx(Fragment, { children: content });
9
- }
10
- return /* @__PURE__ */ jsx(Component, {});
11
- }
12
- function createRenderWithStore(store) {
13
- return function createRender2(selector, compare) {
14
- function Component() {
15
- const result = useSelector(store, (state) => state, compare);
16
- const content = selector(result);
17
- return /* @__PURE__ */ jsx(Fragment, { children: content });
18
- }
19
- return /* @__PURE__ */ jsx(Component, {});
20
- };
21
- }
22
- export {
23
- createRender,
24
- createRenderWithStore
25
- };
@@ -1,13 +0,0 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { TextEditorProps as TextEditorProps$1 } from 'gw-react-text-editor';
3
- import { Store } from 'gw-store';
4
-
5
- type TextEditorProps<TState> = {
6
- store: Store<TState>;
7
- name?: keyof TState;
8
- getter?: (state: TState) => string;
9
- setter?: (state: TState, value: string) => void;
10
- } & TextEditorProps$1;
11
- declare function TextEditor<TState>({ store, name, getter, setter, defaultValue, ref, ...props }: TextEditorProps<TState>): react_jsx_runtime.JSX.Element;
12
-
13
- export { TextEditor, type TextEditorProps };
@@ -1,13 +0,0 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { TextEditorProps as TextEditorProps$1 } from 'gw-react-text-editor';
3
- import { Store } from 'gw-store';
4
-
5
- type TextEditorProps<TState> = {
6
- store: Store<TState>;
7
- name?: keyof TState;
8
- getter?: (state: TState) => string;
9
- setter?: (state: TState, value: string) => void;
10
- } & TextEditorProps$1;
11
- declare function TextEditor<TState>({ store, name, getter, setter, defaultValue, ref, ...props }: TextEditorProps<TState>): react_jsx_runtime.JSX.Element;
12
-
13
- export { TextEditor, type TextEditorProps };