@archipelago-js/react 0.1.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 +17 -0
- package/dist/ErrorBoundary.d.ts +15 -0
- package/dist/ErrorBoundary.js +24 -0
- package/dist/ErrorBoundary.js.map +1 -0
- package/dist/actionCable.d.ts +16 -0
- package/dist/actionCable.js +16 -0
- package/dist/actionCable.js.map +1 -0
- package/dist/bootstrapper.d.ts +13 -0
- package/dist/bootstrapper.js +144 -0
- package/dist/bootstrapper.js.map +1 -0
- package/dist/context.d.ts +25 -0
- package/dist/context.js +28 -0
- package/dist/context.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/useIslandForm.d.ts +21 -0
- package/dist/useIslandForm.js +94 -0
- package/dist/useIslandForm.js.map +1 -0
- package/dist/useIslandProps.d.ts +11 -0
- package/dist/useIslandProps.js +66 -0
- package/dist/useIslandProps.js.map +1 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# @archipelago-js/react
|
|
2
|
+
|
|
3
|
+
React bindings for Archipelago islands.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
yarn add @archipelago-js/react @archipelago-js/client react react-dom
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## API
|
|
12
|
+
|
|
13
|
+
- `bootArchipelagoIslands(registry)`
|
|
14
|
+
- `useIslandProps(options?)`
|
|
15
|
+
- `useIslandForm(options)`
|
|
16
|
+
- `IslandProvider`
|
|
17
|
+
- `ErrorBoundary`
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
interface ErrorBoundaryProps {
|
|
3
|
+
children: React.ReactNode;
|
|
4
|
+
fallback?: React.ReactNode;
|
|
5
|
+
}
|
|
6
|
+
interface ErrorBoundaryState {
|
|
7
|
+
hasError: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
10
|
+
constructor(props: ErrorBoundaryProps);
|
|
11
|
+
static getDerivedStateFromError(): ErrorBoundaryState;
|
|
12
|
+
componentDidCatch(error: Error): void;
|
|
13
|
+
render(): React.ReactNode;
|
|
14
|
+
}
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
export class ErrorBoundary extends React.Component {
|
|
4
|
+
constructor(props) {
|
|
5
|
+
super(props);
|
|
6
|
+
this.state = { hasError: false };
|
|
7
|
+
}
|
|
8
|
+
static getDerivedStateFromError() {
|
|
9
|
+
return { hasError: true };
|
|
10
|
+
}
|
|
11
|
+
componentDidCatch(error) {
|
|
12
|
+
if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
|
|
13
|
+
// Per-island crash visibility without breaking sibling islands.
|
|
14
|
+
console.error("Archipelago island crashed", error);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
render() {
|
|
18
|
+
if (this.state.hasError) {
|
|
19
|
+
return this.props.fallback ?? _jsx("div", { "data-archipelago-error": "true", children: "Island failed to render" });
|
|
20
|
+
}
|
|
21
|
+
return this.props.children;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=ErrorBoundary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorBoundary.js","sourceRoot":"","sources":["../src/ErrorBoundary.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAWzB,MAAM,OAAO,aAAc,SAAQ,KAAK,CAAC,SAAiD;IACxF,YAAmB,KAAyB;QAC1C,KAAK,CAAC,KAAK,CAAC,CAAA;QACZ,IAAI,CAAC,KAAK,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAA;IAClC,CAAC;IAEM,MAAM,CAAC,wBAAwB;QACpC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAA;IAC3B,CAAC;IAEM,iBAAiB,CAAC,KAAY;QACnC,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;YAC5E,gEAAgE;YAChE,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAA;QACpD,CAAC;IACH,CAAC;IAEM,MAAM;QACX,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,wCAA4B,MAAM,wCAA8B,CAAA;QAChG,CAAC;QAED,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAA;IAC5B,CAAC;CACF"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type CableSubscription = {
|
|
2
|
+
unsubscribe: () => void;
|
|
3
|
+
};
|
|
4
|
+
export type CableConsumer = {
|
|
5
|
+
subscriptions: {
|
|
6
|
+
create: (params: Record<string, unknown>, callbacks: {
|
|
7
|
+
received?: (data: unknown) => void;
|
|
8
|
+
}) => CableSubscription;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
export type StreamSubscriptionOptions = {
|
|
12
|
+
streamName: string;
|
|
13
|
+
onMessage: (payload: unknown) => void;
|
|
14
|
+
consumer?: CableConsumer;
|
|
15
|
+
};
|
|
16
|
+
export declare function subscribeToIslandStream({ streamName, onMessage, consumer }: StreamSubscriptionOptions): CableSubscription;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
function globalConsumer() {
|
|
2
|
+
const globalValue = globalThis;
|
|
3
|
+
return globalValue.Archipelago?.cable ?? globalValue.App?.cable;
|
|
4
|
+
}
|
|
5
|
+
export function subscribeToIslandStream({ streamName, onMessage, consumer = globalConsumer() }) {
|
|
6
|
+
if (!consumer || !streamName) {
|
|
7
|
+
return { unsubscribe: () => undefined };
|
|
8
|
+
}
|
|
9
|
+
return consumer.subscriptions.create({
|
|
10
|
+
channel: "Archipelago::IslandChannel",
|
|
11
|
+
stream_name: streamName
|
|
12
|
+
}, {
|
|
13
|
+
received: onMessage
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=actionCable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actionCable.js","sourceRoot":"","sources":["../src/actionCable.ts"],"names":[],"mappings":"AAmBA,SAAS,cAAc;IACrB,MAAM,WAAW,GAAG,UAGnB,CAAA;IAED,OAAO,WAAW,CAAC,WAAW,EAAE,KAAK,IAAI,WAAW,CAAC,GAAG,EAAE,KAAK,CAAA;AACjE,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,EACtC,UAAU,EACV,SAAS,EACT,QAAQ,GAAG,cAAc,EAAE,EACD;IAC1B,IAAI,CAAC,QAAQ,IAAI,CAAC,UAAU,EAAE,CAAC;QAC7B,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,SAAS,EAAE,CAAA;IACzC,CAAC;IAED,OAAO,QAAQ,CAAC,aAAa,CAAC,MAAM,CAClC;QACE,OAAO,EAAE,4BAA4B;QACrC,WAAW,EAAE,UAAU;KACxB,EACD;QACE,QAAQ,EAAE,SAAS;KACpB,CACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export type IslandComponent = React.ComponentType;
|
|
3
|
+
export type IslandLoader = () => Promise<{
|
|
4
|
+
default: IslandComponent;
|
|
5
|
+
} | IslandComponent>;
|
|
6
|
+
export type IslandRegistryEntry = IslandComponent | {
|
|
7
|
+
load: IslandLoader;
|
|
8
|
+
fallback?: React.ReactNode;
|
|
9
|
+
};
|
|
10
|
+
export type IslandRegistry = Record<string, IslandRegistryEntry>;
|
|
11
|
+
export declare function bootArchipelagoIslands(registry: IslandRegistry): Promise<void>;
|
|
12
|
+
export declare function unmountArchipelagoIslands(): void;
|
|
13
|
+
export declare function defineIslandLoader(load: IslandLoader, fallback?: React.ReactNode): IslandRegistryEntry;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { createRoot } from "react-dom/client";
|
|
4
|
+
import { ErrorBoundary } from "./ErrorBoundary";
|
|
5
|
+
import { IslandProvider } from "./context";
|
|
6
|
+
const mounted = new Map();
|
|
7
|
+
const mounting = new Set();
|
|
8
|
+
let listenersAttached = false;
|
|
9
|
+
let activeRegistry = null;
|
|
10
|
+
let observer = null;
|
|
11
|
+
let bootScheduled = false;
|
|
12
|
+
function scheduleBoot() {
|
|
13
|
+
if (!activeRegistry || bootScheduled) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
bootScheduled = true;
|
|
17
|
+
queueMicrotask(() => {
|
|
18
|
+
bootScheduled = false;
|
|
19
|
+
if (activeRegistry) {
|
|
20
|
+
void bootArchipelagoIslands(activeRegistry);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function nodeContainsIsland(node) {
|
|
25
|
+
if (!(node instanceof Element)) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
return node.hasAttribute("data-island") || node.querySelector("[data-island]") != null;
|
|
29
|
+
}
|
|
30
|
+
function ensureObserverAttached() {
|
|
31
|
+
if (observer || typeof MutationObserver === "undefined") {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const target = document.body ?? document.documentElement;
|
|
35
|
+
if (!target) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
observer = new MutationObserver((mutations) => {
|
|
39
|
+
for (const mutation of mutations) {
|
|
40
|
+
for (const node of Array.from(mutation.addedNodes)) {
|
|
41
|
+
if (nodeContainsIsland(node)) {
|
|
42
|
+
scheduleBoot();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
observer.observe(target, { childList: true, subtree: true });
|
|
49
|
+
}
|
|
50
|
+
function parseJsonObject(value) {
|
|
51
|
+
if (!value || value.trim().length === 0) {
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
54
|
+
const parsed = JSON.parse(value);
|
|
55
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
56
|
+
throw new Error("Expected JSON object");
|
|
57
|
+
}
|
|
58
|
+
return parsed;
|
|
59
|
+
}
|
|
60
|
+
function parseVersion(value) {
|
|
61
|
+
if (!value) {
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
64
|
+
const parsed = Number(value);
|
|
65
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
66
|
+
}
|
|
67
|
+
async function resolveComponent(entry) {
|
|
68
|
+
if (typeof entry === "function") {
|
|
69
|
+
return entry;
|
|
70
|
+
}
|
|
71
|
+
const loaded = await entry.load();
|
|
72
|
+
return "default" in loaded ? loaded.default : loaded;
|
|
73
|
+
}
|
|
74
|
+
function mountElement(element, component, fallback) {
|
|
75
|
+
let params = {};
|
|
76
|
+
let initialProps = {};
|
|
77
|
+
let initialVersion = 0;
|
|
78
|
+
try {
|
|
79
|
+
params = parseJsonObject(element.dataset.params);
|
|
80
|
+
initialProps = parseJsonObject(element.dataset.props);
|
|
81
|
+
initialVersion = parseVersion(element.dataset.version);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const root = createRoot(element);
|
|
87
|
+
mounted.set(element, { root });
|
|
88
|
+
const { component: componentName, instance, stream } = element.dataset;
|
|
89
|
+
root.render(_jsx(ErrorBoundary, { fallback: fallback, children: _jsx(IslandProvider, { component: componentName ?? "", params: params, initialProps: initialProps, initialVersion: initialVersion, instance: instance, stream: stream, children: React.createElement(component) }) }));
|
|
90
|
+
}
|
|
91
|
+
export async function bootArchipelagoIslands(registry) {
|
|
92
|
+
activeRegistry = registry;
|
|
93
|
+
const islands = Array.from(document.querySelectorAll("[data-island]"));
|
|
94
|
+
for (const island of islands) {
|
|
95
|
+
if (mounted.has(island) || mounting.has(island)) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const componentName = island.dataset.component;
|
|
99
|
+
if (!componentName) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const entry = registry[componentName];
|
|
103
|
+
if (!entry) {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
mounting.add(island);
|
|
107
|
+
try {
|
|
108
|
+
const component = await resolveComponent(entry);
|
|
109
|
+
if (mounted.has(island)) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const fallback = typeof entry === "function" ? undefined : entry.fallback;
|
|
113
|
+
mountElement(island, component, fallback);
|
|
114
|
+
if (mounted.has(island)) {
|
|
115
|
+
island.dataset.mounted = "true";
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
mounting.delete(island);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (!listenersAttached) {
|
|
123
|
+
listenersAttached = true;
|
|
124
|
+
document.addEventListener("turbo:load", scheduleBoot);
|
|
125
|
+
document.addEventListener("turbo:render", scheduleBoot);
|
|
126
|
+
document.addEventListener("turbo:frame-load", scheduleBoot);
|
|
127
|
+
document.addEventListener("turbo:before-cache", () => {
|
|
128
|
+
unmountArchipelagoIslands();
|
|
129
|
+
});
|
|
130
|
+
ensureObserverAttached();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export function unmountArchipelagoIslands() {
|
|
134
|
+
mounting.clear();
|
|
135
|
+
for (const [element, record] of mounted.entries()) {
|
|
136
|
+
record.root.unmount();
|
|
137
|
+
element.removeAttribute("data-mounted");
|
|
138
|
+
mounted.delete(element);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
export function defineIslandLoader(load, fallback) {
|
|
142
|
+
return { load, fallback };
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=bootstrapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrapper.js","sourceRoot":"","sources":["../src/bootstrapper.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
type IslandState = {
|
|
3
|
+
props: Record<string, unknown>;
|
|
4
|
+
version: number;
|
|
5
|
+
};
|
|
6
|
+
export interface IslandContextValue {
|
|
7
|
+
component: string;
|
|
8
|
+
params: Record<string, unknown>;
|
|
9
|
+
instance?: string;
|
|
10
|
+
stream?: string;
|
|
11
|
+
state: IslandState;
|
|
12
|
+
setState: React.Dispatch<React.SetStateAction<IslandState>>;
|
|
13
|
+
}
|
|
14
|
+
export interface IslandProviderProps {
|
|
15
|
+
children: React.ReactNode;
|
|
16
|
+
component: string;
|
|
17
|
+
params: Record<string, unknown>;
|
|
18
|
+
instance?: string;
|
|
19
|
+
stream?: string;
|
|
20
|
+
initialProps?: Record<string, unknown>;
|
|
21
|
+
initialVersion?: number;
|
|
22
|
+
}
|
|
23
|
+
export declare function IslandProvider({ children, component, params, instance, stream, initialProps, initialVersion }: IslandProviderProps): React.ReactElement;
|
|
24
|
+
export declare function useIslandContext(): IslandContextValue;
|
|
25
|
+
export {};
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useMemo, useState } from "react";
|
|
3
|
+
const IslandContext = createContext(null);
|
|
4
|
+
export function IslandProvider({ children, component, params, instance, stream, initialProps = {}, initialVersion = 0 }) {
|
|
5
|
+
const [state, setState] = useState({
|
|
6
|
+
props: initialProps,
|
|
7
|
+
version: initialVersion
|
|
8
|
+
});
|
|
9
|
+
const value = useMemo(() => {
|
|
10
|
+
return {
|
|
11
|
+
component,
|
|
12
|
+
params,
|
|
13
|
+
instance,
|
|
14
|
+
stream,
|
|
15
|
+
state,
|
|
16
|
+
setState
|
|
17
|
+
};
|
|
18
|
+
}, [component, instance, params, state, stream]);
|
|
19
|
+
return _jsx(IslandContext.Provider, { value: value, children: children });
|
|
20
|
+
}
|
|
21
|
+
export function useIslandContext() {
|
|
22
|
+
const value = useContext(IslandContext);
|
|
23
|
+
if (!value) {
|
|
24
|
+
throw new Error("useIslandContext must be used inside IslandProvider");
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAgB3E,MAAM,aAAa,GAAG,aAAa,CAA4B,IAAI,CAAC,CAAA;AAYpE,MAAM,UAAU,cAAc,CAAC,EAC7B,QAAQ,EACR,SAAS,EACT,MAAM,EACN,QAAQ,EACR,MAAM,EACN,YAAY,GAAG,EAAE,EACjB,cAAc,GAAG,CAAC,EACE;IACpB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAc;QAC9C,KAAK,EAAE,YAAY;QACnB,OAAO,EAAE,cAAc;KACxB,CAAC,CAAA;IAEF,MAAM,KAAK,GAAG,OAAO,CAAqB,GAAG,EAAE;QAC7C,OAAO;YACL,SAAS;YACT,MAAM;YACN,QAAQ;YACR,MAAM;YACN,KAAK;YACL,QAAQ;SACT,CAAA;IACH,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAA;IAEhD,OAAO,KAAC,aAAa,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAA0B,CAAA;AAClF,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,MAAM,KAAK,GAAG,UAAU,CAAC,aAAa,CAAC,CAAA;IACvC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAA;IACxE,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
type SubmitOverrides = {
|
|
2
|
+
payload?: Record<string, unknown>;
|
|
3
|
+
navigate?: (location: string) => void;
|
|
4
|
+
};
|
|
5
|
+
export interface UseIslandFormOptions<TData extends Record<string, unknown>> {
|
|
6
|
+
initialData: TData;
|
|
7
|
+
clearFieldErrorsOnChange?: boolean;
|
|
8
|
+
fixedParams?: Record<string, unknown>;
|
|
9
|
+
}
|
|
10
|
+
export declare function useIslandForm<TData extends Record<string, unknown>>({ initialData, clearFieldErrorsOnChange, fixedParams }: UseIslandFormOptions<TData>): {
|
|
11
|
+
data: TData;
|
|
12
|
+
setData: <K extends keyof TData>(field: K, value: TData[K]) => void;
|
|
13
|
+
errors: Record<string, string[]>;
|
|
14
|
+
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;
|
|
20
|
+
};
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { islandFetch } from "@archipelago-js/client";
|
|
2
|
+
import { useCallback, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { useIslandContext } from "./context";
|
|
4
|
+
export function useIslandForm({ initialData, clearFieldErrorsOnChange = true, fixedParams = {} }) {
|
|
5
|
+
const { component, params, stream, setState } = useIslandContext();
|
|
6
|
+
const [data, setDataState] = useState(initialData);
|
|
7
|
+
const [errors, setErrors] = useState({});
|
|
8
|
+
const [processing, setProcessing] = useState(false);
|
|
9
|
+
const requestRef = useRef(null);
|
|
10
|
+
const setData = useCallback((field, value) => {
|
|
11
|
+
setDataState((previous) => ({
|
|
12
|
+
...previous,
|
|
13
|
+
[field]: value
|
|
14
|
+
}));
|
|
15
|
+
if (clearFieldErrorsOnChange) {
|
|
16
|
+
setErrors((previous) => {
|
|
17
|
+
if (!(field in previous)) {
|
|
18
|
+
return previous;
|
|
19
|
+
}
|
|
20
|
+
const next = { ...previous };
|
|
21
|
+
delete next[field];
|
|
22
|
+
return next;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}, [clearFieldErrorsOnChange]);
|
|
26
|
+
const submit = useCallback(async (method, operation, overrides = {}) => {
|
|
27
|
+
requestRef.current?.controller.abort();
|
|
28
|
+
const controller = new AbortController();
|
|
29
|
+
const requestId = (requestRef.current?.id ?? 0) + 1;
|
|
30
|
+
requestRef.current = { id: requestId, controller };
|
|
31
|
+
setProcessing(true);
|
|
32
|
+
const payload = method === "post" ? data : { ...data, _method: method };
|
|
33
|
+
try {
|
|
34
|
+
const response = await islandFetch(component, operation, payload, {
|
|
35
|
+
signal: controller.signal,
|
|
36
|
+
fixedParams: {
|
|
37
|
+
...params,
|
|
38
|
+
...fixedParams,
|
|
39
|
+
...(stream ? { __stream: stream } : {})
|
|
40
|
+
},
|
|
41
|
+
overridePayload: overrides.payload,
|
|
42
|
+
navigate: overrides.navigate
|
|
43
|
+
});
|
|
44
|
+
if (requestRef.current?.id !== requestId) {
|
|
45
|
+
return response;
|
|
46
|
+
}
|
|
47
|
+
if (response.status === "ok") {
|
|
48
|
+
setErrors({});
|
|
49
|
+
setState((previous) => ({
|
|
50
|
+
props: response.props,
|
|
51
|
+
version: typeof response.version === "number" && response.version > previous.version
|
|
52
|
+
? response.version
|
|
53
|
+
: previous.version
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
if (response.status === "error") {
|
|
57
|
+
setErrors(response.errors);
|
|
58
|
+
}
|
|
59
|
+
if (response.status === "forbidden") {
|
|
60
|
+
setErrors({ _base: ["forbidden"] });
|
|
61
|
+
}
|
|
62
|
+
return response;
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
if (error.name === "AbortError") {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
if (requestRef.current?.id === requestId) {
|
|
72
|
+
setProcessing(false);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}, [component, data, fixedParams, params, setState, stream]);
|
|
76
|
+
const reset = useCallback(() => {
|
|
77
|
+
setDataState(initialData);
|
|
78
|
+
setErrors({});
|
|
79
|
+
}, [initialData]);
|
|
80
|
+
return useMemo(() => {
|
|
81
|
+
return {
|
|
82
|
+
data,
|
|
83
|
+
setData,
|
|
84
|
+
errors,
|
|
85
|
+
processing,
|
|
86
|
+
post: (operation, overrides) => submit("post", operation, overrides),
|
|
87
|
+
put: (operation, overrides) => submit("put", operation, overrides),
|
|
88
|
+
patch: (operation, overrides) => submit("patch", operation, overrides),
|
|
89
|
+
delete: (operation, overrides) => submit("delete", operation, overrides),
|
|
90
|
+
reset
|
|
91
|
+
};
|
|
92
|
+
}, [data, errors, processing, reset, setData, submit]);
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=useIslandForm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useIslandForm.js","sourceRoot":"","sources":["../src/useIslandForm.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type CableConsumer } from "./actionCable";
|
|
2
|
+
export interface UseIslandPropsOptions {
|
|
3
|
+
stream?: string;
|
|
4
|
+
onLiveProps?: (next: Record<string, unknown>, previous: Record<string, unknown>) => Record<string, unknown>;
|
|
5
|
+
consumer?: CableConsumer;
|
|
6
|
+
}
|
|
7
|
+
export declare function useIslandProps(options?: UseIslandPropsOptions): {
|
|
8
|
+
props: Record<string, unknown>;
|
|
9
|
+
setProps: (next: Record<string, unknown>) => void;
|
|
10
|
+
version: number;
|
|
11
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { parseIslandResponse } from "@archipelago-js/client";
|
|
2
|
+
import { useEffect, useMemo, useRef } from "react";
|
|
3
|
+
import { subscribeToIslandStream } from "./actionCable";
|
|
4
|
+
import { useIslandContext } from "./context";
|
|
5
|
+
function streamVersion(payload, currentVersion) {
|
|
6
|
+
return typeof payload.version === "number" ? payload.version : currentVersion + 1;
|
|
7
|
+
}
|
|
8
|
+
export function useIslandProps(options = {}) {
|
|
9
|
+
const { state, setState, stream: contextStream } = useIslandContext();
|
|
10
|
+
const onLiveProps = options.onLiveProps;
|
|
11
|
+
const stream = options.stream ?? contextStream;
|
|
12
|
+
const versionRef = useRef(state.version);
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
versionRef.current = state.version;
|
|
15
|
+
}, [state.version]);
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (!stream) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
const subscription = subscribeToIslandStream({
|
|
21
|
+
streamName: stream,
|
|
22
|
+
consumer: options.consumer,
|
|
23
|
+
onMessage: (rawPayload) => {
|
|
24
|
+
let parsed;
|
|
25
|
+
try {
|
|
26
|
+
parsed = parseIslandResponse(rawPayload);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (parsed.status !== "ok") {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const nextVersion = streamVersion(parsed, versionRef.current);
|
|
35
|
+
if (nextVersion <= versionRef.current) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
setState((previous) => {
|
|
39
|
+
const nextProps = onLiveProps
|
|
40
|
+
? onLiveProps(parsed.props, previous.props)
|
|
41
|
+
: parsed.props;
|
|
42
|
+
return {
|
|
43
|
+
props: nextProps,
|
|
44
|
+
version: nextVersion
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
return () => {
|
|
50
|
+
subscription.unsubscribe();
|
|
51
|
+
};
|
|
52
|
+
}, [onLiveProps, options.consumer, setState, stream]);
|
|
53
|
+
return useMemo(() => {
|
|
54
|
+
return {
|
|
55
|
+
props: state.props,
|
|
56
|
+
setProps: (next) => {
|
|
57
|
+
setState((previous) => ({
|
|
58
|
+
props: next,
|
|
59
|
+
version: previous.version
|
|
60
|
+
}));
|
|
61
|
+
},
|
|
62
|
+
version: state.version
|
|
63
|
+
};
|
|
64
|
+
}, [setState, state.props, state.version]);
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=useIslandProps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useIslandProps.js","sourceRoot":"","sources":["../src/useIslandProps.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@archipelago-js/react",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "React bindings for Archipelago islands",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"clean": "rm -rf dist",
|
|
22
|
+
"build": "yarn clean && tsc -p tsconfig.build.json",
|
|
23
|
+
"test": "vitest run packages/react/test",
|
|
24
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"@archipelago-js/client": "^0.1.0",
|
|
28
|
+
"react": "^19.0.0",
|
|
29
|
+
"react-dom": "^19.0.0"
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/robrace/archipelago.git",
|
|
37
|
+
"directory": "packages/react"
|
|
38
|
+
},
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/robrace/archipelago/issues"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://github.com/robrace/archipelago/tree/main/packages/react"
|
|
43
|
+
}
|