@vector-im/compound-web 7.10.2 → 7.12.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.
- package/dist/components/Dropdown/Dropdown.cjs +1 -0
- package/dist/components/Dropdown/Dropdown.cjs.map +1 -1
- package/dist/components/Dropdown/Dropdown.js +1 -0
- package/dist/components/Dropdown/Dropdown.js.map +1 -1
- package/dist/components/Menu/ContextMenu.cjs +2 -1
- package/dist/components/Menu/ContextMenu.cjs.map +1 -1
- package/dist/components/Menu/ContextMenu.d.ts +5 -0
- package/dist/components/Menu/ContextMenu.d.ts.map +1 -1
- package/dist/components/Menu/ContextMenu.js +2 -1
- package/dist/components/Menu/ContextMenu.js.map +1 -1
- package/dist/components/Menu/RadioMenuItem.cjs +38 -0
- package/dist/components/Menu/RadioMenuItem.cjs.map +1 -0
- package/dist/components/Menu/RadioMenuItem.d.ts +23 -0
- package/dist/components/Menu/RadioMenuItem.d.ts.map +1 -0
- package/dist/components/Menu/RadioMenuItem.js +38 -0
- package/dist/components/Menu/RadioMenuItem.js.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/src/components/Menu/ContextMenu.tsx +9 -1
- package/src/components/Menu/RadioMenuItem.tsx +62 -0
- package/src/index.ts +1 -0
- package/dist/{style.css → style.css.css} +119 -119
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dropdown.cjs","sources":["../../../src/components/Dropdown/Dropdown.tsx"],"sourcesContent":["/*\nCopyright 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 ChevronDown from \"@vector-im/compound-design-tokens/assets/web/icons/chevron-down\";\nimport Check from \"@vector-im/compound-design-tokens/assets/web/icons/check\";\nimport Error from \"@vector-im/compound-design-tokens/assets/web/icons/error-solid\";\n\nimport React, {\n Dispatch,\n forwardRef,\n HTMLProps,\n memo,\n RefObject,\n SetStateAction,\n useCallback,\n useEffect,\n useRef,\n useState,\n KeyboardEvent,\n useMemo,\n} from \"react\";\n\nimport classNames from \"classnames\";\n\nimport styles from \"./Dropdown.module.css\";\nimport { useId } from \"@floating-ui/react\";\n\ntype DropdownProps = {\n /**\n * The CSS class name.\n */\n className?: string;\n /**\n * The controlled value of the dropdown.\n */\n value?: string;\n /**\n * The default value of the dropdown, used when uncontrolled.\n */\n defaultValue?: string;\n /**\n * The values of the dropdown.\n * [value, text]\n */\n values: [string, string][];\n /**\n * The placeholder text.\n */\n placeholder: string;\n /**\n * The label to display at the top of the dropdown\n */\n label: string;\n /**\n * The help label to display at the bottom of the dropdown\n */\n helpLabel?: string;\n /**\n * Callback for when the value changes.\n * @param value\n */\n onValueChange?: (value: string) => void;\n /**\n * The error message to display.\n */\n error?: string;\n};\n\n/**\n * The dropdown content.\n */\nexport const Dropdown = forwardRef<HTMLButtonElement, DropdownProps>(\n function Dropdown(\n {\n className,\n label,\n placeholder,\n helpLabel,\n onValueChange,\n error,\n value: controlledValue,\n defaultValue,\n values,\n ...props\n },\n ref,\n ) {\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n const value = controlledValue ?? uncontrolledValue;\n const text = useMemo(\n () =>\n value === undefined\n ? placeholder\n : (values.find(([v]) => v === value)?.[1] ?? placeholder),\n [value, values, placeholder],\n );\n\n const setValue = useCallback(\n (value: string) => {\n setUncontrolledValue(value);\n onValueChange?.(value);\n },\n [setUncontrolledValue, onValueChange],\n );\n\n const [open, setOpen, dropdownRef] = useOpen();\n const { listRef, onComboboxKeyDown, onOptionKeyDown } = useKeyboardShortcut(\n open,\n setOpen,\n setValue,\n );\n\n const buttonRef = useRef<HTMLButtonElement | null>(null);\n useEffect(() => {\n // Focus the button when the value is set\n // Test if the value is undefined to avoid focusing on the first render\n if (value !== undefined) buttonRef.current?.focus();\n }, [value]);\n\n const hasPlaceholder = text === placeholder;\n const buttonClasses = classNames({\n [styles.placeholder]: hasPlaceholder,\n });\n const borderClasses = classNames(styles.border, {\n [styles.open]: open,\n });\n const contentClasses = classNames(styles.content, {\n [styles.open]: open,\n });\n\n /**\n * Ids for accessibility.\n */\n const labelId = useId();\n const contentId = useId();\n\n return (\n <div\n ref={dropdownRef}\n className={classNames(className, styles.container)}\n aria-invalid={Boolean(error)}\n >\n <label id={labelId}>{label}</label>\n <button\n className={buttonClasses}\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n aria-labelledby={labelId}\n aria-controls={contentId}\n aria-expanded={open}\n ref={(element) => {\n // Private ref to focus the button\n buttonRef.current = element;\n // Handle forwarded ref\n if (typeof ref === \"function\") {\n ref(element);\n } else if (ref) {\n ref.current = element;\n }\n }}\n onClick={() => setOpen((_open) => !_open)}\n onKeyDown={onComboboxKeyDown}\n {...props}\n >\n {text}\n <ChevronDown width=\"24\" height=\"24\" />\n </button>\n <div className={borderClasses} />\n <div className={contentClasses}>\n <ul\n ref={listRef}\n id={contentId}\n role=\"listbox\"\n className={styles.content}\n >\n {values.map(([v, text]) => (\n <DropdownItem\n key={v}\n isDisplayed={open}\n isSelected={value === v}\n onClick={() => {\n setOpen(false);\n setValue(v);\n }}\n onKeyDown={(e) => onOptionKeyDown(e, v)}\n >\n {text}\n </DropdownItem>\n ))}\n </ul>\n </div>\n {!error && helpLabel && (\n <span className={styles.help}>{helpLabel}</span>\n )}\n {error && (\n <span className={styles.error}>\n <Error width=\"20\" height=\"20\" />\n {error}\n </span>\n )}\n </div>\n );\n },\n);\n\ntype DropdownItemProps = HTMLProps<HTMLLIElement> & {\n /**\n * Whether the dropdown item is selected.\n */\n isSelected: boolean;\n /**\n * Whether the dropdown item is displayed.\n */\n isDisplayed: boolean;\n /**\n * The text to display in the dropdown item.\n */\n children: string;\n};\n\n/**\n * A dropdown item component.\n */\nconst DropdownItem = memo(function DropdownItem({\n children,\n isSelected,\n isDisplayed,\n ...props\n}: DropdownItemProps) {\n const ref = useRef<HTMLLIElement>(null);\n\n // Focus the item if the dropdown is open and the item is already selected\n useEffect(() => {\n if (isSelected && isDisplayed) {\n ref.current?.focus();\n }\n }, [isSelected, isDisplayed]);\n\n return (\n <li\n tabIndex={0}\n role=\"option\"\n ref={ref}\n aria-selected={isSelected}\n {...props}\n >\n {children} {isSelected && <Check width=\"20\" height=\"20\" />}\n </li>\n );\n});\n\n/**\n * A hook to manage the open state of the dropdown.\n */\nfunction useOpen(): [\n boolean,\n Dispatch<SetStateAction<boolean>>,\n RefObject<HTMLDivElement | null>,\n] {\n const [open, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement | null>(null);\n\n // If the user clicks outside the dropdown, close it\n useEffect(() => {\n const closeIfOutside = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n\n document.addEventListener(\"click\", closeIfOutside);\n return () => document.removeEventListener(\"click\", closeIfOutside);\n }, [setOpen]);\n\n return [open, setOpen, ref];\n}\n\n/**\n * A hook to manage the keyboard shortcuts of the dropdown.\n * @param open - the dropdown open state.\n * @param setOpen - the dropdown open state setter.\n * @param setValue - set the selected value and text\n */\nfunction useKeyboardShortcut(\n open: boolean,\n setOpen: Dispatch<SetStateAction<boolean>>,\n setValue: (value: string) => void,\n) {\n const listRef = useRef<HTMLUListElement>(null);\n const onComboboxKeyDown = useCallback(\n ({ key }: KeyboardEvent) => {\n switch (key) {\n // Enter and Space already managed because it's a button\n case \"Escape\":\n setOpen(false);\n break;\n case \"ArrowDown\":\n setOpen(true);\n // If open, focus the first element\n if (open) {\n (listRef.current?.firstElementChild as HTMLElement)?.focus();\n }\n break;\n case \"ArrowUp\":\n setOpen(true);\n break;\n case \"Home\": {\n setOpen(true);\n // Wait for the dropdown to be opened\n Promise.resolve().then(() => {\n (listRef.current?.firstElementChild as HTMLElement)?.focus();\n });\n break;\n }\n case \"End\": {\n setOpen(true);\n // Wait for the dropdown to be opened\n Promise.resolve().then(() => {\n (listRef.current?.lastElementChild as HTMLElement)?.focus();\n });\n break;\n }\n }\n },\n [listRef, open, setOpen],\n );\n\n const onOptionKeyDown = useCallback(\n (evt: KeyboardEvent, value: string) => {\n const { key, altKey } = evt;\n evt.stopPropagation();\n evt.preventDefault();\n\n switch (key) {\n case \"Enter\":\n case \" \": {\n setValue(value);\n setOpen(false);\n break;\n }\n case \"Tab\":\n case \"Escape\":\n setOpen(false);\n break;\n case \"ArrowDown\": {\n const currentFocus = document.activeElement;\n if (listRef.current?.contains(currentFocus) && currentFocus) {\n (currentFocus.nextElementSibling as HTMLElement)?.focus();\n }\n break;\n }\n case \"ArrowUp\": {\n if (altKey) {\n setValue(value);\n setOpen(false);\n } else {\n const currentFocus = document.activeElement;\n if (listRef.current?.contains(currentFocus) && currentFocus) {\n (currentFocus.previousElementSibling as HTMLElement)?.focus();\n }\n }\n break;\n }\n case \"Home\": {\n (listRef.current?.firstElementChild as HTMLElement)?.focus();\n break;\n }\n case \"End\": {\n (listRef.current?.lastElementChild as HTMLElement)?.focus();\n break;\n }\n }\n },\n [listRef, setValue, setOpen],\n );\n\n return { listRef, onComboboxKeyDown, onOptionKeyDown };\n}\n"],"names":["forwardRef","Dropdown","useState","useMemo","useCallback","value","useRef","useEffect","styles","useId","jsxs","jsx","text","Error","memo","DropdownItem","Check"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Dropdown.cjs","sources":["../../../src/components/Dropdown/Dropdown.tsx"],"sourcesContent":["/*\nCopyright 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 ChevronDown from \"@vector-im/compound-design-tokens/assets/web/icons/chevron-down\";\nimport Check from \"@vector-im/compound-design-tokens/assets/web/icons/check\";\nimport Error from \"@vector-im/compound-design-tokens/assets/web/icons/error-solid\";\n\nimport React, {\n Dispatch,\n forwardRef,\n HTMLProps,\n memo,\n RefObject,\n SetStateAction,\n useCallback,\n useEffect,\n useRef,\n useState,\n KeyboardEvent,\n useMemo,\n} from \"react\";\n\nimport classNames from \"classnames\";\n\nimport styles from \"./Dropdown.module.css\";\nimport { useId } from \"@floating-ui/react\";\n\ntype DropdownProps = {\n /**\n * The CSS class name.\n */\n className?: string;\n /**\n * The controlled value of the dropdown.\n */\n value?: string;\n /**\n * The default value of the dropdown, used when uncontrolled.\n */\n defaultValue?: string;\n /**\n * The values of the dropdown.\n * [value, text]\n */\n values: [string, string][];\n /**\n * The placeholder text.\n */\n placeholder: string;\n /**\n * The label to display at the top of the dropdown\n */\n label: string;\n /**\n * The help label to display at the bottom of the dropdown\n */\n helpLabel?: string;\n /**\n * Callback for when the value changes.\n * @param value\n */\n onValueChange?: (value: string) => void;\n /**\n * The error message to display.\n */\n error?: string;\n};\n\n/**\n * The dropdown content.\n */\nexport const Dropdown = forwardRef<HTMLButtonElement, DropdownProps>(\n function Dropdown(\n {\n className,\n label,\n placeholder,\n helpLabel,\n onValueChange,\n error,\n value: controlledValue,\n defaultValue,\n values,\n ...props\n },\n ref,\n ) {\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n const value = controlledValue ?? uncontrolledValue;\n const text = useMemo(\n () =>\n value === undefined\n ? placeholder\n : (values.find(([v]) => v === value)?.[1] ?? placeholder),\n [value, values, placeholder],\n );\n\n const setValue = useCallback(\n (value: string) => {\n setUncontrolledValue(value);\n onValueChange?.(value);\n },\n [setUncontrolledValue, onValueChange],\n );\n\n const [open, setOpen, dropdownRef] = useOpen();\n const { listRef, onComboboxKeyDown, onOptionKeyDown } = useKeyboardShortcut(\n open,\n setOpen,\n setValue,\n );\n\n const buttonRef = useRef<HTMLButtonElement | null>(null);\n useEffect(() => {\n // Focus the button when the value is set\n // Test if the value is undefined to avoid focusing on the first render\n if (value !== undefined) buttonRef.current?.focus();\n }, [value]);\n\n const hasPlaceholder = text === placeholder;\n const buttonClasses = classNames({\n [styles.placeholder]: hasPlaceholder,\n });\n const borderClasses = classNames(styles.border, {\n [styles.open]: open,\n });\n const contentClasses = classNames(styles.content, {\n [styles.open]: open,\n });\n\n /**\n * Ids for accessibility.\n */\n const labelId = useId();\n const contentId = useId();\n\n return (\n <div\n ref={dropdownRef}\n className={classNames(className, styles.container)}\n aria-invalid={Boolean(error)}\n >\n <label id={labelId}>{label}</label>\n <button\n className={buttonClasses}\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n aria-labelledby={labelId}\n aria-controls={contentId}\n aria-expanded={open}\n ref={(element) => {\n // Private ref to focus the button\n buttonRef.current = element;\n // Handle forwarded ref\n if (typeof ref === \"function\") {\n ref(element);\n } else if (ref) {\n ref.current = element;\n }\n }}\n onClick={() => setOpen((_open) => !_open)}\n onKeyDown={onComboboxKeyDown}\n {...props}\n >\n {text}\n <ChevronDown width=\"24\" height=\"24\" />\n </button>\n <div className={borderClasses} />\n <div className={contentClasses}>\n <ul\n ref={listRef}\n id={contentId}\n role=\"listbox\"\n className={styles.content}\n >\n {values.map(([v, text]) => (\n <DropdownItem\n key={v}\n isDisplayed={open}\n isSelected={value === v}\n onClick={() => {\n setOpen(false);\n setValue(v);\n }}\n onKeyDown={(e) => onOptionKeyDown(e, v)}\n >\n {text}\n </DropdownItem>\n ))}\n </ul>\n </div>\n {!error && helpLabel && (\n <span className={styles.help}>{helpLabel}</span>\n )}\n {error && (\n <span className={styles.error}>\n <Error width=\"20\" height=\"20\" />\n {error}\n </span>\n )}\n </div>\n );\n },\n);\n\ntype DropdownItemProps = HTMLProps<HTMLLIElement> & {\n /**\n * Whether the dropdown item is selected.\n */\n isSelected: boolean;\n /**\n * Whether the dropdown item is displayed.\n */\n isDisplayed: boolean;\n /**\n * The text to display in the dropdown item.\n */\n children: string;\n};\n\n/**\n * A dropdown item component.\n */\nconst DropdownItem = memo(function DropdownItem({\n children,\n isSelected,\n isDisplayed,\n ...props\n}: DropdownItemProps) {\n const ref = useRef<HTMLLIElement>(null);\n\n // Focus the item if the dropdown is open and the item is already selected\n useEffect(() => {\n if (isSelected && isDisplayed) {\n ref.current?.focus();\n }\n }, [isSelected, isDisplayed]);\n\n return (\n <li\n tabIndex={0}\n role=\"option\"\n ref={ref}\n aria-selected={isSelected}\n {...props}\n >\n {children} {isSelected && <Check width=\"20\" height=\"20\" />}\n </li>\n );\n});\n\n/**\n * A hook to manage the open state of the dropdown.\n */\nfunction useOpen(): [\n boolean,\n Dispatch<SetStateAction<boolean>>,\n RefObject<HTMLDivElement | null>,\n] {\n const [open, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement | null>(null);\n\n // If the user clicks outside the dropdown, close it\n useEffect(() => {\n const closeIfOutside = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n\n document.addEventListener(\"click\", closeIfOutside);\n return () => document.removeEventListener(\"click\", closeIfOutside);\n }, [setOpen]);\n\n return [open, setOpen, ref];\n}\n\n/**\n * A hook to manage the keyboard shortcuts of the dropdown.\n * @param open - the dropdown open state.\n * @param setOpen - the dropdown open state setter.\n * @param setValue - set the selected value and text\n */\nfunction useKeyboardShortcut(\n open: boolean,\n setOpen: Dispatch<SetStateAction<boolean>>,\n setValue: (value: string) => void,\n) {\n const listRef = useRef<HTMLUListElement>(null);\n const onComboboxKeyDown = useCallback(\n ({ key }: KeyboardEvent) => {\n switch (key) {\n // Enter and Space already managed because it's a button\n case \"Escape\":\n setOpen(false);\n break;\n case \"ArrowDown\":\n setOpen(true);\n // If open, focus the first element\n if (open) {\n (listRef.current?.firstElementChild as HTMLElement)?.focus();\n }\n break;\n case \"ArrowUp\":\n setOpen(true);\n break;\n case \"Home\": {\n setOpen(true);\n // Wait for the dropdown to be opened\n Promise.resolve().then(() => {\n (listRef.current?.firstElementChild as HTMLElement)?.focus();\n });\n break;\n }\n case \"End\": {\n setOpen(true);\n // Wait for the dropdown to be opened\n Promise.resolve().then(() => {\n (listRef.current?.lastElementChild as HTMLElement)?.focus();\n });\n break;\n }\n }\n },\n [listRef, open, setOpen],\n );\n\n const onOptionKeyDown = useCallback(\n (evt: KeyboardEvent, value: string) => {\n const { key, altKey } = evt;\n evt.stopPropagation();\n evt.preventDefault();\n\n switch (key) {\n case \"Enter\":\n case \" \": {\n setValue(value);\n setOpen(false);\n break;\n }\n case \"Tab\":\n case \"Escape\":\n setOpen(false);\n break;\n case \"ArrowDown\": {\n const currentFocus = document.activeElement;\n if (listRef.current?.contains(currentFocus) && currentFocus) {\n (currentFocus.nextElementSibling as HTMLElement)?.focus();\n }\n break;\n }\n case \"ArrowUp\": {\n if (altKey) {\n setValue(value);\n setOpen(false);\n } else {\n const currentFocus = document.activeElement;\n if (listRef.current?.contains(currentFocus) && currentFocus) {\n (currentFocus.previousElementSibling as HTMLElement)?.focus();\n }\n }\n break;\n }\n case \"Home\": {\n (listRef.current?.firstElementChild as HTMLElement)?.focus();\n break;\n }\n case \"End\": {\n (listRef.current?.lastElementChild as HTMLElement)?.focus();\n break;\n }\n }\n },\n [listRef, setValue, setOpen],\n );\n\n return { listRef, onComboboxKeyDown, onOptionKeyDown };\n}\n"],"names":["forwardRef","Dropdown","useState","useMemo","useCallback","value","useRef","useEffect","styles","useId","jsxs","jsx","text","Error","memo","DropdownItem","Check"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dropdown.js","sources":["../../../src/components/Dropdown/Dropdown.tsx"],"sourcesContent":["/*\nCopyright 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 ChevronDown from \"@vector-im/compound-design-tokens/assets/web/icons/chevron-down\";\nimport Check from \"@vector-im/compound-design-tokens/assets/web/icons/check\";\nimport Error from \"@vector-im/compound-design-tokens/assets/web/icons/error-solid\";\n\nimport React, {\n Dispatch,\n forwardRef,\n HTMLProps,\n memo,\n RefObject,\n SetStateAction,\n useCallback,\n useEffect,\n useRef,\n useState,\n KeyboardEvent,\n useMemo,\n} from \"react\";\n\nimport classNames from \"classnames\";\n\nimport styles from \"./Dropdown.module.css\";\nimport { useId } from \"@floating-ui/react\";\n\ntype DropdownProps = {\n /**\n * The CSS class name.\n */\n className?: string;\n /**\n * The controlled value of the dropdown.\n */\n value?: string;\n /**\n * The default value of the dropdown, used when uncontrolled.\n */\n defaultValue?: string;\n /**\n * The values of the dropdown.\n * [value, text]\n */\n values: [string, string][];\n /**\n * The placeholder text.\n */\n placeholder: string;\n /**\n * The label to display at the top of the dropdown\n */\n label: string;\n /**\n * The help label to display at the bottom of the dropdown\n */\n helpLabel?: string;\n /**\n * Callback for when the value changes.\n * @param value\n */\n onValueChange?: (value: string) => void;\n /**\n * The error message to display.\n */\n error?: string;\n};\n\n/**\n * The dropdown content.\n */\nexport const Dropdown = forwardRef<HTMLButtonElement, DropdownProps>(\n function Dropdown(\n {\n className,\n label,\n placeholder,\n helpLabel,\n onValueChange,\n error,\n value: controlledValue,\n defaultValue,\n values,\n ...props\n },\n ref,\n ) {\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n const value = controlledValue ?? uncontrolledValue;\n const text = useMemo(\n () =>\n value === undefined\n ? placeholder\n : (values.find(([v]) => v === value)?.[1] ?? placeholder),\n [value, values, placeholder],\n );\n\n const setValue = useCallback(\n (value: string) => {\n setUncontrolledValue(value);\n onValueChange?.(value);\n },\n [setUncontrolledValue, onValueChange],\n );\n\n const [open, setOpen, dropdownRef] = useOpen();\n const { listRef, onComboboxKeyDown, onOptionKeyDown } = useKeyboardShortcut(\n open,\n setOpen,\n setValue,\n );\n\n const buttonRef = useRef<HTMLButtonElement | null>(null);\n useEffect(() => {\n // Focus the button when the value is set\n // Test if the value is undefined to avoid focusing on the first render\n if (value !== undefined) buttonRef.current?.focus();\n }, [value]);\n\n const hasPlaceholder = text === placeholder;\n const buttonClasses = classNames({\n [styles.placeholder]: hasPlaceholder,\n });\n const borderClasses = classNames(styles.border, {\n [styles.open]: open,\n });\n const contentClasses = classNames(styles.content, {\n [styles.open]: open,\n });\n\n /**\n * Ids for accessibility.\n */\n const labelId = useId();\n const contentId = useId();\n\n return (\n <div\n ref={dropdownRef}\n className={classNames(className, styles.container)}\n aria-invalid={Boolean(error)}\n >\n <label id={labelId}>{label}</label>\n <button\n className={buttonClasses}\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n aria-labelledby={labelId}\n aria-controls={contentId}\n aria-expanded={open}\n ref={(element) => {\n // Private ref to focus the button\n buttonRef.current = element;\n // Handle forwarded ref\n if (typeof ref === \"function\") {\n ref(element);\n } else if (ref) {\n ref.current = element;\n }\n }}\n onClick={() => setOpen((_open) => !_open)}\n onKeyDown={onComboboxKeyDown}\n {...props}\n >\n {text}\n <ChevronDown width=\"24\" height=\"24\" />\n </button>\n <div className={borderClasses} />\n <div className={contentClasses}>\n <ul\n ref={listRef}\n id={contentId}\n role=\"listbox\"\n className={styles.content}\n >\n {values.map(([v, text]) => (\n <DropdownItem\n key={v}\n isDisplayed={open}\n isSelected={value === v}\n onClick={() => {\n setOpen(false);\n setValue(v);\n }}\n onKeyDown={(e) => onOptionKeyDown(e, v)}\n >\n {text}\n </DropdownItem>\n ))}\n </ul>\n </div>\n {!error && helpLabel && (\n <span className={styles.help}>{helpLabel}</span>\n )}\n {error && (\n <span className={styles.error}>\n <Error width=\"20\" height=\"20\" />\n {error}\n </span>\n )}\n </div>\n );\n },\n);\n\ntype DropdownItemProps = HTMLProps<HTMLLIElement> & {\n /**\n * Whether the dropdown item is selected.\n */\n isSelected: boolean;\n /**\n * Whether the dropdown item is displayed.\n */\n isDisplayed: boolean;\n /**\n * The text to display in the dropdown item.\n */\n children: string;\n};\n\n/**\n * A dropdown item component.\n */\nconst DropdownItem = memo(function DropdownItem({\n children,\n isSelected,\n isDisplayed,\n ...props\n}: DropdownItemProps) {\n const ref = useRef<HTMLLIElement>(null);\n\n // Focus the item if the dropdown is open and the item is already selected\n useEffect(() => {\n if (isSelected && isDisplayed) {\n ref.current?.focus();\n }\n }, [isSelected, isDisplayed]);\n\n return (\n <li\n tabIndex={0}\n role=\"option\"\n ref={ref}\n aria-selected={isSelected}\n {...props}\n >\n {children} {isSelected && <Check width=\"20\" height=\"20\" />}\n </li>\n );\n});\n\n/**\n * A hook to manage the open state of the dropdown.\n */\nfunction useOpen(): [\n boolean,\n Dispatch<SetStateAction<boolean>>,\n RefObject<HTMLDivElement | null>,\n] {\n const [open, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement | null>(null);\n\n // If the user clicks outside the dropdown, close it\n useEffect(() => {\n const closeIfOutside = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n\n document.addEventListener(\"click\", closeIfOutside);\n return () => document.removeEventListener(\"click\", closeIfOutside);\n }, [setOpen]);\n\n return [open, setOpen, ref];\n}\n\n/**\n * A hook to manage the keyboard shortcuts of the dropdown.\n * @param open - the dropdown open state.\n * @param setOpen - the dropdown open state setter.\n * @param setValue - set the selected value and text\n */\nfunction useKeyboardShortcut(\n open: boolean,\n setOpen: Dispatch<SetStateAction<boolean>>,\n setValue: (value: string) => void,\n) {\n const listRef = useRef<HTMLUListElement>(null);\n const onComboboxKeyDown = useCallback(\n ({ key }: KeyboardEvent) => {\n switch (key) {\n // Enter and Space already managed because it's a button\n case \"Escape\":\n setOpen(false);\n break;\n case \"ArrowDown\":\n setOpen(true);\n // If open, focus the first element\n if (open) {\n (listRef.current?.firstElementChild as HTMLElement)?.focus();\n }\n break;\n case \"ArrowUp\":\n setOpen(true);\n break;\n case \"Home\": {\n setOpen(true);\n // Wait for the dropdown to be opened\n Promise.resolve().then(() => {\n (listRef.current?.firstElementChild as HTMLElement)?.focus();\n });\n break;\n }\n case \"End\": {\n setOpen(true);\n // Wait for the dropdown to be opened\n Promise.resolve().then(() => {\n (listRef.current?.lastElementChild as HTMLElement)?.focus();\n });\n break;\n }\n }\n },\n [listRef, open, setOpen],\n );\n\n const onOptionKeyDown = useCallback(\n (evt: KeyboardEvent, value: string) => {\n const { key, altKey } = evt;\n evt.stopPropagation();\n evt.preventDefault();\n\n switch (key) {\n case \"Enter\":\n case \" \": {\n setValue(value);\n setOpen(false);\n break;\n }\n case \"Tab\":\n case \"Escape\":\n setOpen(false);\n break;\n case \"ArrowDown\": {\n const currentFocus = document.activeElement;\n if (listRef.current?.contains(currentFocus) && currentFocus) {\n (currentFocus.nextElementSibling as HTMLElement)?.focus();\n }\n break;\n }\n case \"ArrowUp\": {\n if (altKey) {\n setValue(value);\n setOpen(false);\n } else {\n const currentFocus = document.activeElement;\n if (listRef.current?.contains(currentFocus) && currentFocus) {\n (currentFocus.previousElementSibling as HTMLElement)?.focus();\n }\n }\n break;\n }\n case \"Home\": {\n (listRef.current?.firstElementChild as HTMLElement)?.focus();\n break;\n }\n case \"End\": {\n (listRef.current?.lastElementChild as HTMLElement)?.focus();\n break;\n }\n }\n },\n [listRef, setValue, setOpen],\n );\n\n return { listRef, onComboboxKeyDown, onOptionKeyDown };\n}\n"],"names":["Dropdown","value","text","Error","DropdownItem","Check"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Dropdown.js","sources":["../../../src/components/Dropdown/Dropdown.tsx"],"sourcesContent":["/*\nCopyright 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 ChevronDown from \"@vector-im/compound-design-tokens/assets/web/icons/chevron-down\";\nimport Check from \"@vector-im/compound-design-tokens/assets/web/icons/check\";\nimport Error from \"@vector-im/compound-design-tokens/assets/web/icons/error-solid\";\n\nimport React, {\n Dispatch,\n forwardRef,\n HTMLProps,\n memo,\n RefObject,\n SetStateAction,\n useCallback,\n useEffect,\n useRef,\n useState,\n KeyboardEvent,\n useMemo,\n} from \"react\";\n\nimport classNames from \"classnames\";\n\nimport styles from \"./Dropdown.module.css\";\nimport { useId } from \"@floating-ui/react\";\n\ntype DropdownProps = {\n /**\n * The CSS class name.\n */\n className?: string;\n /**\n * The controlled value of the dropdown.\n */\n value?: string;\n /**\n * The default value of the dropdown, used when uncontrolled.\n */\n defaultValue?: string;\n /**\n * The values of the dropdown.\n * [value, text]\n */\n values: [string, string][];\n /**\n * The placeholder text.\n */\n placeholder: string;\n /**\n * The label to display at the top of the dropdown\n */\n label: string;\n /**\n * The help label to display at the bottom of the dropdown\n */\n helpLabel?: string;\n /**\n * Callback for when the value changes.\n * @param value\n */\n onValueChange?: (value: string) => void;\n /**\n * The error message to display.\n */\n error?: string;\n};\n\n/**\n * The dropdown content.\n */\nexport const Dropdown = forwardRef<HTMLButtonElement, DropdownProps>(\n function Dropdown(\n {\n className,\n label,\n placeholder,\n helpLabel,\n onValueChange,\n error,\n value: controlledValue,\n defaultValue,\n values,\n ...props\n },\n ref,\n ) {\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n const value = controlledValue ?? uncontrolledValue;\n const text = useMemo(\n () =>\n value === undefined\n ? placeholder\n : (values.find(([v]) => v === value)?.[1] ?? placeholder),\n [value, values, placeholder],\n );\n\n const setValue = useCallback(\n (value: string) => {\n setUncontrolledValue(value);\n onValueChange?.(value);\n },\n [setUncontrolledValue, onValueChange],\n );\n\n const [open, setOpen, dropdownRef] = useOpen();\n const { listRef, onComboboxKeyDown, onOptionKeyDown } = useKeyboardShortcut(\n open,\n setOpen,\n setValue,\n );\n\n const buttonRef = useRef<HTMLButtonElement | null>(null);\n useEffect(() => {\n // Focus the button when the value is set\n // Test if the value is undefined to avoid focusing on the first render\n if (value !== undefined) buttonRef.current?.focus();\n }, [value]);\n\n const hasPlaceholder = text === placeholder;\n const buttonClasses = classNames({\n [styles.placeholder]: hasPlaceholder,\n });\n const borderClasses = classNames(styles.border, {\n [styles.open]: open,\n });\n const contentClasses = classNames(styles.content, {\n [styles.open]: open,\n });\n\n /**\n * Ids for accessibility.\n */\n const labelId = useId();\n const contentId = useId();\n\n return (\n <div\n ref={dropdownRef}\n className={classNames(className, styles.container)}\n aria-invalid={Boolean(error)}\n >\n <label id={labelId}>{label}</label>\n <button\n className={buttonClasses}\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n aria-labelledby={labelId}\n aria-controls={contentId}\n aria-expanded={open}\n ref={(element) => {\n // Private ref to focus the button\n buttonRef.current = element;\n // Handle forwarded ref\n if (typeof ref === \"function\") {\n ref(element);\n } else if (ref) {\n ref.current = element;\n }\n }}\n onClick={() => setOpen((_open) => !_open)}\n onKeyDown={onComboboxKeyDown}\n {...props}\n >\n {text}\n <ChevronDown width=\"24\" height=\"24\" />\n </button>\n <div className={borderClasses} />\n <div className={contentClasses}>\n <ul\n ref={listRef}\n id={contentId}\n role=\"listbox\"\n className={styles.content}\n >\n {values.map(([v, text]) => (\n <DropdownItem\n key={v}\n isDisplayed={open}\n isSelected={value === v}\n onClick={() => {\n setOpen(false);\n setValue(v);\n }}\n onKeyDown={(e) => onOptionKeyDown(e, v)}\n >\n {text}\n </DropdownItem>\n ))}\n </ul>\n </div>\n {!error && helpLabel && (\n <span className={styles.help}>{helpLabel}</span>\n )}\n {error && (\n <span className={styles.error}>\n <Error width=\"20\" height=\"20\" />\n {error}\n </span>\n )}\n </div>\n );\n },\n);\n\ntype DropdownItemProps = HTMLProps<HTMLLIElement> & {\n /**\n * Whether the dropdown item is selected.\n */\n isSelected: boolean;\n /**\n * Whether the dropdown item is displayed.\n */\n isDisplayed: boolean;\n /**\n * The text to display in the dropdown item.\n */\n children: string;\n};\n\n/**\n * A dropdown item component.\n */\nconst DropdownItem = memo(function DropdownItem({\n children,\n isSelected,\n isDisplayed,\n ...props\n}: DropdownItemProps) {\n const ref = useRef<HTMLLIElement>(null);\n\n // Focus the item if the dropdown is open and the item is already selected\n useEffect(() => {\n if (isSelected && isDisplayed) {\n ref.current?.focus();\n }\n }, [isSelected, isDisplayed]);\n\n return (\n <li\n tabIndex={0}\n role=\"option\"\n ref={ref}\n aria-selected={isSelected}\n {...props}\n >\n {children} {isSelected && <Check width=\"20\" height=\"20\" />}\n </li>\n );\n});\n\n/**\n * A hook to manage the open state of the dropdown.\n */\nfunction useOpen(): [\n boolean,\n Dispatch<SetStateAction<boolean>>,\n RefObject<HTMLDivElement | null>,\n] {\n const [open, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement | null>(null);\n\n // If the user clicks outside the dropdown, close it\n useEffect(() => {\n const closeIfOutside = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n\n document.addEventListener(\"click\", closeIfOutside);\n return () => document.removeEventListener(\"click\", closeIfOutside);\n }, [setOpen]);\n\n return [open, setOpen, ref];\n}\n\n/**\n * A hook to manage the keyboard shortcuts of the dropdown.\n * @param open - the dropdown open state.\n * @param setOpen - the dropdown open state setter.\n * @param setValue - set the selected value and text\n */\nfunction useKeyboardShortcut(\n open: boolean,\n setOpen: Dispatch<SetStateAction<boolean>>,\n setValue: (value: string) => void,\n) {\n const listRef = useRef<HTMLUListElement>(null);\n const onComboboxKeyDown = useCallback(\n ({ key }: KeyboardEvent) => {\n switch (key) {\n // Enter and Space already managed because it's a button\n case \"Escape\":\n setOpen(false);\n break;\n case \"ArrowDown\":\n setOpen(true);\n // If open, focus the first element\n if (open) {\n (listRef.current?.firstElementChild as HTMLElement)?.focus();\n }\n break;\n case \"ArrowUp\":\n setOpen(true);\n break;\n case \"Home\": {\n setOpen(true);\n // Wait for the dropdown to be opened\n Promise.resolve().then(() => {\n (listRef.current?.firstElementChild as HTMLElement)?.focus();\n });\n break;\n }\n case \"End\": {\n setOpen(true);\n // Wait for the dropdown to be opened\n Promise.resolve().then(() => {\n (listRef.current?.lastElementChild as HTMLElement)?.focus();\n });\n break;\n }\n }\n },\n [listRef, open, setOpen],\n );\n\n const onOptionKeyDown = useCallback(\n (evt: KeyboardEvent, value: string) => {\n const { key, altKey } = evt;\n evt.stopPropagation();\n evt.preventDefault();\n\n switch (key) {\n case \"Enter\":\n case \" \": {\n setValue(value);\n setOpen(false);\n break;\n }\n case \"Tab\":\n case \"Escape\":\n setOpen(false);\n break;\n case \"ArrowDown\": {\n const currentFocus = document.activeElement;\n if (listRef.current?.contains(currentFocus) && currentFocus) {\n (currentFocus.nextElementSibling as HTMLElement)?.focus();\n }\n break;\n }\n case \"ArrowUp\": {\n if (altKey) {\n setValue(value);\n setOpen(false);\n } else {\n const currentFocus = document.activeElement;\n if (listRef.current?.contains(currentFocus) && currentFocus) {\n (currentFocus.previousElementSibling as HTMLElement)?.focus();\n }\n }\n break;\n }\n case \"Home\": {\n (listRef.current?.firstElementChild as HTMLElement)?.focus();\n break;\n }\n case \"End\": {\n (listRef.current?.lastElementChild as HTMLElement)?.focus();\n break;\n }\n }\n },\n [listRef, setValue, setOpen],\n );\n\n return { listRef, onComboboxKeyDown, onOptionKeyDown };\n}\n"],"names":["Dropdown","value","text","Error","DropdownItem","Check"],"mappings":"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"}
|
|
@@ -16,6 +16,7 @@ const ContextMenuItemWrapper = ({
|
|
|
16
16
|
}) => /* @__PURE__ */ jsxRuntime.jsx(reactContextMenu.ContextMenuItem, { onSelect: onSelect ?? void 0, asChild: true, children });
|
|
17
17
|
const ContextMenu = ({
|
|
18
18
|
title,
|
|
19
|
+
showTitle = true,
|
|
19
20
|
onOpenChange: onOpenChangeProp,
|
|
20
21
|
trigger: triggerProp,
|
|
21
22
|
hasAccessibleAlternative,
|
|
@@ -56,7 +57,7 @@ const ContextMenu = ({
|
|
|
56
57
|
] }) })
|
|
57
58
|
] }) : /* @__PURE__ */ jsxRuntime.jsxs(reactContextMenu.Root, { onOpenChange, children: [
|
|
58
59
|
trigger,
|
|
59
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactContextMenu.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(reactContextMenu.Content, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(FloatingMenu.FloatingMenu, { title, children }) }) })
|
|
60
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactContextMenu.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(reactContextMenu.Content, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(FloatingMenu.FloatingMenu, { showTitle, title, children }) }) })
|
|
60
61
|
] });
|
|
61
62
|
};
|
|
62
63
|
exports.ContextMenu = ContextMenu;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContextMenu.cjs","sources":["../../../src/components/Menu/ContextMenu.tsx"],"sourcesContent":["/*\nCopyright 2023 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 React, { FC, ReactNode, useCallback, useMemo, useState } from \"react\";\nimport {\n Root,\n Trigger,\n Portal,\n Content,\n ContextMenuItem,\n} from \"@radix-ui/react-context-menu\";\nimport { FloatingMenu } from \"./FloatingMenu\";\nimport { Drawer } from \"vaul\";\nimport classnames from \"classnames\";\nimport drawerStyles from \"./DrawerMenu.module.css\";\nimport { MenuContext, MenuData, MenuItemWrapperProps } from \"./MenuContext\";\nimport { DrawerMenu } from \"./DrawerMenu\";\nimport { getPlatform } from \"../../utils/platform\";\n\ninterface Props {\n /**\n * The menu title.\n */\n title: string;\n /**\n * Event handler called when the open state of the menu changes.\n */\n onOpenChange?: (open: boolean) => void;\n /**\n * The trigger that can be right-clicked or long-pressed to open the menu.\n * This must be a component that accepts a ref and spreads props.\n * https://www.radix-ui.com/primitives/docs/guides/composition\n */\n trigger: ReactNode;\n /**\n * Whether the functionality of this menu is available through some other\n * keyboard-accessible means. Preferably this should be true, because context\n * menus are potentially difficult to discover, but if false the trigger will\n * become focusable so that it can be opened via keyboard navigation.\n */\n hasAccessibleAlternative: boolean;\n /**\n * The menu contents.\n */\n children: ReactNode;\n}\n\nconst ContextMenuItemWrapper: FC<MenuItemWrapperProps> = ({\n onSelect,\n children,\n}) => (\n <ContextMenuItem onSelect={onSelect ?? undefined} asChild>\n {children}\n </ContextMenuItem>\n);\n\n/**\n * A menu opened by right-clicking or long-pressing another UI element.\n */\nexport const ContextMenu: FC<Props> = ({\n title,\n onOpenChange: onOpenChangeProp,\n trigger: triggerProp,\n hasAccessibleAlternative,\n children: childrenProp,\n}) => {\n const [open, setOpen] = useState(false);\n const onOpenChange = useCallback(\n (value: boolean) => {\n setOpen(value);\n onOpenChangeProp?.(value);\n },\n [setOpen, onOpenChangeProp],\n );\n\n // Normally, the menu takes the form of a floating box. But on Android and\n // iOS, the menu should morph into a drawer\n const platform = getPlatform();\n const drawer = platform === \"android\" || platform === \"ios\";\n const context: MenuData = useMemo(\n () => ({\n MenuItemWrapper: drawer ? null : ContextMenuItemWrapper,\n onOpenChange,\n }),\n [onOpenChange],\n );\n const children = (\n <MenuContext.Provider value={context}>{childrenProp}</MenuContext.Provider>\n );\n\n const trigger = (\n <Trigger\n aria-haspopup=\"menu\"\n tabIndex={hasAccessibleAlternative ? undefined : 0}\n asChild\n >\n {triggerProp}\n </Trigger>\n );\n\n // This is a small hack: Vaul drawers only support buttons as triggers, so\n // we end up mounting an empty Radix context menu tree alongside the\n // drawer tree, purely so we can use its Trigger component (which supports\n // touch for free). The resulting behavior and DOM tree looks exactly the\n // same as if Vaul provided a long-press trigger of its own, so I think\n // this is fine.\n return drawer ? (\n <>\n <Root onOpenChange={onOpenChange}>{trigger}</Root>\n <Drawer.Root open={open} onOpenChange={onOpenChange}>\n <Drawer.Portal>\n <Drawer.Overlay className={classnames(drawerStyles.bg)} />\n <Drawer.Content asChild>\n <DrawerMenu title={title}>{children}</DrawerMenu>\n </Drawer.Content>\n </Drawer.Portal>\n </Drawer.Root>\n </>\n ) : (\n <Root onOpenChange={onOpenChange}>\n {trigger}\n <Portal>\n <Content asChild>\n <FloatingMenu title={title}
|
|
1
|
+
{"version":3,"file":"ContextMenu.cjs","sources":["../../../src/components/Menu/ContextMenu.tsx"],"sourcesContent":["/*\nCopyright 2023 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 React, { FC, ReactNode, useCallback, useMemo, useState } from \"react\";\nimport {\n Root,\n Trigger,\n Portal,\n Content,\n ContextMenuItem,\n} from \"@radix-ui/react-context-menu\";\nimport { FloatingMenu } from \"./FloatingMenu\";\nimport { Drawer } from \"vaul\";\nimport classnames from \"classnames\";\nimport drawerStyles from \"./DrawerMenu.module.css\";\nimport { MenuContext, MenuData, MenuItemWrapperProps } from \"./MenuContext\";\nimport { DrawerMenu } from \"./DrawerMenu\";\nimport { getPlatform } from \"../../utils/platform\";\n\ninterface Props {\n /**\n * The menu title.\n */\n title: string;\n /**\n * Wether the title is displayed.\n * @default true\n */\n showTitle?: boolean;\n /**\n * Event handler called when the open state of the menu changes.\n */\n onOpenChange?: (open: boolean) => void;\n /**\n * The trigger that can be right-clicked or long-pressed to open the menu.\n * This must be a component that accepts a ref and spreads props.\n * https://www.radix-ui.com/primitives/docs/guides/composition\n */\n trigger: ReactNode;\n /**\n * Whether the functionality of this menu is available through some other\n * keyboard-accessible means. Preferably this should be true, because context\n * menus are potentially difficult to discover, but if false the trigger will\n * become focusable so that it can be opened via keyboard navigation.\n */\n hasAccessibleAlternative: boolean;\n /**\n * The menu contents.\n */\n children: ReactNode;\n}\n\nconst ContextMenuItemWrapper: FC<MenuItemWrapperProps> = ({\n onSelect,\n children,\n}) => (\n <ContextMenuItem onSelect={onSelect ?? undefined} asChild>\n {children}\n </ContextMenuItem>\n);\n\n/**\n * A menu opened by right-clicking or long-pressing another UI element.\n */\nexport const ContextMenu: FC<Props> = ({\n title,\n showTitle = true,\n onOpenChange: onOpenChangeProp,\n trigger: triggerProp,\n hasAccessibleAlternative,\n children: childrenProp,\n}) => {\n const [open, setOpen] = useState(false);\n const onOpenChange = useCallback(\n (value: boolean) => {\n setOpen(value);\n onOpenChangeProp?.(value);\n },\n [setOpen, onOpenChangeProp],\n );\n\n // Normally, the menu takes the form of a floating box. But on Android and\n // iOS, the menu should morph into a drawer\n const platform = getPlatform();\n const drawer = platform === \"android\" || platform === \"ios\";\n const context: MenuData = useMemo(\n () => ({\n MenuItemWrapper: drawer ? null : ContextMenuItemWrapper,\n onOpenChange,\n }),\n [onOpenChange],\n );\n const children = (\n <MenuContext.Provider value={context}>{childrenProp}</MenuContext.Provider>\n );\n\n const trigger = (\n <Trigger\n aria-haspopup=\"menu\"\n tabIndex={hasAccessibleAlternative ? undefined : 0}\n asChild\n >\n {triggerProp}\n </Trigger>\n );\n\n // This is a small hack: Vaul drawers only support buttons as triggers, so\n // we end up mounting an empty Radix context menu tree alongside the\n // drawer tree, purely so we can use its Trigger component (which supports\n // touch for free). The resulting behavior and DOM tree looks exactly the\n // same as if Vaul provided a long-press trigger of its own, so I think\n // this is fine.\n return drawer ? (\n <>\n <Root onOpenChange={onOpenChange}>{trigger}</Root>\n <Drawer.Root open={open} onOpenChange={onOpenChange}>\n <Drawer.Portal>\n <Drawer.Overlay className={classnames(drawerStyles.bg)} />\n <Drawer.Content asChild>\n <DrawerMenu title={title}>{children}</DrawerMenu>\n </Drawer.Content>\n </Drawer.Portal>\n </Drawer.Root>\n </>\n ) : (\n <Root onOpenChange={onOpenChange}>\n {trigger}\n <Portal>\n <Content asChild>\n <FloatingMenu showTitle={showTitle} title={title}>\n {children}\n </FloatingMenu>\n </Content>\n </Portal>\n </Root>\n );\n};\n"],"names":["ContextMenuItem","useState","useCallback","platform","getPlatform","useMemo","jsx","MenuContext","Trigger","jsxs","Fragment","Root","Drawer","classnames","drawerStyles","DrawerMenu","Portal","Content","FloatingMenu"],"mappings":";;;;;;;;;;;;AAwDA,MAAM,yBAAmD,CAAC;AAAA,EACxD;AAAA,EACA;AACF,qCACGA,kCAAgB,EAAA,UAAU,YAAY,QAAW,SAAO,MACtD,UACH;AAMK,MAAM,cAAyB,CAAC;AAAA,EACrC;AAAA,EACA,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,SAAS;AAAA,EACT;AAAA,EACA,UAAU;AACZ,MAAM;AACJ,QAAM,CAAC,MAAM,OAAO,IAAIC,MAAAA,SAAS,KAAK;AACtC,QAAM,eAAeC,MAAA;AAAA,IACnB,CAAC,UAAmB;AAClB,cAAQ,KAAK;AACb,yBAAmB,KAAK;AAAA,IAC1B;AAAA,IACA,CAAC,SAAS,gBAAgB;AAAA,EAC5B;AAIA,QAAMC,aAAWC,SAAAA,YAAY;AACvB,QAAA,SAASD,eAAa,aAAaA,eAAa;AACtD,QAAM,UAAoBE,MAAA;AAAA,IACxB,OAAO;AAAA,MACL,iBAAiB,SAAS,OAAO;AAAA,MACjC;AAAA,IAAA;AAAA,IAEF,CAAC,YAAY;AAAA,EACf;AACA,QAAM,WACHC,+BAAAC,YAAAA,YAAY,UAAZ,EAAqB,OAAO,SAAU,UAAa,cAAA;AAGtD,QAAM,UACJD,2BAAA;AAAA,IAACE,iBAAA;AAAA,IAAA;AAAA,MACC,iBAAc;AAAA,MACd,UAAU,2BAA2B,SAAY;AAAA,MACjD,SAAO;AAAA,MAEN,UAAA;AAAA,IAAA;AAAA,EACH;AASF,SAAO,SAEHC,2BAAAA,KAAAC,WAAA,UAAA,EAAA,UAAA;AAAA,IAACJ,2BAAAA,IAAAK,iBAAAA,MAAA,EAAK,cAA6B,UAAQ,QAAA,CAAA;AAAA,IAC3CL,2BAAAA,IAACM,YAAO,MAAP,EAAY,MAAY,cACvB,UAAAH,gCAACG,KAAAA,OAAO,QAAP,EACC,UAAA;AAAA,MAAAN,+BAACM,KAAAA,OAAO,SAAP,EAAe,WAAWC,WAAWC,kBAAA,QAAa,EAAE,GAAG;AAAA,MACxDR,2BAAAA,IAACM,KAAO,OAAA,SAAP,EAAe,SAAO,MACrB,UAACN,2BAAAA,IAAAS,WAAAA,YAAA,EAAW,OAAe,SAAS,CAAA,EACtC,CAAA;AAAA,IAAA,EAAA,CACF,EACF,CAAA;AAAA,EACF,EAAA,CAAA,IAECN,2BAAA,KAAAE,iBAAA,MAAA,EAAK,cACH,UAAA;AAAA,IAAA;AAAA,IACAL,2BAAA,IAAAU,iBAAA,QAAA,EACC,UAACV,2BAAA,IAAAW,iBAAA,SAAA,EAAQ,SAAO,MACd,UAACX,2BAAA,IAAAY,aAAA,cAAA,EAAa,WAAsB,OACjC,SACH,CAAA,EAAA,CACF,EACF,CAAA;AAAA,EAAA,GACF;AAEJ;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContextMenu.d.ts","sourceRoot":"","sources":["../../../src/components/Menu/ContextMenu.tsx"],"names":[],"mappings":"AAOA,OAAc,EAAE,EAAE,EAAE,SAAS,EAAkC,MAAM,OAAO,CAAC;AAgB7E,UAAU,KAAK;IACb;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC;;;;OAIG;IACH,OAAO,EAAE,SAAS,CAAC;IACnB;;;;;OAKG;IACH,wBAAwB,EAAE,OAAO,CAAC;IAClC;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;CACrB;AAWD;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,EAAE,CAAC,KAAK,
|
|
1
|
+
{"version":3,"file":"ContextMenu.d.ts","sourceRoot":"","sources":["../../../src/components/Menu/ContextMenu.tsx"],"names":[],"mappings":"AAOA,OAAc,EAAE,EAAE,EAAE,SAAS,EAAkC,MAAM,OAAO,CAAC;AAgB7E,UAAU,KAAK;IACb;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;OAEG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC;;;;OAIG;IACH,OAAO,EAAE,SAAS,CAAC;IACnB;;;;;OAKG;IACH,wBAAwB,EAAE,OAAO,CAAC;IAClC;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;CACrB;AAWD;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,EAAE,CAAC,KAAK,CAwEjC,CAAC"}
|
|
@@ -14,6 +14,7 @@ const ContextMenuItemWrapper = ({
|
|
|
14
14
|
}) => /* @__PURE__ */ jsx(ContextMenuItem, { onSelect: onSelect ?? void 0, asChild: true, children });
|
|
15
15
|
const ContextMenu = ({
|
|
16
16
|
title,
|
|
17
|
+
showTitle = true,
|
|
17
18
|
onOpenChange: onOpenChangeProp,
|
|
18
19
|
trigger: triggerProp,
|
|
19
20
|
hasAccessibleAlternative,
|
|
@@ -54,7 +55,7 @@ const ContextMenu = ({
|
|
|
54
55
|
] }) })
|
|
55
56
|
] }) : /* @__PURE__ */ jsxs(Root, { onOpenChange, children: [
|
|
56
57
|
trigger,
|
|
57
|
-
/* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Content, { asChild: true, children: /* @__PURE__ */ jsx(FloatingMenu, { title, children }) }) })
|
|
58
|
+
/* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Content, { asChild: true, children: /* @__PURE__ */ jsx(FloatingMenu, { showTitle, title, children }) }) })
|
|
58
59
|
] });
|
|
59
60
|
};
|
|
60
61
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContextMenu.js","sources":["../../../src/components/Menu/ContextMenu.tsx"],"sourcesContent":["/*\nCopyright 2023 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 React, { FC, ReactNode, useCallback, useMemo, useState } from \"react\";\nimport {\n Root,\n Trigger,\n Portal,\n Content,\n ContextMenuItem,\n} from \"@radix-ui/react-context-menu\";\nimport { FloatingMenu } from \"./FloatingMenu\";\nimport { Drawer } from \"vaul\";\nimport classnames from \"classnames\";\nimport drawerStyles from \"./DrawerMenu.module.css\";\nimport { MenuContext, MenuData, MenuItemWrapperProps } from \"./MenuContext\";\nimport { DrawerMenu } from \"./DrawerMenu\";\nimport { getPlatform } from \"../../utils/platform\";\n\ninterface Props {\n /**\n * The menu title.\n */\n title: string;\n /**\n * Event handler called when the open state of the menu changes.\n */\n onOpenChange?: (open: boolean) => void;\n /**\n * The trigger that can be right-clicked or long-pressed to open the menu.\n * This must be a component that accepts a ref and spreads props.\n * https://www.radix-ui.com/primitives/docs/guides/composition\n */\n trigger: ReactNode;\n /**\n * Whether the functionality of this menu is available through some other\n * keyboard-accessible means. Preferably this should be true, because context\n * menus are potentially difficult to discover, but if false the trigger will\n * become focusable so that it can be opened via keyboard navigation.\n */\n hasAccessibleAlternative: boolean;\n /**\n * The menu contents.\n */\n children: ReactNode;\n}\n\nconst ContextMenuItemWrapper: FC<MenuItemWrapperProps> = ({\n onSelect,\n children,\n}) => (\n <ContextMenuItem onSelect={onSelect ?? undefined} asChild>\n {children}\n </ContextMenuItem>\n);\n\n/**\n * A menu opened by right-clicking or long-pressing another UI element.\n */\nexport const ContextMenu: FC<Props> = ({\n title,\n onOpenChange: onOpenChangeProp,\n trigger: triggerProp,\n hasAccessibleAlternative,\n children: childrenProp,\n}) => {\n const [open, setOpen] = useState(false);\n const onOpenChange = useCallback(\n (value: boolean) => {\n setOpen(value);\n onOpenChangeProp?.(value);\n },\n [setOpen, onOpenChangeProp],\n );\n\n // Normally, the menu takes the form of a floating box. But on Android and\n // iOS, the menu should morph into a drawer\n const platform = getPlatform();\n const drawer = platform === \"android\" || platform === \"ios\";\n const context: MenuData = useMemo(\n () => ({\n MenuItemWrapper: drawer ? null : ContextMenuItemWrapper,\n onOpenChange,\n }),\n [onOpenChange],\n );\n const children = (\n <MenuContext.Provider value={context}>{childrenProp}</MenuContext.Provider>\n );\n\n const trigger = (\n <Trigger\n aria-haspopup=\"menu\"\n tabIndex={hasAccessibleAlternative ? undefined : 0}\n asChild\n >\n {triggerProp}\n </Trigger>\n );\n\n // This is a small hack: Vaul drawers only support buttons as triggers, so\n // we end up mounting an empty Radix context menu tree alongside the\n // drawer tree, purely so we can use its Trigger component (which supports\n // touch for free). The resulting behavior and DOM tree looks exactly the\n // same as if Vaul provided a long-press trigger of its own, so I think\n // this is fine.\n return drawer ? (\n <>\n <Root onOpenChange={onOpenChange}>{trigger}</Root>\n <Drawer.Root open={open} onOpenChange={onOpenChange}>\n <Drawer.Portal>\n <Drawer.Overlay className={classnames(drawerStyles.bg)} />\n <Drawer.Content asChild>\n <DrawerMenu title={title}>{children}</DrawerMenu>\n </Drawer.Content>\n </Drawer.Portal>\n </Drawer.Root>\n </>\n ) : (\n <Root onOpenChange={onOpenChange}>\n {trigger}\n <Portal>\n <Content asChild>\n <FloatingMenu title={title}
|
|
1
|
+
{"version":3,"file":"ContextMenu.js","sources":["../../../src/components/Menu/ContextMenu.tsx"],"sourcesContent":["/*\nCopyright 2023 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 React, { FC, ReactNode, useCallback, useMemo, useState } from \"react\";\nimport {\n Root,\n Trigger,\n Portal,\n Content,\n ContextMenuItem,\n} from \"@radix-ui/react-context-menu\";\nimport { FloatingMenu } from \"./FloatingMenu\";\nimport { Drawer } from \"vaul\";\nimport classnames from \"classnames\";\nimport drawerStyles from \"./DrawerMenu.module.css\";\nimport { MenuContext, MenuData, MenuItemWrapperProps } from \"./MenuContext\";\nimport { DrawerMenu } from \"./DrawerMenu\";\nimport { getPlatform } from \"../../utils/platform\";\n\ninterface Props {\n /**\n * The menu title.\n */\n title: string;\n /**\n * Wether the title is displayed.\n * @default true\n */\n showTitle?: boolean;\n /**\n * Event handler called when the open state of the menu changes.\n */\n onOpenChange?: (open: boolean) => void;\n /**\n * The trigger that can be right-clicked or long-pressed to open the menu.\n * This must be a component that accepts a ref and spreads props.\n * https://www.radix-ui.com/primitives/docs/guides/composition\n */\n trigger: ReactNode;\n /**\n * Whether the functionality of this menu is available through some other\n * keyboard-accessible means. Preferably this should be true, because context\n * menus are potentially difficult to discover, but if false the trigger will\n * become focusable so that it can be opened via keyboard navigation.\n */\n hasAccessibleAlternative: boolean;\n /**\n * The menu contents.\n */\n children: ReactNode;\n}\n\nconst ContextMenuItemWrapper: FC<MenuItemWrapperProps> = ({\n onSelect,\n children,\n}) => (\n <ContextMenuItem onSelect={onSelect ?? undefined} asChild>\n {children}\n </ContextMenuItem>\n);\n\n/**\n * A menu opened by right-clicking or long-pressing another UI element.\n */\nexport const ContextMenu: FC<Props> = ({\n title,\n showTitle = true,\n onOpenChange: onOpenChangeProp,\n trigger: triggerProp,\n hasAccessibleAlternative,\n children: childrenProp,\n}) => {\n const [open, setOpen] = useState(false);\n const onOpenChange = useCallback(\n (value: boolean) => {\n setOpen(value);\n onOpenChangeProp?.(value);\n },\n [setOpen, onOpenChangeProp],\n );\n\n // Normally, the menu takes the form of a floating box. But on Android and\n // iOS, the menu should morph into a drawer\n const platform = getPlatform();\n const drawer = platform === \"android\" || platform === \"ios\";\n const context: MenuData = useMemo(\n () => ({\n MenuItemWrapper: drawer ? null : ContextMenuItemWrapper,\n onOpenChange,\n }),\n [onOpenChange],\n );\n const children = (\n <MenuContext.Provider value={context}>{childrenProp}</MenuContext.Provider>\n );\n\n const trigger = (\n <Trigger\n aria-haspopup=\"menu\"\n tabIndex={hasAccessibleAlternative ? undefined : 0}\n asChild\n >\n {triggerProp}\n </Trigger>\n );\n\n // This is a small hack: Vaul drawers only support buttons as triggers, so\n // we end up mounting an empty Radix context menu tree alongside the\n // drawer tree, purely so we can use its Trigger component (which supports\n // touch for free). The resulting behavior and DOM tree looks exactly the\n // same as if Vaul provided a long-press trigger of its own, so I think\n // this is fine.\n return drawer ? (\n <>\n <Root onOpenChange={onOpenChange}>{trigger}</Root>\n <Drawer.Root open={open} onOpenChange={onOpenChange}>\n <Drawer.Portal>\n <Drawer.Overlay className={classnames(drawerStyles.bg)} />\n <Drawer.Content asChild>\n <DrawerMenu title={title}>{children}</DrawerMenu>\n </Drawer.Content>\n </Drawer.Portal>\n </Drawer.Root>\n </>\n ) : (\n <Root onOpenChange={onOpenChange}>\n {trigger}\n <Portal>\n <Content asChild>\n <FloatingMenu showTitle={showTitle} title={title}>\n {children}\n </FloatingMenu>\n </Content>\n </Portal>\n </Root>\n );\n};\n"],"names":["classnames","drawerStyles"],"mappings":";;;;;;;;;;AAwDA,MAAM,yBAAmD,CAAC;AAAA,EACxD;AAAA,EACA;AACF,0BACG,iBAAgB,EAAA,UAAU,YAAY,QAAW,SAAO,MACtD,UACH;AAMK,MAAM,cAAyB,CAAC;AAAA,EACrC;AAAA,EACA,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,SAAS;AAAA,EACT;AAAA,EACA,UAAU;AACZ,MAAM;AACJ,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AACtC,QAAM,eAAe;AAAA,IACnB,CAAC,UAAmB;AAClB,cAAQ,KAAK;AACb,yBAAmB,KAAK;AAAA,IAC1B;AAAA,IACA,CAAC,SAAS,gBAAgB;AAAA,EAC5B;AAIA,QAAM,WAAW,YAAY;AACvB,QAAA,SAAS,aAAa,aAAa,aAAa;AACtD,QAAM,UAAoB;AAAA,IACxB,OAAO;AAAA,MACL,iBAAiB,SAAS,OAAO;AAAA,MACjC;AAAA,IAAA;AAAA,IAEF,CAAC,YAAY;AAAA,EACf;AACA,QAAM,WACH,oBAAA,YAAY,UAAZ,EAAqB,OAAO,SAAU,UAAa,cAAA;AAGtD,QAAM,UACJ;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,iBAAc;AAAA,MACd,UAAU,2BAA2B,SAAY;AAAA,MACjD,SAAO;AAAA,MAEN,UAAA;AAAA,IAAA;AAAA,EACH;AASF,SAAO,SAEH,qBAAA,UAAA,EAAA,UAAA;AAAA,IAAC,oBAAA,MAAA,EAAK,cAA6B,UAAQ,QAAA,CAAA;AAAA,IAC3C,oBAAC,OAAO,MAAP,EAAY,MAAY,cACvB,UAAA,qBAAC,OAAO,QAAP,EACC,UAAA;AAAA,MAAA,oBAAC,OAAO,SAAP,EAAe,WAAWA,WAAWC,WAAa,EAAE,GAAG;AAAA,MACxD,oBAAC,OAAO,SAAP,EAAe,SAAO,MACrB,UAAC,oBAAA,YAAA,EAAW,OAAe,SAAS,CAAA,EACtC,CAAA;AAAA,IAAA,EAAA,CACF,EACF,CAAA;AAAA,EACF,EAAA,CAAA,IAEC,qBAAA,MAAA,EAAK,cACH,UAAA;AAAA,IAAA;AAAA,IACA,oBAAA,QAAA,EACC,UAAC,oBAAA,SAAA,EAAQ,SAAO,MACd,UAAC,oBAAA,cAAA,EAAa,WAAsB,OACjC,SACH,CAAA,EAAA,CACF,EACF,CAAA;AAAA,EAAA,GACF;AAEJ;"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const React = require("react");
|
|
5
|
+
const MenuItem = require("./MenuItem.cjs");
|
|
6
|
+
const Radio = require("../Form/Controls/Radio/Radio.cjs");
|
|
7
|
+
const RadioMenuItem = React.forwardRef(
|
|
8
|
+
function RadioMenuItem2({ className, label, onSelect, checked, disabled }, ref) {
|
|
9
|
+
const toggleId = React.useId();
|
|
10
|
+
const onChange = React.useCallback(() => {
|
|
11
|
+
}, []);
|
|
12
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
13
|
+
MenuItem.MenuItem,
|
|
14
|
+
{
|
|
15
|
+
as: "div",
|
|
16
|
+
role: "menuitemradio",
|
|
17
|
+
"aria-checked": checked,
|
|
18
|
+
className,
|
|
19
|
+
label,
|
|
20
|
+
onSelect,
|
|
21
|
+
disabled,
|
|
22
|
+
Icon: /* @__PURE__ */ jsxRuntime.jsx(
|
|
23
|
+
Radio.RadioInput,
|
|
24
|
+
{
|
|
25
|
+
id: toggleId,
|
|
26
|
+
ref,
|
|
27
|
+
"aria-hidden": true,
|
|
28
|
+
checked,
|
|
29
|
+
disabled,
|
|
30
|
+
onChange
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
exports.RadioMenuItem = RadioMenuItem;
|
|
38
|
+
//# sourceMappingURL=RadioMenuItem.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RadioMenuItem.cjs","sources":["../../../src/components/Menu/RadioMenuItem.tsx"],"sourcesContent":["/*\n * Copyright 2025 New Vector Ltd\n *\n * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n * Please see LICENSE files in the repository root for full details.\n */\n\nimport React, { ComponentProps, forwardRef, useCallback, useId } from \"react\";\nimport { MenuItem } from \"./MenuItem\";\nimport { RadioInput } from \"../Form\";\n\ntype Props = Pick<\n ComponentProps<typeof MenuItem>,\n \"className\" | \"label\" | \"onSelect\" | \"disabled\"\n> & {\n /**\n * Whether the radio is checked.\n */\n checked: boolean;\n};\n\n/**\n * A menu item with a radio control.\n * Must be used within a compound Menu or other `menu` or `menubar` aria role subtree.\n */\nexport const RadioMenuItem = forwardRef<HTMLInputElement, Props>(\n function RadioMenuItem(\n { className, label, onSelect, checked, disabled },\n ref,\n ) {\n const toggleId = useId();\n // The radio is controlled and we intend to ignore its events. We do need\n // to at least set onChange though to make React happy.\n const onChange = useCallback(() => {}, []);\n\n // <label> elements are not allowed to have a role like menuitemradio, so\n // we must instead use a plain <div> for the menu item and use aria-checked\n // etc. to communicate its state.\n return (\n <MenuItem\n as=\"div\"\n role=\"menuitemradio\"\n aria-checked={checked}\n className={className}\n label={label}\n onSelect={onSelect}\n disabled={disabled}\n Icon={\n <RadioInput\n id={toggleId}\n ref={ref}\n // This is purely cosmetic; really the whole MenuItem is the toggle.\n aria-hidden\n checked={checked}\n disabled={disabled}\n onChange={onChange}\n />\n }\n ></MenuItem>\n );\n },\n);\n"],"names":["forwardRef","RadioMenuItem","useId","useCallback","jsx","MenuItem","RadioInput"],"mappings":";;;;;;AAyBO,MAAM,gBAAgBA,MAAA;AAAA,EAC3B,SAASC,eACP,EAAE,WAAW,OAAO,UAAU,SAAS,SAAS,GAChD,KACA;AACA,UAAM,WAAWC,MAAAA,MAAM;AAGjB,UAAA,WAAWC,MAAAA,YAAY,MAAM;AAAA,IAAC,GAAG,EAAE;AAMvC,WAAAC,2BAAA;AAAA,MAACC,SAAA;AAAA,MAAA;AAAA,QACC,IAAG;AAAA,QACH,MAAK;AAAA,QACL,gBAAc;AAAA,QACd;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,MACED,2BAAA;AAAA,UAACE,MAAA;AAAA,UAAA;AAAA,YACC,IAAI;AAAA,YACJ;AAAA,YAEA,eAAW;AAAA,YACX;AAAA,YACA;AAAA,YACA;AAAA,UAAA;AAAA,QAAA;AAAA,MACF;AAAA,IAEH;AAAA,EAAA;AAGP;;"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* A menu item with a radio control.
|
|
4
|
+
* Must be used within a compound Menu or other `menu` or `menubar` aria role subtree.
|
|
5
|
+
*/
|
|
6
|
+
export declare const RadioMenuItem: React.ForwardRefExoticComponent<Pick<{
|
|
7
|
+
as?: ("a" | "button" | "div") | undefined;
|
|
8
|
+
className?: string;
|
|
9
|
+
Icon?: React.ComponentType<React.SVGAttributes<SVGElement>> | React.ReactElement;
|
|
10
|
+
label: string | null;
|
|
11
|
+
labelProps?: React.ComponentPropsWithoutRef<typeof import('../..').Text>;
|
|
12
|
+
onSelect: ((e: Event) => void) | null;
|
|
13
|
+
onClick?: React.MouseEventHandler<HTMLAnchorElement | HTMLButtonElement | HTMLDivElement> | undefined;
|
|
14
|
+
kind?: "primary" | "critical";
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
hideChevron?: boolean;
|
|
17
|
+
} & Omit<Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> | Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> | Omit<React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>, "ref">, "onClick" | "onSelect">, "label" | "className" | "disabled" | "onSelect"> & {
|
|
18
|
+
/**
|
|
19
|
+
* Whether the radio is checked.
|
|
20
|
+
*/
|
|
21
|
+
checked: boolean;
|
|
22
|
+
} & React.RefAttributes<HTMLInputElement>>;
|
|
23
|
+
//# sourceMappingURL=RadioMenuItem.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RadioMenuItem.d.ts","sourceRoot":"","sources":["../../../src/components/Menu/RadioMenuItem.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAyD,MAAM,OAAO,CAAC;AAc9E;;;GAGG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;IAVxB;;OAEG;aACM,OAAO;0CA2CjB,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useId, useCallback } from "react";
|
|
3
|
+
import { MenuItem } from "./MenuItem.js";
|
|
4
|
+
import { RadioInput } from "../Form/Controls/Radio/Radio.js";
|
|
5
|
+
const RadioMenuItem = forwardRef(
|
|
6
|
+
function RadioMenuItem2({ className, label, onSelect, checked, disabled }, ref) {
|
|
7
|
+
const toggleId = useId();
|
|
8
|
+
const onChange = useCallback(() => {
|
|
9
|
+
}, []);
|
|
10
|
+
return /* @__PURE__ */ jsx(
|
|
11
|
+
MenuItem,
|
|
12
|
+
{
|
|
13
|
+
as: "div",
|
|
14
|
+
role: "menuitemradio",
|
|
15
|
+
"aria-checked": checked,
|
|
16
|
+
className,
|
|
17
|
+
label,
|
|
18
|
+
onSelect,
|
|
19
|
+
disabled,
|
|
20
|
+
Icon: /* @__PURE__ */ jsx(
|
|
21
|
+
RadioInput,
|
|
22
|
+
{
|
|
23
|
+
id: toggleId,
|
|
24
|
+
ref,
|
|
25
|
+
"aria-hidden": true,
|
|
26
|
+
checked,
|
|
27
|
+
disabled,
|
|
28
|
+
onChange
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
export {
|
|
36
|
+
RadioMenuItem
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=RadioMenuItem.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RadioMenuItem.js","sources":["../../../src/components/Menu/RadioMenuItem.tsx"],"sourcesContent":["/*\n * Copyright 2025 New Vector Ltd\n *\n * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\n * Please see LICENSE files in the repository root for full details.\n */\n\nimport React, { ComponentProps, forwardRef, useCallback, useId } from \"react\";\nimport { MenuItem } from \"./MenuItem\";\nimport { RadioInput } from \"../Form\";\n\ntype Props = Pick<\n ComponentProps<typeof MenuItem>,\n \"className\" | \"label\" | \"onSelect\" | \"disabled\"\n> & {\n /**\n * Whether the radio is checked.\n */\n checked: boolean;\n};\n\n/**\n * A menu item with a radio control.\n * Must be used within a compound Menu or other `menu` or `menubar` aria role subtree.\n */\nexport const RadioMenuItem = forwardRef<HTMLInputElement, Props>(\n function RadioMenuItem(\n { className, label, onSelect, checked, disabled },\n ref,\n ) {\n const toggleId = useId();\n // The radio is controlled and we intend to ignore its events. We do need\n // to at least set onChange though to make React happy.\n const onChange = useCallback(() => {}, []);\n\n // <label> elements are not allowed to have a role like menuitemradio, so\n // we must instead use a plain <div> for the menu item and use aria-checked\n // etc. to communicate its state.\n return (\n <MenuItem\n as=\"div\"\n role=\"menuitemradio\"\n aria-checked={checked}\n className={className}\n label={label}\n onSelect={onSelect}\n disabled={disabled}\n Icon={\n <RadioInput\n id={toggleId}\n ref={ref}\n // This is purely cosmetic; really the whole MenuItem is the toggle.\n aria-hidden\n checked={checked}\n disabled={disabled}\n onChange={onChange}\n />\n }\n ></MenuItem>\n );\n },\n);\n"],"names":["RadioMenuItem"],"mappings":";;;;AAyBO,MAAM,gBAAgB;AAAA,EAC3B,SAASA,eACP,EAAE,WAAW,OAAO,UAAU,SAAS,SAAS,GAChD,KACA;AACA,UAAM,WAAW,MAAM;AAGjB,UAAA,WAAW,YAAY,MAAM;AAAA,IAAC,GAAG,EAAE;AAMvC,WAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,IAAG;AAAA,QACH,MAAK;AAAA,QACL,gBAAc;AAAA,QACd;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,MACE;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,IAAI;AAAA,YACJ;AAAA,YAEA,eAAW;AAAA,YACX;AAAA,YACA;AAAA,YACA;AAAA,UAAA;AAAA,QAAA;AAAA,MACF;AAAA,IAEH;AAAA,EAAA;AAGP;"}
|
package/dist/index.cjs
CHANGED
|
@@ -14,6 +14,7 @@ const Link = require("./components/Link/Link.cjs");
|
|
|
14
14
|
const Menu = require("./components/Menu/Menu.cjs");
|
|
15
15
|
const MenuItem = require("./components/Menu/MenuItem.cjs");
|
|
16
16
|
const MenuTitle = require("./components/Menu/MenuTitle.cjs");
|
|
17
|
+
const RadioMenuItem = require("./components/Menu/RadioMenuItem.cjs");
|
|
17
18
|
const Progress = require("./components/Progress/Progress.cjs");
|
|
18
19
|
const Search = require("./components/Search/Search.cjs");
|
|
19
20
|
const Separator = require("./components/Separator/Separator.cjs");
|
|
@@ -76,6 +77,7 @@ exports.Link = Link.Link;
|
|
|
76
77
|
exports.Menu = Menu.Menu;
|
|
77
78
|
exports.MenuItem = MenuItem.MenuItem;
|
|
78
79
|
exports.MenuTitle = MenuTitle.MenuTitle;
|
|
80
|
+
exports.RadioMenuItem = RadioMenuItem.RadioMenuItem;
|
|
79
81
|
exports.Progress = Progress.Progress;
|
|
80
82
|
exports.Search = Search.Search;
|
|
81
83
|
exports.Separator = Separator.Separator;
|
package/dist/index.cjs.map
CHANGED
|
@@ -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
|
@@ -18,6 +18,7 @@ export { NavBar, NavItem } from './components/Nav';
|
|
|
18
18
|
export { Menu } from './components/Menu/Menu';
|
|
19
19
|
export { MenuItem } from './components/Menu/MenuItem';
|
|
20
20
|
export { MenuTitle } from './components/Menu/MenuTitle';
|
|
21
|
+
export { RadioMenuItem } from './components/Menu/RadioMenuItem';
|
|
21
22
|
export { Progress } from './components/Progress/Progress';
|
|
22
23
|
export { Search } from './components/Search/Search';
|
|
23
24
|
export { Separator } from './components/Separator/Separator';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import { Link } from "./components/Link/Link.js";
|
|
|
12
12
|
import { Menu } from "./components/Menu/Menu.js";
|
|
13
13
|
import { MenuItem } from "./components/Menu/MenuItem.js";
|
|
14
14
|
import { MenuTitle } from "./components/Menu/MenuTitle.js";
|
|
15
|
+
import { RadioMenuItem } from "./components/Menu/RadioMenuItem.js";
|
|
15
16
|
import { Progress } from "./components/Progress/Progress.js";
|
|
16
17
|
import { Search } from "./components/Search/Search.js";
|
|
17
18
|
import { Separator } from "./components/Separator/Separator.js";
|
|
@@ -107,6 +108,7 @@ export {
|
|
|
107
108
|
RadioInput as Radio,
|
|
108
109
|
RadioControl,
|
|
109
110
|
RadioInput2 as RadioInput,
|
|
111
|
+
RadioMenuItem,
|
|
110
112
|
ReleaseAnnouncement,
|
|
111
113
|
Root,
|
|
112
114
|
Search,
|
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vector-im/compound-web",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.12.0",
|
|
4
4
|
"description": "Compound components for the Web",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -76,15 +76,15 @@
|
|
|
76
76
|
"@typescript-eslint/parser": "^8.0.0",
|
|
77
77
|
"@vector-im/compound-design-tokens": "^4.0.0",
|
|
78
78
|
"@vitejs/plugin-react": "^4.0.4",
|
|
79
|
-
"@vitest/coverage-v8": "^
|
|
79
|
+
"@vitest/coverage-v8": "^3.0.0",
|
|
80
80
|
"browserslist-to-esbuild": "^2.0.0",
|
|
81
81
|
"eslint": "^8.49.0",
|
|
82
|
-
"eslint-config-prettier": "^
|
|
82
|
+
"eslint-config-prettier": "^10.0.0",
|
|
83
83
|
"eslint-plugin-matrix-org": "^1.2.1",
|
|
84
84
|
"eslint-plugin-prettier": "^5.0.0",
|
|
85
85
|
"eslint-plugin-react": "^7.33.2",
|
|
86
86
|
"eslint-plugin-storybook": "^0.11.0",
|
|
87
|
-
"jsdom": "^
|
|
87
|
+
"jsdom": "^26.0.0",
|
|
88
88
|
"prettier": "3.4.2",
|
|
89
89
|
"react": "^19.0.0",
|
|
90
90
|
"react-dom": "^19.0.0",
|
|
@@ -98,9 +98,9 @@
|
|
|
98
98
|
"stylelint-use-logical": "^2.1.0",
|
|
99
99
|
"stylelint-value-no-unknown-custom-properties": "^6.0.0",
|
|
100
100
|
"typescript": "^5.2.2",
|
|
101
|
-
"vite": "^
|
|
101
|
+
"vite": "^6.0.0",
|
|
102
102
|
"vite-plugin-dts": "^4.0.0",
|
|
103
|
-
"vitest": "^
|
|
103
|
+
"vitest": "^3.0.0"
|
|
104
104
|
},
|
|
105
105
|
"dependencies": {
|
|
106
106
|
"@floating-ui/react": "^0.27.0",
|
|
@@ -26,6 +26,11 @@ interface Props {
|
|
|
26
26
|
* The menu title.
|
|
27
27
|
*/
|
|
28
28
|
title: string;
|
|
29
|
+
/**
|
|
30
|
+
* Wether the title is displayed.
|
|
31
|
+
* @default true
|
|
32
|
+
*/
|
|
33
|
+
showTitle?: boolean;
|
|
29
34
|
/**
|
|
30
35
|
* Event handler called when the open state of the menu changes.
|
|
31
36
|
*/
|
|
@@ -63,6 +68,7 @@ const ContextMenuItemWrapper: FC<MenuItemWrapperProps> = ({
|
|
|
63
68
|
*/
|
|
64
69
|
export const ContextMenu: FC<Props> = ({
|
|
65
70
|
title,
|
|
71
|
+
showTitle = true,
|
|
66
72
|
onOpenChange: onOpenChangeProp,
|
|
67
73
|
trigger: triggerProp,
|
|
68
74
|
hasAccessibleAlternative,
|
|
@@ -125,7 +131,9 @@ export const ContextMenu: FC<Props> = ({
|
|
|
125
131
|
{trigger}
|
|
126
132
|
<Portal>
|
|
127
133
|
<Content asChild>
|
|
128
|
-
<FloatingMenu title={title}>
|
|
134
|
+
<FloatingMenu showTitle={showTitle} title={title}>
|
|
135
|
+
{children}
|
|
136
|
+
</FloatingMenu>
|
|
129
137
|
</Content>
|
|
130
138
|
</Portal>
|
|
131
139
|
</Root>
|
|
@@ -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 { RadioInput } from "../Form";
|
|
11
|
+
|
|
12
|
+
type Props = Pick<
|
|
13
|
+
ComponentProps<typeof MenuItem>,
|
|
14
|
+
"className" | "label" | "onSelect" | "disabled"
|
|
15
|
+
> & {
|
|
16
|
+
/**
|
|
17
|
+
* Whether the radio is checked.
|
|
18
|
+
*/
|
|
19
|
+
checked: boolean;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* A menu item with a radio control.
|
|
24
|
+
* Must be used within a compound Menu or other `menu` or `menubar` aria role subtree.
|
|
25
|
+
*/
|
|
26
|
+
export const RadioMenuItem = forwardRef<HTMLInputElement, Props>(
|
|
27
|
+
function RadioMenuItem(
|
|
28
|
+
{ className, label, onSelect, checked, disabled },
|
|
29
|
+
ref,
|
|
30
|
+
) {
|
|
31
|
+
const toggleId = useId();
|
|
32
|
+
// The radio 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 menuitemradio, 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="menuitemradio"
|
|
43
|
+
aria-checked={checked}
|
|
44
|
+
className={className}
|
|
45
|
+
label={label}
|
|
46
|
+
onSelect={onSelect}
|
|
47
|
+
disabled={disabled}
|
|
48
|
+
Icon={
|
|
49
|
+
<RadioInput
|
|
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
|
+
></MenuItem>
|
|
60
|
+
);
|
|
61
|
+
},
|
|
62
|
+
);
|
package/src/index.ts
CHANGED
|
@@ -35,6 +35,7 @@ export { NavBar, NavItem } from "./components/Nav";
|
|
|
35
35
|
export { Menu } from "./components/Menu/Menu";
|
|
36
36
|
export { MenuItem } from "./components/Menu/MenuItem";
|
|
37
37
|
export { MenuTitle } from "./components/Menu/MenuTitle";
|
|
38
|
+
export { RadioMenuItem } from "./components/Menu/RadioMenuItem";
|
|
38
39
|
export { Progress } from "./components/Progress/Progress";
|
|
39
40
|
export { Search } from "./components/Search/Search";
|
|
40
41
|
export { Separator } from "./components/Separator/Separator";
|
|
@@ -956,6 +956,125 @@ button._item_dyt4i_8 {
|
|
|
956
956
|
color: var(--cpd-color-text-disabled);
|
|
957
957
|
}
|
|
958
958
|
/*
|
|
959
|
+
Copyright 2025 New Vector Ltd.
|
|
960
|
+
Copyright 2023 The Matrix.org Foundation C.I.C.
|
|
961
|
+
Copyright 2023 New Vector Ltd
|
|
962
|
+
|
|
963
|
+
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
964
|
+
Please see LICENSE files in the repository root for full details.
|
|
965
|
+
*/
|
|
966
|
+
|
|
967
|
+
._container_1e0uz_10 {
|
|
968
|
+
--size: 20px;
|
|
969
|
+
|
|
970
|
+
display: grid;
|
|
971
|
+
inline-size: var(--size);
|
|
972
|
+
block-size: var(--size);
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
._input_1e0uz_18,
|
|
976
|
+
._ui_1e0uz_19 {
|
|
977
|
+
box-sizing: border-box;
|
|
978
|
+
grid-area: 1/1;
|
|
979
|
+
inline-size: var(--size);
|
|
980
|
+
block-size: var(--size);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
._input_1e0uz_18 {
|
|
984
|
+
opacity: 0;
|
|
985
|
+
margin: 0;
|
|
986
|
+
cursor: pointer;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
._ui_1e0uz_19 {
|
|
990
|
+
pointer-events: none;
|
|
991
|
+
border-radius: 50%;
|
|
992
|
+
border: 1px solid;
|
|
993
|
+
border-color: var(--cpd-color-border-interactive-primary);
|
|
994
|
+
|
|
995
|
+
/* To align the ::after pseudo-element to the center of the radio button */
|
|
996
|
+
display: flex;
|
|
997
|
+
align-items: center;
|
|
998
|
+
justify-content: center;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
._ui_1e0uz_19::after {
|
|
1002
|
+
content: "";
|
|
1003
|
+
inline-size: 6px;
|
|
1004
|
+
block-size: 6px;
|
|
1005
|
+
border-radius: 50%;
|
|
1006
|
+
background: transparent;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
._input_1e0uz_18:checked + ._ui_1e0uz_19 {
|
|
1010
|
+
background-color: var(--cpd-color-bg-accent-rest);
|
|
1011
|
+
border-color: var(--cpd-color-bg-accent-rest);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
._input_1e0uz_18:checked + ._ui_1e0uz_19::after {
|
|
1015
|
+
background: var(--cpd-color-icon-on-solid-primary);
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
._input_1e0uz_18:focus-visible + ._ui_1e0uz_19 {
|
|
1019
|
+
outline: 2px solid var(--cpd-color-border-focused);
|
|
1020
|
+
outline-offset: 1px;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
._input_1e0uz_18[readonly] {
|
|
1024
|
+
pointer-events: none;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
._input_1e0uz_18[readonly] + ._ui_1e0uz_19 {
|
|
1028
|
+
border-color: var(--cpd-color-border-interactive-secondary);
|
|
1029
|
+
background: var(--cpd-color-bg-subtle-secondary);
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
._input_1e0uz_18[disabled] + ._ui_1e0uz_19 {
|
|
1033
|
+
border-color: var(--cpd-color-border-disabled);
|
|
1034
|
+
background: var(--cpd-color-bg-canvas-disabled);
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
._input_1e0uz_18[disabled]:checked + ._ui_1e0uz_19 {
|
|
1038
|
+
border-color: var(--cpd-color-bg-action-primary-disabled);
|
|
1039
|
+
background: var(--cpd-color-bg-action-primary-disabled);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
._input_1e0uz_18[readonly]:checked + ._ui_1e0uz_19::after {
|
|
1043
|
+
background-color: var(--cpd-color-icon-secondary);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
@media (hover) {
|
|
1047
|
+
._input_1e0uz_18:not([disabled], [readonly], :checked):hover + ._ui_1e0uz_19 {
|
|
1048
|
+
border-color: var(--cpd-color-bg-accent-hovered);
|
|
1049
|
+
|
|
1050
|
+
/** TODO: have the shadow in the design tokens */
|
|
1051
|
+
box-shadow: 0 1.2px 2.4px 0 rgb(0 0 0 / 15%);
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
._input_1e0uz_18:not([disabled], [readonly], :checked):hover + ._ui_1e0uz_19::after {
|
|
1055
|
+
background: var(--cpd-color-icon-quaternary);
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
._input_1e0uz_18:not([disabled], [readonly]):checked:hover + ._ui_1e0uz_19 {
|
|
1059
|
+
border-color: var(--cpd-color-bg-accent-hovered);
|
|
1060
|
+
background: var(--cpd-color-bg-accent-hovered);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
._input_1e0uz_18[data-invalid]:not([disabled], [readonly]):checked:hover + ._ui_1e0uz_19 {
|
|
1064
|
+
border-color: var(--cpd-color-bg-critical-hovered);
|
|
1065
|
+
background: var(--cpd-color-bg-critical-hovered);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
._input_1e0uz_18[data-invalid]:not([disabled], [readonly], :checked) + ._ui_1e0uz_19 {
|
|
1070
|
+
border-color: var(--cpd-color-border-critical-primary);
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
._input_1e0uz_18[data-invalid]:not([disabled], [readonly]):checked + ._ui_1e0uz_19 {
|
|
1074
|
+
background-color: var(--cpd-color-bg-critical-primary);
|
|
1075
|
+
border-color: var(--cpd-color-bg-critical-primary);
|
|
1076
|
+
}
|
|
1077
|
+
/*
|
|
959
1078
|
Copyright 2024 New Vector Ltd.
|
|
960
1079
|
|
|
961
1080
|
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
@@ -1988,125 +2107,6 @@ Please see LICENSE files in the repository root for full details.
|
|
|
1988
2107
|
background-color: var(--cpd-color-bg-info-subtle);
|
|
1989
2108
|
}
|
|
1990
2109
|
/*
|
|
1991
|
-
Copyright 2025 New Vector Ltd.
|
|
1992
|
-
Copyright 2023 The Matrix.org Foundation C.I.C.
|
|
1993
|
-
Copyright 2023 New Vector Ltd
|
|
1994
|
-
|
|
1995
|
-
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
1996
|
-
Please see LICENSE files in the repository root for full details.
|
|
1997
|
-
*/
|
|
1998
|
-
|
|
1999
|
-
._container_1e0uz_10 {
|
|
2000
|
-
--size: 20px;
|
|
2001
|
-
|
|
2002
|
-
display: grid;
|
|
2003
|
-
inline-size: var(--size);
|
|
2004
|
-
block-size: var(--size);
|
|
2005
|
-
}
|
|
2006
|
-
|
|
2007
|
-
._input_1e0uz_18,
|
|
2008
|
-
._ui_1e0uz_19 {
|
|
2009
|
-
box-sizing: border-box;
|
|
2010
|
-
grid-area: 1/1;
|
|
2011
|
-
inline-size: var(--size);
|
|
2012
|
-
block-size: var(--size);
|
|
2013
|
-
}
|
|
2014
|
-
|
|
2015
|
-
._input_1e0uz_18 {
|
|
2016
|
-
opacity: 0;
|
|
2017
|
-
margin: 0;
|
|
2018
|
-
cursor: pointer;
|
|
2019
|
-
}
|
|
2020
|
-
|
|
2021
|
-
._ui_1e0uz_19 {
|
|
2022
|
-
pointer-events: none;
|
|
2023
|
-
border-radius: 50%;
|
|
2024
|
-
border: 1px solid;
|
|
2025
|
-
border-color: var(--cpd-color-border-interactive-primary);
|
|
2026
|
-
|
|
2027
|
-
/* To align the ::after pseudo-element to the center of the radio button */
|
|
2028
|
-
display: flex;
|
|
2029
|
-
align-items: center;
|
|
2030
|
-
justify-content: center;
|
|
2031
|
-
}
|
|
2032
|
-
|
|
2033
|
-
._ui_1e0uz_19::after {
|
|
2034
|
-
content: "";
|
|
2035
|
-
inline-size: 6px;
|
|
2036
|
-
block-size: 6px;
|
|
2037
|
-
border-radius: 50%;
|
|
2038
|
-
background: transparent;
|
|
2039
|
-
}
|
|
2040
|
-
|
|
2041
|
-
._input_1e0uz_18:checked + ._ui_1e0uz_19 {
|
|
2042
|
-
background-color: var(--cpd-color-bg-accent-rest);
|
|
2043
|
-
border-color: var(--cpd-color-bg-accent-rest);
|
|
2044
|
-
}
|
|
2045
|
-
|
|
2046
|
-
._input_1e0uz_18:checked + ._ui_1e0uz_19::after {
|
|
2047
|
-
background: var(--cpd-color-icon-on-solid-primary);
|
|
2048
|
-
}
|
|
2049
|
-
|
|
2050
|
-
._input_1e0uz_18:focus-visible + ._ui_1e0uz_19 {
|
|
2051
|
-
outline: 2px solid var(--cpd-color-border-focused);
|
|
2052
|
-
outline-offset: 1px;
|
|
2053
|
-
}
|
|
2054
|
-
|
|
2055
|
-
._input_1e0uz_18[readonly] {
|
|
2056
|
-
pointer-events: none;
|
|
2057
|
-
}
|
|
2058
|
-
|
|
2059
|
-
._input_1e0uz_18[readonly] + ._ui_1e0uz_19 {
|
|
2060
|
-
border-color: var(--cpd-color-border-interactive-secondary);
|
|
2061
|
-
background: var(--cpd-color-bg-subtle-secondary);
|
|
2062
|
-
}
|
|
2063
|
-
|
|
2064
|
-
._input_1e0uz_18[disabled] + ._ui_1e0uz_19 {
|
|
2065
|
-
border-color: var(--cpd-color-border-disabled);
|
|
2066
|
-
background: var(--cpd-color-bg-canvas-disabled);
|
|
2067
|
-
}
|
|
2068
|
-
|
|
2069
|
-
._input_1e0uz_18[disabled]:checked + ._ui_1e0uz_19 {
|
|
2070
|
-
border-color: var(--cpd-color-bg-action-primary-disabled);
|
|
2071
|
-
background: var(--cpd-color-bg-action-primary-disabled);
|
|
2072
|
-
}
|
|
2073
|
-
|
|
2074
|
-
._input_1e0uz_18[readonly]:checked + ._ui_1e0uz_19::after {
|
|
2075
|
-
background-color: var(--cpd-color-icon-secondary);
|
|
2076
|
-
}
|
|
2077
|
-
|
|
2078
|
-
@media (hover) {
|
|
2079
|
-
._input_1e0uz_18:not([disabled], [readonly], :checked):hover + ._ui_1e0uz_19 {
|
|
2080
|
-
border-color: var(--cpd-color-bg-accent-hovered);
|
|
2081
|
-
|
|
2082
|
-
/** TODO: have the shadow in the design tokens */
|
|
2083
|
-
box-shadow: 0 1.2px 2.4px 0 rgb(0 0 0 / 15%);
|
|
2084
|
-
}
|
|
2085
|
-
|
|
2086
|
-
._input_1e0uz_18:not([disabled], [readonly], :checked):hover + ._ui_1e0uz_19::after {
|
|
2087
|
-
background: var(--cpd-color-icon-quaternary);
|
|
2088
|
-
}
|
|
2089
|
-
|
|
2090
|
-
._input_1e0uz_18:not([disabled], [readonly]):checked:hover + ._ui_1e0uz_19 {
|
|
2091
|
-
border-color: var(--cpd-color-bg-accent-hovered);
|
|
2092
|
-
background: var(--cpd-color-bg-accent-hovered);
|
|
2093
|
-
}
|
|
2094
|
-
|
|
2095
|
-
._input_1e0uz_18[data-invalid]:not([disabled], [readonly]):checked:hover + ._ui_1e0uz_19 {
|
|
2096
|
-
border-color: var(--cpd-color-bg-critical-hovered);
|
|
2097
|
-
background: var(--cpd-color-bg-critical-hovered);
|
|
2098
|
-
}
|
|
2099
|
-
}
|
|
2100
|
-
|
|
2101
|
-
._input_1e0uz_18[data-invalid]:not([disabled], [readonly], :checked) + ._ui_1e0uz_19 {
|
|
2102
|
-
border-color: var(--cpd-color-border-critical-primary);
|
|
2103
|
-
}
|
|
2104
|
-
|
|
2105
|
-
._input_1e0uz_18[data-invalid]:not([disabled], [readonly]):checked + ._ui_1e0uz_19 {
|
|
2106
|
-
background-color: var(--cpd-color-bg-critical-primary);
|
|
2107
|
-
border-color: var(--cpd-color-bg-critical-primary);
|
|
2108
|
-
}
|
|
2109
|
-
/*
|
|
2110
2110
|
Copyright 2024 New Vector Ltd.
|
|
2111
2111
|
|
|
2112
2112
|
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|