@ahrowe/ui 0.50.0 → 0.52.0

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.
Files changed (86) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +2 -1
  3. package/dist/cjs-types/common/actionIcon/actionIcon.types.d.ts +10 -6
  4. package/dist/cjs-types/common/ahroweProvider/ahroweProvider.d.ts +1 -1
  5. package/dist/cjs-types/common/button/button.types.d.ts +25 -8
  6. package/dist/cjs-types/common/chatMessageList/chatMessageList.types.d.ts +1 -1
  7. package/dist/cjs-types/common/configProvider/configProvider.d.ts +2 -1
  8. package/dist/cjs-types/common/configProvider/configProvider.types.d.ts +8 -0
  9. package/dist/cjs-types/common/copyButton/copyButton.types.d.ts +4 -1
  10. package/dist/cjs-types/common/dropdown/dropdown.types.d.ts +3 -2
  11. package/dist/cjs-types/common/infiniteBlock/useInfiniteScroll.d.ts +1 -1
  12. package/dist/cjs-types/common/listItem/index.d.ts +2 -0
  13. package/dist/cjs-types/common/listItem/listItem.d.ts +4 -0
  14. package/dist/cjs-types/common/listItem/listItem.types.d.ts +33 -0
  15. package/dist/cjs-types/common/menu/menu.types.d.ts +2 -2
  16. package/dist/cjs-types/common/menu/menuItem.d.ts +5 -2
  17. package/dist/cjs-types/common/themeProvider/theme.types.d.ts +4 -0
  18. package/dist/cjs-types/common/utils/navigation.d.ts +12 -0
  19. package/dist/cjs-types/index.d.ts +2 -0
  20. package/dist/esm/common/ahroweProvider/ahroweProvider.mjs +1 -1
  21. package/dist/esm/common/ahroweProvider/ahroweProvider.mjs.map +1 -1
  22. package/dist/esm/common/breadcrumb/breadcrumb.mjs +1 -1
  23. package/dist/esm/common/breadcrumb/breadcrumb.mjs.map +1 -1
  24. package/dist/esm/common/button/button.mjs +1 -1
  25. package/dist/esm/common/button/button.mjs.map +1 -1
  26. package/dist/esm/common/button/button.module.mjs +1 -1
  27. package/dist/esm/common/button/button.module.mjs.map +1 -1
  28. package/dist/esm/common/button/button.types.mjs.map +1 -1
  29. package/dist/esm/common/chatMessageList/chatMessageList.mjs +1 -1
  30. package/dist/esm/common/chatMessageList/chatMessageList.mjs.map +1 -1
  31. package/dist/esm/common/commandPalette/commandPalette.mjs +1 -1
  32. package/dist/esm/common/commandPalette/commandPalette.mjs.map +1 -1
  33. package/dist/esm/common/configProvider/configProvider.mjs +1 -1
  34. package/dist/esm/common/configProvider/configProvider.mjs.map +1 -1
  35. package/dist/esm/common/dropdown/dropdown.mjs +1 -1
  36. package/dist/esm/common/dropdown/dropdown.mjs.map +1 -1
  37. package/dist/esm/common/dropdown/dropdown.module.mjs +1 -1
  38. package/dist/esm/common/dropdown/dropdown.module.mjs.map +1 -1
  39. package/dist/esm/common/infiniteBlock/useInfiniteScroll.mjs +1 -1
  40. package/dist/esm/common/infiniteBlock/useInfiniteScroll.mjs.map +1 -1
  41. package/dist/esm/common/listItem/listItem.mjs +2 -0
  42. package/dist/esm/common/listItem/listItem.mjs.map +1 -0
  43. package/dist/esm/common/listItem/listItem.module.mjs +2 -0
  44. package/dist/esm/common/listItem/listItem.module.mjs.map +1 -0
  45. package/dist/esm/common/loader/loader.module.mjs.map +1 -1
  46. package/dist/esm/common/menu/menu.mjs +1 -1
  47. package/dist/esm/common/menu/menu.mjs.map +1 -1
  48. package/dist/esm/common/menu/menu.types.mjs.map +1 -1
  49. package/dist/esm/common/menu/menuItem.mjs +1 -1
  50. package/dist/esm/common/menu/menuItem.mjs.map +1 -1
  51. package/dist/esm/common/utils/navigation.mjs +2 -0
  52. package/dist/esm/common/utils/navigation.mjs.map +1 -0
  53. package/dist/esm/index.mjs +1 -1
  54. package/dist/index.cjs +5 -5
  55. package/dist/index.cjs.map +1 -1
  56. package/dist/style.css +1 -1
  57. package/dist/types/common/actionIcon/actionIcon.types.d.ts +10 -6
  58. package/dist/types/common/ahroweProvider/ahroweProvider.d.ts +1 -1
  59. package/dist/types/common/button/button.types.d.ts +25 -8
  60. package/dist/types/common/chatMessageList/chatMessageList.types.d.ts +1 -1
  61. package/dist/types/common/configProvider/configProvider.d.ts +2 -1
  62. package/dist/types/common/configProvider/configProvider.types.d.ts +8 -0
  63. package/dist/types/common/copyButton/copyButton.types.d.ts +4 -1
  64. package/dist/types/common/dropdown/dropdown.types.d.ts +3 -2
  65. package/dist/types/common/infiniteBlock/useInfiniteScroll.d.ts +1 -1
  66. package/dist/types/common/listItem/index.d.ts +2 -0
  67. package/dist/types/common/listItem/listItem.d.ts +4 -0
  68. package/dist/types/common/listItem/listItem.types.d.ts +33 -0
  69. package/dist/types/common/menu/menu.types.d.ts +2 -2
  70. package/dist/types/common/menu/menuItem.d.ts +5 -2
  71. package/dist/types/common/themeProvider/theme.types.d.ts +4 -0
  72. package/dist/types/common/utils/navigation.d.ts +12 -0
  73. package/dist/types/index.d.ts +2 -0
  74. package/docs/ActionIcon.md +7 -3
  75. package/docs/AhroweProvider.md +2 -1
  76. package/docs/Breadcrumb.md +4 -24
  77. package/docs/Button.md +44 -9
  78. package/docs/CLAUDE.md +4 -0
  79. package/docs/ChatMessageList.md +5 -5
  80. package/docs/CommandPalette.md +1 -1
  81. package/docs/ConfigProvider.md +34 -1
  82. package/docs/Dropdown.md +17 -3
  83. package/docs/ListItem.md +97 -0
  84. package/docs/Menu.md +2 -2
  85. package/docs/ScrollbarProvider.md +1 -1
  86. package/package.json +1 -1
@@ -1,2 +1,2 @@
1
- "use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import{ThemeColor as n}from"../themeProvider/color.types.mjs";import r from"../loader/loader.mjs";import{ButtonStyleType as i}from"../button/button.types.mjs";import a from"../actionIcon/actionIcon.mjs";import{useInfiniteScroll as o}from"../infiniteBlock/useInfiniteScroll.mjs";import s from"./chatMessageList.module.mjs";import{useCallback as c,useImperativeHandle as l,useLayoutEffect as u,useRef as d,useState as f}from"react";import p from"classnames";import{faArrowDown as m}from"@fortawesome/free-solid-svg-icons";import{jsx as h,jsxs as g}from"react/jsx-runtime";var _={messages:`Messages`,scrollToBottom:`Jump to latest`},v=32,y=()=>Promise.resolve();function b(e,t,n){let r=e.getBoundingClientRect().top,i=t.children,a=+(i[0]===n),o=i.length-1,s=null;for(;a<=o;){let e=a+o>>1;i[e].getBoundingClientRect().bottom>r?(s=i[e],o=e-1):a=e+1}return s&&{element:s,top:s.getBoundingClientRect().top-r}}function x(){return typeof window.matchMedia==`function`&&window.matchMedia(`(prefers-reduced-motion: reduce)`).matches}function S(S){let{children:C,onLoadMore:w,ref:T,labels:E,className:D,style:O,classNames:k,styles:A,...j}=e(`ChatMessageList`,S),M=t(`ChatMessageList`,E,_),N=d(null),P=d(null),F=d(!0),I=d(0),L=d(null),R=d(null),[z,B]=f(!1),[V,H]=f(!1),{isLoading:U,sentinelRef:W}=o(N,w??y,!w),G=()=>{let e=N.current,t=P.current;if(!e||!t)return;let{scrollTop:n,scrollHeight:r,clientHeight:i}=e,a=r-n-i;n<I.current&&a>1?F.current=!1:a<=v&&(F.current=!0),I.current=n,B(!F.current),F.current&&H(!1),L.current=b(e,t,W.current)};u(()=>{let e=N.current,t=P.current;if(!e||!t)return;let n=()=>{let n=t.lastElementChild;if(!F.current&&n!==R.current&&H(!0),R.current=n,F.current)e.scrollTop=e.scrollHeight;else{let t=L.current;t?.element.isConnected&&(e.scrollTop+=t.element.getBoundingClientRect().top-e.getBoundingClientRect().top-t.top)}I.current=e.scrollTop,L.current=b(e,t,W.current)};n();let r=new ResizeObserver(n);return r.observe(e),r.observe(t),()=>r.disconnect()},[W]);let K=c(()=>{let e=N.current;e&&(F.current=!0,B(!1),H(!1),e.scrollTo({top:e.scrollHeight,behavior:x()?`auto`:`smooth`}))},[]);return l(T,()=>({scrollToBottom:K}),[K]),g(`div`,{...j,className:p(s.chatMessageList,D),style:O,children:[h(`div`,{ref:N,role:`log`,"aria-label":M.messages,"aria-busy":U||void 0,tabIndex:0,className:p(s.chatMessageListViewport,k?.viewport),style:A?.viewport,onScroll:G,children:g(`div`,{ref:P,className:p(s.chatMessageListContent,k?.content),style:A?.content,children:[w&&h(`div`,{ref:W,className:s.chatMessageListLoader,children:U&&h(r,{size:`1.5em`})}),C]})}),z&&h(a,{icon:m,styleType:V?i.Filled:i.Plain,color:V?n.Primary:n.Neutral,"aria-label":M.scrollToBottom,title:M.scrollToBottom,className:p(s.chatMessageListScrollButton,!V&&s.chatMessageListScrollButtonSurface,k?.scrollButton),style:A?.scrollButton,onClick:K})]})}export{S as default};
1
+ "use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import{ThemeColor as n}from"../themeProvider/color.types.mjs";import r from"../loader/loader.mjs";import{ButtonStyleType as i}from"../button/button.types.mjs";import a from"../actionIcon/actionIcon.mjs";import{useInfiniteScroll as o}from"../infiniteBlock/useInfiniteScroll.mjs";import s from"./chatMessageList.module.mjs";import{useCallback as c,useImperativeHandle as l,useLayoutEffect as u,useRef as d,useState as f}from"react";import p from"classnames";import{faArrowDown as m}from"@fortawesome/free-solid-svg-icons";import{jsx as h,jsxs as g}from"react/jsx-runtime";var _={messages:`Messages`,scrollToBottom:`Jump to latest`},v=32;function y(e,t,n){let r=e.getBoundingClientRect().top,i=t.children,a=+(i[0]===n),o=i.length-1,s=null;for(;a<=o;){let e=a+o>>1;i[e].getBoundingClientRect().bottom>r?(s=i[e],o=e-1):a=e+1}return s&&{element:s,top:s.getBoundingClientRect().top-r}}function b(){return typeof window.matchMedia==`function`&&window.matchMedia(`(prefers-reduced-motion: reduce)`).matches}function x(x){let{children:S,onLoadMore:C,ref:w,labels:T,className:E,style:D,classNames:O,styles:k,...A}=e(`ChatMessageList`,x),j=t(`ChatMessageList`,T,_),M=d(null),N=d(null),P=d(!0),F=d(0),I=d(null),L=d(null),[R,z]=f(!1),[B,V]=f(!1),{isLoading:H,sentinelRef:U}=o(M,C),W=()=>{let e=M.current,t=N.current;if(!e||!t)return;let{scrollTop:n,scrollHeight:r,clientHeight:i}=e,a=r-n-i;n<F.current&&a>1?P.current=!1:a<=v&&(P.current=!0),F.current=n,z(!P.current),P.current&&V(!1),I.current=y(e,t,U.current)};u(()=>{let e=M.current,t=N.current;if(!e||!t)return;let n=()=>{let n=t.lastElementChild;if(!P.current&&n!==L.current&&V(!0),L.current=n,P.current)e.scrollTop=e.scrollHeight;else{let t=I.current;t?.element.isConnected&&(e.scrollTop+=t.element.getBoundingClientRect().top-e.getBoundingClientRect().top-t.top)}F.current=e.scrollTop,I.current=y(e,t,U.current)};n();let r=new ResizeObserver(n);return r.observe(e),r.observe(t),()=>r.disconnect()},[U]);let G=c(()=>{let e=M.current;e&&(P.current=!0,z(!1),V(!1),e.scrollTo({top:e.scrollHeight,behavior:b()?`auto`:`smooth`}))},[]);return l(w,()=>({scrollToBottom:G}),[G]),g(`div`,{...A,className:p(s.chatMessageList,E),style:D,children:[h(`div`,{ref:M,role:`log`,"aria-label":j.messages,"aria-busy":H||void 0,tabIndex:0,className:p(s.chatMessageListViewport,O?.viewport),style:k?.viewport,onScroll:W,children:g(`div`,{ref:N,className:p(s.chatMessageListContent,O?.content),style:k?.content,children:[C&&h(`div`,{ref:U,className:s.chatMessageListLoader,children:H&&h(r,{size:`1.5em`})}),S]})}),R&&h(a,{icon:m,styleType:B?i.Filled:i.Plain,color:B?n.Primary:n.Neutral,"aria-label":j.scrollToBottom,title:j.scrollToBottom,className:p(s.chatMessageListScrollButton,!B&&s.chatMessageListScrollButtonSurface,O?.scrollButton),style:k?.scrollButton,onClick:G})]})}export{x as default};
2
2
  //# sourceMappingURL=chatMessageList.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"chatMessageList.mjs","names":[],"sources":["../../../../package/common/chatMessageList/chatMessageList.tsx"],"sourcesContent":["import { useCallback, useImperativeHandle, useLayoutEffect, useRef, useState, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faArrowDown } from '@fortawesome/free-solid-svg-icons';\n\nimport ActionIcon from '../actionIcon';\nimport { ButtonStyleType } from '../button/button.types';\nimport Loader from '../loader';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { useInfiniteScroll } from '../infiniteBlock/useInfiniteScroll';\nimport { ThemeColor } from '../themeProvider/color.types';\nimport type { ChatMessageListLabels, ChatMessageListProps } from './chatMessageList.types';\nimport styles from './chatMessageList.module.pcss';\n\nconst DEFAULT_LABELS: Required<ChatMessageListLabels> = {\n messages: 'Messages',\n scrollToBottom: 'Jump to latest',\n};\n\n/** How close to the bottom a user scrolling down gets before new messages are followed again. */\nconst STICK_DISTANCE = 32;\n\nconst NOTHING_TO_LOAD = () => Promise.resolve();\n\ninterface Anchor {\n element: Element;\n /** Distance from the viewport's top edge. */\n top: number;\n}\n\n/**\n * The first message still showing at the top of the viewport, found by bisection because a long\n * conversation has thousands of children and this runs on every scroll event.\n */\nfunction findAnchor(viewport: HTMLElement, content: HTMLElement, skip: Element | null): Anchor | null {\n const viewportTop = viewport.getBoundingClientRect().top;\n const items = content.children;\n let low = items[0] === skip ? 1 : 0;\n let high = items.length - 1;\n let found: Element | null = null;\n while (low <= high) {\n const middle = (low + high) >> 1;\n if (items[middle].getBoundingClientRect().bottom > viewportTop) {\n found = items[middle];\n high = middle - 1;\n } else {\n low = middle + 1;\n }\n }\n return found && { element: found, top: found.getBoundingClientRect().top - viewportTop };\n}\n\nfunction prefersReducedMotion(): boolean {\n return typeof window.matchMedia === 'function' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n}\n\nfunction ChatMessageList(props: ChatMessageListProps): ReactElement {\n const {\n children,\n onLoadMore,\n ref,\n labels,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n } = useComponentDefaults('ChatMessageList', props);\n const text = useLabels('ChatMessageList', labels, DEFAULT_LABELS);\n const viewportRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const isStuckRef = useRef(true);\n const lastScrollTopRef = useRef(0);\n const anchorRef = useRef<Anchor | null>(null);\n const lastChildRef = useRef<Element | null>(null);\n const [isAway, setIsAway] = useState(false);\n const [hasNew, setHasNew] = useState(false);\n const { isLoading, sentinelRef } = useInfiniteScroll(viewportRef, onLoadMore ?? NOTHING_TO_LOAD, !onLoadMore);\n\n const handleScroll = () => {\n const viewport = viewportRef.current;\n const content = contentRef.current;\n if (!viewport || !content) return;\n const { scrollTop, scrollHeight, clientHeight } = viewport;\n const distance = scrollHeight - scrollTop - clientHeight;\n // Any move up lets go, however small: a slow drag would otherwise be pulled back down by each\n // line of a streaming answer. The browser clamping scrollTop as content shrinks also moves it\n // up, but lands on the bottom, which is why the distance has to be checked too.\n if (scrollTop < lastScrollTopRef.current && distance > 1) isStuckRef.current = false;\n else if (distance <= STICK_DISTANCE) isStuckRef.current = true;\n lastScrollTopRef.current = scrollTop;\n setIsAway(!isStuckRef.current);\n if (isStuckRef.current) setHasNew(false);\n anchorRef.current = findAnchor(viewport, content, sentinelRef.current);\n };\n\n // Scroll anchoring by hand: Safari has no `overflow-anchor`, and where it exists it does nothing\n // at scrollTop 0, which is exactly where older messages get prepended. Runs in a ResizeObserver\n // callback, after layout and before paint, so neither the jump nor its correction is ever seen.\n useLayoutEffect(() => {\n const viewport = viewportRef.current;\n const content = contentRef.current;\n if (!viewport || !content) return undefined;\n\n const follow = () => {\n // A new last child is a message arriving; one prepended at the top or growing as it streams\n // leaves it the same.\n const last = content.lastElementChild;\n if (!isStuckRef.current && last !== lastChildRef.current) setHasNew(true);\n lastChildRef.current = last;\n if (isStuckRef.current) {\n viewport.scrollTop = viewport.scrollHeight;\n } else {\n const anchor = anchorRef.current;\n if (anchor?.element.isConnected) {\n viewport.scrollTop +=\n anchor.element.getBoundingClientRect().top - viewport.getBoundingClientRect().top - anchor.top;\n }\n }\n // So the scroll event this causes is not taken for the user scrolling up.\n lastScrollTopRef.current = viewport.scrollTop;\n anchorRef.current = findAnchor(viewport, content, sentinelRef.current);\n };\n\n follow();\n // The viewport too: a composer growing or a phone keyboard opening shrinks it from below.\n const observer = new ResizeObserver(follow);\n observer.observe(viewport);\n observer.observe(content);\n return () => observer.disconnect();\n }, [sentinelRef]);\n\n const scrollToBottom = useCallback(() => {\n const viewport = viewportRef.current;\n if (!viewport) return;\n isStuckRef.current = true;\n setIsAway(false);\n setHasNew(false);\n viewport.scrollTo({ top: viewport.scrollHeight, behavior: prefersReducedMotion() ? 'auto' : 'smooth' });\n }, []);\n\n useImperativeHandle(ref, () => ({ scrollToBottom }), [scrollToBottom]);\n\n return (\n <div {...rest} className={cx(styles.chatMessageList, className)} style={style}>\n <div\n ref={viewportRef}\n role='log'\n aria-label={text.messages}\n aria-busy={isLoading || undefined}\n // A stop of its own, so the history can be scrolled from the keyboard between messages\n // that hold nothing focusable.\n tabIndex={0}\n className={cx(styles.chatMessageListViewport, classNames?.viewport)}\n style={slotStyles?.viewport}\n onScroll={handleScroll}\n >\n <div\n ref={contentRef}\n className={cx(styles.chatMessageListContent, classNames?.content)}\n style={slotStyles?.content}\n >\n {onLoadMore && (\n <div ref={sentinelRef} className={styles.chatMessageListLoader}>\n {isLoading && <Loader size='1.5em' />}\n </div>\n )}\n {children}\n </div>\n </div>\n {isAway && (\n <ActionIcon\n icon={faArrowDown}\n styleType={hasNew ? ButtonStyleType.Filled : ButtonStyleType.Plain}\n color={hasNew ? ThemeColor.Primary : ThemeColor.Neutral}\n aria-label={text.scrollToBottom}\n title={text.scrollToBottom}\n className={cx(\n styles.chatMessageListScrollButton,\n !hasNew && styles.chatMessageListScrollButtonSurface,\n classNames?.scrollButton,\n )}\n style={slotStyles?.scrollButton}\n onClick={scrollToBottom}\n />\n )}\n </div>\n );\n}\n\nexport default ChatMessageList;\n"],"mappings":"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"}
1
+ {"version":3,"file":"chatMessageList.mjs","names":[],"sources":["../../../../package/common/chatMessageList/chatMessageList.tsx"],"sourcesContent":["import { useCallback, useImperativeHandle, useLayoutEffect, useRef, useState, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faArrowDown } from '@fortawesome/free-solid-svg-icons';\n\nimport ActionIcon from '../actionIcon';\nimport { ButtonStyleType } from '../button/button.types';\nimport Loader from '../loader';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { useInfiniteScroll } from '../infiniteBlock/useInfiniteScroll';\nimport { ThemeColor } from '../themeProvider/color.types';\nimport type { ChatMessageListLabels, ChatMessageListProps } from './chatMessageList.types';\nimport styles from './chatMessageList.module.pcss';\n\nconst DEFAULT_LABELS: Required<ChatMessageListLabels> = {\n messages: 'Messages',\n scrollToBottom: 'Jump to latest',\n};\n\n/** How close to the bottom a user scrolling down gets before new messages are followed again. */\nconst STICK_DISTANCE = 32;\n\ninterface Anchor {\n element: Element;\n /** Distance from the viewport's top edge. */\n top: number;\n}\n\n/**\n * The first message still showing at the top of the viewport, found by bisection because a long\n * conversation has thousands of children and this runs on every scroll event.\n */\nfunction findAnchor(viewport: HTMLElement, content: HTMLElement, skip: Element | null): Anchor | null {\n const viewportTop = viewport.getBoundingClientRect().top;\n const items = content.children;\n let low = items[0] === skip ? 1 : 0;\n let high = items.length - 1;\n let found: Element | null = null;\n while (low <= high) {\n const middle = (low + high) >> 1;\n if (items[middle].getBoundingClientRect().bottom > viewportTop) {\n found = items[middle];\n high = middle - 1;\n } else {\n low = middle + 1;\n }\n }\n return found && { element: found, top: found.getBoundingClientRect().top - viewportTop };\n}\n\nfunction prefersReducedMotion(): boolean {\n return typeof window.matchMedia === 'function' && window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n}\n\nfunction ChatMessageList(props: ChatMessageListProps): ReactElement {\n const {\n children,\n onLoadMore,\n ref,\n labels,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n } = useComponentDefaults('ChatMessageList', props);\n const text = useLabels('ChatMessageList', labels, DEFAULT_LABELS);\n const viewportRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const isStuckRef = useRef(true);\n const lastScrollTopRef = useRef(0);\n const anchorRef = useRef<Anchor | null>(null);\n const lastChildRef = useRef<Element | null>(null);\n const [isAway, setIsAway] = useState(false);\n const [hasNew, setHasNew] = useState(false);\n const { isLoading, sentinelRef } = useInfiniteScroll(viewportRef, onLoadMore);\n\n const handleScroll = () => {\n const viewport = viewportRef.current;\n const content = contentRef.current;\n if (!viewport || !content) return;\n const { scrollTop, scrollHeight, clientHeight } = viewport;\n const distance = scrollHeight - scrollTop - clientHeight;\n // Any move up lets go, however small: a slow drag would otherwise be pulled back down by each\n // line of a streaming answer. The browser clamping scrollTop as content shrinks also moves it\n // up, but lands on the bottom, which is why the distance has to be checked too.\n if (scrollTop < lastScrollTopRef.current && distance > 1) isStuckRef.current = false;\n else if (distance <= STICK_DISTANCE) isStuckRef.current = true;\n lastScrollTopRef.current = scrollTop;\n setIsAway(!isStuckRef.current);\n if (isStuckRef.current) setHasNew(false);\n anchorRef.current = findAnchor(viewport, content, sentinelRef.current);\n };\n\n // Scroll anchoring by hand: Safari has no `overflow-anchor`, and where it exists it does nothing\n // at scrollTop 0, which is exactly where older messages get prepended. Runs in a ResizeObserver\n // callback, after layout and before paint, so neither the jump nor its correction is ever seen.\n useLayoutEffect(() => {\n const viewport = viewportRef.current;\n const content = contentRef.current;\n if (!viewport || !content) return undefined;\n\n const follow = () => {\n // A new last child is a message arriving; one prepended at the top or growing as it streams\n // leaves it the same.\n const last = content.lastElementChild;\n if (!isStuckRef.current && last !== lastChildRef.current) setHasNew(true);\n lastChildRef.current = last;\n if (isStuckRef.current) {\n viewport.scrollTop = viewport.scrollHeight;\n } else {\n const anchor = anchorRef.current;\n if (anchor?.element.isConnected) {\n viewport.scrollTop +=\n anchor.element.getBoundingClientRect().top - viewport.getBoundingClientRect().top - anchor.top;\n }\n }\n // So the scroll event this causes is not taken for the user scrolling up.\n lastScrollTopRef.current = viewport.scrollTop;\n anchorRef.current = findAnchor(viewport, content, sentinelRef.current);\n };\n\n follow();\n // The viewport too: a composer growing or a phone keyboard opening shrinks it from below.\n const observer = new ResizeObserver(follow);\n observer.observe(viewport);\n observer.observe(content);\n return () => observer.disconnect();\n }, [sentinelRef]);\n\n const scrollToBottom = useCallback(() => {\n const viewport = viewportRef.current;\n if (!viewport) return;\n isStuckRef.current = true;\n setIsAway(false);\n setHasNew(false);\n viewport.scrollTo({ top: viewport.scrollHeight, behavior: prefersReducedMotion() ? 'auto' : 'smooth' });\n }, []);\n\n useImperativeHandle(ref, () => ({ scrollToBottom }), [scrollToBottom]);\n\n return (\n <div {...rest} className={cx(styles.chatMessageList, className)} style={style}>\n <div\n ref={viewportRef}\n role='log'\n aria-label={text.messages}\n aria-busy={isLoading || undefined}\n // A stop of its own, so the history can be scrolled from the keyboard between messages\n // that hold nothing focusable.\n tabIndex={0}\n className={cx(styles.chatMessageListViewport, classNames?.viewport)}\n style={slotStyles?.viewport}\n onScroll={handleScroll}\n >\n <div\n ref={contentRef}\n className={cx(styles.chatMessageListContent, classNames?.content)}\n style={slotStyles?.content}\n >\n {onLoadMore && (\n <div ref={sentinelRef} className={styles.chatMessageListLoader}>\n {isLoading && <Loader size='1.5em' />}\n </div>\n )}\n {children}\n </div>\n </div>\n {isAway && (\n <ActionIcon\n icon={faArrowDown}\n styleType={hasNew ? ButtonStyleType.Filled : ButtonStyleType.Plain}\n color={hasNew ? ThemeColor.Primary : ThemeColor.Neutral}\n aria-label={text.scrollToBottom}\n title={text.scrollToBottom}\n className={cx(\n styles.chatMessageListScrollButton,\n !hasNew && styles.chatMessageListScrollButtonSurface,\n classNames?.scrollButton,\n )}\n style={slotStyles?.scrollButton}\n onClick={scrollToBottom}\n />\n )}\n </div>\n );\n}\n\nexport default ChatMessageList;\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import{renderIcon as n}from"../utils/renderIcon.mjs";import{ariaKeyshortcuts as r,formatHotkey as i}from"../hooks/useHotkeys.mjs";import a from"../modal/modal.mjs";import{edgeIndex as o,nextIndex as ee,scrollOptionIntoView as s}from"../utils/listNavigation.mjs";import{hasAnyIcon as c,renderItemIcon as te,runMenuAction as l}from"../menu/menuItem.mjs";import{filterCommands as ne,groupCommands as u}from"./filterCommands.mjs";import d from"./commandPalette.module.mjs";import{useEffect as f,useId as p,useRef as m,useState as h}from"react";import g from"classnames";import{faMagnifyingGlass as re}from"@fortawesome/free-solid-svg-icons";import{jsx as _,jsxs as v}from"react/jsx-runtime";var y={label:`Search commands`,placeholder:`Type a command or search…`,empty:`No results`,loading:`Searching…`,results:e=>e===1?`1 result`:`${e} results`};function b(b){let{items:x,isOpen:S,onClose:C,onAction:w,onQueryChange:T,filter:E=!0,isLoading:D=!1,presentation:O,labels:ie,className:ae,style:oe,classNames:k,styles:A,...j}=e(`CommandPalette`,b),M=t(`CommandPalette`,ie,y),N=p(),P=m(null),[F,I]=h(``),[L,R]=h(null),[z,B]=h(S);S!==z&&(B(S),S&&(I(``),R(null)));let V=m(T);f(()=>{V.current=T}),f(()=>{S&&V.current?.(``)},[S]);let H=E?ne(u(x),F):u(x),U=c(x),W=H.flatMap(e=>e.actions),G=e=>!!W[e]?.disabled,K=W.findIndex(e=>e.id===L&&!e.disabled),q=K>=0?K:o(`first`,W.length,{isDisabled:G}),J=e=>`${N}-${encodeURIComponent(e.id)}`,Y=q===null?void 0:J(W[q]);f(()=>{Y&&s(P.current,document.getElementById(Y))},[Y]);function X(e){e&&!e.disabled&&(l(e,w),C())}function se(e){if(!e.nativeEvent.isComposing){if(e.key===`ArrowDown`||e.key===`ArrowUp`){let t=ee(q,e.key===`ArrowDown`?1:-1,W.length,{isDisabled:G});R(t===null?null:W[t].id)}else if(e.key===`Enter`)X(q===null?void 0:W[q]);else return;e.preventDefault()}}let Z=F.trim()!==``,Q=D?M.loading:M.empty,$=-1;return v(a,{...j,"aria-label":M.label,isOpen:S,onClose:C,hideHeader:!0,presentation:O,className:g(d.commandPalette,ae,k?.root),style:{...oe,...A?.root},children:[v(`div`,{className:g(d.commandPaletteSearch,k?.search),style:A?.search,children:[n(re,{className:d.commandPaletteSearchIcon}),_(`input`,{type:`text`,role:`combobox`,"aria-label":M.label,"aria-expanded":`true`,"aria-controls":N,"aria-autocomplete":`list`,"aria-activedescendant":Y,autoComplete:`off`,spellCheck:!1,placeholder:M.placeholder,value:F,onChange:e=>{I(e.target.value),R(null),T?.(e.target.value)},onKeyDown:se,className:g(d.commandPaletteInput,k?.input),style:A?.input})]}),_(`div`,{ref:P,id:N,role:`listbox`,"aria-label":M.label,className:g(d.commandPaletteList,k?.list),style:A?.list,children:H.map((e,t)=>{let n=`${N}-heading-${t}`;return v(`div`,{role:e.heading===void 0?`presentation`:`group`,"aria-labelledby":e.heading===void 0?void 0:n,children:[e.separated&&t>0&&_(`div`,{"aria-hidden":!0,className:g(d.commandPaletteSeparator,k?.separator),style:A?.separator}),e.heading!==void 0&&_(`div`,{id:n,role:`presentation`,className:g(d.commandPaletteHeading,k?.heading),style:A?.heading,children:e.heading}),e.actions.map(e=>{$+=1;let t=$;return v(`div`,{id:J(e),role:`option`,"aria-selected":t===q,"aria-disabled":e.disabled||void 0,"aria-keyshortcuts":r(e.shortcut),className:g(d.commandPaletteItem,t===q&&d.commandPaletteItemActive,e.danger&&d.commandPaletteItemDanger,e.disabled&&d.commandPaletteItemDisabled,k?.item),style:A?.item,onMouseDown:e=>e.preventDefault(),onMouseMove:()=>!e.disabled&&R(e.id),onClick:()=>X(e),children:[te(e.icon,U,{className:g(d.commandPaletteItemIcon,k?.itemIcon),style:A?.itemIcon}),_(`span`,{className:g(d.commandPaletteItemLabel,k?.itemLabel),style:A?.itemLabel,children:e.label}),e.shortcut!=null&&_(`span`,{"aria-hidden":!0,className:g(d.commandPaletteItemShortcut,k?.itemShortcut),style:A?.itemShortcut,children:i(e.shortcut)})]},e.id)})]},t)})}),Z&&W.length===0&&_(`div`,{className:g(d.commandPaletteEmpty,k?.empty),style:A?.empty,children:Q}),_(`span`,{className:d.commandPaletteStatus,role:`status`,"aria-live":`polite`,children:Z?W.length>0?M.results(W.length):Q:``})]})}export{b as default};
1
+ "use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import{renderIcon as n}from"../utils/renderIcon.mjs";import{ariaKeyshortcuts as ee,formatHotkey as te}from"../hooks/useHotkeys.mjs";import{useFollowHref as ne}from"../utils/navigation.mjs";import r from"../modal/modal.mjs";import{edgeIndex as i,nextIndex as a,scrollOptionIntoView as o}from"../utils/listNavigation.mjs";import{hasAnyIcon as s,renderItemIcon as c,runMenuAction as l}from"../menu/menuItem.mjs";import{filterCommands as u,groupCommands as d}from"./filterCommands.mjs";import f from"./commandPalette.module.mjs";import{useEffect as p,useId as m,useRef as h,useState as g}from"react";import _ from"classnames";import{faMagnifyingGlass as v}from"@fortawesome/free-solid-svg-icons";import{jsx as y,jsxs as b}from"react/jsx-runtime";var x={label:`Search commands`,placeholder:`Type a command or search…`,empty:`No results`,loading:`Searching…`,results:e=>e===1?`1 result`:`${e} results`};function S(S){let{items:C,isOpen:w,onClose:T,onAction:re,onQueryChange:E,filter:ie=!0,isLoading:ae=!1,presentation:oe,labels:se,className:ce,style:D,classNames:O,styles:k,...A}=e(`CommandPalette`,S),j=t(`CommandPalette`,se,x),M=m(),N=h(null),P=ne(),[F,I]=g(``),[L,R]=g(null),[z,B]=g(w);w!==z&&(B(w),w&&(I(``),R(null)));let V=h(E);p(()=>{V.current=E}),p(()=>{w&&V.current?.(``)},[w]);let H=ie?u(d(C),F):d(C),U=s(C),W=H.flatMap(e=>e.actions),G=e=>!!W[e]?.disabled,K=W.findIndex(e=>e.id===L&&!e.disabled),q=K>=0?K:i(`first`,W.length,{isDisabled:G}),J=e=>`${M}-${encodeURIComponent(e.id)}`,Y=q===null?void 0:J(W[q]);p(()=>{Y&&o(N.current,document.getElementById(Y))},[Y]);function X(e){e&&!e.disabled&&(l(e,re,P),T())}function le(e){if(!e.nativeEvent.isComposing){if(e.key===`ArrowDown`||e.key===`ArrowUp`){let t=a(q,e.key===`ArrowDown`?1:-1,W.length,{isDisabled:G});R(t===null?null:W[t].id)}else if(e.key===`Enter`)X(q===null?void 0:W[q]);else return;e.preventDefault()}}let Z=F.trim()!==``,Q=ae?j.loading:j.empty,$=-1;return b(r,{...A,"aria-label":j.label,isOpen:w,onClose:T,hideHeader:!0,presentation:oe,className:_(f.commandPalette,ce,O?.root),style:{...D,...k?.root},children:[b(`div`,{className:_(f.commandPaletteSearch,O?.search),style:k?.search,children:[n(v,{className:f.commandPaletteSearchIcon}),y(`input`,{type:`text`,role:`combobox`,"aria-label":j.label,"aria-expanded":`true`,"aria-controls":M,"aria-autocomplete":`list`,"aria-activedescendant":Y,autoComplete:`off`,spellCheck:!1,placeholder:j.placeholder,value:F,onChange:e=>{I(e.target.value),R(null),E?.(e.target.value)},onKeyDown:le,className:_(f.commandPaletteInput,O?.input),style:k?.input})]}),y(`div`,{ref:N,id:M,role:`listbox`,"aria-label":j.label,className:_(f.commandPaletteList,O?.list),style:k?.list,children:H.map((e,t)=>{let n=`${M}-heading-${t}`;return b(`div`,{role:e.heading===void 0?`presentation`:`group`,"aria-labelledby":e.heading===void 0?void 0:n,children:[e.separated&&t>0&&y(`div`,{"aria-hidden":!0,className:_(f.commandPaletteSeparator,O?.separator),style:k?.separator}),e.heading!==void 0&&y(`div`,{id:n,role:`presentation`,className:_(f.commandPaletteHeading,O?.heading),style:k?.heading,children:e.heading}),e.actions.map(e=>{$+=1;let t=$;return b(`div`,{id:J(e),role:`option`,"aria-selected":t===q,"aria-disabled":e.disabled||void 0,"aria-keyshortcuts":ee(e.shortcut),className:_(f.commandPaletteItem,t===q&&f.commandPaletteItemActive,e.danger&&f.commandPaletteItemDanger,e.disabled&&f.commandPaletteItemDisabled,O?.item),style:k?.item,onMouseDown:e=>e.preventDefault(),onMouseMove:()=>!e.disabled&&R(e.id),onClick:()=>X(e),children:[c(e.icon,U,{className:_(f.commandPaletteItemIcon,O?.itemIcon),style:k?.itemIcon}),y(`span`,{className:_(f.commandPaletteItemLabel,O?.itemLabel),style:k?.itemLabel,children:e.label}),e.shortcut!=null&&y(`span`,{"aria-hidden":!0,className:_(f.commandPaletteItemShortcut,O?.itemShortcut),style:k?.itemShortcut,children:te(e.shortcut)})]},e.id)})]},t)})}),Z&&W.length===0&&y(`div`,{className:_(f.commandPaletteEmpty,O?.empty),style:k?.empty,children:Q}),y(`span`,{className:f.commandPaletteStatus,role:`status`,"aria-live":`polite`,children:Z?W.length>0?j.results(W.length):Q:``})]})}export{S as default};
2
2
  //# sourceMappingURL=commandPalette.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"commandPalette.mjs","names":[],"sources":["../../../../package/common/commandPalette/commandPalette.tsx"],"sourcesContent":["import { useEffect, useId, useRef, useState, type KeyboardEvent, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faMagnifyingGlass } from '@fortawesome/free-solid-svg-icons';\n\nimport Modal from '../modal';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { ariaKeyshortcuts, formatHotkey } from '../hooks/useHotkeys';\nimport { edgeIndex, nextIndex, scrollOptionIntoView } from '../utils/listNavigation';\nimport { renderIcon } from '../utils/renderIcon';\nimport type { MenuAction } from '../menu/menu.types';\nimport { hasAnyIcon, renderItemIcon, runMenuAction } from '../menu/menuItem';\nimport { filterCommands, groupCommands } from './filterCommands';\nimport type { CommandPaletteLabels, CommandPaletteProps } from './commandPalette.types';\nimport styles from './commandPalette.module.pcss';\n\nconst DEFAULT_LABELS: Required<CommandPaletteLabels> = {\n label: 'Search commands',\n placeholder: 'Type a command or search…',\n empty: 'No results',\n loading: 'Searching…',\n results: (count) => (count === 1 ? '1 result' : `${count} results`),\n};\n\nfunction CommandPalette(props: CommandPaletteProps): ReactElement {\n const {\n items,\n isOpen,\n onClose,\n onAction,\n onQueryChange,\n filter = true,\n isLoading = false,\n presentation,\n labels,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n } = useComponentDefaults('CommandPalette', props);\n const text = useLabels('CommandPalette', labels, DEFAULT_LABELS);\n const listId = useId();\n const listRef = useRef<HTMLDivElement>(null);\n\n const [query, setQuery] = useState('');\n // An id rather than an index, so the highlight stays on the same command while server results\n // are replaced around it. Typing clears it instead, so Enter runs the best match.\n const [activeId, setActiveId] = useState<string | null>(null);\n const [wasOpen, setWasOpen] = useState(isOpen);\n if (isOpen !== wasOpen) {\n setWasOpen(isOpen);\n if (isOpen) {\n setQuery('');\n setActiveId(null);\n }\n }\n\n // Read through a ref so an inline handler does not re-fire the effect on every parent render.\n const onQueryChangeRef = useRef(onQueryChange);\n useEffect(() => {\n onQueryChangeRef.current = onQueryChange;\n });\n useEffect(() => {\n if (isOpen) onQueryChangeRef.current?.('');\n }, [isOpen]);\n\n const groups = filter ? filterCommands(groupCommands(items), query) : groupCommands(items);\n // From all items, not the matches, so the labels do not jump sideways while the user types.\n const hasIcons = hasAnyIcon(items);\n const actions = groups.flatMap((group) => group.actions);\n const isDisabled = (index: number) => !!actions[index]?.disabled;\n const found = actions.findIndex((action) => action.id === activeId && !action.disabled);\n const activeIndex = found >= 0 ? found : edgeIndex('first', actions.length, { isDisabled });\n // From the command's id, not its position: an id that stayed the same while the option under it\n // changed would not make a screen reader announce the new highlight.\n const optionId = (action: MenuAction) => `${listId}-${encodeURIComponent(action.id)}`;\n const activeOptionId = activeIndex === null ? undefined : optionId(actions[activeIndex]);\n\n useEffect(() => {\n if (!activeOptionId) return;\n scrollOptionIntoView(listRef.current, document.getElementById(activeOptionId));\n }, [activeOptionId]);\n\n function run(action: MenuAction | undefined) {\n if (!action || action.disabled) return;\n runMenuAction(action, onAction);\n onClose();\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLInputElement>) {\n // Enter confirms an IME composition before it means anything here.\n if (event.nativeEvent.isComposing) return;\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n const next = nextIndex(activeIndex, event.key === 'ArrowDown' ? 1 : -1, actions.length, { isDisabled });\n setActiveId(next === null ? null : actions[next].id);\n } else if (event.key === 'Enter') {\n run(activeIndex === null ? undefined : actions[activeIndex]);\n } else {\n return;\n }\n event.preventDefault();\n }\n\n const hasQuery = query.trim() !== '';\n const emptyText = isLoading ? text.loading : text.empty;\n let index = -1;\n\n return (\n <Modal\n {...rest}\n aria-label={text.label}\n isOpen={isOpen}\n onClose={onClose}\n hideHeader\n presentation={presentation}\n className={cx(styles.commandPalette, className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n >\n <div className={cx(styles.commandPaletteSearch, classNames?.search)} style={slotStyles?.search}>\n {renderIcon(faMagnifyingGlass, { className: styles.commandPaletteSearchIcon })}\n <input\n type='text'\n role='combobox'\n aria-label={text.label}\n aria-expanded='true'\n aria-controls={listId}\n aria-autocomplete='list'\n aria-activedescendant={activeOptionId}\n autoComplete='off'\n spellCheck={false}\n placeholder={text.placeholder}\n value={query}\n onChange={(event) => {\n setQuery(event.target.value);\n setActiveId(null);\n onQueryChange?.(event.target.value);\n }}\n onKeyDown={handleKeyDown}\n className={cx(styles.commandPaletteInput, classNames?.input)}\n style={slotStyles?.input}\n />\n </div>\n\n <div\n ref={listRef}\n id={listId}\n role='listbox'\n aria-label={text.label}\n className={cx(styles.commandPaletteList, classNames?.list)}\n style={slotStyles?.list}\n >\n {groups.map((group, groupIndex) => {\n const headingId = `${listId}-heading-${groupIndex}`;\n return (\n <div\n // Groups carry no id of their own; a new query re-renders the list wholesale anyway.\n key={groupIndex}\n role={group.heading === undefined ? 'presentation' : 'group'}\n aria-labelledby={group.heading === undefined ? undefined : headingId}\n >\n {group.separated && groupIndex > 0 && (\n <div\n aria-hidden\n className={cx(styles.commandPaletteSeparator, classNames?.separator)}\n style={slotStyles?.separator}\n />\n )}\n {group.heading !== undefined && (\n <div\n id={headingId}\n role='presentation'\n className={cx(styles.commandPaletteHeading, classNames?.heading)}\n style={slotStyles?.heading}\n >\n {group.heading}\n </div>\n )}\n {group.actions.map((action) => {\n index += 1;\n const own = index;\n return (\n <div\n key={action.id}\n id={optionId(action)}\n role='option'\n aria-selected={own === activeIndex}\n aria-disabled={action.disabled || undefined}\n aria-keyshortcuts={ariaKeyshortcuts(action.shortcut)}\n className={cx(\n styles.commandPaletteItem,\n own === activeIndex && styles.commandPaletteItemActive,\n action.danger && styles.commandPaletteItemDanger,\n action.disabled && styles.commandPaletteItemDisabled,\n classNames?.item,\n )}\n style={slotStyles?.item}\n // Keeps the focus, and so the caret, in the search field.\n onMouseDown={(event) => event.preventDefault()}\n // Move, not enter: a list scrolling under a resting pointer must not steal the highlight.\n onMouseMove={() => !action.disabled && setActiveId(action.id)}\n onClick={() => run(action)}\n >\n {renderItemIcon(action.icon, hasIcons, {\n className: cx(styles.commandPaletteItemIcon, classNames?.itemIcon),\n style: slotStyles?.itemIcon,\n })}\n <span\n className={cx(styles.commandPaletteItemLabel, classNames?.itemLabel)}\n style={slotStyles?.itemLabel}\n >\n {action.label}\n </span>\n {action.shortcut != null && (\n <span\n aria-hidden\n className={cx(styles.commandPaletteItemShortcut, classNames?.itemShortcut)}\n style={slotStyles?.itemShortcut}\n >\n {formatHotkey(action.shortcut)}\n </span>\n )}\n </div>\n );\n })}\n </div>\n );\n })}\n </div>\n\n {hasQuery && actions.length === 0 && (\n <div className={cx(styles.commandPaletteEmpty, classNames?.empty)} style={slotStyles?.empty}>\n {emptyText}\n </div>\n )}\n <span className={styles.commandPaletteStatus} role='status' aria-live='polite'>\n {hasQuery ? (actions.length > 0 ? text.results(actions.length) : emptyText) : ''}\n </span>\n </Modal>\n );\n}\n\nexport default CommandPalette;\n"],"mappings":"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"}
1
+ {"version":3,"file":"commandPalette.mjs","names":[],"sources":["../../../../package/common/commandPalette/commandPalette.tsx"],"sourcesContent":["import { useEffect, useId, useRef, useState, type KeyboardEvent, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faMagnifyingGlass } from '@fortawesome/free-solid-svg-icons';\n\nimport Modal from '../modal';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { ariaKeyshortcuts, formatHotkey } from '../hooks/useHotkeys';\nimport { edgeIndex, nextIndex, scrollOptionIntoView } from '../utils/listNavigation';\nimport { renderIcon } from '../utils/renderIcon';\nimport { useFollowHref } from '../utils/navigation';\nimport type { MenuAction } from '../menu/menu.types';\nimport { hasAnyIcon, renderItemIcon, runMenuAction } from '../menu/menuItem';\nimport { filterCommands, groupCommands } from './filterCommands';\nimport type { CommandPaletteLabels, CommandPaletteProps } from './commandPalette.types';\nimport styles from './commandPalette.module.pcss';\n\nconst DEFAULT_LABELS: Required<CommandPaletteLabels> = {\n label: 'Search commands',\n placeholder: 'Type a command or search…',\n empty: 'No results',\n loading: 'Searching…',\n results: (count) => (count === 1 ? '1 result' : `${count} results`),\n};\n\nfunction CommandPalette(props: CommandPaletteProps): ReactElement {\n const {\n items,\n isOpen,\n onClose,\n onAction,\n onQueryChange,\n filter = true,\n isLoading = false,\n presentation,\n labels,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n } = useComponentDefaults('CommandPalette', props);\n const text = useLabels('CommandPalette', labels, DEFAULT_LABELS);\n const listId = useId();\n const listRef = useRef<HTMLDivElement>(null);\n const followHref = useFollowHref();\n\n const [query, setQuery] = useState('');\n // An id rather than an index, so the highlight stays on the same command while server results\n // are replaced around it. Typing clears it instead, so Enter runs the best match.\n const [activeId, setActiveId] = useState<string | null>(null);\n const [wasOpen, setWasOpen] = useState(isOpen);\n if (isOpen !== wasOpen) {\n setWasOpen(isOpen);\n if (isOpen) {\n setQuery('');\n setActiveId(null);\n }\n }\n\n // Read through a ref so an inline handler does not re-fire the effect on every parent render.\n const onQueryChangeRef = useRef(onQueryChange);\n useEffect(() => {\n onQueryChangeRef.current = onQueryChange;\n });\n useEffect(() => {\n if (isOpen) onQueryChangeRef.current?.('');\n }, [isOpen]);\n\n const groups = filter ? filterCommands(groupCommands(items), query) : groupCommands(items);\n // From all items, not the matches, so the labels do not jump sideways while the user types.\n const hasIcons = hasAnyIcon(items);\n const actions = groups.flatMap((group) => group.actions);\n const isDisabled = (index: number) => !!actions[index]?.disabled;\n const found = actions.findIndex((action) => action.id === activeId && !action.disabled);\n const activeIndex = found >= 0 ? found : edgeIndex('first', actions.length, { isDisabled });\n // From the command's id, not its position: an id that stayed the same while the option under it\n // changed would not make a screen reader announce the new highlight.\n const optionId = (action: MenuAction) => `${listId}-${encodeURIComponent(action.id)}`;\n const activeOptionId = activeIndex === null ? undefined : optionId(actions[activeIndex]);\n\n useEffect(() => {\n if (!activeOptionId) return;\n scrollOptionIntoView(listRef.current, document.getElementById(activeOptionId));\n }, [activeOptionId]);\n\n function run(action: MenuAction | undefined) {\n if (!action || action.disabled) return;\n runMenuAction(action, onAction, followHref);\n onClose();\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLInputElement>) {\n // Enter confirms an IME composition before it means anything here.\n if (event.nativeEvent.isComposing) return;\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n const next = nextIndex(activeIndex, event.key === 'ArrowDown' ? 1 : -1, actions.length, { isDisabled });\n setActiveId(next === null ? null : actions[next].id);\n } else if (event.key === 'Enter') {\n run(activeIndex === null ? undefined : actions[activeIndex]);\n } else {\n return;\n }\n event.preventDefault();\n }\n\n const hasQuery = query.trim() !== '';\n const emptyText = isLoading ? text.loading : text.empty;\n let index = -1;\n\n return (\n <Modal\n {...rest}\n aria-label={text.label}\n isOpen={isOpen}\n onClose={onClose}\n hideHeader\n presentation={presentation}\n className={cx(styles.commandPalette, className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n >\n <div className={cx(styles.commandPaletteSearch, classNames?.search)} style={slotStyles?.search}>\n {renderIcon(faMagnifyingGlass, { className: styles.commandPaletteSearchIcon })}\n <input\n type='text'\n role='combobox'\n aria-label={text.label}\n aria-expanded='true'\n aria-controls={listId}\n aria-autocomplete='list'\n aria-activedescendant={activeOptionId}\n autoComplete='off'\n spellCheck={false}\n placeholder={text.placeholder}\n value={query}\n onChange={(event) => {\n setQuery(event.target.value);\n setActiveId(null);\n onQueryChange?.(event.target.value);\n }}\n onKeyDown={handleKeyDown}\n className={cx(styles.commandPaletteInput, classNames?.input)}\n style={slotStyles?.input}\n />\n </div>\n\n <div\n ref={listRef}\n id={listId}\n role='listbox'\n aria-label={text.label}\n className={cx(styles.commandPaletteList, classNames?.list)}\n style={slotStyles?.list}\n >\n {groups.map((group, groupIndex) => {\n const headingId = `${listId}-heading-${groupIndex}`;\n return (\n <div\n // Groups carry no id of their own; a new query re-renders the list wholesale anyway.\n key={groupIndex}\n role={group.heading === undefined ? 'presentation' : 'group'}\n aria-labelledby={group.heading === undefined ? undefined : headingId}\n >\n {group.separated && groupIndex > 0 && (\n <div\n aria-hidden\n className={cx(styles.commandPaletteSeparator, classNames?.separator)}\n style={slotStyles?.separator}\n />\n )}\n {group.heading !== undefined && (\n <div\n id={headingId}\n role='presentation'\n className={cx(styles.commandPaletteHeading, classNames?.heading)}\n style={slotStyles?.heading}\n >\n {group.heading}\n </div>\n )}\n {group.actions.map((action) => {\n index += 1;\n const own = index;\n return (\n <div\n key={action.id}\n id={optionId(action)}\n role='option'\n aria-selected={own === activeIndex}\n aria-disabled={action.disabled || undefined}\n aria-keyshortcuts={ariaKeyshortcuts(action.shortcut)}\n className={cx(\n styles.commandPaletteItem,\n own === activeIndex && styles.commandPaletteItemActive,\n action.danger && styles.commandPaletteItemDanger,\n action.disabled && styles.commandPaletteItemDisabled,\n classNames?.item,\n )}\n style={slotStyles?.item}\n // Keeps the focus, and so the caret, in the search field.\n onMouseDown={(event) => event.preventDefault()}\n // Move, not enter: a list scrolling under a resting pointer must not steal the highlight.\n onMouseMove={() => !action.disabled && setActiveId(action.id)}\n onClick={() => run(action)}\n >\n {renderItemIcon(action.icon, hasIcons, {\n className: cx(styles.commandPaletteItemIcon, classNames?.itemIcon),\n style: slotStyles?.itemIcon,\n })}\n <span\n className={cx(styles.commandPaletteItemLabel, classNames?.itemLabel)}\n style={slotStyles?.itemLabel}\n >\n {action.label}\n </span>\n {action.shortcut != null && (\n <span\n aria-hidden\n className={cx(styles.commandPaletteItemShortcut, classNames?.itemShortcut)}\n style={slotStyles?.itemShortcut}\n >\n {formatHotkey(action.shortcut)}\n </span>\n )}\n </div>\n );\n })}\n </div>\n );\n })}\n </div>\n\n {hasQuery && actions.length === 0 && (\n <div className={cx(styles.commandPaletteEmpty, classNames?.empty)} style={slotStyles?.empty}>\n {emptyText}\n </div>\n )}\n <span className={styles.commandPaletteStatus} role='status' aria-live='polite'>\n {hasQuery ? (actions.length > 0 ? text.results(actions.length) : emptyText) : ''}\n </span>\n </Modal>\n );\n}\n\nexport default CommandPalette;\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use client";import{createContext as e,useContext as t,useMemo as n}from"react";import{jsx as r}from"react/jsx-runtime";var i=e(null),a=e(void 0);function o(e,t){if(!e)return t??{};if(!t)return e;let n={...e};for(let r in t)n[r]={...e[r],...t[r]};return n}function s({children:e,defaultProps:s,presentation:c}){let l=t(i),u=n(()=>o(l,s),[l,s]),d=t(a),f=c??d;return r(i.Provider,{value:u,children:r(a.Provider,{value:f,children:e})})}export{i as ConfigContext,a as PresentationContext,s as default};
1
+ "use client";import{createContext as e,useContext as t,useMemo as n}from"react";import{jsx as r}from"react/jsx-runtime";var i=e(null),a=e(void 0),o=e(void 0);function s(e,t){if(!e)return t??{};if(!t)return e;let n={...e};for(let r in t)n[r]={...e[r],...t[r]};return n}function c({children:e,defaultProps:c,presentation:l,navigate:u}){let d=t(i),f=n(()=>s(d,c),[d,c]),p=t(a),m=l??p,h=t(o);return r(i.Provider,{value:f,children:r(a.Provider,{value:m,children:r(o.Provider,{value:u??h,children:e})})})}export{i as ConfigContext,o as NavigateContext,a as PresentationContext,c as default};
2
2
  //# sourceMappingURL=configProvider.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"configProvider.mjs","names":[],"sources":["../../../../package/common/configProvider/configProvider.tsx"],"sourcesContent":["import React, { createContext, useContext, useMemo } from 'react';\nimport type { ComponentDefaults, ConfigProviderProps } from './configProvider.types';\nimport type { Presentation } from '../configProvider/presentation.types';\n\nexport const ConfigContext = createContext<ComponentDefaults | null>(null);\n\n/**\n * One setting for every component that can present as a bottom sheet, kept separate from\n * `defaultProps` so that turning sheets on (or off) app-wide is a single switch rather than one\n * entry per component key — the components that can do it are unrelated to each other, and a\n * consumer who missed one would get sheets in half their app.\n */\nexport const PresentationContext = createContext<Presentation | undefined>(undefined);\n\n/**\n * Shallow-merge two default maps per component key, so nested ConfigProviders\n * compose: an inner provider's `Input` defaults extend (and override on\n * conflict) the outer provider's `Input` defaults rather than replacing them.\n */\nfunction mergeDefaults(parent: ComponentDefaults | null, own: ComponentDefaults | undefined): ComponentDefaults {\n if (!parent) return own ?? {};\n if (!own) return parent;\n\n const merged: Record<string, object> = { ...parent };\n for (const key in own) {\n merged[key] = {\n ...(parent as Record<string, object>)[key],\n ...(own as Record<string, object>)[key],\n };\n }\n return merged as ComponentDefaults;\n}\n\nfunction ConfigProvider({ children, defaultProps, presentation }: ConfigProviderProps) {\n const parent = useContext(ConfigContext);\n const value = useMemo(() => mergeDefaults(parent, defaultProps), [parent, defaultProps]);\n\n // A nested provider that doesn't set `presentation` keeps the outer one's, the same way\n // `defaultProps` compose.\n const inheritedPresentation = useContext(PresentationContext);\n const presentationValue = presentation ?? inheritedPresentation;\n\n return (\n <ConfigContext.Provider value={value}>\n <PresentationContext.Provider value={presentationValue}>{children}</PresentationContext.Provider>\n </ConfigContext.Provider>\n );\n}\n\nexport default ConfigProvider;\n"],"mappings":"wHAIA,IAAa,EAAgB,EAAwC,IAAI,EAQ5D,EAAsB,EAAwC,IAAA,EAAS,EAOpF,SAAS,EAAc,EAAkC,EAAuD,CAC9G,GAAI,CAAC,EAAQ,OAAO,GAAO,CAAC,EAC5B,GAAI,CAAC,EAAK,OAAO,EAEjB,IAAM,EAAiC,CAAE,GAAG,CAAO,EACnD,IAAK,IAAM,KAAO,EAChB,EAAO,GAAO,CACZ,GAAI,EAAkC,GACtC,GAAI,EAA+B,EACrC,EAEF,OAAO,CACT,CAEA,SAAS,EAAe,CAAE,WAAU,eAAc,gBAAqC,CACrF,IAAM,EAAS,EAAW,CAAa,EACjC,EAAQ,MAAc,EAAc,EAAQ,CAAY,EAAG,CAAC,EAAQ,CAAY,CAAC,EAIjF,EAAwB,EAAW,CAAmB,EACtD,EAAoB,GAAgB,EAE1C,OACE,EAAC,EAAc,SAAf,CAA+B,QAC7B,SAAA,EAAC,EAAoB,SAArB,CAA8B,MAAO,EAAoB,UAAuC,CAAA,CAC1E,CAAA,CAE5B"}
1
+ {"version":3,"file":"configProvider.mjs","names":[],"sources":["../../../../package/common/configProvider/configProvider.tsx"],"sourcesContent":["import React, { createContext, useContext, useMemo } from 'react';\nimport type { ComponentDefaults, ConfigProviderProps } from './configProvider.types';\nimport type { Presentation } from '../configProvider/presentation.types';\n\nexport const ConfigContext = createContext<ComponentDefaults | null>(null);\n\n/**\n * One setting for every component that can present as a bottom sheet, kept separate from\n * `defaultProps` so that turning sheets on (or off) app-wide is a single switch rather than one\n * entry per component key — the components that can do it are unrelated to each other, and a\n * consumer who missed one would get sheets in half their app.\n */\nexport const PresentationContext = createContext<Presentation | undefined>(undefined);\n\nexport const NavigateContext = createContext<ConfigProviderProps['navigate']>(undefined);\n\n/**\n * Shallow-merge two default maps per component key, so nested ConfigProviders\n * compose: an inner provider's `Input` defaults extend (and override on\n * conflict) the outer provider's `Input` defaults rather than replacing them.\n */\nfunction mergeDefaults(parent: ComponentDefaults | null, own: ComponentDefaults | undefined): ComponentDefaults {\n if (!parent) return own ?? {};\n if (!own) return parent;\n\n const merged: Record<string, object> = { ...parent };\n for (const key in own) {\n merged[key] = {\n ...(parent as Record<string, object>)[key],\n ...(own as Record<string, object>)[key],\n };\n }\n return merged as ComponentDefaults;\n}\n\nfunction ConfigProvider({ children, defaultProps, presentation, navigate }: ConfigProviderProps) {\n const parent = useContext(ConfigContext);\n const value = useMemo(() => mergeDefaults(parent, defaultProps), [parent, defaultProps]);\n\n // A nested provider that doesn't set `presentation` keeps the outer one's, the same way\n // `defaultProps` compose.\n const inheritedPresentation = useContext(PresentationContext);\n const presentationValue = presentation ?? inheritedPresentation;\n const inheritedNavigate = useContext(NavigateContext);\n\n return (\n <ConfigContext.Provider value={value}>\n <PresentationContext.Provider value={presentationValue}>\n <NavigateContext.Provider value={navigate ?? inheritedNavigate}>{children}</NavigateContext.Provider>\n </PresentationContext.Provider>\n </ConfigContext.Provider>\n );\n}\n\nexport default ConfigProvider;\n"],"mappings":"wHAIA,IAAa,EAAgB,EAAwC,IAAI,EAQ5D,EAAsB,EAAwC,IAAA,EAAS,EAEvE,EAAkB,EAA+C,IAAA,EAAS,EAOvF,SAAS,EAAc,EAAkC,EAAuD,CAC9G,GAAI,CAAC,EAAQ,OAAO,GAAO,CAAC,EAC5B,GAAI,CAAC,EAAK,OAAO,EAEjB,IAAM,EAAiC,CAAE,GAAG,CAAO,EACnD,IAAK,IAAM,KAAO,EAChB,EAAO,GAAO,CACZ,GAAI,EAAkC,GACtC,GAAI,EAA+B,EACrC,EAEF,OAAO,CACT,CAEA,SAAS,EAAe,CAAE,WAAU,eAAc,eAAc,YAAiC,CAC/F,IAAM,EAAS,EAAW,CAAa,EACjC,EAAQ,MAAc,EAAc,EAAQ,CAAY,EAAG,CAAC,EAAQ,CAAY,CAAC,EAIjF,EAAwB,EAAW,CAAmB,EACtD,EAAoB,GAAgB,EACpC,EAAoB,EAAW,CAAe,EAEpD,OACE,EAAC,EAAc,SAAf,CAA+B,QAC7B,SAAA,EAAC,EAAoB,SAArB,CAA8B,MAAO,EACnC,SAAA,EAAC,EAAgB,SAAjB,CAA0B,MAAO,GAAY,EAAoB,UAAmC,CAAA,CACxE,CAAA,CACR,CAAA,CAE5B"}
@@ -1,2 +1,2 @@
1
- "use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{usePresentationDefault as t}from"../configProvider/usePresentationDefault.mjs";import{Presentation as n}from"../configProvider/presentation.types.mjs";import r from"../loader/loader.mjs";import i from"../styles/sheet.module.mjs";import{PopoverAlign as ee,PopoverPlacement as te}from"../popover/popover.types.mjs";import ne from"../hooks/useSheet.mjs";import re from"../popover/popover.mjs";import{useInfiniteScroll as ie}from"../infiniteBlock/useInfiniteScroll.mjs";import ae from"../tooltip/tooltip.mjs";import{edgeIndex as oe,nextIndex as se,scrollOptionIntoView as ce}from"../utils/listNavigation.mjs";import a from"./dropdown.module.mjs";import{useEffect as o,useId as le,useLayoutEffect as ue,useRef as s,useState as c}from"react";import l from"classnames";import{FontAwesomeIcon as de}from"@fortawesome/react-fontawesome";import{faChevronDown as fe}from"@fortawesome/free-solid-svg-icons";import{jsx as u,jsxs as d}from"react/jsx-runtime";var pe=500;function f(f){let{errorMessage:p=``,className:me=``,style:he,label:m=``,value:ge=``,isValid:_e=!0,tabIndex:ve=0,isRequired:ye=!1,customProps:h={},isRow:be=!1,disabled:g=!1,readOnly:_=!1,onScroll:xe=()=>Promise.resolve(),onClick:v,onChange:y,onFocus:Se=null,onBlur:Ce=null,onMouseEnter:we=null,onMouseLeave:Te=null,dropdownLabel:Ee,isDropdownListWider:b=!1,formValidator:x=null,classNames:S,styles:C,items:w=[],useMatLabelStyle:T=!0,alwaysFloatLabel:E=!1,placeholder:D=``,plainStyle:O=!1,scrollToSelected:De=!1,presentation:Oe,reactRef:ke,...Ae}=e(`Dropdown`,f),[je,Me]=c(!1),[Ne,Pe]=c(!1),[,Fe]=c(0),k=s(x),A=s(null),j=s(null),M=s(null),[Ie,Le]=c(!1),[N,P]=c(null),F=s({buffer:``,at:0}),I=le(),L=`${I}-listbox`,R=`${I}-label`,z=e=>`${I}-option-${e}`,B=!g&&!_,V=Ie&&B,{isLoading:Re,sentinelRef:ze}=ie(M,xe,!1),Be=t(),H=Oe??Be??n.Default,{isSheet:U}=ne(H);ue(()=>{let e=A.current,t=j.current;V&&!U&&e&&t&&(t.style.fontSize=window.getComputedStyle(e).fontSize)},[V,U]),o(()=>{if(k.current=x,!k.current)return;k.current.label=m??``,k.current.validate();let e=()=>Fe(e=>e+1);return k.current.registerOnUpdateListener(e),()=>k.current?.removeOnUpdateListener(e)},[x,m]);let W=x?x.value:ge,G=w.findIndex(e=>e.key===W);o(()=>{V&&N!==null&&ce(M.current,document.getElementById(z(N)))},[V,N]);let Ve=e=>{Pe(!0),we?.(e)},He=e=>{Pe(!1),Te?.(e)},Ue=e=>{Me(!0),Se?.(e)},We=e=>{k.current&&(k.current.touched=!0),Me(!1),Ce?.(e)},Ge=()=>x?x.touched&&x.hasError():!_e||!!p,K=()=>!!(ye||x?.validators.find(e=>e.validatorId===`required`)),Ke=e=>{x?(x.set(e),y?.(x.value,h)):y?.(e,h)};function q(e){(!e||B)&&(Le(e),e?P(De&&G>=0?G:null):F.current={buffer:``,at:0})}function J(e){e.disabled||(Ke(e.key),q(!1))}let qe={isDisabled:e=>!!w[e].disabled};function Je(e){let t=Date.now(),n=t-F.current.at>pe?e:F.current.buffer+e;F.current={buffer:n,at:t};let r=n.toLowerCase(),i=w.findIndex(e=>!e.disabled&&typeof e.label==`string`&&e.label.toLowerCase().startsWith(r));i!==-1&&(V?P(i):J(w[i]))}function Ye(e){if(B){if(!V){e.key===`ArrowDown`||e.key===`ArrowUp`||e.key===`Enter`||e.key===` `?(e.preventDefault(),q(!0)):e.key.length===1&&Je(e.key);return}if(e.key===`ArrowDown`||e.key===`ArrowUp`)e.preventDefault(),P(se(N,e.key===`ArrowDown`?1:-1,w.length,qe));else if(e.key===`Home`||e.key===`End`)e.preventDefault(),P(oe(e.key===`Home`?`first`:`last`,w.length,qe));else if(e.key===`Enter`||e.key===` `){e.preventDefault();let t=N===null?void 0:w[N];t?J(t):q(!1)}else e.key===`Escape`||e.key===`Tab`?q(!1):e.key.length===1&&Je(e.key)}}let Y=Ee,X=Ge();w.forEach(e=>{e.key===W&&(Y=e.label)});let Z=Y||W,Q=Z==null||typeof Z==`string`&&Z.trim()===``,Xe=K()?u(`span`,{className:a.requiredMark,children:`*`}):null,Ze=m&&K()?`${m} *`:m,$=je&&!_;return d(`div`,{...Ae,className:l(me,S?.root,a.dropdownLabel,a.dropdown,{[a.dropdownLabelColumn]:!be,[a.dropdownWithLabel]:T&&!!m}),style:{...he,...C?.root},onMouseEnter:Ve,onMouseLeave:He,children:[u(`div`,{children:u(re,{isOpen:V,onOpenChange:q,disabled:!B,placement:te.Bottom,align:ee.Start,offset:0,withArrow:!1,constrainWidth:!1,width:`trigger`,focusOnOpen:!1,presentation:H,classNames:{panel:a.dropdownPanel},content:u(`div`,{ref:j,className:l(!U&&a.dropdownInputListAnim,{[a.dropdownInputListAnimWider]:b&&!U}),children:d(`div`,{ref:M,id:L,role:`listbox`,"aria-label":m||D||void 0,className:l(a.dropdownInputList,U&&i.sheetScroll,S?.list),style:C?.list,children:[w.map((e,t)=>u(`div`,{id:z(t),role:`option`,"aria-selected":e.key===W,"aria-disabled":e.disabled||void 0,style:C?.option,className:l(a.dropdownInputListItem,S?.option,{[a.dropdownInputListItemActive]:N===t,[a.dropdownInputListItemSelected]:e.key===W,[a.dropdownInputListItemDisabled]:!!e.disabled}),onMouseDown:e=>e.preventDefault(),onMouseEnter:()=>P(t),onClick:()=>J(e),children:e.label},e.key)),u(`div`,{ref:ze,className:a.dropdownSentinel,"aria-hidden":`true`}),Re&&u(`div`,{className:a.dropdownLoadMore,"aria-hidden":`true`,children:u(r,{size:32})})]})}),children:d(`div`,{ref:A,onFocus:Ue,onBlur:We,className:l(a.dropdownInput,{[a.dropdownInputPlain]:O,[a.dropdownInputReadOnly]:_&&!g},S?.field),style:C?.field,children:[u(`button`,{tabIndex:ve,type:`button`,disabled:g,role:`combobox`,"aria-haspopup":`listbox`,"aria-expanded":V,"aria-controls":L,"aria-activedescendant":V&&N!==null?z(N):void 0,"aria-labelledby":m?R:void 0,"aria-label":m?void 0:D||void 0,"aria-readonly":_||void 0,className:l(a.dropdownInputButton,{[a.dropdownInputButtonDisabled]:g,[a.dropdownInputButtonReadOnly]:_&&!g},S?.control),style:{...b?{padding:`6px`,border:`none`}:{},...C?.control},onKeyDown:Ye,onClick:v,ref:ke,children:Q?`\xA0`:Z}),u(`button`,{type:`button`,tabIndex:-1,disabled:g,"aria-hidden":`true`,className:l(a.dropdownInputButtonIcon,S?.icon),style:C?.icon,onMouseDown:e=>e.preventDefault(),onClick:v,children:!g&&!_&&u(de,{icon:fe,className:l(a.dropdownInputButtonIconArrow,{[a.dropdownInputButtonIconArrowOpen]:V})})}),u(`fieldset`,{className:l(a.dropdownFieldset,{[a.dropdownBorderPlain]:O,[a.dropdownBorderFocused]:$,[a.dropdownBorderInvalid]:X,[a.dropdownBorderError]:X}),children:u(`legend`,{className:l(a.dropdownFieldsetLegend,{[a.dropdownFieldsetLegendActive]:T&&(E||$||!Q)}),children:u(`span`,{children:Ze})})}),T&&m&&d(`div`,{id:R,className:l(a.dropdownMatLabel,{[a.dropdownMatLabelFloating]:E||$||!Q},S?.label),style:C?.label,children:[m,Xe]}),T&&D&&u(`div`,{className:l(a.dropdownPlaceholder,$&&!Y&&a.dropdownPlaceholderShown),children:D})]})})}),m&&!T&&d(`div`,{id:R,className:l(a.dropdownLabelText,S?.label),style:C?.label,children:[m,Xe]}),u(ae,{variant:`error`,message:x?x.getCurrentErrorMessage():p||``,isVisible:X&&(je||Ne)})]})}export{f as default};
1
+ "use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{usePresentationDefault as t}from"../configProvider/usePresentationDefault.mjs";import{Presentation as n}from"../configProvider/presentation.types.mjs";import ee from"../loader/loader.mjs";import r from"../styles/sheet.module.mjs";import{PopoverAlign as te,PopoverPlacement as ne}from"../popover/popover.types.mjs";import re from"../hooks/useSheet.mjs";import ie from"../popover/popover.mjs";import{useInfiniteScroll as ae}from"../infiniteBlock/useInfiniteScroll.mjs";import oe from"../tooltip/tooltip.mjs";import{edgeIndex as se,nextIndex as ce,scrollOptionIntoView as le}from"../utils/listNavigation.mjs";import i from"./dropdown.module.mjs";import{useEffect as ue,useId as de,useLayoutEffect as fe,useRef as a,useState as o}from"react";import s from"classnames";import{FontAwesomeIcon as pe}from"@fortawesome/react-fontawesome";import{faChevronDown as me}from"@fortawesome/free-solid-svg-icons";import{jsx as c,jsxs as l}from"react/jsx-runtime";var he=500;function u(u){let{errorMessage:ge=``,className:_e=``,style:ve,label:d=``,value:ye=``,isValid:be=!0,tabIndex:xe=0,isRequired:Se=!1,customProps:Ce={},isRow:we=!1,disabled:f=!1,readOnly:p=!1,onLoadMore:Te,onClick:m,onChange:h,onFocus:Ee=null,onBlur:De=null,onMouseEnter:Oe=null,onMouseLeave:ke=null,dropdownLabel:Ae,isDropdownListWider:je=!1,formValidator:g=null,classNames:_,styles:v,items:y=[],useMatLabelStyle:b=!0,alwaysFloatLabel:x=!1,placeholder:S=``,plainStyle:C=!1,scrollToSelected:Me=!1,presentation:Ne,reactRef:Pe,...Fe}=e(`Dropdown`,u),[w,T]=o(!1),[Ie,E]=o(!1),[,Le]=o(0),D=a(g),O=a(null),Re=a(null),k=a(null),[ze,Be]=o(!1),[A,j]=o(null),M=a({buffer:``,at:0}),N=de(),P=`${N}-listbox`,F=`${N}-label`,I=e=>`${N}-option-${e}`,L=!f&&!p,R=ze&&L,{isLoading:z,sentinelRef:Ve}=ae(k,R?Te:void 0),He=t(),B=Ne??He??n.Default,{isSheet:V}=re(B);fe(()=>{let e=O.current,t=Re.current;R&&!V&&e&&t&&(t.style.fontSize=window.getComputedStyle(e).fontSize)},[R,V]),ue(()=>{if(D.current=g,!D.current)return;D.current.label=d??``,D.current.validate();let e=()=>Le(e=>e+1);return D.current.registerOnUpdateListener(e),()=>D.current?.removeOnUpdateListener(e)},[g,d]);let H=g?g.value:ye,U=y.findIndex(e=>e.key===H);ue(()=>{R&&A!==null&&le(k.current,document.getElementById(I(A)))},[R,A]);let Ue=e=>{E(!0),Oe?.(e)},We=e=>{E(!1),ke?.(e)},Ge=e=>{T(!0),Ee?.(e)},Ke=e=>{D.current&&(D.current.touched=!0),T(!1),De?.(e)},qe=()=>g?g.touched&&g.hasError():!be||!!ge,W=()=>!!(Se||g?.validators.find(e=>e.validatorId===`required`)),Je=e=>{g?(g.set(e),h?.(g.value,Ce)):h?.(e,Ce)};function G(e){(!e||L)&&(Be(e),e?j(Me&&U>=0?U:null):M.current={buffer:``,at:0})}function K(e){e.disabled||(Je(e.key),G(!1))}let q={isDisabled:e=>!!y[e].disabled};function J(e){let t=Date.now(),n=t-M.current.at>he?e:M.current.buffer+e;M.current={buffer:n,at:t};let ee=n.toLowerCase(),r=y.findIndex(e=>!e.disabled&&typeof e.label==`string`&&e.label.toLowerCase().startsWith(ee));r!==-1&&(R?j(r):K(y[r]))}function Ye(e){if(L){if(!R){e.key===`ArrowDown`||e.key===`ArrowUp`||e.key===`Enter`||e.key===` `?(e.preventDefault(),G(!0)):e.key.length===1&&J(e.key);return}if(e.key===`ArrowDown`||e.key===`ArrowUp`)e.preventDefault(),j(ce(A,e.key===`ArrowDown`?1:-1,y.length,q));else if(e.key===`Home`||e.key===`End`)e.preventDefault(),j(se(e.key===`Home`?`first`:`last`,y.length,q));else if(e.key===`Enter`||e.key===` `){e.preventDefault();let t=A===null?void 0:y[A];t?K(t):G(!1)}else e.key===`Escape`||e.key===`Tab`?G(!1):e.key.length===1&&J(e.key)}}let Y=Ae,X=qe();y.forEach(e=>{e.key===H&&(Y=e.label)});let Z=Y||H,Q=Z==null||typeof Z==`string`&&Z.trim()===``,Xe=W()?c(`span`,{className:i.requiredMark,children:`*`}):null,Ze=d&&W()?`${d} *`:d,$=w&&!p;return l(`div`,{...Fe,className:s(_e,_?.root,i.dropdownLabel,i.dropdown,{[i.dropdownLabelColumn]:!we,[i.dropdownWithLabel]:b&&!!d}),style:{...ve,...v?.root},onMouseEnter:Ue,onMouseLeave:We,children:[c(`div`,{children:c(ie,{isOpen:R,onOpenChange:G,disabled:!L,placement:ne.Bottom,align:te.Start,offset:0,withArrow:!1,constrainWidth:!1,width:`trigger`,focusOnOpen:!1,presentation:B,classNames:{panel:i.dropdownPanel},content:c(`div`,{ref:Re,className:s(V?i.dropdownInputListSheet:i.dropdownInputListAnim,{[i.dropdownInputListAnimWider]:je&&!V}),children:l(`div`,{ref:k,id:P,role:`listbox`,"aria-label":d||S||void 0,"aria-busy":z||void 0,className:s(i.dropdownInputList,V&&r.sheetScroll,_?.list),style:v?.list,children:[y.map((e,t)=>c(`div`,{id:I(t),role:`option`,"aria-selected":e.key===H,"aria-disabled":e.disabled||void 0,style:v?.option,className:s(i.dropdownInputListItem,_?.option,{[i.dropdownInputListItemActive]:A===t,[i.dropdownInputListItemSelected]:e.key===H,[i.dropdownInputListItemDisabled]:!!e.disabled}),onMouseDown:e=>e.preventDefault(),onMouseEnter:()=>j(t),onClick:()=>K(e),children:e.label},e.key)),c(`div`,{ref:Ve,className:i.dropdownSentinel,"aria-hidden":`true`}),z&&c(`div`,{className:i.dropdownLoadMore,"aria-hidden":`true`,children:c(ee,{size:`1.125em`})})]})}),children:l(`div`,{ref:O,onFocus:Ge,onBlur:Ke,className:s(i.dropdownInput,{[i.dropdownInputPlain]:C,[i.dropdownInputReadOnly]:p&&!f},_?.field),style:v?.field,children:[c(`button`,{tabIndex:xe,type:`button`,disabled:f,role:`combobox`,"aria-haspopup":`listbox`,"aria-expanded":R,"aria-controls":P,"aria-activedescendant":R&&A!==null?I(A):void 0,"aria-labelledby":d?F:void 0,"aria-label":d?void 0:S||void 0,"aria-readonly":p||void 0,className:s(i.dropdownInputButton,{[i.dropdownInputButtonDisabled]:f,[i.dropdownInputButtonReadOnly]:p&&!f},_?.control),style:{...je?{padding:`6px`,border:`none`}:{},...v?.control},onKeyDown:Ye,onClick:m,ref:Pe,children:Q?`\xA0`:Z}),c(`button`,{type:`button`,tabIndex:-1,disabled:f,"aria-hidden":`true`,className:s(i.dropdownInputButtonIcon,_?.icon),style:v?.icon,onMouseDown:e=>e.preventDefault(),onClick:m,children:!f&&!p&&c(pe,{icon:me,className:s(i.dropdownInputButtonIconArrow,{[i.dropdownInputButtonIconArrowOpen]:R})})}),c(`fieldset`,{className:s(i.dropdownFieldset,{[i.dropdownBorderPlain]:C,[i.dropdownBorderFocused]:$,[i.dropdownBorderInvalid]:X,[i.dropdownBorderError]:X}),children:c(`legend`,{className:s(i.dropdownFieldsetLegend,{[i.dropdownFieldsetLegendActive]:b&&(x||$||!Q)}),children:c(`span`,{children:Ze})})}),b&&d&&l(`div`,{id:F,className:s(i.dropdownMatLabel,{[i.dropdownMatLabelFloating]:x||$||!Q},_?.label),style:v?.label,children:[d,Xe]}),b&&S&&c(`div`,{className:s(i.dropdownPlaceholder,$&&!Y&&i.dropdownPlaceholderShown),children:S})]})})}),d&&!b&&l(`div`,{id:F,className:s(i.dropdownLabelText,_?.label),style:v?.label,children:[d,Xe]}),c(oe,{variant:`error`,message:g?g.getCurrentErrorMessage():ge||``,isVisible:X&&(w||Ie)})]})}export{u as default};
2
2
  //# sourceMappingURL=dropdown.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"dropdown.mjs","names":[],"sources":["../../../../package/common/dropdown/dropdown.tsx"],"sourcesContent":["import React, { useEffect, useId, useLayoutEffect, useRef, useState } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronDown as dropdownIcon } from '@fortawesome/free-solid-svg-icons';\nimport Tooltip from 'components/common/tooltip';\n\nimport Popover from '../popover';\nimport Loader from '../loader';\nimport { useInfiniteScroll } from '../infiniteBlock/useInfiniteScroll';\nimport { edgeIndex, nextIndex, scrollOptionIntoView, type ActiveIndex } from '../utils/listNavigation';\nimport { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport { useComponentDefaults, usePresentationDefault } from '../configProvider';\nimport { useSheet } from '../hooks/useSheet';\nimport { Presentation } from '../configProvider/presentation.types';\nimport styles from './dropdown.module.pcss';\nimport sheetStyles from '../styles/sheet.module.pcss';\nimport type { DropdownProps, DropdownItem } from './dropdown.types';\n\n/** How long a pause resets the type-ahead buffer, matching a native `<select>`. */\nconst TYPEAHEAD_RESET_MS = 500;\n\nfunction Dropdown(props: DropdownProps) {\n const {\n errorMessage = '',\n className = '',\n style,\n label = '',\n value = '',\n isValid = true,\n tabIndex = 0,\n isRequired = false,\n customProps = {},\n isRow = false,\n disabled = false,\n readOnly = false,\n onScroll = () => Promise.resolve(),\n onClick,\n onChange,\n onFocus: onFocusProp = null,\n onBlur: onBlurProp = null,\n onMouseEnter: onMouseEnterProp = null,\n onMouseLeave: onMouseLeaveProp = null,\n dropdownLabel: dropdownLabelProp,\n isDropdownListWider = false,\n formValidator = null,\n classNames,\n styles: slotStyles,\n items = [],\n useMatLabelStyle = true,\n alwaysFloatLabel = false,\n placeholder = '',\n plainStyle = false,\n scrollToSelected = false,\n presentation: presentationProp,\n reactRef,\n ...rest\n } = useComponentDefaults('Dropdown', props);\n const [isFocused, setIsFocused] = useState(false);\n const [isHovered, setIsHovered] = useState(false);\n const [, forceUpdate] = useState(0);\n const fvRef = useRef(formValidator);\n const triggerRef = useRef<HTMLDivElement>(null);\n const anchorRef = useRef<HTMLDivElement>(null);\n const listRef = useRef<HTMLDivElement>(null);\n const [menuIsOpen, setMenuIsOpen] = useState(false);\n // `null` is \"nothing highlighted\", the state a list opens in unless `scrollToSelected`\n // asks for the current value. Arrowing from there enters the list at either end.\n const [activeIndex, setActiveIndex] = useState<ActiveIndex>(null);\n const typeahead = useRef({ buffer: '', at: 0 });\n\n const baseId = useId();\n const listboxId = `${baseId}-listbox`;\n const labelId = `${baseId}-label`;\n const optionId = (index: number) => `${baseId}-option-${index}`;\n\n const isInteractive = !disabled && !readOnly;\n const isOpen = menuIsOpen && isInteractive;\n\n // The list is its own scroll container. It used to render through an `InfiniteBlock`, which\n // also brought a tabbable container and a second key handler: inside a listbox that put a\n // focusable element with no role in the tab order and gave the arrows two owners.\n const { isLoading: showScrollSpinner, sentinelRef } = useInfiniteScroll(listRef, onScroll, false);\n\n // Explicit prop → this component's global default → the app-wide one → anchored.\n // Resolved here rather than left to Popover so that this component and the panel\n // it opens can never disagree about whether they are presenting as a sheet.\n const appPresentation = usePresentationDefault();\n const presentation = presentationProp ?? appPresentation ?? Presentation.Default;\n const { isSheet } = useSheet(presentation);\n\n // The list reads at the field's size, which is not always the page's: a Dropdown\n // inside something with a smaller font should not open a list at the default one.\n // Width is Popover's job, through `width='trigger'`.\n useLayoutEffect(() => {\n const trigger = triggerRef.current;\n const menu = anchorRef.current;\n if (!isOpen || isSheet || !trigger || !menu) return;\n menu.style.fontSize = window.getComputedStyle(trigger).fontSize;\n }, [isOpen, isSheet]);\n\n useEffect(() => {\n fvRef.current = formValidator;\n if (!fvRef.current) return;\n fvRef.current.label = label ?? '';\n fvRef.current.validate();\n const listener = () => forceUpdate((n) => n + 1);\n fvRef.current.registerOnUpdateListener(listener);\n return () => fvRef.current?.removeOnUpdateListener(listener);\n }, [formValidator, label]);\n\n const currentValue = formValidator ? (formValidator.value as string | number) : value;\n const selectedIndex = items.findIndex((item) => item.key === currentValue);\n\n useEffect(() => {\n if (!isOpen || activeIndex === null) return;\n scrollOptionIntoView(listRef.current, document.getElementById(optionId(activeIndex)));\n // optionId is derived from a stable useId, so it cannot go stale between renders.\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n }, [isOpen, activeIndex]);\n\n const onMouseEnterHandler = (event: React.MouseEvent) => {\n setIsHovered(true);\n onMouseEnterProp?.(event);\n };\n\n const onMouseLeaveHandler = (event: React.MouseEvent) => {\n setIsHovered(false);\n onMouseLeaveProp?.(event);\n };\n\n const onFocusHandler = (event: React.FocusEvent) => {\n setIsFocused(true);\n onFocusProp?.(event);\n };\n\n const onBlurHandler = (event: React.FocusEvent) => {\n if (fvRef.current) fvRef.current.touched = true;\n setIsFocused(false);\n onBlurProp?.(event);\n };\n\n const hasError = (): boolean => {\n return formValidator ? formValidator.touched && formValidator.hasError() : !isValid || !!errorMessage;\n };\n\n const computedIsRequired = (): boolean => {\n return !!(\n isRequired || formValidator?.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required')\n );\n };\n\n const onChangeHandler = (val: string | number): void => {\n if (formValidator) {\n formValidator.set(val);\n onChange?.(formValidator.value, customProps);\n } else {\n onChange?.(val, customProps);\n }\n };\n\n function handleOpenChange(next: boolean) {\n if (next && !isInteractive) return;\n setMenuIsOpen(next);\n if (next) setActiveIndex(scrollToSelected && selectedIndex >= 0 ? selectedIndex : null);\n else typeahead.current = { buffer: '', at: 0 };\n }\n\n function select(item: DropdownItem) {\n if (item.disabled) return;\n onChangeHandler(item.key);\n handleOpenChange(false);\n }\n\n const navigation = { isDisabled: (index: number) => !!items[index].disabled };\n\n /** Jumps to the option whose label starts with what was typed, as a native `<select>` does. */\n function handleTypeahead(key: string) {\n const now = Date.now();\n const buffer = now - typeahead.current.at > TYPEAHEAD_RESET_MS ? key : typeahead.current.buffer + key;\n typeahead.current = { buffer, at: now };\n\n const needle = buffer.toLowerCase();\n const match = items.findIndex(\n (item) => !item.disabled && typeof item.label === 'string' && item.label.toLowerCase().startsWith(needle),\n );\n if (match === -1) return;\n if (isOpen) setActiveIndex(match);\n else select(items[match]);\n }\n\n function handleKeyDown(event: React.KeyboardEvent) {\n if (!isInteractive) return;\n\n if (!isOpen) {\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp' || event.key === 'Enter' || event.key === ' ') {\n // Without this the browser turns the key into a click on the trigger, which\n // Popover would read as a second toggle and close the list again.\n event.preventDefault();\n handleOpenChange(true);\n } else if (event.key.length === 1) {\n handleTypeahead(event.key);\n }\n return;\n }\n\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n setActiveIndex(nextIndex(activeIndex, event.key === 'ArrowDown' ? 1 : -1, items.length, navigation));\n } else if (event.key === 'Home' || event.key === 'End') {\n event.preventDefault();\n setActiveIndex(edgeIndex(event.key === 'Home' ? 'first' : 'last', items.length, navigation));\n } else if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n const item = activeIndex === null ? undefined : items[activeIndex];\n if (item) select(item);\n else handleOpenChange(false);\n } else if (event.key === 'Escape' || event.key === 'Tab') {\n handleOpenChange(false);\n } else if (event.key.length === 1) {\n handleTypeahead(event.key);\n }\n }\n\n let dropdownLabel = dropdownLabelProp;\n const hasErr = hasError();\n\n items.forEach((item: DropdownItem) => {\n if (item.key === currentValue) {\n dropdownLabel = item.label as string;\n }\n });\n\n const displayValue = dropdownLabel || currentValue;\n const isDisplayBlank = displayValue == null || (typeof displayValue === 'string' && displayValue.trim() === '');\n\n const requiredMark = computedIsRequired() ? <span className={styles.requiredMark}>*</span> : null;\n const editedLabel = label && computedIsRequired() ? `${label} *` : label;\n\n const showFocus = isFocused && !readOnly;\n\n return (\n <div\n {...rest}\n className={cx(className, classNames?.root, styles.dropdownLabel, styles.dropdown, {\n [styles.dropdownLabelColumn]: !isRow,\n [styles.dropdownWithLabel]: useMatLabelStyle && !!label,\n })}\n style={{ ...style, ...slotStyles?.root }}\n onMouseEnter={onMouseEnterHandler}\n onMouseLeave={onMouseLeaveHandler}\n >\n <div>\n <Popover\n // Popover owns opening and closing, including the outside click, Escape and the\n // click on the field itself. The trigger only has to say what it wants.\n isOpen={isOpen}\n onOpenChange={handleOpenChange}\n disabled={!isInteractive}\n placement={PopoverPlacement.Bottom}\n align={PopoverAlign.Start}\n offset={0}\n withArrow={false}\n constrainWidth={false}\n width='trigger'\n // The focus stays on the trigger while the list is open, because that is what\n // `aria-activedescendant` points from.\n focusOnOpen={false}\n presentation={presentation}\n classNames={{ panel: styles.dropdownPanel }}\n content={\n <div\n ref={anchorRef}\n // A sheet's own panel does the clipping and the scrolling, and\n // spans the screen rather than sizing itself to the field.\n className={cx(!isSheet && styles.dropdownInputListAnim, {\n [styles.dropdownInputListAnimWider]: isDropdownListWider && !isSheet,\n })}\n >\n <div\n ref={listRef}\n id={listboxId}\n role='listbox'\n aria-label={label || placeholder || undefined}\n className={cx(styles.dropdownInputList, isSheet && sheetStyles.sheetScroll, classNames?.list)}\n style={slotStyles?.list}\n >\n {items.map((item: DropdownItem, index: number) => (\n <div\n key={item.key}\n id={optionId(index)}\n role='option'\n aria-selected={item.key === currentValue}\n aria-disabled={item.disabled || undefined}\n style={slotStyles?.option}\n className={cx(styles.dropdownInputListItem, classNames?.option, {\n [styles.dropdownInputListItemActive]: activeIndex === index,\n [styles.dropdownInputListItemSelected]: item.key === currentValue,\n [styles.dropdownInputListItemDisabled]: !!item.disabled,\n })}\n // Selecting must not move the focus off the trigger, which is what\n // `aria-activedescendant` is anchored to.\n onMouseDown={(event) => event.preventDefault()}\n onMouseEnter={() => setActiveIndex(index)}\n onClick={() => select(item)}\n >\n {item.label}\n </div>\n ))}\n <div ref={sentinelRef} className={styles.dropdownSentinel} aria-hidden='true' />\n {showScrollSpinner && (\n <div className={styles.dropdownLoadMore} aria-hidden='true'>\n <Loader size={32} />\n </div>\n )}\n </div>\n </div>\n }\n >\n <div\n ref={triggerRef}\n onFocus={onFocusHandler}\n onBlur={onBlurHandler}\n className={cx(\n styles.dropdownInput,\n {\n [styles.dropdownInputPlain]: plainStyle,\n [styles.dropdownInputReadOnly]: readOnly && !disabled,\n },\n classNames?.field,\n )}\n style={slotStyles?.field}\n >\n <button\n tabIndex={tabIndex}\n type='button'\n disabled={disabled}\n // A select-only combobox: the accessible name comes from the field's label,\n // and the element's own content is read as its value. A plain `button` role\n // would announce the value as the name and never mention the label.\n role='combobox'\n aria-haspopup='listbox'\n aria-expanded={isOpen}\n aria-controls={listboxId}\n aria-activedescendant={isOpen && activeIndex !== null ? optionId(activeIndex) : undefined}\n aria-labelledby={label ? labelId : undefined}\n aria-label={label ? undefined : placeholder || undefined}\n aria-readonly={readOnly || undefined}\n className={cx(\n styles.dropdownInputButton,\n {\n [styles.dropdownInputButtonDisabled]: disabled,\n [styles.dropdownInputButtonReadOnly]: readOnly && !disabled,\n },\n classNames?.control,\n )}\n style={{ ...(isDropdownListWider ? { padding: '6px', border: 'none' } : {}), ...slotStyles?.control }}\n onKeyDown={handleKeyDown}\n onClick={onClick}\n ref={reactRef}\n >\n {/* Non-breaking space when blank (same text-node layout as a\n real value) so the button keeps a full line of height. */}\n {isDisplayBlank ? ' ' : displayValue}\n </button>\n <button\n type='button'\n tabIndex={-1}\n disabled={disabled}\n // The chevron repeats what the combobox beside it already does, so it is a\n // pointer affordance only. Announcing it would offer the same control twice.\n aria-hidden='true'\n className={cx(styles.dropdownInputButtonIcon, classNames?.icon)}\n style={slotStyles?.icon}\n // Keeping the focus where it is, rather than letting mousedown move it here:\n // the trigger is what `aria-activedescendant` points from, and the click\n // still reaches Popover by bubbling.\n onMouseDown={(event: React.MouseEvent<HTMLButtonElement>) => event.preventDefault()}\n onClick={onClick}\n >\n {!disabled && !readOnly && (\n <FontAwesomeIcon\n icon={dropdownIcon}\n className={cx(styles.dropdownInputButtonIconArrow, {\n [styles.dropdownInputButtonIconArrowOpen]: isOpen,\n })}\n />\n )}\n </button>\n <fieldset\n className={cx(styles.dropdownFieldset, {\n [styles.dropdownBorderPlain]: plainStyle,\n [styles.dropdownBorderFocused]: showFocus,\n [styles.dropdownBorderInvalid]: hasErr,\n [styles.dropdownBorderError]: hasErr,\n })}\n >\n <legend\n className={cx(styles.dropdownFieldsetLegend, {\n [styles.dropdownFieldsetLegendActive]:\n useMatLabelStyle && (alwaysFloatLabel || showFocus || !isDisplayBlank),\n })}\n >\n <span>{editedLabel}</span>\n </legend>\n </fieldset>\n {useMatLabelStyle && label && (\n <div\n id={labelId}\n className={cx(\n styles.dropdownMatLabel,\n {\n [styles.dropdownMatLabelFloating]: alwaysFloatLabel || showFocus || !isDisplayBlank,\n },\n // The floating and the stacked label are two renderings of\n // the same thing, so one slot reaches whichever is in use.\n classNames?.label,\n )}\n style={slotStyles?.label}\n >\n {label}\n {requiredMark}\n </div>\n )}\n {useMatLabelStyle && placeholder && (\n <div\n className={cx(\n styles.dropdownPlaceholder,\n showFocus && !dropdownLabel && styles.dropdownPlaceholderShown,\n )}\n >\n {placeholder}\n </div>\n )}\n </div>\n </Popover>\n </div>\n {label && !useMatLabelStyle && (\n <div id={labelId} className={cx(styles.dropdownLabelText, classNames?.label)} style={slotStyles?.label}>\n {label}\n {requiredMark}\n </div>\n )}\n <Tooltip\n variant='error'\n message={formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || ''}\n isVisible={hasErr && (isFocused || isHovered)}\n />\n </div>\n );\n}\n\nexport default Dropdown;\n"],"mappings":"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"}
1
+ {"version":3,"file":"dropdown.mjs","names":[],"sources":["../../../../package/common/dropdown/dropdown.tsx"],"sourcesContent":["import React, { useEffect, useId, useLayoutEffect, useRef, useState } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronDown as dropdownIcon } from '@fortawesome/free-solid-svg-icons';\nimport Tooltip from 'components/common/tooltip';\n\nimport Popover from '../popover';\nimport Loader from '../loader';\nimport { useInfiniteScroll } from '../infiniteBlock/useInfiniteScroll';\nimport { edgeIndex, nextIndex, scrollOptionIntoView, type ActiveIndex } from '../utils/listNavigation';\nimport { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport { useComponentDefaults, usePresentationDefault } from '../configProvider';\nimport { useSheet } from '../hooks/useSheet';\nimport { Presentation } from '../configProvider/presentation.types';\nimport styles from './dropdown.module.pcss';\nimport sheetStyles from '../styles/sheet.module.pcss';\nimport type { DropdownProps, DropdownItem } from './dropdown.types';\n\n/** How long a pause resets the type-ahead buffer, matching a native `<select>`. */\nconst TYPEAHEAD_RESET_MS = 500;\n\nfunction Dropdown(props: DropdownProps) {\n const {\n errorMessage = '',\n className = '',\n style,\n label = '',\n value = '',\n isValid = true,\n tabIndex = 0,\n isRequired = false,\n customProps = {},\n isRow = false,\n disabled = false,\n readOnly = false,\n onLoadMore,\n onClick,\n onChange,\n onFocus: onFocusProp = null,\n onBlur: onBlurProp = null,\n onMouseEnter: onMouseEnterProp = null,\n onMouseLeave: onMouseLeaveProp = null,\n dropdownLabel: dropdownLabelProp,\n isDropdownListWider = false,\n formValidator = null,\n classNames,\n styles: slotStyles,\n items = [],\n useMatLabelStyle = true,\n alwaysFloatLabel = false,\n placeholder = '',\n plainStyle = false,\n scrollToSelected = false,\n presentation: presentationProp,\n reactRef,\n ...rest\n } = useComponentDefaults('Dropdown', props);\n const [isFocused, setIsFocused] = useState(false);\n const [isHovered, setIsHovered] = useState(false);\n const [, forceUpdate] = useState(0);\n const fvRef = useRef(formValidator);\n const triggerRef = useRef<HTMLDivElement>(null);\n const anchorRef = useRef<HTMLDivElement>(null);\n const listRef = useRef<HTMLDivElement>(null);\n const [menuIsOpen, setMenuIsOpen] = useState(false);\n // `null` is \"nothing highlighted\", the state a list opens in unless `scrollToSelected`\n // asks for the current value. Arrowing from there enters the list at either end.\n const [activeIndex, setActiveIndex] = useState<ActiveIndex>(null);\n const typeahead = useRef({ buffer: '', at: 0 });\n\n const baseId = useId();\n const listboxId = `${baseId}-listbox`;\n const labelId = `${baseId}-label`;\n const optionId = (index: number) => `${baseId}-option-${index}`;\n\n const isInteractive = !disabled && !readOnly;\n const isOpen = menuIsOpen && isInteractive;\n\n // The list is its own scroll container. It used to render through an `InfiniteBlock`, which\n // also brought a tabbable container and a second key handler: inside a listbox that put a\n // focusable element with no role in the tab order and gave the arrows two owners.\n // Only while open: the list it watches is rendered then, and not before.\n const { isLoading: showScrollSpinner, sentinelRef } = useInfiniteScroll(listRef, isOpen ? onLoadMore : undefined);\n\n // Explicit prop → this component's global default → the app-wide one → anchored.\n // Resolved here rather than left to Popover so that this component and the panel\n // it opens can never disagree about whether they are presenting as a sheet.\n const appPresentation = usePresentationDefault();\n const presentation = presentationProp ?? appPresentation ?? Presentation.Default;\n const { isSheet } = useSheet(presentation);\n\n // The list reads at the field's size, which is not always the page's: a Dropdown\n // inside something with a smaller font should not open a list at the default one.\n // Width is Popover's job, through `width='trigger'`.\n useLayoutEffect(() => {\n const trigger = triggerRef.current;\n const menu = anchorRef.current;\n if (!isOpen || isSheet || !trigger || !menu) return;\n menu.style.fontSize = window.getComputedStyle(trigger).fontSize;\n }, [isOpen, isSheet]);\n\n useEffect(() => {\n fvRef.current = formValidator;\n if (!fvRef.current) return;\n fvRef.current.label = label ?? '';\n fvRef.current.validate();\n const listener = () => forceUpdate((n) => n + 1);\n fvRef.current.registerOnUpdateListener(listener);\n return () => fvRef.current?.removeOnUpdateListener(listener);\n }, [formValidator, label]);\n\n const currentValue = formValidator ? (formValidator.value as string | number) : value;\n const selectedIndex = items.findIndex((item) => item.key === currentValue);\n\n useEffect(() => {\n if (!isOpen || activeIndex === null) return;\n scrollOptionIntoView(listRef.current, document.getElementById(optionId(activeIndex)));\n // optionId is derived from a stable useId, so it cannot go stale between renders.\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n }, [isOpen, activeIndex]);\n\n const onMouseEnterHandler = (event: React.MouseEvent) => {\n setIsHovered(true);\n onMouseEnterProp?.(event);\n };\n\n const onMouseLeaveHandler = (event: React.MouseEvent) => {\n setIsHovered(false);\n onMouseLeaveProp?.(event);\n };\n\n const onFocusHandler = (event: React.FocusEvent) => {\n setIsFocused(true);\n onFocusProp?.(event);\n };\n\n const onBlurHandler = (event: React.FocusEvent) => {\n if (fvRef.current) fvRef.current.touched = true;\n setIsFocused(false);\n onBlurProp?.(event);\n };\n\n const hasError = (): boolean => {\n return formValidator ? formValidator.touched && formValidator.hasError() : !isValid || !!errorMessage;\n };\n\n const computedIsRequired = (): boolean => {\n return !!(\n isRequired || formValidator?.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required')\n );\n };\n\n const onChangeHandler = (val: string | number): void => {\n if (formValidator) {\n formValidator.set(val);\n onChange?.(formValidator.value, customProps);\n } else {\n onChange?.(val, customProps);\n }\n };\n\n function handleOpenChange(next: boolean) {\n if (next && !isInteractive) return;\n setMenuIsOpen(next);\n if (next) setActiveIndex(scrollToSelected && selectedIndex >= 0 ? selectedIndex : null);\n else typeahead.current = { buffer: '', at: 0 };\n }\n\n function select(item: DropdownItem) {\n if (item.disabled) return;\n onChangeHandler(item.key);\n handleOpenChange(false);\n }\n\n const navigation = { isDisabled: (index: number) => !!items[index].disabled };\n\n /** Jumps to the option whose label starts with what was typed, as a native `<select>` does. */\n function handleTypeahead(key: string) {\n const now = Date.now();\n const buffer = now - typeahead.current.at > TYPEAHEAD_RESET_MS ? key : typeahead.current.buffer + key;\n typeahead.current = { buffer, at: now };\n\n const needle = buffer.toLowerCase();\n const match = items.findIndex(\n (item) => !item.disabled && typeof item.label === 'string' && item.label.toLowerCase().startsWith(needle),\n );\n if (match === -1) return;\n if (isOpen) setActiveIndex(match);\n else select(items[match]);\n }\n\n function handleKeyDown(event: React.KeyboardEvent) {\n if (!isInteractive) return;\n\n if (!isOpen) {\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp' || event.key === 'Enter' || event.key === ' ') {\n // Without this the browser turns the key into a click on the trigger, which\n // Popover would read as a second toggle and close the list again.\n event.preventDefault();\n handleOpenChange(true);\n } else if (event.key.length === 1) {\n handleTypeahead(event.key);\n }\n return;\n }\n\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n setActiveIndex(nextIndex(activeIndex, event.key === 'ArrowDown' ? 1 : -1, items.length, navigation));\n } else if (event.key === 'Home' || event.key === 'End') {\n event.preventDefault();\n setActiveIndex(edgeIndex(event.key === 'Home' ? 'first' : 'last', items.length, navigation));\n } else if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n const item = activeIndex === null ? undefined : items[activeIndex];\n if (item) select(item);\n else handleOpenChange(false);\n } else if (event.key === 'Escape' || event.key === 'Tab') {\n handleOpenChange(false);\n } else if (event.key.length === 1) {\n handleTypeahead(event.key);\n }\n }\n\n let dropdownLabel = dropdownLabelProp;\n const hasErr = hasError();\n\n items.forEach((item: DropdownItem) => {\n if (item.key === currentValue) {\n dropdownLabel = item.label as string;\n }\n });\n\n const displayValue = dropdownLabel || currentValue;\n const isDisplayBlank = displayValue == null || (typeof displayValue === 'string' && displayValue.trim() === '');\n\n const requiredMark = computedIsRequired() ? <span className={styles.requiredMark}>*</span> : null;\n const editedLabel = label && computedIsRequired() ? `${label} *` : label;\n\n const showFocus = isFocused && !readOnly;\n\n return (\n <div\n {...rest}\n className={cx(className, classNames?.root, styles.dropdownLabel, styles.dropdown, {\n [styles.dropdownLabelColumn]: !isRow,\n [styles.dropdownWithLabel]: useMatLabelStyle && !!label,\n })}\n style={{ ...style, ...slotStyles?.root }}\n onMouseEnter={onMouseEnterHandler}\n onMouseLeave={onMouseLeaveHandler}\n >\n <div>\n <Popover\n // Popover owns opening and closing, including the outside click, Escape and the\n // click on the field itself. The trigger only has to say what it wants.\n isOpen={isOpen}\n onOpenChange={handleOpenChange}\n disabled={!isInteractive}\n placement={PopoverPlacement.Bottom}\n align={PopoverAlign.Start}\n offset={0}\n withArrow={false}\n constrainWidth={false}\n width='trigger'\n // The focus stays on the trigger while the list is open, because that is what\n // `aria-activedescendant` points from.\n focusOnOpen={false}\n presentation={presentation}\n classNames={{ panel: styles.dropdownPanel }}\n content={\n <div\n ref={anchorRef}\n // A sheet's own panel does the clipping, and spans the screen rather than\n // sizing itself to the field.\n className={cx(isSheet ? styles.dropdownInputListSheet : styles.dropdownInputListAnim, {\n [styles.dropdownInputListAnimWider]: isDropdownListWider && !isSheet,\n })}\n >\n <div\n ref={listRef}\n id={listboxId}\n role='listbox'\n aria-label={label || placeholder || undefined}\n aria-busy={showScrollSpinner || undefined}\n className={cx(styles.dropdownInputList, isSheet && sheetStyles.sheetScroll, classNames?.list)}\n style={slotStyles?.list}\n >\n {items.map((item: DropdownItem, index: number) => (\n <div\n key={item.key}\n id={optionId(index)}\n role='option'\n aria-selected={item.key === currentValue}\n aria-disabled={item.disabled || undefined}\n style={slotStyles?.option}\n className={cx(styles.dropdownInputListItem, classNames?.option, {\n [styles.dropdownInputListItemActive]: activeIndex === index,\n [styles.dropdownInputListItemSelected]: item.key === currentValue,\n [styles.dropdownInputListItemDisabled]: !!item.disabled,\n })}\n // Selecting must not move the focus off the trigger, which is what\n // `aria-activedescendant` is anchored to.\n onMouseDown={(event) => event.preventDefault()}\n onMouseEnter={() => setActiveIndex(index)}\n onClick={() => select(item)}\n >\n {item.label}\n </div>\n ))}\n <div ref={sentinelRef} className={styles.dropdownSentinel} aria-hidden='true' />\n {showScrollSpinner && (\n <div className={styles.dropdownLoadMore} aria-hidden='true'>\n <Loader size='1.125em' />\n </div>\n )}\n </div>\n </div>\n }\n >\n <div\n ref={triggerRef}\n onFocus={onFocusHandler}\n onBlur={onBlurHandler}\n className={cx(\n styles.dropdownInput,\n {\n [styles.dropdownInputPlain]: plainStyle,\n [styles.dropdownInputReadOnly]: readOnly && !disabled,\n },\n classNames?.field,\n )}\n style={slotStyles?.field}\n >\n <button\n tabIndex={tabIndex}\n type='button'\n disabled={disabled}\n // A select-only combobox: the accessible name comes from the field's label,\n // and the element's own content is read as its value. A plain `button` role\n // would announce the value as the name and never mention the label.\n role='combobox'\n aria-haspopup='listbox'\n aria-expanded={isOpen}\n aria-controls={listboxId}\n aria-activedescendant={isOpen && activeIndex !== null ? optionId(activeIndex) : undefined}\n aria-labelledby={label ? labelId : undefined}\n aria-label={label ? undefined : placeholder || undefined}\n aria-readonly={readOnly || undefined}\n className={cx(\n styles.dropdownInputButton,\n {\n [styles.dropdownInputButtonDisabled]: disabled,\n [styles.dropdownInputButtonReadOnly]: readOnly && !disabled,\n },\n classNames?.control,\n )}\n style={{ ...(isDropdownListWider ? { padding: '6px', border: 'none' } : {}), ...slotStyles?.control }}\n onKeyDown={handleKeyDown}\n onClick={onClick}\n ref={reactRef}\n >\n {/* Non-breaking space when blank (same text-node layout as a\n real value) so the button keeps a full line of height. */}\n {isDisplayBlank ? ' ' : displayValue}\n </button>\n <button\n type='button'\n tabIndex={-1}\n disabled={disabled}\n // The chevron repeats what the combobox beside it already does, so it is a\n // pointer affordance only. Announcing it would offer the same control twice.\n aria-hidden='true'\n className={cx(styles.dropdownInputButtonIcon, classNames?.icon)}\n style={slotStyles?.icon}\n // Keeping the focus where it is, rather than letting mousedown move it here:\n // the trigger is what `aria-activedescendant` points from, and the click\n // still reaches Popover by bubbling.\n onMouseDown={(event: React.MouseEvent<HTMLButtonElement>) => event.preventDefault()}\n onClick={onClick}\n >\n {!disabled && !readOnly && (\n <FontAwesomeIcon\n icon={dropdownIcon}\n className={cx(styles.dropdownInputButtonIconArrow, {\n [styles.dropdownInputButtonIconArrowOpen]: isOpen,\n })}\n />\n )}\n </button>\n <fieldset\n className={cx(styles.dropdownFieldset, {\n [styles.dropdownBorderPlain]: plainStyle,\n [styles.dropdownBorderFocused]: showFocus,\n [styles.dropdownBorderInvalid]: hasErr,\n [styles.dropdownBorderError]: hasErr,\n })}\n >\n <legend\n className={cx(styles.dropdownFieldsetLegend, {\n [styles.dropdownFieldsetLegendActive]:\n useMatLabelStyle && (alwaysFloatLabel || showFocus || !isDisplayBlank),\n })}\n >\n <span>{editedLabel}</span>\n </legend>\n </fieldset>\n {useMatLabelStyle && label && (\n <div\n id={labelId}\n className={cx(\n styles.dropdownMatLabel,\n {\n [styles.dropdownMatLabelFloating]: alwaysFloatLabel || showFocus || !isDisplayBlank,\n },\n // The floating and the stacked label are two renderings of\n // the same thing, so one slot reaches whichever is in use.\n classNames?.label,\n )}\n style={slotStyles?.label}\n >\n {label}\n {requiredMark}\n </div>\n )}\n {useMatLabelStyle && placeholder && (\n <div\n className={cx(\n styles.dropdownPlaceholder,\n showFocus && !dropdownLabel && styles.dropdownPlaceholderShown,\n )}\n >\n {placeholder}\n </div>\n )}\n </div>\n </Popover>\n </div>\n {label && !useMatLabelStyle && (\n <div id={labelId} className={cx(styles.dropdownLabelText, classNames?.label)} style={slotStyles?.label}>\n {label}\n {requiredMark}\n </div>\n )}\n <Tooltip\n variant='error'\n message={formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || ''}\n isVisible={hasErr && (isFocused || isHovered)}\n />\n </div>\n );\n}\n\nexport default Dropdown;\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use client";var e=`required-mark_fy7Fm`,t=`dropdown_XTshY`,n=`dropdown-withLabel_Izzz3`,r=`dropdown-label_TIpeK`,i=`dropdown-label-column_cMCBc`,a=`dropdown-label-text_u-UKq`,o=`dropdown-input_WiPcb`,s=`dropdown-input-plain_Ap12N`,c=`dropdown-input-readOnly_3GWSO`,l=`dropdown-input-button_ziKKi`,u=`dropdown-input-button-disabled_JEaO-`,d=`dropdown-input-button-readOnly_QhBYY`,f=`dropdown-input-button-icon_jl7EY`,p=`dropdown-input-button-icon-arrow_SFJbx`,m=`dropdown-input-button-icon-arrow-open_8yQbZ`,h=`dropdown-input-list-anim_knSMT`,g=`dropdown-input-list-anim-wider_-YyLJ`,_=`dropdown-input-list_JnMbA`,v=`dropdown-input-list-item_EAy5L`,y=`dropdown-input-list-item-active_0dhia`,b=`dropdown-input-list-item-selected_hShJf`,x=`dropdown-input-list-item-disabled_3r-Yn`,S=`dropdown-fieldset_wQwTf`,C=`dropdown-fieldset-legend_a1M4L`,w=`dropdown-fieldset-legend-active_bnR5F`,T=`dropdown-placeholder_QYMPZ`,E=`dropdown-placeholder-shown_-IkNH`,D=`fadeIn_oMJZj`,O=`dropdown-matLabel_iHQnt`,k=`dropdown-matLabel-floating_zY51S`,A=`dropdown-border-focused_Te-ER`,j=`dropdown-border-plain_Bbh-3`,M=`dropdown-border-invalid_ySVaS`,N=`dropdown-border-error_-JB33`,P=`dropdownPanel_RYxpN`,F=`dropdown-sentinel_TVDF9`,I=`dropdown-load-more_UQvzV`,L={"required-mark":`required-mark_fy7Fm`,requiredMark:e,dropdown:t,"dropdown-withLabel":`dropdown-withLabel_Izzz3`,dropdownWithLabel:n,"dropdown-label":`dropdown-label_TIpeK`,dropdownLabel:r,"dropdown-label-column":`dropdown-label-column_cMCBc`,dropdownLabelColumn:i,"dropdown-label-text":`dropdown-label-text_u-UKq`,dropdownLabelText:a,"dropdown-input":`dropdown-input_WiPcb`,dropdownInput:o,"dropdown-input-plain":`dropdown-input-plain_Ap12N`,dropdownInputPlain:s,"dropdown-input-readOnly":`dropdown-input-readOnly_3GWSO`,dropdownInputReadOnly:c,"dropdown-input-button":`dropdown-input-button_ziKKi`,dropdownInputButton:l,"dropdown-input-button-disabled":`dropdown-input-button-disabled_JEaO-`,dropdownInputButtonDisabled:u,"dropdown-input-button-readOnly":`dropdown-input-button-readOnly_QhBYY`,dropdownInputButtonReadOnly:d,"dropdown-input-button-icon":`dropdown-input-button-icon_jl7EY`,dropdownInputButtonIcon:f,"dropdown-input-button-icon-arrow":`dropdown-input-button-icon-arrow_SFJbx`,dropdownInputButtonIconArrow:p,"dropdown-input-button-icon-arrow-open":`dropdown-input-button-icon-arrow-open_8yQbZ`,dropdownInputButtonIconArrowOpen:m,"dropdown-input-list-anim":`dropdown-input-list-anim_knSMT`,dropdownInputListAnim:h,"dropdown-input-list-anim-wider":`dropdown-input-list-anim-wider_-YyLJ`,dropdownInputListAnimWider:g,"dropdown-input-list":`dropdown-input-list_JnMbA`,dropdownInputList:_,"dropdown-input-list-item":`dropdown-input-list-item_EAy5L`,dropdownInputListItem:v,"dropdown-input-list-item-active":`dropdown-input-list-item-active_0dhia`,dropdownInputListItemActive:y,"dropdown-input-list-item-selected":`dropdown-input-list-item-selected_hShJf`,dropdownInputListItemSelected:b,"dropdown-input-list-item-disabled":`dropdown-input-list-item-disabled_3r-Yn`,dropdownInputListItemDisabled:x,"dropdown-fieldset":`dropdown-fieldset_wQwTf`,dropdownFieldset:S,"dropdown-fieldset-legend":`dropdown-fieldset-legend_a1M4L`,dropdownFieldsetLegend:C,"dropdown-fieldset-legend-active":`dropdown-fieldset-legend-active_bnR5F`,dropdownFieldsetLegendActive:w,"dropdown-placeholder":`dropdown-placeholder_QYMPZ`,dropdownPlaceholder:T,"dropdown-placeholder-shown":`dropdown-placeholder-shown_-IkNH`,dropdownPlaceholderShown:E,fadeIn:D,"dropdown-matLabel":`dropdown-matLabel_iHQnt`,dropdownMatLabel:O,"dropdown-matLabel-floating":`dropdown-matLabel-floating_zY51S`,dropdownMatLabelFloating:k,"dropdown-border-focused":`dropdown-border-focused_Te-ER`,dropdownBorderFocused:A,"dropdown-border-plain":`dropdown-border-plain_Bbh-3`,dropdownBorderPlain:j,"dropdown-border-invalid":`dropdown-border-invalid_ySVaS`,dropdownBorderInvalid:M,"dropdown-border-error":`dropdown-border-error_-JB33`,dropdownBorderError:N,dropdownPanel:P,"dropdown-sentinel":`dropdown-sentinel_TVDF9`,dropdownSentinel:F,"dropdown-load-more":`dropdown-load-more_UQvzV`,dropdownLoadMore:I};export{L as default,t as dropdown,N as dropdownBorderError,A as dropdownBorderFocused,M as dropdownBorderInvalid,j as dropdownBorderPlain,S as dropdownFieldset,C as dropdownFieldsetLegend,w as dropdownFieldsetLegendActive,o as dropdownInput,l as dropdownInputButton,u as dropdownInputButtonDisabled,f as dropdownInputButtonIcon,p as dropdownInputButtonIconArrow,m as dropdownInputButtonIconArrowOpen,d as dropdownInputButtonReadOnly,_ as dropdownInputList,h as dropdownInputListAnim,g as dropdownInputListAnimWider,v as dropdownInputListItem,y as dropdownInputListItemActive,x as dropdownInputListItemDisabled,b as dropdownInputListItemSelected,s as dropdownInputPlain,c as dropdownInputReadOnly,r as dropdownLabel,i as dropdownLabelColumn,a as dropdownLabelText,I as dropdownLoadMore,O as dropdownMatLabel,k as dropdownMatLabelFloating,P as dropdownPanel,T as dropdownPlaceholder,E as dropdownPlaceholderShown,F as dropdownSentinel,n as dropdownWithLabel,D as fadeIn,e as requiredMark};
1
+ "use client";var e=`required-mark_fy7Fm`,t=`dropdown_XTshY`,n=`dropdown-withLabel_Izzz3`,r=`dropdown-label_TIpeK`,i=`dropdown-label-column_cMCBc`,a=`dropdown-label-text_u-UKq`,o=`dropdown-input_WiPcb`,s=`dropdown-input-plain_Ap12N`,c=`dropdown-input-readOnly_3GWSO`,l=`dropdown-input-button_ziKKi`,u=`dropdown-input-button-disabled_JEaO-`,d=`dropdown-input-button-readOnly_QhBYY`,f=`dropdown-input-button-icon_jl7EY`,p=`dropdown-input-button-icon-arrow_SFJbx`,m=`dropdown-input-button-icon-arrow-open_8yQbZ`,h=`dropdown-input-list-anim_knSMT`,g=`dropdown-input-list-anim-wider_-YyLJ`,_=`dropdown-input-list-sheet_4gRYD`,v=`dropdown-input-list_JnMbA`,y=`dropdown-input-list-item_EAy5L`,b=`dropdown-input-list-item-active_0dhia`,x=`dropdown-input-list-item-selected_hShJf`,S=`dropdown-input-list-item-disabled_3r-Yn`,C=`dropdown-fieldset_wQwTf`,w=`dropdown-fieldset-legend_a1M4L`,T=`dropdown-fieldset-legend-active_bnR5F`,E=`dropdown-placeholder_QYMPZ`,D=`dropdown-placeholder-shown_-IkNH`,O=`fadeIn_oMJZj`,k=`dropdown-matLabel_iHQnt`,A=`dropdown-matLabel-floating_zY51S`,j=`dropdown-border-focused_Te-ER`,M=`dropdown-border-plain_Bbh-3`,N=`dropdown-border-invalid_ySVaS`,P=`dropdown-border-error_-JB33`,F=`dropdownPanel_RYxpN`,I=`dropdown-sentinel_TVDF9`,L=`dropdown-load-more_UQvzV`,R={"required-mark":`required-mark_fy7Fm`,requiredMark:e,dropdown:t,"dropdown-withLabel":`dropdown-withLabel_Izzz3`,dropdownWithLabel:n,"dropdown-label":`dropdown-label_TIpeK`,dropdownLabel:r,"dropdown-label-column":`dropdown-label-column_cMCBc`,dropdownLabelColumn:i,"dropdown-label-text":`dropdown-label-text_u-UKq`,dropdownLabelText:a,"dropdown-input":`dropdown-input_WiPcb`,dropdownInput:o,"dropdown-input-plain":`dropdown-input-plain_Ap12N`,dropdownInputPlain:s,"dropdown-input-readOnly":`dropdown-input-readOnly_3GWSO`,dropdownInputReadOnly:c,"dropdown-input-button":`dropdown-input-button_ziKKi`,dropdownInputButton:l,"dropdown-input-button-disabled":`dropdown-input-button-disabled_JEaO-`,dropdownInputButtonDisabled:u,"dropdown-input-button-readOnly":`dropdown-input-button-readOnly_QhBYY`,dropdownInputButtonReadOnly:d,"dropdown-input-button-icon":`dropdown-input-button-icon_jl7EY`,dropdownInputButtonIcon:f,"dropdown-input-button-icon-arrow":`dropdown-input-button-icon-arrow_SFJbx`,dropdownInputButtonIconArrow:p,"dropdown-input-button-icon-arrow-open":`dropdown-input-button-icon-arrow-open_8yQbZ`,dropdownInputButtonIconArrowOpen:m,"dropdown-input-list-anim":`dropdown-input-list-anim_knSMT`,dropdownInputListAnim:h,"dropdown-input-list-anim-wider":`dropdown-input-list-anim-wider_-YyLJ`,dropdownInputListAnimWider:g,"dropdown-input-list-sheet":`dropdown-input-list-sheet_4gRYD`,dropdownInputListSheet:_,"dropdown-input-list":`dropdown-input-list_JnMbA`,dropdownInputList:v,"dropdown-input-list-item":`dropdown-input-list-item_EAy5L`,dropdownInputListItem:y,"dropdown-input-list-item-active":`dropdown-input-list-item-active_0dhia`,dropdownInputListItemActive:b,"dropdown-input-list-item-selected":`dropdown-input-list-item-selected_hShJf`,dropdownInputListItemSelected:x,"dropdown-input-list-item-disabled":`dropdown-input-list-item-disabled_3r-Yn`,dropdownInputListItemDisabled:S,"dropdown-fieldset":`dropdown-fieldset_wQwTf`,dropdownFieldset:C,"dropdown-fieldset-legend":`dropdown-fieldset-legend_a1M4L`,dropdownFieldsetLegend:w,"dropdown-fieldset-legend-active":`dropdown-fieldset-legend-active_bnR5F`,dropdownFieldsetLegendActive:T,"dropdown-placeholder":`dropdown-placeholder_QYMPZ`,dropdownPlaceholder:E,"dropdown-placeholder-shown":`dropdown-placeholder-shown_-IkNH`,dropdownPlaceholderShown:D,fadeIn:O,"dropdown-matLabel":`dropdown-matLabel_iHQnt`,dropdownMatLabel:k,"dropdown-matLabel-floating":`dropdown-matLabel-floating_zY51S`,dropdownMatLabelFloating:A,"dropdown-border-focused":`dropdown-border-focused_Te-ER`,dropdownBorderFocused:j,"dropdown-border-plain":`dropdown-border-plain_Bbh-3`,dropdownBorderPlain:M,"dropdown-border-invalid":`dropdown-border-invalid_ySVaS`,dropdownBorderInvalid:N,"dropdown-border-error":`dropdown-border-error_-JB33`,dropdownBorderError:P,dropdownPanel:F,"dropdown-sentinel":`dropdown-sentinel_TVDF9`,dropdownSentinel:I,"dropdown-load-more":`dropdown-load-more_UQvzV`,dropdownLoadMore:L};export{R as default,t as dropdown,P as dropdownBorderError,j as dropdownBorderFocused,N as dropdownBorderInvalid,M as dropdownBorderPlain,C as dropdownFieldset,w as dropdownFieldsetLegend,T as dropdownFieldsetLegendActive,o as dropdownInput,l as dropdownInputButton,u as dropdownInputButtonDisabled,f as dropdownInputButtonIcon,p as dropdownInputButtonIconArrow,m as dropdownInputButtonIconArrowOpen,d as dropdownInputButtonReadOnly,v as dropdownInputList,h as dropdownInputListAnim,g as dropdownInputListAnimWider,y as dropdownInputListItem,b as dropdownInputListItemActive,S as dropdownInputListItemDisabled,x as dropdownInputListItemSelected,_ as dropdownInputListSheet,s as dropdownInputPlain,c as dropdownInputReadOnly,r as dropdownLabel,i as dropdownLabelColumn,a as dropdownLabelText,L as dropdownLoadMore,k as dropdownMatLabel,A as dropdownMatLabelFloating,F as dropdownPanel,E as dropdownPlaceholder,D as dropdownPlaceholderShown,I as dropdownSentinel,n as dropdownWithLabel,O as fadeIn,e as requiredMark};
2
2
  //# sourceMappingURL=dropdown.module.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"dropdown.module.mjs","names":[],"sources":["../../../../package/common/dropdown/dropdown.module.pcss"],"sourcesContent":[".required-mark {\n padding: 0 0 0 4px;\n color: var(--required-mark-color, var(--error-color));\n}\n\n.dropdown {\n position: relative;\n\n /* Nothing inside gives the root a width, so as a flex or grid item it collapsed to the chevron\n and drew its border through the label. `input` and `multiSelect` say the same. */\n width: 100%;\n\n --dropdown-notch-height: 1.75em;\n --field-label-offset: 0px;\n\n /* Input's --field-label-offset, for the same reason: a floating label hangs into the field. */\n &-withLabel {\n --field-label-offset: 1px;\n }\n\n /* See input.module.pcss for why this exists: the visible border lives on `-input`/`-fieldset`\n below, not this root, so a wrapping ButtonGroup's --group-radius-* custom properties (which\n inherit through any nesting depth, unlike border-radius itself) are what let this component\n flatten correctly when it sits inside a ButtonGroup. */\n --dropdown-radius: var(--group-radius-tl, var(--default-border-radius))\n var(--group-radius-tr, var(--default-border-radius)) var(--group-radius-br, var(--default-border-radius))\n var(--group-radius-bl, var(--default-border-radius));\n\n &-label {\n display: flex;\n\n &-column {\n flex-direction: column;\n }\n\n &-text {\n white-space: nowrap;\n margin-right: 5px;\n margin-bottom: 5px;\n display: flex;\n align-items: center;\n color: var(--text-color);\n font-size: 12px;\n position: absolute;\n top: -15px;\n }\n }\n\n &-input {\n position: relative;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n background-color: var(--input-background);\n border-radius: var(--dropdown-radius);\n\n &-plain {\n background: transparent;\n }\n\n &-readOnly {\n cursor: default;\n }\n\n &:focus-within {\n outline: none;\n }\n\n &-button {\n padding: calc(7px + var(--field-label-offset)) 20% calc(7px - var(--field-label-offset)) 7.5px !important;\n background-color: transparent;\n border: none;\n font-family: inherit;\n font-size: inherit;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n text-align: left;\n width: 100%;\n line-height: 1.25;\n color: var(--text-color);\n\n &-disabled {\n background-color: var(--disabled-input-color);\n color: var(--disabled-font-color);\n border-radius: var(--dropdown-radius);\n }\n\n &-readOnly {\n cursor: default;\n }\n\n &:focus {\n outline: none;\n }\n\n &-icon {\n float: right;\n background-color: transparent;\n border: none;\n position: absolute;\n outline: none;\n right: var(--spacing-s);\n top: calc(50% + var(--field-label-offset));\n transform: translateY(-50%);\n color: var(--text-color);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n &-arrow {\n transition: transform 0.25s;\n pointer-events: none;\n\n &-open {\n transform: rotate(180deg);\n }\n }\n }\n }\n\n /* Chrome, placement and the portal come from the Popover this renders inside.\n What is left is the clipping that keeps the first and last row inside the\n panel's rounded corners. */\n &-list-anim {\n overflow: hidden;\n border-radius: inherit;\n\n &-wider {\n width: max-content !important;\n }\n }\n\n &-list {\n width: 100%;\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n max-height: 10em;\n list-style-type: none;\n\n &-item {\n border: none;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n background-color: var(--background-modal);\n padding: 0.5em;\n text-align: left;\n border-radius: 0;\n transition: background-color 0.3s;\n\n &-active {\n background-color: var(--background-accent);\n }\n\n &-selected {\n font-weight: 500;\n }\n\n &:focus {\n outline: none;\n }\n\n &:hover {\n background-color: var(--background-accent);\n }\n\n &-disabled {\n background-color: var(--disabled-input-color);\n color: var(--disabled-font-color);\n cursor: not-allowed;\n\n &:hover {\n background-color: var(--disabled-input-color);\n }\n }\n }\n }\n }\n\n &-fieldset {\n position: absolute;\n width: 100%;\n margin: 0;\n box-sizing: border-box;\n border-radius: var(--dropdown-radius);\n font-size: 0.8em;\n inset: calc(var(--dropdown-notch-height) / -2) 0 0;\n padding-left: 8px;\n border-width: 1px;\n border-style: solid;\n border-color: var(--border-color);\n pointer-events: none;\n user-select: none;\n\n &-legend {\n display: block;\n\n /* Not taken from the text: an empty span gets no line box, which raised a label-less\n field's border half a notch. */\n height: var(--dropdown-notch-height);\n max-width: 0;\n transition: max-width 0.2s;\n white-space: nowrap;\n padding: 0;\n\n &-active {\n max-width: 300px;\n }\n\n span {\n line-height: var(--dropdown-notch-height);\n padding: 0 5px;\n opacity: 0;\n }\n\n /* With an empty label the span would still reserve its padding, leaving a gap in\n the fieldset's top border. Collapse it so the notch closes when there's no label. */\n span:empty {\n padding: 0;\n }\n }\n }\n\n &-placeholder {\n position: absolute;\n top: calc(50% + var(--field-label-offset));\n left: 7.5px;\n transform: translateY(-50%);\n transition: all 0.2s;\n opacity: 0;\n\n &-shown {\n animation: 0.2s fadeIn 0.3s forwards;\n }\n }\n\n &-matLabel {\n position: absolute;\n transition: all 0.3s;\n left: 7.5px;\n right: 0;\n top: calc(50% + var(--field-label-offset));\n transform: translateY(-50%);\n font-size: 1em;\n color: var(--input-label-resting-color, var(--text-light));\n pointer-events: none;\n user-select: none;\n\n &-floating {\n inset: 0 0 auto 14px;\n font-size: 0.8em;\n color: var(--input-label-floating-color, var(--text-color));\n transform: translateY(-50%);\n }\n }\n\n @keyframes fadeIn {\n from {\n opacity: 0;\n }\n\n to {\n opacity: 0.5;\n }\n }\n\n &-border {\n &-focused {\n outline-color: var(--focus-border-color, var(--primary-color));\n border-color: var(--focus-border-color, var(--primary-color));\n }\n\n &-plain:not(&-focused) {\n border-color: transparent;\n outline-color: transparent;\n }\n\n &-invalid {\n outline-color: var(--error-border-color);\n border-color: var(--error-border-color);\n }\n\n &-invalid:hover {\n outline-color: var(--error-border-color);\n border-color: var(--error-border-color);\n }\n\n &-error {\n outline-color: var(--error-border-color);\n border-color: var(--error-border-color);\n }\n }\n}\n\n/* The list brings its own padding and its rows reach the panel edge. */\n.dropdownPanel {\n padding: 0;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dropdown-matLabel,\n .dropdown-placeholder,\n .dropdown-input-button-icon-arrow {\n transition: none;\n }\n}\n\n/* The sentinel the infinite-scroll hook watches, and the spinner shown while a page loads.\n These came with `InfiniteBlock` when the list rendered through it; the list is its own scroll\n container now, so they live here. */\n.dropdown-sentinel {\n height: 1px;\n}\n\n.dropdown-load-more {\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 4px 0;\n}\n"],"mappings":""}
1
+ {"version":3,"file":"dropdown.module.mjs","names":[],"sources":["../../../../package/common/dropdown/dropdown.module.pcss"],"sourcesContent":[".required-mark {\n padding: 0 0 0 4px;\n color: var(--required-mark-color, var(--error-color));\n}\n\n.dropdown {\n position: relative;\n\n /* Nothing inside gives the root a width, so as a flex or grid item it collapsed to the chevron\n and drew its border through the label. `input` and `multiSelect` say the same. */\n width: 100%;\n\n --dropdown-notch-height: 1.75em;\n --field-label-offset: 0px;\n\n /* Input's --field-label-offset, for the same reason: a floating label hangs into the field. */\n &-withLabel {\n --field-label-offset: 1px;\n }\n\n /* See input.module.pcss for why this exists: the visible border lives on `-input`/`-fieldset`\n below, not this root, so a wrapping ButtonGroup's --group-radius-* custom properties (which\n inherit through any nesting depth, unlike border-radius itself) are what let this component\n flatten correctly when it sits inside a ButtonGroup. */\n --dropdown-radius: var(--group-radius-tl, var(--default-border-radius))\n var(--group-radius-tr, var(--default-border-radius)) var(--group-radius-br, var(--default-border-radius))\n var(--group-radius-bl, var(--default-border-radius));\n\n &-label {\n display: flex;\n\n &-column {\n flex-direction: column;\n }\n\n &-text {\n white-space: nowrap;\n margin-right: 5px;\n margin-bottom: 5px;\n display: flex;\n align-items: center;\n color: var(--text-color);\n font-size: 12px;\n position: absolute;\n top: -15px;\n }\n }\n\n &-input {\n position: relative;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n background-color: var(--input-background);\n border-radius: var(--dropdown-radius);\n\n &-plain {\n background: transparent;\n }\n\n &-readOnly {\n cursor: default;\n }\n\n &:focus-within {\n outline: none;\n }\n\n &-button {\n padding: calc(7px + var(--field-label-offset)) 20% calc(7px - var(--field-label-offset)) 7.5px !important;\n background-color: transparent;\n border: none;\n font-family: inherit;\n font-size: inherit;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n text-align: left;\n width: 100%;\n line-height: 1.25;\n color: var(--text-color);\n\n &-disabled {\n background-color: var(--disabled-input-color);\n color: var(--disabled-font-color);\n border-radius: var(--dropdown-radius);\n }\n\n &-readOnly {\n cursor: default;\n }\n\n &:focus {\n outline: none;\n }\n\n &-icon {\n float: right;\n background-color: transparent;\n border: none;\n position: absolute;\n outline: none;\n right: var(--spacing-s);\n top: calc(50% + var(--field-label-offset));\n transform: translateY(-50%);\n color: var(--text-color);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n &-arrow {\n transition: transform 0.25s;\n pointer-events: none;\n\n &-open {\n transform: rotate(180deg);\n }\n }\n }\n }\n\n /* Chrome, placement and the portal come from the Popover this renders inside.\n What is left is the clipping that keeps the first and last row inside the\n panel's rounded corners. */\n &-list-anim {\n overflow: hidden;\n border-radius: inherit;\n\n &-wider {\n width: max-content !important;\n }\n }\n\n /* Passes the sheet's height on to the list, so that the list scrolls rather than the panel:\n it is what keyboard navigation scrolls and what loading more watches. */\n &-list-sheet {\n display: flex;\n flex-direction: column;\n min-height: 0;\n }\n\n &-list {\n width: 100%;\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n max-height: 10em;\n list-style-type: none;\n\n &-item {\n border: none;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n background-color: var(--background-modal);\n padding: 0.5em;\n text-align: left;\n border-radius: 0;\n transition: background-color 0.3s;\n\n &-active {\n background-color: var(--background-accent);\n }\n\n &-selected {\n font-weight: 500;\n }\n\n &:focus {\n outline: none;\n }\n\n &:hover {\n background-color: var(--background-accent);\n }\n\n &-disabled {\n background-color: var(--disabled-input-color);\n color: var(--disabled-font-color);\n cursor: not-allowed;\n\n &:hover {\n background-color: var(--disabled-input-color);\n }\n }\n }\n }\n }\n\n &-fieldset {\n position: absolute;\n width: 100%;\n margin: 0;\n box-sizing: border-box;\n border-radius: var(--dropdown-radius);\n font-size: 0.8em;\n inset: calc(var(--dropdown-notch-height) / -2) 0 0;\n padding-left: 8px;\n border-width: 1px;\n border-style: solid;\n border-color: var(--border-color);\n pointer-events: none;\n user-select: none;\n\n &-legend {\n display: block;\n\n /* Not taken from the text: an empty span gets no line box, which raised a label-less\n field's border half a notch. */\n height: var(--dropdown-notch-height);\n max-width: 0;\n transition: max-width 0.2s;\n white-space: nowrap;\n padding: 0;\n\n &-active {\n max-width: 300px;\n }\n\n span {\n line-height: var(--dropdown-notch-height);\n padding: 0 5px;\n opacity: 0;\n }\n\n /* With an empty label the span would still reserve its padding, leaving a gap in\n the fieldset's top border. Collapse it so the notch closes when there's no label. */\n span:empty {\n padding: 0;\n }\n }\n }\n\n &-placeholder {\n position: absolute;\n top: calc(50% + var(--field-label-offset));\n left: 7.5px;\n transform: translateY(-50%);\n transition: all 0.2s;\n opacity: 0;\n\n &-shown {\n animation: 0.2s fadeIn 0.3s forwards;\n }\n }\n\n &-matLabel {\n position: absolute;\n transition: all 0.3s;\n left: 7.5px;\n right: 0;\n top: calc(50% + var(--field-label-offset));\n transform: translateY(-50%);\n font-size: 1em;\n color: var(--input-label-resting-color, var(--text-light));\n pointer-events: none;\n user-select: none;\n\n &-floating {\n inset: 0 0 auto 14px;\n font-size: 0.8em;\n color: var(--input-label-floating-color, var(--text-color));\n transform: translateY(-50%);\n }\n }\n\n @keyframes fadeIn {\n from {\n opacity: 0;\n }\n\n to {\n opacity: 0.5;\n }\n }\n\n &-border {\n &-focused {\n outline-color: var(--focus-border-color, var(--primary-color));\n border-color: var(--focus-border-color, var(--primary-color));\n }\n\n &-plain:not(&-focused) {\n border-color: transparent;\n outline-color: transparent;\n }\n\n &-invalid {\n outline-color: var(--error-border-color);\n border-color: var(--error-border-color);\n }\n\n &-invalid:hover {\n outline-color: var(--error-border-color);\n border-color: var(--error-border-color);\n }\n\n &-error {\n outline-color: var(--error-border-color);\n border-color: var(--error-border-color);\n }\n }\n}\n\n/* The list brings its own padding and its rows reach the panel edge. */\n.dropdownPanel {\n padding: 0;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dropdown-matLabel,\n .dropdown-placeholder,\n .dropdown-input-button-icon-arrow {\n transition: none;\n }\n}\n\n/* The sentinel the infinite-scroll hook watches, and the spinner shown while a page loads.\n These came with `InfiniteBlock` when the list rendered through it; the list is its own scroll\n container now, so they live here. */\n.dropdown-sentinel {\n height: 1px;\n}\n\n/* An option's padding around a loader one line of text high, so the row is an option's height. */\n.dropdown-load-more {\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 0.5em 0;\n}\n"],"mappings":""}
@@ -1,2 +1,2 @@
1
- "use client";import{useEffect as e,useRef as t,useState as n}from"react";var r=3e3;function i(i,a,o){let[s,c]=n(!1),l=t(!1),u=t(null),d=t(a);return e(()=>{d.current=a}),e(()=>{let e=i.current,t=u.current;if(o||!e||!t)return;let n=!1,a=0,s=()=>{p.unobserve(t),p.observe(t)},f=()=>{if(l.current)return;l.current=!0,c(!0);let e=e=>{l.current=!1,c(!1),!n&&(e?s():a=window.setTimeout(s,r))};d.current().then(()=>e(!0),()=>e(!1))},p=new IntersectionObserver(([e])=>{e.isIntersecting&&f()},{root:e});return p.observe(t),()=>{n=!0,window.clearTimeout(a),p.disconnect()}},[i,o]),{isLoading:s,sentinelRef:u}}export{i as useInfiniteScroll};
1
+ "use client";import{useEffect as e,useLayoutEffect as t,useRef as n,useState as r}from"react";var i=3e3,a=`100px 0px`;function o(o,s){let[c,l]=r(!1),u=n(!1),d=n(null),f=n(()=>{}),p=n(0),m=n(null),h=n(s);e(()=>{h.current=s});let g=s!==void 0;return t(()=>{let e=m.current;c||e===null||d.current?.parentElement?.childElementCount!==e&&(m.current=null,f.current())}),e(()=>{let e=o.current,t=d.current;if(!g||!e||!t)return;let n=()=>{s.unobserve(t),s.observe(t)},r=()=>{let e=h.current;e&&!u.current&&(u.current=!0,m.current=t.parentElement?.childElementCount??null,l(!0),e().then(()=>{u.current=!1,l(!1)},()=>{u.current=!1,m.current=null,l(!1),p.current=window.setTimeout(()=>f.current(),i)}))},s=new IntersectionObserver(([e])=>{e.isIntersecting&&r()},{root:e,rootMargin:a});return s.observe(t),f.current=n,()=>{window.clearTimeout(p.current),s.disconnect(),f.current=()=>{}}},[o,g]),{isLoading:c,sentinelRef:d}}export{o as useInfiniteScroll};
2
2
  //# sourceMappingURL=useInfiniteScroll.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useInfiniteScroll.mjs","names":[],"sources":["../../../../package/common/infiniteBlock/useInfiniteScroll.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport type React from 'react';\n\nconst RETRY_DELAY = 3000;\n\n// Loads more items once a sentinel element at the end of the list scrolls into view.\n// IntersectionObserver only fires on a change of intersection state, so if a page of\n// freshly-loaded items still doesn't fill the container, the sentinel stays visible and\n// we have to re-arm the observer ourselves to pick up the next page.\nexport function useInfiniteScroll(\n containerRef: React.RefObject<HTMLDivElement | null>,\n onScroll: () => Promise<void>,\n disabled: boolean,\n) {\n const [isLoading, setIsLoading] = useState(false);\n const isLoadingRef = useRef(false);\n const sentinelRef = useRef<HTMLDivElement>(null);\n\n // Kept in a ref so a caller passing a fresh onScroll function each render doesn't\n // tear down and recreate the observer every time.\n const onScrollRef = useRef(onScroll);\n useEffect(() => {\n onScrollRef.current = onScroll;\n });\n\n useEffect(() => {\n const root = containerRef.current;\n const sentinel = sentinelRef.current;\n if (disabled || !root || !sentinel) return undefined;\n\n let cancelled = false;\n let retry = 0;\n\n // Observing again reports the sentinel at once, for a page that still left it in view.\n const rearm = () => {\n observer.unobserve(sentinel);\n observer.observe(sentinel);\n };\n\n const loadMore = () => {\n if (isLoadingRef.current) return;\n isLoadingRef.current = true;\n setIsLoading(true);\n\n const settle = (succeeded: boolean) => {\n // Reset even once cancelled: a load can outlive its observer when the caller disables it\n // mid-request, and a flag left set would block every load after it.\n isLoadingRef.current = false;\n setIsLoading(false);\n if (cancelled) return;\n if (succeeded) rearm();\n // A failure waits before trying again, or it would retry without end while the sentinel\n // stays in view. It does try again: a list too short to scroll offers no other way.\n else retry = window.setTimeout(rearm, RETRY_DELAY);\n };\n\n onScrollRef.current().then(\n () => settle(true),\n () => settle(false),\n );\n };\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) loadMore();\n },\n { root },\n );\n\n observer.observe(sentinel);\n\n return () => {\n cancelled = true;\n window.clearTimeout(retry);\n observer.disconnect();\n };\n }, [containerRef, disabled]);\n\n return { isLoading, sentinelRef };\n}\n"],"mappings":"yEAGA,IAAM,EAAc,IAMpB,SAAgB,EACd,EACA,EACA,EACA,CACA,GAAM,CAAC,EAAW,GAAgB,EAAS,EAAK,EAC1C,EAAe,EAAO,EAAK,EAC3B,EAAc,EAAuB,IAAI,EAIzC,EAAc,EAAO,CAAQ,EA0DnC,OAzDA,MAAgB,CACd,EAAY,QAAU,CACxB,CAAC,EAED,MAAgB,CACd,IAAM,EAAO,EAAa,QACpB,EAAW,EAAY,QAC7B,GAAI,GAAY,CAAC,GAAQ,CAAC,EAAU,OAEpC,IAAI,EAAY,GACZ,EAAQ,EAGN,MAAc,CAClB,EAAS,UAAU,CAAQ,EAC3B,EAAS,QAAQ,CAAQ,CAC3B,EAEM,MAAiB,CACrB,GAAI,EAAa,QAAS,OAC1B,EAAa,QAAU,GACvB,EAAa,EAAI,EAEjB,IAAM,EAAU,GAAuB,CAGrC,EAAa,QAAU,GACvB,EAAa,EAAK,EACd,KACA,EAAW,EAAM,EAGhB,EAAQ,OAAO,WAAW,EAAO,CAAW,EACnD,EAEA,EAAY,QAAQ,CAAC,CAAC,SACd,EAAO,EAAI,MACX,EAAO,EAAK,CACpB,CACF,EAEM,EAAW,IAAI,sBAClB,CAAC,KAAW,CACP,EAAM,gBAAgB,EAAS,CACrC,EACA,CAAE,MAAK,CACT,EAIA,OAFA,EAAS,QAAQ,CAAQ,MAEZ,CACX,EAAY,GACZ,OAAO,aAAa,CAAK,EACzB,EAAS,WAAW,CACtB,CACF,EAAG,CAAC,EAAc,CAAQ,CAAC,EAEpB,CAAE,YAAW,aAAY,CAClC"}
1
+ {"version":3,"file":"useInfiniteScroll.mjs","names":[],"sources":["../../../../package/common/infiniteBlock/useInfiniteScroll.ts"],"sourcesContent":["import { useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport type React from 'react';\n\nconst RETRY_DELAY = 3000;\n// How far ahead of the end a load starts, so that scrolling at a normal pace rarely reaches the\n// spinner. The same as VirtualList's default `loadMoreThreshold`.\nconst LOAD_AHEAD = '100px 0px';\n\n// Loads more items once a sentinel element at the end of the list comes near.\n// IntersectionObserver only fires on a change of intersection state, so if a page of\n// freshly-loaded items still doesn't fill the container, the sentinel stays in range and\n// we have to re-arm the observer ourselves to pick up the next page.\n// Without `onLoadMore` it watches nothing. Passing it only once the container and sentinel are\n// rendered is what starts the watching: an observer set up before then never would.\nexport function useInfiniteScroll(\n containerRef: React.RefObject<HTMLDivElement | null>,\n onLoadMore: (() => Promise<void>) | undefined,\n) {\n const [isLoading, setIsLoading] = useState(false);\n const isLoadingRef = useRef(false);\n const sentinelRef = useRef<HTMLDivElement>(null);\n // The observer of the current effect, so that a load which outlives its own still re-arms this one.\n const rearmRef = useRef(() => {});\n const retryRef = useRef(0);\n // Elements beside the sentinel when the last load started, until the list holds a different number.\n const loadedFromRef = useRef<number | null>(null);\n\n // Kept in a ref so a caller passing a fresh onLoadMore function each render doesn't\n // tear down and recreate the observer every time.\n const onLoadMoreRef = useRef(onLoadMore);\n useEffect(() => {\n onLoadMoreRef.current = onLoadMore;\n });\n const isEnabled = onLoadMore !== undefined;\n\n // A load re-arms only once the list has changed, so one that added nothing ends the asking. Not\n // when it settles: its items often render later, a React Query page a whole task later. Counted\n // in the DOM rather than from the caller's items, where a Fragment of messages counts as one.\n useLayoutEffect(() => {\n const from = loadedFromRef.current;\n if (isLoading || from === null) return;\n if (sentinelRef.current?.parentElement?.childElementCount === from) return;\n loadedFromRef.current = null;\n rearmRef.current();\n });\n\n useEffect(() => {\n const root = containerRef.current;\n const sentinel = sentinelRef.current;\n if (!isEnabled || !root || !sentinel) return undefined;\n\n // Observing again reports the sentinel at once, for a page that still left it in range.\n const rearm = () => {\n observer.unobserve(sentinel);\n observer.observe(sentinel);\n };\n\n const loadMore = () => {\n const load = onLoadMoreRef.current;\n if (!load || isLoadingRef.current) return;\n isLoadingRef.current = true;\n loadedFromRef.current = sentinel.parentElement?.childElementCount ?? null;\n setIsLoading(true);\n\n load().then(\n () => {\n isLoadingRef.current = false;\n setIsLoading(false);\n },\n () => {\n isLoadingRef.current = false;\n loadedFromRef.current = null;\n setIsLoading(false);\n // A failure waits before trying again, or it would retry without end while the sentinel\n // stays in range. It does try again: a list too short to scroll offers no other way.\n retryRef.current = window.setTimeout(() => rearmRef.current(), RETRY_DELAY);\n },\n );\n };\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) loadMore();\n },\n { root, rootMargin: LOAD_AHEAD },\n );\n\n observer.observe(sentinel);\n rearmRef.current = rearm;\n\n return () => {\n window.clearTimeout(retryRef.current);\n observer.disconnect();\n rearmRef.current = () => {};\n };\n }, [containerRef, isEnabled]);\n\n return { isLoading, sentinelRef };\n}\n"],"mappings":"8FAGA,IAAM,EAAc,IAGd,EAAa,YAQnB,SAAgB,EACd,EACA,EACA,CACA,GAAM,CAAC,EAAW,GAAgB,EAAS,EAAK,EAC1C,EAAe,EAAO,EAAK,EAC3B,EAAc,EAAuB,IAAI,EAEzC,EAAW,MAAa,CAAC,CAAC,EAC1B,EAAW,EAAO,CAAC,EAEnB,EAAgB,EAAsB,IAAI,EAI1C,EAAgB,EAAO,CAAU,EACvC,MAAgB,CACd,EAAc,QAAU,CAC1B,CAAC,EACD,IAAM,EAAY,IAAe,IAAA,GAgEjC,OA3DA,MAAsB,CACpB,IAAM,EAAO,EAAc,QACvB,GAAa,IAAS,MACtB,EAAY,SAAS,eAAe,oBAAsB,IAC9D,EAAc,QAAU,KACxB,EAAS,QAAQ,EACnB,CAAC,EAED,MAAgB,CACd,IAAM,EAAO,EAAa,QACpB,EAAW,EAAY,QAC7B,GAAI,CAAC,GAAa,CAAC,GAAQ,CAAC,EAAU,OAGtC,IAAM,MAAc,CAClB,EAAS,UAAU,CAAQ,EAC3B,EAAS,QAAQ,CAAQ,CAC3B,EAEM,MAAiB,CACrB,IAAM,EAAO,EAAc,QACtB,GAAQ,GAAa,UAC1B,EAAa,QAAU,GACvB,EAAc,QAAU,EAAS,eAAe,mBAAqB,KACrE,EAAa,EAAI,EAEjB,EAAK,CAAC,CAAC,SACC,CACJ,EAAa,QAAU,GACvB,EAAa,EAAK,CACpB,MACM,CACJ,EAAa,QAAU,GACvB,EAAc,QAAU,KACxB,EAAa,EAAK,EAGlB,EAAS,QAAU,OAAO,eAAiB,EAAS,QAAQ,EAAG,CAAW,CAC5E,CACF,EACF,EAEM,EAAW,IAAI,sBAClB,CAAC,KAAW,CACP,EAAM,gBAAgB,EAAS,CACrC,EACA,CAAE,OAAM,WAAY,CAAW,CACjC,EAKA,OAHA,EAAS,QAAQ,CAAQ,EACzB,EAAS,QAAU,MAEN,CACX,OAAO,aAAa,EAAS,OAAO,EACpC,EAAS,WAAW,EACpB,EAAS,YAAgB,CAAC,CAC5B,CACF,EAAG,CAAC,EAAc,CAAS,CAAC,EAErB,CAAE,YAAW,aAAY,CAClC"}
@@ -0,0 +1,2 @@
1
+ "use client";import{renderIcon as e}from"../utils/renderIcon.mjs";import{useLinkClick as t}from"../utils/navigation.mjs";import n from"./listItem.module.mjs";import{useId as r}from"react";import i from"classnames";import{faChevronRight as a}from"@fortawesome/free-solid-svg-icons";import{jsx as o,jsxs as s}from"react/jsx-runtime";var c=e=>e!=null&&typeof e!=`boolean`&&e!==``;function l({icon:l,title:u,subtitle:d,endContent:f,href:p,target:m,rel:h,download:g,onClick:_,selected:v=!1,chevron:y=p!==void 0,className:b,style:x,classNames:S,styles:C,...w}){let T=t(),E=r(),D=c(d),O=c(f),k=p!==void 0||_!==void 0,A=[D&&`${E}-subtitle`,typeof f==`string`&&`${E}-end`].filter(Boolean).join(` `),j=o(`span`,{className:i(n.listItemTitle,S?.title),style:C?.title,children:u}),M={className:i(n.listItemTrigger,S?.trigger),style:C?.trigger,"aria-describedby":A||void 0},N=p===void 0?_?o(`button`,{...M,type:`button`,"aria-pressed":v||void 0,onClick:_,children:j}):j:o(`a`,{...M,href:p,target:m,rel:h,download:g,"aria-current":v?`page`:void 0,onClick:e=>{_?.(e),T(e)},children:j});return s(`div`,{...w,className:i(n.listItem,{[n.listItemInteractive]:k,[n.listItemSelected]:v},b,S?.root),style:{...x,...C?.root},children:[l!=null&&e(l,{className:i(n.listItemIcon,S?.icon),style:C?.icon}),s(`div`,{className:i(n.listItemLabels,S?.labels),style:C?.labels,children:[N,D&&o(`div`,{id:`${E}-subtitle`,className:i(n.listItemSubtitle,S?.subtitle),style:C?.subtitle,children:d})]}),O&&o(`div`,{id:`${E}-end`,className:i(n.listItemEndContent,S?.endContent),style:C?.endContent,children:f}),y&&e(a,{className:i(n.listItemChevron,S?.chevron),style:C?.chevron})]})}export{l as default};
2
+ //# sourceMappingURL=listItem.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listItem.mjs","names":[],"sources":["../../../../package/common/listItem/listItem.tsx"],"sourcesContent":["import { useId, type ReactElement, type ReactNode } from 'react';\nimport cx from 'classnames';\nimport { faChevronRight } from '@fortawesome/free-solid-svg-icons';\n\nimport type { ListItemProps } from './listItem.types';\nimport styles from './listItem.module.pcss';\nimport { renderIcon } from '../utils/renderIcon';\nimport { useLinkClick } from '../utils/navigation';\n\n// `subtitle={isOverdue && 'Overdue'}` passes false, which renders nothing and needs no box.\nconst isShown = (node: ReactNode) => node != null && typeof node !== 'boolean' && node !== '';\n\nfunction ListItem({\n icon,\n title,\n subtitle,\n endContent,\n href,\n target,\n rel,\n download,\n onClick,\n selected = false,\n chevron = href !== undefined,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n}: ListItemProps): ReactElement {\n const followLink = useLinkClick();\n const id = useId();\n const hasSubtitle = isShown(subtitle);\n const hasEndContent = isShown(endContent);\n const isInteractive = href !== undefined || onClick !== undefined;\n // A status in endContent (\"Open\") is part of what the row says, so the link is described by it\n // too, where it is text: a control there would only read its own name a second time.\n const describedBy = [hasSubtitle && `${id}-subtitle`, typeof endContent === 'string' && `${id}-end`]\n .filter(Boolean)\n .join(' ');\n\n const titleElement = (\n <span className={cx(styles.listItemTitle, classNames?.title)} style={slotStyles?.title}>\n {title}\n </span>\n );\n // Only the title is the link or button: the rest of the row can then hold controls of its own,\n // which an <a> or <button> may not contain. Its ::after stretches over the whole row instead.\n const triggerProps = {\n className: cx(styles.listItemTrigger, classNames?.trigger),\n style: slotStyles?.trigger,\n 'aria-describedby': describedBy || undefined,\n };\n const trigger =\n href !== undefined ? (\n <a\n {...triggerProps}\n href={href}\n target={target}\n rel={rel}\n download={download}\n aria-current={selected ? 'page' : undefined}\n onClick={(event) => {\n onClick?.(event);\n followLink(event);\n }}\n >\n {titleElement}\n </a>\n ) : onClick ? (\n <button {...triggerProps} type='button' aria-pressed={selected || undefined} onClick={onClick}>\n {titleElement}\n </button>\n ) : (\n titleElement\n );\n\n return (\n <div\n {...rest}\n className={cx(\n styles.listItem,\n { [styles.listItemInteractive]: isInteractive, [styles.listItemSelected]: selected },\n className,\n classNames?.root,\n )}\n style={{ ...style, ...slotStyles?.root }}\n >\n {icon != null &&\n renderIcon(icon, { className: cx(styles.listItemIcon, classNames?.icon), style: slotStyles?.icon })}\n <div className={cx(styles.listItemLabels, classNames?.labels)} style={slotStyles?.labels}>\n {trigger}\n {hasSubtitle && (\n <div\n id={`${id}-subtitle`}\n className={cx(styles.listItemSubtitle, classNames?.subtitle)}\n style={slotStyles?.subtitle}\n >\n {subtitle}\n </div>\n )}\n </div>\n {hasEndContent && (\n <div\n id={`${id}-end`}\n className={cx(styles.listItemEndContent, classNames?.endContent)}\n style={slotStyles?.endContent}\n >\n {endContent}\n </div>\n )}\n {chevron &&\n renderIcon(faChevronRight, {\n className: cx(styles.listItemChevron, classNames?.chevron),\n style: slotStyles?.chevron,\n })}\n </div>\n );\n}\n\nexport default ListItem;\n"],"mappings":"2UAUA,IAAM,EAAW,GAAoB,GAAQ,MAAQ,OAAO,GAAS,WAAa,IAAS,GAE3F,SAAS,EAAS,CAChB,OACA,QACA,WACA,aACA,OACA,SACA,MACA,WACA,UACA,WAAW,GACX,UAAU,IAAS,IAAA,GACnB,YACA,QACA,aACA,OAAQ,EACR,GAAG,GAC2B,CAC9B,IAAM,EAAa,EAAa,EAC1B,EAAK,EAAM,EACX,EAAc,EAAQ,CAAQ,EAC9B,EAAgB,EAAQ,CAAU,EAClC,EAAgB,IAAS,IAAA,IAAa,IAAY,IAAA,GAGlD,EAAc,CAAC,GAAe,GAAG,EAAG,WAAY,OAAO,GAAe,UAAY,GAAG,EAAG,KAAK,CAAC,CACjG,OAAO,OAAO,CAAC,CACf,KAAK,GAAG,EAEL,EACJ,EAAC,OAAD,CAAM,UAAW,EAAG,EAAO,cAAe,GAAY,KAAK,EAAG,MAAO,GAAY,MAC9E,SAAA,CACG,CAAA,EAIF,EAAe,CACnB,UAAW,EAAG,EAAO,gBAAiB,GAAY,OAAO,EACzD,MAAO,GAAY,QACnB,mBAAoB,GAAe,IAAA,EACrC,EACM,EACJ,IAAS,IAAA,GAeL,EACF,EAAC,SAAD,CAAQ,GAAI,EAAc,KAAK,SAAS,eAAc,GAAY,IAAA,GAAoB,UACnF,SAAA,CACK,CAAA,EAER,EAnBA,EAAC,IAAD,CACE,GAAI,EACE,OACE,SACH,MACK,WACV,eAAc,EAAW,OAAS,IAAA,GAClC,QAAU,GAAU,CAClB,IAAU,CAAK,EACf,EAAW,CAAK,CAClB,EAEC,SAAA,CACA,CAAA,EASP,OACE,EAAC,MAAD,CACE,GAAI,EACJ,UAAW,EACT,EAAO,SACP,EAAG,EAAO,qBAAsB,GAAgB,EAAO,kBAAmB,CAAS,EACnF,EACA,GAAY,IACd,EACA,MAAO,CAAE,GAAG,EAAO,GAAG,GAAY,IAAK,EARzC,SAAA,CAUG,GAAQ,MACP,EAAW,EAAM,CAAE,UAAW,EAAG,EAAO,aAAc,GAAY,IAAI,EAAG,MAAO,GAAY,IAAK,CAAC,EACpG,EAAC,MAAD,CAAK,UAAW,EAAG,EAAO,eAAgB,GAAY,MAAM,EAAG,MAAO,GAAY,OAAlF,SAAA,CACG,EACA,GACC,EAAC,MAAD,CACE,GAAI,GAAG,EAAG,WACV,UAAW,EAAG,EAAO,iBAAkB,GAAY,QAAQ,EAC3D,MAAO,GAAY,SAElB,SAAA,CACE,CAAA,CAEJ,IACJ,GACC,EAAC,MAAD,CACE,GAAI,GAAG,EAAG,MACV,UAAW,EAAG,EAAO,mBAAoB,GAAY,UAAU,EAC/D,MAAO,GAAY,WAElB,SAAA,CACE,CAAA,EAEN,GACC,EAAW,EAAgB,CACzB,UAAW,EAAG,EAAO,gBAAiB,GAAY,OAAO,EACzD,MAAO,GAAY,OACrB,CAAC,CACA,GAET"}
@@ -0,0 +1,2 @@
1
+ "use client";var e=`listItem_i7EoH`,t=`listItem-icon_HEdQy`,n=`listItem-labels_dZXiH`,r=`listItem-title_zmlAW`,i=`listItem-trigger_07n1-`,a=`listItem-selected_zY4Ni`,o=`listItem-subtitle_-XwvP`,s=`listItem-endContent_cQoge`,c=`listItem-chevron_19GEH`,l=`listItem-interactive_GFbY-`,u={listItem:e,"listItem-icon":`listItem-icon_HEdQy`,listItemIcon:t,"listItem-labels":`listItem-labels_dZXiH`,listItemLabels:n,"listItem-title":`listItem-title_zmlAW`,listItemTitle:r,"listItem-trigger":`listItem-trigger_07n1-`,listItemTrigger:i,"listItem-selected":`listItem-selected_zY4Ni`,listItemSelected:a,"listItem-subtitle":`listItem-subtitle_-XwvP`,listItemSubtitle:o,"listItem-endContent":`listItem-endContent_cQoge`,listItemEndContent:s,"listItem-chevron":`listItem-chevron_19GEH`,listItemChevron:c,"listItem-interactive":`listItem-interactive_GFbY-`,listItemInteractive:l};export{u as default,e as listItem,c as listItemChevron,s as listItemEndContent,t as listItemIcon,l as listItemInteractive,n as listItemLabels,a as listItemSelected,o as listItemSubtitle,r as listItemTitle,i as listItemTrigger};
2
+ //# sourceMappingURL=listItem.module.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listItem.module.mjs","names":[],"sources":["../../../../package/common/listItem/listItem.module.pcss"],"sourcesContent":["/* position: relative so the trigger's ::after can stretch over the whole row, and isolation so\n its z-index stays inside the row instead of rising over a sticky header of the page. */\n.listItem {\n position: relative;\n isolation: isolate;\n display: flex;\n align-items: center;\n gap: var(--spacing-m, 8px);\n box-sizing: border-box;\n width: 100%;\n min-height: var(--list-item-min-height, 48px);\n padding: var(--spacing-m, 8px);\n\n &-icon {\n /* FontAwesome's own rule is unlayered and outranks `width` from inside the library's layer. */\n --fa-width: 1.25em;\n\n flex-shrink: 0;\n }\n\n /* min-width: 0 lets the labels shrink, so a long title ellipses instead of pushing\n endContent and the chevron out of the row. */\n &-labels {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n }\n\n &-title {\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* The <a> or <button> around the title. */\n &-trigger {\n display: block;\n margin: 0;\n padding: 0;\n border: none;\n background: none;\n color: inherit;\n font: inherit;\n text-align: start;\n text-decoration: none;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n /* Over the whole row. It belongs to the link, so a middle click and a long press anywhere on\n the row act on the link too. */\n\n /* z-index above content that positions itself, a Chip among many, which would otherwise\n paint over it in tree order and swallow the click. */\n &::after {\n content: '';\n position: absolute;\n inset: 0;\n z-index: 1;\n transition: background-color 0.15s;\n }\n\n /* A tint of the text colour, so it shows on any surface the row sits on. (hover: hover)\n because on touch a tap latches :hover until the next tap elsewhere. */\n @media (hover: hover) {\n &:hover::after {\n background-color: color-mix(in srgb, currentcolor 6%, transparent);\n }\n }\n\n &:active::after {\n background-color: color-mix(in srgb, currentcolor 12%, transparent);\n }\n\n /* On the stretched area, to ring the row rather than the title. Inside the edge: a row\n usually fills a card that clips its overflow. */\n &:focus-visible {\n outline: none;\n }\n\n &:focus-visible::after {\n outline: 2px solid var(--focus-border-color, var(--primary-color));\n outline-offset: -2px;\n }\n }\n\n /* Under the trigger's hover tint, which still shows on top of it. */\n &-selected {\n background-color: var(--list-item-selected-background, color-mix(in srgb, var(--primary-color) 12%, transparent));\n }\n\n &-subtitle {\n display: -webkit-box;\n overflow: hidden;\n color: var(--text-dark);\n font-size: 0.85em;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n line-clamp: 2;\n }\n\n &-endContent {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n gap: var(--spacing-s, 4px);\n }\n\n &-chevron {\n flex-shrink: 0;\n color: var(--text-dark);\n font-size: 0.85em;\n }\n}\n\n/* Controls elsewhere in the row sit above the stretched area, so they stay clickable while the\n text around them still opens the row. Any `a`, not `a[href]`: a disabled link Button drops its\n href, and a click on it must not open the row. All in :where(), for no specificity at all: a\n control's own position wins, so an input it positions absolutely stays absolute. */\n:where(.listItem-interactive)\n :where(a, button, input, select, textarea, label, [tabindex]):where(:not(.listItem-trigger)) {\n position: relative;\n z-index: 2;\n}\n"],"mappings":""}