@vector-im/compound-web 7.6.3 → 7.7.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 (67) hide show
  1. package/dist/components/ChatFilter/ChatFilter.cjs +22 -0
  2. package/dist/components/ChatFilter/ChatFilter.cjs.map +1 -0
  3. package/dist/components/ChatFilter/ChatFilter.d.ts +14 -0
  4. package/dist/components/ChatFilter/ChatFilter.d.ts.map +1 -0
  5. package/dist/components/ChatFilter/ChatFilter.js +22 -0
  6. package/dist/components/ChatFilter/ChatFilter.js.map +1 -0
  7. package/dist/components/ChatFilter/ChatFilter.module.css.cjs +7 -0
  8. package/dist/components/ChatFilter/ChatFilter.module.css.cjs.map +1 -0
  9. package/dist/components/ChatFilter/ChatFilter.module.css.js +7 -0
  10. package/dist/components/ChatFilter/ChatFilter.module.css.js.map +1 -0
  11. package/dist/components/ChatFilter/index.d.ts +2 -0
  12. package/dist/components/ChatFilter/index.d.ts.map +1 -0
  13. package/dist/components/Menu/CheckboxMenuItem.d.ts +23 -0
  14. package/dist/components/Menu/CheckboxMenuItem.d.ts.map +1 -0
  15. package/dist/components/Menu/FloatingMenu.cjs +2 -3
  16. package/dist/components/Menu/FloatingMenu.cjs.map +1 -1
  17. package/dist/components/Menu/FloatingMenu.d.ts.map +1 -1
  18. package/dist/components/Menu/FloatingMenu.js +2 -3
  19. package/dist/components/Menu/FloatingMenu.js.map +1 -1
  20. package/dist/components/Menu/FloatingMenu.module.css.cjs +2 -2
  21. package/dist/components/Menu/FloatingMenu.module.css.js +2 -2
  22. package/dist/components/Menu/MenuItem.cjs +2 -1
  23. package/dist/components/Menu/MenuItem.cjs.map +1 -1
  24. package/dist/components/Menu/MenuItem.d.ts +11 -2
  25. package/dist/components/Menu/MenuItem.d.ts.map +1 -1
  26. package/dist/components/Menu/MenuItem.js +2 -1
  27. package/dist/components/Menu/MenuItem.js.map +1 -1
  28. package/dist/components/Menu/MenuTitle.cjs +16 -0
  29. package/dist/components/Menu/MenuTitle.cjs.map +1 -0
  30. package/dist/components/Menu/MenuTitle.d.ts +18 -0
  31. package/dist/components/Menu/MenuTitle.d.ts.map +1 -0
  32. package/dist/components/Menu/MenuTitle.js +16 -0
  33. package/dist/components/Menu/MenuTitle.js.map +1 -0
  34. package/dist/components/Menu/MenuTitle.module.css.cjs +7 -0
  35. package/dist/components/Menu/MenuTitle.module.css.cjs.map +1 -0
  36. package/dist/components/Menu/MenuTitle.module.css.js +7 -0
  37. package/dist/components/Menu/MenuTitle.module.css.js.map +1 -0
  38. package/dist/components/Menu/ToggleMenuItem.cjs +2 -1
  39. package/dist/components/Menu/ToggleMenuItem.cjs.map +1 -1
  40. package/dist/components/Menu/ToggleMenuItem.d.ts +3 -1
  41. package/dist/components/Menu/ToggleMenuItem.d.ts.map +1 -1
  42. package/dist/components/Menu/ToggleMenuItem.js +2 -1
  43. package/dist/components/Menu/ToggleMenuItem.js.map +1 -1
  44. package/dist/components/Tooltip/Tooltip.cjs +6 -1
  45. package/dist/components/Tooltip/Tooltip.cjs.map +1 -1
  46. package/dist/components/Tooltip/Tooltip.js +6 -1
  47. package/dist/components/Tooltip/Tooltip.js.map +1 -1
  48. package/dist/index.cjs +2 -0
  49. package/dist/index.cjs.map +1 -1
  50. package/dist/index.d.ts +1 -0
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +2 -0
  53. package/dist/index.js.map +1 -1
  54. package/dist/style.css +59 -15
  55. package/package.json +1 -1
  56. package/src/components/ChatFilter/ChatFilter.module.css +33 -0
  57. package/src/components/ChatFilter/ChatFilter.tsx +41 -0
  58. package/src/components/ChatFilter/index.ts +8 -0
  59. package/src/components/Menu/CheckboxMenuItem.tsx +62 -0
  60. package/src/components/Menu/FloatingMenu.module.css +2 -5
  61. package/src/components/Menu/FloatingMenu.tsx +4 -13
  62. package/src/components/Menu/MenuItem.tsx +12 -2
  63. package/src/components/Menu/MenuTitle.module.css +14 -0
  64. package/src/components/Menu/MenuTitle.tsx +40 -0
  65. package/src/components/Menu/ToggleMenuItem.tsx +3 -2
  66. package/src/components/Tooltip/Tooltip.tsx +6 -1
  67. package/src/index.ts +1 -0
@@ -1 +1 @@
1
- {"version":3,"file":"Tooltip.cjs","sources":["../../../src/components/Tooltip/Tooltip.tsx"],"sourcesContent":["/*\nCopyright 2023, 2024 New Vector Ltd.\n\nSPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\nPlease see LICENSE files in the repository root for full details.\n*/\n\nimport { TooltipContext, useTooltipContext } from \"./TooltipContext\";\nimport {\n FloatingArrow,\n FloatingPortal,\n useMergeRefs,\n} from \"@floating-ui/react\";\nimport React, {\n PropsWithChildren,\n Ref,\n JSX,\n isValidElement,\n cloneElement,\n useMemo,\n ReactNode,\n FC,\n ReactElement,\n} from \"react\";\n\nimport classNames from \"classnames\";\nimport styles from \"./Tooltip.module.css\";\nimport {\n CommonUseTooltipProps,\n TooltipDescription,\n TooltipLabel,\n useTooltip,\n} from \"./useTooltip\";\n\n// Unfortunately Omit doesn't distribute nicely over sum types, so we have to\n// piece together the useTooltip options type by hand\ntype TooltipProps = Omit<CommonUseTooltipProps, \"isTriggerInteractive\"> &\n (TooltipLabel | TooltipDescription) & {\n /**\n * Whether the trigger element is interactive.\n * When trigger is interactive:\n * - tooltip will be shown after a 300ms delay.\n * When trigger is not interactive:\n * - tooltip will be shown instantly when pointer enters trigger.\n * - trigger will be wrapped in a span with a tab index from prop nonInteractiveTriggerTabIndex\n * @default true\n */\n isTriggerInteractive?: boolean;\n /**\n * The tab index for the non interactive trigger.\n * @default 0\n */\n nonInteractiveTriggerTabIndex?: number;\n };\n\nconst hasLabel = (\n props: TooltipLabel | TooltipDescription,\n): props is TooltipLabel => \"label\" in props && !!props.label;\n\n/**\n * A tooltip component\n */\nexport function Tooltip({\n children,\n isTriggerInteractive = true,\n nonInteractiveTriggerTabIndex = 0,\n ...props\n}: PropsWithChildren<TooltipProps>): JSX.Element {\n const context = useTooltip({ isTriggerInteractive, ...props });\n\n return (\n <TooltipContext.Provider value={context}>\n <TooltipAnchor\n isTriggerInteractive={isTriggerInteractive}\n nonInteractiveTriggerTabIndex={nonInteractiveTriggerTabIndex}\n >\n {children}\n </TooltipAnchor>\n <TooltipContent>\n <span id={context.labelId}>\n {hasLabel(props) ? props.label : props.description}\n </span>\n <Caption />\n </TooltipContent>\n </TooltipContext.Provider>\n );\n}\n\nfunction Caption() {\n const { caption, captionId } = useTooltipContext();\n if (!caption) return null;\n\n const isCaptionString = typeof caption === \"string\";\n const Container = isCaptionString ? \"span\" : \"div\";\n\n /**\n * Forcing dark theme, so that we have the correct contrast when\n * using the text color secondary on a solid dark background.\n * This is temporary and should only remain until we figure out\n * the approach to on-solid tokens\n **/\n return (\n <Container\n id={captionId}\n className={classNames(styles.caption, \"cpd-theme-dark\")}\n >\n {caption}\n </Container>\n );\n}\n\n/**\n * The content of the tooltip\n * @param children\n */\nfunction TooltipContent({\n children,\n}: Readonly<PropsWithChildren>): JSX.Element | null {\n const {\n context: floatingContext,\n open,\n arrowRef,\n purpose,\n ...rest\n } = useTooltipContext();\n\n // Label tooltips are kept in the DOM even when not visually open\n if (!open && purpose !== \"label\") return null;\n\n return (\n <FloatingPortal>\n <div\n ref={rest.refs.setFloating}\n style={rest.floatingStyles}\n {...rest.tooltipProps}\n {...rest.getFloatingProps()}\n className={classNames(styles.tooltip, {\n [styles.invisible]: purpose === \"label\" && !open,\n })}\n >\n <FloatingArrow\n ref={arrowRef}\n context={floatingContext}\n // design absolute value\n width={10}\n height={6}\n className={styles.arrow}\n />\n {children}\n </div>\n </FloatingPortal>\n );\n}\n\ninterface TooltipAnchorProps {\n children: ReactNode;\n isTriggerInteractive: boolean;\n nonInteractiveTriggerTabIndex?: number;\n}\n\n/**\n * The anchor of the tooltip\n * @param children\n */\nconst TooltipAnchor: FC<TooltipAnchorProps> = ({\n children,\n isTriggerInteractive,\n nonInteractiveTriggerTabIndex,\n}) => {\n const context = useTooltipContext();\n\n // The children can have a ref and we don't want to discard it\n // Doing a dirty cast to get the optional ref\n const childrenRef = (children as unknown as { ref?: Ref<HTMLElement> })?.ref;\n const ref = useMergeRefs([context.refs.setReference, childrenRef]);\n\n const element = useMemo(() => {\n if (!isValidElement(children)) return;\n\n if (isTriggerInteractive) {\n const props = context.getReferenceProps({ ref });\n return cloneElement(children, props);\n } else {\n // For a non-interactive trigger, we want most of the props to go on the\n // span element that we provide, since that's what receives focus, but it\n // should still be the trigger that receives the label/description. It\n // would be wrong to label the span element, as it lacks a role.\n const props = context.getReferenceProps({\n ref,\n tabIndex: nonInteractiveTriggerTabIndex,\n });\n const {\n \"aria-labelledby\": labelId,\n \"aria-describedby\": descriptionId,\n ...spanProps\n } = props;\n return (\n <span tabIndex={nonInteractiveTriggerTabIndex} {...spanProps}>\n {cloneElement(children as ReactElement<Record<string, unknown>>, {\n \"aria-labelledby\": labelId,\n \"aria-describedby\": descriptionId,\n })}\n </span>\n );\n }\n }, [context, ref, children]);\n\n if (!element) {\n throw new Error(\"Tooltip anchor must be a single valid React element\");\n }\n\n return element;\n};\n"],"names":["useTooltip","jsxs","TooltipContext","jsx","useTooltipContext","styles","FloatingPortal","FloatingArrow","useMergeRefs","useMemo","isValidElement","cloneElement"],"mappings":";;;;;;;;;AAuDA,MAAM,WAAW,CACf,UAC0B,WAAW,SAAS,CAAC,CAAC,MAAM;AAKjD,SAAS,QAAQ;AAAA,EACtB;AAAA,EACA,uBAAuB;AAAA,EACvB,gCAAgC;AAAA,EAChC,GAAG;AACL,GAAiD;AAC/C,QAAM,UAAUA,WAAW,WAAA,EAAE,sBAAsB,GAAG,OAAO;AAE7D,SACGC,2BAAAA,KAAAC,eAAAA,eAAe,UAAf,EAAwB,OAAO,SAC9B,UAAA;AAAA,IAAAC,2BAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QAEC;AAAA,MAAA;AAAA,IACH;AAAA,oCACC,gBACC,EAAA,UAAA;AAAA,MAACA,2BAAAA,IAAA,QAAA,EAAK,IAAI,QAAQ,SACf,UAAA,SAAS,KAAK,IAAI,MAAM,QAAQ,MAAM,YACzC,CAAA;AAAA,qCACC,SAAQ,CAAA,CAAA;AAAA,IAAA,EACX,CAAA;AAAA,EAAA,GACF;AAEJ;AAEA,SAAS,UAAU;AACjB,QAAM,EAAE,SAAS,UAAU,IAAIC,iCAAkB;AAC7C,MAAA,CAAC,QAAgB,QAAA;AAEf,QAAA,kBAAkB,OAAO,YAAY;AACrC,QAAA,YAAY,kBAAkB,SAAS;AAS3C,SAAAD,2BAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,IAAI;AAAA,MACJ,WAAW,WAAWE,uBAAO,SAAS,gBAAgB;AAAA,MAErD,UAAA;AAAA,IAAA;AAAA,EACH;AAEJ;AAMA,SAAS,eAAe;AAAA,EACtB;AACF,GAAoD;AAC5C,QAAA;AAAA,IACJ,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,MACDD,iCAAkB;AAGtB,MAAI,CAAC,QAAQ,YAAY,QAAgB,QAAA;AAEzC,wCACGE,MAAAA,gBACC,EAAA,UAAAL,2BAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAK,KAAK,KAAK;AAAA,MACf,OAAO,KAAK;AAAA,MACX,GAAG,KAAK;AAAA,MACR,GAAG,KAAK,iBAAiB;AAAA,MAC1B,WAAW,WAAWI,eAAA,QAAO,SAAS;AAAA,QACpC,CAACA,uBAAO,SAAS,GAAG,YAAY,WAAW,CAAC;AAAA,MAAA,CAC7C;AAAA,MAED,UAAA;AAAA,QAAAF,2BAAA;AAAA,UAACI,MAAA;AAAA,UAAA;AAAA,YACC,KAAK;AAAA,YACL,SAAS;AAAA,YAET,OAAO;AAAA,YACP,QAAQ;AAAA,YACR,WAAWF,eAAAA,QAAO;AAAA,UAAA;AAAA,QACpB;AAAA,QACC;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA,GAEL;AAEJ;AAYA,MAAM,gBAAwC,CAAC;AAAA,EAC7C;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,QAAM,UAAUD,eAAAA,kBAAkB;AAIlC,QAAM,cAAe,UAAoD;AACzE,QAAM,MAAMI,MAAAA,aAAa,CAAC,QAAQ,KAAK,cAAc,WAAW,CAAC;AAE3D,QAAA,UAAUC,MAAAA,QAAQ,MAAM;AACxB,QAAA,CAACC,MAAAA,eAAe,QAAQ,EAAG;AAE/B,QAAI,sBAAsB;AACxB,YAAM,QAAQ,QAAQ,kBAAkB,EAAE,KAAK;AACxC,aAAAC,MAAA,aAAa,UAAU,KAAK;AAAA,IAAA,OAC9B;AAKC,YAAA,QAAQ,QAAQ,kBAAkB;AAAA,QACtC;AAAA,QACA,UAAU;AAAA,MAAA,CACX;AACK,YAAA;AAAA,QACJ,mBAAmB;AAAA,QACnB,oBAAoB;AAAA,QACpB,GAAG;AAAA,MAAA,IACD;AACJ,4CACG,QAAK,EAAA,UAAU,+BAAgC,GAAG,WAChD,6BAAa,UAAmD;AAAA,QAC/D,mBAAmB;AAAA,QACnB,oBAAoB;AAAA,MACrB,CAAA,GACH;AAAA,IAAA;AAAA,EAGH,GAAA,CAAC,SAAS,KAAK,QAAQ,CAAC;AAE3B,MAAI,CAAC,SAAS;AACN,UAAA,IAAI,MAAM,qDAAqD;AAAA,EAAA;AAGhE,SAAA;AACT;;"}
1
+ {"version":3,"file":"Tooltip.cjs","sources":["../../../src/components/Tooltip/Tooltip.tsx"],"sourcesContent":["/*\nCopyright 2023, 2024 New Vector Ltd.\n\nSPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\nPlease see LICENSE files in the repository root for full details.\n*/\n\nimport { TooltipContext, useTooltipContext } from \"./TooltipContext\";\nimport {\n FloatingArrow,\n FloatingPortal,\n useMergeRefs,\n} from \"@floating-ui/react\";\nimport React, {\n PropsWithChildren,\n Ref,\n JSX,\n isValidElement,\n cloneElement,\n useMemo,\n ReactNode,\n FC,\n ReactElement,\n} from \"react\";\n\nimport classNames from \"classnames\";\nimport styles from \"./Tooltip.module.css\";\nimport {\n CommonUseTooltipProps,\n TooltipDescription,\n TooltipLabel,\n useTooltip,\n} from \"./useTooltip\";\n\n// Unfortunately Omit doesn't distribute nicely over sum types, so we have to\n// piece together the useTooltip options type by hand\ntype TooltipProps = Omit<CommonUseTooltipProps, \"isTriggerInteractive\"> &\n (TooltipLabel | TooltipDescription) & {\n /**\n * Whether the trigger element is interactive.\n * When trigger is interactive:\n * - tooltip will be shown after a 300ms delay.\n * When trigger is not interactive:\n * - tooltip will be shown instantly when pointer enters trigger.\n * - trigger will be wrapped in a span with a tab index from prop nonInteractiveTriggerTabIndex\n * @default true\n */\n isTriggerInteractive?: boolean;\n /**\n * The tab index for the non interactive trigger.\n * @default 0\n */\n nonInteractiveTriggerTabIndex?: number;\n };\n\nconst hasLabel = (\n props: TooltipLabel | TooltipDescription,\n): props is TooltipLabel => \"label\" in props && !!props.label;\n\n/**\n * A tooltip component\n */\nexport function Tooltip({\n children,\n isTriggerInteractive = true,\n nonInteractiveTriggerTabIndex = 0,\n ...props\n}: PropsWithChildren<TooltipProps>): JSX.Element {\n const context = useTooltip({ isTriggerInteractive, ...props });\n\n return (\n <TooltipContext.Provider value={context}>\n <TooltipAnchor\n isTriggerInteractive={isTriggerInteractive}\n nonInteractiveTriggerTabIndex={nonInteractiveTriggerTabIndex}\n >\n {children}\n </TooltipAnchor>\n <TooltipContent>\n <span id={context.labelId}>\n {hasLabel(props) ? props.label : props.description}\n </span>\n <Caption />\n </TooltipContent>\n </TooltipContext.Provider>\n );\n}\n\nfunction Caption() {\n const { caption, captionId } = useTooltipContext();\n if (!caption) return null;\n\n const isCaptionString = typeof caption === \"string\";\n const Container = isCaptionString ? \"span\" : \"div\";\n\n /**\n * Forcing dark theme, so that we have the correct contrast when\n * using the text color secondary on a solid dark background.\n * This is temporary and should only remain until we figure out\n * the approach to on-solid tokens\n **/\n return (\n <Container\n id={captionId}\n className={classNames(styles.caption, \"cpd-theme-dark\")}\n >\n {caption}\n </Container>\n );\n}\n\n/**\n * The content of the tooltip\n * @param children\n */\nfunction TooltipContent({\n children,\n}: Readonly<PropsWithChildren>): JSX.Element | null {\n const {\n context: floatingContext,\n open,\n arrowRef,\n purpose,\n ...rest\n } = useTooltipContext();\n\n // Label tooltips are kept in the DOM even when not visually open\n if (!open && purpose !== \"label\") return null;\n\n return (\n <FloatingPortal>\n <div\n ref={rest.refs.setFloating}\n style={rest.floatingStyles}\n {...rest.tooltipProps}\n {...rest.getFloatingProps()}\n className={classNames(styles.tooltip, {\n [styles.invisible]: purpose === \"label\" && !open,\n })}\n >\n <FloatingArrow\n ref={arrowRef}\n context={floatingContext}\n // design absolute value\n width={10}\n height={6}\n className={styles.arrow}\n />\n {children}\n </div>\n </FloatingPortal>\n );\n}\n\ninterface TooltipAnchorProps {\n children: ReactNode;\n isTriggerInteractive: boolean;\n nonInteractiveTriggerTabIndex?: number;\n}\n\n/**\n * The anchor of the tooltip\n * @param children\n */\nconst TooltipAnchor: FC<TooltipAnchorProps> = ({\n children,\n isTriggerInteractive,\n nonInteractiveTriggerTabIndex,\n}) => {\n const context = useTooltipContext();\n\n // The children can have a ref and we don't want to discard it\n // Doing a dirty cast to get the optional ref\n const childrenRef = (children as unknown as { ref?: Ref<HTMLElement> })?.ref;\n const ref = useMergeRefs([context.refs.setReference, childrenRef]);\n\n const element = useMemo(() => {\n if (!isValidElement(children)) return;\n\n if (isTriggerInteractive) {\n const props = context.getReferenceProps({\n // To support React 18, we need to explicitly pass the children's props. See https://github.com/element-hq/compound/issues/333\n // In React 19, this is not necessary. `getReferenceProps` is able to get the props directly from the ref.\n ...(typeof children.props === \"object\" ? children.props : {}),\n ref,\n });\n return cloneElement(children, props);\n } else {\n // For a non-interactive trigger, we want most of the props to go on the\n // span element that we provide, since that's what receives focus, but it\n // should still be the trigger that receives the label/description. It\n // would be wrong to label the span element, as it lacks a role.\n const props = context.getReferenceProps({\n ref,\n tabIndex: nonInteractiveTriggerTabIndex,\n });\n const {\n \"aria-labelledby\": labelId,\n \"aria-describedby\": descriptionId,\n ...spanProps\n } = props;\n return (\n <span tabIndex={nonInteractiveTriggerTabIndex} {...spanProps}>\n {cloneElement(children as ReactElement<Record<string, unknown>>, {\n \"aria-labelledby\": labelId,\n \"aria-describedby\": descriptionId,\n })}\n </span>\n );\n }\n }, [context, ref, children]);\n\n if (!element) {\n throw new Error(\"Tooltip anchor must be a single valid React element\");\n }\n\n return element;\n};\n"],"names":["useTooltip","jsxs","TooltipContext","jsx","useTooltipContext","styles","FloatingPortal","FloatingArrow","useMergeRefs","useMemo","isValidElement","cloneElement"],"mappings":"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"}
@@ -90,7 +90,12 @@ const TooltipAnchor = ({
90
90
  const element = useMemo(() => {
91
91
  if (!isValidElement(children)) return;
92
92
  if (isTriggerInteractive) {
93
- const props = context.getReferenceProps({ ref });
93
+ const props = context.getReferenceProps({
94
+ // To support React 18, we need to explicitly pass the children's props. See https://github.com/element-hq/compound/issues/333
95
+ // In React 19, this is not necessary. `getReferenceProps` is able to get the props directly from the ref.
96
+ ...typeof children.props === "object" ? children.props : {},
97
+ ref
98
+ });
94
99
  return cloneElement(children, props);
95
100
  } else {
96
101
  const props = context.getReferenceProps({
@@ -1 +1 @@
1
- {"version":3,"file":"Tooltip.js","sources":["../../../src/components/Tooltip/Tooltip.tsx"],"sourcesContent":["/*\nCopyright 2023, 2024 New Vector Ltd.\n\nSPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\nPlease see LICENSE files in the repository root for full details.\n*/\n\nimport { TooltipContext, useTooltipContext } from \"./TooltipContext\";\nimport {\n FloatingArrow,\n FloatingPortal,\n useMergeRefs,\n} from \"@floating-ui/react\";\nimport React, {\n PropsWithChildren,\n Ref,\n JSX,\n isValidElement,\n cloneElement,\n useMemo,\n ReactNode,\n FC,\n ReactElement,\n} from \"react\";\n\nimport classNames from \"classnames\";\nimport styles from \"./Tooltip.module.css\";\nimport {\n CommonUseTooltipProps,\n TooltipDescription,\n TooltipLabel,\n useTooltip,\n} from \"./useTooltip\";\n\n// Unfortunately Omit doesn't distribute nicely over sum types, so we have to\n// piece together the useTooltip options type by hand\ntype TooltipProps = Omit<CommonUseTooltipProps, \"isTriggerInteractive\"> &\n (TooltipLabel | TooltipDescription) & {\n /**\n * Whether the trigger element is interactive.\n * When trigger is interactive:\n * - tooltip will be shown after a 300ms delay.\n * When trigger is not interactive:\n * - tooltip will be shown instantly when pointer enters trigger.\n * - trigger will be wrapped in a span with a tab index from prop nonInteractiveTriggerTabIndex\n * @default true\n */\n isTriggerInteractive?: boolean;\n /**\n * The tab index for the non interactive trigger.\n * @default 0\n */\n nonInteractiveTriggerTabIndex?: number;\n };\n\nconst hasLabel = (\n props: TooltipLabel | TooltipDescription,\n): props is TooltipLabel => \"label\" in props && !!props.label;\n\n/**\n * A tooltip component\n */\nexport function Tooltip({\n children,\n isTriggerInteractive = true,\n nonInteractiveTriggerTabIndex = 0,\n ...props\n}: PropsWithChildren<TooltipProps>): JSX.Element {\n const context = useTooltip({ isTriggerInteractive, ...props });\n\n return (\n <TooltipContext.Provider value={context}>\n <TooltipAnchor\n isTriggerInteractive={isTriggerInteractive}\n nonInteractiveTriggerTabIndex={nonInteractiveTriggerTabIndex}\n >\n {children}\n </TooltipAnchor>\n <TooltipContent>\n <span id={context.labelId}>\n {hasLabel(props) ? props.label : props.description}\n </span>\n <Caption />\n </TooltipContent>\n </TooltipContext.Provider>\n );\n}\n\nfunction Caption() {\n const { caption, captionId } = useTooltipContext();\n if (!caption) return null;\n\n const isCaptionString = typeof caption === \"string\";\n const Container = isCaptionString ? \"span\" : \"div\";\n\n /**\n * Forcing dark theme, so that we have the correct contrast when\n * using the text color secondary on a solid dark background.\n * This is temporary and should only remain until we figure out\n * the approach to on-solid tokens\n **/\n return (\n <Container\n id={captionId}\n className={classNames(styles.caption, \"cpd-theme-dark\")}\n >\n {caption}\n </Container>\n );\n}\n\n/**\n * The content of the tooltip\n * @param children\n */\nfunction TooltipContent({\n children,\n}: Readonly<PropsWithChildren>): JSX.Element | null {\n const {\n context: floatingContext,\n open,\n arrowRef,\n purpose,\n ...rest\n } = useTooltipContext();\n\n // Label tooltips are kept in the DOM even when not visually open\n if (!open && purpose !== \"label\") return null;\n\n return (\n <FloatingPortal>\n <div\n ref={rest.refs.setFloating}\n style={rest.floatingStyles}\n {...rest.tooltipProps}\n {...rest.getFloatingProps()}\n className={classNames(styles.tooltip, {\n [styles.invisible]: purpose === \"label\" && !open,\n })}\n >\n <FloatingArrow\n ref={arrowRef}\n context={floatingContext}\n // design absolute value\n width={10}\n height={6}\n className={styles.arrow}\n />\n {children}\n </div>\n </FloatingPortal>\n );\n}\n\ninterface TooltipAnchorProps {\n children: ReactNode;\n isTriggerInteractive: boolean;\n nonInteractiveTriggerTabIndex?: number;\n}\n\n/**\n * The anchor of the tooltip\n * @param children\n */\nconst TooltipAnchor: FC<TooltipAnchorProps> = ({\n children,\n isTriggerInteractive,\n nonInteractiveTriggerTabIndex,\n}) => {\n const context = useTooltipContext();\n\n // The children can have a ref and we don't want to discard it\n // Doing a dirty cast to get the optional ref\n const childrenRef = (children as unknown as { ref?: Ref<HTMLElement> })?.ref;\n const ref = useMergeRefs([context.refs.setReference, childrenRef]);\n\n const element = useMemo(() => {\n if (!isValidElement(children)) return;\n\n if (isTriggerInteractive) {\n const props = context.getReferenceProps({ ref });\n return cloneElement(children, props);\n } else {\n // For a non-interactive trigger, we want most of the props to go on the\n // span element that we provide, since that's what receives focus, but it\n // should still be the trigger that receives the label/description. It\n // would be wrong to label the span element, as it lacks a role.\n const props = context.getReferenceProps({\n ref,\n tabIndex: nonInteractiveTriggerTabIndex,\n });\n const {\n \"aria-labelledby\": labelId,\n \"aria-describedby\": descriptionId,\n ...spanProps\n } = props;\n return (\n <span tabIndex={nonInteractiveTriggerTabIndex} {...spanProps}>\n {cloneElement(children as ReactElement<Record<string, unknown>>, {\n \"aria-labelledby\": labelId,\n \"aria-describedby\": descriptionId,\n })}\n </span>\n );\n }\n }, [context, ref, children]);\n\n if (!element) {\n throw new Error(\"Tooltip anchor must be a single valid React element\");\n }\n\n return element;\n};\n"],"names":[],"mappings":";;;;;;;AAuDA,MAAM,WAAW,CACf,UAC0B,WAAW,SAAS,CAAC,CAAC,MAAM;AAKjD,SAAS,QAAQ;AAAA,EACtB;AAAA,EACA,uBAAuB;AAAA,EACvB,gCAAgC;AAAA,EAChC,GAAG;AACL,GAAiD;AAC/C,QAAM,UAAU,WAAW,EAAE,sBAAsB,GAAG,OAAO;AAE7D,SACG,qBAAA,eAAe,UAAf,EAAwB,OAAO,SAC9B,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QAEC;AAAA,MAAA;AAAA,IACH;AAAA,yBACC,gBACC,EAAA,UAAA;AAAA,MAAC,oBAAA,QAAA,EAAK,IAAI,QAAQ,SACf,UAAA,SAAS,KAAK,IAAI,MAAM,QAAQ,MAAM,YACzC,CAAA;AAAA,0BACC,SAAQ,CAAA,CAAA;AAAA,IAAA,EACX,CAAA;AAAA,EAAA,GACF;AAEJ;AAEA,SAAS,UAAU;AACjB,QAAM,EAAE,SAAS,UAAU,IAAI,kBAAkB;AAC7C,MAAA,CAAC,QAAgB,QAAA;AAEf,QAAA,kBAAkB,OAAO,YAAY;AACrC,QAAA,YAAY,kBAAkB,SAAS;AAS3C,SAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,IAAI;AAAA,MACJ,WAAW,WAAW,OAAO,SAAS,gBAAgB;AAAA,MAErD,UAAA;AAAA,IAAA;AAAA,EACH;AAEJ;AAMA,SAAS,eAAe;AAAA,EACtB;AACF,GAAoD;AAC5C,QAAA;AAAA,IACJ,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,MACD,kBAAkB;AAGtB,MAAI,CAAC,QAAQ,YAAY,QAAgB,QAAA;AAEzC,6BACG,gBACC,EAAA,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAK,KAAK,KAAK;AAAA,MACf,OAAO,KAAK;AAAA,MACX,GAAG,KAAK;AAAA,MACR,GAAG,KAAK,iBAAiB;AAAA,MAC1B,WAAW,WAAW,OAAO,SAAS;AAAA,QACpC,CAAC,OAAO,SAAS,GAAG,YAAY,WAAW,CAAC;AAAA,MAAA,CAC7C;AAAA,MAED,UAAA;AAAA,QAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,KAAK;AAAA,YACL,SAAS;AAAA,YAET,OAAO;AAAA,YACP,QAAQ;AAAA,YACR,WAAW,OAAO;AAAA,UAAA;AAAA,QACpB;AAAA,QACC;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA,GAEL;AAEJ;AAYA,MAAM,gBAAwC,CAAC;AAAA,EAC7C;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,QAAM,UAAU,kBAAkB;AAIlC,QAAM,cAAe,UAAoD;AACzE,QAAM,MAAM,aAAa,CAAC,QAAQ,KAAK,cAAc,WAAW,CAAC;AAE3D,QAAA,UAAU,QAAQ,MAAM;AACxB,QAAA,CAAC,eAAe,QAAQ,EAAG;AAE/B,QAAI,sBAAsB;AACxB,YAAM,QAAQ,QAAQ,kBAAkB,EAAE,KAAK;AACxC,aAAA,aAAa,UAAU,KAAK;AAAA,IAAA,OAC9B;AAKC,YAAA,QAAQ,QAAQ,kBAAkB;AAAA,QACtC;AAAA,QACA,UAAU;AAAA,MAAA,CACX;AACK,YAAA;AAAA,QACJ,mBAAmB;AAAA,QACnB,oBAAoB;AAAA,QACpB,GAAG;AAAA,MAAA,IACD;AACJ,iCACG,QAAK,EAAA,UAAU,+BAAgC,GAAG,WAChD,uBAAa,UAAmD;AAAA,QAC/D,mBAAmB;AAAA,QACnB,oBAAoB;AAAA,MACrB,CAAA,GACH;AAAA,IAAA;AAAA,EAGH,GAAA,CAAC,SAAS,KAAK,QAAQ,CAAC;AAE3B,MAAI,CAAC,SAAS;AACN,UAAA,IAAI,MAAM,qDAAqD;AAAA,EAAA;AAGhE,SAAA;AACT;"}
1
+ {"version":3,"file":"Tooltip.js","sources":["../../../src/components/Tooltip/Tooltip.tsx"],"sourcesContent":["/*\nCopyright 2023, 2024 New Vector Ltd.\n\nSPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\nPlease see LICENSE files in the repository root for full details.\n*/\n\nimport { TooltipContext, useTooltipContext } from \"./TooltipContext\";\nimport {\n FloatingArrow,\n FloatingPortal,\n useMergeRefs,\n} from \"@floating-ui/react\";\nimport React, {\n PropsWithChildren,\n Ref,\n JSX,\n isValidElement,\n cloneElement,\n useMemo,\n ReactNode,\n FC,\n ReactElement,\n} from \"react\";\n\nimport classNames from \"classnames\";\nimport styles from \"./Tooltip.module.css\";\nimport {\n CommonUseTooltipProps,\n TooltipDescription,\n TooltipLabel,\n useTooltip,\n} from \"./useTooltip\";\n\n// Unfortunately Omit doesn't distribute nicely over sum types, so we have to\n// piece together the useTooltip options type by hand\ntype TooltipProps = Omit<CommonUseTooltipProps, \"isTriggerInteractive\"> &\n (TooltipLabel | TooltipDescription) & {\n /**\n * Whether the trigger element is interactive.\n * When trigger is interactive:\n * - tooltip will be shown after a 300ms delay.\n * When trigger is not interactive:\n * - tooltip will be shown instantly when pointer enters trigger.\n * - trigger will be wrapped in a span with a tab index from prop nonInteractiveTriggerTabIndex\n * @default true\n */\n isTriggerInteractive?: boolean;\n /**\n * The tab index for the non interactive trigger.\n * @default 0\n */\n nonInteractiveTriggerTabIndex?: number;\n };\n\nconst hasLabel = (\n props: TooltipLabel | TooltipDescription,\n): props is TooltipLabel => \"label\" in props && !!props.label;\n\n/**\n * A tooltip component\n */\nexport function Tooltip({\n children,\n isTriggerInteractive = true,\n nonInteractiveTriggerTabIndex = 0,\n ...props\n}: PropsWithChildren<TooltipProps>): JSX.Element {\n const context = useTooltip({ isTriggerInteractive, ...props });\n\n return (\n <TooltipContext.Provider value={context}>\n <TooltipAnchor\n isTriggerInteractive={isTriggerInteractive}\n nonInteractiveTriggerTabIndex={nonInteractiveTriggerTabIndex}\n >\n {children}\n </TooltipAnchor>\n <TooltipContent>\n <span id={context.labelId}>\n {hasLabel(props) ? props.label : props.description}\n </span>\n <Caption />\n </TooltipContent>\n </TooltipContext.Provider>\n );\n}\n\nfunction Caption() {\n const { caption, captionId } = useTooltipContext();\n if (!caption) return null;\n\n const isCaptionString = typeof caption === \"string\";\n const Container = isCaptionString ? \"span\" : \"div\";\n\n /**\n * Forcing dark theme, so that we have the correct contrast when\n * using the text color secondary on a solid dark background.\n * This is temporary and should only remain until we figure out\n * the approach to on-solid tokens\n **/\n return (\n <Container\n id={captionId}\n className={classNames(styles.caption, \"cpd-theme-dark\")}\n >\n {caption}\n </Container>\n );\n}\n\n/**\n * The content of the tooltip\n * @param children\n */\nfunction TooltipContent({\n children,\n}: Readonly<PropsWithChildren>): JSX.Element | null {\n const {\n context: floatingContext,\n open,\n arrowRef,\n purpose,\n ...rest\n } = useTooltipContext();\n\n // Label tooltips are kept in the DOM even when not visually open\n if (!open && purpose !== \"label\") return null;\n\n return (\n <FloatingPortal>\n <div\n ref={rest.refs.setFloating}\n style={rest.floatingStyles}\n {...rest.tooltipProps}\n {...rest.getFloatingProps()}\n className={classNames(styles.tooltip, {\n [styles.invisible]: purpose === \"label\" && !open,\n })}\n >\n <FloatingArrow\n ref={arrowRef}\n context={floatingContext}\n // design absolute value\n width={10}\n height={6}\n className={styles.arrow}\n />\n {children}\n </div>\n </FloatingPortal>\n );\n}\n\ninterface TooltipAnchorProps {\n children: ReactNode;\n isTriggerInteractive: boolean;\n nonInteractiveTriggerTabIndex?: number;\n}\n\n/**\n * The anchor of the tooltip\n * @param children\n */\nconst TooltipAnchor: FC<TooltipAnchorProps> = ({\n children,\n isTriggerInteractive,\n nonInteractiveTriggerTabIndex,\n}) => {\n const context = useTooltipContext();\n\n // The children can have a ref and we don't want to discard it\n // Doing a dirty cast to get the optional ref\n const childrenRef = (children as unknown as { ref?: Ref<HTMLElement> })?.ref;\n const ref = useMergeRefs([context.refs.setReference, childrenRef]);\n\n const element = useMemo(() => {\n if (!isValidElement(children)) return;\n\n if (isTriggerInteractive) {\n const props = context.getReferenceProps({\n // To support React 18, we need to explicitly pass the children's props. See https://github.com/element-hq/compound/issues/333\n // In React 19, this is not necessary. `getReferenceProps` is able to get the props directly from the ref.\n ...(typeof children.props === \"object\" ? children.props : {}),\n ref,\n });\n return cloneElement(children, props);\n } else {\n // For a non-interactive trigger, we want most of the props to go on the\n // span element that we provide, since that's what receives focus, but it\n // should still be the trigger that receives the label/description. It\n // would be wrong to label the span element, as it lacks a role.\n const props = context.getReferenceProps({\n ref,\n tabIndex: nonInteractiveTriggerTabIndex,\n });\n const {\n \"aria-labelledby\": labelId,\n \"aria-describedby\": descriptionId,\n ...spanProps\n } = props;\n return (\n <span tabIndex={nonInteractiveTriggerTabIndex} {...spanProps}>\n {cloneElement(children as ReactElement<Record<string, unknown>>, {\n \"aria-labelledby\": labelId,\n \"aria-describedby\": descriptionId,\n })}\n </span>\n );\n }\n }, [context, ref, children]);\n\n if (!element) {\n throw new Error(\"Tooltip anchor must be a single valid React element\");\n }\n\n return element;\n};\n"],"names":[],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -35,6 +35,7 @@ const InlineSpinner = require("./components/InlineSpinner/InlineSpinner.cjs");
35
35
  const Breadcrumb = require("./components/Breadcrumb/Breadcrumb.cjs");
36
36
  const VisualList = require("./components/VisualList/VisualList.cjs");
37
37
  const VisualListItem = require("./components/VisualList/VisualListItem.cjs");
38
+ const ChatFilter = require("./components/ChatFilter/ChatFilter.cjs");
38
39
  const Text$1 = require("./components/Form/Controls/Text/Text.cjs");
39
40
  const Action = require("./components/Form/Controls/Action/Action.cjs");
40
41
  const Password = require("./components/Form/Controls/Password/Password.cjs");
@@ -90,6 +91,7 @@ exports.InlineSpinner = InlineSpinner.InlineSpinner;
90
91
  exports.Breadcrumb = Breadcrumb.Breadcrumb;
91
92
  exports.VisualList = VisualList.VisualList;
92
93
  exports.VisualListItem = VisualListItem.VisualListItem;
94
+ exports.ChatFilter = ChatFilter.ChatFilter;
93
95
  exports.Control = Text$1.TextControl;
94
96
  exports.TextControl = Text$1.TextControl;
95
97
  exports.TextInput = Text$1.TextInput;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/index.d.ts CHANGED
@@ -29,6 +29,7 @@ export { Dropdown } from './components/Dropdown';
29
29
  export { InlineSpinner } from './components/InlineSpinner';
30
30
  export { Breadcrumb } from './components/Breadcrumb';
31
31
  export { VisualList, VisualListItem } from './components/VisualList';
32
+ export { ChatFilter } from './components/ChatFilter';
32
33
  export { TextControl, TextControl as Control, TextInput, ActionControl, ActionInput, PasswordControl, PasswordInput, MFAControl, MFAInput, CheckboxControl, CheckboxInput, CheckboxInput as Checkbox, RadioControl, RadioInput, RadioInput as Radio, ToggleControl, ToggleInput, ToggleInput as Toggle, Root, Submit, Message, ErrorMessage, HelpMessage, ValidityState, Field, InlineField, Label, EditInPlace, } from './components/Form';
33
34
  export * as Form from './components/Form';
34
35
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AACjD,OAAO,EACL,OAAO,EACP,EAAE,EACF,EAAE,EACF,EAAE,EACF,EAAE,EACF,EAAE,EACF,EAAE,GACH,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,+CAA+C,CAAC;AAC9E,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAErE,OAAO,EACL,WAAW,EAEX,WAAW,IAAI,OAAO,EACtB,SAAS,EACT,aAAa,EACb,WAAW,EACX,eAAe,EACf,aAAa,EACb,UAAU,EACV,QAAQ,EACR,eAAe,EACf,aAAa,EAEb,aAAa,IAAI,QAAQ,EACzB,YAAY,EACZ,UAAU,EAEV,UAAU,IAAI,KAAK,EACnB,aAAa,EACb,WAAW,EAEX,WAAW,IAAI,MAAM,EACrB,IAAI,EACJ,MAAM,EACN,OAAO,EACP,YAAY,EACZ,WAAW,EACX,aAAa,EACb,KAAK,EACL,WAAW,EACX,KAAK,EACL,WAAW,GACZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,IAAI,MAAM,mBAAmB,CAAC;AAE1C;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AACjD,OAAO,EACL,OAAO,EACP,EAAE,EACF,EAAE,EACF,EAAE,EACF,EAAE,EACF,EAAE,EACF,EAAE,GACH,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,+CAA+C,CAAC;AAC9E,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD,OAAO,EACL,WAAW,EAEX,WAAW,IAAI,OAAO,EACtB,SAAS,EACT,aAAa,EACb,WAAW,EACX,eAAe,EACf,aAAa,EACb,UAAU,EACV,QAAQ,EACR,eAAe,EACf,aAAa,EAEb,aAAa,IAAI,QAAQ,EACzB,YAAY,EACZ,UAAU,EAEV,UAAU,IAAI,KAAK,EACnB,aAAa,EACb,WAAW,EAEX,WAAW,IAAI,MAAM,EACrB,IAAI,EACJ,MAAM,EACN,OAAO,EACP,YAAY,EACZ,WAAW,EACX,aAAa,EACb,KAAK,EACL,WAAW,EACX,KAAK,EACL,WAAW,GACZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,IAAI,MAAM,mBAAmB,CAAC;AAE1C;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -33,6 +33,7 @@ import { InlineSpinner } from "./components/InlineSpinner/InlineSpinner.js";
33
33
  import { Breadcrumb } from "./components/Breadcrumb/Breadcrumb.js";
34
34
  import { VisualList } from "./components/VisualList/VisualList.js";
35
35
  import { VisualListItem } from "./components/VisualList/VisualListItem.js";
36
+ import { ChatFilter } from "./components/ChatFilter/ChatFilter.js";
36
37
  import { TextControl, TextControl as TextControl2, TextInput } from "./components/Form/Controls/Text/Text.js";
37
38
  import { ActionControl, ActionInput } from "./components/Form/Controls/Action/Action.js";
38
39
  import { PasswordControl, PasswordInput } from "./components/Form/Controls/Password/Password.js";
@@ -59,6 +60,7 @@ export {
59
60
  Body,
60
61
  Breadcrumb,
61
62
  Button,
63
+ ChatFilter,
62
64
  CheckboxInput as Checkbox,
63
65
  CheckboxControl,
64
66
  CheckboxInput2 as CheckboxInput,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/style.css CHANGED
@@ -594,7 +594,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
594
594
  Please see LICENSE files in the repository root for full details.
595
595
  */
596
596
 
597
- ._menu_eeuw6_8 {
597
+ ._menu_19sse_8 {
598
598
  border-radius: var(--cpd-space-3x);
599
599
  background: var(--cpd-color-bg-canvas-default);
600
600
 
@@ -616,56 +616,67 @@ Please see LICENSE files in the repository root for full details.
616
616
  --cpd-separator-inset: var(--cpd-space-4x);
617
617
  }
618
618
 
619
- @keyframes _slide-in_eeuw6_1 {
619
+ @keyframes _slide-in_19sse_1 {
620
620
  from {
621
621
  opacity: 0;
622
622
  transform: translate(0, var(--cpd-space-3x));
623
623
  }
624
624
  }
625
625
 
626
- @keyframes _slide-out_eeuw6_1 {
626
+ @keyframes _slide-out_19sse_1 {
627
627
  to {
628
628
  opacity: 0;
629
629
  transform: translate(0, var(--cpd-space-2x));
630
630
  }
631
631
  }
632
632
 
633
- ._menu_eeuw6_8[data-state="open"] {
634
- animation: _slide-in_eeuw6_1 180ms;
633
+ ._menu_19sse_8[data-state="open"] {
634
+ animation: _slide-in_19sse_1 180ms;
635
635
  }
636
636
 
637
- ._menu_eeuw6_8[data-state="closed"] {
638
- animation: _slide-out_eeuw6_1 110ms;
637
+ ._menu_19sse_8[data-state="closed"] {
638
+ animation: _slide-out_19sse_1 110ms;
639
639
  }
640
640
 
641
- @keyframes _fade-in_eeuw6_1 {
641
+ @keyframes _fade-in_19sse_1 {
642
642
  from {
643
643
  opacity: 0;
644
644
  }
645
645
  }
646
646
 
647
- @keyframes _fade-out_eeuw6_1 {
647
+ @keyframes _fade-out_19sse_1 {
648
648
  to {
649
649
  opacity: 0;
650
650
  }
651
651
  }
652
652
 
653
653
  @media (prefers-reduced-motion) {
654
- ._menu_eeuw6_8[data-state="open"] {
655
- animation-name: _fade-in_eeuw6_1;
654
+ ._menu_19sse_8[data-state="open"] {
655
+ animation-name: _fade-in_19sse_1;
656
656
  }
657
657
 
658
- ._menu_eeuw6_8[data-state="closed"] {
659
- animation-name: _fade-out_eeuw6_1;
658
+ ._menu_19sse_8[data-state="closed"] {
659
+ animation-name: _fade-out_19sse_1;
660
660
  }
661
661
  }
662
662
 
663
- ._title_eeuw6_74 {
663
+ ._title_19sse_74 {
664
+ /** Override MenuTitle margin top **/
665
+ margin-block-start: 0 !important;
666
+ }
667
+ /*
668
+ * Copyright 2025 New Vector Ltd
669
+ *
670
+ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
671
+ * Please see LICENSE files in the repository root for full details.
672
+ */
673
+
674
+ ._menu-title_1sgvx_8 {
664
675
  color: var(--cpd-color-text-secondary);
665
676
  padding-inline: var(--cpd-space-4x);
666
677
  padding-block-end: calc(var(--cpd-space-2x) - var(--cpd-border-width-1));
667
678
  border-block-end: var(--cpd-border-width-1) solid var(--cpd-color-gray-400);
668
- margin-block: 0 var(--cpd-space-2x);
679
+ margin-block: var(--cpd-space-2x);
669
680
  }
670
681
  /*
671
682
  Copyright 2023 New Vector Ltd.
@@ -2582,3 +2593,36 @@ Please see LICENSE files in the repository root for full details.
2582
2593
  ._visual-list-item-icon-destructive_1ma3e_26 {
2583
2594
  color: var(--cpd-color-icon-critical-primary);
2584
2595
  }
2596
+ /*
2597
+ * Copyright 2025 New Vector Ltd
2598
+ *
2599
+ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
2600
+ * Please see LICENSE files in the repository root for full details.
2601
+ */
2602
+
2603
+ ._chat-filter_5qdp0_8 {
2604
+ font: var(--cpd-font-body-sm-medium);
2605
+ color: var(--cpd-color-text-primary);
2606
+ background-color: transparent;
2607
+ border: var(--cpd-border-width-1) solid
2608
+ var(--cpd-color-border-interactive-secondary);
2609
+ border-radius: 99px;
2610
+ cursor: pointer;
2611
+ display: flex;
2612
+ align-items: center;
2613
+ justify-content: center;
2614
+ padding: var(--cpd-space-1x) var(--cpd-space-2x);
2615
+ }
2616
+
2617
+ @media (hover) {
2618
+ ._chat-filter_5qdp0_8:hover {
2619
+ border-color: var(--cpd-color-border-interactive-primary);
2620
+ background: var(--cpd-color-bg-subtle-primary);
2621
+ }
2622
+ }
2623
+
2624
+ ._chat-filter_5qdp0_8[aria-selected="true"] {
2625
+ border-color: var(--cpd-color-bg-action-primary-rest);
2626
+ background: var(--cpd-color-bg-action-primary-rest);
2627
+ color: var(--cpd-color-text-on-solid-primary);
2628
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vector-im/compound-web",
3
- "version": "7.6.3",
3
+ "version": "7.7.0",
4
4
  "description": "Compound components for the Web",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -0,0 +1,33 @@
1
+ /*
2
+ * Copyright 2025 New Vector Ltd
3
+ *
4
+ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5
+ * Please see LICENSE files in the repository root for full details.
6
+ */
7
+
8
+ .chat-filter {
9
+ font: var(--cpd-font-body-sm-medium);
10
+ color: var(--cpd-color-text-primary);
11
+ background-color: transparent;
12
+ border: var(--cpd-border-width-1) solid
13
+ var(--cpd-color-border-interactive-secondary);
14
+ border-radius: 99px;
15
+ cursor: pointer;
16
+ display: flex;
17
+ align-items: center;
18
+ justify-content: center;
19
+ padding: var(--cpd-space-1x) var(--cpd-space-2x);
20
+ }
21
+
22
+ @media (hover) {
23
+ .chat-filter:hover {
24
+ border-color: var(--cpd-color-border-interactive-primary);
25
+ background: var(--cpd-color-bg-subtle-primary);
26
+ }
27
+ }
28
+
29
+ .chat-filter[aria-selected="true"] {
30
+ border-color: var(--cpd-color-bg-action-primary-rest);
31
+ background: var(--cpd-color-bg-action-primary-rest);
32
+ color: var(--cpd-color-text-on-solid-primary);
33
+ }
@@ -0,0 +1,41 @@
1
+ /*
2
+ * Copyright 2025 New Vector Ltd
3
+ *
4
+ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5
+ * Please see LICENSE files in the repository root for full details.
6
+ */
7
+
8
+ import React, { ForwardedRef, forwardRef } from "react";
9
+ import {
10
+ UnstyledButton,
11
+ UnstyledButtonPropsFor,
12
+ } from "../Button/UnstyledButton";
13
+ import styles from "./ChatFilter.module.css";
14
+
15
+ type ChatFilterProps = Omit<UnstyledButtonPropsFor<"button">, "disabled"> & {
16
+ /**
17
+ * Whether the filter is selected.
18
+ */
19
+ selected?: boolean;
20
+ };
21
+
22
+ /**
23
+ * A chat filter button.
24
+ */
25
+ export const ChatFilter = forwardRef(function ChatFilter(
26
+ { children, selected, ...props }: ChatFilterProps,
27
+ ref: ForwardedRef<HTMLButtonElement>,
28
+ ): React.ReactElement {
29
+ return (
30
+ <UnstyledButton
31
+ {...props}
32
+ className={styles["chat-filter"]}
33
+ aria-selected={selected}
34
+ as="button"
35
+ ref={ref}
36
+ tabIndex={0}
37
+ >
38
+ {children}
39
+ </UnstyledButton>
40
+ );
41
+ });
@@ -0,0 +1,8 @@
1
+ /*
2
+ * Copyright 2025 New Vector Ltd
3
+ *
4
+ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5
+ * Please see LICENSE files in the repository root for full details.
6
+ */
7
+
8
+ export { ChatFilter } from "./ChatFilter";
@@ -0,0 +1,62 @@
1
+ /*
2
+ * Copyright 2025 New Vector Ltd
3
+ *
4
+ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5
+ * Please see LICENSE files in the repository root for full details.
6
+ */
7
+
8
+ import React, { ComponentProps, forwardRef, useCallback, useId } from "react";
9
+ import { MenuItem } from "./MenuItem";
10
+ import { CheckboxInput } from "../Form";
11
+
12
+ type Props = Pick<
13
+ ComponentProps<typeof MenuItem>,
14
+ "className" | "label" | "onSelect" | "disabled"
15
+ > & {
16
+ /**
17
+ * Whether the checkbox is checked.
18
+ */
19
+ checked: boolean;
20
+ };
21
+
22
+ /**
23
+ * A menu item with a checkbox control.
24
+ * Must be used within a compound Menu or other `menu` or `menubar` aria role subtree.
25
+ */
26
+ export const CheckboxMenuItem = forwardRef<HTMLInputElement, Props>(
27
+ function CheckboxMenuItem(
28
+ { className, label, onSelect, checked, disabled },
29
+ ref,
30
+ ) {
31
+ const toggleId = useId();
32
+ // The checkbox is controlled and we intend to ignore its events. We do need
33
+ // to at least set onChange though to make React happy.
34
+ const onChange = useCallback(() => {}, []);
35
+
36
+ // <label> elements are not allowed to have a role like menuitemcheckbox, so
37
+ // we must instead use a plain <div> for the menu item and use aria-checked
38
+ // etc. to communicate its state.
39
+ return (
40
+ <MenuItem
41
+ as="div"
42
+ role="menuitemcheckbox"
43
+ aria-checked={checked}
44
+ className={className}
45
+ label={label}
46
+ onSelect={onSelect}
47
+ disabled={disabled}
48
+ Icon={
49
+ <CheckboxInput
50
+ id={toggleId}
51
+ ref={ref}
52
+ // This is purely cosmetic; really the whole MenuItem is the toggle.
53
+ aria-hidden
54
+ checked={checked}
55
+ disabled={disabled}
56
+ onChange={onChange}
57
+ />
58
+ }
59
+ />
60
+ );
61
+ },
62
+ );
@@ -72,9 +72,6 @@ Please see LICENSE files in the repository root for full details.
72
72
  }
73
73
 
74
74
  .title {
75
- color: var(--cpd-color-text-secondary);
76
- padding-inline: var(--cpd-space-4x);
77
- padding-block-end: calc(var(--cpd-space-2x) - var(--cpd-border-width-1));
78
- border-block-end: var(--cpd-border-width-1) solid var(--cpd-color-gray-400);
79
- margin-block: 0 var(--cpd-space-2x);
75
+ /** Override MenuTitle margin top **/
76
+ margin-block-start: 0 !important;
80
77
  }
@@ -13,18 +13,7 @@ import React, {
13
13
  useId,
14
14
  } from "react";
15
15
  import styles from "./FloatingMenu.module.css";
16
- import { Text } from "../Typography/Text";
17
-
18
- interface TitleProps {
19
- title: string;
20
- id: string;
21
- }
22
-
23
- const MenuTitle: React.FC<TitleProps> = ({ title, id }) => (
24
- <Text as="h3" id={id} className={styles.title} size="sm" weight="semibold">
25
- {title}
26
- </Text>
27
- );
16
+ import { MenuTitle } from "./MenuTitle.tsx";
28
17
 
29
18
  interface Props extends ComponentPropsWithoutRef<"div"> {
30
19
  /**
@@ -62,7 +51,9 @@ export const FloatingMenu = forwardRef<HTMLDivElement, Props>(
62
51
  className={classnames(className, styles.menu)}
63
52
  {...props}
64
53
  >
65
- {showTitle && <MenuTitle title={title} id={titleId} />}
54
+ {showTitle && (
55
+ <MenuTitle className={styles.title} title={title} id={titleId} />
56
+ )}
66
57
  {children}
67
58
  </div>
68
59
  );
@@ -55,13 +55,22 @@ type Props<C extends MenuItemElement> = {
55
55
  */
56
56
  // This prop is required because it's rare to not want a selection handler
57
57
  onSelect: ((e: Event) => void) | null;
58
+ /**
59
+ * Event callback for when the item is clicked.
60
+ * @param e
61
+ */
62
+ onClick?: (e: Event) => void;
58
63
  /**
59
64
  * The color variant of the menu item.
60
65
  * @default primary
61
66
  */
62
67
  kind?: "primary" | "critical";
63
68
  disabled?: boolean;
64
- } & Omit<ComponentPropsWithoutRef<C>, "onSelect">;
69
+ /**
70
+ * Whether to hide the chevron navigation hint.
71
+ */
72
+ hideChevron?: boolean;
73
+ } & Omit<ComponentPropsWithoutRef<C>, "onSelect" | "onClick">;
65
74
 
66
75
  /**
67
76
  * An item within a menu, acting either as a navigation button, or simply a
@@ -79,6 +88,7 @@ export const MenuItem = <C extends MenuItemElement = "button">({
79
88
  children,
80
89
  onClick: onClickProp,
81
90
  disabled,
91
+ hideChevron,
82
92
  ...props
83
93
  }: Props<C>): React.ReactElement => {
84
94
  const Component = as ?? ("button" as ElementType);
@@ -143,7 +153,7 @@ export const MenuItem = <C extends MenuItemElement = "button">({
143
153
  )}
144
154
  {/* We use CSS to swap between this navigation hint and the provided
145
155
  children on hover - see the styles module. */}
146
- {(Component === "button" || Component === "a") && (
156
+ {!hideChevron && (Component === "button" || Component === "a") && (
147
157
  <ChevronRightIcon
148
158
  width={8}
149
159
  height={24}
@@ -0,0 +1,14 @@
1
+ /*
2
+ * Copyright 2025 New Vector Ltd
3
+ *
4
+ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5
+ * Please see LICENSE files in the repository root for full details.
6
+ */
7
+
8
+ .menu-title {
9
+ color: var(--cpd-color-text-secondary);
10
+ padding-inline: var(--cpd-space-4x);
11
+ padding-block-end: calc(var(--cpd-space-2x) - var(--cpd-border-width-1));
12
+ border-block-end: var(--cpd-border-width-1) solid var(--cpd-color-gray-400);
13
+ margin-block: var(--cpd-space-2x);
14
+ }
@@ -0,0 +1,40 @@
1
+ /*
2
+ * Copyright 2025 New Vector Ltd
3
+ *
4
+ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5
+ * Please see LICENSE files in the repository root for full details.
6
+ */
7
+
8
+ import React from "react";
9
+ import { Text } from "../Typography/Text.tsx";
10
+ import styles from "./MenuTitle.module.css";
11
+ import classnames from "classnames";
12
+
13
+ interface MenuTitleProps {
14
+ /**
15
+ * The title of the menu.
16
+ */
17
+ title: string;
18
+ /**
19
+ * The id of the menu title.
20
+ */
21
+ id?: string;
22
+ /**
23
+ * The CSS class.
24
+ */
25
+ className?: string;
26
+ }
27
+
28
+ export const MenuTitle: React.FC<MenuTitleProps> = ({
29
+ title,
30
+ id,
31
+ className,
32
+ }) => {
33
+ const classes = classnames(styles["menu-title"], className);
34
+
35
+ return (
36
+ <Text as="h3" id={id} className={classes} size="sm" weight="semibold">
37
+ {title}
38
+ </Text>
39
+ );
40
+ };
@@ -11,7 +11,7 @@ import { ToggleInput } from "../Form/Controls/Toggle";
11
11
 
12
12
  type Props = Pick<
13
13
  ComponentProps<typeof MenuItem>,
14
- "className" | "Icon" | "label" | "onSelect" | "disabled"
14
+ "className" | "Icon" | "label" | "onSelect" | "disabled" | "onClick"
15
15
  > & {
16
16
  /**
17
17
  * Whether the toggle is checked.
@@ -25,7 +25,7 @@ type Props = Pick<
25
25
  */
26
26
  export const ToggleMenuItem = forwardRef<HTMLInputElement, Props>(
27
27
  function ToggleMenuItem(
28
- { className, Icon, label, onSelect, checked, disabled },
28
+ { className, Icon, label, onSelect, checked, disabled, onClick },
29
29
  ref,
30
30
  ) {
31
31
  const toggleId = useId();
@@ -46,6 +46,7 @@ export const ToggleMenuItem = forwardRef<HTMLInputElement, Props>(
46
46
  label={label}
47
47
  onSelect={onSelect}
48
48
  disabled={disabled}
49
+ onClick={onClick}
49
50
  >
50
51
  <ToggleInput
51
52
  id={toggleId}
@@ -178,7 +178,12 @@ const TooltipAnchor: FC<TooltipAnchorProps> = ({
178
178
  if (!isValidElement(children)) return;
179
179
 
180
180
  if (isTriggerInteractive) {
181
- const props = context.getReferenceProps({ ref });
181
+ const props = context.getReferenceProps({
182
+ // To support React 18, we need to explicitly pass the children's props. See https://github.com/element-hq/compound/issues/333
183
+ // In React 19, this is not necessary. `getReferenceProps` is able to get the props directly from the ref.
184
+ ...(typeof children.props === "object" ? children.props : {}),
185
+ ref,
186
+ });
182
187
  return cloneElement(children, props);
183
188
  } else {
184
189
  // For a non-interactive trigger, we want most of the props to go on the
package/src/index.ts CHANGED
@@ -46,6 +46,7 @@ export { Dropdown } from "./components/Dropdown";
46
46
  export { InlineSpinner } from "./components/InlineSpinner";
47
47
  export { Breadcrumb } from "./components/Breadcrumb";
48
48
  export { VisualList, VisualListItem } from "./components/VisualList";
49
+ export { ChatFilter } from "./components/ChatFilter";
49
50
 
50
51
  export {
51
52
  TextControl,