@reause/integrations 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.d.ts +13 -0
  3. package/dist/index.iife.js +1621 -0
  4. package/dist/index.iife.min.js +1 -0
  5. package/dist/index.js +13 -0
  6. package/dist/useAsyncValidator.d.ts +83 -0
  7. package/dist/useAsyncValidator.iife.js +192 -0
  8. package/dist/useAsyncValidator.iife.min.js +1 -0
  9. package/dist/useAsyncValidator.js +168 -0
  10. package/dist/useAxios.d.ts +125 -0
  11. package/dist/useAxios.iife.js +288 -0
  12. package/dist/useAxios.iife.min.js +1 -0
  13. package/dist/useAxios.js +265 -0
  14. package/dist/useChangeCase.d.ts +57 -0
  15. package/dist/useChangeCase.iife.js +91 -0
  16. package/dist/useChangeCase.iife.min.js +1 -0
  17. package/dist/useChangeCase.js +68 -0
  18. package/dist/useCookies.d.ts +117 -0
  19. package/dist/useCookies.iife.js +142 -0
  20. package/dist/useCookies.iife.min.js +1 -0
  21. package/dist/useCookies.js +117 -0
  22. package/dist/useDrauu.d.ts +152 -0
  23. package/dist/useDrauu.iife.js +221 -0
  24. package/dist/useDrauu.iife.min.js +1 -0
  25. package/dist/useDrauu.js +221 -0
  26. package/dist/useFocusTrap.d.ts +116 -0
  27. package/dist/useFocusTrap.iife.js +125 -0
  28. package/dist/useFocusTrap.iife.min.js +1 -0
  29. package/dist/useFocusTrap.js +125 -0
  30. package/dist/useFuse.d.ts +86 -0
  31. package/dist/useFuse.iife.js +95 -0
  32. package/dist/useFuse.iife.min.js +1 -0
  33. package/dist/useFuse.js +72 -0
  34. package/dist/useIDBKeyval.d.ts +139 -0
  35. package/dist/useIDBKeyval.iife.js +175 -0
  36. package/dist/useIDBKeyval.iife.min.js +1 -0
  37. package/dist/useIDBKeyval.js +174 -0
  38. package/dist/useJwt.d.ts +52 -0
  39. package/dist/useJwt.iife.js +54 -0
  40. package/dist/useJwt.iife.min.js +1 -0
  41. package/dist/useJwt.js +53 -0
  42. package/dist/useNProgress.d.ts +115 -0
  43. package/dist/useNProgress.iife.js +148 -0
  44. package/dist/useNProgress.iife.min.js +1 -0
  45. package/dist/useNProgress.js +125 -0
  46. package/dist/useQRCode.d.ts +38 -0
  47. package/dist/useQRCode.iife.js +78 -0
  48. package/dist/useQRCode.iife.min.js +1 -0
  49. package/dist/useQRCode.js +55 -0
  50. package/dist/useSortable.d.ts +143 -0
  51. package/dist/useSortable.iife.js +199 -0
  52. package/dist/useSortable.iife.min.js +1 -0
  53. package/dist/useSortable.js +173 -0
  54. package/package.json +92 -0
@@ -0,0 +1,152 @@
1
+ import { Dispatch, SetStateAction } from "react";
2
+ import { ListenerOn } from "@reause/shared";
3
+ import { Brush, Drauu, Options } from "drauu";
4
+ //#region useDrauu/index.d.ts
5
+ /**
6
+ * Options accepted by `useDrauu` — drauu's own options minus `el`, which the
7
+ * hook supplies from the resolved target (upstream `UseDrauuOptions`).
8
+ */
9
+ export type UseDrauuOptions = Omit<Options, 'el'>;
10
+ export interface UseDrauuReturn {
11
+ /**
12
+ * The mounted drauu instance — `undefined` until the target resolves to an
13
+ * `<svg>` element (upstream writable `Ref<Drauu | undefined>`). The hook owns
14
+ * the instance lifecycle, so this is a read-only output and has no paired
15
+ * setter (precedent: `useFileSystemAccess`'s `file`, `useTextareaAutosize`'s
16
+ * `textarea`).
17
+ */
18
+ drauuInstance: Drauu | undefined;
19
+ /**
20
+ * Load an SVG string into the instance (upstream `Ref<Drauu>.load`).
21
+ */
22
+ load: (svg: string) => void;
23
+ /**
24
+ * Serialize the current canvas as an SVG string.
25
+ */
26
+ dump: () => string | undefined;
27
+ /**
28
+ * Clear the canvas and the operation stack.
29
+ */
30
+ clear: () => void;
31
+ /**
32
+ * Cancel the stroke in progress.
33
+ */
34
+ cancel: () => void;
35
+ /**
36
+ * Undo the last operation — `undefined` when there is no instance.
37
+ */
38
+ undo: () => boolean | undefined;
39
+ /**
40
+ * Redo the last undone operation — `undefined` when there is no instance.
41
+ */
42
+ redo: () => boolean | undefined;
43
+ /**
44
+ * Whether there is an operation to undo (upstream writable
45
+ * `ShallowRef<boolean>`). The hook re-reads it from the instance on every
46
+ * drauu `changed` event, so it is a read-only output and has no paired setter.
47
+ */
48
+ canUndo: boolean;
49
+ /**
50
+ * Whether there is an operation to redo (upstream writable
51
+ * `ShallowRef<boolean>`). The hook re-reads it from the instance on every
52
+ * drauu `changed` event, so it is a read-only output and has no paired setter.
53
+ */
54
+ canRedo: boolean;
55
+ /**
56
+ * The current brush (upstream writable `Ref<Brush>`) — the hook's only
57
+ * caller-writable value, paired with `setBrush`.
58
+ */
59
+ brush: Brush;
60
+ /**
61
+ * React writable-side analog of the upstream `brush` ref, paired with
62
+ * `brush`: `setBrush(next)` or `setBrush(prev => next)` (the React state
63
+ * setter protocol — `Dispatch<SetStateAction<Brush>>`). It writes the
64
+ * returned `brush` value AND the mounted instance's brush / mode.
65
+ */
66
+ setBrush: Dispatch<SetStateAction<Brush>>;
67
+ /**
68
+ * Register a listener for drauu's `changed` event — `useListener(onChanged, cb)`.
69
+ */
70
+ onChanged: ListenerOn<() => void>;
71
+ /**
72
+ * Register a listener for drauu's `committed` event — `useListener(onCommitted, cb)`.
73
+ * The callback receives the committed `<svg>` node (or `undefined`), matching
74
+ * drauu's `committed` event payload.
75
+ */
76
+ onCommitted: ListenerOn<(node: SVGElement | undefined) => void>;
77
+ /**
78
+ * Register a listener for drauu's `start` event — `useListener(onStart, cb)`.
79
+ */
80
+ onStart: ListenerOn<() => void>;
81
+ /**
82
+ * Register a listener for drauu's `end` event — `useListener(onEnd, cb)`.
83
+ */
84
+ onEnd: ListenerOn<() => void>;
85
+ /**
86
+ * Register a listener for drauu's `canceled` event — `useListener(onCanceled, cb)`.
87
+ */
88
+ onCanceled: ListenerOn<() => void>;
89
+ }
90
+ /** Accepted DOM target kinds — mirrors upstream's `MaybeElement`. */
91
+ type MaybeElement = HTMLElement | SVGElement | null | undefined;
92
+ /** A plain element or a React ref-like object (`{ current }`) — upstream `MaybeElementRef`. */
93
+ type MaybeElementRef = MaybeElement | {
94
+ readonly current: MaybeElement;
95
+ };
96
+ /** Drauu target (upstream `MaybeComputedElementRef`, without its getter branch). */
97
+ type DrauuTarget = MaybeElementRef;
98
+ /**
99
+ * React port of VueUse's `useDrauu` — reactive instance for
100
+ * [drauu](https://github.com/antfu/drauu).
101
+ *
102
+ * Map from @vueuse/integrations `useDrauu`
103
+ * (`source/vueuse/packages/integrations/useDrauu/`), which creates a drauu
104
+ * instance for an `<svg>` element and exposes the drawing API plus its events.
105
+ * Upstream has no test file — the co-located `useDrauu.test.tsx` is authored
106
+ * for this port.
107
+ *
108
+ * Adjustment for React (upstream returns `Ref` / `ShallowRef` / `EventHookOn`):
109
+ * - the return is an OBJECT with a paired setter for every caller-writable
110
+ * value (return-shape rule 5). `brush` is the only such value — upstream's
111
+ * writable `Ref<Brush>` — and it is paired with `setBrush`, the React state
112
+ * setter (`Dispatch<SetStateAction<Brush>>`), so `setBrush(next)` and
113
+ * `setBrush(prev => next)` both work; `setBrush` writes the state AND the
114
+ * mounted instance's brush / mode, mirroring upstream's deep watcher;
115
+ * - `drauuInstance`, `canUndo` and `canRedo` are plain values from state
116
+ * instead of refs and stay read-only outputs without paired setters: the
117
+ * hook owns the instance lifecycle and re-reads the undo / redo status from
118
+ * the instance on every drauu `changed` event, so a caller write would be
119
+ * overwritten (upstream returns them as writable `Ref` / `ShallowRef`s;
120
+ * precedent: `useFileSystemAccess`'s `file`, `useAsyncState`'s `isReady` /
121
+ * `error`, `useTextareaAutosize`'s `textarea`);
122
+ * - the five `on*` members are §2D registrars — `(fn) => ({ off })` typed
123
+ * `ListenerOn<T>` (`@reause/shared`), consumable as
124
+ * `useListener(onChanged, cb)` for automatic cleanup on unmount, and `off()`
125
+ * removes exactly that listener and is idempotent;
126
+ * - the instance is created in an effect keyed on the resolved element's
127
+ * identity (upstream: `watch(() => unrefElement(target), ..., { flush: 'post' })`)
128
+ * and unmounted on cleanup (`tryOnScopeDispose`); an element-identity change
129
+ * destroys and recreates the instance. **Divergence:** resolving to `null` —
130
+ * a React ref whose element left the tree — destroys the instance and frees
131
+ * drauu's window listeners, where upstream keeps the last instance alive
132
+ * until scope dispose. Destroying is deliberate in React: a `null` ref means
133
+ * the element is gone, and a stale live instance would keep drawing on a
134
+ * detached `<svg>`; the co-located test pins this behavior;
135
+ * - the element is resolved locally from `@reause/shared`'s `toValue` /
136
+ * `isRefLike` (precedent: `useFocusTrap.ts`), never from `@reause/core` —
137
+ * `packages/integrations` must not depend on core (eslint
138
+ * `no-restricted-imports`). Only an `SVGSVGElement` target mounts, matching
139
+ * upstream's guard.
140
+ *
141
+ * @param target - the target `<svg>` element or a React ref-like object (`{ current }`)
142
+ * @param options - drauu options (`Omit<Options, 'el'>`); `brush` is merged over the defaults
143
+ *
144
+ * @__NO_SIDE_EFFECTS__
145
+ * @example
146
+ * const target = useRef<SVGSVGElement>(null)
147
+ * const { undo, redo, canUndo, canRedo, brush, setBrush } = useDrauu(target)
148
+ * setBrush(prev => ({ ...prev, color: '#ef4444' }))
149
+ * return <svg ref={target} />
150
+ */
151
+ export declare function useDrauu(target: DrauuTarget, options?: UseDrauuOptions): UseDrauuReturn;
152
+ //#endregion
@@ -0,0 +1,221 @@
1
+ (function(exports, _reause_shared, drauu, react) {
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ //#region useDrauu/index.tsx
4
+ /**
5
+ * Resolve the target to a DOM element — a plain element, a React ref-like
6
+ * object (`{ current }`), or `null` when it cannot be resolved. Upstream
7
+ * resolves elements with `unrefElement` (`@vueuse/core`); the React port
8
+ * composes the same unwrapping from `toValue` / `isRefLike` (`@reause/shared`)
9
+ * — one pass unwraps a ref-like object, a second one covers a ref-like object
10
+ * holding another ref-like (`{ current: { current: element } }`).
11
+ */
12
+ function resolveElement(value) {
13
+ let el = (0, _reause_shared.toValue)(value);
14
+ if ((0, _reause_shared.isRefLike)(el)) el = (0, _reause_shared.toValue)(el);
15
+ if (typeof el === "object" && el !== null && (el instanceof HTMLElement || el instanceof SVGElement)) return el;
16
+ return null;
17
+ }
18
+ /** Default brush — upstream's `brush` ref defaults (draw / black / size 3). */
19
+ const DEFAULT_BRUSH = {
20
+ color: "black",
21
+ size: 3,
22
+ arrowEnd: false,
23
+ cornerRadius: 0,
24
+ dasharray: void 0,
25
+ fill: "transparent",
26
+ mode: "draw"
27
+ };
28
+ /**
29
+ * Minimal event hook — mirrors `@reause/shared`'s `createEventHook` shape
30
+ * (`on` / `trigger`) while staying identity-stable across renders: the
31
+ * listener set lives in a ref, so `on` never changes identity and
32
+ * `useListener(on, cb)` does not re-register on every render. Upstream
33
+ * allocates its `createEventHook()` once per setup; React has no setup phase,
34
+ * so the ref is the equivalent.
35
+ */
36
+ function useEventHook() {
37
+ const fnsRef = (0, react.useRef)(/* @__PURE__ */ new Set());
38
+ return {
39
+ on: (0, react.useCallback)((fn) => {
40
+ fnsRef.current.add(fn);
41
+ return { off: () => {
42
+ fnsRef.current.delete(fn);
43
+ } };
44
+ }, []),
45
+ trigger: (0, react.useCallback)((...args) => {
46
+ Array.from(fnsRef.current).forEach((fn) => fn(...args));
47
+ }, [])
48
+ };
49
+ }
50
+ /**
51
+ * React port of VueUse's `useDrauu` — reactive instance for
52
+ * [drauu](https://github.com/antfu/drauu).
53
+ *
54
+ * Map from @vueuse/integrations `useDrauu`
55
+ * (`source/vueuse/packages/integrations/useDrauu/`), which creates a drauu
56
+ * instance for an `<svg>` element and exposes the drawing API plus its events.
57
+ * Upstream has no test file — the co-located `useDrauu.test.tsx` is authored
58
+ * for this port.
59
+ *
60
+ * Adjustment for React (upstream returns `Ref` / `ShallowRef` / `EventHookOn`):
61
+ * - the return is an OBJECT with a paired setter for every caller-writable
62
+ * value (return-shape rule 5). `brush` is the only such value — upstream's
63
+ * writable `Ref<Brush>` — and it is paired with `setBrush`, the React state
64
+ * setter (`Dispatch<SetStateAction<Brush>>`), so `setBrush(next)` and
65
+ * `setBrush(prev => next)` both work; `setBrush` writes the state AND the
66
+ * mounted instance's brush / mode, mirroring upstream's deep watcher;
67
+ * - `drauuInstance`, `canUndo` and `canRedo` are plain values from state
68
+ * instead of refs and stay read-only outputs without paired setters: the
69
+ * hook owns the instance lifecycle and re-reads the undo / redo status from
70
+ * the instance on every drauu `changed` event, so a caller write would be
71
+ * overwritten (upstream returns them as writable `Ref` / `ShallowRef`s;
72
+ * precedent: `useFileSystemAccess`'s `file`, `useAsyncState`'s `isReady` /
73
+ * `error`, `useTextareaAutosize`'s `textarea`);
74
+ * - the five `on*` members are §2D registrars — `(fn) => ({ off })` typed
75
+ * `ListenerOn<T>` (`@reause/shared`), consumable as
76
+ * `useListener(onChanged, cb)` for automatic cleanup on unmount, and `off()`
77
+ * removes exactly that listener and is idempotent;
78
+ * - the instance is created in an effect keyed on the resolved element's
79
+ * identity (upstream: `watch(() => unrefElement(target), ..., { flush: 'post' })`)
80
+ * and unmounted on cleanup (`tryOnScopeDispose`); an element-identity change
81
+ * destroys and recreates the instance. **Divergence:** resolving to `null` —
82
+ * a React ref whose element left the tree — destroys the instance and frees
83
+ * drauu's window listeners, where upstream keeps the last instance alive
84
+ * until scope dispose. Destroying is deliberate in React: a `null` ref means
85
+ * the element is gone, and a stale live instance would keep drawing on a
86
+ * detached `<svg>`; the co-located test pins this behavior;
87
+ * - the element is resolved locally from `@reause/shared`'s `toValue` /
88
+ * `isRefLike` (precedent: `useFocusTrap.ts`), never from `@reause/core` —
89
+ * `packages/integrations` must not depend on core (eslint
90
+ * `no-restricted-imports`). Only an `SVGSVGElement` target mounts, matching
91
+ * upstream's guard.
92
+ *
93
+ * @param target - the target `<svg>` element or a React ref-like object (`{ current }`)
94
+ * @param options - drauu options (`Omit<Options, 'el'>`); `brush` is merged over the defaults
95
+ *
96
+ * @__NO_SIDE_EFFECTS__
97
+ * @example
98
+ * const target = useRef<SVGSVGElement>(null)
99
+ * const { undo, redo, canUndo, canRedo, brush, setBrush } = useDrauu(target)
100
+ * setBrush(prev => ({ ...prev, color: '#ef4444' }))
101
+ * return <svg ref={target} />
102
+ */
103
+ function useDrauu(target, options) {
104
+ const [drauuInstance, setDrauuInstance] = (0, react.useState)(void 0);
105
+ const [canUndo, setCanUndo] = (0, react.useState)(false);
106
+ const [canRedo, setCanRedo] = (0, react.useState)(false);
107
+ const [brush, setBrushState] = (0, react.useState)(() => ({
108
+ ...DEFAULT_BRUSH,
109
+ ...options === null || options === void 0 ? void 0 : options.brush
110
+ }));
111
+ const instanceRef = (0, react.useRef)(null);
112
+ const disposablesRef = (0, react.useRef)([]);
113
+ const elementRef = (0, react.useRef)(null);
114
+ const targetRef = (0, react.useRef)(target);
115
+ targetRef.current = target;
116
+ const optionsRef = (0, react.useRef)(options);
117
+ optionsRef.current = options;
118
+ const brushRef = (0, react.useRef)(brush);
119
+ brushRef.current = brush;
120
+ const { on: onChanged, trigger: triggerChanged } = useEventHook();
121
+ const { on: onCommitted, trigger: triggerCommitted } = useEventHook();
122
+ const { on: onStart, trigger: triggerStart } = useEventHook();
123
+ const { on: onEnd, trigger: triggerEnd } = useEventHook();
124
+ const { on: onCanceled, trigger: triggerCanceled } = useEventHook();
125
+ const syncStatus = (0, react.useCallback)(() => {
126
+ const instance = instanceRef.current;
127
+ if (!instance) return;
128
+ setCanUndo(instance.canUndo());
129
+ setCanRedo(instance.canRedo());
130
+ }, []);
131
+ const destroyInstance = (0, react.useCallback)(() => {
132
+ const instance = instanceRef.current;
133
+ if (instance) {
134
+ disposablesRef.current.forEach((dispose) => dispose());
135
+ disposablesRef.current = [];
136
+ instance.unmount();
137
+ instanceRef.current = null;
138
+ }
139
+ setDrauuInstance(void 0);
140
+ setCanUndo(false);
141
+ setCanRedo(false);
142
+ }, []);
143
+ (0, react.useEffect)(() => {
144
+ const el = resolveElement(targetRef.current);
145
+ const svgEl = typeof SVGSVGElement === "undefined" || !(el instanceof SVGSVGElement) ? null : el;
146
+ if (svgEl === elementRef.current) return;
147
+ elementRef.current = svgEl;
148
+ destroyInstance();
149
+ if (!svgEl) return;
150
+ const instance = (0, drauu.createDrauu)({
151
+ ...optionsRef.current,
152
+ el: svgEl,
153
+ brush: brushRef.current
154
+ });
155
+ instanceRef.current = instance;
156
+ disposablesRef.current = [
157
+ instance.on("canceled", () => triggerCanceled()),
158
+ instance.on("committed", (node) => triggerCommitted(node)),
159
+ instance.on("start", () => triggerStart()),
160
+ instance.on("end", () => triggerEnd()),
161
+ instance.on("changed", () => {
162
+ syncStatus();
163
+ triggerChanged();
164
+ })
165
+ ];
166
+ syncStatus();
167
+ setDrauuInstance(instance);
168
+ });
169
+ (0, react.useEffect)(() => () => {
170
+ destroyInstance();
171
+ elementRef.current = null;
172
+ }, [destroyInstance]);
173
+ return {
174
+ drauuInstance,
175
+ load: (0, react.useCallback)((svg) => {
176
+ var _instanceRef$current;
177
+ (_instanceRef$current = instanceRef.current) === null || _instanceRef$current === void 0 || _instanceRef$current.load(svg);
178
+ }, []),
179
+ dump: (0, react.useCallback)(() => {
180
+ var _instanceRef$current2;
181
+ return (_instanceRef$current2 = instanceRef.current) === null || _instanceRef$current2 === void 0 ? void 0 : _instanceRef$current2.dump();
182
+ }, []),
183
+ clear: (0, react.useCallback)(() => {
184
+ var _instanceRef$current3;
185
+ (_instanceRef$current3 = instanceRef.current) === null || _instanceRef$current3 === void 0 || _instanceRef$current3.clear();
186
+ }, []),
187
+ cancel: (0, react.useCallback)(() => {
188
+ var _instanceRef$current4;
189
+ (_instanceRef$current4 = instanceRef.current) === null || _instanceRef$current4 === void 0 || _instanceRef$current4.cancel();
190
+ }, []),
191
+ undo: (0, react.useCallback)(() => {
192
+ var _instanceRef$current5;
193
+ return (_instanceRef$current5 = instanceRef.current) === null || _instanceRef$current5 === void 0 ? void 0 : _instanceRef$current5.undo();
194
+ }, []),
195
+ redo: (0, react.useCallback)(() => {
196
+ var _instanceRef$current6;
197
+ return (_instanceRef$current6 = instanceRef.current) === null || _instanceRef$current6 === void 0 ? void 0 : _instanceRef$current6.redo();
198
+ }, []),
199
+ canUndo,
200
+ canRedo,
201
+ brush,
202
+ setBrush: (0, react.useCallback)((next) => {
203
+ const value = typeof next === "function" ? next(brushRef.current) : next;
204
+ brushRef.current = value;
205
+ setBrushState(value);
206
+ const instance = instanceRef.current;
207
+ if (instance) {
208
+ instance.brush = value;
209
+ if (value.mode) instance.mode = value.mode;
210
+ }
211
+ }, []),
212
+ onChanged,
213
+ onCommitted,
214
+ onStart,
215
+ onEnd,
216
+ onCanceled
217
+ };
218
+ }
219
+ //#endregion
220
+ exports.useDrauu = useDrauu;
221
+ })(this.reause = this.reause || {}, reause, Drauu, React);
@@ -0,0 +1 @@
1
+ (function(e,t,n,r){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function i(e){let n=(0,t.toValue)(e);return(0,t.isRefLike)(n)&&(n=(0,t.toValue)(n)),typeof n==`object`&&n&&(n instanceof HTMLElement||n instanceof SVGElement)?n:null}let a={color:`black`,size:3,arrowEnd:!1,cornerRadius:0,dasharray:void 0,fill:`transparent`,mode:`draw`};function o(){let e=(0,r.useRef)(new Set);return{on:(0,r.useCallback)(t=>(e.current.add(t),{off:()=>{e.current.delete(t)}}),[]),trigger:(0,r.useCallback)((...t)=>{Array.from(e.current).forEach(e=>e(...t))},[])}}function s(e,t){let[s,c]=(0,r.useState)(void 0),[l,u]=(0,r.useState)(!1),[d,f]=(0,r.useState)(!1),[p,m]=(0,r.useState)(()=>({...a,...t==null?void 0:t.brush})),h=(0,r.useRef)(null),g=(0,r.useRef)([]),_=(0,r.useRef)(null),v=(0,r.useRef)(e);v.current=e;let y=(0,r.useRef)(t);y.current=t;let b=(0,r.useRef)(p);b.current=p;let{on:x,trigger:S}=o(),{on:C,trigger:w}=o(),{on:T,trigger:E}=o(),{on:D,trigger:O}=o(),{on:k,trigger:A}=o(),j=(0,r.useCallback)(()=>{let e=h.current;e&&(u(e.canUndo()),f(e.canRedo()))},[]),M=(0,r.useCallback)(()=>{let e=h.current;e&&(g.current.forEach(e=>e()),g.current=[],e.unmount(),h.current=null),c(void 0),u(!1),f(!1)},[]);return(0,r.useEffect)(()=>{let e=i(v.current),t=typeof SVGSVGElement>`u`||!(e instanceof SVGSVGElement)?null:e;if(t===_.current||(_.current=t,M(),!t))return;let r=(0,n.createDrauu)({...y.current,el:t,brush:b.current});h.current=r,g.current=[r.on(`canceled`,()=>A()),r.on(`committed`,e=>w(e)),r.on(`start`,()=>E()),r.on(`end`,()=>O()),r.on(`changed`,()=>{j(),S()})],j(),c(r)}),(0,r.useEffect)(()=>()=>{M(),_.current=null},[M]),{drauuInstance:s,load:(0,r.useCallback)(e=>{var t;(t=h.current)==null||t.load(e)},[]),dump:(0,r.useCallback)(()=>{var e;return(e=h.current)==null?void 0:e.dump()},[]),clear:(0,r.useCallback)(()=>{var e;(e=h.current)==null||e.clear()},[]),cancel:(0,r.useCallback)(()=>{var e;(e=h.current)==null||e.cancel()},[]),undo:(0,r.useCallback)(()=>{var e;return(e=h.current)==null?void 0:e.undo()},[]),redo:(0,r.useCallback)(()=>{var e;return(e=h.current)==null?void 0:e.redo()},[]),canUndo:l,canRedo:d,brush:p,setBrush:(0,r.useCallback)(e=>{let t=typeof e==`function`?e(b.current):e;b.current=t,m(t);let n=h.current;n&&(n.brush=t,t.mode&&(n.mode=t.mode))},[]),onChanged:x,onCommitted:C,onStart:T,onEnd:D,onCanceled:k}}e.useDrauu=s})(this.reause=this.reause||{},reause,Drauu,React);
@@ -0,0 +1,221 @@
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
+ import { isRefLike, toValue } from "@reause/shared";
3
+ import { createDrauu } from "drauu";
4
+ //#region useDrauu/index.tsx
5
+ /**
6
+ * Resolve the target to a DOM element — a plain element, a React ref-like
7
+ * object (`{ current }`), or `null` when it cannot be resolved. Upstream
8
+ * resolves elements with `unrefElement` (`@vueuse/core`); the React port
9
+ * composes the same unwrapping from `toValue` / `isRefLike` (`@reause/shared`)
10
+ * — one pass unwraps a ref-like object, a second one covers a ref-like object
11
+ * holding another ref-like (`{ current: { current: element } }`).
12
+ */
13
+ function resolveElement(value) {
14
+ let el = toValue(value);
15
+ if (isRefLike(el)) el = toValue(el);
16
+ if (typeof el === "object" && el !== null && (el instanceof HTMLElement || el instanceof SVGElement)) return el;
17
+ return null;
18
+ }
19
+ /** Default brush — upstream's `brush` ref defaults (draw / black / size 3). */
20
+ const DEFAULT_BRUSH = {
21
+ color: "black",
22
+ size: 3,
23
+ arrowEnd: false,
24
+ cornerRadius: 0,
25
+ dasharray: void 0,
26
+ fill: "transparent",
27
+ mode: "draw"
28
+ };
29
+ /**
30
+ * Minimal event hook — mirrors `@reause/shared`'s `createEventHook` shape
31
+ * (`on` / `trigger`) while staying identity-stable across renders: the
32
+ * listener set lives in a ref, so `on` never changes identity and
33
+ * `useListener(on, cb)` does not re-register on every render. Upstream
34
+ * allocates its `createEventHook()` once per setup; React has no setup phase,
35
+ * so the ref is the equivalent.
36
+ */
37
+ function useEventHook() {
38
+ const fnsRef = useRef(/* @__PURE__ */ new Set());
39
+ return {
40
+ on: useCallback((fn) => {
41
+ fnsRef.current.add(fn);
42
+ return { off: () => {
43
+ fnsRef.current.delete(fn);
44
+ } };
45
+ }, []),
46
+ trigger: useCallback((...args) => {
47
+ Array.from(fnsRef.current).forEach((fn) => fn(...args));
48
+ }, [])
49
+ };
50
+ }
51
+ /**
52
+ * React port of VueUse's `useDrauu` — reactive instance for
53
+ * [drauu](https://github.com/antfu/drauu).
54
+ *
55
+ * Map from @vueuse/integrations `useDrauu`
56
+ * (`source/vueuse/packages/integrations/useDrauu/`), which creates a drauu
57
+ * instance for an `<svg>` element and exposes the drawing API plus its events.
58
+ * Upstream has no test file — the co-located `useDrauu.test.tsx` is authored
59
+ * for this port.
60
+ *
61
+ * Adjustment for React (upstream returns `Ref` / `ShallowRef` / `EventHookOn`):
62
+ * - the return is an OBJECT with a paired setter for every caller-writable
63
+ * value (return-shape rule 5). `brush` is the only such value — upstream's
64
+ * writable `Ref<Brush>` — and it is paired with `setBrush`, the React state
65
+ * setter (`Dispatch<SetStateAction<Brush>>`), so `setBrush(next)` and
66
+ * `setBrush(prev => next)` both work; `setBrush` writes the state AND the
67
+ * mounted instance's brush / mode, mirroring upstream's deep watcher;
68
+ * - `drauuInstance`, `canUndo` and `canRedo` are plain values from state
69
+ * instead of refs and stay read-only outputs without paired setters: the
70
+ * hook owns the instance lifecycle and re-reads the undo / redo status from
71
+ * the instance on every drauu `changed` event, so a caller write would be
72
+ * overwritten (upstream returns them as writable `Ref` / `ShallowRef`s;
73
+ * precedent: `useFileSystemAccess`'s `file`, `useAsyncState`'s `isReady` /
74
+ * `error`, `useTextareaAutosize`'s `textarea`);
75
+ * - the five `on*` members are §2D registrars — `(fn) => ({ off })` typed
76
+ * `ListenerOn<T>` (`@reause/shared`), consumable as
77
+ * `useListener(onChanged, cb)` for automatic cleanup on unmount, and `off()`
78
+ * removes exactly that listener and is idempotent;
79
+ * - the instance is created in an effect keyed on the resolved element's
80
+ * identity (upstream: `watch(() => unrefElement(target), ..., { flush: 'post' })`)
81
+ * and unmounted on cleanup (`tryOnScopeDispose`); an element-identity change
82
+ * destroys and recreates the instance. **Divergence:** resolving to `null` —
83
+ * a React ref whose element left the tree — destroys the instance and frees
84
+ * drauu's window listeners, where upstream keeps the last instance alive
85
+ * until scope dispose. Destroying is deliberate in React: a `null` ref means
86
+ * the element is gone, and a stale live instance would keep drawing on a
87
+ * detached `<svg>`; the co-located test pins this behavior;
88
+ * - the element is resolved locally from `@reause/shared`'s `toValue` /
89
+ * `isRefLike` (precedent: `useFocusTrap.ts`), never from `@reause/core` —
90
+ * `packages/integrations` must not depend on core (eslint
91
+ * `no-restricted-imports`). Only an `SVGSVGElement` target mounts, matching
92
+ * upstream's guard.
93
+ *
94
+ * @param target - the target `<svg>` element or a React ref-like object (`{ current }`)
95
+ * @param options - drauu options (`Omit<Options, 'el'>`); `brush` is merged over the defaults
96
+ *
97
+ * @__NO_SIDE_EFFECTS__
98
+ * @example
99
+ * const target = useRef<SVGSVGElement>(null)
100
+ * const { undo, redo, canUndo, canRedo, brush, setBrush } = useDrauu(target)
101
+ * setBrush(prev => ({ ...prev, color: '#ef4444' }))
102
+ * return <svg ref={target} />
103
+ */
104
+ function useDrauu(target, options) {
105
+ const [drauuInstance, setDrauuInstance] = useState(void 0);
106
+ const [canUndo, setCanUndo] = useState(false);
107
+ const [canRedo, setCanRedo] = useState(false);
108
+ const [brush, setBrushState] = useState(() => ({
109
+ ...DEFAULT_BRUSH,
110
+ ...options === null || options === void 0 ? void 0 : options.brush
111
+ }));
112
+ const instanceRef = useRef(null);
113
+ const disposablesRef = useRef([]);
114
+ const elementRef = useRef(null);
115
+ const targetRef = useRef(target);
116
+ targetRef.current = target;
117
+ const optionsRef = useRef(options);
118
+ optionsRef.current = options;
119
+ const brushRef = useRef(brush);
120
+ brushRef.current = brush;
121
+ const { on: onChanged, trigger: triggerChanged } = useEventHook();
122
+ const { on: onCommitted, trigger: triggerCommitted } = useEventHook();
123
+ const { on: onStart, trigger: triggerStart } = useEventHook();
124
+ const { on: onEnd, trigger: triggerEnd } = useEventHook();
125
+ const { on: onCanceled, trigger: triggerCanceled } = useEventHook();
126
+ const syncStatus = useCallback(() => {
127
+ const instance = instanceRef.current;
128
+ if (!instance) return;
129
+ setCanUndo(instance.canUndo());
130
+ setCanRedo(instance.canRedo());
131
+ }, []);
132
+ const destroyInstance = useCallback(() => {
133
+ const instance = instanceRef.current;
134
+ if (instance) {
135
+ disposablesRef.current.forEach((dispose) => dispose());
136
+ disposablesRef.current = [];
137
+ instance.unmount();
138
+ instanceRef.current = null;
139
+ }
140
+ setDrauuInstance(void 0);
141
+ setCanUndo(false);
142
+ setCanRedo(false);
143
+ }, []);
144
+ useEffect(() => {
145
+ const el = resolveElement(targetRef.current);
146
+ const svgEl = typeof SVGSVGElement === "undefined" || !(el instanceof SVGSVGElement) ? null : el;
147
+ if (svgEl === elementRef.current) return;
148
+ elementRef.current = svgEl;
149
+ destroyInstance();
150
+ if (!svgEl) return;
151
+ const instance = createDrauu({
152
+ ...optionsRef.current,
153
+ el: svgEl,
154
+ brush: brushRef.current
155
+ });
156
+ instanceRef.current = instance;
157
+ disposablesRef.current = [
158
+ instance.on("canceled", () => triggerCanceled()),
159
+ instance.on("committed", (node) => triggerCommitted(node)),
160
+ instance.on("start", () => triggerStart()),
161
+ instance.on("end", () => triggerEnd()),
162
+ instance.on("changed", () => {
163
+ syncStatus();
164
+ triggerChanged();
165
+ })
166
+ ];
167
+ syncStatus();
168
+ setDrauuInstance(instance);
169
+ });
170
+ useEffect(() => () => {
171
+ destroyInstance();
172
+ elementRef.current = null;
173
+ }, [destroyInstance]);
174
+ return {
175
+ drauuInstance,
176
+ load: useCallback((svg) => {
177
+ var _instanceRef$current;
178
+ (_instanceRef$current = instanceRef.current) === null || _instanceRef$current === void 0 || _instanceRef$current.load(svg);
179
+ }, []),
180
+ dump: useCallback(() => {
181
+ var _instanceRef$current2;
182
+ return (_instanceRef$current2 = instanceRef.current) === null || _instanceRef$current2 === void 0 ? void 0 : _instanceRef$current2.dump();
183
+ }, []),
184
+ clear: useCallback(() => {
185
+ var _instanceRef$current3;
186
+ (_instanceRef$current3 = instanceRef.current) === null || _instanceRef$current3 === void 0 || _instanceRef$current3.clear();
187
+ }, []),
188
+ cancel: useCallback(() => {
189
+ var _instanceRef$current4;
190
+ (_instanceRef$current4 = instanceRef.current) === null || _instanceRef$current4 === void 0 || _instanceRef$current4.cancel();
191
+ }, []),
192
+ undo: useCallback(() => {
193
+ var _instanceRef$current5;
194
+ return (_instanceRef$current5 = instanceRef.current) === null || _instanceRef$current5 === void 0 ? void 0 : _instanceRef$current5.undo();
195
+ }, []),
196
+ redo: useCallback(() => {
197
+ var _instanceRef$current6;
198
+ return (_instanceRef$current6 = instanceRef.current) === null || _instanceRef$current6 === void 0 ? void 0 : _instanceRef$current6.redo();
199
+ }, []),
200
+ canUndo,
201
+ canRedo,
202
+ brush,
203
+ setBrush: useCallback((next) => {
204
+ const value = typeof next === "function" ? next(brushRef.current) : next;
205
+ brushRef.current = value;
206
+ setBrushState(value);
207
+ const instance = instanceRef.current;
208
+ if (instance) {
209
+ instance.brush = value;
210
+ if (value.mode) instance.mode = value.mode;
211
+ }
212
+ }, []),
213
+ onChanged,
214
+ onCommitted,
215
+ onStart,
216
+ onEnd,
217
+ onCanceled
218
+ };
219
+ }
220
+ //#endregion
221
+ export { useDrauu };