@charcoal-ui/react 6.1.0-beta.0 → 6.1.0-beta.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/dist/components/Notification/NotificationItem.cjs +30 -0
- package/dist/components/Notification/NotificationItem.cjs.map +1 -0
- package/dist/components/Notification/NotificationItem.d.ts +15 -0
- package/dist/components/Notification/NotificationItem.d.ts.map +1 -0
- package/dist/components/Notification/NotificationItem.js +30 -0
- package/dist/components/Notification/NotificationItem.js.map +1 -0
- package/dist/components/Notification/NotificationRegion.cjs +50 -0
- package/dist/components/Notification/NotificationRegion.cjs.map +1 -0
- package/dist/components/Notification/NotificationRegion.d.ts +14 -0
- package/dist/components/Notification/NotificationRegion.d.ts.map +1 -0
- package/dist/components/Notification/NotificationRegion.js +50 -0
- package/dist/components/Notification/NotificationRegion.js.map +1 -0
- package/dist/components/Notification/Snackbar/index.d.ts +45 -0
- package/dist/components/Notification/Snackbar/index.d.ts.map +1 -0
- package/dist/components/Notification/Snackbar/index2.cjs +99 -0
- package/dist/components/Notification/Snackbar/index2.cjs.map +1 -0
- package/dist/components/Notification/Snackbar/index2.js +94 -0
- package/dist/components/Notification/Snackbar/index2.js.map +1 -0
- package/dist/components/Notification/Toast/index.d.ts +28 -0
- package/dist/components/Notification/Toast/index.d.ts.map +1 -0
- package/dist/components/Notification/Toast/index2.cjs +86 -0
- package/dist/components/Notification/Toast/index2.cjs.map +1 -0
- package/dist/components/Notification/Toast/index2.js +81 -0
- package/dist/components/Notification/Toast/index2.js.map +1 -0
- package/dist/components/Notification/types.d.ts +27 -0
- package/dist/components/Notification/types.d.ts.map +1 -0
- package/dist/components/Notification/useNotificationQueue.cjs +155 -0
- package/dist/components/Notification/useNotificationQueue.cjs.map +1 -0
- package/dist/components/Notification/useNotificationQueue.d.ts +16 -0
- package/dist/components/Notification/useNotificationQueue.d.ts.map +1 -0
- package/dist/components/Notification/useNotificationQueue.js +155 -0
- package/dist/components/Notification/useNotificationQueue.js.map +1 -0
- package/dist/index.cjs +4 -1
- package/dist/index.css +1315 -1250
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/layered.css +1315 -1250
- package/dist/layered.css.map +1 -1
- package/package.json +5 -5
- package/src/__tests__/css-output.test.ts +39 -6
- package/src/components/Notification/NotificationItem.tsx +51 -0
- package/src/components/Notification/NotificationRegion.tsx +76 -0
- package/src/components/{Snackbar → Notification/Snackbar}/__snapshots__/index.css.snap +1 -42
- package/src/components/{Snackbar → Notification/Snackbar}/index.css +1 -43
- package/src/components/{Snackbar → Notification/Snackbar}/index.story.tsx +52 -23
- package/src/components/{Snackbar → Notification/Snackbar}/index.test.tsx +61 -11
- package/src/components/Notification/Snackbar/index.tsx +131 -0
- package/src/components/Notification/Toast/__snapshots__/index.css.snap +62 -0
- package/src/components/Notification/Toast/index.css +61 -0
- package/src/components/Notification/Toast/index.story.tsx +174 -0
- package/src/components/Notification/Toast/index.test.tsx +263 -0
- package/src/components/Notification/Toast/index.tsx +86 -0
- package/src/components/Notification/__snapshots__/layout.css.snap +47 -0
- package/src/components/Notification/layout.css +46 -0
- package/src/components/Notification/types.ts +28 -0
- package/src/components/Notification/useNotificationQueue.ts +235 -0
- package/src/index.ts +14 -2
- package/dist/components/Snackbar/index.d.ts +0 -52
- package/dist/components/Snackbar/index.d.ts.map +0 -1
- package/dist/components/Snackbar/index2.cjs +0 -268
- package/dist/components/Snackbar/index2.cjs.map +0 -1
- package/dist/components/Snackbar/index2.js +0 -263
- package/dist/components/Snackbar/index2.js.map +0 -1
- package/src/components/Snackbar/index.tsx +0 -455
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/* empty css */
|
|
3
|
+
import { NotificationItem } from "../NotificationItem.js";
|
|
4
|
+
import { NotificationRegion } from "../NotificationRegion.js";
|
|
5
|
+
import { useNotificationQueue } from "../useNotificationQueue.js";
|
|
6
|
+
/* empty css */
|
|
7
|
+
import { forwardRef, useImperativeHandle, useRef } from "react";
|
|
8
|
+
import { jsx } from "react/jsx-runtime";
|
|
9
|
+
import { c } from "react-compiler-runtime";
|
|
10
|
+
|
|
11
|
+
//#region src/components/Notification/Toast/index.tsx
|
|
12
|
+
const Toast = forwardRef(function Toast({ position = "top", duration, order, ...regionProps }, ref) {
|
|
13
|
+
"use memo";
|
|
14
|
+
const { state, itemRef, enqueue, onHoverStart, onHoverEnd } = useNotificationQueue("toast", {
|
|
15
|
+
duration,
|
|
16
|
+
order
|
|
17
|
+
});
|
|
18
|
+
function show(message, options) {
|
|
19
|
+
enqueue({
|
|
20
|
+
message,
|
|
21
|
+
type: options.type
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
useImperativeHandle(ref, () => ({ show }));
|
|
25
|
+
return /* @__PURE__ */ jsx(NotificationRegion, {
|
|
26
|
+
name: "toast",
|
|
27
|
+
state,
|
|
28
|
+
position,
|
|
29
|
+
...regionProps,
|
|
30
|
+
children: state.visibleToasts.map((toast) => /* @__PURE__ */ jsx(NotificationItem, {
|
|
31
|
+
name: "toast",
|
|
32
|
+
toast,
|
|
33
|
+
state,
|
|
34
|
+
itemRef,
|
|
35
|
+
onHoverStart,
|
|
36
|
+
onHoverEnd,
|
|
37
|
+
"data-type": toast.content.type
|
|
38
|
+
}, toast.key))
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
function useToast(t0) {
|
|
42
|
+
"use memo";
|
|
43
|
+
const $ = c(7);
|
|
44
|
+
let t1;
|
|
45
|
+
if ($[0] !== t0) {
|
|
46
|
+
t1 = t0 === void 0 ? {} : t0;
|
|
47
|
+
$[0] = t0;
|
|
48
|
+
$[1] = t1;
|
|
49
|
+
} else t1 = $[1];
|
|
50
|
+
const props = t1;
|
|
51
|
+
const toastHandlerRef = useRef(null);
|
|
52
|
+
let t2;
|
|
53
|
+
if ($[2] !== props) {
|
|
54
|
+
t2 = /* @__PURE__ */ jsx(Toast, {
|
|
55
|
+
ref: toastHandlerRef,
|
|
56
|
+
...props
|
|
57
|
+
});
|
|
58
|
+
$[2] = props;
|
|
59
|
+
$[3] = t2;
|
|
60
|
+
} else t2 = $[3];
|
|
61
|
+
const element = t2;
|
|
62
|
+
let t3;
|
|
63
|
+
if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
|
|
64
|
+
t3 = function show(message, options) {
|
|
65
|
+
toastHandlerRef.current?.show(message, options);
|
|
66
|
+
};
|
|
67
|
+
$[4] = t3;
|
|
68
|
+
} else t3 = $[4];
|
|
69
|
+
const show = t3;
|
|
70
|
+
let t4;
|
|
71
|
+
if ($[5] !== element) {
|
|
72
|
+
t4 = [element, show];
|
|
73
|
+
$[5] = element;
|
|
74
|
+
$[6] = t4;
|
|
75
|
+
} else t4 = $[6];
|
|
76
|
+
return t4;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
//#endregion
|
|
80
|
+
export { Toast as default, useToast };
|
|
81
|
+
//# sourceMappingURL=index2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index2.js","names":["toastHandlerRef"],"sources":["../../../../src/components/Notification/Toast/index.tsx"],"sourcesContent":["import '../layout.css'\nimport './index.css'\n\nimport { forwardRef, useImperativeHandle, useRef } from 'react'\nimport type { ReactNode } from 'react'\nimport { NotificationItem } from '../NotificationItem'\nimport { NotificationRegion } from '../NotificationRegion'\nimport { useNotificationQueue } from '../useNotificationQueue'\nimport type { NotificationProps, Position } from '../types'\n\nexport type ToastType = 'success' | 'error'\n\nexport type ShowToastOptions = {\n type: ToastType\n}\n\nexport type ToastHandler = {\n show: (message: ReactNode, options: ShowToastOptions) => void\n}\n\nexport type ToastProps = Omit<NotificationProps, 'position'> & {\n /**\n * Toast の表示位置\n * @default 'top'\n */\n position?: Position\n}\n\ntype ToastContent = {\n message: ReactNode\n type: ToastType\n}\n\nconst Toast = forwardRef<ToastHandler, ToastProps>(function Toast(\n { position = 'top', duration, order, ...regionProps },\n ref,\n) {\n 'use memo'\n\n const { state, itemRef, enqueue, onHoverStart, onHoverEnd } =\n useNotificationQueue<ToastContent>('toast', { duration, order })\n\n function show(message: ReactNode, options: ShowToastOptions) {\n enqueue({\n message,\n type: options.type,\n })\n }\n\n useImperativeHandle(ref, () => ({ show }))\n\n return (\n <NotificationRegion\n name=\"toast\"\n state={state}\n position={position}\n {...regionProps}\n >\n {state.visibleToasts.map((toast) => (\n <NotificationItem\n key={toast.key}\n name=\"toast\"\n toast={toast}\n state={state}\n itemRef={itemRef}\n onHoverStart={onHoverStart}\n onHoverEnd={onHoverEnd}\n data-type={toast.content.type}\n />\n ))}\n </NotificationRegion>\n )\n})\n\nexport default Toast\n\nexport function useToast(props: ToastProps = {}) {\n 'use memo'\n\n const toastHandlerRef = useRef<ToastHandler>(null)\n const element = <Toast ref={toastHandlerRef} {...props} />\n function show(message: ReactNode, options: ShowToastOptions) {\n toastHandlerRef.current?.show(message, options)\n }\n return [element, show] as const\n}\n"],"mappings":";;;;;;;;;;;AAiCA,MAAM,QAAQ,WAAqC,SAAS,MAC1D,EAAE,WAAW,OAAO,UAAU,OAAO,GAAG,eACxC,KACA;CACA;CAEA,MAAM,EAAE,OAAO,SAAS,SAAS,cAAc,eAC7C,qBAAmC,SAAS;EAAE;EAAU;CAAM,CAAC;CAEjE,SAAS,KAAK,SAAoB,SAA2B;EAC3D,QAAQ;GACN;GACA,MAAM,QAAQ;EAChB,CAAC;CACH;CAEA,oBAAoB,YAAY,EAAE,KAAK,EAAE;CAEzC,OACE,oBAAC,oBAAD;EACE,MAAK;EACE;EACG;EACV,GAAI;YAEH,MAAM,cAAc,KAAK,UACxB,oBAAC,kBAAD;GAEE,MAAK;GACE;GACA;GACE;GACK;GACF;GACZ,aAAW,MAAM,QAAQ;EAAK,GAPzB,MAAM,GAOmB,CAEjC;CACiB;AAExB,CAAC;AAID,SAAO,SAAA,IAAA;CAAA;CAAA,MAAA,IAAA,EAAA,CAAA;CAAA,IAAA;CAAA,IAAA,EAAA,OAAA,IAAA;EAAkB,KAAA,OAAA,SAAA,CAAqB,IAArB;EAAsB,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAtB,MAAA,QAAA;CAGvB,MAAA,kBAAwB,OAAqB,IAAI;CAAC,IAAA;CAAA,IAAA,EAAA,OAAA,OAAA;EAClC,KAAA,oBAAC,OAAD;GAAYA,KAAA;GAAe,GAAM;EAAK;EAAI,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAA1D,MAAA,UAAgB;CAA0C,IAAA;CAAA,IAAA,EAAA,OAAA,OAAA,IAAA,2BAAA,GAAA;EAC1D,KAAA,SAAA,KAAA,SAAA,SAAA;GACE,gBAAe,SAAc,KAAC,SAAS,OAAO;EAAC;EAChD,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAFD,MAAA,OAAA;CAEC,IAAA;CAAA,IAAA,EAAA,OAAA,SAAA;EACM,KAAA,CAAC,SAAS,IAAI;EAAC,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,OAAf;AAAwB"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type NotificationName = 'snackbar' | 'toast';
|
|
2
|
+
export type Position = 'top' | 'bottom';
|
|
3
|
+
export type NotificationOrder = 'queue' | 'replace';
|
|
4
|
+
export type UseNotificationOptions = {
|
|
5
|
+
position?: Position;
|
|
6
|
+
/**
|
|
7
|
+
* 画面端からの距離(ピクセル)
|
|
8
|
+
* @default 16
|
|
9
|
+
*/
|
|
10
|
+
offset?: number;
|
|
11
|
+
/**
|
|
12
|
+
* 通知を表示する時間(ミリ秒)。負の値は 0、数値でない値は 5000 として扱う
|
|
13
|
+
* @default 5000
|
|
14
|
+
*/
|
|
15
|
+
duration?: number;
|
|
16
|
+
/**
|
|
17
|
+
* 表示中の通知がある場合の次の通知の表示方法
|
|
18
|
+
* @default 'queue'
|
|
19
|
+
*/
|
|
20
|
+
order?: NotificationOrder;
|
|
21
|
+
};
|
|
22
|
+
export type NotificationProps = UseNotificationOptions & {
|
|
23
|
+
zIndex?: number;
|
|
24
|
+
portalContainer?: HTMLElement;
|
|
25
|
+
className?: string;
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/Notification/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,OAAO,CAAA;AACnD,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,QAAQ,CAAA;AACvC,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,SAAS,CAAA;AAEnD,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,KAAK,CAAC,EAAE,iBAAiB,CAAA;CAC1B,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG,sBAAsB,GAAG;IACvD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,eAAe,CAAC,EAAE,WAAW,CAAA;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
let react = require("react");
|
|
3
|
+
let react_stately_useToastState = require("react-stately/useToastState");
|
|
4
|
+
|
|
5
|
+
//#region src/components/Notification/useNotificationQueue.ts
|
|
6
|
+
const DEFAULT_DURATION_MS = 5e3;
|
|
7
|
+
const ANIMATION_DURATION_MS = 300;
|
|
8
|
+
function useNotificationQueue(name, { duration: durationOption, order = "queue", timeoutReason, unmountedReason } = {}) {
|
|
9
|
+
const itemRef = (0, react.useRef)(null);
|
|
10
|
+
const queueRef = (0, react.useRef)([]);
|
|
11
|
+
const isShowingRef = (0, react.useRef)(false);
|
|
12
|
+
const activeRef = (0, react.useRef)();
|
|
13
|
+
const replaceRef = (0, react.useRef)(false);
|
|
14
|
+
const hoverRef = (0, react.useRef)({
|
|
15
|
+
active: false,
|
|
16
|
+
pending: void 0
|
|
17
|
+
});
|
|
18
|
+
const playNextRef = (0, react.useRef)(void 0);
|
|
19
|
+
const state = (0, react_stately_useToastState.useToastState)({
|
|
20
|
+
maxVisibleToasts: 1,
|
|
21
|
+
wrapUpdate: (0, react.useCallback)(function wrapUpdate(update, action) {
|
|
22
|
+
if (action !== "remove") {
|
|
23
|
+
update();
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
function notifyActive() {
|
|
27
|
+
const active = activeRef.current;
|
|
28
|
+
if (active === void 0 || active.notified) return;
|
|
29
|
+
active.notified = true;
|
|
30
|
+
const reason = active.reason ?? timeoutReason;
|
|
31
|
+
if (reason !== void 0) active.onClose?.(reason);
|
|
32
|
+
}
|
|
33
|
+
function finish() {
|
|
34
|
+
activeRef.current = void 0;
|
|
35
|
+
update();
|
|
36
|
+
playNextRef.current?.();
|
|
37
|
+
}
|
|
38
|
+
notifyActive();
|
|
39
|
+
if (hoverRef.current.active) {
|
|
40
|
+
hoverRef.current.pending = () => wrapUpdate(update, "remove");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (itemRef.current === null) {
|
|
44
|
+
finish();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (replaceRef.current) {
|
|
48
|
+
replaceRef.current = false;
|
|
49
|
+
finish();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const item = itemRef.current;
|
|
53
|
+
item.dataset.exiting = "true";
|
|
54
|
+
let completed = false;
|
|
55
|
+
let fallbackTimer = 0;
|
|
56
|
+
function handleAnimationEnd(event) {
|
|
57
|
+
if (event.target === item && event.animationName === `charcoal-${name}-exit`) complete();
|
|
58
|
+
}
|
|
59
|
+
function complete() {
|
|
60
|
+
if (completed) return;
|
|
61
|
+
completed = true;
|
|
62
|
+
item.removeEventListener("animationend", handleAnimationEnd);
|
|
63
|
+
window.clearTimeout(fallbackTimer);
|
|
64
|
+
finish();
|
|
65
|
+
}
|
|
66
|
+
item.addEventListener("animationend", handleAnimationEnd);
|
|
67
|
+
fallbackTimer = window.setTimeout(complete, 400);
|
|
68
|
+
if (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) complete();
|
|
69
|
+
}, [name, timeoutReason])
|
|
70
|
+
});
|
|
71
|
+
(0, react.useEffect)(() => {
|
|
72
|
+
return () => {
|
|
73
|
+
const active = activeRef.current;
|
|
74
|
+
if (active !== void 0 && !active.notified) {
|
|
75
|
+
if (unmountedReason !== void 0) active.onClose?.(unmountedReason);
|
|
76
|
+
}
|
|
77
|
+
activeRef.current = void 0;
|
|
78
|
+
queueRef.current = [];
|
|
79
|
+
isShowingRef.current = false;
|
|
80
|
+
};
|
|
81
|
+
}, [unmountedReason]);
|
|
82
|
+
function playNext() {
|
|
83
|
+
const next = queueRef.current.shift();
|
|
84
|
+
if (next === void 0) {
|
|
85
|
+
isShowingRef.current = false;
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const { duration, onClose, content } = next;
|
|
89
|
+
isShowingRef.current = true;
|
|
90
|
+
const enterMs = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ? 0 : ANIMATION_DURATION_MS;
|
|
91
|
+
const key = state.add(content, { timeout: Math.max(1, duration + enterMs) });
|
|
92
|
+
activeRef.current = {
|
|
93
|
+
key,
|
|
94
|
+
onClose,
|
|
95
|
+
notified: false
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
playNextRef.current = playNext;
|
|
99
|
+
function enqueue(content, onClose) {
|
|
100
|
+
const duration = typeof durationOption === "number" && Number.isFinite(durationOption) ? Math.max(0, durationOption) : DEFAULT_DURATION_MS;
|
|
101
|
+
queueRef.current.push({
|
|
102
|
+
content,
|
|
103
|
+
onClose,
|
|
104
|
+
duration
|
|
105
|
+
});
|
|
106
|
+
if (!isShowingRef.current) playNext();
|
|
107
|
+
else if (order === "replace") {
|
|
108
|
+
const active = activeRef.current;
|
|
109
|
+
if (active === void 0) return;
|
|
110
|
+
active.reason = "replaced";
|
|
111
|
+
replaceRef.current = true;
|
|
112
|
+
hoverRef.current.active = false;
|
|
113
|
+
const pending = hoverRef.current.pending;
|
|
114
|
+
hoverRef.current.pending = void 0;
|
|
115
|
+
if (pending !== void 0) pending();
|
|
116
|
+
else state.close(active.key);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function close(key, reason) {
|
|
120
|
+
const active = activeRef.current;
|
|
121
|
+
if (active?.key !== key) return;
|
|
122
|
+
active.reason = reason;
|
|
123
|
+
hoverRef.current.active = false;
|
|
124
|
+
const pending = hoverRef.current.pending;
|
|
125
|
+
hoverRef.current.pending = void 0;
|
|
126
|
+
if (pending !== void 0) pending();
|
|
127
|
+
else state.close(key);
|
|
128
|
+
}
|
|
129
|
+
function onHoverStart() {
|
|
130
|
+
hoverRef.current.active = true;
|
|
131
|
+
}
|
|
132
|
+
function onHoverEnd() {
|
|
133
|
+
hoverRef.current.active = false;
|
|
134
|
+
const pending = hoverRef.current.pending;
|
|
135
|
+
hoverRef.current.pending = void 0;
|
|
136
|
+
pending?.();
|
|
137
|
+
}
|
|
138
|
+
function clearHover() {
|
|
139
|
+
hoverRef.current.active = false;
|
|
140
|
+
hoverRef.current.pending = void 0;
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
state,
|
|
144
|
+
itemRef,
|
|
145
|
+
enqueue,
|
|
146
|
+
close,
|
|
147
|
+
onHoverStart,
|
|
148
|
+
onHoverEnd,
|
|
149
|
+
clearHover
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
//#endregion
|
|
154
|
+
exports.useNotificationQueue = useNotificationQueue;
|
|
155
|
+
//# sourceMappingURL=useNotificationQueue.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useNotificationQueue.cjs","names":[],"sources":["../../../src/components/Notification/useNotificationQueue.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react'\nimport { useToastState } from 'react-stately/useToastState'\nimport type { NotificationName, NotificationOrder } from './types'\n\nconst DEFAULT_DURATION_MS = 5000\nconst ANIMATION_DURATION_MS = 300\n\ntype QueueItem<TContent, TCloseReason> = {\n content: TContent\n onClose?: (reason: TCloseReason) => void\n}\n\nexport function useNotificationQueue<\n TContent,\n TCloseReason extends string = never,\n>(\n name: NotificationName,\n {\n duration: durationOption,\n order = 'queue',\n timeoutReason,\n unmountedReason,\n }: {\n duration?: number\n order?: NotificationOrder\n timeoutReason?: TCloseReason\n unmountedReason?: TCloseReason\n } = {},\n) {\n const itemRef = useRef<HTMLDivElement>(null)\n const queueRef = useRef<\n Array<QueueItem<TContent, TCloseReason> & { duration: number }>\n >([])\n const isShowingRef = useRef(false)\n const activeRef = useRef<{\n key: string\n onClose?: (reason: TCloseReason) => void\n reason?: TCloseReason\n notified: boolean\n }>()\n const replaceRef = useRef(false)\n const hoverRef = useRef({\n active: false,\n pending: undefined as (() => void) | undefined,\n })\n // wrapUpdate を固定したまま、後から定義する playNext の最新版を呼ぶ\n const playNextRef = useRef<(() => void) | undefined>(undefined)\n\n // wrapUpdate の参照が変わると useToastState が ToastQueue を作り直すため useCallback で固定する\n const wrapUpdate = useCallback(\n function wrapUpdate(\n update: () => void,\n action: 'add' | 'remove' | 'clear',\n ) {\n if (action !== 'remove') {\n update()\n return\n }\n\n function notifyActive() {\n const active = activeRef.current\n if (active === undefined || active.notified) return\n active.notified = true\n const reason = active.reason ?? timeoutReason\n if (reason !== undefined) {\n active.onClose?.(reason)\n }\n }\n\n function finish() {\n activeRef.current = undefined\n update()\n playNextRef.current?.()\n }\n\n notifyActive()\n\n if (hoverRef.current.active) {\n hoverRef.current.pending = () => wrapUpdate(update, 'remove')\n return\n }\n\n if (itemRef.current === null) {\n finish()\n return\n }\n\n if (replaceRef.current) {\n replaceRef.current = false\n finish()\n return\n }\n const item: HTMLDivElement = itemRef.current\n\n // allow-discreteが Newly Available で使えないので、要素の削除をアニメーション完了まで待つ\n item.dataset.exiting = 'true'\n let completed = false\n let fallbackTimer = 0 // complete 内で clearTimeout(setTimeout(...)) すると新規タイマーを即キャンセルしてしまう\n function handleAnimationEnd(event: AnimationEvent) {\n if (\n event.target === item &&\n event.animationName === `charcoal-${name}-exit`\n ) {\n complete()\n }\n }\n function complete() {\n if (completed) return\n completed = true\n item.removeEventListener('animationend', handleAnimationEnd)\n window.clearTimeout(fallbackTimer)\n finish()\n }\n item.addEventListener('animationend', handleAnimationEnd)\n fallbackTimer = window.setTimeout(complete, ANIMATION_DURATION_MS + 100)\n if (window.matchMedia?.('(prefers-reduced-motion: reduce)').matches) {\n complete()\n }\n },\n [name, timeoutReason],\n )\n\n const state = useToastState<TContent>({\n maxVisibleToasts: 1,\n wrapUpdate,\n })\n\n useEffect(() => {\n return () => {\n const active = activeRef.current\n if (active !== undefined && !active.notified) {\n if (unmountedReason !== undefined) {\n active.onClose?.(unmountedReason)\n }\n }\n activeRef.current = undefined\n queueRef.current = []\n isShowingRef.current = false\n }\n }, [unmountedReason])\n\n function playNext() {\n const next = queueRef.current.shift()\n if (next === undefined) {\n isShowingRef.current = false\n return\n }\n\n const { duration, onClose, content } = next\n isShowingRef.current = true\n const enterMs = window.matchMedia?.('(prefers-reduced-motion: reduce)')\n .matches\n ? 0\n : ANIMATION_DURATION_MS\n const key = state.add(content, {\n // react-stately は 0 を「タイマーなし」と扱うため、最小の正数を渡す\n timeout: Math.max(1, duration + enterMs),\n })\n activeRef.current = { key, onClose, notified: false }\n }\n playNextRef.current = playNext\n\n function enqueue(\n content: TContent,\n onClose?: (reason: TCloseReason) => void,\n ) {\n const duration =\n typeof durationOption === 'number' && Number.isFinite(durationOption)\n ? Math.max(0, durationOption)\n : DEFAULT_DURATION_MS\n\n queueRef.current.push({\n content,\n onClose,\n duration,\n })\n if (!isShowingRef.current) {\n playNext()\n } else if (order === 'replace') {\n const active = activeRef.current\n if (active === undefined) return\n\n active.reason = 'replaced' as TCloseReason\n replaceRef.current = true\n hoverRef.current.active = false\n const pending = hoverRef.current.pending\n hoverRef.current.pending = undefined\n if (pending !== undefined) {\n pending()\n } else {\n state.close(active.key)\n }\n }\n }\n\n function close(key: string, reason: TCloseReason) {\n const active = activeRef.current\n if (active?.key !== key) return\n active.reason = reason\n hoverRef.current.active = false\n const pending = hoverRef.current.pending\n hoverRef.current.pending = undefined\n if (pending !== undefined) {\n pending()\n } else {\n state.close(key)\n }\n }\n\n function onHoverStart() {\n hoverRef.current.active = true\n }\n\n function onHoverEnd() {\n hoverRef.current.active = false\n const pending = hoverRef.current.pending\n hoverRef.current.pending = undefined\n pending?.()\n }\n\n function clearHover() {\n hoverRef.current.active = false\n hoverRef.current.pending = undefined\n }\n\n return {\n state,\n itemRef,\n enqueue,\n close,\n onHoverStart,\n onHoverEnd,\n clearHover,\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { NotificationName, NotificationOrder } from './types';
|
|
2
|
+
export declare function useNotificationQueue<TContent, TCloseReason extends string = never>(name: NotificationName, { duration: durationOption, order, timeoutReason, unmountedReason, }?: {
|
|
3
|
+
duration?: number;
|
|
4
|
+
order?: NotificationOrder;
|
|
5
|
+
timeoutReason?: TCloseReason;
|
|
6
|
+
unmountedReason?: TCloseReason;
|
|
7
|
+
}): {
|
|
8
|
+
state: import("react-stately/useToastState").ToastState<TContent>;
|
|
9
|
+
itemRef: import("react").RefObject<HTMLDivElement>;
|
|
10
|
+
enqueue: (content: TContent, onClose?: (reason: TCloseReason) => void) => void;
|
|
11
|
+
close: (key: string, reason: TCloseReason) => void;
|
|
12
|
+
onHoverStart: () => void;
|
|
13
|
+
onHoverEnd: () => void;
|
|
14
|
+
clearHover: () => void;
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=useNotificationQueue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useNotificationQueue.d.ts","sourceRoot":"","sources":["../../../src/components/Notification/useNotificationQueue.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAUlE,wBAAgB,oBAAoB,CAClC,QAAQ,EACR,YAAY,SAAS,MAAM,GAAG,KAAK,EAEnC,IAAI,EAAE,gBAAgB,EACtB,EACE,QAAQ,EAAE,cAAc,EACxB,KAAe,EACf,aAAa,EACb,eAAe,GAChB,GAAE;IACD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,iBAAiB,CAAA;IACzB,aAAa,CAAC,EAAE,YAAY,CAAA;IAC5B,eAAe,CAAC,EAAE,YAAY,CAAA;CAC1B;;;uBAwIK,QAAQ,YACP,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI;iBA+BtB,MAAM,UAAU,YAAY;;;;EAuCjD"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
3
|
+
import { useToastState } from "react-stately/useToastState";
|
|
4
|
+
|
|
5
|
+
//#region src/components/Notification/useNotificationQueue.ts
|
|
6
|
+
const DEFAULT_DURATION_MS = 5e3;
|
|
7
|
+
const ANIMATION_DURATION_MS = 300;
|
|
8
|
+
function useNotificationQueue(name, { duration: durationOption, order = "queue", timeoutReason, unmountedReason } = {}) {
|
|
9
|
+
const itemRef = useRef(null);
|
|
10
|
+
const queueRef = useRef([]);
|
|
11
|
+
const isShowingRef = useRef(false);
|
|
12
|
+
const activeRef = useRef();
|
|
13
|
+
const replaceRef = useRef(false);
|
|
14
|
+
const hoverRef = useRef({
|
|
15
|
+
active: false,
|
|
16
|
+
pending: void 0
|
|
17
|
+
});
|
|
18
|
+
const playNextRef = useRef(void 0);
|
|
19
|
+
const state = useToastState({
|
|
20
|
+
maxVisibleToasts: 1,
|
|
21
|
+
wrapUpdate: useCallback(function wrapUpdate(update, action) {
|
|
22
|
+
if (action !== "remove") {
|
|
23
|
+
update();
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
function notifyActive() {
|
|
27
|
+
const active = activeRef.current;
|
|
28
|
+
if (active === void 0 || active.notified) return;
|
|
29
|
+
active.notified = true;
|
|
30
|
+
const reason = active.reason ?? timeoutReason;
|
|
31
|
+
if (reason !== void 0) active.onClose?.(reason);
|
|
32
|
+
}
|
|
33
|
+
function finish() {
|
|
34
|
+
activeRef.current = void 0;
|
|
35
|
+
update();
|
|
36
|
+
playNextRef.current?.();
|
|
37
|
+
}
|
|
38
|
+
notifyActive();
|
|
39
|
+
if (hoverRef.current.active) {
|
|
40
|
+
hoverRef.current.pending = () => wrapUpdate(update, "remove");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (itemRef.current === null) {
|
|
44
|
+
finish();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (replaceRef.current) {
|
|
48
|
+
replaceRef.current = false;
|
|
49
|
+
finish();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const item = itemRef.current;
|
|
53
|
+
item.dataset.exiting = "true";
|
|
54
|
+
let completed = false;
|
|
55
|
+
let fallbackTimer = 0;
|
|
56
|
+
function handleAnimationEnd(event) {
|
|
57
|
+
if (event.target === item && event.animationName === `charcoal-${name}-exit`) complete();
|
|
58
|
+
}
|
|
59
|
+
function complete() {
|
|
60
|
+
if (completed) return;
|
|
61
|
+
completed = true;
|
|
62
|
+
item.removeEventListener("animationend", handleAnimationEnd);
|
|
63
|
+
window.clearTimeout(fallbackTimer);
|
|
64
|
+
finish();
|
|
65
|
+
}
|
|
66
|
+
item.addEventListener("animationend", handleAnimationEnd);
|
|
67
|
+
fallbackTimer = window.setTimeout(complete, 400);
|
|
68
|
+
if (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) complete();
|
|
69
|
+
}, [name, timeoutReason])
|
|
70
|
+
});
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
return () => {
|
|
73
|
+
const active = activeRef.current;
|
|
74
|
+
if (active !== void 0 && !active.notified) {
|
|
75
|
+
if (unmountedReason !== void 0) active.onClose?.(unmountedReason);
|
|
76
|
+
}
|
|
77
|
+
activeRef.current = void 0;
|
|
78
|
+
queueRef.current = [];
|
|
79
|
+
isShowingRef.current = false;
|
|
80
|
+
};
|
|
81
|
+
}, [unmountedReason]);
|
|
82
|
+
function playNext() {
|
|
83
|
+
const next = queueRef.current.shift();
|
|
84
|
+
if (next === void 0) {
|
|
85
|
+
isShowingRef.current = false;
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const { duration, onClose, content } = next;
|
|
89
|
+
isShowingRef.current = true;
|
|
90
|
+
const enterMs = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ? 0 : ANIMATION_DURATION_MS;
|
|
91
|
+
const key = state.add(content, { timeout: Math.max(1, duration + enterMs) });
|
|
92
|
+
activeRef.current = {
|
|
93
|
+
key,
|
|
94
|
+
onClose,
|
|
95
|
+
notified: false
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
playNextRef.current = playNext;
|
|
99
|
+
function enqueue(content, onClose) {
|
|
100
|
+
const duration = typeof durationOption === "number" && Number.isFinite(durationOption) ? Math.max(0, durationOption) : DEFAULT_DURATION_MS;
|
|
101
|
+
queueRef.current.push({
|
|
102
|
+
content,
|
|
103
|
+
onClose,
|
|
104
|
+
duration
|
|
105
|
+
});
|
|
106
|
+
if (!isShowingRef.current) playNext();
|
|
107
|
+
else if (order === "replace") {
|
|
108
|
+
const active = activeRef.current;
|
|
109
|
+
if (active === void 0) return;
|
|
110
|
+
active.reason = "replaced";
|
|
111
|
+
replaceRef.current = true;
|
|
112
|
+
hoverRef.current.active = false;
|
|
113
|
+
const pending = hoverRef.current.pending;
|
|
114
|
+
hoverRef.current.pending = void 0;
|
|
115
|
+
if (pending !== void 0) pending();
|
|
116
|
+
else state.close(active.key);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function close(key, reason) {
|
|
120
|
+
const active = activeRef.current;
|
|
121
|
+
if (active?.key !== key) return;
|
|
122
|
+
active.reason = reason;
|
|
123
|
+
hoverRef.current.active = false;
|
|
124
|
+
const pending = hoverRef.current.pending;
|
|
125
|
+
hoverRef.current.pending = void 0;
|
|
126
|
+
if (pending !== void 0) pending();
|
|
127
|
+
else state.close(key);
|
|
128
|
+
}
|
|
129
|
+
function onHoverStart() {
|
|
130
|
+
hoverRef.current.active = true;
|
|
131
|
+
}
|
|
132
|
+
function onHoverEnd() {
|
|
133
|
+
hoverRef.current.active = false;
|
|
134
|
+
const pending = hoverRef.current.pending;
|
|
135
|
+
hoverRef.current.pending = void 0;
|
|
136
|
+
pending?.();
|
|
137
|
+
}
|
|
138
|
+
function clearHover() {
|
|
139
|
+
hoverRef.current.active = false;
|
|
140
|
+
hoverRef.current.pending = void 0;
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
state,
|
|
144
|
+
itemRef,
|
|
145
|
+
enqueue,
|
|
146
|
+
close,
|
|
147
|
+
onHoverStart,
|
|
148
|
+
onHoverEnd,
|
|
149
|
+
clearHover
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
//#endregion
|
|
154
|
+
export { useNotificationQueue };
|
|
155
|
+
//# sourceMappingURL=useNotificationQueue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useNotificationQueue.js","names":[],"sources":["../../../src/components/Notification/useNotificationQueue.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react'\nimport { useToastState } from 'react-stately/useToastState'\nimport type { NotificationName, NotificationOrder } from './types'\n\nconst DEFAULT_DURATION_MS = 5000\nconst ANIMATION_DURATION_MS = 300\n\ntype QueueItem<TContent, TCloseReason> = {\n content: TContent\n onClose?: (reason: TCloseReason) => void\n}\n\nexport function useNotificationQueue<\n TContent,\n TCloseReason extends string = never,\n>(\n name: NotificationName,\n {\n duration: durationOption,\n order = 'queue',\n timeoutReason,\n unmountedReason,\n }: {\n duration?: number\n order?: NotificationOrder\n timeoutReason?: TCloseReason\n unmountedReason?: TCloseReason\n } = {},\n) {\n const itemRef = useRef<HTMLDivElement>(null)\n const queueRef = useRef<\n Array<QueueItem<TContent, TCloseReason> & { duration: number }>\n >([])\n const isShowingRef = useRef(false)\n const activeRef = useRef<{\n key: string\n onClose?: (reason: TCloseReason) => void\n reason?: TCloseReason\n notified: boolean\n }>()\n const replaceRef = useRef(false)\n const hoverRef = useRef({\n active: false,\n pending: undefined as (() => void) | undefined,\n })\n // wrapUpdate を固定したまま、後から定義する playNext の最新版を呼ぶ\n const playNextRef = useRef<(() => void) | undefined>(undefined)\n\n // wrapUpdate の参照が変わると useToastState が ToastQueue を作り直すため useCallback で固定する\n const wrapUpdate = useCallback(\n function wrapUpdate(\n update: () => void,\n action: 'add' | 'remove' | 'clear',\n ) {\n if (action !== 'remove') {\n update()\n return\n }\n\n function notifyActive() {\n const active = activeRef.current\n if (active === undefined || active.notified) return\n active.notified = true\n const reason = active.reason ?? timeoutReason\n if (reason !== undefined) {\n active.onClose?.(reason)\n }\n }\n\n function finish() {\n activeRef.current = undefined\n update()\n playNextRef.current?.()\n }\n\n notifyActive()\n\n if (hoverRef.current.active) {\n hoverRef.current.pending = () => wrapUpdate(update, 'remove')\n return\n }\n\n if (itemRef.current === null) {\n finish()\n return\n }\n\n if (replaceRef.current) {\n replaceRef.current = false\n finish()\n return\n }\n const item: HTMLDivElement = itemRef.current\n\n // allow-discreteが Newly Available で使えないので、要素の削除をアニメーション完了まで待つ\n item.dataset.exiting = 'true'\n let completed = false\n let fallbackTimer = 0 // complete 内で clearTimeout(setTimeout(...)) すると新規タイマーを即キャンセルしてしまう\n function handleAnimationEnd(event: AnimationEvent) {\n if (\n event.target === item &&\n event.animationName === `charcoal-${name}-exit`\n ) {\n complete()\n }\n }\n function complete() {\n if (completed) return\n completed = true\n item.removeEventListener('animationend', handleAnimationEnd)\n window.clearTimeout(fallbackTimer)\n finish()\n }\n item.addEventListener('animationend', handleAnimationEnd)\n fallbackTimer = window.setTimeout(complete, ANIMATION_DURATION_MS + 100)\n if (window.matchMedia?.('(prefers-reduced-motion: reduce)').matches) {\n complete()\n }\n },\n [name, timeoutReason],\n )\n\n const state = useToastState<TContent>({\n maxVisibleToasts: 1,\n wrapUpdate,\n })\n\n useEffect(() => {\n return () => {\n const active = activeRef.current\n if (active !== undefined && !active.notified) {\n if (unmountedReason !== undefined) {\n active.onClose?.(unmountedReason)\n }\n }\n activeRef.current = undefined\n queueRef.current = []\n isShowingRef.current = false\n }\n }, [unmountedReason])\n\n function playNext() {\n const next = queueRef.current.shift()\n if (next === undefined) {\n isShowingRef.current = false\n return\n }\n\n const { duration, onClose, content } = next\n isShowingRef.current = true\n const enterMs = window.matchMedia?.('(prefers-reduced-motion: reduce)')\n .matches\n ? 0\n : ANIMATION_DURATION_MS\n const key = state.add(content, {\n // react-stately は 0 を「タイマーなし」と扱うため、最小の正数を渡す\n timeout: Math.max(1, duration + enterMs),\n })\n activeRef.current = { key, onClose, notified: false }\n }\n playNextRef.current = playNext\n\n function enqueue(\n content: TContent,\n onClose?: (reason: TCloseReason) => void,\n ) {\n const duration =\n typeof durationOption === 'number' && Number.isFinite(durationOption)\n ? Math.max(0, durationOption)\n : DEFAULT_DURATION_MS\n\n queueRef.current.push({\n content,\n onClose,\n duration,\n })\n if (!isShowingRef.current) {\n playNext()\n } else if (order === 'replace') {\n const active = activeRef.current\n if (active === undefined) return\n\n active.reason = 'replaced' as TCloseReason\n replaceRef.current = true\n hoverRef.current.active = false\n const pending = hoverRef.current.pending\n hoverRef.current.pending = undefined\n if (pending !== undefined) {\n pending()\n } else {\n state.close(active.key)\n }\n }\n }\n\n function close(key: string, reason: TCloseReason) {\n const active = activeRef.current\n if (active?.key !== key) return\n active.reason = reason\n hoverRef.current.active = false\n const pending = hoverRef.current.pending\n hoverRef.current.pending = undefined\n if (pending !== undefined) {\n pending()\n } else {\n state.close(key)\n }\n }\n\n function onHoverStart() {\n hoverRef.current.active = true\n }\n\n function onHoverEnd() {\n hoverRef.current.active = false\n const pending = hoverRef.current.pending\n hoverRef.current.pending = undefined\n pending?.()\n }\n\n function clearHover() {\n hoverRef.current.active = false\n hoverRef.current.pending = undefined\n }\n\n return {\n state,\n itemRef,\n enqueue,\n close,\n onHoverStart,\n onHoverEnd,\n clearHover,\n }\n}\n"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -27,7 +27,8 @@ const require_index$19 = require('./components/HintText/index2.cjs');
|
|
|
27
27
|
const require_index$20 = require('./components/TextEllipsis/index2.cjs');
|
|
28
28
|
const require_index$21 = require('./components/Pagination/index2.cjs');
|
|
29
29
|
const require_index$22 = require('./components/Carousel/index2.cjs');
|
|
30
|
-
const require_index$23 = require('./components/Snackbar/index2.cjs');
|
|
30
|
+
const require_index$23 = require('./components/Notification/Snackbar/index2.cjs');
|
|
31
|
+
const require_index$24 = require('./components/Notification/Toast/index2.cjs');
|
|
31
32
|
require('./components/FocusRing/index.cjs');
|
|
32
33
|
let react_aria_private_overlays_useModal = require("react-aria/private/overlays/useModal");
|
|
33
34
|
|
|
@@ -76,7 +77,9 @@ exports.prefersColorScheme = require_themeHelper.prefersColorScheme;
|
|
|
76
77
|
exports.themeSelector = require_themeHelper.themeSelector;
|
|
77
78
|
exports.themeSetter = require_themeHelper.themeSetter;
|
|
78
79
|
exports.unstable_Snackbar = require_index$23.default;
|
|
80
|
+
exports.unstable_Toast = require_index$24.default;
|
|
79
81
|
exports.unstable_useSnackbar = require_index$23.useSnackbar;
|
|
82
|
+
exports.unstable_useToast = require_index$24.useToast;
|
|
80
83
|
exports.useLocalStorage = require_themeHelper.useLocalStorage;
|
|
81
84
|
exports.useMedia = require_themeHelper.useMedia;
|
|
82
85
|
exports.useTheme = require_themeHelper.useTheme;
|