no-frills-ui 0.0.14-rc.1 → 0.0.14-rc.3
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/index.js +446 -225
- package/dist/index.js.map +1 -1
- package/lib-esm/components/Accordion/AccordionStep.js +9 -8
- package/lib-esm/components/Accordion/AccordionStep.js.map +1 -1
- package/lib-esm/components/Chip/Chip.js +5 -4
- package/lib-esm/components/Chip/Chip.js.map +1 -1
- package/lib-esm/components/ChipInput/ChipInput.js +80 -51
- package/lib-esm/components/ChipInput/ChipInput.js.map +1 -1
- package/lib-esm/components/DragAndDrop/DragAndDrop.js +2 -2
- package/lib-esm/components/DragAndDrop/DragItem.js +2 -2
- package/lib-esm/components/Drawer/Drawer.d.ts +1 -1
- package/lib-esm/components/Drawer/Drawer.js +2 -3
- package/lib-esm/components/Drawer/Drawer.js.map +1 -1
- package/lib-esm/components/Groups/Group.js +3 -3
- package/lib-esm/components/Groups/Group.js.map +1 -1
- package/lib-esm/components/Input/Checkbox.d.ts +2 -0
- package/lib-esm/components/Input/Checkbox.js +54 -23
- package/lib-esm/components/Input/Checkbox.js.map +1 -1
- package/lib-esm/components/Input/Dropdown.d.ts +2 -0
- package/lib-esm/components/Input/Dropdown.js +123 -59
- package/lib-esm/components/Input/Dropdown.js.map +1 -1
- package/lib-esm/components/Input/Input.js +17 -8
- package/lib-esm/components/Input/Input.js.map +1 -1
- package/lib-esm/components/Input/Radio.d.ts +2 -0
- package/lib-esm/components/Input/Radio.js +22 -10
- package/lib-esm/components/Input/Radio.js.map +1 -1
- package/lib-esm/components/Input/RadioButton.d.ts +2 -0
- package/lib-esm/components/Input/RadioButton.js +21 -9
- package/lib-esm/components/Input/RadioButton.js.map +1 -1
- package/lib-esm/components/Input/Select.js +21 -11
- package/lib-esm/components/Input/Select.js.map +1 -1
- package/lib-esm/components/Input/TextArea.js +17 -8
- package/lib-esm/components/Input/TextArea.js.map +1 -1
- package/lib-esm/components/Input/Toggle.d.ts +2 -0
- package/lib-esm/components/Input/Toggle.js +45 -15
- package/lib-esm/components/Input/Toggle.js.map +1 -1
- package/lib-esm/components/Input/index.d.ts +1 -0
- package/lib-esm/components/Menu/MenuItem.d.ts +1 -1
- package/lib-esm/components/Menu/MenuItem.js +1 -1
- package/lib-esm/components/Menu/MenuItem.js.map +1 -1
- package/lib-esm/components/Modal/Modal.d.ts +1 -1
- package/lib-esm/components/Modal/Modal.js +1 -2
- package/lib-esm/components/Modal/Modal.js.map +1 -1
- package/lib-esm/components/Notification/NotificationManager.js +1 -0
- package/lib-esm/components/Notification/NotificationManager.js.map +1 -1
- package/lib-esm/components/Popover/Popover.d.ts +1 -1
- package/lib-esm/components/Popover/Popover.js +3 -3
- package/lib-esm/components/Popover/Popover.js.map +1 -1
- package/lib-esm/components/Stepper/Stepper.js +14 -5
- package/lib-esm/components/Stepper/Stepper.js.map +1 -1
- package/lib-esm/index.js +1 -1
- package/lib-esm/shared/LayerManager.js +2 -2
- package/lib-esm/shared/styles.d.ts +4 -0
- package/lib-esm/shared/styles.js +10 -6
- package/lib-esm/shared/styles.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Drawer.js","sources":["../../../src/components/Drawer/Drawer.tsx"],"sourcesContent":["import React from 'react';\nimport styled from '@emotion/styled';\nimport { getThemeValue, THEME_NAME } from '../../shared/constants';\nimport LayerManager, { LAYER_POSITION } from '../../shared/LayerManager';\n\nexport {\n Header as DrawerHeader,\n Body as DrawerBody,\n Footer as DrawerFooter,\n} from '../../shared/styles';\n\nexport enum DRAWER_POSITION {\n LEFT = 'LEFT',\n RIGHT = 'RIGHT',\n BOTTOM = 'BOTTOM',\n}\n\nconst positionStyle = (size?: string) => ({\n [DRAWER_POSITION.LEFT]: {\n before: `height: 100vh; min-width: ${size || '300px'}; transform: translateX(-100%);`,\n after: 'transform: translateX(0%);',\n },\n [DRAWER_POSITION.RIGHT]: {\n before: `height: 100vh; min-width: ${size || '300px'}; transform: translateX(100%);`,\n after: 'transform: translateX(0%);',\n },\n [DRAWER_POSITION.BOTTOM]: {\n before: `\n position: absolute;\n bottom: 0;\n width: 100%;\n height: ${size || '90vh'};\n transform: translateY(100%);\n border-radius: 15px 15px 0 0; \n `,\n after: 'transform: translateX(0%);',\n },\n});\n\nconst DrawerDiv = styled.div<{ position: DRAWER_POSITION; size?: string }>`\n display: flex;\n flex-direction: column;\n background-color: ${getThemeValue(THEME_NAME.BACKGROUND)};\n transition: transform 0.3s ease;\n box-shadow: ${getThemeValue(THEME_NAME.MODAL_SHADOW)};\n ${(props) => positionStyle(props.size)[props.position].before}\n\n .nf-layer-enter & {\n transform: translateX(0%);\n ${(props) => positionStyle(props.size)[props.position].after}\n }\n`;\n\ntype DrawerProps = {\n /** Opens the drawer */\n open: boolean;\n /** position of the drawer */\n position: DRAWER_POSITION;\n /** size of the drawer */\n size?: string;\n /** Shows an overlay behind the drawer. */\n overlay?: boolean;\n /** Closes the drawer on esc */\n closeOnEsc?: boolean;\n /** Closes the drawer on overlay click */\n closeOnOverlayClick?: boolean;\n /** Call back function called when the drawer closes. */\n onClose?: () => void;\n /** Ref forwarded to the underlying HTMLDivElement of the drawer container */\n forwardRef?: React.Ref<HTMLDivElement> | React.MutableRefObject<HTMLDivElement | null>;\n};\n\ninterface DrawerState {\n open: boolean;\n}\n\nconst positionMap = {\n [DRAWER_POSITION.LEFT]: LAYER_POSITION.TOP_LEFT,\n [DRAWER_POSITION.RIGHT]: LAYER_POSITION.TOP_RIGHT,\n [DRAWER_POSITION.BOTTOM]: LAYER_POSITION.BOTTOM_LEFT,\n};\n\n/**\n * Drawer component\n *\n * A panel that slides in from the edge of the screen.\n * It sits on top of the application content and is often used for navigation or details.\n *\n * Accessibility:\n * - Implements ARIA `role=\"dialog\"` and `aria-modal=\"true\"`.\n * - Traps focus effectively within the drawer while open.\n * - Restores focus to the triggering element upon closure.\n * - Supports closing via ESC key and overlay click.\n */\nexport default class Drawer extends React.Component<\n React.PropsWithChildren<DrawerProps>,\n DrawerState\n> {\n state = {\n open: false,\n };\n\n static defaultProps = {\n overlay: true,\n position: DRAWER_POSITION.LEFT,\n closeOnEsc: true,\n closeOnOverlayClick: true,\n };\n\n /**\n * Syncs state with props.\n */\n static getDerivedStateFromProps(props: DrawerProps) {\n if (props.open) {\n return {\n open: true,\n };\n }\n return null;\n }\n\n private layer?: ReturnType<typeof LayerManager.renderLayer>;\n\n private closeCallback?: (resp?: unknown) => void;\n\n /**\n * Internal close handler.\n * Restores focus and calls the external onClose callback.\n */\n private onClose = () => {\n this.restoreFocus();\n this.setState({\n open: false,\n });\n this.props.onClose?.();\n this.closeCallback = undefined;\n this.layer = undefined;\n };\n\n private lastFocusedElement: HTMLElement | null = null;\n private drawerRef = React.createRef<HTMLDivElement>();\n\n /**\n * Retrieves all focusable elements within the drawer.\n */\n private getFocusableElements = (): HTMLElement[] => {\n if (!this.drawerRef.current) return [];\n return Array.from(\n this.drawerRef.current.querySelectorAll(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n ),\n ) as HTMLElement[];\n };\n\n /**\n * Handles keydown events to implement the focus trap.\n * Traps Tab and Shift+Tab within the drawer.\n */\n private handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Tab') {\n const focusableElements = this.getFocusableElements();\n if (focusableElements.length === 0) return;\n\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n if (e.shiftKey) {\n if (document.activeElement === firstElement) {\n lastElement.focus();\n e.preventDefault();\n }\n } else {\n if (document.activeElement === lastElement) {\n firstElement.focus();\n e.preventDefault();\n }\n }\n }\n };\n\n /**\n * Lifecycle method to save the currently focused element when the drawer mounts while open.\n */\n componentDidMount() {\n if (this.props.open) {\n this.lastFocusedElement = document.activeElement as HTMLElement;\n // Handle initial open state\n this.handleOpen();\n }\n }\n\n /**\n * Handles opening the drawer by creating the layer.\n */\n private handleOpen = () => {\n const { closeOnEsc, closeOnOverlayClick, position, size, overlay, children, ...rest } =\n this.props;\n\n this.layer = LayerManager.renderLayer({\n overlay,\n exitDelay: 300,\n position: positionMap[position],\n closeCallback: this.onClose,\n closeOnEsc,\n closeOnOverlayClick,\n component: (\n <DrawerDiv\n {...rest}\n ref={this.setDrawerRef}\n role=\"dialog\"\n aria-modal=\"true\"\n tabIndex={-1}\n onKeyDown={this.handleKeyDown}\n position={position}\n size={size}\n onClick={(e) => e.stopPropagation()}\n >\n {children}\n </DrawerDiv>\n ),\n });\n this.closeCallback = this.layer[1];\n this.forceUpdate();\n };\n\n /**\n * Lifecycle method to restore focus when the drawer unmounts.\n */\n componentWillUnmount() {\n if (this.props.open) {\n this.restoreFocus();\n }\n }\n\n /**\n * Restores focus to the element that was focused before the drawer opened.\n */\n private restoreFocus = () => {\n if (this.lastFocusedElement) {\n // Check if the element is still in the document\n const elementToBeFocused = this.lastFocusedElement;\n this.lastFocusedElement = null;\n setTimeout(() => {\n if (document.body.contains(elementToBeFocused)) {\n elementToBeFocused.focus();\n }\n }, 100);\n }\n };\n\n /**\n * Callback ref to capture the Drawer DOM element.\n * Triggers initial focus setting when the element mounts.\n */\n private setDrawerRef = (node: HTMLDivElement | null) => {\n // Update ref\n (this.drawerRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n\n if (node) {\n // Set initial focus when the node is mounted\n this.setInitialFocus(node);\n }\n };\n\n /**\n * Sets initial focus within the drawer.\n * Tries to focus the header first, then the first interactive element, or falls back to the container.\n */\n private setInitialFocus = (root: HTMLElement) => {\n // Try to find the header (assumed to be the first child)\n const firstChild = root.firstElementChild as HTMLElement;\n if (firstChild) {\n // Ensure it's focusable\n if (firstChild.getAttribute('tabindex') === null) {\n firstChild.setAttribute('tabindex', '-1');\n }\n firstChild.focus();\n return;\n }\n\n // Fallback to focusable elements\n const focusableElements = this.getFocusableElements();\n if (focusableElements.length > 0) {\n focusableElements[0].focus();\n } else {\n // Fallback to container\n root.focus();\n }\n };\n\n /**\n * Lifecycle method to handle Drawer updates.\n * Manages opening/closing logic via LayerManager and focus preservation.\n */\n getSnapshotBeforeUpdate(prevProps: DrawerProps) {\n const { open } = this.props;\n\n if (prevProps.open && !open) {\n this.closeCallback?.();\n this.restoreFocus();\n }\n\n if (!prevProps.open && open) {\n // Save current focus\n this.lastFocusedElement = document.activeElement as HTMLElement;\n this.handleOpen();\n }\n\n return null;\n }\n\n /**\n * Sets the ref prop passed to the Drawer Container component.\n * @param node\n */\n setRefProp = (node: HTMLDivElement | null) => {\n if (this.props.forwardRef && typeof this.props.forwardRef === 'function') {\n this.props.forwardRef(node);\n } else if (this.props.forwardRef && typeof this.props.forwardRef === 'object') {\n (this.props.forwardRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n }\n };\n\n /**\n * Renders the Drawer component via the LayerManager portal.\n */\n render() {\n if (this.state.open && this.layer) {\n const [Component] = this.layer;\n return <Component ref={this.setRefProp} />;\n }\n\n return null;\n }\n}\n"],"names":["DRAWER_POSITION","positionStyle","size","before","after","DrawerDiv","styled","getThemeValue","THEME_NAME","BACKGROUND","MODAL_SHADOW","props","position","positionMap","LAYER_POSITION","TOP_LEFT","TOP_RIGHT","BOTTOM_LEFT","Drawer","React","Component","getDerivedStateFromProps","open","componentDidMount","lastFocusedElement","document","activeElement","handleOpen","componentWillUnmount","restoreFocus","getSnapshotBeforeUpdate","prevProps","closeCallback","render","state","layer","_jsx","ref","setRefProp","onClose","setState","undefined","drawerRef","createRef","getFocusableElements","current","Array","from","querySelectorAll","handleKeyDown","e","key","focusableElements","length","firstElement","lastElement","shiftKey","focus","preventDefault","closeOnEsc","closeOnOverlayClick","overlay","children","rest","LayerManager","renderLayer","exitDelay","component","setDrawerRef","role","aria-modal","tabIndex","onKeyDown","onClick","stopPropagation","forceUpdate","elementToBeFocused","setTimeout","body","contains","node","setInitialFocus","root","firstChild","firstElementChild","getAttribute","setAttribute","forwardRef","defaultProps"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Drawer.js","sources":["../../../src/components/Drawer/Drawer.tsx"],"sourcesContent":["import React from 'react';\nimport styled from '@emotion/styled';\nimport { getThemeValue, THEME_NAME } from '../../shared/constants';\nimport LayerManager, { LAYER_POSITION } from '../../shared/LayerManager';\n\nexport {\n Header as DrawerHeader,\n Body as DrawerBody,\n Footer as DrawerFooter,\n} from '../../shared/styles';\n\nexport enum DRAWER_POSITION {\n LEFT = 'LEFT',\n RIGHT = 'RIGHT',\n BOTTOM = 'BOTTOM',\n}\n\nconst positionStyle = (size?: string) => ({\n [DRAWER_POSITION.LEFT]: {\n before: `height: 100vh; min-width: ${size || '300px'}; transform: translateX(-100%);`,\n after: 'transform: translateX(0%);',\n },\n [DRAWER_POSITION.RIGHT]: {\n before: `height: 100vh; min-width: ${size || '300px'}; transform: translateX(100%);`,\n after: 'transform: translateX(0%);',\n },\n [DRAWER_POSITION.BOTTOM]: {\n before: `\n position: absolute;\n bottom: 0;\n width: 100%;\n height: ${size || '90vh'};\n transform: translateY(100%);\n border-radius: 15px 15px 0 0; \n `,\n after: 'transform: translateX(0%);',\n },\n});\n\nconst DrawerDiv = styled.div<{ position: DRAWER_POSITION; size?: string }>`\n display: flex;\n flex-direction: column;\n background-color: ${getThemeValue(THEME_NAME.BACKGROUND)};\n transition: transform 0.3s ease;\n box-shadow: ${getThemeValue(THEME_NAME.MODAL_SHADOW)};\n ${(props) => positionStyle(props.size)[props.position].before}\n\n .nf-layer-enter & {\n transform: translateX(0%);\n ${(props) => positionStyle(props.size)[props.position].after}\n }\n`;\n\ntype DrawerProps = {\n /** Opens the drawer */\n open: boolean;\n /** position of the drawer */\n position: DRAWER_POSITION;\n /** size of the drawer */\n size?: string;\n /** Shows an overlay behind the drawer. */\n overlay?: boolean;\n /** Closes the drawer on esc */\n closeOnEsc?: boolean;\n /** Closes the drawer on overlay click */\n closeOnOverlayClick?: boolean;\n /** Call back function called when the drawer closes. */\n onClose?: () => void;\n /** Ref forwarded to the underlying HTMLDivElement of the drawer container */\n forwardRef?: React.Ref<HTMLDivElement> | React.MutableRefObject<HTMLDivElement | null>;\n};\n\ninterface DrawerState {\n open: boolean;\n}\n\nconst positionMap = {\n [DRAWER_POSITION.LEFT]: LAYER_POSITION.TOP_LEFT,\n [DRAWER_POSITION.RIGHT]: LAYER_POSITION.TOP_RIGHT,\n [DRAWER_POSITION.BOTTOM]: LAYER_POSITION.BOTTOM_LEFT,\n};\n\n/**\n * Drawer component\n *\n * A panel that slides in from the edge of the screen.\n * It sits on top of the application content and is often used for navigation or details.\n *\n * Accessibility:\n * - Implements ARIA `role=\"dialog\"` and `aria-modal=\"true\"`.\n * - Traps focus effectively within the drawer while open.\n * - Restores focus to the triggering element upon closure.\n * - Supports closing via ESC key and overlay click.\n */\nexport default class Drawer extends React.Component<\n React.PropsWithChildren<DrawerProps>,\n DrawerState\n> {\n state = {\n open: false,\n };\n\n static defaultProps = {\n overlay: true,\n position: DRAWER_POSITION.LEFT,\n closeOnEsc: true,\n closeOnOverlayClick: true,\n };\n\n /**\n * Syncs state with props.\n */\n static getDerivedStateFromProps(props: DrawerProps) {\n if (props.open) {\n return {\n open: true,\n };\n }\n return null;\n }\n\n private layer?: ReturnType<typeof LayerManager.renderLayer>;\n\n private closeCallback?: (resp?: unknown) => void;\n\n /**\n * Internal close handler.\n * Restores focus and calls the external onClose callback.\n */\n private onClose = () => {\n this.restoreFocus();\n this.setState({\n open: false,\n });\n this.props.onClose?.();\n this.closeCallback = undefined;\n this.layer = undefined;\n };\n\n private lastFocusedElement: HTMLElement | null = null;\n private drawerRef = React.createRef<HTMLDivElement>();\n\n /**\n * Retrieves all focusable elements within the drawer.\n */\n private getFocusableElements = (): HTMLElement[] => {\n if (!this.drawerRef.current) return [];\n return Array.from(\n this.drawerRef.current.querySelectorAll(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n ),\n ) as HTMLElement[];\n };\n\n /**\n * Handles keydown events to implement the focus trap.\n * Traps Tab and Shift+Tab within the drawer.\n */\n private handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Tab') {\n const focusableElements = this.getFocusableElements();\n if (focusableElements.length === 0) return;\n\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n if (e.shiftKey) {\n if (document.activeElement === firstElement) {\n lastElement.focus();\n e.preventDefault();\n }\n } else {\n if (document.activeElement === lastElement) {\n firstElement.focus();\n e.preventDefault();\n }\n }\n }\n };\n\n /**\n * Lifecycle method to save the currently focused element when the drawer mounts while open.\n */\n componentDidMount() {\n if (this.props.open) {\n this.lastFocusedElement = document.activeElement as HTMLElement;\n // Handle initial open state\n this.handleOpen();\n }\n }\n\n /**\n * Handles opening the drawer by creating the layer.\n */\n private handleOpen = () => {\n const { closeOnEsc, closeOnOverlayClick, position, size, overlay, children, ...rest } =\n this.props;\n\n this.layer = LayerManager.renderLayer({\n overlay,\n exitDelay: 300,\n position: positionMap[position],\n closeCallback: this.onClose,\n closeOnEsc,\n closeOnOverlayClick,\n component: (\n <DrawerDiv\n {...rest}\n ref={this.setDrawerRef}\n role=\"dialog\"\n aria-modal=\"true\"\n tabIndex={-1}\n onKeyDown={this.handleKeyDown}\n position={position}\n size={size}\n onClick={(e) => e.stopPropagation()}\n >\n {children}\n </DrawerDiv>\n ),\n });\n this.closeCallback = this.layer[1];\n this.forceUpdate();\n };\n\n /**\n * Lifecycle method to restore focus when the drawer unmounts.\n */\n componentWillUnmount() {\n if (this.props.open) {\n this.restoreFocus();\n }\n }\n\n /**\n * Restores focus to the element that was focused before the drawer opened.\n */\n private restoreFocus = () => {\n if (this.lastFocusedElement) {\n // Check if the element is still in the document\n const elementToBeFocused = this.lastFocusedElement;\n this.lastFocusedElement = null;\n setTimeout(() => {\n if (document.body.contains(elementToBeFocused)) {\n elementToBeFocused.focus();\n }\n }, 100);\n }\n };\n\n /**\n * Callback ref to capture the Drawer DOM element.\n * Triggers initial focus setting when the element mounts.\n */\n private setDrawerRef = (node: HTMLDivElement | null) => {\n // Update ref\n (this.drawerRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n\n if (node) {\n // Set initial focus when the node is mounted\n this.setInitialFocus(node);\n }\n };\n\n /**\n * Sets initial focus within the drawer.\n * Tries to focus the header first, then the first interactive element, or falls back to the container.\n */\n private setInitialFocus = (root: HTMLElement) => {\n // Try to find the header (assumed to be the first child)\n const firstChild = root.firstElementChild as HTMLElement;\n if (firstChild) {\n // Ensure it's focusable\n if (firstChild.getAttribute('tabindex') === null) {\n firstChild.setAttribute('tabindex', '-1');\n }\n firstChild.focus();\n return;\n }\n\n // Fallback to focusable elements\n const focusableElements = this.getFocusableElements();\n if (focusableElements.length > 0) {\n focusableElements[0].focus();\n } else {\n // Fallback to container\n root.focus();\n }\n };\n\n /**\n * Lifecycle method to handle Drawer updates.\n * Manages opening/closing logic via LayerManager and focus preservation.\n */\n componentDidUpdate(prevProps: DrawerProps) {\n const { open } = this.props;\n\n if (prevProps.open && !open) {\n this.closeCallback?.();\n this.restoreFocus();\n }\n\n if (!prevProps.open && open) {\n // Save current focus\n this.lastFocusedElement = document.activeElement as HTMLElement;\n this.handleOpen();\n }\n }\n\n /**\n * Sets the ref prop passed to the Drawer Container component.\n * @param node\n */\n setRefProp = (node: HTMLDivElement | null) => {\n if (this.props.forwardRef && typeof this.props.forwardRef === 'function') {\n this.props.forwardRef(node);\n } else if (this.props.forwardRef && typeof this.props.forwardRef === 'object') {\n (this.props.forwardRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n }\n };\n\n /**\n * Renders the Drawer component via the LayerManager portal.\n */\n render() {\n if (this.state.open && this.layer) {\n const [Component] = this.layer;\n return <Component ref={this.setRefProp} />;\n }\n\n return null;\n }\n}\n"],"names":["DRAWER_POSITION","positionStyle","size","before","after","DrawerDiv","styled","getThemeValue","THEME_NAME","BACKGROUND","MODAL_SHADOW","props","position","positionMap","LAYER_POSITION","TOP_LEFT","TOP_RIGHT","BOTTOM_LEFT","Drawer","React","Component","getDerivedStateFromProps","open","componentDidMount","lastFocusedElement","document","activeElement","handleOpen","componentWillUnmount","restoreFocus","componentDidUpdate","prevProps","closeCallback","render","state","layer","_jsx","ref","setRefProp","onClose","setState","undefined","drawerRef","createRef","getFocusableElements","current","Array","from","querySelectorAll","handleKeyDown","e","key","focusableElements","length","firstElement","lastElement","shiftKey","focus","preventDefault","closeOnEsc","closeOnOverlayClick","overlay","children","rest","LayerManager","renderLayer","exitDelay","component","setDrawerRef","role","aria-modal","tabIndex","onKeyDown","onClick","stopPropagation","forceUpdate","elementToBeFocused","setTimeout","body","contains","node","setInitialFocus","root","firstChild","firstElementChild","getAttribute","setAttribute","forwardRef","defaultProps"],"mappings":"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"}
|
|
@@ -4,9 +4,9 @@ import styled from '@emotion/styled';
|
|
|
4
4
|
import { getThemeValue, THEME_NAME } from '../../shared/constants.js';
|
|
5
5
|
|
|
6
6
|
const Container$3 = /*#__PURE__*/ styled("div", {
|
|
7
|
-
target: "
|
|
7
|
+
target: "e1m58trk0",
|
|
8
8
|
label: "Container"
|
|
9
|
-
})("display:inline-flex;border:1px solid ", getThemeValue(THEME_NAME.BORDER_COLOR), ";border-radius:3px;margin:5px;& button,& label{margin:0;border:none;border-radius:0;border-left:1px solid ", getThemeValue(THEME_NAME.BORDER_COLOR), ";box-shadow:none;height:32px;}& > div button{border-left:none;}& input,& select{border:none;height:32px;}& input,& select{border-radius:0;}& input:active,& select:active{box-shadow:none;}& > div > span{top:8px;}& > *:first-
|
|
9
|
+
})("display:inline-flex;border:1px solid ", getThemeValue(THEME_NAME.BORDER_COLOR), ";border-radius:3px;margin:5px;& button,& label{margin:0;border:none;border-radius:0;border-left:1px solid ", getThemeValue(THEME_NAME.BORDER_COLOR), ";box-shadow:none;height:32px;}& > div button{border-left:none;}& input,& select{border:none;height:32px;}& input,& select{border-radius:0;}& input:active,& select:active{box-shadow:none;}& > div > span{top:8px;}& > *:first-of-type,& > label:first-of-type input,& > label:first-of-type select,& > *:first-of-type label,& > *:first-of-type input{border-left:none;border-radius:2px 0 0 2px;}& > *:last-child,& > label:last-child input,& > label:last-child select,& > *:last-child label,& > *:last-child input{border-radius:0 2px 2px 0;}& *:focus,& *:focus + span{z-index:1;}&:focus-within,&:hover{box-shadow:", getThemeValue(THEME_NAME.HOVER_SHADOW), ";}", (props)=>props.errorText ? `
|
|
10
10
|
border-color: ${getThemeValue(THEME_NAME.ERROR)};
|
|
11
11
|
|
|
12
12
|
& > button, & > label {
|
|
@@ -14,7 +14,7 @@ const Container$3 = /*#__PURE__*/ styled("div", {
|
|
|
14
14
|
}
|
|
15
15
|
` : '');
|
|
16
16
|
const ErrorContainer = /*#__PURE__*/ styled("div", {
|
|
17
|
-
target: "
|
|
17
|
+
target: "e1m58trk1",
|
|
18
18
|
label: "ErrorContainer"
|
|
19
19
|
})("color:", getThemeValue(THEME_NAME.ERROR), ";margin-left:8px;font-size:12px;");
|
|
20
20
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Group.js","sources":["../../../src/components/Groups/Group.tsx"],"sourcesContent":["import React, { useId } from 'react';\nimport styled from '@emotion/styled';\nimport { getThemeValue, THEME_NAME } from '../../shared/constants';\n\nconst Container = styled.div<GroupProps>`\n display: inline-flex;\n border: 1px solid ${getThemeValue(THEME_NAME.BORDER_COLOR)};\n border-radius: 3px;\n margin: 5px;\n\n /* overrides */\n & button,\n & label {\n margin: 0;\n border: none;\n border-radius: 0;\n border-left: 1px solid ${getThemeValue(THEME_NAME.BORDER_COLOR)};\n box-shadow: none;\n height: 32px;\n }\n\n & > div button {\n border-left: none;\n }\n\n & input,\n & select {\n border: none;\n height: 32px;\n }\n\n & input,\n & select {\n border-radius: 0;\n }\n\n & input:active,\n & select:active {\n box-shadow: none;\n }\n\n & > div > span {\n top: 8px;\n }\n\n /* Handling for first and last child */\n & > *:first-
|
|
1
|
+
{"version":3,"file":"Group.js","sources":["../../../src/components/Groups/Group.tsx"],"sourcesContent":["import React, { useId } from 'react';\nimport styled from '@emotion/styled';\nimport { getThemeValue, THEME_NAME } from '../../shared/constants';\n\nconst Container = styled.div<GroupProps>`\n display: inline-flex;\n border: 1px solid ${getThemeValue(THEME_NAME.BORDER_COLOR)};\n border-radius: 3px;\n margin: 5px;\n\n /* overrides */\n & button,\n & label {\n margin: 0;\n border: none;\n border-radius: 0;\n border-left: 1px solid ${getThemeValue(THEME_NAME.BORDER_COLOR)};\n box-shadow: none;\n height: 32px;\n }\n\n & > div button {\n border-left: none;\n }\n\n & input,\n & select {\n border: none;\n height: 32px;\n }\n\n & input,\n & select {\n border-radius: 0;\n }\n\n & input:active,\n & select:active {\n box-shadow: none;\n }\n\n & > div > span {\n top: 8px;\n }\n\n /* Handling for first and last child */\n & > *:first-of-type,\n & > label:first-of-type input,\n & > label:first-of-type select,\n & > *:first-of-type label,\n & > *:first-of-type input {\n border-left: none;\n border-radius: 2px 0 0 2px;\n }\n\n & > *:last-child,\n & > label:last-child input,\n & > label:last-child select,\n & > *:last-child label,\n & > *:last-child input {\n border-radius: 0 2px 2px 0;\n }\n\n /* focus */\n & *:focus,\n & *:focus + span {\n z-index: 1;\n }\n\n &:focus-within,\n &:hover {\n box-shadow: ${getThemeValue(THEME_NAME.HOVER_SHADOW)};\n }\n\n ${(props) =>\n props.errorText\n ? `\n border-color: ${getThemeValue(THEME_NAME.ERROR)};\n\n & > button, & > label {\n border-color: ${getThemeValue(THEME_NAME.ERROR)};\n }\n `\n : ''}\n`;\n\nconst ErrorContainer = styled.div`\n color: ${getThemeValue(THEME_NAME.ERROR)};\n margin-left: 8px;\n font-size: 12px;\n`;\n\ntype GroupProps = React.PropsWithChildren<{\n /** Error Message for the group */\n errorText?: string;\n}>;\n\n/**\n * Group Component\n * @param props - Component props\n * @param ref - Ref forwarded to the underlying HTMLDivElement\n */\nfunction GroupComponent(\n props: React.PropsWithChildren<GroupProps>,\n ref: React.Ref<HTMLDivElement>,\n) {\n const errorId = useId();\n\n return (\n <>\n <Container\n {...props}\n ref={ref}\n aria-describedby={props.errorText ? errorId : undefined}\n >\n {props.children}\n </Container>\n {props.errorText && <ErrorContainer id={errorId}>{props.errorText}</ErrorContainer>}\n </>\n );\n}\n\nconst Group = React.forwardRef<HTMLDivElement, GroupProps>(GroupComponent);\nexport default Group;\n"],"names":["Container","styled","getThemeValue","THEME_NAME","BORDER_COLOR","HOVER_SHADOW","props","errorText","ERROR","ErrorContainer","GroupComponent","ref","errorId","useId","_jsxs","_Fragment","_jsx","aria-describedby","undefined","children","id","Group","React","forwardRef"],"mappings":";;;;;AAIA,MAAMA,WAAAA,iBAAYC,MAAAA,CAAAA,KAAAA,EAAAA;;;AAEMC,CAAAA,CAAAA,CAAAA,uCAAAA,EAAAA,aAAAA,CAAcC,WAAWC,YAAY,CAAA,EAAA,4GAAA,EAU5BF,aAAAA,CAAcC,UAAAA,CAAWC,YAAY,CAAA,EAAA,+lBAAA,EAuDhDF,aAAAA,CAAcC,UAAAA,CAAWE,YAAY,SAGrD,CAACC,KAAAA,GACCA,KAAAA,CAAMC,SAAS,GACT;sBACQ,EAAEL,aAAAA,CAAcC,UAAAA,CAAWK,KAAK,CAAA,CAAE;;;0BAG9B,EAAEN,aAAAA,CAAcC,UAAAA,CAAWK,KAAK,CAAA,CAAE;;AAExD,IAAA,CAAC,GACS,EAAA,CAAA;AAGd,MAAMC,cAAAA,iBAAiBR,MAAAA,CAAAA,KAAAA,EAAAA;;;AACVC,CAAAA,CAAAA,CAAAA,QAAAA,EAAAA,aAAAA,CAAcC,WAAWK,KAAK,CAAA,EAAA,kCAAA,CAAA;AAU3C;;;;AAIC,IACD,SAASE,cAAAA,CACLJ,KAA0C,EAC1CK,GAA8B,EAAA;AAE9B,IAAA,MAAMC,OAAAA,GAAUC,KAAAA,EAAAA;IAEhB,qBACIC,IAAA,CAAAC,QAAA,EAAA;;0BACIC,GAAA,CAAChB,WAAAA,EAAAA;AACI,gBAAA,GAAGM,KAAK;gBACTK,GAAAA,EAAKA,GAAAA;gBACLM,kBAAAA,EAAkBX,KAAAA,CAAMC,SAAS,GAAGK,OAAAA,GAAUM,SAAAA;AAE7CZ,gBAAAA,QAAAA,EAAAA,KAAAA,CAAMa;;YAEVb,KAAAA,CAAMC,SAAS,kBAAIS,GAAA,CAACP,cAAAA,EAAAA;gBAAeW,EAAAA,EAAIR,OAAAA;AAAUN,gBAAAA,QAAAA,EAAAA,KAAAA,CAAMC;;;;AAGpE;AAEA,MAAMc,KAAAA,iBAAQC,KAAAA,CAAMC,UAAU,CAA6Bb,cAAAA;;;;"}
|
|
@@ -10,5 +10,7 @@ declare const Checkbox: React.ForwardRefExoticComponent<{
|
|
|
10
10
|
* @default false
|
|
11
11
|
*/
|
|
12
12
|
indeterminate?: boolean;
|
|
13
|
+
/** Error text to be shown below the field */
|
|
14
|
+
errorText?: string;
|
|
13
15
|
} & React.InputHTMLAttributes<HTMLInputElement> & React.RefAttributes<HTMLInputElement>>;
|
|
14
16
|
export default Checkbox;
|
|
@@ -1,53 +1,84 @@
|
|
|
1
1
|
import { jsxs, jsx } from '@emotion/react/jsx-runtime';
|
|
2
|
-
import React, { useCallback } from 'react';
|
|
2
|
+
import React, { useRef, useId, useImperativeHandle, useEffect, useCallback } from 'react';
|
|
3
3
|
import styled from '@emotion/styled';
|
|
4
4
|
import { getThemeValue, THEME_NAME } from '../../shared/constants.js';
|
|
5
5
|
|
|
6
6
|
const Label$2 = /*#__PURE__*/ styled("label", {
|
|
7
|
-
target: "
|
|
7
|
+
target: "e74kvzt0",
|
|
8
8
|
label: "Label"
|
|
9
9
|
})("margin:5px 0;position:relative;display:inline-flex;align-items:center;cursor:pointer;");
|
|
10
10
|
const StyledCheckmark = /*#__PURE__*/ styled("span", {
|
|
11
|
-
target: "
|
|
11
|
+
target: "e74kvzt1",
|
|
12
12
|
label: "StyledCheckmark"
|
|
13
13
|
})("width:16px;height:16px;border:1px solid ", getThemeValue(THEME_NAME.BORDER_COLOR), ";display:inline-block;border-radius:3px;margin-right:5px;background-color:", getThemeValue(THEME_NAME.BACKGROUND), ";transition:all 0.3s ease;position:relative;flex-shrink:0;&::after{content:'';width:3px;height:10px;border-right:2px solid ", getThemeValue(THEME_NAME.TEXT_COLOR_LIGHT), ";border-bottom:2px solid ", getThemeValue(THEME_NAME.TEXT_COLOR_LIGHT), ";position:absolute;top:1px;left:6px;opacity:0;transform:rotate(45deg) scale(0);transition:all 0.2s ease;}");
|
|
14
14
|
const HiddenInput$1 = /*#__PURE__*/ styled("input", {
|
|
15
|
-
target: "
|
|
15
|
+
target: "e74kvzt2",
|
|
16
16
|
label: "HiddenInput"
|
|
17
17
|
})("opacity:0;width:0;height:0;position:absolute;margin:0;&:checked + ", StyledCheckmark, "{background-color:", getThemeValue(THEME_NAME.PRIMARY), ";border-color:", getThemeValue(THEME_NAME.PRIMARY), ";}&:checked + ", StyledCheckmark, "::after{opacity:1;transform:rotate(45deg) scale(1);}&:indeterminate + ", StyledCheckmark, "{background-color:", getThemeValue(THEME_NAME.PRIMARY), ";border-color:", getThemeValue(THEME_NAME.PRIMARY), ";}&:indeterminate + ", StyledCheckmark, "::after{opacity:1;height:0;width:8px;border-right:none;border-bottom:2px solid ", getThemeValue(THEME_NAME.TEXT_COLOR_LIGHT), ";transform:rotate(0deg) scale(1);top:7px;left:4px;}&:enabled:active + ", StyledCheckmark, ",&:focus + ", StyledCheckmark, "{border-color:", getThemeValue(THEME_NAME.PRIMARY), ";box-shadow:0 0 0 3px ", getThemeValue(THEME_NAME.PRIMARY_LIGHT), ";}&:enabled:active ~ span,&:focus ~ span{color:", getThemeValue(THEME_NAME.PRIMARY), ";}&:enabled:hover + ", StyledCheckmark, "{border-color:", getThemeValue(THEME_NAME.PRIMARY), ";}&:enabled:hover ~ span{color:", getThemeValue(THEME_NAME.PRIMARY), ";}&:disabled + ", StyledCheckmark, "{border-color:", getThemeValue(THEME_NAME.DISABLED_BORDER), ";cursor:not-allowed;}&:disabled ~ span{color:", getThemeValue(THEME_NAME.DISABLED), ";cursor:not-allowed;}&:checked:disabled + ", StyledCheckmark, ",&:indeterminate:disabled + ", StyledCheckmark, "{background-color:", getThemeValue(THEME_NAME.DISABLED), ";}");
|
|
18
|
+
const ErrorContainer$2 = /*#__PURE__*/ styled("div", {
|
|
19
|
+
target: "e74kvzt3",
|
|
20
|
+
label: "ErrorContainer"
|
|
21
|
+
})("color:", getThemeValue(THEME_NAME.ERROR), ";padding-top:3px;font-size:12px;line-height:14px;");
|
|
22
|
+
const Container$6 = /*#__PURE__*/ styled("div", {
|
|
23
|
+
target: "e74kvzt4",
|
|
24
|
+
label: "Container"
|
|
25
|
+
})("display:inline-flex;flex-direction:column;");
|
|
18
26
|
/**
|
|
19
27
|
* Checkbox Component
|
|
20
28
|
* @param props - Component props
|
|
21
29
|
* @param fwdRef - Ref forwarded to the underlying HTMLInputElement
|
|
22
30
|
*/ function CheckboxComponent(props, fwdRef) {
|
|
23
|
-
const { label = '', indeterminate = false, checked, ...rest } = props;
|
|
31
|
+
const { label = '', indeterminate = false, checked, errorText, ...rest } = props;
|
|
32
|
+
const internalRef = useRef(null);
|
|
33
|
+
const errorId = useId();
|
|
34
|
+
useImperativeHandle(fwdRef, ()=>internalRef.current);
|
|
35
|
+
useEffect(()=>{
|
|
36
|
+
if (internalRef.current) {
|
|
37
|
+
internalRef.current.setCustomValidity(errorText || '');
|
|
38
|
+
}
|
|
39
|
+
}, [
|
|
40
|
+
errorText
|
|
41
|
+
]);
|
|
24
42
|
const ref = useCallback((node)=>{
|
|
43
|
+
internalRef.current = node;
|
|
25
44
|
// Ensure the DOM `indeterminate` flag always matches the prop
|
|
26
45
|
if (node) {
|
|
27
46
|
node.indeterminate = !!indeterminate;
|
|
28
47
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
48
|
+
// Forward the node (or null) to the parent ref (supports function or ref object)
|
|
49
|
+
// Note: Since we use useImperativeHandle now, we don't technically need to manually forward here
|
|
50
|
+
// if we weren't doing the ref callback pattern for indeterminate.
|
|
51
|
+
// However, useImperativeHandle handles the forwarding.
|
|
52
|
+
// BUT, our local ref callback `ref` is passed to the input `ref={ref}`.
|
|
53
|
+
// React will call this callback with the node.
|
|
54
|
+
// Inside this callback, we set internalRef.current.
|
|
55
|
+
// useImperativeHandle exposes internalRef.current to fwdRef.
|
|
56
|
+
// So we don't need manual forwarding logic here anymore!
|
|
35
57
|
}, [
|
|
36
|
-
indeterminate
|
|
37
|
-
fwdRef
|
|
58
|
+
indeterminate
|
|
38
59
|
]);
|
|
39
|
-
return /*#__PURE__*/ jsxs(
|
|
60
|
+
return /*#__PURE__*/ jsxs(Container$6, {
|
|
40
61
|
children: [
|
|
41
|
-
/*#__PURE__*/
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
62
|
+
/*#__PURE__*/ jsxs(Label$2, {
|
|
63
|
+
children: [
|
|
64
|
+
/*#__PURE__*/ jsx(HiddenInput$1, {
|
|
65
|
+
...rest,
|
|
66
|
+
ref: ref,
|
|
67
|
+
type: "checkbox",
|
|
68
|
+
checked: checked,
|
|
69
|
+
"aria-checked": indeterminate ? 'mixed' : checked,
|
|
70
|
+
"aria-invalid": !!errorText,
|
|
71
|
+
"aria-describedby": errorText ? errorId : undefined
|
|
72
|
+
}),
|
|
73
|
+
/*#__PURE__*/ jsx(StyledCheckmark, {}),
|
|
74
|
+
/*#__PURE__*/ jsx("span", {
|
|
75
|
+
children: label
|
|
76
|
+
})
|
|
77
|
+
]
|
|
47
78
|
}),
|
|
48
|
-
/*#__PURE__*/ jsx(
|
|
49
|
-
|
|
50
|
-
children:
|
|
79
|
+
errorText && /*#__PURE__*/ jsx(ErrorContainer$2, {
|
|
80
|
+
id: errorId,
|
|
81
|
+
children: errorText
|
|
51
82
|
})
|
|
52
83
|
]
|
|
53
84
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkbox.js","sources":["../../../src/components/Input/Checkbox.tsx"],"sourcesContent":["import React, { useCallback } from 'react';\nimport styled from '@emotion/styled';\nimport { getThemeValue, THEME_NAME } from '../../shared/constants';\n\nconst Label = styled.label`\n margin: 5px 0;\n position: relative;\n display: inline-flex;\n align-items: center;\n cursor: pointer;\n`;\n\nconst StyledCheckmark = styled.span`\n width: 16px;\n height: 16px;\n border: 1px solid ${getThemeValue(THEME_NAME.BORDER_COLOR)};\n display: inline-block;\n border-radius: 3px;\n margin-right: 5px;\n background-color: ${getThemeValue(THEME_NAME.BACKGROUND)};\n transition: all 0.3s ease;\n position: relative;\n flex-shrink: 0;\n\n &::after {\n content: '';\n width: 3px;\n height: 10px;\n border-right: 2px solid ${getThemeValue(THEME_NAME.TEXT_COLOR_LIGHT)};\n border-bottom: 2px solid ${getThemeValue(THEME_NAME.TEXT_COLOR_LIGHT)};\n position: absolute;\n top: 1px;\n left: 6px;\n opacity: 0;\n transform: rotate(45deg) scale(0);\n transition: all 0.2s ease;\n }\n`;\n\nconst HiddenInput = styled.input`\n opacity: 0;\n width: 0;\n height: 0;\n position: absolute;\n margin: 0;\n\n /** checked */\n &:checked + ${StyledCheckmark} {\n background-color: ${getThemeValue(THEME_NAME.PRIMARY)};\n border-color: ${getThemeValue(THEME_NAME.PRIMARY)};\n }\n\n &:checked + ${StyledCheckmark}::after {\n opacity: 1;\n transform: rotate(45deg) scale(1);\n }\n\n /** indeterminate */\n &:indeterminate + ${StyledCheckmark} {\n background-color: ${getThemeValue(THEME_NAME.PRIMARY)};\n border-color: ${getThemeValue(THEME_NAME.PRIMARY)};\n }\n\n &:indeterminate + ${StyledCheckmark}::after {\n opacity: 1;\n height: 0;\n width: 8px;\n border-right: none;\n border-bottom: 2px solid ${getThemeValue(THEME_NAME.TEXT_COLOR_LIGHT)};\n transform: rotate(0deg) scale(1);\n top: 7px;\n left: 4px;\n }\n\n /** active and focus */\n &:enabled:active + ${StyledCheckmark}, &:focus + ${StyledCheckmark} {\n border-color: ${getThemeValue(THEME_NAME.PRIMARY)};\n box-shadow: 0 0 0 3px ${getThemeValue(THEME_NAME.PRIMARY_LIGHT)};\n }\n\n &:enabled:active ~ span,\n &:focus ~ span {\n color: ${getThemeValue(THEME_NAME.PRIMARY)};\n }\n\n /** hover */\n &:enabled:hover + ${StyledCheckmark} {\n border-color: ${getThemeValue(THEME_NAME.PRIMARY)};\n }\n\n &:enabled:hover ~ span {\n color: ${getThemeValue(THEME_NAME.PRIMARY)};\n }\n\n /** disabled */\n &:disabled + ${StyledCheckmark} {\n border-color: ${getThemeValue(THEME_NAME.DISABLED_BORDER)};\n cursor: not-allowed;\n }\n\n &:disabled ~ span {\n color: ${getThemeValue(THEME_NAME.DISABLED)};\n cursor: not-allowed;\n }\n\n &:checked:disabled + ${StyledCheckmark}, &:indeterminate:disabled + ${StyledCheckmark} {\n background-color: ${getThemeValue(THEME_NAME.DISABLED)};\n }\n`;\n\ntype CheckboxProps = {\n /**\n * Label for the field\n * @default ''\n */\n label?: string;\n /**\n * If the field is in indeterminate state\n * @default false\n */\n indeterminate?: boolean;\n} & React.InputHTMLAttributes<HTMLInputElement>;\n\n/**\n * Checkbox Component\n * @param props - Component props\n * @param fwdRef - Ref forwarded to the underlying HTMLInputElement\n */\nfunction CheckboxComponent(props: CheckboxProps, fwdRef: React.Ref<HTMLInputElement>) {\n const { label = '', indeterminate = false, checked, ...rest } = props;\n\n const ref = useCallback(\n (node: HTMLInputElement | null) => {\n // Ensure the DOM `indeterminate` flag always matches the prop\n if (node) {\n node.indeterminate = !!indeterminate;\n }\n\n // Forward the node (or null) to the parent ref (supports function or ref object)\n
|
|
1
|
+
{"version":3,"file":"Checkbox.js","sources":["../../../src/components/Input/Checkbox.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useId, useRef, useImperativeHandle } from 'react';\nimport styled from '@emotion/styled';\nimport { getThemeValue, THEME_NAME } from '../../shared/constants';\n\nconst Label = styled.label`\n margin: 5px 0;\n position: relative;\n display: inline-flex;\n align-items: center;\n cursor: pointer;\n`;\n\nconst StyledCheckmark = styled.span`\n width: 16px;\n height: 16px;\n border: 1px solid ${getThemeValue(THEME_NAME.BORDER_COLOR)};\n display: inline-block;\n border-radius: 3px;\n margin-right: 5px;\n background-color: ${getThemeValue(THEME_NAME.BACKGROUND)};\n transition: all 0.3s ease;\n position: relative;\n flex-shrink: 0;\n\n &::after {\n content: '';\n width: 3px;\n height: 10px;\n border-right: 2px solid ${getThemeValue(THEME_NAME.TEXT_COLOR_LIGHT)};\n border-bottom: 2px solid ${getThemeValue(THEME_NAME.TEXT_COLOR_LIGHT)};\n position: absolute;\n top: 1px;\n left: 6px;\n opacity: 0;\n transform: rotate(45deg) scale(0);\n transition: all 0.2s ease;\n }\n`;\n\nconst HiddenInput = styled.input`\n opacity: 0;\n width: 0;\n height: 0;\n position: absolute;\n margin: 0;\n\n /** checked */\n &:checked + ${StyledCheckmark} {\n background-color: ${getThemeValue(THEME_NAME.PRIMARY)};\n border-color: ${getThemeValue(THEME_NAME.PRIMARY)};\n }\n\n &:checked + ${StyledCheckmark}::after {\n opacity: 1;\n transform: rotate(45deg) scale(1);\n }\n\n /** indeterminate */\n &:indeterminate + ${StyledCheckmark} {\n background-color: ${getThemeValue(THEME_NAME.PRIMARY)};\n border-color: ${getThemeValue(THEME_NAME.PRIMARY)};\n }\n\n &:indeterminate + ${StyledCheckmark}::after {\n opacity: 1;\n height: 0;\n width: 8px;\n border-right: none;\n border-bottom: 2px solid ${getThemeValue(THEME_NAME.TEXT_COLOR_LIGHT)};\n transform: rotate(0deg) scale(1);\n top: 7px;\n left: 4px;\n }\n\n /** active and focus */\n &:enabled:active + ${StyledCheckmark}, &:focus + ${StyledCheckmark} {\n border-color: ${getThemeValue(THEME_NAME.PRIMARY)};\n box-shadow: 0 0 0 3px ${getThemeValue(THEME_NAME.PRIMARY_LIGHT)};\n }\n\n &:enabled:active ~ span,\n &:focus ~ span {\n color: ${getThemeValue(THEME_NAME.PRIMARY)};\n }\n\n /** hover */\n &:enabled:hover + ${StyledCheckmark} {\n border-color: ${getThemeValue(THEME_NAME.PRIMARY)};\n }\n\n &:enabled:hover ~ span {\n color: ${getThemeValue(THEME_NAME.PRIMARY)};\n }\n\n /** disabled */\n &:disabled + ${StyledCheckmark} {\n border-color: ${getThemeValue(THEME_NAME.DISABLED_BORDER)};\n cursor: not-allowed;\n }\n\n &:disabled ~ span {\n color: ${getThemeValue(THEME_NAME.DISABLED)};\n cursor: not-allowed;\n }\n\n &:checked:disabled + ${StyledCheckmark}, &:indeterminate:disabled + ${StyledCheckmark} {\n background-color: ${getThemeValue(THEME_NAME.DISABLED)};\n }\n`;\n\nconst ErrorContainer = styled.div`\n color: ${getThemeValue(THEME_NAME.ERROR)};\n padding-top: 3px;\n font-size: 12px;\n line-height: 14px;\n`;\n\nconst Container = styled.div`\n display: inline-flex;\n flex-direction: column;\n`;\n\ntype CheckboxProps = {\n /**\n * Label for the field\n * @default ''\n */\n label?: string;\n /**\n * If the field is in indeterminate state\n * @default false\n */\n indeterminate?: boolean;\n /** Error text to be shown below the field */\n errorText?: string;\n} & React.InputHTMLAttributes<HTMLInputElement>;\n\n/**\n * Checkbox Component\n * @param props - Component props\n * @param fwdRef - Ref forwarded to the underlying HTMLInputElement\n */\nfunction CheckboxComponent(props: CheckboxProps, fwdRef: React.Ref<HTMLInputElement>) {\n const { label = '', indeterminate = false, checked, errorText, ...rest } = props;\n const internalRef = useRef<HTMLInputElement | null>(null);\n const errorId = useId();\n\n useImperativeHandle(fwdRef, () => internalRef.current as HTMLInputElement);\n\n useEffect(() => {\n if (internalRef.current) {\n internalRef.current.setCustomValidity(errorText || '');\n }\n }, [errorText]);\n\n const ref = useCallback(\n (node: HTMLInputElement | null) => {\n internalRef.current = node;\n // Ensure the DOM `indeterminate` flag always matches the prop\n if (node) {\n node.indeterminate = !!indeterminate;\n }\n\n // Forward the node (or null) to the parent ref (supports function or ref object)\n // Note: Since we use useImperativeHandle now, we don't technically need to manually forward here\n // if we weren't doing the ref callback pattern for indeterminate.\n // However, useImperativeHandle handles the forwarding.\n // BUT, our local ref callback `ref` is passed to the input `ref={ref}`.\n // React will call this callback with the node.\n // Inside this callback, we set internalRef.current.\n // useImperativeHandle exposes internalRef.current to fwdRef.\n // So we don't need manual forwarding logic here anymore!\n },\n [indeterminate],\n );\n\n return (\n <Container>\n <Label>\n <HiddenInput\n {...rest}\n ref={ref}\n type=\"checkbox\"\n checked={checked}\n aria-checked={indeterminate ? 'mixed' : checked}\n aria-invalid={!!errorText}\n aria-describedby={errorText ? errorId : undefined}\n />\n <StyledCheckmark />\n <span>{label}</span>\n </Label>\n {errorText && <ErrorContainer id={errorId}>{errorText}</ErrorContainer>}\n </Container>\n );\n}\n\nconst Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(CheckboxComponent);\n\nexport default Checkbox;\n"],"names":["Label","styled","StyledCheckmark","getThemeValue","THEME_NAME","BORDER_COLOR","BACKGROUND","TEXT_COLOR_LIGHT","HiddenInput","PRIMARY","PRIMARY_LIGHT","DISABLED_BORDER","DISABLED","ErrorContainer","ERROR","Container","CheckboxComponent","props","fwdRef","label","indeterminate","checked","errorText","rest","internalRef","useRef","errorId","useId","useImperativeHandle","current","useEffect","setCustomValidity","ref","useCallback","node","_jsxs","_jsx","type","aria-checked","aria-invalid","aria-describedby","undefined","span","id","Checkbox","React","forwardRef"],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { MenuItemProps } from '../Menu/MenuItem';
|
|
2
3
|
type DropdownProps<T> = React.PropsWithChildren<{
|
|
3
4
|
/** Value of the control */
|
|
4
5
|
value?: T | T[];
|
|
@@ -17,6 +18,7 @@ type DropdownProps<T> = React.PropsWithChildren<{
|
|
|
17
18
|
required?: boolean;
|
|
18
19
|
/** Disables the field */
|
|
19
20
|
disabled?: boolean;
|
|
21
|
+
children?: React.ReactElement<MenuItemProps<T>> | React.ReactElement<MenuItemProps<T>>[];
|
|
20
22
|
}> & Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value'>;
|
|
21
23
|
declare const Dropdown: <T>(props: DropdownProps<T> & React.RefAttributes<HTMLInputElement>) => React.ReactElement | null;
|
|
22
24
|
export default Dropdown;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from '@emotion/react/jsx-runtime';
|
|
2
|
-
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import React, { useState, useRef, useEffect } from 'react';
|
|
3
3
|
import styled from '@emotion/styled';
|
|
4
4
|
import Input from './Input.js';
|
|
5
5
|
import Popover, { POPOVER_POSITION } from '../Popover/Popover.js';
|
|
@@ -7,9 +7,63 @@ import Menu from '../Menu/Menu.js';
|
|
|
7
7
|
import ExpandMore from '../../icons/ExpandMore.js';
|
|
8
8
|
|
|
9
9
|
const ArrowContainer = /*#__PURE__*/ styled("span", {
|
|
10
|
-
target: "
|
|
10
|
+
target: "eph6dat0",
|
|
11
11
|
label: "ArrowContainer"
|
|
12
12
|
})("position:absolute;right:12px;top:16px;pointer-events:none;");
|
|
13
|
+
/**
|
|
14
|
+
* DropdownTrigger Component
|
|
15
|
+
*/ const DropdownTrigger = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
16
|
+
const { displayValue, label, errorText, open, menuId, toggleOpen, onKeyDown, forwardedRef, ...rest } = props;
|
|
17
|
+
const triggerRef = React.useRef(null);
|
|
18
|
+
// Helper to assign both internal triggerRef and external forwarded ref
|
|
19
|
+
const assignRefs = React.useCallback((node)=>{
|
|
20
|
+
triggerRef.current = node;
|
|
21
|
+
if (!forwardedRef) return;
|
|
22
|
+
if (typeof forwardedRef === 'function') {
|
|
23
|
+
forwardedRef(node);
|
|
24
|
+
} else {
|
|
25
|
+
forwardedRef.current = node;
|
|
26
|
+
}
|
|
27
|
+
}, [
|
|
28
|
+
forwardedRef
|
|
29
|
+
]);
|
|
30
|
+
// Combine the ref passed by parent with our assignRefs so both are updated
|
|
31
|
+
const combinedRef = React.useCallback((node)=>{
|
|
32
|
+
assignRefs(node);
|
|
33
|
+
if (typeof ref === 'function') {
|
|
34
|
+
ref(node);
|
|
35
|
+
} else if (ref) {
|
|
36
|
+
ref.current = node;
|
|
37
|
+
}
|
|
38
|
+
}, [
|
|
39
|
+
assignRefs,
|
|
40
|
+
ref
|
|
41
|
+
]);
|
|
42
|
+
return /*#__PURE__*/ jsxs(Fragment, {
|
|
43
|
+
children: [
|
|
44
|
+
/*#__PURE__*/ jsx(Input, {
|
|
45
|
+
...rest,
|
|
46
|
+
ref: combinedRef,
|
|
47
|
+
type: "text",
|
|
48
|
+
value: displayValue,
|
|
49
|
+
label: label,
|
|
50
|
+
errorText: errorText,
|
|
51
|
+
onClick: toggleOpen,
|
|
52
|
+
onKeyDown: onKeyDown,
|
|
53
|
+
inputMode: "none",
|
|
54
|
+
role: "combobox",
|
|
55
|
+
"aria-haspopup": "listbox",
|
|
56
|
+
"aria-expanded": open,
|
|
57
|
+
"aria-controls": menuId
|
|
58
|
+
}),
|
|
59
|
+
/*#__PURE__*/ jsx(ArrowContainer, {
|
|
60
|
+
"aria-hidden": "true",
|
|
61
|
+
children: /*#__PURE__*/ jsx(ExpandMore, {})
|
|
62
|
+
})
|
|
63
|
+
]
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
DropdownTrigger.displayName = 'DropdownTrigger';
|
|
13
67
|
/**
|
|
14
68
|
* Dropdown component that allows selection from a list of options.
|
|
15
69
|
* Supports single and multi-select modes.
|
|
@@ -17,11 +71,6 @@ const ArrowContainer = /*#__PURE__*/ styled("span", {
|
|
|
17
71
|
* @template T - The type of the value(s) in the dropdown.
|
|
18
72
|
* @param props - The properties for the Dropdown component.
|
|
19
73
|
* @returns The rendered Dropdown component.
|
|
20
|
-
*/ /**
|
|
21
|
-
* Dropdown Component
|
|
22
|
-
* @template T - The type of value(s) in the dropdown.
|
|
23
|
-
* @param props - Component props
|
|
24
|
-
* @param outerRef - Ref forwarded to the underlying HTMLInputElement
|
|
25
74
|
*/ function DropdownComponent(props, outerRef) {
|
|
26
75
|
const { multiSelect = false, onChange, children, value: propValue, label, errorText, required, disabled, ...rest } = props;
|
|
27
76
|
const [open, setOpen] = useState(false);
|
|
@@ -30,6 +79,42 @@ const ArrowContainer = /*#__PURE__*/ styled("span", {
|
|
|
30
79
|
const menuId = `${id}-menu`;
|
|
31
80
|
const menuRef = React.useRef(null);
|
|
32
81
|
const triggerRef = React.useRef(null);
|
|
82
|
+
/**
|
|
83
|
+
* Gets the display value for the dropdown based on the current value and children.
|
|
84
|
+
*
|
|
85
|
+
* @param currentValue - The current value of the dropdown.
|
|
86
|
+
* @param currentChildren - The children of the dropdown.
|
|
87
|
+
* @returns The display value.
|
|
88
|
+
*/ const getDisplayValue = (currentValue, currentChildren)=>{
|
|
89
|
+
if (currentValue === undefined || currentValue === null) return '';
|
|
90
|
+
const findLabel = (val)=>{
|
|
91
|
+
let label = '';
|
|
92
|
+
React.Children.forEach(currentChildren, (child)=>{
|
|
93
|
+
if (/*#__PURE__*/ React.isValidElement(child)) {
|
|
94
|
+
const props = child.props;
|
|
95
|
+
if ('value' in props && props.value === val) {
|
|
96
|
+
label = String(props.children);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
return label;
|
|
101
|
+
};
|
|
102
|
+
if (Array.isArray(currentValue)) {
|
|
103
|
+
return currentValue.map(findLabel).filter(Boolean).join(', ');
|
|
104
|
+
}
|
|
105
|
+
return findLabel(currentValue);
|
|
106
|
+
};
|
|
107
|
+
const displayValue = getDisplayValue(value, children) || (value ? String(value) : '');
|
|
108
|
+
// Sync prop value with state
|
|
109
|
+
const prevValueRef = useRef(undefined);
|
|
110
|
+
useEffect(()=>{
|
|
111
|
+
if (propValue !== prevValueRef.current) {
|
|
112
|
+
setValue(propValue);
|
|
113
|
+
prevValueRef.current = propValue;
|
|
114
|
+
}
|
|
115
|
+
}, [
|
|
116
|
+
propValue
|
|
117
|
+
]);
|
|
33
118
|
// Focus menu when opened
|
|
34
119
|
useEffect(()=>{
|
|
35
120
|
if (open) {
|
|
@@ -51,7 +136,7 @@ const ArrowContainer = /*#__PURE__*/ styled("span", {
|
|
|
51
136
|
* Opens the menu on 'Enter', 'Space', 'ArrowDown', or 'ArrowUp'.
|
|
52
137
|
*
|
|
53
138
|
* @param {React.KeyboardEvent<HTMLInputElement>} e - The keyboard event.
|
|
54
|
-
*/ const onKeyDown = (e)=>{
|
|
139
|
+
*/ const onKeyDown = React.useCallback((e)=>{
|
|
55
140
|
if ([
|
|
56
141
|
'ArrowDown',
|
|
57
142
|
'ArrowUp',
|
|
@@ -60,8 +145,11 @@ const ArrowContainer = /*#__PURE__*/ styled("span", {
|
|
|
60
145
|
].includes(e.key)) {
|
|
61
146
|
e.preventDefault();
|
|
62
147
|
setOpen(true);
|
|
148
|
+
} else if (e.key !== 'Tab') {
|
|
149
|
+
// Prevent typing to mimic readOnly behavior while allowing native validation
|
|
150
|
+
e.preventDefault();
|
|
63
151
|
}
|
|
64
|
-
};
|
|
152
|
+
}, []);
|
|
65
153
|
/**
|
|
66
154
|
* Handles changes to the dropdown value.
|
|
67
155
|
* Updates local state and calls the external onChange handler.
|
|
@@ -79,59 +167,35 @@ const ArrowContainer = /*#__PURE__*/ styled("span", {
|
|
|
79
167
|
};
|
|
80
168
|
/**
|
|
81
169
|
* Toggles the dropdown open state on click.
|
|
82
|
-
*/ const clickHandler = ()=>setOpen(true);
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const combinedRef = (node)=>{
|
|
96
|
-
assignRefs(node);
|
|
97
|
-
if (typeof ref === 'function') {
|
|
98
|
-
ref(node);
|
|
99
|
-
} else if (ref) {
|
|
100
|
-
ref.current = node;
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
return /*#__PURE__*/ jsxs(Fragment, {
|
|
104
|
-
children: [
|
|
105
|
-
/*#__PURE__*/ jsx(Input, {
|
|
106
|
-
...rest,
|
|
107
|
-
...passedProps,
|
|
108
|
-
ref: combinedRef,
|
|
109
|
-
type: "text",
|
|
110
|
-
value: value && String(value),
|
|
111
|
-
label: label,
|
|
112
|
-
errorText: errorText,
|
|
113
|
-
onClick: clickHandler,
|
|
114
|
-
onKeyDown: onKeyDown,
|
|
115
|
-
required: required,
|
|
116
|
-
disabled: disabled,
|
|
117
|
-
readOnly: true,
|
|
118
|
-
role: "combobox",
|
|
119
|
-
"aria-haspopup": "listbox",
|
|
120
|
-
"aria-expanded": open,
|
|
121
|
-
"aria-controls": menuId
|
|
122
|
-
}),
|
|
123
|
-
/*#__PURE__*/ jsx(ArrowContainer, {
|
|
124
|
-
"aria-hidden": "true",
|
|
125
|
-
children: /*#__PURE__*/ jsx(ExpandMore, {})
|
|
126
|
-
})
|
|
127
|
-
]
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
TriggerElement.displayName = 'DropdownTrigger';
|
|
170
|
+
*/ const clickHandler = React.useCallback(()=>setOpen(true), []);
|
|
171
|
+
/**
|
|
172
|
+
* Forwarded ref handler for the trigger input.
|
|
173
|
+
*/ const handleForwardedRef = React.useCallback((node)=>{
|
|
174
|
+
triggerRef.current = node;
|
|
175
|
+
if (typeof outerRef === 'function') {
|
|
176
|
+
outerRef(node);
|
|
177
|
+
} else if (outerRef) {
|
|
178
|
+
outerRef.current = node;
|
|
179
|
+
}
|
|
180
|
+
}, [
|
|
181
|
+
outerRef
|
|
182
|
+
]);
|
|
131
183
|
return /*#__PURE__*/ jsx(Popover, {
|
|
132
184
|
position: POPOVER_POSITION.BOTTOM_LEFT,
|
|
133
185
|
open: open,
|
|
134
|
-
element:
|
|
186
|
+
element: /*#__PURE__*/ jsx(DropdownTrigger, {
|
|
187
|
+
...rest,
|
|
188
|
+
displayValue: displayValue,
|
|
189
|
+
label: label,
|
|
190
|
+
errorText: errorText,
|
|
191
|
+
open: open,
|
|
192
|
+
menuId: menuId,
|
|
193
|
+
toggleOpen: clickHandler,
|
|
194
|
+
onKeyDown: onKeyDown,
|
|
195
|
+
required: required,
|
|
196
|
+
disabled: disabled,
|
|
197
|
+
forwardedRef: handleForwardedRef
|
|
198
|
+
}),
|
|
135
199
|
onClose: ()=>{
|
|
136
200
|
setOpen(false);
|
|
137
201
|
triggerRef.current?.focus();
|