@omerdlw/base-framework 1.0.0 → 1.0.1
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 +1 -1
- package/README.md +121 -52
- package/dist/hooks/index.d.ts +1 -10
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +2 -538
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/types.d.ts +0 -53
- package/dist/hooks/types.d.ts.map +1 -1
- package/dist/hooks/use-global-event.d.ts +0 -2
- package/dist/hooks/use-global-event.d.ts.map +1 -1
- package/dist/index.js +138 -742
- package/dist/index.js.map +1 -1
- package/dist/kernel/index.js +3 -3
- package/dist/kernel/index.js.map +1 -1
- package/dist/modules/ambient/index.js.map +1 -1
- package/dist/modules/background/index.js +5 -5
- package/dist/modules/background/index.js.map +1 -1
- package/dist/modules/context-menu/index.js +4 -4
- package/dist/modules/context-menu/index.js.map +1 -1
- package/dist/modules/controls/index.js +3 -3
- package/dist/modules/controls/index.js.map +1 -1
- package/dist/modules/dock/cards.d.ts +1 -1
- package/dist/modules/dock/hooks.d.ts +1 -1
- package/dist/modules/dock/index.js +3 -3
- package/dist/modules/dock/index.js.map +1 -1
- package/dist/modules/index.js +34 -115
- package/dist/modules/index.js.map +1 -1
- package/dist/modules/loading/index.js +3 -3
- package/dist/modules/loading/index.js.map +1 -1
- package/dist/modules/media/index.js.map +1 -1
- package/dist/modules/modal/index.js +11 -95
- package/dist/modules/modal/index.js.map +1 -1
- package/dist/modules/notification/index.js +7 -5
- package/dist/modules/notification/index.js.map +1 -1
- package/dist/provider.js.map +1 -1
- package/dist/utils/dom.d.ts +0 -2
- package/dist/utils/dom.d.ts.map +1 -1
- package/dist/utils/index.d.ts +5 -6
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +19 -84
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/number.d.ts +0 -1
- package/dist/utils/number.d.ts.map +1 -1
- package/dist/utils/object.d.ts +0 -2
- package/dist/utils/object.d.ts.map +1 -1
- package/dist/utils/string.d.ts +0 -3
- package/dist/utils/string.d.ts.map +1 -1
- package/dist/utils/timing.d.ts +0 -1
- package/dist/utils/timing.d.ts.map +1 -1
- package/docs/README.md +124 -0
- package/docs/architecture-and-rules.md +129 -0
- package/docs/core-engine.md +196 -0
- package/docs/custom-modules.md +267 -0
- package/docs/getting-started.md +199 -0
- package/docs/modules/README.md +176 -0
- package/docs/modules/ambient.md +150 -0
- package/docs/modules/background.md +195 -0
- package/docs/modules/context-menu.md +248 -0
- package/docs/modules/controls.md +159 -0
- package/docs/modules/dock.md +734 -0
- package/docs/modules/error-boundary.md +160 -0
- package/docs/modules/kernel.md +266 -0
- package/docs/modules/loading.md +157 -0
- package/docs/modules/media.md +196 -0
- package/docs/modules/modal.md +229 -0
- package/docs/modules/notification.md +161 -0
- package/docs/modules/result.md +135 -0
- package/docs/testing.md +160 -0
- package/docs/theming.md +185 -0
- package/package.json +3 -1
- package/templates/AGENTS.md +165 -0
package/dist/hooks/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { useLayoutEffect, useEffect, use, useRef, useCallback, useSyncExternalStore
|
|
2
|
+
import { useLayoutEffect, useEffect, use, useRef, useCallback, useSyncExternalStore } from 'react';
|
|
3
3
|
import 'clsx';
|
|
4
4
|
import 'tailwind-merge';
|
|
5
5
|
|
|
@@ -67,208 +67,8 @@ function report(scope, error, level = "error") {
|
|
|
67
67
|
console[level](`[${scope}]`, error);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
// src/utils/user-message.ts
|
|
71
|
-
var USER_MESSAGES = Object.freeze({
|
|
72
|
-
generic: "Something went wrong. Please try again",
|
|
73
|
-
network: "You appear to be offline. Check your connection and try again",
|
|
74
|
-
timeout: "That took too long. Please try again",
|
|
75
|
-
unauthorized: "Your session has expired. Please sign in again",
|
|
76
|
-
forbidden: "You don't have permission to do that",
|
|
77
|
-
notFound: "We couldn't find what you were looking for",
|
|
78
|
-
rateLimited: "Too many attempts. Please wait a moment and try again",
|
|
79
|
-
server: "We're having trouble on our end. Please try again in a moment"
|
|
80
|
-
});
|
|
81
|
-
var UserError = class extends Error {
|
|
82
|
-
status;
|
|
83
|
-
constructor(message, status) {
|
|
84
|
-
super(message);
|
|
85
|
-
this.name = "UserError";
|
|
86
|
-
this.status = status;
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
var NETWORK_FAILURE = /failed to fetch|network|load failed|fetch failed/i;
|
|
90
|
-
function readField(source, field, type) {
|
|
91
|
-
const value = source?.[field];
|
|
92
|
-
return typeof value === type ? value : void 0;
|
|
93
|
-
}
|
|
94
|
-
function readPayloadMessage(error) {
|
|
95
|
-
const payload = error?.payload;
|
|
96
|
-
const message = readField(payload, "error", "string")?.trim();
|
|
97
|
-
return message || void 0;
|
|
98
|
-
}
|
|
99
|
-
function toUserMessage(error, { codes, fallback = USER_MESSAGES.generic } = {}) {
|
|
100
|
-
if (typeof error === "string") return error.trim() || fallback;
|
|
101
|
-
if (!error || typeof error !== "object") return fallback;
|
|
102
|
-
if (error instanceof UserError) {
|
|
103
|
-
if (error.status === 401) return USER_MESSAGES.unauthorized;
|
|
104
|
-
return error.message || fallback;
|
|
105
|
-
}
|
|
106
|
-
if (error.name === "AbortError") return fallback;
|
|
107
|
-
const code = readField(error, "code", "string");
|
|
108
|
-
if (code && codes?.[code]) return codes[code];
|
|
109
|
-
const status = readField(error, "status", "number");
|
|
110
|
-
if (status === 401) return USER_MESSAGES.unauthorized;
|
|
111
|
-
if (status === 429) return USER_MESSAGES.rateLimited;
|
|
112
|
-
if (status === 408 || status === 504) return USER_MESSAGES.timeout;
|
|
113
|
-
if (status !== void 0 && status >= 500) return USER_MESSAGES.server;
|
|
114
|
-
if (status !== void 0 && status >= 400) {
|
|
115
|
-
const authored = readPayloadMessage(error);
|
|
116
|
-
if (authored) return authored;
|
|
117
|
-
if (status === 403) return USER_MESSAGES.forbidden;
|
|
118
|
-
if (status === 404) return USER_MESSAGES.notFound;
|
|
119
|
-
return fallback;
|
|
120
|
-
}
|
|
121
|
-
const isOffline = typeof navigator !== "undefined" && navigator.onLine === false;
|
|
122
|
-
if (isOffline || NETWORK_FAILURE.test(error.message ?? "") && (error instanceof TypeError || /fetch/i.test(error.name))) {
|
|
123
|
-
return USER_MESSAGES.network;
|
|
124
|
-
}
|
|
125
|
-
return fallback;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
70
|
// src/hooks/use-isomorphic-layout-effect.ts
|
|
129
71
|
var useIsomorphicLayoutEffect = isBrowser ? useLayoutEffect : useEffect;
|
|
130
|
-
function useDebounce(value, delay = 300) {
|
|
131
|
-
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
132
|
-
useEffect(() => {
|
|
133
|
-
const timer = setTimeout(() => setDebouncedValue(value), delay);
|
|
134
|
-
return () => clearTimeout(timer);
|
|
135
|
-
}, [value, delay]);
|
|
136
|
-
return debouncedValue;
|
|
137
|
-
}
|
|
138
|
-
var emptySubscribe = () => () => {
|
|
139
|
-
};
|
|
140
|
-
function useMounted() {
|
|
141
|
-
return useSyncExternalStore(
|
|
142
|
-
emptySubscribe,
|
|
143
|
-
() => true,
|
|
144
|
-
() => false
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
function useMediaQuery(query, serverFallback = false) {
|
|
148
|
-
const subscribe2 = useCallback(
|
|
149
|
-
(onStoreChange) => {
|
|
150
|
-
if (!isBrowser || !query || typeof window.matchMedia !== "function") {
|
|
151
|
-
return () => {
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
const mediaQueryList = window.matchMedia(query);
|
|
155
|
-
mediaQueryList.addEventListener("change", onStoreChange);
|
|
156
|
-
return () => mediaQueryList.removeEventListener("change", onStoreChange);
|
|
157
|
-
},
|
|
158
|
-
[query]
|
|
159
|
-
);
|
|
160
|
-
const getSnapshot2 = useCallback(() => {
|
|
161
|
-
if (!isBrowser || !query || typeof window.matchMedia !== "function") {
|
|
162
|
-
return serverFallback;
|
|
163
|
-
}
|
|
164
|
-
return window.matchMedia(query).matches;
|
|
165
|
-
}, [query, serverFallback]);
|
|
166
|
-
const getServerSnapshot = useCallback(() => serverFallback, [serverFallback]);
|
|
167
|
-
return useSyncExternalStore(subscribe2, getSnapshot2, getServerSnapshot);
|
|
168
|
-
}
|
|
169
|
-
function useControllableState({
|
|
170
|
-
defaultValue,
|
|
171
|
-
onChange,
|
|
172
|
-
value
|
|
173
|
-
}) {
|
|
174
|
-
const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
|
|
175
|
-
const isControlled = value !== void 0;
|
|
176
|
-
const currentValue = isControlled ? value : uncontrolledValue;
|
|
177
|
-
const valueRef = useRef(currentValue);
|
|
178
|
-
const onChangeRef = useRef(onChange);
|
|
179
|
-
useIsomorphicLayoutEffect(() => {
|
|
180
|
-
valueRef.current = currentValue;
|
|
181
|
-
onChangeRef.current = onChange;
|
|
182
|
-
});
|
|
183
|
-
const setValue = useCallback(
|
|
184
|
-
(next) => {
|
|
185
|
-
const resolvedNext = typeof next === "function" ? next(valueRef.current) : next;
|
|
186
|
-
if (!Object.is(valueRef.current, resolvedNext)) {
|
|
187
|
-
if (!isControlled) {
|
|
188
|
-
setUncontrolledValue(resolvedNext);
|
|
189
|
-
}
|
|
190
|
-
onChangeRef.current?.(resolvedNext);
|
|
191
|
-
}
|
|
192
|
-
},
|
|
193
|
-
[isControlled]
|
|
194
|
-
);
|
|
195
|
-
return [currentValue, setValue];
|
|
196
|
-
}
|
|
197
|
-
function isEditableElement(target) {
|
|
198
|
-
if (!target || typeof target.tagName !== "string") {
|
|
199
|
-
return false;
|
|
200
|
-
}
|
|
201
|
-
const element = target;
|
|
202
|
-
const tagName = element.tagName.toLowerCase();
|
|
203
|
-
if (tagName === "input" || tagName === "textarea" || tagName === "select") {
|
|
204
|
-
return true;
|
|
205
|
-
}
|
|
206
|
-
return Boolean(element.isContentEditable);
|
|
207
|
-
}
|
|
208
|
-
function matchesKeyCombo(event, combo) {
|
|
209
|
-
const parts = combo.toLowerCase().split("+").map((p) => p.trim()).filter(Boolean);
|
|
210
|
-
if (parts.length === 0) return false;
|
|
211
|
-
const keyPart = parts[parts.length - 1];
|
|
212
|
-
const modifiers = new Set(parts.slice(0, -1));
|
|
213
|
-
const wantsMod = modifiers.has("mod") || modifiers.has("cmd") || modifiers.has("meta");
|
|
214
|
-
const wantsCtrl = modifiers.has("ctrl") || modifiers.has("control");
|
|
215
|
-
const wantsAlt = modifiers.has("alt") || modifiers.has("option");
|
|
216
|
-
const wantsShift = modifiers.has("shift");
|
|
217
|
-
const isModPressed = event.metaKey || event.ctrlKey;
|
|
218
|
-
if (wantsMod && !isModPressed) return false;
|
|
219
|
-
if (!wantsMod && !wantsCtrl && (event.metaKey || event.ctrlKey)) return false;
|
|
220
|
-
if (wantsCtrl && !event.ctrlKey) return false;
|
|
221
|
-
if (wantsAlt !== event.altKey) return false;
|
|
222
|
-
if (wantsShift !== event.shiftKey) return false;
|
|
223
|
-
const pressedKey = event.key.toLowerCase();
|
|
224
|
-
if (keyPart === "esc" || keyPart === "escape") {
|
|
225
|
-
return pressedKey === "escape" || pressedKey === "esc";
|
|
226
|
-
}
|
|
227
|
-
if (keyPart === "space") {
|
|
228
|
-
return pressedKey === " " || pressedKey === "spacebar";
|
|
229
|
-
}
|
|
230
|
-
return pressedKey === keyPart;
|
|
231
|
-
}
|
|
232
|
-
function useHotkey(combo, callback, options) {
|
|
233
|
-
const callbackRef = useRef(callback);
|
|
234
|
-
useIsomorphicLayoutEffect(() => {
|
|
235
|
-
callbackRef.current = callback;
|
|
236
|
-
});
|
|
237
|
-
const enabled = options?.enabled ?? true;
|
|
238
|
-
const enableOnFormTags = options?.enableOnFormTags ?? false;
|
|
239
|
-
const preventDefault = options?.preventDefault ?? true;
|
|
240
|
-
const stopPropagation = options?.stopPropagation ?? false;
|
|
241
|
-
useEffect(() => {
|
|
242
|
-
if (!isBrowser || !enabled || !combo) return;
|
|
243
|
-
const combos = (Array.isArray(combo) ? combo : [combo]).filter(Boolean);
|
|
244
|
-
if (combos.length === 0) return;
|
|
245
|
-
const handleKeyDown = (event) => {
|
|
246
|
-
if (!enableOnFormTags && isEditableElement(event.target)) return;
|
|
247
|
-
const matched = combos.some((c) => matchesKeyCombo(event, c));
|
|
248
|
-
if (!matched) return;
|
|
249
|
-
if (preventDefault) event.preventDefault();
|
|
250
|
-
if (stopPropagation) event.stopPropagation();
|
|
251
|
-
callbackRef.current?.(event);
|
|
252
|
-
};
|
|
253
|
-
window.addEventListener("keydown", handleKeyDown);
|
|
254
|
-
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
255
|
-
}, [combo, enabled, enableOnFormTags, preventDefault, stopPropagation]);
|
|
256
|
-
}
|
|
257
|
-
function useEscapeKey(callback, enabled = true) {
|
|
258
|
-
useHotkey("escape", callback, {
|
|
259
|
-
enabled,
|
|
260
|
-
enableOnFormTags: true,
|
|
261
|
-
preventDefault: false
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// src/events.ts
|
|
266
|
-
var EVENT_TYPES = Object.freeze({
|
|
267
|
-
API_UNAUTHORIZED: "API_UNAUTHORIZED",
|
|
268
|
-
STATE_CHANGE: "STATE_CHANGE",
|
|
269
|
-
API_ERROR: "API_ERROR",
|
|
270
|
-
APP_ERROR: "APP_ERROR"
|
|
271
|
-
});
|
|
272
72
|
var EventEmitter = class {
|
|
273
73
|
events = /* @__PURE__ */ new Map();
|
|
274
74
|
debounceTimers = /* @__PURE__ */ new Map();
|
|
@@ -400,342 +200,6 @@ function useGlobalEvent(event, callback, options) {
|
|
|
400
200
|
};
|
|
401
201
|
}, [event, debounceMs, throttleMs]);
|
|
402
202
|
}
|
|
403
|
-
function useEventState(event, initialValue, reducer) {
|
|
404
|
-
const storeRef = useRef(null);
|
|
405
|
-
if (storeRef.current == null) {
|
|
406
|
-
storeRef.current = {
|
|
407
|
-
value: initialValue,
|
|
408
|
-
listeners: /* @__PURE__ */ new Set(),
|
|
409
|
-
reducer
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
useIsomorphicLayoutEffect(() => {
|
|
413
|
-
if (storeRef.current != null) {
|
|
414
|
-
storeRef.current.reducer = reducer;
|
|
415
|
-
}
|
|
416
|
-
});
|
|
417
|
-
const subscribe2 = useCallback(
|
|
418
|
-
(onStoreChange) => {
|
|
419
|
-
if (!event || !storeRef.current) return () => {
|
|
420
|
-
};
|
|
421
|
-
storeRef.current.listeners.add(onStoreChange);
|
|
422
|
-
const unsub = globalEvents.subscribe(event, (payload) => {
|
|
423
|
-
if (!storeRef.current) return;
|
|
424
|
-
const next = storeRef.current.reducer(storeRef.current.value, payload);
|
|
425
|
-
if (!Object.is(storeRef.current.value, next)) {
|
|
426
|
-
storeRef.current.value = next;
|
|
427
|
-
storeRef.current.listeners.forEach((listener) => listener());
|
|
428
|
-
}
|
|
429
|
-
});
|
|
430
|
-
return () => {
|
|
431
|
-
storeRef.current?.listeners.delete(onStoreChange);
|
|
432
|
-
unsub();
|
|
433
|
-
};
|
|
434
|
-
},
|
|
435
|
-
[event]
|
|
436
|
-
);
|
|
437
|
-
const getSnapshot2 = useCallback(() => {
|
|
438
|
-
return storeRef.current?.value ?? initialValue;
|
|
439
|
-
}, [initialValue]);
|
|
440
|
-
const getServerSnapshot = useCallback(() => {
|
|
441
|
-
return initialValue;
|
|
442
|
-
}, [initialValue]);
|
|
443
|
-
return useSyncExternalStore(subscribe2, getSnapshot2, getServerSnapshot);
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
// src/result.ts
|
|
447
|
-
function err(error, code) {
|
|
448
|
-
return { error, success: false, ...{} };
|
|
449
|
-
}
|
|
450
|
-
function isResult(value) {
|
|
451
|
-
return typeof value === "object" && value !== null && "success" in value && typeof value.success === "boolean" && ("data" in value && value.success === true || "error" in value && value.success === false);
|
|
452
|
-
}
|
|
453
|
-
function isErr(result) {
|
|
454
|
-
return result.success === false;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
// src/hooks/use-async-action.ts
|
|
458
|
-
function useAsyncAction(actionFn, options = {}) {
|
|
459
|
-
const {
|
|
460
|
-
errorMessage = null,
|
|
461
|
-
onError = null,
|
|
462
|
-
onSuccess = null,
|
|
463
|
-
successMessage = null,
|
|
464
|
-
toast = null
|
|
465
|
-
} = options;
|
|
466
|
-
const [isPending, setIsPending] = useState(false);
|
|
467
|
-
const [error, setError] = useState(null);
|
|
468
|
-
const execute = useCallback(
|
|
469
|
-
async (...args) => {
|
|
470
|
-
setIsPending(true);
|
|
471
|
-
setError(null);
|
|
472
|
-
try {
|
|
473
|
-
const result = await actionFn(...args);
|
|
474
|
-
if (isResult(result) && isErr(result)) {
|
|
475
|
-
setError(result.error);
|
|
476
|
-
const resolvedMessage = errorMessage || toUserMessage(result.error);
|
|
477
|
-
if (typeof toast === "function") {
|
|
478
|
-
toast(resolvedMessage);
|
|
479
|
-
}
|
|
480
|
-
globalEvents.emit(EVENT_TYPES.APP_ERROR, {
|
|
481
|
-
error: result.error,
|
|
482
|
-
message: resolvedMessage,
|
|
483
|
-
notify: toast === true
|
|
484
|
-
});
|
|
485
|
-
await onError?.(result.error);
|
|
486
|
-
return result;
|
|
487
|
-
}
|
|
488
|
-
if (successMessage) {
|
|
489
|
-
if (typeof toast === "function") {
|
|
490
|
-
toast(successMessage);
|
|
491
|
-
}
|
|
492
|
-
globalEvents.emit(EVENT_TYPES.STATE_CHANGE, {
|
|
493
|
-
message: successMessage,
|
|
494
|
-
notify: toast === true,
|
|
495
|
-
status: "success"
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
await onSuccess?.(result);
|
|
499
|
-
return result;
|
|
500
|
-
} catch (err2) {
|
|
501
|
-
setError(err2);
|
|
502
|
-
const resolvedMessage = errorMessage || toUserMessage(err2);
|
|
503
|
-
if (typeof toast === "function") {
|
|
504
|
-
toast(resolvedMessage);
|
|
505
|
-
}
|
|
506
|
-
globalEvents.emit(EVENT_TYPES.APP_ERROR, {
|
|
507
|
-
error: err2,
|
|
508
|
-
message: resolvedMessage,
|
|
509
|
-
notify: toast === true
|
|
510
|
-
});
|
|
511
|
-
await onError?.(err2);
|
|
512
|
-
throw err2;
|
|
513
|
-
} finally {
|
|
514
|
-
setIsPending(false);
|
|
515
|
-
}
|
|
516
|
-
},
|
|
517
|
-
[actionFn, errorMessage, onError, onSuccess, successMessage, toast]
|
|
518
|
-
);
|
|
519
|
-
return { error, execute, isPending };
|
|
520
|
-
}
|
|
521
|
-
function useServerAction(actionFn, options = {}) {
|
|
522
|
-
const {
|
|
523
|
-
errorMessage = null,
|
|
524
|
-
onError = null,
|
|
525
|
-
onSuccess = null,
|
|
526
|
-
successMessage = null,
|
|
527
|
-
toast = null
|
|
528
|
-
} = options;
|
|
529
|
-
const [isPending, startTransition] = useTransition();
|
|
530
|
-
const [data, setData] = useState(null);
|
|
531
|
-
const [error, setError] = useState(null);
|
|
532
|
-
const reset = useCallback(() => {
|
|
533
|
-
setData(null);
|
|
534
|
-
setError(null);
|
|
535
|
-
}, []);
|
|
536
|
-
const execute = useCallback(
|
|
537
|
-
(...args) => {
|
|
538
|
-
return new Promise((resolve) => {
|
|
539
|
-
startTransition(async () => {
|
|
540
|
-
setError(null);
|
|
541
|
-
try {
|
|
542
|
-
const result = await actionFn(...args);
|
|
543
|
-
if (isErr(result)) {
|
|
544
|
-
setError(result.error);
|
|
545
|
-
const resolvedMessage = errorMessage || toUserMessage(result.error);
|
|
546
|
-
if (typeof toast === "function") {
|
|
547
|
-
toast(resolvedMessage);
|
|
548
|
-
}
|
|
549
|
-
globalEvents.emit(EVENT_TYPES.APP_ERROR, {
|
|
550
|
-
error: result.error,
|
|
551
|
-
message: resolvedMessage,
|
|
552
|
-
notify: toast === true
|
|
553
|
-
});
|
|
554
|
-
await onError?.(result.error);
|
|
555
|
-
resolve(result);
|
|
556
|
-
return;
|
|
557
|
-
}
|
|
558
|
-
setData(result.data);
|
|
559
|
-
if (successMessage) {
|
|
560
|
-
if (typeof toast === "function") {
|
|
561
|
-
toast(successMessage);
|
|
562
|
-
}
|
|
563
|
-
globalEvents.emit(EVENT_TYPES.STATE_CHANGE, {
|
|
564
|
-
message: successMessage,
|
|
565
|
-
notify: toast === true,
|
|
566
|
-
status: "success"
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
|
-
await onSuccess?.(result.data);
|
|
570
|
-
resolve(result);
|
|
571
|
-
} catch (err2) {
|
|
572
|
-
const mappedError = toUserMessage(err2);
|
|
573
|
-
setError(mappedError);
|
|
574
|
-
const resolvedMessage = errorMessage || mappedError;
|
|
575
|
-
if (typeof toast === "function") {
|
|
576
|
-
toast(resolvedMessage);
|
|
577
|
-
}
|
|
578
|
-
globalEvents.emit(EVENT_TYPES.APP_ERROR, {
|
|
579
|
-
error: err2,
|
|
580
|
-
message: resolvedMessage,
|
|
581
|
-
notify: toast === true
|
|
582
|
-
});
|
|
583
|
-
await onError?.(mappedError);
|
|
584
|
-
resolve(err(mappedError));
|
|
585
|
-
}
|
|
586
|
-
});
|
|
587
|
-
});
|
|
588
|
-
},
|
|
589
|
-
[actionFn, errorMessage, onError, onSuccess, successMessage, toast]
|
|
590
|
-
);
|
|
591
|
-
return { data, error, execute, isPending, reset };
|
|
592
|
-
}
|
|
593
|
-
function getStorageInstance(type) {
|
|
594
|
-
if (!isBrowser) return null;
|
|
595
|
-
try {
|
|
596
|
-
return type === "session" ? window.sessionStorage : window.localStorage;
|
|
597
|
-
} catch {
|
|
598
|
-
return null;
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
var STORAGE_SYNC_EVENT = "base-framework:storage-sync";
|
|
602
|
-
function useStorageState(key, initialValue, options = {}) {
|
|
603
|
-
const storageType = options.storage ?? "local";
|
|
604
|
-
const serialize = options.serialize ?? JSON.stringify;
|
|
605
|
-
const deserialize = options.deserialize ?? JSON.parse;
|
|
606
|
-
const cacheRef = useRef({
|
|
607
|
-
parsed: initialValue,
|
|
608
|
-
raw: null
|
|
609
|
-
});
|
|
610
|
-
const getSnapshot2 = useCallback(() => {
|
|
611
|
-
const storage = getStorageInstance(storageType);
|
|
612
|
-
if (!storage) return initialValue;
|
|
613
|
-
try {
|
|
614
|
-
const raw = storage.getItem(key);
|
|
615
|
-
if (raw === null) {
|
|
616
|
-
cacheRef.current = { parsed: initialValue, raw: null };
|
|
617
|
-
return initialValue;
|
|
618
|
-
}
|
|
619
|
-
if (cacheRef.current.raw === raw) {
|
|
620
|
-
return cacheRef.current.parsed;
|
|
621
|
-
}
|
|
622
|
-
const parsed = deserialize(raw);
|
|
623
|
-
cacheRef.current = { parsed, raw };
|
|
624
|
-
return parsed;
|
|
625
|
-
} catch {
|
|
626
|
-
return initialValue;
|
|
627
|
-
}
|
|
628
|
-
}, [deserialize, initialValue, key, storageType]);
|
|
629
|
-
const getServerSnapshot = useCallback(() => initialValue, [initialValue]);
|
|
630
|
-
const subscribe2 = useCallback(
|
|
631
|
-
(onStoreChange) => {
|
|
632
|
-
if (!isBrowser) return () => {
|
|
633
|
-
};
|
|
634
|
-
const handleStorage = (event) => {
|
|
635
|
-
if (event.key === key && event.storageArea === getStorageInstance(storageType)) {
|
|
636
|
-
onStoreChange();
|
|
637
|
-
}
|
|
638
|
-
};
|
|
639
|
-
const handleCustomSync = (event) => {
|
|
640
|
-
const detail = event.detail;
|
|
641
|
-
if (detail?.key === key && detail?.storageType === storageType) {
|
|
642
|
-
onStoreChange();
|
|
643
|
-
}
|
|
644
|
-
};
|
|
645
|
-
window.addEventListener("storage", handleStorage);
|
|
646
|
-
window.addEventListener(STORAGE_SYNC_EVENT, handleCustomSync);
|
|
647
|
-
return () => {
|
|
648
|
-
window.removeEventListener("storage", handleStorage);
|
|
649
|
-
window.removeEventListener(STORAGE_SYNC_EVENT, handleCustomSync);
|
|
650
|
-
};
|
|
651
|
-
},
|
|
652
|
-
[key, storageType]
|
|
653
|
-
);
|
|
654
|
-
const state = useSyncExternalStore(subscribe2, getSnapshot2, getServerSnapshot);
|
|
655
|
-
const stateRef = useRef(state);
|
|
656
|
-
useIsomorphicLayoutEffect(() => {
|
|
657
|
-
stateRef.current = state;
|
|
658
|
-
});
|
|
659
|
-
const setValue = useCallback(
|
|
660
|
-
(next) => {
|
|
661
|
-
const resolvedNext = typeof next === "function" ? next(stateRef.current) : next;
|
|
662
|
-
stateRef.current = resolvedNext;
|
|
663
|
-
const storage = getStorageInstance(storageType);
|
|
664
|
-
if (!storage) return;
|
|
665
|
-
try {
|
|
666
|
-
const raw = serialize(resolvedNext);
|
|
667
|
-
cacheRef.current = { parsed: resolvedNext, raw };
|
|
668
|
-
storage.setItem(key, raw);
|
|
669
|
-
window.dispatchEvent(
|
|
670
|
-
new CustomEvent(STORAGE_SYNC_EVENT, {
|
|
671
|
-
detail: { key, storageType }
|
|
672
|
-
})
|
|
673
|
-
);
|
|
674
|
-
} catch {
|
|
675
|
-
}
|
|
676
|
-
},
|
|
677
|
-
[key, serialize, storageType]
|
|
678
|
-
);
|
|
679
|
-
const removeValue = useCallback(() => {
|
|
680
|
-
stateRef.current = initialValue;
|
|
681
|
-
cacheRef.current = { parsed: initialValue, raw: null };
|
|
682
|
-
const storage = getStorageInstance(storageType);
|
|
683
|
-
if (!storage) return;
|
|
684
|
-
try {
|
|
685
|
-
storage.removeItem(key);
|
|
686
|
-
window.dispatchEvent(
|
|
687
|
-
new CustomEvent(STORAGE_SYNC_EVENT, {
|
|
688
|
-
detail: { key, storageType }
|
|
689
|
-
})
|
|
690
|
-
);
|
|
691
|
-
} catch {
|
|
692
|
-
}
|
|
693
|
-
}, [initialValue, key, storageType]);
|
|
694
|
-
return [state, setValue, removeValue];
|
|
695
|
-
}
|
|
696
|
-
function useLocalStorage(key, initialValue, options) {
|
|
697
|
-
return useStorageState(key, initialValue, { ...options, storage: "local" });
|
|
698
|
-
}
|
|
699
|
-
function useSessionStorage(key, initialValue, options) {
|
|
700
|
-
return useStorageState(key, initialValue, { ...options, storage: "session" });
|
|
701
|
-
}
|
|
702
|
-
function useIntersectionObserver(targetRef, options = {}) {
|
|
703
|
-
const {
|
|
704
|
-
enabled = true,
|
|
705
|
-
freezeOnceVisible = false,
|
|
706
|
-
onChange,
|
|
707
|
-
root = null,
|
|
708
|
-
rootMargin = "0px",
|
|
709
|
-
threshold = 0
|
|
710
|
-
} = options;
|
|
711
|
-
const [entry, setEntry] = useState(null);
|
|
712
|
-
const onChangeRef = useRef(onChange);
|
|
713
|
-
useIsomorphicLayoutEffect(() => {
|
|
714
|
-
onChangeRef.current = onChange;
|
|
715
|
-
});
|
|
716
|
-
const frozen = Boolean(entry?.isIntersecting && freezeOnceVisible);
|
|
717
|
-
useEffect(() => {
|
|
718
|
-
if (!isBrowser || !enabled || frozen || typeof IntersectionObserver === "undefined") {
|
|
719
|
-
return;
|
|
720
|
-
}
|
|
721
|
-
const node = targetRef?.current;
|
|
722
|
-
if (!node) return;
|
|
723
|
-
const observer = new IntersectionObserver(
|
|
724
|
-
([nextEntry]) => {
|
|
725
|
-
if (!nextEntry) return;
|
|
726
|
-
setEntry(nextEntry);
|
|
727
|
-
onChangeRef.current?.(nextEntry);
|
|
728
|
-
},
|
|
729
|
-
{ root, rootMargin, threshold }
|
|
730
|
-
);
|
|
731
|
-
observer.observe(node);
|
|
732
|
-
return () => observer.disconnect();
|
|
733
|
-
}, [enabled, frozen, root, rootMargin, targetRef, threshold]);
|
|
734
|
-
return {
|
|
735
|
-
entry,
|
|
736
|
-
isIntersecting: Boolean(entry?.isIntersecting)
|
|
737
|
-
};
|
|
738
|
-
}
|
|
739
203
|
var listeners = /* @__PURE__ */ new Set();
|
|
740
204
|
var activeFullscreenStateIds = /* @__PURE__ */ new Set();
|
|
741
205
|
var ACTIVE_FULLSCREEN_ROOT_SELECTOR = '[data-fullscreen-state-root="true"][data-affect-global-state="true"]';
|
|
@@ -799,6 +263,6 @@ function useIsFullscreenStateActive() {
|
|
|
799
263
|
return useSyncExternalStore(subscribe, getSnapshot, () => false);
|
|
800
264
|
}
|
|
801
265
|
|
|
802
|
-
export {
|
|
266
|
+
export { useClickOutside, useGlobalEvent, useIsFullscreenStateActive, useIsomorphicLayoutEffect, useRequiredContext, useStore };
|
|
803
267
|
//# sourceMappingURL=index.js.map
|
|
804
268
|
//# sourceMappingURL=index.js.map
|