@charcoal-ui/react 6.1.0-beta.4 → 6.1.0-beta.5

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.
@@ -2,7 +2,7 @@ import '../layout.css';
2
2
  import './index.css';
3
3
  import { type HTMLAttributes, type ReactNode } from 'react';
4
4
  import type { NotificationProps, Position } from '../types';
5
- export type SnackbarCloseReason = 'timeout' | 'replaced' | 'action' | 'close' | 'unmounted';
5
+ export type SnackbarCloseReason = 'action' | 'unmounted';
6
6
  export type SnackbarRootAttributes = {
7
7
  [key: `data-${string}`]: string | number | boolean | undefined;
8
8
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/Notification/Snackbar/index.tsx"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AACtB,OAAO,aAAa,CAAA;AAEpB,OAAO,EAAc,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAKvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAE3D,MAAM,MAAM,mBAAmB,GAC7B,SAAS,GAAG,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,WAAW,CAAA;AAE3D,MAAM,MAAM,sBAAsB,GAAG;IACnC,CAAC,GAAG,EAAE,QAAQ,MAAM,EAAE,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;CAC/D,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAA;IAC/C,cAAc,CAAC,EAAE,sBAAsB,CAAA;CACxC,CAAA;AAED,KAAK,iBAAiB,GAAG;IACvB,OAAO,EAAE,SAAS,CAAA;IAClB,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,GAAG,CAAC,EAAE,OAAO,CAAA;CACd,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC,CAAA;AAEpD,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,iBAAiB,EAAE,QAAQ,CAAC,GAAG;IAC9D,6BAA6B;IAC7B,MAAM,EAAE,WAAW,CAAC,SAAS,CAAC,CAAA;CAC/B,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC,GAAG;IACnE,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,GAAG,CAAC,EAAE,OAAO,CAAA;CACd,CAAA;AAQD,QAAA,MAAM,QAAQ;IAfZ,6BAA6B;YACrB,WAAW,CAAC,SAAS,CAAC;kDAkB/B,CAAA;eAEc,QAAQ;AAEvB,wBAAgB,WAAW,CAAC,KAAK,GAAE,gBAAqB,gEAmB/B,SAAS,YAAW,mBAAmB,WA8C/D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/Notification/Snackbar/index.tsx"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAA;AACtB,OAAO,aAAa,CAAA;AAEpB,OAAO,EAAc,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAKvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAE3D,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,WAAW,CAAA;AAExD,MAAM,MAAM,sBAAsB,GAAG;IACnC,CAAC,GAAG,EAAE,QAAQ,MAAM,EAAE,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;CAC/D,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAA;IAC/C,cAAc,CAAC,EAAE,sBAAsB,CAAA;CACxC,CAAA;AAED,KAAK,iBAAiB,GAAG;IACvB,OAAO,EAAE,SAAS,CAAA;IAClB,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,GAAG,CAAC,EAAE,OAAO,CAAA;CACd,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC,CAAA;AAEpD,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,iBAAiB,EAAE,QAAQ,CAAC,GAAG;IAC9D,6BAA6B;IAC7B,MAAM,EAAE,WAAW,CAAC,SAAS,CAAC,CAAA;CAC/B,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC,GAAG;IACnE,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,GAAG,CAAC,EAAE,OAAO,CAAA;CACd,CAAA;AAQD,QAAA,MAAM,QAAQ;IAfZ,6BAA6B;YACrB,WAAW,CAAC,SAAS,CAAC;kDAkB/B,CAAA;eAEc,QAAQ;AAEvB,wBAAgB,WAAW,CAAC,KAAK,GAAE,gBAAqB,gEAiB/B,SAAS,YAAW,mBAAmB,WAiD/D"}
@@ -21,16 +21,15 @@ function useSnackbar(props = {}) {
21
21
  const { state, itemRef, enqueue, close, onHoverStart, onHoverEnd } = require_useNotificationQueue.useNotificationQueue("snackbar", {
22
22
  duration,
23
23
  order,
24
- animateReplace: true,
25
- timeoutReason: "timeout",
26
- unmountedReason: "unmounted"
24
+ animateReplace: true
27
25
  });
28
26
  function show(message, options = {}) {
27
+ const onClose = options.onClose;
29
28
  enqueue({
30
29
  message,
31
30
  action: options.action,
32
31
  rootAttributes: options.rootAttributes
33
- }, options.onClose);
32
+ }, onClose === void 0 ? void 0 : (reason) => onClose(reason ?? "unmounted"));
34
33
  }
35
34
  return [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_NotificationRegion.NotificationRegion, {
36
35
  name: "snackbar",
@@ -1 +1 @@
1
- {"version":3,"file":"index2.cjs","names":["toast"],"sources":["../../../../src/components/Notification/Snackbar/index.tsx"],"sourcesContent":["import '../layout.css'\nimport './index.css'\n\nimport { forwardRef, type HTMLAttributes, type ReactNode } from 'react'\nimport { useClassNames } from '../../../_lib/useClassNames'\nimport { NotificationItem } from '../NotificationItem'\nimport { NotificationRegion } from '../NotificationRegion'\nimport { useNotificationQueue } from '../useNotificationQueue'\nimport type { NotificationProps, Position } from '../types'\n\nexport type SnackbarCloseReason =\n 'timeout' | 'replaced' | 'action' | 'close' | 'unmounted'\n\nexport type SnackbarRootAttributes = {\n [key: `data-${string}`]: string | number | boolean | undefined\n}\n\nexport type ShowSnackbarOptions = {\n action?: ReactNode\n onClose?: (reason: SnackbarCloseReason) => void\n rootAttributes?: SnackbarRootAttributes\n}\n\ntype SnackbarBaseProps = {\n message: ReactNode\n action?: ReactNode\n dim?: boolean\n} & Omit<HTMLAttributes<HTMLDivElement>, 'children'>\n\nexport type SnackbarProps = Omit<SnackbarBaseProps, 'action'> & {\n /** Snackbar の右側に表示するアクション */\n action: NonNullable<ReactNode>\n}\n\nexport type UseSnackbarProps = Omit<NotificationProps, 'position'> & {\n position?: Position\n dim?: boolean\n}\n\ntype SnackbarContent = {\n message: ReactNode\n action?: ReactNode\n rootAttributes?: SnackbarRootAttributes\n}\n\nconst Snackbar = forwardRef<HTMLDivElement, SnackbarProps>(\n function Snackbar(props, ref) {\n return <SnackbarBase {...props} ref={ref} />\n },\n)\n\nexport default Snackbar\n\nexport function useSnackbar(props: UseSnackbarProps = {}) {\n 'use memo'\n\n const {\n position = 'bottom',\n dim = false,\n duration,\n order,\n ...regionProps\n } = props\n const { state, itemRef, enqueue, close, onHoverStart, onHoverEnd } =\n useNotificationQueue<SnackbarContent, SnackbarCloseReason>('snackbar', {\n duration,\n order,\n animateReplace: true,\n timeoutReason: 'timeout',\n unmountedReason: 'unmounted',\n })\n\n function show(message: ReactNode, options: ShowSnackbarOptions = {}) {\n enqueue(\n {\n message,\n action: options.action,\n rootAttributes: options.rootAttributes,\n },\n options.onClose,\n )\n }\n\n const hasAction = state.visibleToasts.some(\n (toast) => toast.content.action !== undefined,\n )\n const effectivePosition = hasAction ? 'bottom' : position\n const element = (\n <NotificationRegion\n name=\"snackbar\"\n state={state}\n position={effectivePosition}\n {...regionProps}\n >\n {state.visibleToasts.map((toast) => (\n <NotificationItem\n {...toast.content.rootAttributes}\n key={toast.key}\n name=\"snackbar\"\n toast={toast}\n state={state}\n itemRef={itemRef}\n onHoverStart={onHoverStart}\n onHoverEnd={onHoverEnd}\n data-dim={dim}\n data-with-action={toast.content.action !== undefined}\n >\n {toast.content.action !== undefined && (\n <div onClick={() => close(toast.key, 'action')}>\n <SnackbarAction action={toast.content.action} />\n </div>\n )}\n </NotificationItem>\n ))}\n </NotificationRegion>\n )\n\n return [element, show] as const\n}\n\nconst SnackbarBase = forwardRef<HTMLDivElement, SnackbarBaseProps>(\n function SnackbarBase(\n { message, action, dim = false, className, ...rootProps },\n ref,\n ) {\n const classNames = useClassNames(\n 'charcoal-notification',\n 'charcoal-snackbar',\n className,\n )\n\n return (\n <div\n {...rootProps}\n ref={ref}\n className={classNames}\n data-dim={dim}\n data-with-action={action !== undefined}\n >\n <div role=\"status\" className=\"charcoal-notification-content\">\n <div className=\"charcoal-notification-label\">{message}</div>\n </div>\n {action !== undefined && <SnackbarAction action={action} />}\n </div>\n )\n },\n)\n\nfunction SnackbarAction({ action }: { action: ReactNode }) {\n return <div>{action}</div>\n}\n"],"mappings":";;;;;;;;;;;AA6CA,MAAM,iCACJ,SAAS,SAAS,OAAO,KAAK;CAC5B,OAAO,2CAAC,cAAD;EAAc,GAAI;EAAY;CAAI;AAC3C,CACF;AAIA,SAAgB,YAAY,QAA0B,CAAC,GAAG;CACxD;CAEA,MAAM,EACJ,WAAW,UACX,MAAM,OACN,UACA,OACA,GAAG,gBACD;CACJ,MAAM,EAAE,OAAO,SAAS,SAAS,OAAO,cAAc,eACpD,kDAA2D,YAAY;EACrE;EACA;EACA,gBAAgB;EAChB,eAAe;EACf,iBAAiB;CACnB,CAAC;CAEH,SAAS,KAAK,SAAoB,UAA+B,CAAC,GAAG;EACnE,QACE;GACE;GACA,QAAQ,QAAQ;GAChB,gBAAgB,QAAQ;EAC1B,GACA,QAAQ,OACV;CACF;CAoCA,OAAO,CAAC,2CA7BL,+CAAD;EACE,MAAK;EACE;EACP,UARc,MAAM,cAAc,MACnC,UAAU,MAAM,QAAQ,WAAW,MAEZ,IAAY,WAAW;EAM7C,GAAI;YAEH,MAAM,cAAc,KAAKA,YACxB,yCAAC,2CAAD;GACE,GAAIA,QAAM,QAAQ;GAClB,KAAKA,QAAM;GACX,MAAK;GACL,OAAOA;GACA;GACE;GACK;GACF;GACZ,YAAU;GACV,oBAAkBA,QAAM,QAAQ,WAAW;EAO3B,GALfA,QAAM,QAAQ,WAAW,UACxB,2CAAC,OAAD;GAAK,eAAe,MAAMA,QAAM,KAAK,QAAQ;aAC3C,2CAAC,gBAAD,EAAgB,QAAQA,QAAM,QAAQ,OAAO;EAC1C,EAES,CACnB;CACiB,CAGd,GAAS,IAAI;AACvB;AAEA,MAAM,qCACJ,SAAS,aACP,EAAE,SAAS,QAAQ,MAAM,OAAO,WAAW,GAAG,aAC9C,KACA;CACA,MAAM,aAAa,oCACjB,yBACA,qBACA,SACF;CAEA,OACE,4CAAC,OAAD;EACE,GAAI;EACC;EACL,WAAW;EACX,YAAU;EACV,oBAAkB,WAAW;YAL/B,CAOE,2CAAC,OAAD;GAAK,MAAK;GAAS,WAAU;aAC3B,2CAAC,OAAD;IAAK,WAAU;cAA+B;GAAa;EACxD,IACJ,WAAW,UAAa,2CAAC,gBAAD,EAAwB,OAAO,EACrD;;AAET,CACF;AAEA,SAAS,eAAe,EAAE,UAAiC;CACzD,OAAO,2CAAC,OAAD,YAAM,OAAY;AAC3B"}
1
+ {"version":3,"file":"index2.cjs","names":["toast"],"sources":["../../../../src/components/Notification/Snackbar/index.tsx"],"sourcesContent":["import '../layout.css'\nimport './index.css'\n\nimport { forwardRef, type HTMLAttributes, type ReactNode } from 'react'\nimport { useClassNames } from '../../../_lib/useClassNames'\nimport { NotificationItem } from '../NotificationItem'\nimport { NotificationRegion } from '../NotificationRegion'\nimport { useNotificationQueue } from '../useNotificationQueue'\nimport type { NotificationProps, Position } from '../types'\n\nexport type SnackbarCloseReason = 'action' | 'unmounted'\n\nexport type SnackbarRootAttributes = {\n [key: `data-${string}`]: string | number | boolean | undefined\n}\n\nexport type ShowSnackbarOptions = {\n action?: ReactNode\n onClose?: (reason: SnackbarCloseReason) => void\n rootAttributes?: SnackbarRootAttributes\n}\n\ntype SnackbarBaseProps = {\n message: ReactNode\n action?: ReactNode\n dim?: boolean\n} & Omit<HTMLAttributes<HTMLDivElement>, 'children'>\n\nexport type SnackbarProps = Omit<SnackbarBaseProps, 'action'> & {\n /** Snackbar の右側に表示するアクション */\n action: NonNullable<ReactNode>\n}\n\nexport type UseSnackbarProps = Omit<NotificationProps, 'position'> & {\n position?: Position\n dim?: boolean\n}\n\ntype SnackbarContent = {\n message: ReactNode\n action?: ReactNode\n rootAttributes?: SnackbarRootAttributes\n}\n\nconst Snackbar = forwardRef<HTMLDivElement, SnackbarProps>(\n function Snackbar(props, ref) {\n return <SnackbarBase {...props} ref={ref} />\n },\n)\n\nexport default Snackbar\n\nexport function useSnackbar(props: UseSnackbarProps = {}) {\n 'use memo'\n\n const {\n position = 'bottom',\n dim = false,\n duration,\n order,\n ...regionProps\n } = props\n const { state, itemRef, enqueue, close, onHoverStart, onHoverEnd } =\n useNotificationQueue<SnackbarContent, 'action'>('snackbar', {\n duration,\n order,\n animateReplace: true,\n })\n\n function show(message: ReactNode, options: ShowSnackbarOptions = {}) {\n const onClose = options.onClose\n enqueue(\n {\n message,\n action: options.action,\n rootAttributes: options.rootAttributes,\n },\n onClose === undefined\n ? undefined\n : (reason) => onClose(reason ?? 'unmounted'),\n )\n }\n\n const hasAction = state.visibleToasts.some(\n (toast) => toast.content.action !== undefined,\n )\n const effectivePosition = hasAction ? 'bottom' : position\n const element = (\n <NotificationRegion\n name=\"snackbar\"\n state={state}\n position={effectivePosition}\n {...regionProps}\n >\n {state.visibleToasts.map((toast) => (\n <NotificationItem\n {...toast.content.rootAttributes}\n key={toast.key}\n name=\"snackbar\"\n toast={toast}\n state={state}\n itemRef={itemRef}\n onHoverStart={onHoverStart}\n onHoverEnd={onHoverEnd}\n data-dim={dim}\n data-with-action={toast.content.action !== undefined}\n >\n {toast.content.action !== undefined && (\n <div onClick={() => close(toast.key, 'action')}>\n <SnackbarAction action={toast.content.action} />\n </div>\n )}\n </NotificationItem>\n ))}\n </NotificationRegion>\n )\n\n return [element, show] as const\n}\n\nconst SnackbarBase = forwardRef<HTMLDivElement, SnackbarBaseProps>(\n function SnackbarBase(\n { message, action, dim = false, className, ...rootProps },\n ref,\n ) {\n const classNames = useClassNames(\n 'charcoal-notification',\n 'charcoal-snackbar',\n className,\n )\n\n return (\n <div\n {...rootProps}\n ref={ref}\n className={classNames}\n data-dim={dim}\n data-with-action={action !== undefined}\n >\n <div role=\"status\" className=\"charcoal-notification-content\">\n <div className=\"charcoal-notification-label\">{message}</div>\n </div>\n {action !== undefined && <SnackbarAction action={action} />}\n </div>\n )\n },\n)\n\nfunction SnackbarAction({ action }: { action: ReactNode }) {\n return <div>{action}</div>\n}\n"],"mappings":";;;;;;;;;;;AA4CA,MAAM,iCACJ,SAAS,SAAS,OAAO,KAAK;CAC5B,OAAO,2CAAC,cAAD;EAAc,GAAI;EAAY;CAAI;AAC3C,CACF;AAIA,SAAgB,YAAY,QAA0B,CAAC,GAAG;CACxD;CAEA,MAAM,EACJ,WAAW,UACX,MAAM,OACN,UACA,OACA,GAAG,gBACD;CACJ,MAAM,EAAE,OAAO,SAAS,SAAS,OAAO,cAAc,eACpD,kDAAgD,YAAY;EAC1D;EACA;EACA,gBAAgB;CAClB,CAAC;CAEH,SAAS,KAAK,SAAoB,UAA+B,CAAC,GAAG;EACnE,MAAM,UAAU,QAAQ;EACxB,QACE;GACE;GACA,QAAQ,QAAQ;GAChB,gBAAgB,QAAQ;EAC1B,GACA,YAAY,SACR,UACC,WAAW,QAAQ,UAAU,WAAW,CAC/C;CACF;CAoCA,OAAO,CAAC,2CA7BL,+CAAD;EACE,MAAK;EACE;EACP,UARc,MAAM,cAAc,MACnC,UAAU,MAAM,QAAQ,WAAW,MAEZ,IAAY,WAAW;EAM7C,GAAI;YAEH,MAAM,cAAc,KAAKA,YACxB,yCAAC,2CAAD;GACE,GAAIA,QAAM,QAAQ;GAClB,KAAKA,QAAM;GACX,MAAK;GACL,OAAOA;GACA;GACE;GACK;GACF;GACZ,YAAU;GACV,oBAAkBA,QAAM,QAAQ,WAAW;EAO3B,GALfA,QAAM,QAAQ,WAAW,UACxB,2CAAC,OAAD;GAAK,eAAe,MAAMA,QAAM,KAAK,QAAQ;aAC3C,2CAAC,gBAAD,EAAgB,QAAQA,QAAM,QAAQ,OAAO;EAC1C,EAES,CACnB;CACiB,CAGd,GAAS,IAAI;AACvB;AAEA,MAAM,qCACJ,SAAS,aACP,EAAE,SAAS,QAAQ,MAAM,OAAO,WAAW,GAAG,aAC9C,KACA;CACA,MAAM,aAAa,oCACjB,yBACA,qBACA,SACF;CAEA,OACE,4CAAC,OAAD;EACE,GAAI;EACC;EACL,WAAW;EACX,YAAU;EACV,oBAAkB,WAAW;YAL/B,CAOE,2CAAC,OAAD;GAAK,MAAK;GAAS,WAAU;aAC3B,2CAAC,OAAD;IAAK,WAAU;cAA+B;GAAa;EACxD,IACJ,WAAW,UAAa,2CAAC,gBAAD,EAAwB,OAAO,EACrD;;AAET,CACF;AAEA,SAAS,eAAe,EAAE,UAAiC;CACzD,OAAO,2CAAC,OAAD,YAAM,OAAY;AAC3B"}
@@ -21,16 +21,15 @@ function useSnackbar(props = {}) {
21
21
  const { state, itemRef, enqueue, close, onHoverStart, onHoverEnd } = useNotificationQueue("snackbar", {
22
22
  duration,
23
23
  order,
24
- animateReplace: true,
25
- timeoutReason: "timeout",
26
- unmountedReason: "unmounted"
24
+ animateReplace: true
27
25
  });
28
26
  function show(message, options = {}) {
27
+ const onClose = options.onClose;
29
28
  enqueue({
30
29
  message,
31
30
  action: options.action,
32
31
  rootAttributes: options.rootAttributes
33
- }, options.onClose);
32
+ }, onClose === void 0 ? void 0 : (reason) => onClose(reason ?? "unmounted"));
34
33
  }
35
34
  return [/* @__PURE__ */ jsx(NotificationRegion, {
36
35
  name: "snackbar",
@@ -1 +1 @@
1
- {"version":3,"file":"index2.js","names":["toast"],"sources":["../../../../src/components/Notification/Snackbar/index.tsx"],"sourcesContent":["import '../layout.css'\nimport './index.css'\n\nimport { forwardRef, type HTMLAttributes, type ReactNode } from 'react'\nimport { useClassNames } from '../../../_lib/useClassNames'\nimport { NotificationItem } from '../NotificationItem'\nimport { NotificationRegion } from '../NotificationRegion'\nimport { useNotificationQueue } from '../useNotificationQueue'\nimport type { NotificationProps, Position } from '../types'\n\nexport type SnackbarCloseReason =\n 'timeout' | 'replaced' | 'action' | 'close' | 'unmounted'\n\nexport type SnackbarRootAttributes = {\n [key: `data-${string}`]: string | number | boolean | undefined\n}\n\nexport type ShowSnackbarOptions = {\n action?: ReactNode\n onClose?: (reason: SnackbarCloseReason) => void\n rootAttributes?: SnackbarRootAttributes\n}\n\ntype SnackbarBaseProps = {\n message: ReactNode\n action?: ReactNode\n dim?: boolean\n} & Omit<HTMLAttributes<HTMLDivElement>, 'children'>\n\nexport type SnackbarProps = Omit<SnackbarBaseProps, 'action'> & {\n /** Snackbar の右側に表示するアクション */\n action: NonNullable<ReactNode>\n}\n\nexport type UseSnackbarProps = Omit<NotificationProps, 'position'> & {\n position?: Position\n dim?: boolean\n}\n\ntype SnackbarContent = {\n message: ReactNode\n action?: ReactNode\n rootAttributes?: SnackbarRootAttributes\n}\n\nconst Snackbar = forwardRef<HTMLDivElement, SnackbarProps>(\n function Snackbar(props, ref) {\n return <SnackbarBase {...props} ref={ref} />\n },\n)\n\nexport default Snackbar\n\nexport function useSnackbar(props: UseSnackbarProps = {}) {\n 'use memo'\n\n const {\n position = 'bottom',\n dim = false,\n duration,\n order,\n ...regionProps\n } = props\n const { state, itemRef, enqueue, close, onHoverStart, onHoverEnd } =\n useNotificationQueue<SnackbarContent, SnackbarCloseReason>('snackbar', {\n duration,\n order,\n animateReplace: true,\n timeoutReason: 'timeout',\n unmountedReason: 'unmounted',\n })\n\n function show(message: ReactNode, options: ShowSnackbarOptions = {}) {\n enqueue(\n {\n message,\n action: options.action,\n rootAttributes: options.rootAttributes,\n },\n options.onClose,\n )\n }\n\n const hasAction = state.visibleToasts.some(\n (toast) => toast.content.action !== undefined,\n )\n const effectivePosition = hasAction ? 'bottom' : position\n const element = (\n <NotificationRegion\n name=\"snackbar\"\n state={state}\n position={effectivePosition}\n {...regionProps}\n >\n {state.visibleToasts.map((toast) => (\n <NotificationItem\n {...toast.content.rootAttributes}\n key={toast.key}\n name=\"snackbar\"\n toast={toast}\n state={state}\n itemRef={itemRef}\n onHoverStart={onHoverStart}\n onHoverEnd={onHoverEnd}\n data-dim={dim}\n data-with-action={toast.content.action !== undefined}\n >\n {toast.content.action !== undefined && (\n <div onClick={() => close(toast.key, 'action')}>\n <SnackbarAction action={toast.content.action} />\n </div>\n )}\n </NotificationItem>\n ))}\n </NotificationRegion>\n )\n\n return [element, show] as const\n}\n\nconst SnackbarBase = forwardRef<HTMLDivElement, SnackbarBaseProps>(\n function SnackbarBase(\n { message, action, dim = false, className, ...rootProps },\n ref,\n ) {\n const classNames = useClassNames(\n 'charcoal-notification',\n 'charcoal-snackbar',\n className,\n )\n\n return (\n <div\n {...rootProps}\n ref={ref}\n className={classNames}\n data-dim={dim}\n data-with-action={action !== undefined}\n >\n <div role=\"status\" className=\"charcoal-notification-content\">\n <div className=\"charcoal-notification-label\">{message}</div>\n </div>\n {action !== undefined && <SnackbarAction action={action} />}\n </div>\n )\n },\n)\n\nfunction SnackbarAction({ action }: { action: ReactNode }) {\n return <div>{action}</div>\n}\n"],"mappings":";;;;;;;;;;;AA6CA,MAAM,WAAW,WACf,SAAS,SAAS,OAAO,KAAK;CAC5B,OAAO,oBAAC,cAAD;EAAc,GAAI;EAAY;CAAI;AAC3C,CACF;AAIA,SAAgB,YAAY,QAA0B,CAAC,GAAG;CACxD;CAEA,MAAM,EACJ,WAAW,UACX,MAAM,OACN,UACA,OACA,GAAG,gBACD;CACJ,MAAM,EAAE,OAAO,SAAS,SAAS,OAAO,cAAc,eACpD,qBAA2D,YAAY;EACrE;EACA;EACA,gBAAgB;EAChB,eAAe;EACf,iBAAiB;CACnB,CAAC;CAEH,SAAS,KAAK,SAAoB,UAA+B,CAAC,GAAG;EACnE,QACE;GACE;GACA,QAAQ,QAAQ;GAChB,gBAAgB,QAAQ;EAC1B,GACA,QAAQ,OACV;CACF;CAoCA,OAAO,CAAC,oBA7BL,oBAAD;EACE,MAAK;EACE;EACP,UARc,MAAM,cAAc,MACnC,UAAU,MAAM,QAAQ,WAAW,MAEZ,IAAY,WAAW;EAM7C,GAAI;YAEH,MAAM,cAAc,KAAKA,YACxB,8BAAC,kBAAD;GACE,GAAIA,QAAM,QAAQ;GAClB,KAAKA,QAAM;GACX,MAAK;GACL,OAAOA;GACA;GACE;GACK;GACF;GACZ,YAAU;GACV,oBAAkBA,QAAM,QAAQ,WAAW;EAO3B,GALfA,QAAM,QAAQ,WAAW,UACxB,oBAAC,OAAD;GAAK,eAAe,MAAMA,QAAM,KAAK,QAAQ;aAC3C,oBAAC,gBAAD,EAAgB,QAAQA,QAAM,QAAQ,OAAO;EAC1C,EAES,CACnB;CACiB,CAGd,GAAS,IAAI;AACvB;AAEA,MAAM,eAAe,WACnB,SAAS,aACP,EAAE,SAAS,QAAQ,MAAM,OAAO,WAAW,GAAG,aAC9C,KACA;CACA,MAAM,aAAa,cACjB,yBACA,qBACA,SACF;CAEA,OACE,qBAAC,OAAD;EACE,GAAI;EACC;EACL,WAAW;EACX,YAAU;EACV,oBAAkB,WAAW;YAL/B,CAOE,oBAAC,OAAD;GAAK,MAAK;GAAS,WAAU;aAC3B,oBAAC,OAAD;IAAK,WAAU;cAA+B;GAAa;EACxD,IACJ,WAAW,UAAa,oBAAC,gBAAD,EAAwB,OAAO,EACrD;;AAET,CACF;AAEA,SAAS,eAAe,EAAE,UAAiC;CACzD,OAAO,oBAAC,OAAD,YAAM,OAAY;AAC3B"}
1
+ {"version":3,"file":"index2.js","names":["toast"],"sources":["../../../../src/components/Notification/Snackbar/index.tsx"],"sourcesContent":["import '../layout.css'\nimport './index.css'\n\nimport { forwardRef, type HTMLAttributes, type ReactNode } from 'react'\nimport { useClassNames } from '../../../_lib/useClassNames'\nimport { NotificationItem } from '../NotificationItem'\nimport { NotificationRegion } from '../NotificationRegion'\nimport { useNotificationQueue } from '../useNotificationQueue'\nimport type { NotificationProps, Position } from '../types'\n\nexport type SnackbarCloseReason = 'action' | 'unmounted'\n\nexport type SnackbarRootAttributes = {\n [key: `data-${string}`]: string | number | boolean | undefined\n}\n\nexport type ShowSnackbarOptions = {\n action?: ReactNode\n onClose?: (reason: SnackbarCloseReason) => void\n rootAttributes?: SnackbarRootAttributes\n}\n\ntype SnackbarBaseProps = {\n message: ReactNode\n action?: ReactNode\n dim?: boolean\n} & Omit<HTMLAttributes<HTMLDivElement>, 'children'>\n\nexport type SnackbarProps = Omit<SnackbarBaseProps, 'action'> & {\n /** Snackbar の右側に表示するアクション */\n action: NonNullable<ReactNode>\n}\n\nexport type UseSnackbarProps = Omit<NotificationProps, 'position'> & {\n position?: Position\n dim?: boolean\n}\n\ntype SnackbarContent = {\n message: ReactNode\n action?: ReactNode\n rootAttributes?: SnackbarRootAttributes\n}\n\nconst Snackbar = forwardRef<HTMLDivElement, SnackbarProps>(\n function Snackbar(props, ref) {\n return <SnackbarBase {...props} ref={ref} />\n },\n)\n\nexport default Snackbar\n\nexport function useSnackbar(props: UseSnackbarProps = {}) {\n 'use memo'\n\n const {\n position = 'bottom',\n dim = false,\n duration,\n order,\n ...regionProps\n } = props\n const { state, itemRef, enqueue, close, onHoverStart, onHoverEnd } =\n useNotificationQueue<SnackbarContent, 'action'>('snackbar', {\n duration,\n order,\n animateReplace: true,\n })\n\n function show(message: ReactNode, options: ShowSnackbarOptions = {}) {\n const onClose = options.onClose\n enqueue(\n {\n message,\n action: options.action,\n rootAttributes: options.rootAttributes,\n },\n onClose === undefined\n ? undefined\n : (reason) => onClose(reason ?? 'unmounted'),\n )\n }\n\n const hasAction = state.visibleToasts.some(\n (toast) => toast.content.action !== undefined,\n )\n const effectivePosition = hasAction ? 'bottom' : position\n const element = (\n <NotificationRegion\n name=\"snackbar\"\n state={state}\n position={effectivePosition}\n {...regionProps}\n >\n {state.visibleToasts.map((toast) => (\n <NotificationItem\n {...toast.content.rootAttributes}\n key={toast.key}\n name=\"snackbar\"\n toast={toast}\n state={state}\n itemRef={itemRef}\n onHoverStart={onHoverStart}\n onHoverEnd={onHoverEnd}\n data-dim={dim}\n data-with-action={toast.content.action !== undefined}\n >\n {toast.content.action !== undefined && (\n <div onClick={() => close(toast.key, 'action')}>\n <SnackbarAction action={toast.content.action} />\n </div>\n )}\n </NotificationItem>\n ))}\n </NotificationRegion>\n )\n\n return [element, show] as const\n}\n\nconst SnackbarBase = forwardRef<HTMLDivElement, SnackbarBaseProps>(\n function SnackbarBase(\n { message, action, dim = false, className, ...rootProps },\n ref,\n ) {\n const classNames = useClassNames(\n 'charcoal-notification',\n 'charcoal-snackbar',\n className,\n )\n\n return (\n <div\n {...rootProps}\n ref={ref}\n className={classNames}\n data-dim={dim}\n data-with-action={action !== undefined}\n >\n <div role=\"status\" className=\"charcoal-notification-content\">\n <div className=\"charcoal-notification-label\">{message}</div>\n </div>\n {action !== undefined && <SnackbarAction action={action} />}\n </div>\n )\n },\n)\n\nfunction SnackbarAction({ action }: { action: ReactNode }) {\n return <div>{action}</div>\n}\n"],"mappings":";;;;;;;;;;;AA4CA,MAAM,WAAW,WACf,SAAS,SAAS,OAAO,KAAK;CAC5B,OAAO,oBAAC,cAAD;EAAc,GAAI;EAAY;CAAI;AAC3C,CACF;AAIA,SAAgB,YAAY,QAA0B,CAAC,GAAG;CACxD;CAEA,MAAM,EACJ,WAAW,UACX,MAAM,OACN,UACA,OACA,GAAG,gBACD;CACJ,MAAM,EAAE,OAAO,SAAS,SAAS,OAAO,cAAc,eACpD,qBAAgD,YAAY;EAC1D;EACA;EACA,gBAAgB;CAClB,CAAC;CAEH,SAAS,KAAK,SAAoB,UAA+B,CAAC,GAAG;EACnE,MAAM,UAAU,QAAQ;EACxB,QACE;GACE;GACA,QAAQ,QAAQ;GAChB,gBAAgB,QAAQ;EAC1B,GACA,YAAY,SACR,UACC,WAAW,QAAQ,UAAU,WAAW,CAC/C;CACF;CAoCA,OAAO,CAAC,oBA7BL,oBAAD;EACE,MAAK;EACE;EACP,UARc,MAAM,cAAc,MACnC,UAAU,MAAM,QAAQ,WAAW,MAEZ,IAAY,WAAW;EAM7C,GAAI;YAEH,MAAM,cAAc,KAAKA,YACxB,8BAAC,kBAAD;GACE,GAAIA,QAAM,QAAQ;GAClB,KAAKA,QAAM;GACX,MAAK;GACL,OAAOA;GACA;GACE;GACK;GACF;GACZ,YAAU;GACV,oBAAkBA,QAAM,QAAQ,WAAW;EAO3B,GALfA,QAAM,QAAQ,WAAW,UACxB,oBAAC,OAAD;GAAK,eAAe,MAAMA,QAAM,KAAK,QAAQ;aAC3C,oBAAC,gBAAD,EAAgB,QAAQA,QAAM,QAAQ,OAAO;EAC1C,EAES,CACnB;CACiB,CAGd,GAAS,IAAI;AACvB;AAEA,MAAM,eAAe,WACnB,SAAS,aACP,EAAE,SAAS,QAAQ,MAAM,OAAO,WAAW,GAAG,aAC9C,KACA;CACA,MAAM,aAAa,cACjB,yBACA,qBACA,SACF;CAEA,OACE,qBAAC,OAAD;EACE,GAAI;EACC;EACL,WAAW;EACX,YAAU;EACV,oBAAkB,WAAW;YAL/B,CAOE,oBAAC,OAAD;GAAK,MAAK;GAAS,WAAU;aAC3B,oBAAC,OAAD;IAAK,WAAU;cAA+B;GAAa;EACxD,IACJ,WAAW,UAAa,oBAAC,gBAAD,EAAwB,OAAO,EACrD;;AAET,CACF;AAEA,SAAS,eAAe,EAAE,UAAiC;CACzD,OAAO,oBAAC,OAAD,YAAM,OAAY;AAC3B"}
@@ -5,7 +5,7 @@ let react_stately_useToastState = require("react-stately/useToastState");
5
5
  //#region src/components/Notification/useNotificationQueue.ts
6
6
  const DEFAULT_DURATION_MS = 5e3;
7
7
  const ANIMATION_DURATION_MS = 300;
8
- function useNotificationQueue(name, { duration: durationOption, order = "queue", animateReplace = false, timeoutReason, unmountedReason } = {}) {
8
+ function useNotificationQueue(name, { duration: durationOption, order = "queue", animateReplace = false } = {}) {
9
9
  const itemRef = (0, react.useRef)(null);
10
10
  const queueRef = (0, react.useRef)([]);
11
11
  const isShowingRef = (0, react.useRef)(false);
@@ -29,8 +29,7 @@ function useNotificationQueue(name, { duration: durationOption, order = "queue",
29
29
  update();
30
30
  if (active !== void 0 && !active.notified) {
31
31
  active.notified = true;
32
- const reason = active.reason ?? timeoutReason;
33
- if (reason !== void 0) active.onClose?.(reason);
32
+ active.onClose?.(active.reason);
34
33
  }
35
34
  playNextRef.current?.();
36
35
  }
@@ -64,21 +63,20 @@ function useNotificationQueue(name, { duration: durationOption, order = "queue",
64
63
  item.addEventListener("animationend", handleAnimationEnd);
65
64
  fallbackTimer = window.setTimeout(complete, 400);
66
65
  if (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) complete();
67
- }, [name, timeoutReason])
66
+ }, [name])
68
67
  });
69
68
  (0, react.useEffect)(() => {
70
69
  return () => {
71
70
  const active = activeRef.current;
72
71
  if (active !== void 0 && !active.notified) {
73
72
  active.notified = true;
74
- const reason = active.reason ?? unmountedReason;
75
- if (reason !== void 0) active.onClose?.(reason);
73
+ active.onClose?.(active.reason);
76
74
  }
77
75
  activeRef.current = void 0;
78
76
  queueRef.current = [];
79
77
  isShowingRef.current = false;
80
78
  };
81
- }, [unmountedReason]);
79
+ }, []);
82
80
  function playNext() {
83
81
  const next = queueRef.current.shift();
84
82
  if (next === void 0) {
@@ -107,7 +105,6 @@ function useNotificationQueue(name, { duration: durationOption, order = "queue",
107
105
  else if (order === "replace") {
108
106
  const active = activeRef.current;
109
107
  if (active === void 0) return;
110
- active.reason = "replaced";
111
108
  replaceRef.current = !animateReplace;
112
109
  hoverRef.current.active = false;
113
110
  const pending = hoverRef.current.pending;
@@ -1 +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 animateReplace = false,\n timeoutReason,\n unmountedReason,\n }: {\n duration?: number\n order?: NotificationOrder\n animateReplace?: boolean\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 finish() {\n const active = activeRef.current\n activeRef.current = undefined\n update()\n if (active !== undefined && !active.notified) {\n active.notified = true\n const reason = active.reason ?? timeoutReason\n if (reason !== undefined) {\n active.onClose?.(reason)\n }\n }\n playNextRef.current?.()\n }\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\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 active.notified = true\n const reason = active.reason ?? unmountedReason\n if (reason !== undefined) {\n active.onClose?.(reason)\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 = !animateReplace\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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"}
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 animateReplace = false,\n }: {\n duration?: number\n order?: NotificationOrder\n animateReplace?: boolean\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 finish() {\n const active = activeRef.current\n activeRef.current = undefined\n update()\n if (active !== undefined && !active.notified) {\n active.notified = true\n active.onClose?.(active.reason)\n }\n playNextRef.current?.()\n }\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\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],\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 active.notified = true\n active.onClose?.(active.reason)\n }\n activeRef.current = undefined\n queueRef.current = []\n isShowingRef.current = false\n }\n }, [])\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 replaceRef.current = !animateReplace\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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"}
@@ -1,14 +1,12 @@
1
1
  import type { NotificationName, NotificationOrder } from './types';
2
- export declare function useNotificationQueue<TContent, TCloseReason extends string = never>(name: NotificationName, { duration: durationOption, order, animateReplace, timeoutReason, unmountedReason, }?: {
2
+ export declare function useNotificationQueue<TContent, TCloseReason extends string = never>(name: NotificationName, { duration: durationOption, order, animateReplace, }?: {
3
3
  duration?: number;
4
4
  order?: NotificationOrder;
5
5
  animateReplace?: boolean;
6
- timeoutReason?: TCloseReason;
7
- unmountedReason?: TCloseReason;
8
6
  }): {
9
7
  state: import("react-stately/useToastState").ToastState<TContent>;
10
8
  itemRef: import("react").RefObject<HTMLDivElement>;
11
- enqueue: (content: TContent, onClose?: (reason: TCloseReason) => void) => void;
9
+ enqueue: (content: TContent, onClose?: (reason?: TCloseReason) => void) => void;
12
10
  close: (key: string, reason: TCloseReason) => void;
13
11
  onHoverStart: () => void;
14
12
  onHoverEnd: () => void;
@@ -1 +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,cAAsB,EACtB,aAAa,EACb,eAAe,GAChB,GAAE;IACD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,iBAAiB,CAAA;IACzB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,aAAa,CAAC,EAAE,YAAY,CAAA;IAC5B,eAAe,CAAC,EAAE,YAAY,CAAA;CAC1B;;;uBAuIK,QAAQ,YACP,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI;iBA+BtB,MAAM,UAAU,YAAY;;;;EAuCjD"}
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,cAAsB,GACvB,GAAE;IACD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,iBAAiB,CAAA;IACzB,cAAc,CAAC,EAAE,OAAO,CAAA;CACpB;;;uBAiIK,QAAQ,YACP,CAAC,MAAM,CAAC,EAAE,YAAY,KAAK,IAAI;iBA8BvB,MAAM,UAAU,YAAY;;;;EAuCjD"}
@@ -5,7 +5,7 @@ import { useToastState } from "react-stately/useToastState";
5
5
  //#region src/components/Notification/useNotificationQueue.ts
6
6
  const DEFAULT_DURATION_MS = 5e3;
7
7
  const ANIMATION_DURATION_MS = 300;
8
- function useNotificationQueue(name, { duration: durationOption, order = "queue", animateReplace = false, timeoutReason, unmountedReason } = {}) {
8
+ function useNotificationQueue(name, { duration: durationOption, order = "queue", animateReplace = false } = {}) {
9
9
  const itemRef = useRef(null);
10
10
  const queueRef = useRef([]);
11
11
  const isShowingRef = useRef(false);
@@ -29,8 +29,7 @@ function useNotificationQueue(name, { duration: durationOption, order = "queue",
29
29
  update();
30
30
  if (active !== void 0 && !active.notified) {
31
31
  active.notified = true;
32
- const reason = active.reason ?? timeoutReason;
33
- if (reason !== void 0) active.onClose?.(reason);
32
+ active.onClose?.(active.reason);
34
33
  }
35
34
  playNextRef.current?.();
36
35
  }
@@ -64,21 +63,20 @@ function useNotificationQueue(name, { duration: durationOption, order = "queue",
64
63
  item.addEventListener("animationend", handleAnimationEnd);
65
64
  fallbackTimer = window.setTimeout(complete, 400);
66
65
  if (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) complete();
67
- }, [name, timeoutReason])
66
+ }, [name])
68
67
  });
69
68
  useEffect(() => {
70
69
  return () => {
71
70
  const active = activeRef.current;
72
71
  if (active !== void 0 && !active.notified) {
73
72
  active.notified = true;
74
- const reason = active.reason ?? unmountedReason;
75
- if (reason !== void 0) active.onClose?.(reason);
73
+ active.onClose?.(active.reason);
76
74
  }
77
75
  activeRef.current = void 0;
78
76
  queueRef.current = [];
79
77
  isShowingRef.current = false;
80
78
  };
81
- }, [unmountedReason]);
79
+ }, []);
82
80
  function playNext() {
83
81
  const next = queueRef.current.shift();
84
82
  if (next === void 0) {
@@ -107,7 +105,6 @@ function useNotificationQueue(name, { duration: durationOption, order = "queue",
107
105
  else if (order === "replace") {
108
106
  const active = activeRef.current;
109
107
  if (active === void 0) return;
110
- active.reason = "replaced";
111
108
  replaceRef.current = !animateReplace;
112
109
  hoverRef.current.active = false;
113
110
  const pending = hoverRef.current.pending;
@@ -1 +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 animateReplace = false,\n timeoutReason,\n unmountedReason,\n }: {\n duration?: number\n order?: NotificationOrder\n animateReplace?: boolean\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 finish() {\n const active = activeRef.current\n activeRef.current = undefined\n update()\n if (active !== undefined && !active.notified) {\n active.notified = true\n const reason = active.reason ?? timeoutReason\n if (reason !== undefined) {\n active.onClose?.(reason)\n }\n }\n playNextRef.current?.()\n }\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\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 active.notified = true\n const reason = active.reason ?? unmountedReason\n if (reason !== undefined) {\n active.onClose?.(reason)\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 = !animateReplace\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":";;;;;AAIA,MAAM,sBAAsB;AAC5B,MAAM,wBAAwB;AAO9B,SAAgB,qBAId,MACA,EACE,UAAU,gBACV,QAAQ,SACR,iBAAiB,OACjB,eACA,oBAOE,CAAC,GACL;CACA,MAAM,UAAU,OAAuB,IAAI;CAC3C,MAAM,WAAW,OAEf,CAAA,CAAE;CACJ,MAAM,eAAe,OAAO,KAAK;CACjC,MAAM,YAAY,OAKf;CACH,MAAM,aAAa,OAAO,KAAK;CAC/B,MAAM,WAAW,OAAO;EACtB,QAAQ;EACR,SAAS;CACX,CAAC;CAED,MAAM,cAAc,OAAiC,MAAS;CAyE9D,MAAM,QAAQ,cAAwB;EACpC,kBAAkB;EAClB,YAxEiB,YACjB,SAAS,WACP,QACA,QACA;GACA,IAAI,WAAW,UAAU;IACvB,OAAO;IACP;GACF;GAEA,SAAS,SAAS;IAChB,MAAM,SAAS,UAAU;IACzB,UAAU,UAAU;IACpB,OAAO;IACP,IAAI,WAAW,UAAa,CAAC,OAAO,UAAU;KAC5C,OAAO,WAAW;KAClB,MAAM,SAAS,OAAO,UAAU;KAChC,IAAI,WAAW,QACb,OAAO,UAAU,MAAM;IAE3B;IACA,YAAY,UAAU;GACxB;GAEA,IAAI,SAAS,QAAQ,QAAQ;IAC3B,SAAS,QAAQ,gBAAgB,WAAW,QAAQ,QAAQ;IAC5D;GACF;GAEA,IAAI,QAAQ,YAAY,MAAM;IAC5B,OAAO;IACP;GACF;GAEA,IAAI,WAAW,SAAS;IACtB,WAAW,UAAU;IACrB,OAAO;IACP;GACF;GAEA,MAAM,OAAuB,QAAQ;GAGrC,KAAK,QAAQ,UAAU;GACvB,IAAI,YAAY;GAChB,IAAI,gBAAgB;GACpB,SAAS,mBAAmB,OAAuB;IACjD,IACE,MAAM,WAAW,QACjB,MAAM,kBAAkB,YAAY,KAAI,QAExC,SAAS;GAEb;GACA,SAAS,WAAW;IAClB,IAAI,WAAW;IACf,YAAY;IACZ,KAAK,oBAAoB,gBAAgB,kBAAkB;IAC3D,OAAO,aAAa,aAAa;IACjC,OAAO;GACT;GACA,KAAK,iBAAiB,gBAAgB,kBAAkB;GACxD,gBAAgB,OAAO,WAAW,UAAU,GAA2B;GACvE,IAAI,OAAO,aAAa,kCAAkC,CAAC,CAAC,SAC1D,SAAS;EAEb,GACA,CAAC,MAAM,aAAa,CAKpB;CACF,CAAC;CAED,gBAAgB;EACd,aAAa;GACX,MAAM,SAAS,UAAU;GACzB,IAAI,WAAW,UAAa,CAAC,OAAO,UAAU;IAC5C,OAAO,WAAW;IAClB,MAAM,SAAS,OAAO,UAAU;IAChC,IAAI,WAAW,QACb,OAAO,UAAU,MAAM;GAE3B;GACA,UAAU,UAAU;GACpB,SAAS,UAAU,CAAA;GACnB,aAAa,UAAU;EACzB;CACF,GAAG,CAAC,eAAe,CAAC;CAEpB,SAAS,WAAW;EAClB,MAAM,OAAO,SAAS,QAAQ,MAAM;EACpC,IAAI,SAAS,QAAW;GACtB,aAAa,UAAU;GACvB;EACF;EAEA,MAAM,EAAE,UAAU,SAAS,YAAY;EACvC,aAAa,UAAU;EACvB,MAAM,UAAU,OAAO,aAAa,kCAAkC,CAAC,CACpE,UACC,IACA;EACJ,MAAM,MAAM,MAAM,IAAI,SAAS,EAE7B,SAAS,KAAK,IAAI,GAAG,WAAW,OAAO,EACzC,CAAC;EACD,UAAU,UAAU;GAAE;GAAK;GAAS,UAAU;EAAM;CACtD;CACA,YAAY,UAAU;CAEtB,SAAS,QACP,SACA,SACA;EACA,MAAM,WACJ,OAAO,mBAAmB,YAAY,OAAO,SAAS,cAAc,IAChE,KAAK,IAAI,GAAG,cAAc,IAC1B;EAEN,SAAS,QAAQ,KAAK;GACpB;GACA;GACA;EACF,CAAC;EACD,IAAI,CAAC,aAAa,SAChB,SAAS;OACJ,IAAI,UAAU,WAAW;GAC9B,MAAM,SAAS,UAAU;GACzB,IAAI,WAAW,QAAW;GAE1B,OAAO,SAAS;GAChB,WAAW,UAAU,CAAC;GACtB,SAAS,QAAQ,SAAS;GAC1B,MAAM,UAAU,SAAS,QAAQ;GACjC,SAAS,QAAQ,UAAU;GAC3B,IAAI,YAAY,QACd,QAAQ;QAER,MAAM,MAAM,OAAO,GAAG;EAE1B;CACF;CAEA,SAAS,MAAM,KAAa,QAAsB;EAChD,MAAM,SAAS,UAAU;EACzB,IAAI,QAAQ,QAAQ,KAAK;EACzB,OAAO,SAAS;EAChB,SAAS,QAAQ,SAAS;EAC1B,MAAM,UAAU,SAAS,QAAQ;EACjC,SAAS,QAAQ,UAAU;EAC3B,IAAI,YAAY,QACd,QAAQ;OAER,MAAM,MAAM,GAAG;CAEnB;CAEA,SAAS,eAAe;EACtB,SAAS,QAAQ,SAAS;CAC5B;CAEA,SAAS,aAAa;EACpB,SAAS,QAAQ,SAAS;EAC1B,MAAM,UAAU,SAAS,QAAQ;EACjC,SAAS,QAAQ,UAAU;EAC3B,UAAU;CACZ;CAEA,SAAS,aAAa;EACpB,SAAS,QAAQ,SAAS;EAC1B,SAAS,QAAQ,UAAU;CAC7B;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
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 animateReplace = false,\n }: {\n duration?: number\n order?: NotificationOrder\n animateReplace?: boolean\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 finish() {\n const active = activeRef.current\n activeRef.current = undefined\n update()\n if (active !== undefined && !active.notified) {\n active.notified = true\n active.onClose?.(active.reason)\n }\n playNextRef.current?.()\n }\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\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],\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 active.notified = true\n active.onClose?.(active.reason)\n }\n activeRef.current = undefined\n queueRef.current = []\n isShowingRef.current = false\n }\n }, [])\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 replaceRef.current = !animateReplace\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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"}