@archipelago-js/react 0.1.0 → 0.2.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @archipelago-js/react
2
2
 
3
- React bindings for Archipelago islands.
3
+ React bindings for [Archipelago](https://github.com/robrace/archipelago) islands — server-rendered micro-frontends powered by Rails and React.
4
4
 
5
5
  ## Install
6
6
 
@@ -8,10 +8,254 @@ React bindings for Archipelago islands.
8
8
  yarn add @archipelago-js/react @archipelago-js/client react react-dom
9
9
  ```
10
10
 
11
- ## API
11
+ ## 60-Second Quick Start
12
12
 
13
- - `bootArchipelagoIslands(registry)`
14
- - `useIslandProps(options?)`
15
- - `useIslandForm(options)`
16
- - `IslandProvider`
17
- - `ErrorBoundary`
13
+ ### 1. Rails View Helper
14
+
15
+ Render an island from any ERB template:
16
+
17
+ ```erb
18
+ <%= archipelago_island("TeamMembers", props: { team_id: @team.id }, stream: "team:#{@team.id}") %>
19
+ ```
20
+
21
+ ### 2. React Island Component
22
+
23
+ Create `app/javascript/islands/TeamMembers.tsx`:
24
+
25
+ ```tsx
26
+ import { useIslandProps, useIslandForm } from "@archipelago-js/react"
27
+
28
+ export default function TeamMembers() {
29
+ const { props } = useIslandProps()
30
+ const form = useIslandForm({ initialData: { name: "" } })
31
+
32
+ return (
33
+ <div>
34
+ <h2>Team #{props.team_id}</h2>
35
+ <input
36
+ value={form.data.name}
37
+ onChange={(e) => form.setData("name", e.target.value)}
38
+ />
39
+ <button onClick={() => form.post("add_member")} disabled={form.processing}>
40
+ {form.recentlySuccessful ? "Added!" : "Add Member"}
41
+ </button>
42
+ {form.errors.name?.map((e) => <p key={e}>{e}</p>)}
43
+ </div>
44
+ )
45
+ }
46
+ ```
47
+
48
+ ### 3. Boot Islands
49
+
50
+ In your JS entry file:
51
+
52
+ ```tsx
53
+ import { bootArchipelagoIslands } from "@archipelago-js/react"
54
+ import TeamMembers from "../islands/TeamMembers"
55
+
56
+ void bootArchipelagoIslands({
57
+ TeamMembers
58
+ })
59
+ ```
60
+
61
+ ## Context
62
+
63
+ Every island is wrapped in an `IslandProvider` that provides context via `useIslandContext()`:
64
+
65
+ | Field | Type | Description |
66
+ |-------------|--------------------------------|---------------------------------------------|
67
+ | `component` | `string` | Island component name |
68
+ | `params` | `Record<string, unknown>` | Server-side params embedded in the HTML |
69
+ | `instance` | `string \| undefined` | Optional instance identifier |
70
+ | `stream` | `string \| undefined` | ActionCable stream name for live updates |
71
+ | `state` | `{ props, version }` | Current props and version |
72
+ | `setState` | `Dispatch<SetStateAction<…>>` | Update island state |
73
+
74
+ `useIslandProps()` is the primary way to read props:
75
+
76
+ ```tsx
77
+ const { props, setProps, version } = useIslandProps()
78
+ ```
79
+
80
+ ## Forms
81
+
82
+ ### `useIslandForm` (Controlled)
83
+
84
+ The form hook manages data, errors, processing state, and submission:
85
+
86
+ ```tsx
87
+ const form = useIslandForm({
88
+ initialData: { email: "", role: "member" },
89
+ clearFieldErrorsOnChange: true,
90
+ recentlySuccessfulDuration: 2000,
91
+ transform: (data) => ({ ...data, email: data.email.toLowerCase() }),
92
+ onSuccess: (response) => console.log("Saved!", response),
93
+ onError: (response) => console.log("Validation failed", response),
94
+ onFinish: () => console.log("Request complete")
95
+ })
96
+ ```
97
+
98
+ **Returned values:**
99
+
100
+ | Property | Type | Description |
101
+ |------------------------|-----------------------------------------|--------------------------------------------------|
102
+ | `data` | `TData` | Current form data |
103
+ | `setData(field, val)` | `(field, value) => void` | Set a field value |
104
+ | `errors` | `Record<string, string[]>` | Field-keyed validation errors |
105
+ | `setError(field, msg)` | `(field, message) => void` | Manually set a field error |
106
+ | `clearErrors(...f?)` | `(...fields?) => void` | Clear all or specific errors |
107
+ | `processing` | `boolean` | True while request is in flight |
108
+ | `wasSuccessful` | `boolean` | True after an ok/redirect response |
109
+ | `recentlySuccessful` | `boolean` | True for `recentlySuccessfulDuration` ms after success |
110
+ | `progress` | `{ percentage } \| null` | Upload progress (future XHR support) |
111
+ | `transportError` | `Error \| null` | Network/parse error (not a validation error) |
112
+ | `defaults(...)` | Getter/setter for default values | `defaults()` returns defaults; `defaults(next)` updates them |
113
+ | `reset(...fields?)` | `(...fields?) => void` | Reset all or specific fields to defaults |
114
+ | `resetAndClearErrors` | `(...fields?) => void` | Reset data + clear errors |
115
+ | `post(op, overrides?)` | `(operation, overrides?) => Promise` | Submit with POST |
116
+ | `put` / `patch` / `delete` | Same as `post` | Submit with PUT / PATCH / DELETE |
117
+
118
+ ### `<IslandForm>` (Declarative / Uncontrolled)
119
+
120
+ For simple forms that don't need keystroke-level control:
121
+
122
+ ```tsx
123
+ import { IslandForm } from "@archipelago-js/react"
124
+
125
+ <IslandForm operation="update_settings" method="patch" resetOnSuccess>
126
+ <input name="display_name" defaultValue={props.display_name} />
127
+ <button type="submit">Save</button>
128
+ </IslandForm>
129
+ ```
130
+
131
+ Render-prop children get access to the full form object:
132
+
133
+ ```tsx
134
+ <IslandForm operation="create_post" method="post">
135
+ {(form) => (
136
+ <>
137
+ <input name="title" />
138
+ {form.errors.title?.map((e) => <span key={e}>{e}</span>)}
139
+ <button type="submit" disabled={form.processing}>
140
+ {form.recentlySuccessful ? "Created!" : "Create"}
141
+ </button>
142
+ </>
143
+ )}
144
+ </IslandForm>
145
+ ```
146
+
147
+ **Props:**
148
+
149
+ | Prop | Type | Default |
150
+ |------------------------|--------------------------|-----------|
151
+ | `operation` | `string` | required |
152
+ | `method` | `FormMethod` | `"post"` |
153
+ | `transform` | `(data) => data` | — |
154
+ | `resetOnSuccess` | `boolean` | `false` |
155
+ | `clearErrorsOnSuccess` | `boolean` | `false` |
156
+ | `fixedParams` | `Record<string, unknown>`| — |
157
+ | `onSuccess` | `(response) => void` | — |
158
+ | `onError` | `(response) => void` | — |
159
+ | `onForbidden` | `(response) => void` | — |
160
+ | `onFinish` | `(response?) => void` | — |
161
+
162
+ ## Streams (Live Updates via ActionCable)
163
+
164
+ Islands with a `stream` attribute automatically subscribe to an ActionCable channel. When a broadcast arrives, `useIslandProps()` updates the props in real time.
165
+
166
+ ### Setup
167
+
168
+ Assign an ActionCable consumer globally:
169
+
170
+ ```ts
171
+ import { createConsumer } from "@rails/actioncable"
172
+ window.Archipelago = { cable: createConsumer() }
173
+ ```
174
+
175
+ ### Server Broadcast
176
+
177
+ ```ruby
178
+ Archipelago.broadcast("team:#{team.id}", props: { members: team.members.as_json })
179
+ ```
180
+
181
+ ### Custom Merge Logic
182
+
183
+ Use `onLiveProps` to merge incoming props with the current state:
184
+
185
+ ```tsx
186
+ const { props } = useIslandProps({
187
+ onLiveProps: (next, previous) => ({
188
+ ...previous,
189
+ members: next.members
190
+ })
191
+ })
192
+ ```
193
+
194
+ ## Lazy Islands (`defineIslandLoader`)
195
+
196
+ Code-split islands so their JavaScript is only fetched when the island appears in the DOM:
197
+
198
+ ```tsx
199
+ import { defineIslandLoader, bootArchipelagoIslands } from "@archipelago-js/react"
200
+
201
+ void bootArchipelagoIslands({
202
+ TeamMembers: defineIslandLoader(
203
+ () => import("../islands/TeamMembers"),
204
+ <div>Loading team...</div> // optional fallback
205
+ )
206
+ })
207
+ ```
208
+
209
+ The Rails generator supports `--lazy_registry` to scaffold a lazy registry automatically:
210
+
211
+ ```bash
212
+ bin/rails generate archipelago:install:react --lazy_registry
213
+ ```
214
+
215
+ ## Turbo Lifecycle
216
+
217
+ Archipelago integrates seamlessly with Turbo Drive and Turbo Frames:
218
+
219
+ - **`turbo:load`** / **`turbo:render`** / **`turbo:frame-load`** — Automatically boots any new `[data-island]` elements added to the page.
220
+ - **`turbo:before-cache`** — All mounted islands are unmounted before Turbo caches the page, preventing stale React trees from persisting in the snapshot.
221
+ - **MutationObserver** — A DOM observer detects dynamically inserted islands (e.g. from Turbo Streams or manual DOM manipulation) and boots them automatically.
222
+
223
+ No extra configuration is needed. Just call `bootArchipelagoIslands(registry)` once in your entry file.
224
+
225
+ ## Error Handling
226
+
227
+ ### `FORM_ERROR`
228
+
229
+ The `@archipelago-js/client` package exports a `FORM_ERROR` constant (`"_base"`) for base-level form errors:
230
+
231
+ ```tsx
232
+ import { FORM_ERROR } from "@archipelago-js/client"
233
+
234
+ {form.errors[FORM_ERROR]?.map((e) => <p key={e}>{e}</p>)}
235
+ ```
236
+
237
+ ### `ArchipelagoTransportError`
238
+
239
+ Network failures, HTML responses, and JSON parse errors are wrapped in `ArchipelagoTransportError` (also exported from `@archipelago-js/client`). The form hook captures these into `form.transportError`.
240
+
241
+ ### `ErrorBoundary`
242
+
243
+ Each island is wrapped in an `ErrorBoundary`. If an island crashes, the error is contained and other islands continue to function.
244
+
245
+ ## API Reference
246
+
247
+ | Export | Package | Description |
248
+ |-------------------------------|----------|------------------------------------------|
249
+ | `bootArchipelagoIslands` | react | Mount all `[data-island]` elements |
250
+ | `unmountArchipelagoIslands` | react | Tear down all mounted islands |
251
+ | `defineIslandLoader` | react | Create a lazy-loaded registry entry |
252
+ | `useIslandProps` | react | Read/subscribe to island props |
253
+ | `useIslandForm` | react | Controlled form state and submission |
254
+ | `IslandForm` | react | Declarative form component |
255
+ | `IslandProvider` | react | Context provider (used internally) |
256
+ | `useIslandContext` | react | Access raw island context |
257
+ | `ErrorBoundary` | react | Per-island error boundary |
258
+ | `islandFetch` | client | Low-level island RPC call |
259
+ | `FORM_ERROR` | client | `"_base"` constant for base errors |
260
+ | `ArchipelagoTransportError` | client | Typed transport error class |
261
+ | `parseIslandResponse` | client | Parse raw JSON into typed response |
@@ -0,0 +1,16 @@
1
+ import React from "react";
2
+ import { useIslandForm, type UseIslandFormCallbacks } from "./useIslandForm";
3
+ type FormMethod = "post" | "put" | "patch" | "delete";
4
+ type IslandFormRenderProps = ReturnType<typeof useIslandForm>;
5
+ export interface IslandFormProps extends UseIslandFormCallbacks<Record<string, unknown>> {
6
+ operation: string;
7
+ method?: FormMethod;
8
+ transform?: (payload: Record<string, unknown>) => Record<string, unknown>;
9
+ resetOnSuccess?: boolean;
10
+ clearErrorsOnSuccess?: boolean;
11
+ fixedParams?: Record<string, unknown>;
12
+ className?: string;
13
+ children: React.ReactNode | ((form: IslandFormRenderProps) => React.ReactNode);
14
+ }
15
+ export declare function IslandForm({ operation, method, transform, resetOnSuccess, clearErrorsOnSuccess, fixedParams, className, children, onSuccess, onError, onForbidden, onFinish }: IslandFormProps): React.ReactElement;
16
+ export {};
@@ -0,0 +1,51 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCallback, useRef } from "react";
3
+ import { useIslandForm } from "./useIslandForm";
4
+ function formDataToObject(formData) {
5
+ const result = {};
6
+ formData.forEach((value, key) => {
7
+ const existing = result[key];
8
+ if (existing !== undefined) {
9
+ if (Array.isArray(existing)) {
10
+ existing.push(value);
11
+ }
12
+ else {
13
+ result[key] = [existing, value];
14
+ }
15
+ }
16
+ else {
17
+ result[key] = value;
18
+ }
19
+ });
20
+ return result;
21
+ }
22
+ export function IslandForm({ operation, method = "post", transform, resetOnSuccess = false, clearErrorsOnSuccess = false, fixedParams, className, children, onSuccess, onError, onForbidden, onFinish }) {
23
+ const formRef = useRef(null);
24
+ const form = useIslandForm({
25
+ initialData: {},
26
+ fixedParams,
27
+ transform,
28
+ onSuccess: (response) => {
29
+ if (resetOnSuccess) {
30
+ form.reset();
31
+ formRef.current?.reset();
32
+ }
33
+ if (clearErrorsOnSuccess) {
34
+ form.clearErrors();
35
+ }
36
+ onSuccess?.(response);
37
+ },
38
+ onError,
39
+ onForbidden,
40
+ onFinish
41
+ });
42
+ const handleSubmit = useCallback((event) => {
43
+ event.preventDefault();
44
+ const formData = new FormData(event.currentTarget);
45
+ const payload = formDataToObject(formData);
46
+ const submitFn = form[method];
47
+ submitFn(operation, { payload });
48
+ }, [form, method, operation]);
49
+ return (_jsx("form", { ref: formRef, onSubmit: handleSubmit, className: className, children: typeof children === "function" ? children(form) : children }));
50
+ }
51
+ //# sourceMappingURL=IslandForm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IslandForm.js","sourceRoot":"","sources":["../src/IslandForm.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAElD,OAAO,EAAE,aAAa,EAA+B,MAAM,iBAAiB,CAAA;AAiB5E,SAAS,gBAAgB,CAAC,QAAkB;IAC1C,MAAM,MAAM,GAA4B,EAAE,CAAA;IAE1C,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QAC9B,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;QAC5B,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC5B,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YACtB,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;YACjC,CAAC;QACH,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;QACrB,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,OAAO,MAAM,CAAA;AACf,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,EACzB,SAAS,EACT,MAAM,GAAG,MAAM,EACf,SAAS,EACT,cAAc,GAAG,KAAK,EACtB,oBAAoB,GAAG,KAAK,EAC5B,WAAW,EACX,SAAS,EACT,QAAQ,EACR,SAAS,EACT,OAAO,EACP,WAAW,EACX,QAAQ,EACQ;IAChB,MAAM,OAAO,GAAG,MAAM,CAAkB,IAAI,CAAC,CAAA;IAE7C,MAAM,IAAI,GAAG,aAAa,CAA0B;QAClD,WAAW,EAAE,EAAE;QACf,WAAW;QACX,SAAS;QACT,SAAS,EAAE,CAAC,QAAQ,EAAE,EAAE;YACtB,IAAI,cAAc,EAAE,CAAC;gBACnB,IAAI,CAAC,KAAK,EAAE,CAAA;gBACZ,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,CAAA;YAC1B,CAAC;YACD,IAAI,oBAAoB,EAAE,CAAC;gBACzB,IAAI,CAAC,WAAW,EAAE,CAAA;YACpB,CAAC;YACD,SAAS,EAAE,CAAC,QAAQ,CAAC,CAAA;QACvB,CAAC;QACD,OAAO;QACP,WAAW;QACX,QAAQ;KACT,CAAC,CAAA;IAEF,MAAM,YAAY,GAAG,WAAW,CAC9B,CAAC,KAAuC,EAAE,EAAE;QAC1C,KAAK,CAAC,cAAc,EAAE,CAAA;QAEtB,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,aAAa,CAAC,CAAA;QAClD,MAAM,OAAO,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAA;QAE1C,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,CAAA;QAC7B,QAAQ,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,CAAA;IAClC,CAAC,EACD,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,CAC1B,CAAA;IAED,OAAO,CACL,eAAM,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,YAC7D,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,GACtD,CACR,CAAA;AACH,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./ErrorBoundary";
2
+ export * from "./IslandForm";
2
3
  export * from "./actionCable";
3
4
  export * from "./bootstrapper";
4
5
  export * from "./context";
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./ErrorBoundary";
2
+ export * from "./IslandForm";
2
3
  export * from "./actionCable";
3
4
  export * from "./bootstrapper";
4
5
  export * from "./context";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,WAAW,CAAA;AACzB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,kBAAkB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,cAAc,CAAA;AAC5B,cAAc,eAAe,CAAA;AAC7B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,WAAW,CAAA;AACzB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,kBAAkB,CAAA"}
@@ -1,21 +1,41 @@
1
+ import { type IslandResponse } from "@archipelago-js/client";
1
2
  type SubmitOverrides = {
2
3
  payload?: Record<string, unknown>;
3
4
  navigate?: (location: string) => void;
4
5
  };
5
- export interface UseIslandFormOptions<TData extends Record<string, unknown>> {
6
+ export interface UseIslandFormCallbacks<TData extends Record<string, unknown>> {
7
+ onSuccess?: (response: IslandResponse) => void;
8
+ onError?: (response: IslandResponse) => void;
9
+ onForbidden?: (response: IslandResponse) => void;
10
+ onFinish?: (response: IslandResponse | undefined) => void;
11
+ }
12
+ export interface UseIslandFormOptions<TData extends Record<string, unknown>> extends UseIslandFormCallbacks<TData> {
6
13
  initialData: TData;
7
14
  clearFieldErrorsOnChange?: boolean;
8
15
  fixedParams?: Record<string, unknown>;
16
+ recentlySuccessfulDuration?: number;
17
+ transform?: (payload: TData) => Record<string, unknown>;
9
18
  }
10
- export declare function useIslandForm<TData extends Record<string, unknown>>({ initialData, clearFieldErrorsOnChange, fixedParams }: UseIslandFormOptions<TData>): {
19
+ export type UploadProgress = {
20
+ percentage: number;
21
+ };
22
+ export declare function useIslandForm<TData extends Record<string, unknown>>({ initialData, clearFieldErrorsOnChange, fixedParams, recentlySuccessfulDuration, transform, onSuccess, onError, onForbidden, onFinish }: UseIslandFormOptions<TData>): {
11
23
  data: TData;
12
24
  setData: <K extends keyof TData>(field: K, value: TData[K]) => void;
13
25
  errors: Record<string, string[]>;
26
+ setError: (field: keyof TData | string, message: string | string[]) => void;
27
+ clearErrors: (...fields: (keyof TData)[]) => void;
14
28
  processing: boolean;
15
- post: (operation: string, overrides?: SubmitOverrides) => Promise<import("@archipelago-js/client").IslandResponse | undefined>;
16
- put: (operation: string, overrides?: SubmitOverrides) => Promise<import("@archipelago-js/client").IslandResponse | undefined>;
17
- patch: (operation: string, overrides?: SubmitOverrides) => Promise<import("@archipelago-js/client").IslandResponse | undefined>;
18
- delete: (operation: string, overrides?: SubmitOverrides) => Promise<import("@archipelago-js/client").IslandResponse | undefined>;
19
- reset: () => void;
29
+ wasSuccessful: boolean;
30
+ recentlySuccessful: boolean;
31
+ progress: UploadProgress | null;
32
+ transportError: Error | null;
33
+ defaults: (...args: [] | [Partial<TData>] | [keyof TData, TData[keyof TData]]) => TData;
34
+ reset: (...fields: (keyof TData)[]) => void;
35
+ resetAndClearErrors: (...fields: (keyof TData)[]) => void;
36
+ post: (operation: string, overrides?: SubmitOverrides) => Promise<IslandResponse | undefined>;
37
+ put: (operation: string, overrides?: SubmitOverrides) => Promise<IslandResponse | undefined>;
38
+ patch: (operation: string, overrides?: SubmitOverrides) => Promise<IslandResponse | undefined>;
39
+ delete: (operation: string, overrides?: SubmitOverrides) => Promise<IslandResponse | undefined>;
20
40
  };
21
41
  export {};
@@ -1,12 +1,37 @@
1
1
  import { islandFetch } from "@archipelago-js/client";
2
- import { useCallback, useMemo, useRef, useState } from "react";
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
3
  import { useIslandContext } from "./context";
4
- export function useIslandForm({ initialData, clearFieldErrorsOnChange = true, fixedParams = {} }) {
4
+ function deepClone(value) {
5
+ if (typeof structuredClone === "function") {
6
+ return structuredClone(value);
7
+ }
8
+ return JSON.parse(JSON.stringify(value));
9
+ }
10
+ function isFormDataLike(value) {
11
+ return typeof FormData !== "undefined" && value instanceof FormData;
12
+ }
13
+ export function useIslandForm({ initialData, clearFieldErrorsOnChange = true, fixedParams = {}, recentlySuccessfulDuration = 2000, transform, onSuccess, onError, onForbidden, onFinish }) {
5
14
  const { component, params, stream, setState } = useIslandContext();
6
15
  const [data, setDataState] = useState(initialData);
7
16
  const [errors, setErrors] = useState({});
8
17
  const [processing, setProcessing] = useState(false);
18
+ const [wasSuccessful, setWasSuccessful] = useState(false);
19
+ const [recentlySuccessful, setRecentlySuccessful] = useState(false);
20
+ const [progress, setProgress] = useState(null);
21
+ const [transportError, setTransportError] = useState(null);
9
22
  const requestRef = useRef(null);
23
+ const defaultsRef = useRef(deepClone(initialData));
24
+ const recentlySuccessfulTimerRef = useRef(null);
25
+ const mountedRef = useRef(true);
26
+ useEffect(() => {
27
+ mountedRef.current = true;
28
+ return () => {
29
+ mountedRef.current = false;
30
+ if (recentlySuccessfulTimerRef.current) {
31
+ clearTimeout(recentlySuccessfulTimerRef.current);
32
+ }
33
+ };
34
+ }, []);
10
35
  const setData = useCallback((field, value) => {
11
36
  setDataState((previous) => ({
12
37
  ...previous,
@@ -23,72 +48,194 @@ export function useIslandForm({ initialData, clearFieldErrorsOnChange = true, fi
23
48
  });
24
49
  }
25
50
  }, [clearFieldErrorsOnChange]);
51
+ const clearErrors = useCallback((...fields) => {
52
+ if (fields.length === 0) {
53
+ setErrors({});
54
+ return;
55
+ }
56
+ setErrors((previous) => {
57
+ const next = { ...previous };
58
+ for (const field of fields) {
59
+ delete next[field];
60
+ }
61
+ return next;
62
+ });
63
+ }, []);
64
+ const setError = useCallback((field, message) => {
65
+ const messages = Array.isArray(message) ? message : [message];
66
+ setErrors((previous) => ({
67
+ ...previous,
68
+ [field]: messages
69
+ }));
70
+ }, []);
71
+ const defaults = useCallback((...args) => {
72
+ if (args.length === 0) {
73
+ return deepClone(defaultsRef.current);
74
+ }
75
+ if (args.length === 1 && typeof args[0] === "object") {
76
+ defaultsRef.current = deepClone({ ...defaultsRef.current, ...args[0] });
77
+ return defaultsRef.current;
78
+ }
79
+ if (args.length === 2) {
80
+ const [field, value] = args;
81
+ defaultsRef.current = deepClone({ ...defaultsRef.current, [field]: value });
82
+ return defaultsRef.current;
83
+ }
84
+ return deepClone(defaultsRef.current);
85
+ }, []);
86
+ const reset = useCallback((...fields) => {
87
+ if (fields.length === 0) {
88
+ setDataState(deepClone(defaultsRef.current));
89
+ setErrors({});
90
+ return;
91
+ }
92
+ setDataState((previous) => {
93
+ const next = { ...previous };
94
+ for (const field of fields) {
95
+ if (field in defaultsRef.current) {
96
+ next[field] = deepClone(defaultsRef.current[field]);
97
+ }
98
+ }
99
+ return next;
100
+ });
101
+ }, []);
102
+ const resetAndClearErrors = useCallback((...fields) => {
103
+ reset(...fields);
104
+ clearErrors(...fields);
105
+ }, [reset, clearErrors]);
26
106
  const submit = useCallback(async (method, operation, overrides = {}) => {
27
107
  requestRef.current?.controller.abort();
28
108
  const controller = new AbortController();
29
109
  const requestId = (requestRef.current?.id ?? 0) + 1;
30
110
  requestRef.current = { id: requestId, controller };
31
111
  setProcessing(true);
32
- const payload = method === "post" ? data : { ...data, _method: method };
112
+ setWasSuccessful(false);
113
+ setRecentlySuccessful(false);
114
+ setTransportError(null);
115
+ setProgress(null);
116
+ if (recentlySuccessfulTimerRef.current) {
117
+ clearTimeout(recentlySuccessfulTimerRef.current);
118
+ recentlySuccessfulTimerRef.current = null;
119
+ }
120
+ const rawPayload = method === "post" ? data : { ...data, _method: method };
121
+ const payload = transform ? transform(rawPayload) : rawPayload;
33
122
  try {
34
123
  const response = await islandFetch(component, operation, payload, {
35
124
  signal: controller.signal,
36
125
  fixedParams: {
37
126
  ...params,
38
- ...fixedParams,
39
- ...(stream ? { __stream: stream } : {})
127
+ ...fixedParams
40
128
  },
41
129
  overridePayload: overrides.payload,
42
- navigate: overrides.navigate
130
+ navigate: overrides.navigate,
131
+ stream: stream ?? undefined
43
132
  });
44
133
  if (requestRef.current?.id !== requestId) {
45
134
  return response;
46
135
  }
47
136
  if (response.status === "ok") {
48
137
  setErrors({});
138
+ setWasSuccessful(true);
139
+ setRecentlySuccessful(true);
140
+ recentlySuccessfulTimerRef.current = setTimeout(() => {
141
+ if (mountedRef.current) {
142
+ setRecentlySuccessful(false);
143
+ }
144
+ }, recentlySuccessfulDuration);
49
145
  setState((previous) => ({
50
146
  props: response.props,
51
147
  version: typeof response.version === "number" && response.version > previous.version
52
148
  ? response.version
53
149
  : previous.version
54
150
  }));
151
+ onSuccess?.(response);
152
+ }
153
+ if (response.status === "redirect") {
154
+ setWasSuccessful(true);
155
+ setRecentlySuccessful(true);
156
+ recentlySuccessfulTimerRef.current = setTimeout(() => {
157
+ if (mountedRef.current) {
158
+ setRecentlySuccessful(false);
159
+ }
160
+ }, recentlySuccessfulDuration);
161
+ onSuccess?.(response);
55
162
  }
56
163
  if (response.status === "error") {
57
164
  setErrors(response.errors);
165
+ onError?.(response);
58
166
  }
59
167
  if (response.status === "forbidden") {
60
168
  setErrors({ _base: ["forbidden"] });
169
+ onForbidden?.(response);
61
170
  }
171
+ onFinish?.(response);
62
172
  return response;
63
173
  }
64
174
  catch (error) {
65
175
  if (error.name === "AbortError") {
66
176
  return undefined;
67
177
  }
68
- throw error;
178
+ if (requestRef.current?.id === requestId) {
179
+ setTransportError(error instanceof Error ? error : new Error(String(error)));
180
+ }
181
+ onFinish?.(undefined);
182
+ return undefined;
69
183
  }
70
184
  finally {
71
185
  if (requestRef.current?.id === requestId) {
72
186
  setProcessing(false);
187
+ setProgress(null);
73
188
  }
74
189
  }
75
- }, [component, data, fixedParams, params, setState, stream]);
76
- const reset = useCallback(() => {
77
- setDataState(initialData);
78
- setErrors({});
79
- }, [initialData]);
190
+ }, [
191
+ component,
192
+ data,
193
+ fixedParams,
194
+ onError,
195
+ onFinish,
196
+ onForbidden,
197
+ onSuccess,
198
+ params,
199
+ recentlySuccessfulDuration,
200
+ setState,
201
+ stream,
202
+ transform
203
+ ]);
80
204
  return useMemo(() => {
81
205
  return {
82
206
  data,
83
207
  setData,
84
208
  errors,
209
+ setError,
210
+ clearErrors,
85
211
  processing,
212
+ wasSuccessful,
213
+ recentlySuccessful,
214
+ progress,
215
+ transportError,
216
+ defaults,
217
+ reset,
218
+ resetAndClearErrors,
86
219
  post: (operation, overrides) => submit("post", operation, overrides),
87
220
  put: (operation, overrides) => submit("put", operation, overrides),
88
221
  patch: (operation, overrides) => submit("patch", operation, overrides),
89
- delete: (operation, overrides) => submit("delete", operation, overrides),
90
- reset
222
+ delete: (operation, overrides) => submit("delete", operation, overrides)
91
223
  };
92
- }, [data, errors, processing, reset, setData, submit]);
224
+ }, [
225
+ clearErrors,
226
+ data,
227
+ defaults,
228
+ errors,
229
+ processing,
230
+ progress,
231
+ recentlySuccessful,
232
+ reset,
233
+ resetAndClearErrors,
234
+ setData,
235
+ setError,
236
+ submit,
237
+ transportError,
238
+ wasSuccessful
239
+ ]);
93
240
  }
94
241
  //# sourceMappingURL=useIslandForm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useIslandForm.js","sourceRoot":"","sources":["../src/useIslandForm.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useIslandForm.js","sourceRoot":"","sources":["../src/useIslandForm.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archipelago-js/react",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "React bindings for Archipelago islands",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -24,7 +24,7 @@
24
24
  "typecheck": "tsc -p tsconfig.json --noEmit"
25
25
  },
26
26
  "peerDependencies": {
27
- "@archipelago-js/client": "^0.1.0",
27
+ "@archipelago-js/client": "^0.2.0",
28
28
  "react": "^19.0.0",
29
29
  "react-dom": "^19.0.0"
30
30
  },