@vector-im/compound-web 7.10.2 → 7.11.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/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/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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"}
|
|
@@ -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":"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,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACxD,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,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACtE,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;AACrD,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAC;AAE1E,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":"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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.11.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",
|
|
@@ -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
|