@shivaedev/effect-react 0.2.2 → 0.3.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 +14 -0
- package/README.md +2 -0
- package/dist/client/create.d.ts +2 -0
- package/dist/client/create.d.ts.map +1 -1
- package/dist/client/create.js +3 -1
- package/dist/client/create.js.map +1 -1
- package/dist/client/editor.d.ts +2 -0
- package/dist/client/editor.d.ts.map +1 -1
- package/dist/client/editor.js +3 -0
- package/dist/client/editor.js.map +1 -1
- package/dist/client/sessionBoundary.d.ts.map +1 -1
- package/dist/client/sessionBoundary.js +5 -1
- package/dist/client/sessionBoundary.js.map +1 -1
- package/dist/client/useDraftOwner.d.ts +3 -0
- package/dist/client/useDraftOwner.d.ts.map +1 -0
- package/dist/client/useDraftOwner.js +36 -0
- package/dist/client/useDraftOwner.js.map +1 -0
- package/dist/submission.d.ts +2 -0
- package/dist/submission.d.ts.map +1 -1
- package/dist/submission.js +1 -0
- package/dist/submission.js.map +1 -1
- package/package.json +9 -13
- package/src/client/create.ts +4 -1
- package/src/client/editor.ts +4 -0
- package/src/client/sessionBoundary.ts +9 -1
- package/src/client/useDraftOwner.ts +42 -0
- package/src/submission.ts +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0 - 2026-10-13
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `useEditor` and `useCreate` accept opt-in `persistedDraft` settings with application keys, storage and bounds. They inherit `SessionBoundary` ownership, which drops tracked drafts when the session changes or ends, including drafts from unmounted screens.
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Include `@shivaedev/effect-form` as a workspace dependency for shared draft ownership at the session boundary.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Reconcile `SessionBoundary` draft ownership when a session replacement commits. A suspended, canceled transition preserves the visible session's draft and future persistence; committed replacements also clear old drafts while Activity is hidden.
|
|
16
|
+
|
|
3
17
|
## 0.2.2 - 2026-10-12
|
|
4
18
|
|
|
5
19
|
### Added
|
package/README.md
CHANGED
|
@@ -30,6 +30,8 @@ An **editor** joins a query atom, an effect-form field schema and a save Effect.
|
|
|
30
30
|
|
|
31
31
|
A **session generation** is the string returned by `identify(session)`. `SessionBoundary` owns its client, registry and React subtree. Your auth owner supplies the session and decides when to replace or end it. Cache policy, RPC declarations and transport stay with native Effect and the application; this package supplies their React state and lifecycle boundary.
|
|
32
32
|
|
|
33
|
+
`useEditor` and `useCreate` opt into persisted input with `persistedDraft: { key }`. Choose a stable key for the record or create request, independent of its server revision. The adapters use effect-form's browser storage and inherit ownership from `SessionBoundary`: reload and query refresh preserve edits, successful submission clears accepted input, and a committed session replacement drops tracked drafts. A suspended replacement that is canceled keeps the visible session's draft and future writes. Later edits made during a save remain persisted. Optional `storage`, `maxBytes` and `maxAgeMs` customize the provider and bounds; defaults are `localStorage`, 256 KiB of UTF-8 JSON and seven days. See [effect-form's persisted input API](https://github.com/ShivaeDev/platform/tree/main/packages/effect-form#persist-input-across-reloads) for direct form hooks and limits.
|
|
34
|
+
|
|
33
35
|
### 1. Once per application: own the registry
|
|
34
36
|
|
|
35
37
|
For a screen without a session boundary, wrap it in the native provider:
|
package/dist/client/create.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { type Effect, Option, type Schema } from "effect";
|
|
2
2
|
import type * as Atom from "effect/unstable/reactivity/Atom";
|
|
3
|
+
import { type BrowserDraftConfig } from "@shivaedev/effect-form/react.ts";
|
|
3
4
|
import type { Decoded, Encoded, FieldFailure, Fields, Form, Services } from "@shivaedev/effect-form/shape.ts";
|
|
4
5
|
import type { AtomServices, FieldRejectionMapping } from "#field-rejection.ts";
|
|
5
6
|
import { type SaveState } from "./saveState.ts";
|
|
6
7
|
export type CreateConfig<TFields extends Fields, TCreated, TSaveError, R, TRuntimeError> = {
|
|
8
|
+
readonly persistedDraft?: BrowserDraftConfig | undefined;
|
|
7
9
|
readonly fields: Schema.Struct<TFields>;
|
|
8
10
|
readonly initialValues: Encoded<TFields>;
|
|
9
11
|
readonly create: (values: Decoded<TFields>) => Effect.Effect<TCreated, TSaveError, AtomServices<R>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/client/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAS,MAAM,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AACjE,OAAO,KAAK,KAAK,IAAI,MAAM,iCAAiC,CAAC;AAE7D,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAC9G,OAAO,KAAK,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE/E,OAAO,EAAE,KAAK,SAAS,EAAgB,MAAM,gBAAgB,CAAC;AAC9D,MAAM,MAAM,YAAY,CAAC,OAAO,SAAS,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,EAAE,aAAa,IAAI;IAC1F,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACxC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IACzC,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IACpG,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;CACxE,GAAG,qBAAqB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC/C,MAAM,WAAW,MAAM,CAAC,OAAO,SAAS,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,CAAE,SAAQ,SAAS,CAAC,UAAU,GAAG,aAAa,CAAC;IACjI,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC1C,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,GAAG,YAAY,EAAE,aAAa,CAAC,CAAC;CACjF;AAKD,wBAAgB,SAAS,CAAC,OAAO,SAAS,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,EAAE,aAAa,EACvF,MAAM,EAAE,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,EAAE,aAAa,CAAC,GACnE,MAAM,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,CAAC,
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/client/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAS,MAAM,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AACjE,OAAO,KAAK,KAAK,IAAI,MAAM,iCAAiC,CAAC;AAE7D,OAAO,EAAE,KAAK,kBAAkB,EAAqB,MAAM,iCAAiC,CAAC;AAC7F,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAC9G,OAAO,KAAK,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE/E,OAAO,EAAE,KAAK,SAAS,EAAgB,MAAM,gBAAgB,CAAC;AAC9D,MAAM,MAAM,YAAY,CAAC,OAAO,SAAS,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,EAAE,aAAa,IAAI;IAC1F,QAAQ,CAAC,cAAc,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;IACzD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACxC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IACzC,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IACpG,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;CACxE,GAAG,qBAAqB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC/C,MAAM,WAAW,MAAM,CAAC,OAAO,SAAS,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,CAAE,SAAQ,SAAS,CAAC,UAAU,GAAG,aAAa,CAAC;IACjI,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC1C,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,GAAG,YAAY,EAAE,aAAa,CAAC,CAAC;CACjF;AAKD,wBAAgB,SAAS,CAAC,OAAO,SAAS,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,EAAE,aAAa,EACvF,MAAM,EAAE,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,EAAE,aAAa,CAAC,GACnE,MAAM,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,CAAC,CAkBtD"}
|
package/dist/client/create.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Equal, Option } from "effect";
|
|
2
2
|
import { useCallback, useState } from "react";
|
|
3
|
+
import { usePersistedDraft } from "@shivaedev/effect-form/react.ts";
|
|
3
4
|
import { submission } from "#submission.ts";
|
|
4
5
|
import { useSaveState } from "./saveState.js";
|
|
5
6
|
function editedSince(current, submitted) {
|
|
@@ -7,9 +8,10 @@ function editedSince(current, submitted) {
|
|
|
7
8
|
return Object.fromEntries(Object.entries(current).filter(([name, value]) => !Equal.equals(value, before.get(name))));
|
|
8
9
|
}
|
|
9
10
|
export function useCreate(config) {
|
|
11
|
+
const persistedDraft = usePersistedDraft(config.persistedDraft);
|
|
10
12
|
const [{ initialValues: baseline, fresh }] = useState(() => {
|
|
11
13
|
const { fields, initialValues, runtime, create, rejectField } = config;
|
|
12
|
-
return { fresh: () => submission({ fields, initialValues, rejectField, runtime, save: create }), initialValues };
|
|
14
|
+
return { fresh: () => submission({ fields, initialValues, persistedDraft, rejectField, runtime, save: create }), initialValues };
|
|
13
15
|
});
|
|
14
16
|
const [current, replace] = useState(fresh);
|
|
15
17
|
const [created, record] = useState(Option.none());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/client/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,EAAE,MAAM,EAAe,MAAM,QAAQ,CAAC;AAEjE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/client/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,EAAE,MAAM,EAAe,MAAM,QAAQ,CAAC;AAEjE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC9C,OAAO,EAA2B,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAG7F,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAkB,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAY9D,SAAS,WAAW,CAAoB,OAAW,EAAE,SAAyB;IAC7E,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,CAAC,CAAC;IAC7D,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AACtH,CAAC;AACD,MAAM,UAAU,SAAS,CACxB,MAAqE;IAErE,MAAM,cAAc,GAAG,iBAAiB,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IAChE,MAAM,CAAC,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE;QAC1D,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;QACvE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC;IAClI,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC3C,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,QAAQ,CAA0B,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3E,MAAM,OAAO,GAAG,WAAW,CAC1B,CAAC,KAAe,EAAE,EAAE;QACnB,MAAM,IAAI,GAAG,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;QACtG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3B,OAAO,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,EACD,CAAC,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,CAC1B,CAAC;IACF,OAAO,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;AAChF,CAAC"}
|
package/dist/client/editor.d.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { type Effect, type Schema } from "effect";
|
|
2
2
|
import type * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
|
|
3
3
|
import type * as Atom from "effect/unstable/reactivity/Atom";
|
|
4
|
+
import { type BrowserDraftConfig } from "@shivaedev/effect-form/react.ts";
|
|
4
5
|
import type { Decoded, Encoded, FieldFailure, Fields, Form, Services } from "@shivaedev/effect-form/shape.ts";
|
|
5
6
|
import type { AtomServices, FieldRejectionMapping } from "#field-rejection.ts";
|
|
6
7
|
import { type QueryState } from "./resultState.ts";
|
|
7
8
|
import { type SaveState } from "./saveState.ts";
|
|
8
9
|
export type EditorConfig<TFields extends Fields, TRow, TQueryError, TSaveError, R, TRuntimeError> = {
|
|
10
|
+
readonly persistedDraft?: BrowserDraftConfig | undefined;
|
|
9
11
|
readonly query: Atom.Atom<AsyncResult.AsyncResult<TRow, TQueryError>>;
|
|
10
12
|
readonly fields: Schema.Struct<TFields>;
|
|
11
13
|
readonly values: (row: TRow) => Encoded<TFields>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../src/client/editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAU,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,KAAK,KAAK,WAAW,MAAM,wCAAwC,CAAC;AAC3E,OAAO,KAAK,KAAK,IAAI,MAAM,iCAAiC,CAAC;AAE7D,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAC9G,OAAO,KAAK,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE/E,OAAO,EAAE,KAAK,UAAU,EAAY,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,KAAK,SAAS,EAAgB,MAAM,gBAAgB,CAAC;AAC9D,MAAM,MAAM,YAAY,CAAC,OAAO,SAAS,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,EAAE,aAAa,IAAI;IACnG,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC;IACtE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACjD,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9F,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;CACxE,GAAG,qBAAqB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC/C,MAAM,WAAW,MAAM,CAAC,OAAO,SAAS,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,CAAE,SAAQ,SAAS,CAAC,UAAU,GAAG,aAAa,CAAC;IAC1I,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,UAAU,GAAG,YAAY,EAAE,aAAa,CAAC,GAAG,SAAS,CAAC;IACzF,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;CAC9C;AAMD,wBAAgB,SAAS,CAAC,OAAO,SAAS,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,EAAE,aAAa,EAChG,MAAM,EAAE,YAAY,CAAC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,EAAE,aAAa,CAAC,GAC5E,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,CAAC,
|
|
1
|
+
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../src/client/editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAU,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,KAAK,KAAK,WAAW,MAAM,wCAAwC,CAAC;AAC3E,OAAO,KAAK,KAAK,IAAI,MAAM,iCAAiC,CAAC;AAE7D,OAAO,EAAE,KAAK,kBAAkB,EAAqB,MAAM,iCAAiC,CAAC;AAC7F,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAC9G,OAAO,KAAK,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE/E,OAAO,EAAE,KAAK,UAAU,EAAY,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,KAAK,SAAS,EAAgB,MAAM,gBAAgB,CAAC;AAC9D,MAAM,MAAM,YAAY,CAAC,OAAO,SAAS,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,EAAE,aAAa,IAAI;IACnG,QAAQ,CAAC,cAAc,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;IACzD,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC;IACtE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACjD,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9F,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;CACxE,GAAG,qBAAqB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAC/C,MAAM,WAAW,MAAM,CAAC,OAAO,SAAS,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,CAAE,SAAQ,SAAS,CAAC,UAAU,GAAG,aAAa,CAAC;IAC1I,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,UAAU,GAAG,YAAY,EAAE,aAAa,CAAC,GAAG,SAAS,CAAC;IACzF,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;CAC9C;AAMD,wBAAgB,SAAS,CAAC,OAAO,SAAS,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,EAAE,aAAa,EAChG,MAAM,EAAE,YAAY,CAAC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,EAAE,aAAa,CAAC,GAC5E,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,CAAC,CA8B/D"}
|
package/dist/client/editor.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { Option } from "effect";
|
|
2
2
|
import { useCallback, useEffect, useState } from "react";
|
|
3
|
+
import { usePersistedDraft } from "@shivaedev/effect-form/react.ts";
|
|
3
4
|
import { submission } from "#submission.ts";
|
|
4
5
|
import { useQuery } from "./resultState.js";
|
|
5
6
|
import { useSaveState } from "./saveState.js";
|
|
6
7
|
export function useEditor(config) {
|
|
8
|
+
const persistedDraft = usePersistedDraft(config.persistedDraft);
|
|
7
9
|
const query = useQuery(config.query);
|
|
8
10
|
const row = Option.getOrUndefined(query.data);
|
|
9
11
|
const [held, hold] = useState(undefined);
|
|
@@ -13,6 +15,7 @@ export function useEditor(config) {
|
|
|
13
15
|
form: submission({
|
|
14
16
|
fields: config.fields,
|
|
15
17
|
initialValues: config.values(row),
|
|
18
|
+
persistedDraft,
|
|
16
19
|
rejectField: config.rejectField,
|
|
17
20
|
runtime: config.runtime,
|
|
18
21
|
save: config.save,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor.js","sourceRoot":"","sources":["../../src/client/editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,MAAM,EAAe,MAAM,QAAQ,CAAC;AAG1D,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"editor.js","sourceRoot":"","sources":["../../src/client/editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,MAAM,EAAe,MAAM,QAAQ,CAAC;AAG1D,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACzD,OAAO,EAA2B,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAG7F,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAmB,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAkB,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAkB9D,MAAM,UAAU,SAAS,CACxB,MAA8E;IAE9E,MAAM,cAAc,GAAG,iBAAiB,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IAChE,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,GAAG,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC9C,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,QAAQ,CAA6D,SAAS,CAAC,CAAC;IACrG,IAAI,OAAO,GAAG,IAAI,EAAE,KAAK,KAAK,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9D,IAAI,OAAO,KAAK,SAAS,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QAChD,OAAO,GAAG;YACT,IAAI,EAAE,UAAU,CAAC;gBAChB,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC;gBACjC,cAAc;gBACd,WAAW,EAAE,MAAM,CAAC,WAAW;gBAC/B,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,IAAI,EAAE,MAAM,CAAC,IAAI;aACjB,CAAC,CAAC,IAAI;YACP,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,MAAM,EAAE,MAAM,CAAC,MAAM;SACrB,CAAC;QACF,IAAI,CAAC,OAAO,CAAC,CAAC;IACf,CAAC;IACD,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,CAAC;IAC3B,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,CAAC;IAC/B,SAAS,CAAC,GAAG,EAAE;QACd,IAAI,IAAI,IAAI,MAAM,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACzC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3B,CAAC;IACF,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;IACxB,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,KAAW,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IACzH,OAAO,EAAE,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACxD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionBoundary.d.ts","sourceRoot":"","sources":["../../src/client/sessionBoundary.ts"],"names":[],"mappings":"AAEA,OAAO,EAA0C,KAAK,SAAS,EAAmC,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"sessionBoundary.d.ts","sourceRoot":"","sources":["../../src/client/sessionBoundary.ts"],"names":[],"mappings":"AAEA,OAAO,EAA0C,KAAK,SAAS,EAAmC,MAAM,OAAO,CAAC;AAGhH,MAAM,WAAW,oBAAoB,CAAC,QAAQ,EAAE,OAAO;IACtD,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,SAAS,CAAC;IAClD,QAAQ,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,OAAO,CAAC;IACjD,QAAQ,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,MAAM,CAAC;IACjD,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,QAAQ,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;CAC/B;AAED,wBAAgB,iBAAiB,IAAI,MAAM,IAAI,CAE9C;AAsDD,wBAAgB,eAAe,CAAC,QAAQ,EAAE,OAAO,EAAE,EAClD,OAAO,EACP,QAAQ,EACR,OAAO,EACP,OAAO,EACP,SAAS,EACT,QAAQ,GACR,EAAE,oBAAoB,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,SAAS,CAUrD"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { RegistryContext } from "@effect/atom-react";
|
|
2
2
|
import * as AtomRegistry from "effect/unstable/reactivity/AtomRegistry";
|
|
3
3
|
import { createContext, createElement, Fragment, useContext, useEffect, useState } from "react";
|
|
4
|
+
import { DraftSessionContext } from "@shivaedev/effect-form/react.ts";
|
|
5
|
+
import { useDraftOwner } from "./useDraftOwner.js";
|
|
4
6
|
const RecheckContext = createContext(() => undefined);
|
|
5
7
|
export function useSessionRecheck() {
|
|
6
8
|
return useContext(RecheckContext);
|
|
@@ -44,8 +46,10 @@ function Generation({ session, connect, recheck, children }) {
|
|
|
44
46
|
return createElement(RecheckContext.Provider, { value: recheck }, createElement(RegistryContext.Provider, { value: owned.registry }, children(owned.client)));
|
|
45
47
|
}
|
|
46
48
|
export function SessionBoundary({ session, identify, connect, recheck, signedOut, children, }) {
|
|
49
|
+
const id = session === undefined ? undefined : identify(session);
|
|
50
|
+
const current = useDraftOwner(id);
|
|
47
51
|
return session === undefined
|
|
48
52
|
? createElement(Fragment, null, signedOut)
|
|
49
|
-
: createElement(Generation, { children, connect, key:
|
|
53
|
+
: createElement(DraftSessionContext.Provider, { value: current }, createElement(Generation, { children, connect, key: id, recheck, session }));
|
|
50
54
|
}
|
|
51
55
|
//# sourceMappingURL=sessionBoundary.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessionBoundary.js","sourceRoot":"","sources":["../../src/client/sessionBoundary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,YAAY,MAAM,yCAAyC,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,QAAQ,EAAkB,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"sessionBoundary.js","sourceRoot":"","sources":["../../src/client/sessionBoundary.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDraftOwner.d.ts","sourceRoot":"","sources":["../../src/client/useDraftOwner.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,YAAY,EAAgB,MAAM,wCAAwC,CAAC;AAuBzF,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,YAAY,GAAG,SAAS,CAiB9E"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useInsertionEffect, useRef, useState } from "react";
|
|
2
|
+
import { draftSession } from "@shivaedev/effect-form/draftSession.ts";
|
|
3
|
+
function owner(id, active) {
|
|
4
|
+
const session = draftSession(id);
|
|
5
|
+
let committed = active;
|
|
6
|
+
return {
|
|
7
|
+
activate: () => {
|
|
8
|
+
committed = true;
|
|
9
|
+
},
|
|
10
|
+
session: {
|
|
11
|
+
...session,
|
|
12
|
+
get active() {
|
|
13
|
+
return committed && session.active;
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function useDraftOwner(id) {
|
|
19
|
+
const [held, hold] = useState(() => (id === undefined ? undefined : owner(id, true)));
|
|
20
|
+
const committedRef = useRef(held);
|
|
21
|
+
let current = held;
|
|
22
|
+
if (current?.session.id !== id) {
|
|
23
|
+
current = id === undefined ? undefined : owner(id, committedRef.current === undefined);
|
|
24
|
+
hold(current);
|
|
25
|
+
}
|
|
26
|
+
// Insertion effects also run for Activity commits while layout and passive effects are disconnected.
|
|
27
|
+
useInsertionEffect(() => {
|
|
28
|
+
if (committedRef.current !== current) {
|
|
29
|
+
committedRef.current?.session.drop();
|
|
30
|
+
current?.activate();
|
|
31
|
+
committedRef.current = current;
|
|
32
|
+
}
|
|
33
|
+
}, [current]);
|
|
34
|
+
return current?.session;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=useDraftOwner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDraftOwner.js","sourceRoot":"","sources":["../../src/client/useDraftOwner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC7D,OAAO,EAAqB,YAAY,EAAE,MAAM,wCAAwC,CAAC;AAOzF,SAAS,KAAK,CAAC,EAAU,EAAE,MAAe;IACzC,MAAM,OAAO,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;IACjC,IAAI,SAAS,GAAG,MAAM,CAAC;IACvB,OAAO;QACN,QAAQ,EAAE,GAAG,EAAE;YACd,SAAS,GAAG,IAAI,CAAC;QAClB,CAAC;QACD,OAAO,EAAE;YACR,GAAG,OAAO;YACV,IAAI,MAAM;gBACT,OAAO,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;YACpC,CAAC;SACD;KACD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,EAAsB;IACnD,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IACtF,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,OAAO,GAAG,IAAI,CAAC;IACnB,IAAI,OAAO,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QAChC,OAAO,GAAG,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,YAAY,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC;QACvF,IAAI,CAAC,OAAO,CAAC,CAAC;IACf,CAAC;IACD,qGAAqG;IACrG,kBAAkB,CAAC,GAAG,EAAE;QACvB,IAAI,YAAY,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;YACtC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;YACrC,OAAO,EAAE,QAAQ,EAAE,CAAC;YACpB,YAAY,CAAC,OAAO,GAAG,OAAO,CAAC;QAChC,CAAC;IACF,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACd,OAAO,OAAO,EAAE,OAAO,CAAC;AACzB,CAAC"}
|
package/dist/submission.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { Effect, Schema } from "effect";
|
|
2
2
|
import type * as Atom from "effect/unstable/reactivity/Atom";
|
|
3
|
+
import type { PersistedDraftConfig } from "@shivaedev/effect-form/persistence.ts";
|
|
3
4
|
import type { Decoded, Encoded, FieldFailure, Fields, Form, Services } from "@shivaedev/effect-form/shape.ts";
|
|
4
5
|
import { type AtomServices, type RejectField } from "./field-rejection.ts";
|
|
5
6
|
export interface SubmissionConfig<F extends Fields, A, E, R, ER> {
|
|
6
7
|
readonly fields: Schema.Struct<F>;
|
|
7
8
|
readonly initialValues: Encoded<F>;
|
|
9
|
+
readonly persistedDraft?: PersistedDraftConfig | undefined;
|
|
8
10
|
readonly rejectField: RejectField<F, E> | undefined;
|
|
9
11
|
readonly runtime: Atom.AtomRuntime<Services<F, R>, ER>;
|
|
10
12
|
readonly save: (values: Decoded<F>) => Effect.Effect<A, E, AtomServices<R>>;
|
package/dist/submission.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"submission.d.ts","sourceRoot":"","sources":["../src/submission.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,KAAK,IAAI,MAAM,iCAAiC,CAAC;AAE7D,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAC9G,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAa,MAAM,sBAAsB,CAAC;AAEtF,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE;IAC9D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC;IACpD,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACvD,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;CAC5E;AAED,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE;IACrD,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,YAAY,EAAE,EAAE,CAAC,CAAC;IAChD,QAAQ,CAAC,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;CACjD;AAED,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,UAAU,gBAAgB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAG,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,
|
|
1
|
+
{"version":3,"file":"submission.d.ts","sourceRoot":"","sources":["../src/submission.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,KAAK,IAAI,MAAM,iCAAiC,CAAC;AAE7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAClF,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAC9G,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAa,MAAM,sBAAsB,CAAC;AAEtF,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE;IAC9D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAClC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACnC,QAAQ,CAAC,cAAc,CAAC,EAAE,oBAAoB,GAAG,SAAS,CAAC;IAC3D,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC;IACpD,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACvD,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;CAC5E;AAED,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE;IACrD,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,YAAY,EAAE,EAAE,CAAC,CAAC;IAChD,QAAQ,CAAC,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;CACjD;AAED,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,UAAU,gBAAgB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAG,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAc1H,CAAC"}
|
package/dist/submission.js
CHANGED
package/dist/submission.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"submission.js","sourceRoot":"","sources":["../src/submission.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"submission.js","sourceRoot":"","sources":["../src/submission.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,gCAAgC,CAAC;AAGtD,OAAO,EAAuC,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAgBtF,MAAM,CAAC,MAAM,UAAU,GAAG,CAAgC,MAAwC,EAA2B,EAAE;IAC9H,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;IAClD,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;IAChE,IAAI,SAAiC,CAAC;IACtC,MAAM,IAAI,GAAqC,IAAI,CAAC,MAAM,EAAE;QAC3D,aAAa;QACb,QAAQ,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE;YACzC,SAAS,GAAG,QAAQ,CAAC;YACrB,OAAO,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAChC,CAAC;QACD,cAAc,EAAE,MAAM,CAAC,cAAc;QACrC,OAAO;KACP,CAAC,CAAC;IACH,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,SAAS,EAAE,CAAC;AAC7C,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shivaedev/effect-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "React query, action, editor and session hooks for native Effect atoms",
|
|
5
5
|
"homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/effect-react#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -129,14 +129,14 @@
|
|
|
129
129
|
"README.md"
|
|
130
130
|
],
|
|
131
131
|
"dependencies": {
|
|
132
|
-
"@shivaedev/effect-contract": "0.4.1"
|
|
132
|
+
"@shivaedev/effect-contract": "0.4.1",
|
|
133
|
+
"@shivaedev/effect-form": "0.3.0"
|
|
133
134
|
},
|
|
134
135
|
"devDependencies": {
|
|
135
136
|
"@effect/atom-react": "4.0.0-rc.112",
|
|
136
137
|
"@effect/platform-node": "4.0.0-rc.112",
|
|
137
138
|
"@effect/sql-pg": "4.0.0-rc.112",
|
|
138
139
|
"@effect/sql-sqlite-node": "4.0.0-rc.112",
|
|
139
|
-
"@shivaedev/effect-form": "0.2.1",
|
|
140
140
|
"@shivaedev/effect-pg-boss": "0.4.0",
|
|
141
141
|
"@shivaedev/effect-service": "0.2.0",
|
|
142
142
|
"@shivaedev/effect-sql": "0.2.1",
|
|
@@ -147,29 +147,25 @@
|
|
|
147
147
|
"@shivaedev/test-story": "0.1.1",
|
|
148
148
|
"@tanstack/react-router": "1.170.41",
|
|
149
149
|
"@types/node": "24.19.1",
|
|
150
|
-
"@types/react": "19.
|
|
151
|
-
"@types/react-dom": "19.
|
|
150
|
+
"@types/react": "19.3.0",
|
|
151
|
+
"@types/react-dom": "19.3.0",
|
|
152
152
|
"@typescript/native": "npm:typescript@7.0.2",
|
|
153
153
|
"effect": "4.0.0-rc.112",
|
|
154
154
|
"happy-dom": "20.14.5",
|
|
155
|
-
"react": "19.
|
|
156
|
-
"react-dom": "19.
|
|
155
|
+
"react": "19.3.0",
|
|
156
|
+
"react-dom": "19.3.0",
|
|
157
157
|
"typescript": "npm:@typescript/typescript6@6.0.2",
|
|
158
158
|
"vitest": "4.1.11"
|
|
159
159
|
},
|
|
160
160
|
"peerDependencies": {
|
|
161
161
|
"@effect/atom-react": "4.0.0-rc.112",
|
|
162
|
-
"@shivaedev/effect-form": "0.2.1",
|
|
163
162
|
"@tanstack/react-router": "1.170.41",
|
|
164
163
|
"effect": "4.0.0-rc.112",
|
|
165
|
-
"react": "19.
|
|
166
|
-
"react-dom": "19.
|
|
164
|
+
"react": "19.3.0",
|
|
165
|
+
"react-dom": "19.3.0",
|
|
167
166
|
"vitest": "4.1.11"
|
|
168
167
|
},
|
|
169
168
|
"peerDependenciesMeta": {
|
|
170
|
-
"@shivaedev/effect-form": {
|
|
171
|
-
"optional": true
|
|
172
|
-
},
|
|
173
169
|
"@tanstack/react-router": {
|
|
174
170
|
"optional": true
|
|
175
171
|
},
|
package/src/client/create.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { type Effect, Equal, Option, type Schema } from "effect";
|
|
2
2
|
import type * as Atom from "effect/unstable/reactivity/Atom";
|
|
3
3
|
import { useCallback, useState } from "react";
|
|
4
|
+
import { type BrowserDraftConfig, usePersistedDraft } from "@shivaedev/effect-form/react.ts";
|
|
4
5
|
import type { Decoded, Encoded, FieldFailure, Fields, Form, Services } from "@shivaedev/effect-form/shape.ts";
|
|
5
6
|
import type { AtomServices, FieldRejectionMapping } from "#field-rejection.ts";
|
|
6
7
|
import { submission } from "#submission.ts";
|
|
7
8
|
import { type SaveState, useSaveState } from "./saveState.ts";
|
|
8
9
|
export type CreateConfig<TFields extends Fields, TCreated, TSaveError, R, TRuntimeError> = {
|
|
10
|
+
readonly persistedDraft?: BrowserDraftConfig | undefined;
|
|
9
11
|
readonly fields: Schema.Struct<TFields>;
|
|
10
12
|
readonly initialValues: Encoded<TFields>;
|
|
11
13
|
readonly create: (values: Decoded<TFields>) => Effect.Effect<TCreated, TSaveError, AtomServices<R>>;
|
|
@@ -22,9 +24,10 @@ function editedSince<TV extends object>(current: TV, submitted: TV | undefined):
|
|
|
22
24
|
export function useCreate<TFields extends Fields, TCreated, TSaveError, R, TRuntimeError>(
|
|
23
25
|
config: CreateConfig<TFields, TCreated, TSaveError, R, TRuntimeError>,
|
|
24
26
|
): Create<TFields, TCreated, TSaveError, TRuntimeError> {
|
|
27
|
+
const persistedDraft = usePersistedDraft(config.persistedDraft);
|
|
25
28
|
const [{ initialValues: baseline, fresh }] = useState(() => {
|
|
26
29
|
const { fields, initialValues, runtime, create, rejectField } = config;
|
|
27
|
-
return { fresh: () => submission({ fields, initialValues, rejectField, runtime, save: create }), initialValues };
|
|
30
|
+
return { fresh: () => submission({ fields, initialValues, persistedDraft, rejectField, runtime, save: create }), initialValues };
|
|
28
31
|
});
|
|
29
32
|
const [current, replace] = useState(fresh);
|
|
30
33
|
const [created, record] = useState<Option.Option<TCreated>>(Option.none());
|
package/src/client/editor.ts
CHANGED
|
@@ -2,12 +2,14 @@ import { type Effect, Option, type Schema } from "effect";
|
|
|
2
2
|
import type * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
|
|
3
3
|
import type * as Atom from "effect/unstable/reactivity/Atom";
|
|
4
4
|
import { useCallback, useEffect, useState } from "react";
|
|
5
|
+
import { type BrowserDraftConfig, usePersistedDraft } from "@shivaedev/effect-form/react.ts";
|
|
5
6
|
import type { Decoded, Encoded, FieldFailure, Fields, Form, Services } from "@shivaedev/effect-form/shape.ts";
|
|
6
7
|
import type { AtomServices, FieldRejectionMapping } from "#field-rejection.ts";
|
|
7
8
|
import { submission } from "#submission.ts";
|
|
8
9
|
import { type QueryState, useQuery } from "./resultState.ts";
|
|
9
10
|
import { type SaveState, useSaveState } from "./saveState.ts";
|
|
10
11
|
export type EditorConfig<TFields extends Fields, TRow, TQueryError, TSaveError, R, TRuntimeError> = {
|
|
12
|
+
readonly persistedDraft?: BrowserDraftConfig | undefined;
|
|
11
13
|
readonly query: Atom.Atom<AsyncResult.AsyncResult<TRow, TQueryError>>;
|
|
12
14
|
readonly fields: Schema.Struct<TFields>;
|
|
13
15
|
readonly values: (row: TRow) => Encoded<TFields>;
|
|
@@ -26,6 +28,7 @@ interface Held<TFields extends Fields, TRow, TSaveError, TRuntimeError> {
|
|
|
26
28
|
export function useEditor<TFields extends Fields, TRow, TQueryError, TSaveError, R, TRuntimeError>(
|
|
27
29
|
config: EditorConfig<TFields, TRow, TQueryError, TSaveError, R, TRuntimeError>,
|
|
28
30
|
): Editor<TFields, TRow, TQueryError, TSaveError, TRuntimeError> {
|
|
31
|
+
const persistedDraft = usePersistedDraft(config.persistedDraft);
|
|
29
32
|
const query = useQuery(config.query);
|
|
30
33
|
const row = Option.getOrUndefined(query.data);
|
|
31
34
|
const [held, hold] = useState<Held<TFields, TRow, TSaveError, TRuntimeError> | undefined>(undefined);
|
|
@@ -35,6 +38,7 @@ export function useEditor<TFields extends Fields, TRow, TQueryError, TSaveError,
|
|
|
35
38
|
form: submission({
|
|
36
39
|
fields: config.fields,
|
|
37
40
|
initialValues: config.values(row),
|
|
41
|
+
persistedDraft,
|
|
38
42
|
rejectField: config.rejectField,
|
|
39
43
|
runtime: config.runtime,
|
|
40
44
|
save: config.save,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { RegistryContext } from "@effect/atom-react";
|
|
2
2
|
import * as AtomRegistry from "effect/unstable/reactivity/AtomRegistry";
|
|
3
3
|
import { createContext, createElement, Fragment, type ReactNode, useContext, useEffect, useState } from "react";
|
|
4
|
+
import { DraftSessionContext } from "@shivaedev/effect-form/react.ts";
|
|
5
|
+
import { useDraftOwner } from "./useDraftOwner.ts";
|
|
4
6
|
export interface SessionBoundaryProps<TSession, TClient> {
|
|
5
7
|
readonly children: (client: TClient) => ReactNode;
|
|
6
8
|
readonly connect: (session: TSession) => TClient;
|
|
@@ -74,7 +76,13 @@ export function SessionBoundary<TSession, TClient>({
|
|
|
74
76
|
signedOut,
|
|
75
77
|
children,
|
|
76
78
|
}: SessionBoundaryProps<TSession, TClient>): ReactNode {
|
|
79
|
+
const id = session === undefined ? undefined : identify(session);
|
|
80
|
+
const current = useDraftOwner(id);
|
|
77
81
|
return session === undefined
|
|
78
82
|
? createElement(Fragment, null, signedOut)
|
|
79
|
-
: createElement(
|
|
83
|
+
: createElement(
|
|
84
|
+
DraftSessionContext.Provider,
|
|
85
|
+
{ value: current },
|
|
86
|
+
createElement(Generation<TSession, TClient>, { children, connect, key: id, recheck, session }),
|
|
87
|
+
);
|
|
80
88
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useInsertionEffect, useRef, useState } from "react";
|
|
2
|
+
import { type DraftSession, draftSession } from "@shivaedev/effect-form/draftSession.ts";
|
|
3
|
+
|
|
4
|
+
interface DraftOwner {
|
|
5
|
+
readonly activate: () => void;
|
|
6
|
+
readonly session: DraftSession;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function owner(id: string, active: boolean): DraftOwner {
|
|
10
|
+
const session = draftSession(id);
|
|
11
|
+
let committed = active;
|
|
12
|
+
return {
|
|
13
|
+
activate: () => {
|
|
14
|
+
committed = true;
|
|
15
|
+
},
|
|
16
|
+
session: {
|
|
17
|
+
...session,
|
|
18
|
+
get active() {
|
|
19
|
+
return committed && session.active;
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function useDraftOwner(id: string | undefined): DraftSession | undefined {
|
|
26
|
+
const [held, hold] = useState(() => (id === undefined ? undefined : owner(id, true)));
|
|
27
|
+
const committedRef = useRef(held);
|
|
28
|
+
let current = held;
|
|
29
|
+
if (current?.session.id !== id) {
|
|
30
|
+
current = id === undefined ? undefined : owner(id, committedRef.current === undefined);
|
|
31
|
+
hold(current);
|
|
32
|
+
}
|
|
33
|
+
// Insertion effects also run for Activity commits while layout and passive effects are disconnected.
|
|
34
|
+
useInsertionEffect(() => {
|
|
35
|
+
if (committedRef.current !== current) {
|
|
36
|
+
committedRef.current?.session.drop();
|
|
37
|
+
current?.activate();
|
|
38
|
+
committedRef.current = current;
|
|
39
|
+
}
|
|
40
|
+
}, [current]);
|
|
41
|
+
return current?.session;
|
|
42
|
+
}
|
package/src/submission.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type { Effect, Schema } from "effect";
|
|
2
2
|
import type * as Atom from "effect/unstable/reactivity/Atom";
|
|
3
3
|
import { make } from "@shivaedev/effect-form/form.ts";
|
|
4
|
+
import type { PersistedDraftConfig } from "@shivaedev/effect-form/persistence.ts";
|
|
4
5
|
import type { Decoded, Encoded, FieldFailure, Fields, Form, Services } from "@shivaedev/effect-form/shape.ts";
|
|
5
6
|
import { type AtomServices, type RejectField, rejecting } from "./field-rejection.ts";
|
|
6
7
|
|
|
7
8
|
export interface SubmissionConfig<F extends Fields, A, E, R, ER> {
|
|
8
9
|
readonly fields: Schema.Struct<F>;
|
|
9
10
|
readonly initialValues: Encoded<F>;
|
|
11
|
+
readonly persistedDraft?: PersistedDraftConfig | undefined;
|
|
10
12
|
readonly rejectField: RejectField<F, E> | undefined;
|
|
11
13
|
readonly runtime: Atom.AtomRuntime<Services<F, R>, ER>;
|
|
12
14
|
readonly save: (values: Decoded<F>) => Effect.Effect<A, E, AtomServices<R>>;
|
|
@@ -27,6 +29,7 @@ export const submission = <F extends Fields, A, E, R, ER>(config: SubmissionConf
|
|
|
27
29
|
submitted = snapshot;
|
|
28
30
|
return save(values, submitter);
|
|
29
31
|
},
|
|
32
|
+
persistedDraft: config.persistedDraft,
|
|
30
33
|
runtime,
|
|
31
34
|
});
|
|
32
35
|
return { form, submitted: () => submitted };
|