@mattermost/compass-ui 0.1.0-alpha.13 → 0.1.0-alpha.14
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/channel-sidebar-item/ChannelSidebarItem.cjs +1 -1
- package/dist/components/channel-sidebar-item/ChannelSidebarItem.cjs.map +1 -1
- package/dist/components/channel-sidebar-item/ChannelSidebarItem.d.ts.map +1 -1
- package/dist/components/channel-sidebar-item/ChannelSidebarItem.js +32 -31
- package/dist/components/channel-sidebar-item/ChannelSidebarItem.js.map +1 -1
- package/dist/components/checkbox/Checkbox.css +1 -1
- package/dist/components/checkbox/Checkbox.module.scss.cjs +1 -1
- package/dist/components/checkbox/Checkbox.module.scss.js +10 -10
- package/dist/components/icon-button/IconButton.css +1 -1
- package/dist/components/icon-button/IconButton.module.scss.cjs +1 -1
- package/dist/components/icon-button/IconButton.module.scss.js +15 -15
- package/dist/components/modal/Modal.cjs +1 -1
- package/dist/components/modal/Modal.cjs.map +1 -1
- package/dist/components/modal/Modal.css +1 -1
- package/dist/components/modal/Modal.d.ts +25 -3
- package/dist/components/modal/Modal.d.ts.map +1 -1
- package/dist/components/modal/Modal.js +105 -68
- package/dist/components/modal/Modal.js.map +1 -1
- package/dist/components/modal/Modal.module.scss.cjs +1 -1
- package/dist/components/modal/Modal.module.scss.js +14 -12
- package/dist/components/modal/Modal.module.scss.js.map +1 -1
- package/dist/components/modal-header/ModalHeader.css +1 -1
- package/dist/components/modal-header/ModalHeader.module.scss.cjs +1 -1
- package/dist/components/modal-header/ModalHeader.module.scss.js +15 -15
- package/dist/components/popover-menu/PopoverMenu.cjs +1 -1
- package/dist/components/popover-menu/PopoverMenu.cjs.map +1 -1
- package/dist/components/popover-menu/PopoverMenu.css +1 -1
- package/dist/components/popover-menu/PopoverMenu.d.ts +9 -1
- package/dist/components/popover-menu/PopoverMenu.d.ts.map +1 -1
- package/dist/components/popover-menu/PopoverMenu.js +39 -28
- package/dist/components/popover-menu/PopoverMenu.js.map +1 -1
- package/dist/components/popover-menu/PopoverMenu.module.scss.cjs +1 -1
- package/dist/components/popover-menu/PopoverMenu.module.scss.js +12 -10
- package/dist/components/popover-menu/PopoverMenu.module.scss.js.map +1 -1
- package/dist/components/radio/Radio.css +1 -1
- package/dist/components/radio/Radio.module.scss.cjs +1 -1
- package/dist/components/radio/Radio.module.scss.js +14 -14
- package/dist/components/switch/Switch.css +1 -1
- package/dist/components/switch/Switch.module.scss.cjs +1 -1
- package/dist/components/switch/Switch.module.scss.js +11 -11
- package/dist/components/tabs/Tabs.cjs +1 -1
- package/dist/components/tabs/Tabs.cjs.map +1 -1
- package/dist/components/tabs/Tabs.css +1 -1
- package/dist/components/tabs/Tabs.d.ts +4 -1
- package/dist/components/tabs/Tabs.d.ts.map +1 -1
- package/dist/components/tabs/Tabs.js +41 -35
- package/dist/components/tabs/Tabs.js.map +1 -1
- package/dist/components/tabs/Tabs.module.scss.cjs +1 -1
- package/dist/components/tabs/Tabs.module.scss.js +13 -12
- package/dist/components/tabs/Tabs.module.scss.js.map +1 -1
- package/dist/components/tabs/index.d.ts +1 -1
- package/dist/components/tabs/index.d.ts.map +1 -1
- package/dist/hooks/use-modal-focus.cjs +2 -0
- package/dist/hooks/use-modal-focus.cjs.map +1 -0
- package/dist/hooks/use-modal-focus.d.ts +26 -0
- package/dist/hooks/use-modal-focus.d.ts.map +1 -0
- package/dist/hooks/use-modal-focus.js +91 -0
- package/dist/hooks/use-modal-focus.js.map +1 -0
- package/dist/hooks/use-popover-transition.cjs +1 -1
- package/dist/hooks/use-popover-transition.cjs.map +1 -1
- package/dist/hooks/use-popover-transition.d.ts +6 -4
- package/dist/hooks/use-popover-transition.d.ts.map +1 -1
- package/dist/hooks/use-popover-transition.js +19 -15
- package/dist/hooks/use-popover-transition.js.map +1 -1
- package/dist/utils/duration.cjs +2 -0
- package/dist/utils/duration.cjs.map +1 -0
- package/dist/utils/duration.d.ts +8 -0
- package/dist/utils/duration.d.ts.map +1 -0
- package/dist/utils/duration.js +11 -0
- package/dist/utils/duration.js.map +1 -0
- package/package.json +4 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tabs.js","sources":["../../../src/components/tabs/Tabs.tsx"],"sourcesContent":["import {\n useEffect,\n useRef,\n useState,\n type HTMLAttributes,\n type KeyboardEvent,\n type ReactNode,\n} from 'react';\nimport type { BuiltInButtonProps } from '@/utils/props';\nimport styles from './Tabs.module.scss';\n\nexport interface TabItem {\n /** Unique key for this tab. */\n key: string;\n /** Tab label. Accepts translated nodes (e.g. `<FormattedMessage/>`). */\n label: ReactNode;\n /** Optional count badge number. */\n countBadge?: number;\n /** When true, shows an unread dot badge. */\n unreadBadge?: boolean;\n /** Optional id for this tab (pair with `panelId` for `aria-controls`). */\n id?: string;\n /** Optional id of the host tabpanel this tab controls. */\n panelId?: string;\n /** When true, the tab can't be selected and arrow-key navigation skips it. */\n disabled?: boolean;\n /** Native tooltip text, e.g. to explain why the tab is disabled. */\n title?: string;\n /**\n * Extra attributes for this tab's `<button>` (e.g. `data-testid`). Tab\n * semantics (`role`, `aria-selected`, `tabIndex`, handlers) take precedence.\n */\n buttonProps?: BuiltInButtonProps;\n}\n\nexport interface TabsProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'onChange'\n> {\n /** Array of tab items to display. */\n tabs: TabItem[];\n /** Key of the currently active tab. */\n activeKey: string;\n /** Called when a tab is selected. */\n onChange: (key: string) => void;\n /** Optional CSS class name. */\n className?: string;\n /** Optional trailing controls rendered to the right of the tabs. */\n controls?: ReactNode;\n /** Visually hidden text announced with a tab's unread dot. Default: \"Unread\". */\n unreadLabel?: ReactNode;\n}\n\n/**\n * Tabs let people switch between sibling views inside the same surface — Mentions in the\n * right sidebar, sections of a settings panel, or filters on a list. The content area\n * changes; the surrounding chrome doesn't.\n */\nexport default function Tabs({\n tabs,\n activeKey,\n onChange,\n className = '',\n controls,\n unreadLabel = 'Unread',\n ...rest\n}: TabsProps) {\n const rootClass = [styles.tabs
|
|
1
|
+
{"version":3,"file":"Tabs.js","sources":["../../../src/components/tabs/Tabs.tsx"],"sourcesContent":["import {\n useEffect,\n useRef,\n useState,\n type HTMLAttributes,\n type KeyboardEvent,\n type ReactNode,\n} from 'react';\nimport type { BuiltInButtonProps } from '@/utils/props';\nimport { toKebab } from '@/utils/string';\nimport styles from './Tabs.module.scss';\n\nexport type TabsAppearance = 'default' | 'underlined';\n\nexport interface TabItem {\n /** Unique key for this tab. */\n key: string;\n /** Tab label. Accepts translated nodes (e.g. `<FormattedMessage/>`). */\n label: ReactNode;\n /** Optional count badge number. */\n countBadge?: number;\n /** When true, shows an unread dot badge. */\n unreadBadge?: boolean;\n /** Optional id for this tab (pair with `panelId` for `aria-controls`). */\n id?: string;\n /** Optional id of the host tabpanel this tab controls. */\n panelId?: string;\n /** When true, the tab can't be selected and arrow-key navigation skips it. */\n disabled?: boolean;\n /** Native tooltip text, e.g. to explain why the tab is disabled. */\n title?: string;\n /**\n * Extra attributes for this tab's `<button>` (e.g. `data-testid`). Tab\n * semantics (`role`, `aria-selected`, `tabIndex`, handlers) take precedence.\n */\n buttonProps?: BuiltInButtonProps;\n}\n\nexport interface TabsProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'onChange'\n> {\n /** Array of tab items to display. */\n tabs: TabItem[];\n /** Key of the currently active tab. */\n activeKey: string;\n /** Called when a tab is selected. */\n onChange: (key: string) => void;\n /** Optional CSS class name. */\n className?: string;\n /** Optional trailing controls rendered to the right of the tabs. */\n controls?: ReactNode;\n /** Visual style. `underlined` shows a bottom rule with an indicator under the active tab. Default: `default`. */\n appearance?: TabsAppearance;\n /** Visually hidden text announced with a tab's unread dot. Default: \"Unread\". */\n unreadLabel?: ReactNode;\n}\n\n/**\n * Tabs let people switch between sibling views inside the same surface — Mentions in the\n * right sidebar, sections of a settings panel, or filters on a list. The content area\n * changes; the surrounding chrome doesn't.\n */\nexport default function Tabs({\n tabs,\n activeKey,\n onChange,\n className = '',\n controls,\n unreadLabel = 'Unread',\n appearance = 'default',\n ...rest\n}: TabsProps) {\n const rootClass = [\n styles.tabs,\n appearance !== 'default'\n ? styles[`tabs--appearance-${toKebab(appearance)}`]\n : '',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n const tabRefs = useRef<Array<HTMLButtonElement | null>>([]);\n const [focusedKey, setFocusedKey] = useState(activeKey);\n\n useEffect(() => {\n setFocusedKey(activeKey);\n }, [activeKey]);\n\n const keys = tabs.map((tab) => tab.key);\n const enabledKeys = tabs.filter((tab) => !tab.disabled).map((tab) => tab.key);\n const focusKey = enabledKeys.includes(focusedKey)\n ? focusedKey\n : enabledKeys.includes(activeKey)\n ? activeKey\n : enabledKeys[0];\n\n const moveFocus = (key: string) => {\n setFocusedKey(key);\n const index = keys.indexOf(key);\n tabRefs.current[index]?.focus();\n };\n\n const handleKeyDown = (\n event: KeyboardEvent<HTMLButtonElement>,\n key: string,\n ) => {\n const count = enabledKeys.length;\n if (count === 0) return;\n const index = enabledKeys.indexOf(key);\n\n if (event.key === 'ArrowRight') {\n event.preventDefault();\n moveFocus(enabledKeys[(index + 1) % count]);\n } else if (event.key === 'ArrowLeft') {\n event.preventDefault();\n moveFocus(\n enabledKeys[index < 0 ? count - 1 : (index - 1 + count) % count],\n );\n } else if (event.key === 'Home') {\n event.preventDefault();\n moveFocus(enabledKeys[0]);\n } else if (event.key === 'End') {\n event.preventDefault();\n moveFocus(enabledKeys[count - 1]);\n } else if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n if (index >= 0) onChange(key);\n }\n };\n\n return (\n <div {...rest} className={rootClass}>\n <div className={styles['tabs__tab-list']} role=\"tablist\">\n {tabs.map((tab, index) => {\n const isActive = tab.key === activeKey;\n const isDisabled = tab.disabled === true;\n const tabClass = [\n styles['tabs__tab'],\n isActive ? styles['tabs__tab--active'] : '',\n isDisabled ? styles['tabs__tab--disabled'] : '',\n tab.buttonProps?.className,\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <button\n {...tab.buttonProps}\n key={tab.key}\n ref={(node) => {\n tabRefs.current[index] = node;\n }}\n id={tab.id ?? tab.buttonProps?.id}\n role=\"tab\"\n type=\"button\"\n tabIndex={focusKey === tab.key ? 0 : -1}\n aria-selected={isActive}\n aria-controls={tab.panelId}\n aria-disabled={isDisabled || undefined}\n title={tab.title ?? tab.buttonProps?.title}\n className={tabClass}\n onMouseDown={\n isDisabled ? (event) => event.preventDefault() : undefined\n }\n onClick={() => {\n if (isDisabled) return;\n setFocusedKey(tab.key);\n onChange(tab.key);\n }}\n onKeyDown={(event) => handleKeyDown(event, tab.key)}\n >\n <span className={styles['tabs__tab-label']}>{tab.label}</span>\n\n {tab.countBadge != null && (\n <span\n className={[\n styles['tabs__count-badge'],\n isActive ? styles['tabs__count-badge--active'] : '',\n ]\n .filter(Boolean)\n .join(' ')}\n >\n {tab.countBadge}\n </span>\n )}\n\n {tab.unreadBadge && !isActive && (\n <span className={styles['tabs__unread-badge']}>\n <span className={styles['tabs__unread-label']}>\n {unreadLabel}\n </span>\n </span>\n )}\n </button>\n );\n })}\n </div>\n\n {controls != null && (\n <div className={styles['tabs__controls']}>{controls}</div>\n )}\n </div>\n );\n}\n"],"names":["Tabs","tabs","activeKey","onChange","className","controls","unreadLabel","appearance","rest","rootClass","styles","toKebab","tabRefs","useRef","focusedKey","setFocusedKey","useState","useEffect","keys","tab","enabledKeys","focusKey","moveFocus","key","index","_a","handleKeyDown","event","count","jsxs","jsx","isActive","isDisabled","tabClass","createElement","node","_b","_c"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});require('./Tabs.css');const _="
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});require('./Tabs.css');const _="_tabs_69hbz_2",a="_tabs__tab_69hbz_7",t="_tabs__controls_69hbz_97",b={tabs:_,"tabs__tab-list":"_tabs__tab-list_69hbz_7",tabs__tab:a,"tabs__tab--active":"_tabs__tab--active_69hbz_31","tabs__tab--disabled":"_tabs__tab--disabled_69hbz_31","tabs__tab-label":"_tabs__tab-label_69hbz_37","tabs__count-badge":"_tabs__count-badge_69hbz_40","tabs__count-badge--active":"_tabs__count-badge--active_69hbz_75","tabs__unread-badge":"_tabs__unread-badge_69hbz_79","tabs__unread-label":"_tabs__unread-label_69hbz_86",tabs__controls:t,"tabs--appearance-underlined":"_tabs--appearance-underlined_69hbz_103"};exports.default=b;exports.tabs=_;exports.tabs__controls=t;exports.tabs__tab=a;
|
|
2
2
|
//# sourceMappingURL=Tabs.module.scss.cjs.map
|
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import './Tabs.css';const _ = "
|
|
1
|
+
import './Tabs.css';const _ = "_tabs_69hbz_2", a = "_tabs__tab_69hbz_7", b = "_tabs__controls_69hbz_97", t = {
|
|
2
2
|
tabs: _,
|
|
3
|
-
"tabs__tab-list": "_tabs__tab-
|
|
3
|
+
"tabs__tab-list": "_tabs__tab-list_69hbz_7",
|
|
4
4
|
tabs__tab: a,
|
|
5
|
-
"tabs__tab--active": "_tabs__tab--
|
|
6
|
-
"tabs__tab--disabled": "_tabs__tab--
|
|
7
|
-
"tabs__tab-label": "_tabs__tab-
|
|
8
|
-
"tabs__count-badge": "_tabs__count-
|
|
9
|
-
"tabs__count-badge--active": "_tabs__count-badge--
|
|
10
|
-
"tabs__unread-badge": "_tabs__unread-
|
|
11
|
-
"tabs__unread-label": "_tabs__unread-
|
|
12
|
-
tabs__controls:
|
|
5
|
+
"tabs__tab--active": "_tabs__tab--active_69hbz_31",
|
|
6
|
+
"tabs__tab--disabled": "_tabs__tab--disabled_69hbz_31",
|
|
7
|
+
"tabs__tab-label": "_tabs__tab-label_69hbz_37",
|
|
8
|
+
"tabs__count-badge": "_tabs__count-badge_69hbz_40",
|
|
9
|
+
"tabs__count-badge--active": "_tabs__count-badge--active_69hbz_75",
|
|
10
|
+
"tabs__unread-badge": "_tabs__unread-badge_69hbz_79",
|
|
11
|
+
"tabs__unread-label": "_tabs__unread-label_69hbz_86",
|
|
12
|
+
tabs__controls: b,
|
|
13
|
+
"tabs--appearance-underlined": "_tabs--appearance-underlined_69hbz_103"
|
|
13
14
|
};
|
|
14
15
|
export {
|
|
15
|
-
|
|
16
|
+
t as default,
|
|
16
17
|
_ as tabs,
|
|
17
|
-
|
|
18
|
+
b as tabs__controls,
|
|
18
19
|
a as tabs__tab
|
|
19
20
|
};
|
|
20
21
|
//# sourceMappingURL=Tabs.module.scss.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tabs.module.scss.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Tabs.module.scss.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/tabs/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,QAAQ,CAAC;AACzC,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/tabs/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,QAAQ,CAAC;AACzC,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("react"),S=["a[href]","area[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])","summary",'[contenteditable=""]','[contenteditable="true"]',"[tabindex]"].join(",");function w(r,s){for(let t=r;t;t=t.parentElement){if(t.hidden||t.hasAttribute("inert")||t.getAttribute("aria-hidden")==="true")return!0;const l=getComputedStyle(t);if(l.display==="none"||t===r&&l.visibility==="hidden")return!0;if(t===s)break}return!1}function x(r){return Array.from(r.querySelectorAll(S)).filter(s=>s.tabIndex>=0&&!s.matches(":disabled")&&!w(s,r))}const u=[];function T(r,{initialFocusRef:s,closeOnEscape:t=!0,onClose:l,active:p=!0}){const y=d.useRef(s);y.current=s;const m=d.useRef({closeOnEscape:t,onClose:l});m.current={closeOnEscape:t,onClose:l};const v=d.useRef(null),a=d.useRef(null);d.useEffect(()=>{var i;const c=document.activeElement;function b(){const o=u[u.length-1],f=v.current;return f&&u.includes(f)?o===f:a.current?a.current.wasTop&&o===a.current.below:o===void 0}const e=r.current,n=(i=y.current)==null?void 0:i.current;return n&&(e!=null&&e.contains(n))&&n.focus(),(!n||document.activeElement!==n)&&(e==null||e.focus()),()=>{b()&&c&&c.isConnected&&typeof c.focus=="function"&&c.focus()}},[r]),d.useEffect(()=>{if(!p)return;const c={};u.push(c),v.current=c,a.current=null;function b(e){var E,k;if(e.defaultPrevented||u[u.length-1]!==c)return;const n=r.current;if(!n)return;if(e.key==="Escape"){m.current.closeOnEscape&&((k=(E=m.current).onClose)==null||k.call(E));return}if(e.key!=="Tab")return;const i=x(n);if(i.length===0){e.preventDefault(),n.focus();return}const o=i[0],f=i[i.length-1],h=document.activeElement,g=!n.contains(h);e.shiftKey?(g||h===o||h===n)&&(e.preventDefault(),f.focus()):(g||h===f)&&(e.preventDefault(),o.focus())}return document.addEventListener("keydown",b),()=>{document.removeEventListener("keydown",b);const e=u.indexOf(c);a.current={wasTop:e===u.length-1,below:u[e-1]},u.splice(e,1)}},[r,p])}exports.useModalFocus=T;
|
|
2
|
+
//# sourceMappingURL=use-modal-focus.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-modal-focus.cjs","sources":["../../src/hooks/use-modal-focus.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\nimport type { RefObject } from 'react';\n\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'area[href]',\n 'button:not([disabled])',\n 'input:not([disabled]):not([type=\"hidden\"])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n 'summary',\n '[contenteditable=\"\"]',\n '[contenteditable=\"true\"]',\n '[tabindex]',\n].join(',');\n\nfunction isHidden(el: HTMLElement, root: HTMLElement): boolean {\n for (let node: HTMLElement | null = el; node; node = node.parentElement) {\n if (node.hidden || node.hasAttribute('inert')) return true;\n if (node.getAttribute('aria-hidden') === 'true') return true;\n const style = getComputedStyle(node);\n if (style.display === 'none') return true;\n if (node === el && style.visibility === 'hidden') return true;\n if (node === root) break;\n }\n return false;\n}\n\nfunction getFocusable(root: HTMLElement): HTMLElement[] {\n return Array.from(\n root.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR),\n ).filter(\n (el) => el.tabIndex >= 0 && !el.matches(':disabled') && !isHidden(el, root),\n );\n}\n\n/** Open modals in mount order; only the last one reacts to Tab and Escape. */\nconst modalStack: object[] = [];\n\nexport interface UseModalFocusOptions {\n /** Element to focus on mount. Falls back to the dialog root. */\n initialFocusRef?: RefObject<HTMLElement | null>;\n /** Close on Escape. Default true. */\n closeOnEscape?: boolean;\n onClose?: () => void;\n /**\n * When false (e.g. during an exit transition) Tab and Escape are no longer\n * handled and the modal leaves the stack, but focus is still restored on\n * unmount. Default true.\n */\n active?: boolean;\n}\n\n/**\n * Dialog focus and keyboard behavior for a modal panel: initial focus, Tab trap\n * (focusable elements computed at keypress), Escape (skipped when already\n * handled), and focus restore on unmount.\n *\n * Tab and Escape are handled by one document listener, so the trap still holds\n * when focus sits outside the dialog (e.g. after a backdrop click), and only\n * the topmost active modal responds. Events whose default was prevented (by an\n * inner widget or the host's `onKeyDown`) are ignored.\n */\nexport function useModalFocus(\n rootRef: RefObject<HTMLElement | null>,\n {\n initialFocusRef,\n closeOnEscape = true,\n onClose,\n active = true,\n }: UseModalFocusOptions,\n): void {\n const initialFocusRefRef = useRef(initialFocusRef);\n initialFocusRefRef.current = initialFocusRef;\n const latest = useRef({ closeOnEscape, onClose });\n latest.current = { closeOnEscape, onClose };\n const keyToken = useRef<object | null>(null);\n // Stack position recorded when the token leaves the stack (exit start).\n const exit = useRef<{ wasTop: boolean; below: object | undefined } | null>(\n null,\n );\n\n useEffect(() => {\n const previous = document.activeElement as HTMLElement | null;\n function shouldRestoreFocus(): boolean {\n const top = modalStack[modalStack.length - 1];\n const token = keyToken.current;\n if (token && modalStack.includes(token)) return top === token;\n if (!exit.current) return top === undefined;\n return exit.current.wasTop && top === exit.current.below;\n }\n const root = rootRef.current;\n const target = initialFocusRefRef.current?.current;\n if (target && root?.contains(target)) target.focus();\n if (!target || document.activeElement !== target) root?.focus();\n return () => {\n if (\n shouldRestoreFocus() &&\n previous &&\n previous.isConnected &&\n typeof previous.focus === 'function'\n ) {\n previous.focus();\n }\n };\n }, [rootRef]);\n\n useEffect(() => {\n if (!active) return;\n const token = {};\n modalStack.push(token);\n keyToken.current = token;\n exit.current = null;\n\n function handle(e: KeyboardEvent) {\n if (e.defaultPrevented || modalStack[modalStack.length - 1] !== token)\n return;\n const dialog = rootRef.current;\n if (!dialog) return;\n if (e.key === 'Escape') {\n if (latest.current.closeOnEscape) latest.current.onClose?.();\n return;\n }\n if (e.key !== 'Tab') return;\n const focusable = getFocusable(dialog);\n if (focusable.length === 0) {\n e.preventDefault();\n dialog.focus();\n return;\n }\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const current = document.activeElement;\n const outside = !dialog.contains(current);\n if (e.shiftKey) {\n if (outside || current === first || current === dialog) {\n e.preventDefault();\n last.focus();\n }\n } else if (outside || current === last) {\n e.preventDefault();\n first.focus();\n }\n }\n document.addEventListener('keydown', handle);\n return () => {\n document.removeEventListener('keydown', handle);\n const index = modalStack.indexOf(token);\n exit.current = {\n wasTop: index === modalStack.length - 1,\n below: modalStack[index - 1],\n };\n modalStack.splice(index, 1);\n };\n }, [rootRef, active]);\n}\n"],"names":["FOCUSABLE_SELECTOR","isHidden","el","root","node","style","getFocusable","modalStack","useModalFocus","rootRef","initialFocusRef","closeOnEscape","onClose","active","initialFocusRefRef","useRef","latest","keyToken","exit","useEffect","previous","shouldRestoreFocus","top","token","target","_a","handle","dialog","_b","focusable","first","last","current","outside","index"],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
export interface UseModalFocusOptions {
|
|
3
|
+
/** Element to focus on mount. Falls back to the dialog root. */
|
|
4
|
+
initialFocusRef?: RefObject<HTMLElement | null>;
|
|
5
|
+
/** Close on Escape. Default true. */
|
|
6
|
+
closeOnEscape?: boolean;
|
|
7
|
+
onClose?: () => void;
|
|
8
|
+
/**
|
|
9
|
+
* When false (e.g. during an exit transition) Tab and Escape are no longer
|
|
10
|
+
* handled and the modal leaves the stack, but focus is still restored on
|
|
11
|
+
* unmount. Default true.
|
|
12
|
+
*/
|
|
13
|
+
active?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Dialog focus and keyboard behavior for a modal panel: initial focus, Tab trap
|
|
17
|
+
* (focusable elements computed at keypress), Escape (skipped when already
|
|
18
|
+
* handled), and focus restore on unmount.
|
|
19
|
+
*
|
|
20
|
+
* Tab and Escape are handled by one document listener, so the trap still holds
|
|
21
|
+
* when focus sits outside the dialog (e.g. after a backdrop click), and only
|
|
22
|
+
* the topmost active modal responds. Events whose default was prevented (by an
|
|
23
|
+
* inner widget or the host's `onKeyDown`) are ignored.
|
|
24
|
+
*/
|
|
25
|
+
export declare function useModalFocus(rootRef: RefObject<HTMLElement | null>, { initialFocusRef, closeOnEscape, onClose, active, }: UseModalFocusOptions): void;
|
|
26
|
+
//# sourceMappingURL=use-modal-focus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-modal-focus.d.ts","sourceRoot":"","sources":["../../src/hooks/use-modal-focus.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAsCvC,MAAM,WAAW,oBAAoB;IACnC,gEAAgE;IAChE,eAAe,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAChD,qCAAqC;IACrC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EACtC,EACE,eAAe,EACf,aAAoB,EACpB,OAAO,EACP,MAAa,GACd,EAAE,oBAAoB,GACtB,IAAI,CAoFN"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { useRef as m, useEffect as x } from "react";
|
|
2
|
+
const w = [
|
|
3
|
+
"a[href]",
|
|
4
|
+
"area[href]",
|
|
5
|
+
"button:not([disabled])",
|
|
6
|
+
'input:not([disabled]):not([type="hidden"])',
|
|
7
|
+
"select:not([disabled])",
|
|
8
|
+
"textarea:not([disabled])",
|
|
9
|
+
"summary",
|
|
10
|
+
'[contenteditable=""]',
|
|
11
|
+
'[contenteditable="true"]',
|
|
12
|
+
"[tabindex]"
|
|
13
|
+
].join(",");
|
|
14
|
+
function A(r, i) {
|
|
15
|
+
for (let t = r; t; t = t.parentElement) {
|
|
16
|
+
if (t.hidden || t.hasAttribute("inert") || t.getAttribute("aria-hidden") === "true") return !0;
|
|
17
|
+
const d = getComputedStyle(t);
|
|
18
|
+
if (d.display === "none" || t === r && d.visibility === "hidden") return !0;
|
|
19
|
+
if (t === i) break;
|
|
20
|
+
}
|
|
21
|
+
return !1;
|
|
22
|
+
}
|
|
23
|
+
function S(r) {
|
|
24
|
+
return Array.from(
|
|
25
|
+
r.querySelectorAll(w)
|
|
26
|
+
).filter(
|
|
27
|
+
(i) => i.tabIndex >= 0 && !i.matches(":disabled") && !A(i, r)
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
const u = [];
|
|
31
|
+
function C(r, {
|
|
32
|
+
initialFocusRef: i,
|
|
33
|
+
closeOnEscape: t = !0,
|
|
34
|
+
onClose: d,
|
|
35
|
+
active: h = !0
|
|
36
|
+
}) {
|
|
37
|
+
const y = m(i);
|
|
38
|
+
y.current = i;
|
|
39
|
+
const p = m({ closeOnEscape: t, onClose: d });
|
|
40
|
+
p.current = { closeOnEscape: t, onClose: d };
|
|
41
|
+
const v = m(null), l = m(
|
|
42
|
+
null
|
|
43
|
+
);
|
|
44
|
+
x(() => {
|
|
45
|
+
var o;
|
|
46
|
+
const c = document.activeElement;
|
|
47
|
+
function a() {
|
|
48
|
+
const s = u[u.length - 1], f = v.current;
|
|
49
|
+
return f && u.includes(f) ? s === f : l.current ? l.current.wasTop && s === l.current.below : s === void 0;
|
|
50
|
+
}
|
|
51
|
+
const e = r.current, n = (o = y.current) == null ? void 0 : o.current;
|
|
52
|
+
return n && (e != null && e.contains(n)) && n.focus(), (!n || document.activeElement !== n) && (e == null || e.focus()), () => {
|
|
53
|
+
a() && c && c.isConnected && typeof c.focus == "function" && c.focus();
|
|
54
|
+
};
|
|
55
|
+
}, [r]), x(() => {
|
|
56
|
+
if (!h) return;
|
|
57
|
+
const c = {};
|
|
58
|
+
u.push(c), v.current = c, l.current = null;
|
|
59
|
+
function a(e) {
|
|
60
|
+
var g, k;
|
|
61
|
+
if (e.defaultPrevented || u[u.length - 1] !== c)
|
|
62
|
+
return;
|
|
63
|
+
const n = r.current;
|
|
64
|
+
if (!n) return;
|
|
65
|
+
if (e.key === "Escape") {
|
|
66
|
+
p.current.closeOnEscape && ((k = (g = p.current).onClose) == null || k.call(g));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (e.key !== "Tab") return;
|
|
70
|
+
const o = S(n);
|
|
71
|
+
if (o.length === 0) {
|
|
72
|
+
e.preventDefault(), n.focus();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const s = o[0], f = o[o.length - 1], b = document.activeElement, E = !n.contains(b);
|
|
76
|
+
e.shiftKey ? (E || b === s || b === n) && (e.preventDefault(), f.focus()) : (E || b === f) && (e.preventDefault(), s.focus());
|
|
77
|
+
}
|
|
78
|
+
return document.addEventListener("keydown", a), () => {
|
|
79
|
+
document.removeEventListener("keydown", a);
|
|
80
|
+
const e = u.indexOf(c);
|
|
81
|
+
l.current = {
|
|
82
|
+
wasTop: e === u.length - 1,
|
|
83
|
+
below: u[e - 1]
|
|
84
|
+
}, u.splice(e, 1);
|
|
85
|
+
};
|
|
86
|
+
}, [r, h]);
|
|
87
|
+
}
|
|
88
|
+
export {
|
|
89
|
+
C as useModalFocus
|
|
90
|
+
};
|
|
91
|
+
//# sourceMappingURL=use-modal-focus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-modal-focus.js","sources":["../../src/hooks/use-modal-focus.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\nimport type { RefObject } from 'react';\n\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'area[href]',\n 'button:not([disabled])',\n 'input:not([disabled]):not([type=\"hidden\"])',\n 'select:not([disabled])',\n 'textarea:not([disabled])',\n 'summary',\n '[contenteditable=\"\"]',\n '[contenteditable=\"true\"]',\n '[tabindex]',\n].join(',');\n\nfunction isHidden(el: HTMLElement, root: HTMLElement): boolean {\n for (let node: HTMLElement | null = el; node; node = node.parentElement) {\n if (node.hidden || node.hasAttribute('inert')) return true;\n if (node.getAttribute('aria-hidden') === 'true') return true;\n const style = getComputedStyle(node);\n if (style.display === 'none') return true;\n if (node === el && style.visibility === 'hidden') return true;\n if (node === root) break;\n }\n return false;\n}\n\nfunction getFocusable(root: HTMLElement): HTMLElement[] {\n return Array.from(\n root.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR),\n ).filter(\n (el) => el.tabIndex >= 0 && !el.matches(':disabled') && !isHidden(el, root),\n );\n}\n\n/** Open modals in mount order; only the last one reacts to Tab and Escape. */\nconst modalStack: object[] = [];\n\nexport interface UseModalFocusOptions {\n /** Element to focus on mount. Falls back to the dialog root. */\n initialFocusRef?: RefObject<HTMLElement | null>;\n /** Close on Escape. Default true. */\n closeOnEscape?: boolean;\n onClose?: () => void;\n /**\n * When false (e.g. during an exit transition) Tab and Escape are no longer\n * handled and the modal leaves the stack, but focus is still restored on\n * unmount. Default true.\n */\n active?: boolean;\n}\n\n/**\n * Dialog focus and keyboard behavior for a modal panel: initial focus, Tab trap\n * (focusable elements computed at keypress), Escape (skipped when already\n * handled), and focus restore on unmount.\n *\n * Tab and Escape are handled by one document listener, so the trap still holds\n * when focus sits outside the dialog (e.g. after a backdrop click), and only\n * the topmost active modal responds. Events whose default was prevented (by an\n * inner widget or the host's `onKeyDown`) are ignored.\n */\nexport function useModalFocus(\n rootRef: RefObject<HTMLElement | null>,\n {\n initialFocusRef,\n closeOnEscape = true,\n onClose,\n active = true,\n }: UseModalFocusOptions,\n): void {\n const initialFocusRefRef = useRef(initialFocusRef);\n initialFocusRefRef.current = initialFocusRef;\n const latest = useRef({ closeOnEscape, onClose });\n latest.current = { closeOnEscape, onClose };\n const keyToken = useRef<object | null>(null);\n // Stack position recorded when the token leaves the stack (exit start).\n const exit = useRef<{ wasTop: boolean; below: object | undefined } | null>(\n null,\n );\n\n useEffect(() => {\n const previous = document.activeElement as HTMLElement | null;\n function shouldRestoreFocus(): boolean {\n const top = modalStack[modalStack.length - 1];\n const token = keyToken.current;\n if (token && modalStack.includes(token)) return top === token;\n if (!exit.current) return top === undefined;\n return exit.current.wasTop && top === exit.current.below;\n }\n const root = rootRef.current;\n const target = initialFocusRefRef.current?.current;\n if (target && root?.contains(target)) target.focus();\n if (!target || document.activeElement !== target) root?.focus();\n return () => {\n if (\n shouldRestoreFocus() &&\n previous &&\n previous.isConnected &&\n typeof previous.focus === 'function'\n ) {\n previous.focus();\n }\n };\n }, [rootRef]);\n\n useEffect(() => {\n if (!active) return;\n const token = {};\n modalStack.push(token);\n keyToken.current = token;\n exit.current = null;\n\n function handle(e: KeyboardEvent) {\n if (e.defaultPrevented || modalStack[modalStack.length - 1] !== token)\n return;\n const dialog = rootRef.current;\n if (!dialog) return;\n if (e.key === 'Escape') {\n if (latest.current.closeOnEscape) latest.current.onClose?.();\n return;\n }\n if (e.key !== 'Tab') return;\n const focusable = getFocusable(dialog);\n if (focusable.length === 0) {\n e.preventDefault();\n dialog.focus();\n return;\n }\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const current = document.activeElement;\n const outside = !dialog.contains(current);\n if (e.shiftKey) {\n if (outside || current === first || current === dialog) {\n e.preventDefault();\n last.focus();\n }\n } else if (outside || current === last) {\n e.preventDefault();\n first.focus();\n }\n }\n document.addEventListener('keydown', handle);\n return () => {\n document.removeEventListener('keydown', handle);\n const index = modalStack.indexOf(token);\n exit.current = {\n wasTop: index === modalStack.length - 1,\n below: modalStack[index - 1],\n };\n modalStack.splice(index, 1);\n };\n }, [rootRef, active]);\n}\n"],"names":["FOCUSABLE_SELECTOR","isHidden","el","root","node","style","getFocusable","modalStack","useModalFocus","rootRef","initialFocusRef","closeOnEscape","onClose","active","initialFocusRefRef","useRef","latest","keyToken","exit","useEffect","previous","shouldRestoreFocus","top","token","target","_a","handle","dialog","_b","focusable","first","last","current","outside","index"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("react"),m=require("../utils/duration.cjs"),s=150;function f(e,i="--duration-quick"){const[a,n]=r.useState(e),[u,o]=r.useState(!1);return r.useEffect(()=>{if(e){n(!0);let t=0;const l=requestAnimationFrame(()=>{t=requestAnimationFrame(()=>o(!0))});return()=>{cancelAnimationFrame(l),t&&cancelAnimationFrame(t)}}o(!1);const c=window.setTimeout(()=>n(!1),m.readDurationMs(i,s));return()=>window.clearTimeout(c)},[e,i]),{mounted:a,visible:u}}exports.POPOVER_TRANSITION_MS=s;exports.usePopoverTransition=f;
|
|
2
2
|
//# sourceMappingURL=use-popover-transition.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-popover-transition.cjs","sources":["../../src/hooks/use-popover-transition.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\n\n
|
|
1
|
+
{"version":3,"file":"use-popover-transition.cjs","sources":["../../src/hooks/use-popover-transition.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { readDurationMs } from '@/utils/duration';\n\n/** Fallback for `--duration-quick` when the token is not available. */\nexport const POPOVER_TRANSITION_MS = 150;\n\n/**\n * Keeps a surface mounted for the duration of its exit animation so the close\n * transition can play before React unmounts the node. The exit wait is read\n * from `durationToken` (default `--duration-quick`) so it always matches the\n * CSS transition. See CLAUDE.md: \"Animation: popover panel open/close\".\n */\nexport function usePopoverTransition(\n open: boolean,\n durationToken = '--duration-quick',\n) {\n const [mounted, setMounted] = useState(open);\n const [visible, setVisible] = useState(false);\n\n useEffect(() => {\n if (open) {\n setMounted(true);\n let raf2 = 0;\n const raf1 = requestAnimationFrame(() => {\n raf2 = requestAnimationFrame(() => setVisible(true));\n });\n return () => {\n cancelAnimationFrame(raf1);\n if (raf2) cancelAnimationFrame(raf2);\n };\n }\n setVisible(false);\n const t = window.setTimeout(\n () => setMounted(false),\n readDurationMs(durationToken, POPOVER_TRANSITION_MS),\n );\n return () => window.clearTimeout(t);\n }, [open, durationToken]);\n\n return { mounted, visible };\n}\n"],"names":["POPOVER_TRANSITION_MS","usePopoverTransition","open","durationToken","mounted","setMounted","useState","visible","setVisible","useEffect","raf2","raf1","t","readDurationMs"],"mappings":"4IAIaA,EAAwB,IAQ9B,SAASC,EACdC,EACAC,EAAgB,mBAChB,CACA,KAAM,CAACC,EAASC,CAAU,EAAIC,EAAAA,SAASJ,CAAI,EACrC,CAACK,EAASC,CAAU,EAAIF,EAAAA,SAAS,EAAK,EAE5CG,OAAAA,EAAAA,UAAU,IAAM,CACd,GAAIP,EAAM,CACRG,EAAW,EAAI,EACf,IAAIK,EAAO,EACX,MAAMC,EAAO,sBAAsB,IAAM,CACvCD,EAAO,sBAAsB,IAAMF,EAAW,EAAI,CAAC,CACrD,CAAC,EACD,MAAO,IAAM,CACX,qBAAqBG,CAAI,EACrBD,wBAA2BA,CAAI,CACrC,CACF,CACAF,EAAW,EAAK,EAChB,MAAMI,EAAI,OAAO,WACf,IAAMP,EAAW,EAAK,EACtBQ,EAAAA,eAAeV,EAAeH,CAAqB,CAAA,EAErD,MAAO,IAAM,OAAO,aAAaY,CAAC,CACpC,EAAG,CAACV,EAAMC,CAAa,CAAC,EAEjB,CAAE,QAAAC,EAAS,QAAAG,CAAA,CACpB"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
/** Fallback for `--duration-quick` when the token is not available. */
|
|
1
2
|
export declare const POPOVER_TRANSITION_MS = 150;
|
|
2
3
|
/**
|
|
3
|
-
* Keeps a
|
|
4
|
-
* transition can play before React unmounts the node.
|
|
5
|
-
*
|
|
4
|
+
* Keeps a surface mounted for the duration of its exit animation so the close
|
|
5
|
+
* transition can play before React unmounts the node. The exit wait is read
|
|
6
|
+
* from `durationToken` (default `--duration-quick`) so it always matches the
|
|
7
|
+
* CSS transition. See CLAUDE.md: "Animation: popover panel open/close".
|
|
6
8
|
*/
|
|
7
|
-
export declare function usePopoverTransition(open: boolean): {
|
|
9
|
+
export declare function usePopoverTransition(open: boolean, durationToken?: string): {
|
|
8
10
|
mounted: boolean;
|
|
9
11
|
visible: boolean;
|
|
10
12
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-popover-transition.d.ts","sourceRoot":"","sources":["../../src/hooks/use-popover-transition.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC
|
|
1
|
+
{"version":3,"file":"use-popover-transition.d.ts","sourceRoot":"","sources":["../../src/hooks/use-popover-transition.ts"],"names":[],"mappings":"AAGA,uEAAuE;AACvE,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,OAAO,EACb,aAAa,SAAqB;;;EA0BnC"}
|
|
@@ -1,25 +1,29 @@
|
|
|
1
|
-
import { useState as
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { useState as o, useEffect as c } from "react";
|
|
2
|
+
import { readDurationMs as f } from "../utils/duration.js";
|
|
3
|
+
const l = 150;
|
|
4
|
+
function w(e, r = "--duration-quick") {
|
|
5
|
+
const [a, i] = o(e), [s, n] = o(!1);
|
|
6
|
+
return c(() => {
|
|
6
7
|
if (e) {
|
|
7
|
-
|
|
8
|
+
i(!0);
|
|
8
9
|
let t = 0;
|
|
9
|
-
const
|
|
10
|
-
t = requestAnimationFrame(() =>
|
|
10
|
+
const m = requestAnimationFrame(() => {
|
|
11
|
+
t = requestAnimationFrame(() => n(!0));
|
|
11
12
|
});
|
|
12
13
|
return () => {
|
|
13
|
-
cancelAnimationFrame(
|
|
14
|
+
cancelAnimationFrame(m), t && cancelAnimationFrame(t);
|
|
14
15
|
};
|
|
15
16
|
}
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
n(!1);
|
|
18
|
+
const u = window.setTimeout(
|
|
19
|
+
() => i(!1),
|
|
20
|
+
f(r, l)
|
|
21
|
+
);
|
|
22
|
+
return () => window.clearTimeout(u);
|
|
23
|
+
}, [e, r]), { mounted: a, visible: s };
|
|
20
24
|
}
|
|
21
25
|
export {
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
l as POPOVER_TRANSITION_MS,
|
|
27
|
+
w as usePopoverTransition
|
|
24
28
|
};
|
|
25
29
|
//# sourceMappingURL=use-popover-transition.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-popover-transition.js","sources":["../../src/hooks/use-popover-transition.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\n\n
|
|
1
|
+
{"version":3,"file":"use-popover-transition.js","sources":["../../src/hooks/use-popover-transition.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { readDurationMs } from '@/utils/duration';\n\n/** Fallback for `--duration-quick` when the token is not available. */\nexport const POPOVER_TRANSITION_MS = 150;\n\n/**\n * Keeps a surface mounted for the duration of its exit animation so the close\n * transition can play before React unmounts the node. The exit wait is read\n * from `durationToken` (default `--duration-quick`) so it always matches the\n * CSS transition. See CLAUDE.md: \"Animation: popover panel open/close\".\n */\nexport function usePopoverTransition(\n open: boolean,\n durationToken = '--duration-quick',\n) {\n const [mounted, setMounted] = useState(open);\n const [visible, setVisible] = useState(false);\n\n useEffect(() => {\n if (open) {\n setMounted(true);\n let raf2 = 0;\n const raf1 = requestAnimationFrame(() => {\n raf2 = requestAnimationFrame(() => setVisible(true));\n });\n return () => {\n cancelAnimationFrame(raf1);\n if (raf2) cancelAnimationFrame(raf2);\n };\n }\n setVisible(false);\n const t = window.setTimeout(\n () => setMounted(false),\n readDurationMs(durationToken, POPOVER_TRANSITION_MS),\n );\n return () => window.clearTimeout(t);\n }, [open, durationToken]);\n\n return { mounted, visible };\n}\n"],"names":["POPOVER_TRANSITION_MS","usePopoverTransition","open","durationToken","mounted","setMounted","useState","visible","setVisible","useEffect","raf2","raf1","t","readDurationMs"],"mappings":";;AAIO,MAAMA,IAAwB;AAQ9B,SAASC,EACdC,GACAC,IAAgB,oBAChB;AACA,QAAM,CAACC,GAASC,CAAU,IAAIC,EAASJ,CAAI,GACrC,CAACK,GAASC,CAAU,IAAIF,EAAS,EAAK;AAE5C,SAAAG,EAAU,MAAM;AACd,QAAIP,GAAM;AACR,MAAAG,EAAW,EAAI;AACf,UAAIK,IAAO;AACX,YAAMC,IAAO,sBAAsB,MAAM;AACvC,QAAAD,IAAO,sBAAsB,MAAMF,EAAW,EAAI,CAAC;AAAA,MACrD,CAAC;AACD,aAAO,MAAM;AACX,6BAAqBG,CAAI,GACrBD,0BAA2BA,CAAI;AAAA,MACrC;AAAA,IACF;AACA,IAAAF,EAAW,EAAK;AAChB,UAAMI,IAAI,OAAO;AAAA,MACf,MAAMP,EAAW,EAAK;AAAA,MACtBQ,EAAeV,GAAeH,CAAqB;AAAA,IAAA;AAErD,WAAO,MAAM,OAAO,aAAaY,CAAC;AAAA,EACpC,GAAG,CAACV,GAAMC,CAAa,CAAC,GAEjB,EAAE,SAAAC,GAAS,SAAAG,EAAA;AACpB;"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function u(n,t){if(typeof document>"u")return t;const o=getComputedStyle(document.documentElement).getPropertyValue(n).trim(),e=/^(\d*\.?\d+)(ms|s)$/.exec(o);if(!e)return t;const r=parseFloat(e[1]);return e[2]==="s"?r*1e3:r}exports.readDurationMs=u;
|
|
2
|
+
//# sourceMappingURL=duration.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duration.cjs","sources":["../../src/utils/duration.ts"],"sourcesContent":["/**\n * Reads a duration token (e.g. `--duration-quick`) from the document root and\n * returns milliseconds, so JS timers that wait on a CSS transition follow the\n * token instead of a hard-coded copy. Falls back when the token is missing\n * (SSR, tests, hosts that did not load the stylesheet).\n */\nexport function readDurationMs(token: string, fallbackMs: number): number {\n if (typeof document === 'undefined') return fallbackMs;\n const raw = getComputedStyle(document.documentElement)\n .getPropertyValue(token)\n .trim();\n const match = /^(\\d*\\.?\\d+)(ms|s)$/.exec(raw);\n if (!match) return fallbackMs;\n const value = parseFloat(match[1]);\n return match[2] === 's' ? value * 1000 : value;\n}\n"],"names":["readDurationMs","token","fallbackMs","raw","match","value"],"mappings":"gFAMO,SAASA,EAAeC,EAAeC,EAA4B,CACxE,GAAI,OAAO,SAAa,IAAa,OAAOA,EAC5C,MAAMC,EAAM,iBAAiB,SAAS,eAAe,EAClD,iBAAiBF,CAAK,EACtB,KAAA,EACGG,EAAQ,sBAAsB,KAAKD,CAAG,EAC5C,GAAI,CAACC,EAAO,OAAOF,EACnB,MAAMG,EAAQ,WAAWD,EAAM,CAAC,CAAC,EACjC,OAAOA,EAAM,CAAC,IAAM,IAAMC,EAAQ,IAAOA,CAC3C"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads a duration token (e.g. `--duration-quick`) from the document root and
|
|
3
|
+
* returns milliseconds, so JS timers that wait on a CSS transition follow the
|
|
4
|
+
* token instead of a hard-coded copy. Falls back when the token is missing
|
|
5
|
+
* (SSR, tests, hosts that did not load the stylesheet).
|
|
6
|
+
*/
|
|
7
|
+
export declare function readDurationMs(token: string, fallbackMs: number): number;
|
|
8
|
+
//# sourceMappingURL=duration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duration.d.ts","sourceRoot":"","sources":["../../src/utils/duration.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CASxE"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
function u(r, t) {
|
|
2
|
+
if (typeof document > "u") return t;
|
|
3
|
+
const o = getComputedStyle(document.documentElement).getPropertyValue(r).trim(), e = /^(\d*\.?\d+)(ms|s)$/.exec(o);
|
|
4
|
+
if (!e) return t;
|
|
5
|
+
const n = parseFloat(e[1]);
|
|
6
|
+
return e[2] === "s" ? n * 1e3 : n;
|
|
7
|
+
}
|
|
8
|
+
export {
|
|
9
|
+
u as readDurationMs
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=duration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duration.js","sources":["../../src/utils/duration.ts"],"sourcesContent":["/**\n * Reads a duration token (e.g. `--duration-quick`) from the document root and\n * returns milliseconds, so JS timers that wait on a CSS transition follow the\n * token instead of a hard-coded copy. Falls back when the token is missing\n * (SSR, tests, hosts that did not load the stylesheet).\n */\nexport function readDurationMs(token: string, fallbackMs: number): number {\n if (typeof document === 'undefined') return fallbackMs;\n const raw = getComputedStyle(document.documentElement)\n .getPropertyValue(token)\n .trim();\n const match = /^(\\d*\\.?\\d+)(ms|s)$/.exec(raw);\n if (!match) return fallbackMs;\n const value = parseFloat(match[1]);\n return match[2] === 's' ? value * 1000 : value;\n}\n"],"names":["readDurationMs","token","fallbackMs","raw","match","value"],"mappings":"AAMO,SAASA,EAAeC,GAAeC,GAA4B;AACxE,MAAI,OAAO,WAAa,IAAa,QAAOA;AAC5C,QAAMC,IAAM,iBAAiB,SAAS,eAAe,EAClD,iBAAiBF,CAAK,EACtB,KAAA,GACGG,IAAQ,sBAAsB,KAAKD,CAAG;AAC5C,MAAI,CAACC,EAAO,QAAOF;AACnB,QAAMG,IAAQ,WAAWD,EAAM,CAAC,CAAC;AACjC,SAAOA,EAAM,CAAC,MAAM,MAAMC,IAAQ,MAAOA;AAC3C;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mattermost/compass-ui",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.14",
|
|
4
4
|
"description": "Compass design system UI components for Mattermost products",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -331,6 +331,9 @@
|
|
|
331
331
|
"hooks/use-exit-animation": [
|
|
332
332
|
"./dist/hooks/use-exit-animation.d.ts"
|
|
333
333
|
],
|
|
334
|
+
"hooks/use-modal-focus": [
|
|
335
|
+
"./dist/hooks/use-modal-focus.d.ts"
|
|
336
|
+
],
|
|
334
337
|
"hooks/use-outside-close": [
|
|
335
338
|
"./dist/hooks/use-outside-close.d.ts"
|
|
336
339
|
],
|