@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,1621 @@
|
|
|
1
|
+
(function(exports, async_validator, react, _reause_shared, axios, change_case, universal_cookie, drauu, focus_trap, fuse_js, idb_keyval, jwt_decode, nprogress, qrcode, sortablejs) {
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
//#region \0rolldown/runtime.js
|
|
4
|
+
var __create = Object.create;
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
9
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
12
|
+
key = keys[i];
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
14
|
+
get: ((k) => from[k]).bind(null, key),
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
21
|
+
value: mod,
|
|
22
|
+
enumerable: true
|
|
23
|
+
}) : target, mod));
|
|
24
|
+
//#endregion
|
|
25
|
+
async_validator = __toESM(async_validator, 1);
|
|
26
|
+
axios = __toESM(axios, 1);
|
|
27
|
+
change_case = __toESM(change_case, 1);
|
|
28
|
+
universal_cookie = __toESM(universal_cookie, 1);
|
|
29
|
+
fuse_js = __toESM(fuse_js, 1);
|
|
30
|
+
nprogress = __toESM(nprogress, 1);
|
|
31
|
+
qrcode = __toESM(qrcode, 1);
|
|
32
|
+
sortablejs = __toESM(sortablejs, 1);
|
|
33
|
+
//#region useAsyncValidator/index.tsx
|
|
34
|
+
const AsyncValidatorSchema = async_validator.default.default || async_validator.default;
|
|
35
|
+
/**
|
|
36
|
+
* React port of VueUse's `useAsyncValidator` — a wrapper around
|
|
37
|
+
* [`async-validator`](https://github.com/yiminghe/async-validator).
|
|
38
|
+
*
|
|
39
|
+
* Map from @vueuse/integrations `useAsyncValidator`
|
|
40
|
+
* (`source/vueuse/packages/integrations/useAsyncValidator/`). The upstream
|
|
41
|
+
* object return is preserved (`{ pass, isFinished, errors, errorInfo,
|
|
42
|
+
* errorFields, execute }`) with the refs unwrapped: every member is a plain
|
|
43
|
+
* value (no `.value`), `pass` starts as `!immediate || manual`, `execute()`
|
|
44
|
+
* flips `isFinished` to `false`, awaits
|
|
45
|
+
* `validator.validate(value, validateOption)` and settles with
|
|
46
|
+
* `pass`, `errorInfo`, `errors` and `errorFields`; `errors` is
|
|
47
|
+
* `errorInfo?.errors || []` and `errorFields` is `errorInfo?.fields || {}`.
|
|
48
|
+
* `value` and `rules` are the hook's **read-only value sources** and take
|
|
49
|
+
* plain values (`Record<string, any>` and `Rules`; upstream:
|
|
50
|
+
* `MaybeRefOrGetter`); the returned
|
|
51
|
+
* object is promise-like (`await useAsyncValidator(...)` resolves with the
|
|
52
|
+
* current snapshot), mirroring `useAsyncState`.
|
|
53
|
+
*
|
|
54
|
+
* Adjustment for React:
|
|
55
|
+
* - upstream re-runs validation from
|
|
56
|
+
* `watch([valueRef, validator], execute, { immediate, deep: true })`. React
|
|
57
|
+
* has no deep observation, so this port re-runs from an effect keyed on the
|
|
58
|
+
* identity of `value` / `rules` and skips it entirely when
|
|
59
|
+
* `manual` is `true`. Consequence: mutating the SAME object in place does NOT
|
|
60
|
+
* re-trigger validation — pass a new object (or new `rules`) or call
|
|
61
|
+
* `execute()` yourself. In React StrictMode dev builds the initial
|
|
62
|
+
* validation may run twice (the effect is double-invoked); the extra run is
|
|
63
|
+
* idempotent.
|
|
64
|
+
* - `errors` and `errorFields` are derived during render from `errorInfo`
|
|
65
|
+
* (upstream uses `computed`); `execute` is stable and ignores results that
|
|
66
|
+
* arrive after unmount.
|
|
67
|
+
*
|
|
68
|
+
* @__NO_SIDE_EFFECTS__
|
|
69
|
+
* @example
|
|
70
|
+
* const { pass, isFinished, errors, errorFields, execute } = useAsyncValidator(
|
|
71
|
+
* form,
|
|
72
|
+
* { name: { type: 'string', min: 5, max: 20 }, age: { type: 'number' } },
|
|
73
|
+
* )
|
|
74
|
+
* // `pass` becomes true once the initial validation resolves
|
|
75
|
+
*
|
|
76
|
+
* @see https://vueuse.org/useAsyncValidator
|
|
77
|
+
* @see https://github.com/yiminghe/async-validator
|
|
78
|
+
*/
|
|
79
|
+
function useAsyncValidator(value, rules, options) {
|
|
80
|
+
const { validateOption = {}, immediate = true, manual = false } = options || {};
|
|
81
|
+
const valueValue = value;
|
|
82
|
+
const rulesValue = rules;
|
|
83
|
+
const [errorInfo, setErrorInfo] = (0, react.useState)(null);
|
|
84
|
+
const [isFinished, setIsFinished] = (0, react.useState)(true);
|
|
85
|
+
const [pass, setPass] = (0, react.useState)(!immediate || manual);
|
|
86
|
+
const validator = (0, react.useMemo)(() => new AsyncValidatorSchema(rulesValue), [rulesValue]);
|
|
87
|
+
const valueRef = (0, react.useRef)(value);
|
|
88
|
+
valueRef.current = value;
|
|
89
|
+
const validatorRef = (0, react.useRef)(validator);
|
|
90
|
+
validatorRef.current = validator;
|
|
91
|
+
const validateOptionRef = (0, react.useRef)(validateOption);
|
|
92
|
+
validateOptionRef.current = validateOption;
|
|
93
|
+
const liveRef = (0, react.useRef)({
|
|
94
|
+
pass,
|
|
95
|
+
isFinished,
|
|
96
|
+
errorInfo
|
|
97
|
+
});
|
|
98
|
+
liveRef.current.pass = pass;
|
|
99
|
+
liveRef.current.isFinished = isFinished;
|
|
100
|
+
liveRef.current.errorInfo = errorInfo;
|
|
101
|
+
const mountedRef = (0, react.useRef)(true);
|
|
102
|
+
const finishedRef = (0, react.useRef)(isFinished);
|
|
103
|
+
const waitersRef = (0, react.useRef)([]);
|
|
104
|
+
const execute = (0, react.useCallback)(async () => {
|
|
105
|
+
finishedRef.current = false;
|
|
106
|
+
liveRef.current.pass = false;
|
|
107
|
+
liveRef.current.isFinished = false;
|
|
108
|
+
setPass(false);
|
|
109
|
+
setIsFinished(false);
|
|
110
|
+
let nextPass = false;
|
|
111
|
+
let nextErrorInfo = null;
|
|
112
|
+
try {
|
|
113
|
+
await validatorRef.current.validate(valueRef.current, validateOptionRef.current);
|
|
114
|
+
nextPass = true;
|
|
115
|
+
nextErrorInfo = null;
|
|
116
|
+
} catch (err) {
|
|
117
|
+
nextErrorInfo = err;
|
|
118
|
+
}
|
|
119
|
+
const result = {
|
|
120
|
+
pass: nextPass,
|
|
121
|
+
errorInfo: nextErrorInfo,
|
|
122
|
+
errors: (nextErrorInfo === null || nextErrorInfo === void 0 ? void 0 : nextErrorInfo.errors) || [],
|
|
123
|
+
errorFields: (nextErrorInfo === null || nextErrorInfo === void 0 ? void 0 : nextErrorInfo.fields) || {}
|
|
124
|
+
};
|
|
125
|
+
if (!mountedRef.current) return result;
|
|
126
|
+
finishedRef.current = true;
|
|
127
|
+
liveRef.current.pass = nextPass;
|
|
128
|
+
liveRef.current.isFinished = true;
|
|
129
|
+
liveRef.current.errorInfo = nextErrorInfo;
|
|
130
|
+
setPass(nextPass);
|
|
131
|
+
setErrorInfo(nextErrorInfo);
|
|
132
|
+
setIsFinished(true);
|
|
133
|
+
return result;
|
|
134
|
+
}, []);
|
|
135
|
+
const lastValueRef = (0, react.useRef)(valueValue);
|
|
136
|
+
const lastRulesRef = (0, react.useRef)(rulesValue);
|
|
137
|
+
const hasAutoRunRef = (0, react.useRef)(false);
|
|
138
|
+
(0, react.useEffect)(() => {
|
|
139
|
+
if (manual) return;
|
|
140
|
+
const first = !hasAutoRunRef.current;
|
|
141
|
+
if (!(first || lastValueRef.current !== valueValue || lastRulesRef.current !== rulesValue)) return;
|
|
142
|
+
hasAutoRunRef.current = true;
|
|
143
|
+
lastValueRef.current = valueValue;
|
|
144
|
+
lastRulesRef.current = rulesValue;
|
|
145
|
+
if (first && !immediate) return;
|
|
146
|
+
execute();
|
|
147
|
+
});
|
|
148
|
+
(0, react.useEffect)(() => {
|
|
149
|
+
mountedRef.current = true;
|
|
150
|
+
return () => {
|
|
151
|
+
mountedRef.current = false;
|
|
152
|
+
};
|
|
153
|
+
}, []);
|
|
154
|
+
const baseRef = (0, react.useRef)(void 0);
|
|
155
|
+
const base = {
|
|
156
|
+
get pass() {
|
|
157
|
+
return liveRef.current.pass;
|
|
158
|
+
},
|
|
159
|
+
get isFinished() {
|
|
160
|
+
return liveRef.current.isFinished;
|
|
161
|
+
},
|
|
162
|
+
get errorInfo() {
|
|
163
|
+
return liveRef.current.errorInfo;
|
|
164
|
+
},
|
|
165
|
+
get errors() {
|
|
166
|
+
var _liveRef$current$erro;
|
|
167
|
+
return ((_liveRef$current$erro = liveRef.current.errorInfo) === null || _liveRef$current$erro === void 0 ? void 0 : _liveRef$current$erro.errors) || [];
|
|
168
|
+
},
|
|
169
|
+
get errorFields() {
|
|
170
|
+
var _liveRef$current$erro2;
|
|
171
|
+
return ((_liveRef$current$erro2 = liveRef.current.errorInfo) === null || _liveRef$current$erro2 === void 0 ? void 0 : _liveRef$current$erro2.fields) || {};
|
|
172
|
+
},
|
|
173
|
+
execute
|
|
174
|
+
};
|
|
175
|
+
baseRef.current = base;
|
|
176
|
+
const waitUntilFinished = (0, react.useCallback)(() => {
|
|
177
|
+
return new Promise((resolve) => {
|
|
178
|
+
if (finishedRef.current) {
|
|
179
|
+
resolve(baseRef.current);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
waitersRef.current.push({ resolve });
|
|
183
|
+
});
|
|
184
|
+
}, []);
|
|
185
|
+
(0, react.useEffect)(() => {
|
|
186
|
+
if (!isFinished) return;
|
|
187
|
+
const waiters = waitersRef.current;
|
|
188
|
+
waitersRef.current = [];
|
|
189
|
+
waiters.forEach((waiter) => waiter.resolve(baseRef.current));
|
|
190
|
+
}, [isFinished]);
|
|
191
|
+
const shell = Object.create(base);
|
|
192
|
+
shell.then = function then(onFulfilled, onRejected) {
|
|
193
|
+
return waitUntilFinished().then(onFulfilled, onRejected);
|
|
194
|
+
};
|
|
195
|
+
return shell;
|
|
196
|
+
}
|
|
197
|
+
//#endregion
|
|
198
|
+
//#region useAxios/index.tsx
|
|
199
|
+
/**
|
|
200
|
+
* React port of VueUse's `useAxios` — wrapper for
|
|
201
|
+
* [`axios`](https://github.com/axios/axios).
|
|
202
|
+
*
|
|
203
|
+
* Map from @vueuse/integrations `useAxios`
|
|
204
|
+
* (`source/vueuse/packages/integrations/useAxios/`). All 12 upstream overloads
|
|
205
|
+
* are kept verbatim (`(url, config?, options?)`, `(url, instance?, options?)`,
|
|
206
|
+
* `(url, config, instance, options?)`, `(config)`, `(instance)`,
|
|
207
|
+
* `(config, instance)`); the url-ful forms expose `execute(url?, config?)`,
|
|
208
|
+
* the url-less forms require `execute(url, config?)`. The returned object
|
|
209
|
+
* mirrors the upstream members — `response`, `data`, `isFinished`,
|
|
210
|
+
* `isLoading`, `isAborted`/`isCanceled`, `error`, `abort`/`cancel`, `execute` —
|
|
211
|
+
* but the members are plain values backed by live getters instead of refs
|
|
212
|
+
* (no `.value`), and the object is thenable (`await useAxios(url)` resolves
|
|
213
|
+
* with the shell once the request settled, rejecting when it failed).
|
|
214
|
+
*
|
|
215
|
+
* Adjustment for React:
|
|
216
|
+
* - the returned object is built with `Object.create` over a getter base whose
|
|
217
|
+
* members read a synchronously-updated live mirror of the state, so a shell
|
|
218
|
+
* captured earlier (e.g. the value `await` resolved with) still exposes
|
|
219
|
+
* fresh values — the same shape `useAsyncState` uses. The base itself is
|
|
220
|
+
* deliberately NOT thenable, so resolving it can never re-adopt `then`;
|
|
221
|
+
* - `execute` returns the shared thenable shell (upstream `return promise`):
|
|
222
|
+
* `await execute()` resolves with the shell once the request finished,
|
|
223
|
+
* rejecting with the request error on failure, and a bare unawaited
|
|
224
|
+
* `execute()` never settles eagerly — so no unhandled rejection can
|
|
225
|
+
* escape. `immediate` requests fire from a mount effect (upstream fires
|
|
226
|
+
* during setup), and the shell is never left without a rejection handler
|
|
227
|
+
* there;
|
|
228
|
+
* - a pending request is aborted on unmount (React-safety deviation: upstream
|
|
229
|
+
* only relies on the `isAborted` guard), so a late response can never
|
|
230
|
+
* populate `data`/`response` after the component is gone;
|
|
231
|
+
* - `shallow` is accepted for API parity but has no effect: React state is
|
|
232
|
+
* never deep-wrapped, so there is no `shallowRef`/`ref` distinction here.
|
|
233
|
+
*
|
|
234
|
+
* @__NO_SIDE_EFFECTS__
|
|
235
|
+
* @example
|
|
236
|
+
* const { data, isLoading, isFinished, execute, abort } = useAxios('/api/posts')
|
|
237
|
+
* await execute('/api/posts/2')
|
|
238
|
+
*
|
|
239
|
+
* @see https://vueuse.org/integrations/useAxios
|
|
240
|
+
*/
|
|
241
|
+
function useAxios(...args) {
|
|
242
|
+
const url = typeof args[0] === "string" ? args[0] : void 0;
|
|
243
|
+
const argsPlaceholder = typeof url === "string" ? 1 : 0;
|
|
244
|
+
const defaultOptions = {
|
|
245
|
+
immediate: !!argsPlaceholder,
|
|
246
|
+
shallow: true,
|
|
247
|
+
abortPrevious: true
|
|
248
|
+
};
|
|
249
|
+
let defaultConfig = {};
|
|
250
|
+
let instance = axios.default;
|
|
251
|
+
let options = defaultOptions;
|
|
252
|
+
const isAxiosInstance = (val) => !!(val === null || val === void 0 ? void 0 : val.request);
|
|
253
|
+
if (args.length > 0 + argsPlaceholder) {
|
|
254
|
+
/**
|
|
255
|
+
* Unable to use `instanceof` here because of (https://github.com/axios/axios/issues/737)
|
|
256
|
+
* so instead we are checking if there is a `request` on the object to see if it is an
|
|
257
|
+
* axios instance
|
|
258
|
+
*/
|
|
259
|
+
if (isAxiosInstance(args[0 + argsPlaceholder])) instance = args[0 + argsPlaceholder];
|
|
260
|
+
else defaultConfig = args[0 + argsPlaceholder];
|
|
261
|
+
}
|
|
262
|
+
if (args.length > 1 + argsPlaceholder) {
|
|
263
|
+
if (isAxiosInstance(args[1 + argsPlaceholder])) instance = args[1 + argsPlaceholder];
|
|
264
|
+
}
|
|
265
|
+
if (args.length === 2 + argsPlaceholder && !isAxiosInstance(args[1 + argsPlaceholder]) || args.length === 3 + argsPlaceholder) options = args[args.length - 1] || defaultOptions;
|
|
266
|
+
const { onSuccess = _reause_shared.noop, onError = _reause_shared.noop, immediate, resetOnExecute = false } = options;
|
|
267
|
+
const initialData = options.initialData;
|
|
268
|
+
const urlRef = (0, react.useRef)(url);
|
|
269
|
+
urlRef.current = url;
|
|
270
|
+
const configRef = (0, react.useRef)(defaultConfig);
|
|
271
|
+
configRef.current = defaultConfig;
|
|
272
|
+
const instanceRef = (0, react.useRef)(instance);
|
|
273
|
+
instanceRef.current = instance;
|
|
274
|
+
const optionsRef = (0, react.useRef)(options);
|
|
275
|
+
optionsRef.current = options;
|
|
276
|
+
const immediateRef = (0, react.useRef)(immediate);
|
|
277
|
+
immediateRef.current = immediate;
|
|
278
|
+
const resetOnExecuteRef = (0, react.useRef)(resetOnExecute);
|
|
279
|
+
resetOnExecuteRef.current = resetOnExecute;
|
|
280
|
+
const initialDataRef = (0, react.useRef)(initialData);
|
|
281
|
+
initialDataRef.current = initialData;
|
|
282
|
+
const onSuccessRef = (0, react.useRef)(onSuccess);
|
|
283
|
+
onSuccessRef.current = onSuccess;
|
|
284
|
+
const onErrorRef = (0, react.useRef)(onError);
|
|
285
|
+
onErrorRef.current = onError;
|
|
286
|
+
const onFinishRef = (0, react.useRef)(options.onFinish);
|
|
287
|
+
onFinishRef.current = options.onFinish;
|
|
288
|
+
const [response, setResponse] = (0, react.useState)(void 0);
|
|
289
|
+
const [data, setData] = (0, react.useState)(initialData);
|
|
290
|
+
const [isFinished, setIsFinished] = (0, react.useState)(false);
|
|
291
|
+
const [isLoading, setIsLoading] = (0, react.useState)(false);
|
|
292
|
+
const [isAborted, setIsAborted] = (0, react.useState)(false);
|
|
293
|
+
const [error, setError] = (0, react.useState)(void 0);
|
|
294
|
+
const liveRef = (0, react.useRef)({
|
|
295
|
+
response,
|
|
296
|
+
data,
|
|
297
|
+
isFinished,
|
|
298
|
+
isLoading,
|
|
299
|
+
isAborted,
|
|
300
|
+
error
|
|
301
|
+
});
|
|
302
|
+
const abortControllerRef = (0, react.useRef)(null);
|
|
303
|
+
if (abortControllerRef.current === null) abortControllerRef.current = new AbortController();
|
|
304
|
+
const executeCounterRef = (0, react.useRef)(0);
|
|
305
|
+
const waitersRef = (0, react.useRef)([]);
|
|
306
|
+
const baseRef = (0, react.useRef)(null);
|
|
307
|
+
const shellRef = (0, react.useRef)(null);
|
|
308
|
+
const abort = (0, react.useCallback)((message) => {
|
|
309
|
+
var _abortControllerRef$c;
|
|
310
|
+
if (liveRef.current.isFinished || !liveRef.current.isLoading) return;
|
|
311
|
+
(_abortControllerRef$c = abortControllerRef.current) === null || _abortControllerRef$c === void 0 || _abortControllerRef$c.abort(message);
|
|
312
|
+
abortControllerRef.current = new AbortController();
|
|
313
|
+
liveRef.current.isAborted = true;
|
|
314
|
+
liveRef.current.isLoading = false;
|
|
315
|
+
liveRef.current.isFinished = false;
|
|
316
|
+
setIsAborted(true);
|
|
317
|
+
setIsLoading(false);
|
|
318
|
+
setIsFinished(false);
|
|
319
|
+
}, []);
|
|
320
|
+
const loading = (0, react.useCallback)((value) => {
|
|
321
|
+
liveRef.current.isLoading = value;
|
|
322
|
+
liveRef.current.isFinished = !value;
|
|
323
|
+
setIsLoading(value);
|
|
324
|
+
setIsFinished(!value);
|
|
325
|
+
}, []);
|
|
326
|
+
/**
|
|
327
|
+
* Reset data to initialData
|
|
328
|
+
*/
|
|
329
|
+
const resetData = (0, react.useCallback)(() => {
|
|
330
|
+
if (!resetOnExecuteRef.current) return;
|
|
331
|
+
liveRef.current.data = initialDataRef.current;
|
|
332
|
+
setData(initialDataRef.current);
|
|
333
|
+
}, []);
|
|
334
|
+
const drainWaiters = (0, react.useCallback)(() => {
|
|
335
|
+
const waiters = waitersRef.current;
|
|
336
|
+
if (waiters.length === 0) return;
|
|
337
|
+
waitersRef.current = [];
|
|
338
|
+
const currentError = liveRef.current.error;
|
|
339
|
+
const result = baseRef.current;
|
|
340
|
+
waiters.forEach(({ resolve, reject }) => {
|
|
341
|
+
if (currentError) reject(currentError);
|
|
342
|
+
else resolve(result);
|
|
343
|
+
});
|
|
344
|
+
}, []);
|
|
345
|
+
const execute = (0, react.useCallback)((executeUrl = urlRef.current, config = {}) => {
|
|
346
|
+
var _urlRef$current, _abortControllerRef$c2;
|
|
347
|
+
liveRef.current.error = void 0;
|
|
348
|
+
setError(void 0);
|
|
349
|
+
const _url = typeof executeUrl === "string" ? executeUrl : (_urlRef$current = urlRef.current) !== null && _urlRef$current !== void 0 ? _urlRef$current : config.url;
|
|
350
|
+
if (_url === void 0) {
|
|
351
|
+
const invalidUrlError = new axios.AxiosError(axios.AxiosError.ERR_INVALID_URL);
|
|
352
|
+
liveRef.current.error = invalidUrlError;
|
|
353
|
+
liveRef.current.isFinished = true;
|
|
354
|
+
setError(invalidUrlError);
|
|
355
|
+
setIsFinished(true);
|
|
356
|
+
return shellRef.current;
|
|
357
|
+
}
|
|
358
|
+
resetData();
|
|
359
|
+
if (optionsRef.current.abortPrevious !== false) abort();
|
|
360
|
+
loading(true);
|
|
361
|
+
executeCounterRef.current += 1;
|
|
362
|
+
const currentExecuteCounter = executeCounterRef.current;
|
|
363
|
+
liveRef.current.isAborted = false;
|
|
364
|
+
setIsAborted(false);
|
|
365
|
+
instanceRef.current(_url, {
|
|
366
|
+
...configRef.current,
|
|
367
|
+
...typeof executeUrl === "object" ? executeUrl : config,
|
|
368
|
+
signal: (_abortControllerRef$c2 = abortControllerRef.current) === null || _abortControllerRef$c2 === void 0 ? void 0 : _abortControllerRef$c2.signal
|
|
369
|
+
}).then((result) => {
|
|
370
|
+
if (liveRef.current.isAborted) return;
|
|
371
|
+
liveRef.current.response = result;
|
|
372
|
+
setResponse(result);
|
|
373
|
+
const payload = result === null || result === void 0 ? void 0 : result.data;
|
|
374
|
+
liveRef.current.data = payload;
|
|
375
|
+
setData(payload);
|
|
376
|
+
onSuccessRef.current(payload);
|
|
377
|
+
}).catch((e) => {
|
|
378
|
+
liveRef.current.error = e;
|
|
379
|
+
setError(e);
|
|
380
|
+
onErrorRef.current(e);
|
|
381
|
+
}).finally(() => {
|
|
382
|
+
var _onFinishRef$current;
|
|
383
|
+
(_onFinishRef$current = onFinishRef.current) === null || _onFinishRef$current === void 0 || _onFinishRef$current.call(onFinishRef);
|
|
384
|
+
if (currentExecuteCounter === executeCounterRef.current) {
|
|
385
|
+
loading(false);
|
|
386
|
+
drainWaiters();
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
return shellRef.current;
|
|
390
|
+
}, [
|
|
391
|
+
abort,
|
|
392
|
+
drainWaiters,
|
|
393
|
+
loading,
|
|
394
|
+
resetData
|
|
395
|
+
]);
|
|
396
|
+
const base = {
|
|
397
|
+
get response() {
|
|
398
|
+
return liveRef.current.response;
|
|
399
|
+
},
|
|
400
|
+
get data() {
|
|
401
|
+
return liveRef.current.data;
|
|
402
|
+
},
|
|
403
|
+
get isFinished() {
|
|
404
|
+
return liveRef.current.isFinished;
|
|
405
|
+
},
|
|
406
|
+
get isLoading() {
|
|
407
|
+
return liveRef.current.isLoading;
|
|
408
|
+
},
|
|
409
|
+
get isAborted() {
|
|
410
|
+
return liveRef.current.isAborted;
|
|
411
|
+
},
|
|
412
|
+
get isCanceled() {
|
|
413
|
+
return liveRef.current.isAborted;
|
|
414
|
+
},
|
|
415
|
+
get error() {
|
|
416
|
+
return liveRef.current.error;
|
|
417
|
+
},
|
|
418
|
+
abort,
|
|
419
|
+
cancel: abort,
|
|
420
|
+
execute
|
|
421
|
+
};
|
|
422
|
+
baseRef.current = base;
|
|
423
|
+
const waitUntilFinished = (0, react.useCallback)(() => {
|
|
424
|
+
return new Promise((resolve, reject) => {
|
|
425
|
+
if (!liveRef.current.isLoading) {
|
|
426
|
+
const currentError = liveRef.current.error;
|
|
427
|
+
if (currentError) reject(currentError);
|
|
428
|
+
else resolve(baseRef.current);
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
waitersRef.current.push({
|
|
432
|
+
resolve,
|
|
433
|
+
reject
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
}, []);
|
|
437
|
+
(0, react.useEffect)(() => {
|
|
438
|
+
if (immediateRef.current && urlRef.current) execute().catch(_reause_shared.noop);
|
|
439
|
+
}, [execute]);
|
|
440
|
+
(0, react.useEffect)(() => () => {
|
|
441
|
+
var _abortControllerRef$c3;
|
|
442
|
+
(_abortControllerRef$c3 = abortControllerRef.current) === null || _abortControllerRef$c3 === void 0 || _abortControllerRef$c3.abort();
|
|
443
|
+
}, []);
|
|
444
|
+
(0, react.useEffect)(() => {
|
|
445
|
+
if (isLoading) return;
|
|
446
|
+
drainWaiters();
|
|
447
|
+
}, [isLoading, drainWaiters]);
|
|
448
|
+
const shell = Object.create(base);
|
|
449
|
+
shell.then = function then(onFulfilled, onRejected) {
|
|
450
|
+
return waitUntilFinished().then(onFulfilled, onRejected);
|
|
451
|
+
};
|
|
452
|
+
shell.catch = function catchFn(onRejected) {
|
|
453
|
+
return waitUntilFinished().catch(onRejected);
|
|
454
|
+
};
|
|
455
|
+
shellRef.current = shell;
|
|
456
|
+
return shell;
|
|
457
|
+
}
|
|
458
|
+
//#endregion
|
|
459
|
+
//#region useChangeCase/index.tsx
|
|
460
|
+
const changeCaseTransforms = /* @__PURE__ */ Object.entries(change_case).filter((entry) => {
|
|
461
|
+
const [name, fn] = entry;
|
|
462
|
+
return typeof fn === "function" && name.endsWith("Case");
|
|
463
|
+
}).reduce((acc, [name, fn]) => {
|
|
464
|
+
acc[name] = fn;
|
|
465
|
+
return acc;
|
|
466
|
+
}, {});
|
|
467
|
+
/**
|
|
468
|
+
* React port of VueUse's `useChangeCase`.
|
|
469
|
+
*
|
|
470
|
+
* Map from @vueuse/integrations `useChangeCase`
|
|
471
|
+
* (`source/vueuse/packages/integrations/useChangeCase/`), a reactive wrapper
|
|
472
|
+
* around the `change-case` package. Upstream returns a writable
|
|
473
|
+
* `WritableComputedRef<string>`; here the writable computed ref maps to a
|
|
474
|
+
* `[value, setValue]` tuple: `value` is the transformed string (`change-case`
|
|
475
|
+
* applied to the internal input state with the current `type`), and
|
|
476
|
+
* `setValue` updates that internal input state like a controlled `useState`.
|
|
477
|
+
* `input` is the hook's **read-only value source** and takes a plain `string`
|
|
478
|
+
* (upstream: `MaybeRef<string>` / `MaybeRefOrGetter<string>`); `type` and
|
|
479
|
+
* `options` stay `RefOrValue` (format knobs, upstream `MaybeRefOrGetter`) and
|
|
480
|
+
* are resolved with `toValue` from `@reause/shared`.
|
|
481
|
+
*
|
|
482
|
+
* Adjustment for React:
|
|
483
|
+
* - upstream's writable computed captures a plain `input` once at setup. Here
|
|
484
|
+
* a changed `input` prop re-syncs the internal state on the next render, so
|
|
485
|
+
* a parent re-render with a new string is reflected; a `setValue` write is
|
|
486
|
+
* never clobbered while the `input` prop is unchanged (the baseline records
|
|
487
|
+
* the last externally synced value);
|
|
488
|
+
* - writes are **not** propagated back to the caller: `setValue` updates the
|
|
489
|
+
* internal input state only (upstream's writable computed writes through to
|
|
490
|
+
* a ref input). A changed `input` prop always wins over the internal state;
|
|
491
|
+
* - `setValue` writes the RAW string; the transform is re-applied on the next
|
|
492
|
+
* render (the upstream computed's `get` re-derives from the ref on access).
|
|
493
|
+
*
|
|
494
|
+
* @__NO_SIDE_EFFECTS__
|
|
495
|
+
* @example
|
|
496
|
+
* const [changeCase, setChangeCase] = useChangeCase('hello world', 'camelCase')
|
|
497
|
+
* changeCase // 'helloWorld'
|
|
498
|
+
* setChangeCase('vue use')
|
|
499
|
+
* changeCase // 'vueUse'
|
|
500
|
+
*/
|
|
501
|
+
function useChangeCase(input, type, options) {
|
|
502
|
+
const [text, setText] = (0, react.useState)(input);
|
|
503
|
+
const lastExternalRef = (0, react.useRef)(input);
|
|
504
|
+
(0, react.useEffect)(() => {
|
|
505
|
+
if (!Object.is(input, lastExternalRef.current)) {
|
|
506
|
+
lastExternalRef.current = input;
|
|
507
|
+
setText(input);
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
const typeName = (0, _reause_shared.toValue)(type);
|
|
511
|
+
const resolvedOptions = (0, _reause_shared.toValue)(options);
|
|
512
|
+
return [(0, react.useMemo)(() => {
|
|
513
|
+
const transform = changeCaseTransforms[typeName];
|
|
514
|
+
if (!transform) throw new Error(`Invalid change case type "${typeName}"`);
|
|
515
|
+
return transform(text, resolvedOptions);
|
|
516
|
+
}, [
|
|
517
|
+
text,
|
|
518
|
+
typeName,
|
|
519
|
+
resolvedOptions
|
|
520
|
+
]), setText];
|
|
521
|
+
}
|
|
522
|
+
//#endregion
|
|
523
|
+
//#region useCookies/index.tsx
|
|
524
|
+
/**
|
|
525
|
+
* React port of VueUse's `createCookies` — creates a `universal-cookie`
|
|
526
|
+
* instance from a request (default is `window.document.cookie`) and returns a
|
|
527
|
+
* {@link useCookies} bound to that instance.
|
|
528
|
+
*
|
|
529
|
+
* Map from @vueuse/integrations `createCookies`
|
|
530
|
+
* (`source/vueuse/packages/integrations/useCookies/index.ts`).
|
|
531
|
+
*
|
|
532
|
+
* @param req - incoming request (for SSR); a plain `cookie` header string is
|
|
533
|
+
* also accepted for convenience
|
|
534
|
+
* @see https://github.com/reactivestack/cookies/tree/master/packages/universal-cookie universal-cookie
|
|
535
|
+
*
|
|
536
|
+
* @__NO_SIDE_EFFECTS__
|
|
537
|
+
* @example
|
|
538
|
+
* const useSsrCookies = createCookies({ headers: { cookie: 'locale=en-US' } })
|
|
539
|
+
* const { get } = useSsrCookies(['locale'])
|
|
540
|
+
* get('locale') // 'en-US'
|
|
541
|
+
*/
|
|
542
|
+
function createCookies(req) {
|
|
543
|
+
var _req$headers$cookie, _req$headers;
|
|
544
|
+
const universalCookie = new universal_cookie.default(typeof req === "string" ? req : (_req$headers$cookie = req === null || req === void 0 || (_req$headers = req.headers) === null || _req$headers === void 0 ? void 0 : _req$headers.cookie) !== null && _req$headers$cookie !== void 0 ? _req$headers$cookie : null);
|
|
545
|
+
return (dependencies, { doNotParse = false, autoUpdateDependencies = false } = {}) => useCookies(dependencies, {
|
|
546
|
+
doNotParse,
|
|
547
|
+
autoUpdateDependencies
|
|
548
|
+
}, universalCookie);
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* React port of VueUse's `useCookies` — reactive methods to work with cookies
|
|
552
|
+
* (use {@link createCookies} instead if you are using SSR).
|
|
553
|
+
*
|
|
554
|
+
* Map from @vueuse/integrations `useCookies`
|
|
555
|
+
* (`source/vueuse/packages/integrations/useCookies/index.ts`).
|
|
556
|
+
*
|
|
557
|
+
* Adjustment for React:
|
|
558
|
+
* - upstream returns a **method object** (`get`, `getAll`, `set`, `remove`,
|
|
559
|
+
* `addChangeListener`, `removeChangeListener`), not a piece of writable
|
|
560
|
+
* state, so the port returns the same object instead of a tuple (§2B);
|
|
561
|
+
* - upstream's `shallowRef(0)` "touches" counter is a `useState` counter read
|
|
562
|
+
* in the render body, so a watched cookie change re-renders the component
|
|
563
|
+
* and the `get`/`getAll` closures from that render observe the new value;
|
|
564
|
+
* - `previousCookies` and the mutable watch list live in refs so they persist
|
|
565
|
+
* across renders exactly as upstream's local variables do;
|
|
566
|
+
* - upstream's `tryOnScopeDispose` maps to a `useEffect` cleanup that removes
|
|
567
|
+
* the change listener.
|
|
568
|
+
*
|
|
569
|
+
* @param dependencies - array of watching cookie's names. Pass empty array if don't want to watch cookies changes.
|
|
570
|
+
* @param options
|
|
571
|
+
* @param options.doNotParse - don't try parse value as JSON
|
|
572
|
+
* @param options.autoUpdateDependencies - automatically update watching dependencies
|
|
573
|
+
* @param cookies - universal-cookie instance
|
|
574
|
+
*
|
|
575
|
+
* @__NO_SIDE_EFFECTS__
|
|
576
|
+
* @example
|
|
577
|
+
* const { get, set } = useCookies(['locale'])
|
|
578
|
+
* get('locale') // reads reactively
|
|
579
|
+
* set('locale', 'en-US')
|
|
580
|
+
*/
|
|
581
|
+
function useCookies(dependencies, { doNotParse = false, autoUpdateDependencies = false } = {}, cookies = new universal_cookie.default()) {
|
|
582
|
+
const watchingDependenciesRef = (0, react.useRef)(autoUpdateDependencies ? [...dependencies || []] : dependencies);
|
|
583
|
+
const previousCookiesRef = (0, react.useRef)(null);
|
|
584
|
+
if (previousCookiesRef.current === null) previousCookiesRef.current = cookies.getAll({ doNotParse: true });
|
|
585
|
+
const [touches, setTouches] = (0, react.useState)(0);
|
|
586
|
+
const previousDependenciesRef = (0, react.useRef)(dependencies);
|
|
587
|
+
(0, react.useEffect)(() => {
|
|
588
|
+
if (previousDependenciesRef.current === dependencies) return;
|
|
589
|
+
previousDependenciesRef.current = dependencies;
|
|
590
|
+
if (!autoUpdateDependencies) watchingDependenciesRef.current = dependencies;
|
|
591
|
+
}, [dependencies, autoUpdateDependencies]);
|
|
592
|
+
const onChange = (0, react.useCallback)(() => {
|
|
593
|
+
var _watchingDependencies;
|
|
594
|
+
const newCookies = cookies.getAll({ doNotParse: true });
|
|
595
|
+
const previous = previousCookiesRef.current;
|
|
596
|
+
if (previous === null) {
|
|
597
|
+
previousCookiesRef.current = newCookies;
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
if (shouldUpdate((_watchingDependencies = watchingDependenciesRef.current) !== null && _watchingDependencies !== void 0 ? _watchingDependencies : null, newCookies, previous)) setTouches((n) => n + 1);
|
|
601
|
+
previousCookiesRef.current = newCookies;
|
|
602
|
+
}, [cookies]);
|
|
603
|
+
(0, react.useEffect)(() => {
|
|
604
|
+
cookies.addChangeListener(onChange);
|
|
605
|
+
return () => {
|
|
606
|
+
cookies.removeChangeListener(onChange);
|
|
607
|
+
};
|
|
608
|
+
}, [cookies, onChange]);
|
|
609
|
+
return {
|
|
610
|
+
get: (name, options) => {
|
|
611
|
+
const watchingDependencies = watchingDependenciesRef.current;
|
|
612
|
+
/**
|
|
613
|
+
* Auto update watching dependencies if needed
|
|
614
|
+
*/
|
|
615
|
+
if (autoUpdateDependencies && watchingDependencies && !watchingDependencies.includes(name)) watchingDependencies.push(name);
|
|
616
|
+
return cookies.get(name, {
|
|
617
|
+
doNotParse,
|
|
618
|
+
...options
|
|
619
|
+
});
|
|
620
|
+
},
|
|
621
|
+
getAll: (options) => cookies.getAll({
|
|
622
|
+
doNotParse,
|
|
623
|
+
...options
|
|
624
|
+
}),
|
|
625
|
+
set: (name, value, options) => cookies.set(name, value, options),
|
|
626
|
+
remove: (name, options) => cookies.remove(name, options),
|
|
627
|
+
addChangeListener: (callback) => cookies.addChangeListener(callback),
|
|
628
|
+
removeChangeListener: (callback) => cookies.removeChangeListener(callback)
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
function shouldUpdate(dependencies, newCookies, oldCookies) {
|
|
632
|
+
if (!dependencies) return true;
|
|
633
|
+
for (const dependency of dependencies) if (newCookies[dependency] !== oldCookies[dependency]) return true;
|
|
634
|
+
return false;
|
|
635
|
+
}
|
|
636
|
+
//#endregion
|
|
637
|
+
//#region useDrauu/index.tsx
|
|
638
|
+
/**
|
|
639
|
+
* Resolve the target to a DOM element — a plain element, a React ref-like
|
|
640
|
+
* object (`{ current }`), or `null` when it cannot be resolved. Upstream
|
|
641
|
+
* resolves elements with `unrefElement` (`@vueuse/core`); the React port
|
|
642
|
+
* composes the same unwrapping from `toValue` / `isRefLike` (`@reause/shared`)
|
|
643
|
+
* — one pass unwraps a ref-like object, a second one covers a ref-like object
|
|
644
|
+
* holding another ref-like (`{ current: { current: element } }`).
|
|
645
|
+
*/
|
|
646
|
+
function resolveElement$2(value) {
|
|
647
|
+
let el = (0, _reause_shared.toValue)(value);
|
|
648
|
+
if ((0, _reause_shared.isRefLike)(el)) el = (0, _reause_shared.toValue)(el);
|
|
649
|
+
if (typeof el === "object" && el !== null && (el instanceof HTMLElement || el instanceof SVGElement)) return el;
|
|
650
|
+
return null;
|
|
651
|
+
}
|
|
652
|
+
/** Default brush — upstream's `brush` ref defaults (draw / black / size 3). */
|
|
653
|
+
const DEFAULT_BRUSH = {
|
|
654
|
+
color: "black",
|
|
655
|
+
size: 3,
|
|
656
|
+
arrowEnd: false,
|
|
657
|
+
cornerRadius: 0,
|
|
658
|
+
dasharray: void 0,
|
|
659
|
+
fill: "transparent",
|
|
660
|
+
mode: "draw"
|
|
661
|
+
};
|
|
662
|
+
/**
|
|
663
|
+
* Minimal event hook — mirrors `@reause/shared`'s `createEventHook` shape
|
|
664
|
+
* (`on` / `trigger`) while staying identity-stable across renders: the
|
|
665
|
+
* listener set lives in a ref, so `on` never changes identity and
|
|
666
|
+
* `useListener(on, cb)` does not re-register on every render. Upstream
|
|
667
|
+
* allocates its `createEventHook()` once per setup; React has no setup phase,
|
|
668
|
+
* so the ref is the equivalent.
|
|
669
|
+
*/
|
|
670
|
+
function useEventHook() {
|
|
671
|
+
const fnsRef = (0, react.useRef)(/* @__PURE__ */ new Set());
|
|
672
|
+
return {
|
|
673
|
+
on: (0, react.useCallback)((fn) => {
|
|
674
|
+
fnsRef.current.add(fn);
|
|
675
|
+
return { off: () => {
|
|
676
|
+
fnsRef.current.delete(fn);
|
|
677
|
+
} };
|
|
678
|
+
}, []),
|
|
679
|
+
trigger: (0, react.useCallback)((...args) => {
|
|
680
|
+
Array.from(fnsRef.current).forEach((fn) => fn(...args));
|
|
681
|
+
}, [])
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* React port of VueUse's `useDrauu` — reactive instance for
|
|
686
|
+
* [drauu](https://github.com/antfu/drauu).
|
|
687
|
+
*
|
|
688
|
+
* Map from @vueuse/integrations `useDrauu`
|
|
689
|
+
* (`source/vueuse/packages/integrations/useDrauu/`), which creates a drauu
|
|
690
|
+
* instance for an `<svg>` element and exposes the drawing API plus its events.
|
|
691
|
+
* Upstream has no test file — the co-located `useDrauu.test.tsx` is authored
|
|
692
|
+
* for this port.
|
|
693
|
+
*
|
|
694
|
+
* Adjustment for React (upstream returns `Ref` / `ShallowRef` / `EventHookOn`):
|
|
695
|
+
* - the return is an OBJECT with a paired setter for every caller-writable
|
|
696
|
+
* value (return-shape rule 5). `brush` is the only such value — upstream's
|
|
697
|
+
* writable `Ref<Brush>` — and it is paired with `setBrush`, the React state
|
|
698
|
+
* setter (`Dispatch<SetStateAction<Brush>>`), so `setBrush(next)` and
|
|
699
|
+
* `setBrush(prev => next)` both work; `setBrush` writes the state AND the
|
|
700
|
+
* mounted instance's brush / mode, mirroring upstream's deep watcher;
|
|
701
|
+
* - `drauuInstance`, `canUndo` and `canRedo` are plain values from state
|
|
702
|
+
* instead of refs and stay read-only outputs without paired setters: the
|
|
703
|
+
* hook owns the instance lifecycle and re-reads the undo / redo status from
|
|
704
|
+
* the instance on every drauu `changed` event, so a caller write would be
|
|
705
|
+
* overwritten (upstream returns them as writable `Ref` / `ShallowRef`s;
|
|
706
|
+
* precedent: `useFileSystemAccess`'s `file`, `useAsyncState`'s `isReady` /
|
|
707
|
+
* `error`, `useTextareaAutosize`'s `textarea`);
|
|
708
|
+
* - the five `on*` members are §2D registrars — `(fn) => ({ off })` typed
|
|
709
|
+
* `ListenerOn<T>` (`@reause/shared`), consumable as
|
|
710
|
+
* `useListener(onChanged, cb)` for automatic cleanup on unmount, and `off()`
|
|
711
|
+
* removes exactly that listener and is idempotent;
|
|
712
|
+
* - the instance is created in an effect keyed on the resolved element's
|
|
713
|
+
* identity (upstream: `watch(() => unrefElement(target), ..., { flush: 'post' })`)
|
|
714
|
+
* and unmounted on cleanup (`tryOnScopeDispose`); an element-identity change
|
|
715
|
+
* destroys and recreates the instance. **Divergence:** resolving to `null` —
|
|
716
|
+
* a React ref whose element left the tree — destroys the instance and frees
|
|
717
|
+
* drauu's window listeners, where upstream keeps the last instance alive
|
|
718
|
+
* until scope dispose. Destroying is deliberate in React: a `null` ref means
|
|
719
|
+
* the element is gone, and a stale live instance would keep drawing on a
|
|
720
|
+
* detached `<svg>`; the co-located test pins this behavior;
|
|
721
|
+
* - the element is resolved locally from `@reause/shared`'s `toValue` /
|
|
722
|
+
* `isRefLike` (precedent: `useFocusTrap.ts`), never from `@reause/core` —
|
|
723
|
+
* `packages/integrations` must not depend on core (eslint
|
|
724
|
+
* `no-restricted-imports`). Only an `SVGSVGElement` target mounts, matching
|
|
725
|
+
* upstream's guard.
|
|
726
|
+
*
|
|
727
|
+
* @param target - the target `<svg>` element or a React ref-like object (`{ current }`)
|
|
728
|
+
* @param options - drauu options (`Omit<Options, 'el'>`); `brush` is merged over the defaults
|
|
729
|
+
*
|
|
730
|
+
* @__NO_SIDE_EFFECTS__
|
|
731
|
+
* @example
|
|
732
|
+
* const target = useRef<SVGSVGElement>(null)
|
|
733
|
+
* const { undo, redo, canUndo, canRedo, brush, setBrush } = useDrauu(target)
|
|
734
|
+
* setBrush(prev => ({ ...prev, color: '#ef4444' }))
|
|
735
|
+
* return <svg ref={target} />
|
|
736
|
+
*/
|
|
737
|
+
function useDrauu(target, options) {
|
|
738
|
+
const [drauuInstance, setDrauuInstance] = (0, react.useState)(void 0);
|
|
739
|
+
const [canUndo, setCanUndo] = (0, react.useState)(false);
|
|
740
|
+
const [canRedo, setCanRedo] = (0, react.useState)(false);
|
|
741
|
+
const [brush, setBrushState] = (0, react.useState)(() => ({
|
|
742
|
+
...DEFAULT_BRUSH,
|
|
743
|
+
...options === null || options === void 0 ? void 0 : options.brush
|
|
744
|
+
}));
|
|
745
|
+
const instanceRef = (0, react.useRef)(null);
|
|
746
|
+
const disposablesRef = (0, react.useRef)([]);
|
|
747
|
+
const elementRef = (0, react.useRef)(null);
|
|
748
|
+
const targetRef = (0, react.useRef)(target);
|
|
749
|
+
targetRef.current = target;
|
|
750
|
+
const optionsRef = (0, react.useRef)(options);
|
|
751
|
+
optionsRef.current = options;
|
|
752
|
+
const brushRef = (0, react.useRef)(brush);
|
|
753
|
+
brushRef.current = brush;
|
|
754
|
+
const { on: onChanged, trigger: triggerChanged } = useEventHook();
|
|
755
|
+
const { on: onCommitted, trigger: triggerCommitted } = useEventHook();
|
|
756
|
+
const { on: onStart, trigger: triggerStart } = useEventHook();
|
|
757
|
+
const { on: onEnd, trigger: triggerEnd } = useEventHook();
|
|
758
|
+
const { on: onCanceled, trigger: triggerCanceled } = useEventHook();
|
|
759
|
+
const syncStatus = (0, react.useCallback)(() => {
|
|
760
|
+
const instance = instanceRef.current;
|
|
761
|
+
if (!instance) return;
|
|
762
|
+
setCanUndo(instance.canUndo());
|
|
763
|
+
setCanRedo(instance.canRedo());
|
|
764
|
+
}, []);
|
|
765
|
+
const destroyInstance = (0, react.useCallback)(() => {
|
|
766
|
+
const instance = instanceRef.current;
|
|
767
|
+
if (instance) {
|
|
768
|
+
disposablesRef.current.forEach((dispose) => dispose());
|
|
769
|
+
disposablesRef.current = [];
|
|
770
|
+
instance.unmount();
|
|
771
|
+
instanceRef.current = null;
|
|
772
|
+
}
|
|
773
|
+
setDrauuInstance(void 0);
|
|
774
|
+
setCanUndo(false);
|
|
775
|
+
setCanRedo(false);
|
|
776
|
+
}, []);
|
|
777
|
+
(0, react.useEffect)(() => {
|
|
778
|
+
const el = resolveElement$2(targetRef.current);
|
|
779
|
+
const svgEl = typeof SVGSVGElement === "undefined" || !(el instanceof SVGSVGElement) ? null : el;
|
|
780
|
+
if (svgEl === elementRef.current) return;
|
|
781
|
+
elementRef.current = svgEl;
|
|
782
|
+
destroyInstance();
|
|
783
|
+
if (!svgEl) return;
|
|
784
|
+
const instance = (0, drauu.createDrauu)({
|
|
785
|
+
...optionsRef.current,
|
|
786
|
+
el: svgEl,
|
|
787
|
+
brush: brushRef.current
|
|
788
|
+
});
|
|
789
|
+
instanceRef.current = instance;
|
|
790
|
+
disposablesRef.current = [
|
|
791
|
+
instance.on("canceled", () => triggerCanceled()),
|
|
792
|
+
instance.on("committed", (node) => triggerCommitted(node)),
|
|
793
|
+
instance.on("start", () => triggerStart()),
|
|
794
|
+
instance.on("end", () => triggerEnd()),
|
|
795
|
+
instance.on("changed", () => {
|
|
796
|
+
syncStatus();
|
|
797
|
+
triggerChanged();
|
|
798
|
+
})
|
|
799
|
+
];
|
|
800
|
+
syncStatus();
|
|
801
|
+
setDrauuInstance(instance);
|
|
802
|
+
});
|
|
803
|
+
(0, react.useEffect)(() => () => {
|
|
804
|
+
destroyInstance();
|
|
805
|
+
elementRef.current = null;
|
|
806
|
+
}, [destroyInstance]);
|
|
807
|
+
return {
|
|
808
|
+
drauuInstance,
|
|
809
|
+
load: (0, react.useCallback)((svg) => {
|
|
810
|
+
var _instanceRef$current;
|
|
811
|
+
(_instanceRef$current = instanceRef.current) === null || _instanceRef$current === void 0 || _instanceRef$current.load(svg);
|
|
812
|
+
}, []),
|
|
813
|
+
dump: (0, react.useCallback)(() => {
|
|
814
|
+
var _instanceRef$current2;
|
|
815
|
+
return (_instanceRef$current2 = instanceRef.current) === null || _instanceRef$current2 === void 0 ? void 0 : _instanceRef$current2.dump();
|
|
816
|
+
}, []),
|
|
817
|
+
clear: (0, react.useCallback)(() => {
|
|
818
|
+
var _instanceRef$current3;
|
|
819
|
+
(_instanceRef$current3 = instanceRef.current) === null || _instanceRef$current3 === void 0 || _instanceRef$current3.clear();
|
|
820
|
+
}, []),
|
|
821
|
+
cancel: (0, react.useCallback)(() => {
|
|
822
|
+
var _instanceRef$current4;
|
|
823
|
+
(_instanceRef$current4 = instanceRef.current) === null || _instanceRef$current4 === void 0 || _instanceRef$current4.cancel();
|
|
824
|
+
}, []),
|
|
825
|
+
undo: (0, react.useCallback)(() => {
|
|
826
|
+
var _instanceRef$current5;
|
|
827
|
+
return (_instanceRef$current5 = instanceRef.current) === null || _instanceRef$current5 === void 0 ? void 0 : _instanceRef$current5.undo();
|
|
828
|
+
}, []),
|
|
829
|
+
redo: (0, react.useCallback)(() => {
|
|
830
|
+
var _instanceRef$current6;
|
|
831
|
+
return (_instanceRef$current6 = instanceRef.current) === null || _instanceRef$current6 === void 0 ? void 0 : _instanceRef$current6.redo();
|
|
832
|
+
}, []),
|
|
833
|
+
canUndo,
|
|
834
|
+
canRedo,
|
|
835
|
+
brush,
|
|
836
|
+
setBrush: (0, react.useCallback)((next) => {
|
|
837
|
+
const value = typeof next === "function" ? next(brushRef.current) : next;
|
|
838
|
+
brushRef.current = value;
|
|
839
|
+
setBrushState(value);
|
|
840
|
+
const instance = instanceRef.current;
|
|
841
|
+
if (instance) {
|
|
842
|
+
instance.brush = value;
|
|
843
|
+
if (value.mode) instance.mode = value.mode;
|
|
844
|
+
}
|
|
845
|
+
}, []),
|
|
846
|
+
onChanged,
|
|
847
|
+
onCommitted,
|
|
848
|
+
onStart,
|
|
849
|
+
onEnd,
|
|
850
|
+
onCanceled
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
//#endregion
|
|
854
|
+
//#region useFocusTrap/index.tsx
|
|
855
|
+
/**
|
|
856
|
+
* Resolve one target item to a focus-trap container: a selector string, a DOM
|
|
857
|
+
* element, or `null` when it cannot be resolved. Upstream resolves elements
|
|
858
|
+
* with `unrefElement` (`@vueuse/core`); the React port composes the same
|
|
859
|
+
* unwrapping from `toValue` / `isRefLike` (`@reause/shared`) — one pass
|
|
860
|
+
* unwraps a ref-like object, a second one covers a ref-like object holding
|
|
861
|
+
* another ref-like (`{ current: { current: element } }`).
|
|
862
|
+
*/
|
|
863
|
+
function resolveElement$1(value) {
|
|
864
|
+
let el = (0, _reause_shared.toValue)(value);
|
|
865
|
+
if (typeof el !== "string" && (0, _reause_shared.isRefLike)(el)) el = (0, _reause_shared.toValue)(el);
|
|
866
|
+
if (typeof el === "string") return el;
|
|
867
|
+
if (typeof el === "object" && el !== null && (el instanceof HTMLElement || el instanceof SVGElement)) return el;
|
|
868
|
+
return null;
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* React port of VueUse's `useFocusTrap` — trap focus within one or more
|
|
872
|
+
* elements.
|
|
873
|
+
*
|
|
874
|
+
* Map from @vueuse/integrations `useFocusTrap`
|
|
875
|
+
* (`source/vueuse/packages/integrations/useFocusTrap/`), a reactive wrapper
|
|
876
|
+
* around the [`focus-trap`](https://github.com/focus-trap/focus-trap) library
|
|
877
|
+
* that keeps focus trapped inside the target element(s) while the trap is
|
|
878
|
+
* active.
|
|
879
|
+
*
|
|
880
|
+
* Adjustment for React: upstream creates the trap inside a `watch` over the
|
|
881
|
+
* resolved targets and exposes `ShallowRef`s for `hasFocus` / `isPaused`. The
|
|
882
|
+
* React port creates the `createFocusTrap` instance in an effect keyed on the
|
|
883
|
+
* resolved targets (mirroring the `watch`), keeps it for the lifetime of the
|
|
884
|
+
* component — target changes go through `updateContainerElements` — and
|
|
885
|
+
* deactivates it on unmount (`tryOnScopeDispose`). `hasFocus` / `isPaused`
|
|
886
|
+
* are plain booleans driven by focus-trap's `onActivate` / `onDeactivate`
|
|
887
|
+
* events plus the pause / unpause calls, and `activate` / `deactivate` /
|
|
888
|
+
* `pause` / `unpause` are stable callbacks delegating to the current trap
|
|
889
|
+
* instance. The `immediate` option activates the trap as soon as the target
|
|
890
|
+
* elements are available.
|
|
891
|
+
*
|
|
892
|
+
* SSR-safe: no `window` or DOM access at module scope — the trap is created
|
|
893
|
+
* lazily inside the effect.
|
|
894
|
+
*
|
|
895
|
+
* @param target - element, React ref object (`{ current }`), selector string,
|
|
896
|
+
* or an array of them
|
|
897
|
+
* @param options - focus-trap options (see
|
|
898
|
+
* https://github.com/focus-trap/focus-trap#createoptions) plus the
|
|
899
|
+
* `immediate` shortcut
|
|
900
|
+
*
|
|
901
|
+
* @example
|
|
902
|
+
* const target = useRef<HTMLDivElement>(null)
|
|
903
|
+
* const { hasFocus, isPaused, activate, deactivate, pause, unpause } = useFocusTrap(target)
|
|
904
|
+
* activate() // traps focus inside target
|
|
905
|
+
*/
|
|
906
|
+
function useFocusTrap(target, options = {}) {
|
|
907
|
+
const [hasFocus, setHasFocus] = (0, react.useState)(false);
|
|
908
|
+
const [isPaused, setIsPaused] = (0, react.useState)(false);
|
|
909
|
+
const trapRef = (0, react.useRef)(null);
|
|
910
|
+
const targetRef = (0, react.useRef)(target);
|
|
911
|
+
targetRef.current = target;
|
|
912
|
+
const optionsRef = (0, react.useRef)(options);
|
|
913
|
+
optionsRef.current = options;
|
|
914
|
+
const resolvedTargetsRef = (0, react.useRef)(null);
|
|
915
|
+
(0, react.useEffect)(() => {
|
|
916
|
+
const targets = (0, _reause_shared.toArray)((0, _reause_shared.toValue)(targetRef.current)).map(resolveElement$1).filter((el) => el != null);
|
|
917
|
+
const previous = resolvedTargetsRef.current;
|
|
918
|
+
resolvedTargetsRef.current = targets;
|
|
919
|
+
if (previous && targets.length === previous.length && targets.every((el, index) => el === previous[index])) return;
|
|
920
|
+
if (!targets.length) return;
|
|
921
|
+
const trap = trapRef.current;
|
|
922
|
+
if (!trap) {
|
|
923
|
+
const { immediate, ...focusTrapOptions } = optionsRef.current;
|
|
924
|
+
trapRef.current = (0, focus_trap.createFocusTrap)(targets, {
|
|
925
|
+
...focusTrapOptions,
|
|
926
|
+
onActivate(params) {
|
|
927
|
+
var _optionsRef$current$o, _optionsRef$current;
|
|
928
|
+
setHasFocus(true);
|
|
929
|
+
(_optionsRef$current$o = (_optionsRef$current = optionsRef.current).onActivate) === null || _optionsRef$current$o === void 0 || _optionsRef$current$o.call(_optionsRef$current, params);
|
|
930
|
+
},
|
|
931
|
+
onDeactivate(params) {
|
|
932
|
+
var _optionsRef$current$o2, _optionsRef$current2;
|
|
933
|
+
setHasFocus(false);
|
|
934
|
+
(_optionsRef$current$o2 = (_optionsRef$current2 = optionsRef.current).onDeactivate) === null || _optionsRef$current$o2 === void 0 || _optionsRef$current$o2.call(_optionsRef$current2, params);
|
|
935
|
+
}
|
|
936
|
+
});
|
|
937
|
+
if (immediate) trapRef.current.activate();
|
|
938
|
+
} else {
|
|
939
|
+
const isActive = trap.active;
|
|
940
|
+
trap.updateContainerElements(targets);
|
|
941
|
+
if (!isActive && optionsRef.current.immediate) trap.activate();
|
|
942
|
+
}
|
|
943
|
+
});
|
|
944
|
+
(0, react.useEffect)(() => () => {
|
|
945
|
+
var _trapRef$current;
|
|
946
|
+
(_trapRef$current = trapRef.current) === null || _trapRef$current === void 0 || _trapRef$current.deactivate();
|
|
947
|
+
trapRef.current = null;
|
|
948
|
+
}, []);
|
|
949
|
+
return {
|
|
950
|
+
hasFocus,
|
|
951
|
+
isPaused,
|
|
952
|
+
activate: (0, react.useCallback)((opts) => {
|
|
953
|
+
var _trapRef$current2;
|
|
954
|
+
(_trapRef$current2 = trapRef.current) === null || _trapRef$current2 === void 0 || _trapRef$current2.activate(opts);
|
|
955
|
+
}, []),
|
|
956
|
+
deactivate: (0, react.useCallback)((opts) => {
|
|
957
|
+
var _trapRef$current3;
|
|
958
|
+
(_trapRef$current3 = trapRef.current) === null || _trapRef$current3 === void 0 || _trapRef$current3.deactivate(opts);
|
|
959
|
+
}, []),
|
|
960
|
+
pause: (0, react.useCallback)(() => {
|
|
961
|
+
if (trapRef.current) {
|
|
962
|
+
trapRef.current.pause();
|
|
963
|
+
setIsPaused(true);
|
|
964
|
+
}
|
|
965
|
+
}, []),
|
|
966
|
+
unpause: (0, react.useCallback)(() => {
|
|
967
|
+
if (trapRef.current) {
|
|
968
|
+
trapRef.current.unpause();
|
|
969
|
+
setIsPaused(false);
|
|
970
|
+
}
|
|
971
|
+
}, [])
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
//#endregion
|
|
975
|
+
//#region useFuse/index.tsx
|
|
976
|
+
/**
|
|
977
|
+
* React port of VueUse's `useFuse` — easily implement fuzzy search with
|
|
978
|
+
* [Fuse.js](https://github.com/krisk/fuse).
|
|
979
|
+
*
|
|
980
|
+
* Map from @vueuse/integrations `useFuse`
|
|
981
|
+
* (`source/vueuse/packages/integrations/useFuse/`), a reactive wrapper around
|
|
982
|
+
* a `Fuse` instance. `search` and `data` are the hook's **read-only value
|
|
983
|
+
* sources** and take plain values (`string` and `readonly DataItem[]`; upstream:
|
|
984
|
+
* `MaybeRefOrGetter`). `options` stays `RefOrValue` (a config object, upstream
|
|
985
|
+
* `MaybeRefOrGetter`).
|
|
986
|
+
*
|
|
987
|
+
* Adjustment for React:
|
|
988
|
+
* - upstream returns `{ fuse: Ref<Fuse>, results: ComputedRef<FuseResult[]> }`;
|
|
989
|
+
* here both are plain values read during render — `fuse` is the `Fuse`
|
|
990
|
+
* instance itself (no `.value`), `results` is a plain array;
|
|
991
|
+
* - upstream rebuilds the index in `watch(() => toValue(options)?.fuseOptions,
|
|
992
|
+
* …, { deep: true })` and refreshes the collection in `watch(() => toValue(data), …)`.
|
|
993
|
+
* React has no deep watcher, and serializing `fuseOptions` to compare them by
|
|
994
|
+
* value would break function-valued options (`sortFn`, `getFn`, `keys[].getFn`),
|
|
995
|
+
* so the `Fuse` instance is memoized on the identity of the `data`
|
|
996
|
+
* array and of `fuseOptions` instead: pass a NEW array reference when the data
|
|
997
|
+
* changes, and a memoized `fuseOptions` object for best performance. Mutating
|
|
998
|
+
* the data array in place is not detected (upstream's deep watch was);
|
|
999
|
+
* - `results` is memoized on the search string and the `data` identity, so a
|
|
1000
|
+
* changed `search`/`data` prop recomputes on the next render;
|
|
1001
|
+
* - `options` is NOT widened: it is a config object (upstream
|
|
1002
|
+
* `MaybeRefOrGetter`, a maintainer decision), so a plain options object, a
|
|
1003
|
+
* ref-like `{ current }` object or a getter are the accepted forms.
|
|
1004
|
+
*
|
|
1005
|
+
* @param search - the search query
|
|
1006
|
+
* @param data - the collection to search
|
|
1007
|
+
* @param options - `fuseOptions` (forwarded to `new Fuse()`), `resultLimit`,
|
|
1008
|
+
* `matchAllWhenSearchEmpty`
|
|
1009
|
+
*
|
|
1010
|
+
* @__NO_SIDE_EFFECTS__
|
|
1011
|
+
* @example
|
|
1012
|
+
* const { fuse, results } = useFuse(input, data, {
|
|
1013
|
+
* fuseOptions: { keys: ['firstName', 'lastName'] },
|
|
1014
|
+
* resultLimit: 10,
|
|
1015
|
+
* matchAllWhenSearchEmpty: true,
|
|
1016
|
+
* })
|
|
1017
|
+
* results[0].item // the best match
|
|
1018
|
+
* fuse.search('john') // search the same index directly
|
|
1019
|
+
*/
|
|
1020
|
+
function useFuse(search, data, options) {
|
|
1021
|
+
const dataValue = data;
|
|
1022
|
+
const optionsValue = (0, _reause_shared.toValue)(options);
|
|
1023
|
+
const fuse = (0, react.useMemo)(() => new fuse_js.default(dataValue, optionsValue === null || optionsValue === void 0 ? void 0 : optionsValue.fuseOptions), [dataValue, optionsValue === null || optionsValue === void 0 ? void 0 : optionsValue.fuseOptions]);
|
|
1024
|
+
const searchValue = search;
|
|
1025
|
+
return {
|
|
1026
|
+
fuse,
|
|
1027
|
+
results: (0, react.useMemo)(() => {
|
|
1028
|
+
if ((optionsValue === null || optionsValue === void 0 ? void 0 : optionsValue.matchAllWhenSearchEmpty) && !searchValue) return dataValue.map((item, index) => ({
|
|
1029
|
+
item,
|
|
1030
|
+
refIndex: index
|
|
1031
|
+
}));
|
|
1032
|
+
const limit = optionsValue === null || optionsValue === void 0 ? void 0 : optionsValue.resultLimit;
|
|
1033
|
+
return fuse.search(searchValue, limit ? { limit } : void 0);
|
|
1034
|
+
}, [
|
|
1035
|
+
dataValue,
|
|
1036
|
+
fuse,
|
|
1037
|
+
optionsValue,
|
|
1038
|
+
searchValue
|
|
1039
|
+
])
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
//#endregion
|
|
1043
|
+
//#region useIDBKeyval/index.tsx
|
|
1044
|
+
function defaultOnError(error) {
|
|
1045
|
+
console.error(error);
|
|
1046
|
+
}
|
|
1047
|
+
function defaultSerializer() {
|
|
1048
|
+
return {
|
|
1049
|
+
read: (raw) => raw,
|
|
1050
|
+
write: (value) => value
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
1054
|
+
* Reactive [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API)
|
|
1055
|
+
* store — React port of VueUse's `useIDBKeyval`.
|
|
1056
|
+
*
|
|
1057
|
+
* Map from @vueuse/integrations `useIDBKeyval`
|
|
1058
|
+
* (`source/vueuse/packages/integrations/useIDBKeyval/`), a reactive wrapper
|
|
1059
|
+
* around [`idb-keyval`](https://github.com/jakearchibald/idb-keyval). The value
|
|
1060
|
+
* is persisted under `key`, read once on mount and kept in sync across tabs
|
|
1061
|
+
* through a `BroadcastChannel`.
|
|
1062
|
+
*
|
|
1063
|
+
* React divergences:
|
|
1064
|
+
* - the upstream object return `{ data, isFinished, isSupported, set }` becomes
|
|
1065
|
+
* the state-like tuple `[data, setData, controls]` (§2B), mirroring
|
|
1066
|
+
* `useStorage`: `data` is `T | null` (`null` = removed) and `setData(null)`
|
|
1067
|
+
* deletes the key through `del`;
|
|
1068
|
+
* - **there is no deep watcher.** Upstream writes on *any* mutation of
|
|
1069
|
+
* `data.value` (`watchPausable(data, write, { deep: true })`), so
|
|
1070
|
+
* `data.value.count++` persists by itself. React state has no deep
|
|
1071
|
+
* observation, so writes happen **explicitly through `setData`** — that is
|
|
1072
|
+
* the React contract. Mutating an object held in `data` in place does *not*
|
|
1073
|
+
* persist; call `setData(next)` with a new value instead. The `deep` /
|
|
1074
|
+
* `shallow` / `flush` options are accepted for parity and have no effect;
|
|
1075
|
+
* - `isFinished` / `isSupported` live in the third tuple slot as plain
|
|
1076
|
+
* booleans (upstream: `ShallowRef` / `ComputedRef`), and `isSupported` is
|
|
1077
|
+
* computed synchronously (`typeof window !== 'undefined' && 'BroadcastChannel'
|
|
1078
|
+
* in window`) instead of going through `useSupported`, so a `BroadcastChannel`
|
|
1079
|
+
* is available on the first mount effect;
|
|
1080
|
+
* - `initialValue` is the hook's **read-only value source** and takes a plain
|
|
1081
|
+
* `T` (upstream: `MaybeRefOrGetter<T>`); it is resolved once at mount, as
|
|
1082
|
+
* upstream's `toValue(initialValue)` is — the hook owns writes, so later
|
|
1083
|
+
* prop changes are ignored;
|
|
1084
|
+
* - a `delete` message from another tab resets `data` to the initial value
|
|
1085
|
+
* (upstream parity) but does not re-write the store: incoming syncs never
|
|
1086
|
+
* write back, mirroring upstream's paused watcher;
|
|
1087
|
+
* - the async read and the channel listener never set state after unmount.
|
|
1088
|
+
*
|
|
1089
|
+
* @__NO_SIDE_EFFECTS__
|
|
1090
|
+
* @example
|
|
1091
|
+
* const [data, setData, { isFinished, isSupported }] = useIDBKeyval('my-store', { count: 0 })
|
|
1092
|
+
*
|
|
1093
|
+
* // explicit write (no deep watcher)
|
|
1094
|
+
* setData({ count: 1 })
|
|
1095
|
+
*
|
|
1096
|
+
* // remove the key
|
|
1097
|
+
* await setData(null)
|
|
1098
|
+
*
|
|
1099
|
+
* @see https://vueuse.org/integrations/useIDBKeyval/
|
|
1100
|
+
*/
|
|
1101
|
+
function useIDBKeyval(key, initialValue, options = {}) {
|
|
1102
|
+
var _rawInitRef$current, _optionsRef$current$w;
|
|
1103
|
+
const optionsRef = (0, react.useRef)(options);
|
|
1104
|
+
const rawInitRef = (0, react.useRef)(void 0);
|
|
1105
|
+
(_rawInitRef$current = rawInitRef.current) !== null && _rawInitRef$current !== void 0 || (rawInitRef.current = { value: initialValue });
|
|
1106
|
+
const [data, setDataState] = (0, react.useState)(() => initialValue !== null && initialValue !== void 0 ? initialValue : null);
|
|
1107
|
+
const [isFinished, setIsFinished] = (0, react.useState)(false);
|
|
1108
|
+
const dataRef = (0, react.useRef)(data);
|
|
1109
|
+
const channelRef = (0, react.useRef)(void 0);
|
|
1110
|
+
const mountedRef = (0, react.useRef)(true);
|
|
1111
|
+
const targetWindow = (_optionsRef$current$w = optionsRef.current.window) !== null && _optionsRef$current$w !== void 0 ? _optionsRef$current$w : typeof window !== "undefined" ? window : void 0;
|
|
1112
|
+
const isSupported = !!targetWindow && "BroadcastChannel" in targetWindow;
|
|
1113
|
+
const keyRef = (0, react.useRef)(key);
|
|
1114
|
+
keyRef.current = key;
|
|
1115
|
+
const getSerializer = (0, react.useCallback)(() => {
|
|
1116
|
+
var _optionsRef$current$s;
|
|
1117
|
+
return (_optionsRef$current$s = optionsRef.current.serializer) !== null && _optionsRef$current$s !== void 0 ? _optionsRef$current$s : defaultSerializer();
|
|
1118
|
+
}, []);
|
|
1119
|
+
const read = (0, react.useCallback)(async () => {
|
|
1120
|
+
const { onError = defaultOnError, writeDefaults = true } = optionsRef.current;
|
|
1121
|
+
const rawInit = rawInitRef.current.value;
|
|
1122
|
+
const currentKey = key;
|
|
1123
|
+
try {
|
|
1124
|
+
const rawValue = await (0, idb_keyval.get)(currentKey);
|
|
1125
|
+
if (!mountedRef.current || keyRef.current !== currentKey) return;
|
|
1126
|
+
if (rawValue === void 0) {
|
|
1127
|
+
if (rawInit !== void 0 && rawInit !== null && writeDefaults) await (0, idb_keyval.set)(currentKey, getSerializer().write(rawInit));
|
|
1128
|
+
} else {
|
|
1129
|
+
const value = getSerializer().read(rawValue);
|
|
1130
|
+
dataRef.current = value;
|
|
1131
|
+
setDataState(value);
|
|
1132
|
+
}
|
|
1133
|
+
} catch (error) {
|
|
1134
|
+
if (mountedRef.current) onError(error);
|
|
1135
|
+
}
|
|
1136
|
+
if (mountedRef.current) setIsFinished(true);
|
|
1137
|
+
}, [key, getSerializer]);
|
|
1138
|
+
const write = (0, react.useCallback)(async (value) => {
|
|
1139
|
+
const { onError = defaultOnError } = optionsRef.current;
|
|
1140
|
+
try {
|
|
1141
|
+
if (value == null) {
|
|
1142
|
+
var _channelRef$current;
|
|
1143
|
+
await (0, idb_keyval.del)(key);
|
|
1144
|
+
(_channelRef$current = channelRef.current) === null || _channelRef$current === void 0 || _channelRef$current.postMessage({ type: "delete" });
|
|
1145
|
+
} else {
|
|
1146
|
+
var _channelRef$current2;
|
|
1147
|
+
const serializedValue = getSerializer().write(value);
|
|
1148
|
+
await (0, idb_keyval.update)(key, () => serializedValue);
|
|
1149
|
+
(_channelRef$current2 = channelRef.current) === null || _channelRef$current2 === void 0 || _channelRef$current2.postMessage({
|
|
1150
|
+
type: "set",
|
|
1151
|
+
value: serializedValue
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
} catch (error) {
|
|
1155
|
+
onError(error);
|
|
1156
|
+
}
|
|
1157
|
+
}, [key, getSerializer]);
|
|
1158
|
+
const setData = (0, react.useCallback)(async (value) => {
|
|
1159
|
+
dataRef.current = value;
|
|
1160
|
+
if (mountedRef.current) setDataState(value);
|
|
1161
|
+
await write(value);
|
|
1162
|
+
}, [write]);
|
|
1163
|
+
(0, react.useEffect)(() => {
|
|
1164
|
+
mountedRef.current = true;
|
|
1165
|
+
read();
|
|
1166
|
+
return () => {
|
|
1167
|
+
mountedRef.current = false;
|
|
1168
|
+
};
|
|
1169
|
+
}, [read]);
|
|
1170
|
+
(0, react.useEffect)(() => {
|
|
1171
|
+
const { onError = defaultOnError, listenToStorageChanges = true } = optionsRef.current;
|
|
1172
|
+
if (!listenToStorageChanges || !isSupported) return;
|
|
1173
|
+
const channel = new BroadcastChannel(`vueuse-idb-${JSON.stringify(key)}`);
|
|
1174
|
+
channelRef.current = channel;
|
|
1175
|
+
const onMessageEvent = (event) => {
|
|
1176
|
+
var _event$data;
|
|
1177
|
+
const { type, value } = (_event$data = event.data) !== null && _event$data !== void 0 ? _event$data : {};
|
|
1178
|
+
try {
|
|
1179
|
+
if (type === "delete") {
|
|
1180
|
+
var _value;
|
|
1181
|
+
const rawInit = (_value = rawInitRef.current.value) !== null && _value !== void 0 ? _value : null;
|
|
1182
|
+
dataRef.current = rawInit;
|
|
1183
|
+
setDataState(rawInit);
|
|
1184
|
+
} else if (type === "set") {
|
|
1185
|
+
const next = getSerializer().read(value);
|
|
1186
|
+
dataRef.current = next;
|
|
1187
|
+
setDataState(next);
|
|
1188
|
+
}
|
|
1189
|
+
} catch (error) {
|
|
1190
|
+
onError(error);
|
|
1191
|
+
}
|
|
1192
|
+
};
|
|
1193
|
+
channel.addEventListener("message", onMessageEvent, { passive: true });
|
|
1194
|
+
return () => {
|
|
1195
|
+
channel.removeEventListener("message", onMessageEvent);
|
|
1196
|
+
if (channelRef.current === channel) channelRef.current = void 0;
|
|
1197
|
+
channel.close();
|
|
1198
|
+
};
|
|
1199
|
+
}, [
|
|
1200
|
+
key,
|
|
1201
|
+
isSupported,
|
|
1202
|
+
getSerializer
|
|
1203
|
+
]);
|
|
1204
|
+
return [
|
|
1205
|
+
data,
|
|
1206
|
+
setData,
|
|
1207
|
+
{
|
|
1208
|
+
isFinished,
|
|
1209
|
+
isSupported
|
|
1210
|
+
}
|
|
1211
|
+
];
|
|
1212
|
+
}
|
|
1213
|
+
//#endregion
|
|
1214
|
+
//#region useJwt/index.tsx
|
|
1215
|
+
/**
|
|
1216
|
+
* React port of VueUse's `useJwt`.
|
|
1217
|
+
*
|
|
1218
|
+
* Map from @vueuse/integrations `useJwt`
|
|
1219
|
+
* (`source/vueuse/packages/integrations/useJwt/`), a wrapper for
|
|
1220
|
+
* [`jwt-decode`](https://github.com/auth0/jwt-decode). `encodedJwt` is the
|
|
1221
|
+
* hook's **read-only value source** and takes a plain string (upstream:
|
|
1222
|
+
* `MaybeRefOrGetter<string>`); the decode is memoized on the token, so a
|
|
1223
|
+
* stable token keeps `header`/`payload` referentially stable across renders.
|
|
1224
|
+
*
|
|
1225
|
+
* Adjustment for React:
|
|
1226
|
+
* - upstream returns `{ header, payload }` as `ComputedRef`s; here the two
|
|
1227
|
+
* fields are plain decoded values (the issue's own `const { header, payload }
|
|
1228
|
+
* = useJwt(encodedJwt)` shape), so read them directly instead of `.value`;
|
|
1229
|
+
* - `fallbackValue` defaults to `null`, and `onError` is kept in a ref so an
|
|
1230
|
+
* inline arrow callback does not churn the memo dependencies;
|
|
1231
|
+
* - decoding happens during render, so under React StrictMode's dev
|
|
1232
|
+
* double-render a bad token may invoke `onError` twice. This is unavoidable
|
|
1233
|
+
* while decoding during render; guard the callback if it must fire once.
|
|
1234
|
+
*
|
|
1235
|
+
* @__NO_SIDE_EFFECTS__
|
|
1236
|
+
* @see https://vueuse.org/useJwt
|
|
1237
|
+
* @example
|
|
1238
|
+
* const { header, payload } = useJwt(encodedJwt)
|
|
1239
|
+
* header.alg // 'HS256'
|
|
1240
|
+
* payload.sub // '1234567890'
|
|
1241
|
+
*/
|
|
1242
|
+
function useJwt(encodedJwt, options = {}) {
|
|
1243
|
+
const { onError, fallbackValue = null } = options;
|
|
1244
|
+
const onErrorRef = (0, react.useRef)(onError);
|
|
1245
|
+
onErrorRef.current = onError;
|
|
1246
|
+
const fallbackValueRef = (0, react.useRef)(fallbackValue);
|
|
1247
|
+
fallbackValueRef.current = fallbackValue;
|
|
1248
|
+
const token = encodedJwt;
|
|
1249
|
+
const decodeWithFallback = (value, jwtOptions) => {
|
|
1250
|
+
try {
|
|
1251
|
+
return (0, jwt_decode.jwtDecode)(value, jwtOptions);
|
|
1252
|
+
} catch (err) {
|
|
1253
|
+
var _onErrorRef$current;
|
|
1254
|
+
(_onErrorRef$current = onErrorRef.current) === null || _onErrorRef$current === void 0 || _onErrorRef$current.call(onErrorRef, err);
|
|
1255
|
+
return fallbackValueRef.current;
|
|
1256
|
+
}
|
|
1257
|
+
};
|
|
1258
|
+
return {
|
|
1259
|
+
header: (0, react.useMemo)(() => decodeWithFallback(token, { header: true }), [token]),
|
|
1260
|
+
payload: (0, react.useMemo)(() => decodeWithFallback(token), [token])
|
|
1261
|
+
};
|
|
1262
|
+
}
|
|
1263
|
+
//#endregion
|
|
1264
|
+
//#region useNProgress/index.tsx
|
|
1265
|
+
/**
|
|
1266
|
+
* React port of VueUse's `useNProgress`.
|
|
1267
|
+
*
|
|
1268
|
+
* Map from @vueuse/integrations `useNProgress`
|
|
1269
|
+
* (`source/vueuse/packages/integrations/useNProgress/index.ts`), a reactive
|
|
1270
|
+
* wrapper around the [`nprogress`](https://github.com/rstacruz/nprogress)
|
|
1271
|
+
* progress bar. `currentProgress` is the hook's **read-only value source** and
|
|
1272
|
+
* takes a plain `number | null | undefined` (upstream: `MaybeRefOrGetter`).
|
|
1273
|
+
*
|
|
1274
|
+
* Adjustment for React:
|
|
1275
|
+
* - the writable `WritableComputedRef<boolean>` `isLoading` and the
|
|
1276
|
+
* `Ref<number | null | undefined>` `progress` become plain state: `isLoading`
|
|
1277
|
+
* is derived (`typeof progress === 'number' && progress < 1`) and written
|
|
1278
|
+
* through `setIsLoading`, `progress` is a plain number written through
|
|
1279
|
+
* `setProgress`; the object return mirrors upstream's object of refs with
|
|
1280
|
+
* every writable value paired with its setter —
|
|
1281
|
+
* `{ progress, setProgress, isLoading, setIsLoading, start, done, remove }`;
|
|
1282
|
+
* - the setters are React `Dispatch<SetStateAction<...>>`: each accepts a plain
|
|
1283
|
+
* value or a functional updater (`prev => next`), like a `useState` setter;
|
|
1284
|
+
* - upstream monkey-patches the module-singleton `nprogress.set` so that its
|
|
1285
|
+
* internal `set` calls (`start` → `set(0)`, `done` → `set(1)`) write back
|
|
1286
|
+
* into `progress.value`, which is what makes `isLoading` flip. This port
|
|
1287
|
+
* never touches the global `nprogress.set`: `setProgress(n)` only sets the
|
|
1288
|
+
* state and the progress effect pushes the number into the bar once per
|
|
1289
|
+
* render, while `start` / `done` / `setIsLoading` mirror the same write-back
|
|
1290
|
+
* by hand (upstream's `start` only calls `set(0)` when the bar was idle, and
|
|
1291
|
+
* `done` only calls `set(1)` when it actually progresses) — so `isLoading`
|
|
1292
|
+
* flips exactly as upstream, without global pollution and safely with
|
|
1293
|
+
* concurrent hook instances;
|
|
1294
|
+
* - `options` are applied once on mount (`nprogress.configure`), like
|
|
1295
|
+
* upstream's setup-time `if (options) nprogress.configure(options)`; later
|
|
1296
|
+
* `options` changes are not re-applied, matching the upstream setup
|
|
1297
|
+
* semantics;
|
|
1298
|
+
* - a provided `currentProgress` is mirrored into the internal state (upstream's
|
|
1299
|
+
* `toRef`): a changed plain number re-syncs between renders, and a write
|
|
1300
|
+
* through `setProgress` / `start` / `done` is only superseded by a genuine
|
|
1301
|
+
* external change. Writes are **not** propagated back to the caller
|
|
1302
|
+
* (upstream's `toRef` writes through to a ref input); the source is
|
|
1303
|
+
* read-only here;
|
|
1304
|
+
* - unmount runs `nprogress.remove()`, mirroring upstream's
|
|
1305
|
+
* `tryOnScopeDispose(nprogress.remove)`. `nprogress` is a module singleton,
|
|
1306
|
+
* so unmounting one hook instance removes the shared bar — the same
|
|
1307
|
+
* semantics as upstream.
|
|
1308
|
+
*
|
|
1309
|
+
* SSR-safe: `nprogress.set` is only called when `isClient` (upstream's
|
|
1310
|
+
* `watchEffect` guard).
|
|
1311
|
+
*
|
|
1312
|
+
* @param currentProgress - initial progress percentage (`0..1`); a changed
|
|
1313
|
+
* plain value re-syncs on the next render
|
|
1314
|
+
* @param options - `nprogress.configure` options, applied once on mount
|
|
1315
|
+
*
|
|
1316
|
+
* @__NO_SIDE_EFFECTS__
|
|
1317
|
+
* @example
|
|
1318
|
+
* const { progress, setProgress, isLoading, setIsLoading, done, remove } = useNProgress()
|
|
1319
|
+
* setIsLoading(true) // starts the bar, isLoading === true
|
|
1320
|
+
* setProgress(0.5) // progress === 0.5, the bar renders at 50%
|
|
1321
|
+
* setProgress(prev => (prev ?? 0) + 0.1) // functional updater, like a useState setter
|
|
1322
|
+
* done() // progress === 1, isLoading === false
|
|
1323
|
+
* remove() // progress === null, the #nprogress element is gone
|
|
1324
|
+
*/
|
|
1325
|
+
function useNProgress(currentProgress = null, options) {
|
|
1326
|
+
const [progress, setProgressState] = (0, react.useState)(currentProgress);
|
|
1327
|
+
const progressRef = (0, react.useRef)(currentProgress);
|
|
1328
|
+
const updateProgress = (0, react.useCallback)((next) => {
|
|
1329
|
+
progressRef.current = next;
|
|
1330
|
+
setProgressState(next);
|
|
1331
|
+
}, []);
|
|
1332
|
+
const lastExternalRef = (0, react.useRef)(currentProgress);
|
|
1333
|
+
(0, react.useEffect)(() => {
|
|
1334
|
+
if (!Object.is(currentProgress, lastExternalRef.current)) {
|
|
1335
|
+
lastExternalRef.current = currentProgress;
|
|
1336
|
+
updateProgress(currentProgress);
|
|
1337
|
+
}
|
|
1338
|
+
});
|
|
1339
|
+
const optionsRef = (0, react.useRef)(options);
|
|
1340
|
+
optionsRef.current = options;
|
|
1341
|
+
(0, react.useEffect)(() => {
|
|
1342
|
+
if (optionsRef.current) nprogress.default.configure(optionsRef.current);
|
|
1343
|
+
}, []);
|
|
1344
|
+
(0, react.useEffect)(() => {
|
|
1345
|
+
if (typeof progress === "number" && _reause_shared.isClient) nprogress.default.set(progress);
|
|
1346
|
+
}, [progress]);
|
|
1347
|
+
(0, react.useEffect)(() => () => {
|
|
1348
|
+
nprogress.default.remove();
|
|
1349
|
+
}, []);
|
|
1350
|
+
const setProgress = (0, react.useCallback)((updater) => {
|
|
1351
|
+
const next = typeof updater === "function" ? updater(progressRef.current) : updater;
|
|
1352
|
+
updateProgress(next);
|
|
1353
|
+
}, [updateProgress]);
|
|
1354
|
+
const start = (0, react.useCallback)(() => {
|
|
1355
|
+
const wasStarted = nprogress.default.isStarted();
|
|
1356
|
+
const result = nprogress.default.start();
|
|
1357
|
+
if (!wasStarted) updateProgress(0);
|
|
1358
|
+
return result;
|
|
1359
|
+
}, [updateProgress]);
|
|
1360
|
+
const done = (0, react.useCallback)((force) => {
|
|
1361
|
+
const status = nprogress.default.status;
|
|
1362
|
+
const result = nprogress.default.done(force);
|
|
1363
|
+
if (force || status) updateProgress(1);
|
|
1364
|
+
return result;
|
|
1365
|
+
}, [updateProgress]);
|
|
1366
|
+
const setIsLoading = (0, react.useCallback)((updater) => {
|
|
1367
|
+
if (typeof updater === "function" ? updater(typeof progressRef.current === "number" && progressRef.current < 1) : updater) start();
|
|
1368
|
+
else done();
|
|
1369
|
+
}, [start, done]);
|
|
1370
|
+
const remove = (0, react.useCallback)(() => {
|
|
1371
|
+
updateProgress(null);
|
|
1372
|
+
nprogress.default.remove();
|
|
1373
|
+
}, [updateProgress]);
|
|
1374
|
+
return {
|
|
1375
|
+
progress,
|
|
1376
|
+
setProgress,
|
|
1377
|
+
isLoading: typeof progress === "number" && progress < 1,
|
|
1378
|
+
setIsLoading,
|
|
1379
|
+
start,
|
|
1380
|
+
done,
|
|
1381
|
+
remove
|
|
1382
|
+
};
|
|
1383
|
+
}
|
|
1384
|
+
//#endregion
|
|
1385
|
+
//#region useQRCode/index.tsx
|
|
1386
|
+
/**
|
|
1387
|
+
* React port of VueUse's `useQRCode`.
|
|
1388
|
+
*
|
|
1389
|
+
* Map from @vueuse/integrations `useQRCode`
|
|
1390
|
+
* (`source/vueuse/packages/integrations/useQRCode/`), a reactive wrapper
|
|
1391
|
+
* around the `qrcode` package: it encodes `text` into a PNG data URL.
|
|
1392
|
+
*
|
|
1393
|
+
* Adjustment for React:
|
|
1394
|
+
* - upstream returns a `shallowRef<string>` holding the data URL; in React the
|
|
1395
|
+
* asynchronous result is state, so the hook returns the plain `string` data
|
|
1396
|
+
* URL directly (no `.value`, no tuple) — `''` until the first encode
|
|
1397
|
+
* resolves. Exactly like upstream, an empty `text` never encodes, so the
|
|
1398
|
+
* previous data URL is kept (it is not cleared);
|
|
1399
|
+
* - `text` is the hook's **read-only value source** and takes a plain string
|
|
1400
|
+
* (upstream: `MaybeRefOrGetter<string>`); a changed `text` prop re-encodes
|
|
1401
|
+
* on the next render;
|
|
1402
|
+
* - the encode is `await`-free but guarded by a `cancelled` flag: when `text`
|
|
1403
|
+
* (or `options`) changes or the component unmounts while an encode is still
|
|
1404
|
+
* pending, the stale promise is ignored, so the last write always wins;
|
|
1405
|
+
* - **pass a memoized `options` object** (`useMemo`/module constant). React has
|
|
1406
|
+
* no dependency-tracking equivalent of Vue's `watch` source, so `options` is
|
|
1407
|
+
* compared by identity: a fresh literal on every render re-runs the effect
|
|
1408
|
+
* and re-encodes the QR code. Options are deliberately not serialized — they
|
|
1409
|
+
* may contain non-JSON values (`toSJISFunc`, `color` functions…), which
|
|
1410
|
+
* serialization would break.
|
|
1411
|
+
*
|
|
1412
|
+
* @__NO_SIDE_EFFECTS__
|
|
1413
|
+
* @see https://vueuse.org/useQRCode
|
|
1414
|
+
* @param text - the text to encode (plain string)
|
|
1415
|
+
* @param options - `qrcode` `toDataURL` options, memoized by the caller
|
|
1416
|
+
* @example
|
|
1417
|
+
* const qrcode = useQRCode('https://vueuse.org')
|
|
1418
|
+
* qrcode // '' at first, then 'data:image/png;base64,…'
|
|
1419
|
+
*/
|
|
1420
|
+
function useQRCode(text, options) {
|
|
1421
|
+
const value = text;
|
|
1422
|
+
const [result, setResult] = (0, react.useState)("");
|
|
1423
|
+
(0, react.useEffect)(() => {
|
|
1424
|
+
if (!value || !_reause_shared.isClient) return;
|
|
1425
|
+
let cancelled = false;
|
|
1426
|
+
qrcode.toDataURL(value, options).then((url) => {
|
|
1427
|
+
if (!cancelled) setResult(url);
|
|
1428
|
+
});
|
|
1429
|
+
return () => {
|
|
1430
|
+
cancelled = true;
|
|
1431
|
+
};
|
|
1432
|
+
}, [value, options]);
|
|
1433
|
+
return result;
|
|
1434
|
+
}
|
|
1435
|
+
//#endregion
|
|
1436
|
+
//#region useSortable/index.tsx
|
|
1437
|
+
/**
|
|
1438
|
+
* Resolve a target to a DOM element, or `null` when it cannot be resolved.
|
|
1439
|
+
* Upstream resolves elements with `unrefElement` (`@vueuse/core`); the React
|
|
1440
|
+
* port composes the same unwrapping from `toValue` / `isRefLike`
|
|
1441
|
+
* (`@reause/shared`) — one pass unwraps a ref-like object, a second one
|
|
1442
|
+
* covers a ref-like object holding another ref-like
|
|
1443
|
+
* (`{ current: { current: element } }`).
|
|
1444
|
+
*/
|
|
1445
|
+
function resolveElement(value) {
|
|
1446
|
+
let el = (0, _reause_shared.toValue)(value);
|
|
1447
|
+
if ((0, _reause_shared.isRefLike)(el)) el = (0, _reause_shared.toValue)(el);
|
|
1448
|
+
if (typeof el === "object" && el !== null && (el instanceof HTMLElement || el instanceof SVGElement)) return el;
|
|
1449
|
+
return null;
|
|
1450
|
+
}
|
|
1451
|
+
/**
|
|
1452
|
+
* React port of VueUse's `useSortable` — wrapper for
|
|
1453
|
+
* [`sortablejs`](https://github.com/SortableJS/Sortable).
|
|
1454
|
+
*
|
|
1455
|
+
* Map from @vueuse/integrations `useSortable`
|
|
1456
|
+
* (`source/vueuse/packages/integrations/useSortable/`), a reactive wrapper
|
|
1457
|
+
* around the `sortablejs` package.
|
|
1458
|
+
*
|
|
1459
|
+
* Adjustment for React: the list is **immutable**. Upstream's
|
|
1460
|
+
* `moveArrayElement` mutates the caller's array in place (deferring the splice
|
|
1461
|
+
* through `nextTick` when the list is a ref), which cannot work in React —
|
|
1462
|
+
* an in-place mutation does not re-render. The reause
|
|
1463
|
+
* `moveArrayElement(list, from, to, e)` is pure: it returns a NEW reordered
|
|
1464
|
+
* array (and still performs upstream's DOM fixup when an event is given), and
|
|
1465
|
+
* the hook forwards that array to `options.onUpdate`.
|
|
1466
|
+
*
|
|
1467
|
+
* Adjustment for React: sortablejs manipulates DOM nodes directly, so the
|
|
1468
|
+
* component must be a controlled list that re-renders from `onUpdate` — stable
|
|
1469
|
+
* `key`s recommended — otherwise React and the DOM desync. Upstream's
|
|
1470
|
+
* `onMounted` maps to a mount effect, `onScopeDispose` to the effect cleanup,
|
|
1471
|
+
* and `watchElement` to an effect keyed on the resolved element identity.
|
|
1472
|
+
*
|
|
1473
|
+
* @param el target element, React ref object (`{ current }`), or CSS selector
|
|
1474
|
+
* @param list current list items, in render order (never mutated)
|
|
1475
|
+
* @param options sortablejs options plus `watchElement` / `document` / `onUpdate`
|
|
1476
|
+
*
|
|
1477
|
+
* @__NO_SIDE_EFFECTS__
|
|
1478
|
+
* @see https://github.com/SortableJS/Sortable#options
|
|
1479
|
+
*/
|
|
1480
|
+
function useSortable(el, list, options = {}) {
|
|
1481
|
+
const { document = globalThis.document, watchElement = false, onUpdate, ...resetOptions } = options;
|
|
1482
|
+
const instanceRef = (0, react.useRef)(void 0);
|
|
1483
|
+
const listRef = (0, react.useRef)(list);
|
|
1484
|
+
const onUpdateRef = (0, react.useRef)(onUpdate);
|
|
1485
|
+
const optionsRef = (0, react.useRef)(resetOptions);
|
|
1486
|
+
listRef.current = list;
|
|
1487
|
+
onUpdateRef.current = onUpdate;
|
|
1488
|
+
optionsRef.current = resetOptions;
|
|
1489
|
+
const cleanup = (0, react.useCallback)(() => {
|
|
1490
|
+
var _instanceRef$current;
|
|
1491
|
+
(_instanceRef$current = instanceRef.current) === null || _instanceRef$current === void 0 || _instanceRef$current.destroy();
|
|
1492
|
+
instanceRef.current = void 0;
|
|
1493
|
+
}, []);
|
|
1494
|
+
const initSortable = (0, react.useCallback)((target) => {
|
|
1495
|
+
if (!target || instanceRef.current !== void 0) return;
|
|
1496
|
+
instanceRef.current = new sortablejs.default(target, {
|
|
1497
|
+
onUpdate: (e) => {
|
|
1498
|
+
var _onUpdateRef$current;
|
|
1499
|
+
const newList = moveArrayElement(listRef.current, e.oldIndex, e.newIndex, e);
|
|
1500
|
+
(_onUpdateRef$current = onUpdateRef.current) === null || _onUpdateRef$current === void 0 || _onUpdateRef$current.call(onUpdateRef, newList, e);
|
|
1501
|
+
},
|
|
1502
|
+
...optionsRef.current
|
|
1503
|
+
});
|
|
1504
|
+
}, []);
|
|
1505
|
+
const start = (0, react.useCallback)(() => {
|
|
1506
|
+
const target = typeof el === "string" ? document === null || document === void 0 ? void 0 : document.querySelector(el) : resolveElement(el);
|
|
1507
|
+
if (target) initSortable(target);
|
|
1508
|
+
}, [
|
|
1509
|
+
document,
|
|
1510
|
+
el,
|
|
1511
|
+
initSortable
|
|
1512
|
+
]);
|
|
1513
|
+
const stop = (0, react.useCallback)(() => {
|
|
1514
|
+
cleanup();
|
|
1515
|
+
}, [cleanup]);
|
|
1516
|
+
const option = (0, react.useCallback)((name, value) => {
|
|
1517
|
+
var _instanceRef$current2, _instanceRef$current3;
|
|
1518
|
+
if (value !== void 0) (_instanceRef$current2 = instanceRef.current) === null || _instanceRef$current2 === void 0 || _instanceRef$current2.option(name, value);
|
|
1519
|
+
else return (_instanceRef$current3 = instanceRef.current) === null || _instanceRef$current3 === void 0 ? void 0 : _instanceRef$current3.option(name);
|
|
1520
|
+
}, []);
|
|
1521
|
+
const resolved = typeof el === "string" ? el : resolveElement(el);
|
|
1522
|
+
(0, react.useEffect)(() => {
|
|
1523
|
+
if (!watchElement || typeof resolved === "string") return;
|
|
1524
|
+
cleanup();
|
|
1525
|
+
if (resolved) initSortable(resolved);
|
|
1526
|
+
return cleanup;
|
|
1527
|
+
}, [
|
|
1528
|
+
cleanup,
|
|
1529
|
+
initSortable,
|
|
1530
|
+
resolved,
|
|
1531
|
+
watchElement
|
|
1532
|
+
]);
|
|
1533
|
+
(0, react.useEffect)(() => {
|
|
1534
|
+
if (watchElement && typeof el !== "string") return;
|
|
1535
|
+
start();
|
|
1536
|
+
return cleanup;
|
|
1537
|
+
}, [
|
|
1538
|
+
cleanup,
|
|
1539
|
+
start,
|
|
1540
|
+
watchElement,
|
|
1541
|
+
el
|
|
1542
|
+
]);
|
|
1543
|
+
return {
|
|
1544
|
+
start,
|
|
1545
|
+
stop,
|
|
1546
|
+
option
|
|
1547
|
+
};
|
|
1548
|
+
}
|
|
1549
|
+
/**
|
|
1550
|
+
* Inserts an element into the DOM at a given index.
|
|
1551
|
+
* @param parentElement
|
|
1552
|
+
* @param element
|
|
1553
|
+
* @param {number} index
|
|
1554
|
+
* @see https://github.com/Alfred-Skyblue/vue-draggable-plus/blob/a3829222095e1949bf2c9a20979d7b5930e66f14/src/utils/index.ts#L81C1-L94C2
|
|
1555
|
+
*/
|
|
1556
|
+
function insertNodeAt(parentElement, element, index) {
|
|
1557
|
+
const refElement = parentElement.children[index];
|
|
1558
|
+
parentElement.insertBefore(element, refElement);
|
|
1559
|
+
}
|
|
1560
|
+
/**
|
|
1561
|
+
* Removes a node from the DOM.
|
|
1562
|
+
* @param {Node} node
|
|
1563
|
+
* @see https://github.com/Alfred-Skyblue/vue-draggable-plus/blob/a3829222095e1949bf2c9a20979d7b5930e66f14/src/utils/index.ts#L96C1-L102C2
|
|
1564
|
+
*/
|
|
1565
|
+
function removeNode(node) {
|
|
1566
|
+
if (node.parentNode) node.parentNode.removeChild(node);
|
|
1567
|
+
}
|
|
1568
|
+
/**
|
|
1569
|
+
* Move an element of `list` from `from` to `to`, returning a NEW array.
|
|
1570
|
+
*
|
|
1571
|
+
* Map from @vueuse/integrations `useSortable`'s `moveArrayElement`, with one
|
|
1572
|
+
* deliberate deviation: upstream mutates the caller's array in place (and
|
|
1573
|
+
* defers the splice with `nextTick` when the list is a ref), which cannot work
|
|
1574
|
+
* in React — an in-place mutation does not re-render. This implementation is
|
|
1575
|
+
* pure: the input array is never mutated and the moved copy is returned. The
|
|
1576
|
+
* move only happens when `to` is in range (`to >= 0 && to < list.length`,
|
|
1577
|
+
* exactly upstream's bounds check); otherwise a copy of `list` is returned
|
|
1578
|
+
* unchanged.
|
|
1579
|
+
*
|
|
1580
|
+
* The DOM fixup of upstream is kept for compatibility: when `e` is given (a
|
|
1581
|
+
* sortablejs event), `e.item` is removed from the DOM and re-inserted at
|
|
1582
|
+
* `from` inside `e.from`. That fixup runs regardless of the bounds check, as
|
|
1583
|
+
* upstream does — note the upstream quirk of inserting at `from` rather than
|
|
1584
|
+
* `to`. Pass `null` (or omit `e`) to only compute the array.
|
|
1585
|
+
*
|
|
1586
|
+
* @param list the current list — never mutated
|
|
1587
|
+
* @param from source index
|
|
1588
|
+
* @param to destination index
|
|
1589
|
+
* @param e the sortablejs event to apply the DOM fixup for, if any
|
|
1590
|
+
* @returns a new array with the element moved
|
|
1591
|
+
*/
|
|
1592
|
+
function moveArrayElement(list, from, to, e) {
|
|
1593
|
+
if (e != null) {
|
|
1594
|
+
removeNode(e.item);
|
|
1595
|
+
insertNodeAt(e.from, e.item, from);
|
|
1596
|
+
}
|
|
1597
|
+
const array = [...list];
|
|
1598
|
+
if (to >= 0 && to < array.length) {
|
|
1599
|
+
const element = array.splice(from, 1)[0];
|
|
1600
|
+
array.splice(to, 0, element);
|
|
1601
|
+
}
|
|
1602
|
+
return array;
|
|
1603
|
+
}
|
|
1604
|
+
//#endregion
|
|
1605
|
+
exports.createCookies = createCookies;
|
|
1606
|
+
exports.insertNodeAt = insertNodeAt;
|
|
1607
|
+
exports.moveArrayElement = moveArrayElement;
|
|
1608
|
+
exports.removeNode = removeNode;
|
|
1609
|
+
exports.useAsyncValidator = useAsyncValidator;
|
|
1610
|
+
exports.useAxios = useAxios;
|
|
1611
|
+
exports.useChangeCase = useChangeCase;
|
|
1612
|
+
exports.useCookies = useCookies;
|
|
1613
|
+
exports.useDrauu = useDrauu;
|
|
1614
|
+
exports.useFocusTrap = useFocusTrap;
|
|
1615
|
+
exports.useFuse = useFuse;
|
|
1616
|
+
exports.useIDBKeyval = useIDBKeyval;
|
|
1617
|
+
exports.useJwt = useJwt;
|
|
1618
|
+
exports.useNProgress = useNProgress;
|
|
1619
|
+
exports.useQRCode = useQRCode;
|
|
1620
|
+
exports.useSortable = useSortable;
|
|
1621
|
+
})(this.reause = this.reause || {}, AsyncValidator, React, reause, axios, changeCase, UniversalCookie, Drauu, focusTrap, Fuse, idbKeyval, jwt_decode, nprogress, QRCode, Sortable);
|