@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.
- package/LICENSE +21 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.iife.js +1621 -0
- package/dist/index.iife.min.js +1 -0
- package/dist/index.js +13 -0
- package/dist/useAsyncValidator.d.ts +83 -0
- package/dist/useAsyncValidator.iife.js +192 -0
- package/dist/useAsyncValidator.iife.min.js +1 -0
- package/dist/useAsyncValidator.js +168 -0
- package/dist/useAxios.d.ts +125 -0
- package/dist/useAxios.iife.js +288 -0
- package/dist/useAxios.iife.min.js +1 -0
- package/dist/useAxios.js +265 -0
- package/dist/useChangeCase.d.ts +57 -0
- package/dist/useChangeCase.iife.js +91 -0
- package/dist/useChangeCase.iife.min.js +1 -0
- package/dist/useChangeCase.js +68 -0
- package/dist/useCookies.d.ts +117 -0
- package/dist/useCookies.iife.js +142 -0
- package/dist/useCookies.iife.min.js +1 -0
- package/dist/useCookies.js +117 -0
- package/dist/useDrauu.d.ts +152 -0
- package/dist/useDrauu.iife.js +221 -0
- package/dist/useDrauu.iife.min.js +1 -0
- package/dist/useDrauu.js +221 -0
- package/dist/useFocusTrap.d.ts +116 -0
- package/dist/useFocusTrap.iife.js +125 -0
- package/dist/useFocusTrap.iife.min.js +1 -0
- package/dist/useFocusTrap.js +125 -0
- package/dist/useFuse.d.ts +86 -0
- package/dist/useFuse.iife.js +95 -0
- package/dist/useFuse.iife.min.js +1 -0
- package/dist/useFuse.js +72 -0
- package/dist/useIDBKeyval.d.ts +139 -0
- package/dist/useIDBKeyval.iife.js +175 -0
- package/dist/useIDBKeyval.iife.min.js +1 -0
- package/dist/useIDBKeyval.js +174 -0
- package/dist/useJwt.d.ts +52 -0
- package/dist/useJwt.iife.js +54 -0
- package/dist/useJwt.iife.min.js +1 -0
- package/dist/useJwt.js +53 -0
- package/dist/useNProgress.d.ts +115 -0
- package/dist/useNProgress.iife.js +148 -0
- package/dist/useNProgress.iife.min.js +1 -0
- package/dist/useNProgress.js +125 -0
- package/dist/useQRCode.d.ts +38 -0
- package/dist/useQRCode.iife.js +78 -0
- package/dist/useQRCode.iife.min.js +1 -0
- package/dist/useQRCode.js +55 -0
- package/dist/useSortable.d.ts +143 -0
- package/dist/useSortable.iife.js +199 -0
- package/dist/useSortable.iife.min.js +1 -0
- package/dist/useSortable.js +173 -0
- 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);
|
package/dist/useDrauu.js
ADDED
|
@@ -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 };
|