@ahrowe/ui 0.32.0 → 0.33.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/esm/common/card/Card.mjs +1 -1
- package/dist/esm/common/card/Card.mjs.map +1 -1
- package/dist/esm/common/card/card.types.mjs.map +1 -1
- package/dist/esm/common/chip/chip.mjs +1 -1
- package/dist/esm/common/chip/chip.mjs.map +1 -1
- package/dist/esm/common/chip/chip.types.mjs.map +1 -1
- package/dist/esm/common/colorPicker/colorPicker.mjs +1 -1
- package/dist/esm/common/colorPicker/colorPicker.mjs.map +1 -1
- package/dist/esm/common/confirmModal/confirmModal.mjs +1 -1
- package/dist/esm/common/confirmModal/confirmModal.mjs.map +1 -1
- package/dist/esm/common/datePicker/components/datePickerMonth/datePickerMonth.mjs +1 -1
- package/dist/esm/common/datePicker/components/datePickerMonth/datePickerMonth.mjs.map +1 -1
- package/dist/esm/common/datePicker/datePicker.mjs +1 -1
- package/dist/esm/common/datePicker/datePicker.mjs.map +1 -1
- package/dist/esm/common/drawer/drawer.mjs +1 -1
- package/dist/esm/common/drawer/drawer.mjs.map +1 -1
- package/dist/esm/common/dropdown/dropdown.mjs +1 -1
- package/dist/esm/common/dropdown/dropdown.mjs.map +1 -1
- package/dist/esm/common/emptyState/emptyState.mjs +1 -1
- package/dist/esm/common/emptyState/emptyState.mjs.map +1 -1
- package/dist/esm/common/fab/components/fabBase/fabBase.mjs +1 -1
- package/dist/esm/common/fab/components/fabBase/fabBase.mjs.map +1 -1
- package/dist/esm/common/fab/fab.mjs +1 -1
- package/dist/esm/common/fab/fab.mjs.map +1 -1
- package/dist/esm/common/infiniteBlock/infiniteBlock.mjs +1 -1
- package/dist/esm/common/infiniteBlock/infiniteBlock.mjs.map +1 -1
- package/dist/esm/common/numberInput/numberInput.mjs +1 -1
- package/dist/esm/common/numberInput/numberInput.mjs.map +1 -1
- package/dist/esm/common/progressBar/progressBar.mjs +1 -1
- package/dist/esm/common/progressBar/progressBar.mjs.map +1 -1
- package/dist/esm/common/searchInput/searchInput.mjs +1 -1
- package/dist/esm/common/searchInput/searchInput.mjs.map +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/types/common/card/card.types.d.ts +6 -1
- package/dist/types/common/chip/chip.types.d.ts +6 -1
- package/dist/types/common/colorPicker/colorPicker.d.ts +1 -1
- package/dist/types/common/colorPicker/colorPicker.types.d.ts +6 -1
- package/dist/types/common/configProvider/configProvider.types.d.ts +4 -0
- package/dist/types/common/confirmModal/confirmModal.types.d.ts +4 -2
- package/dist/types/common/datePicker/components/datePickerMonth/datePickerMonth.d.ts +1 -1
- package/dist/types/common/datePicker/components/datePickerMonth/datePickerMonth.types.d.ts +3 -0
- package/dist/types/common/datePicker/datePicker.types.d.ts +11 -2
- package/dist/types/common/drawer/drawer.d.ts +1 -1
- package/dist/types/common/drawer/drawer.types.d.ts +7 -1
- package/dist/types/common/dropdown/dropdown.types.d.ts +6 -6
- package/dist/types/common/emptyState/emptyState.d.ts +1 -1
- package/dist/types/common/emptyState/emptyState.types.d.ts +6 -1
- package/dist/types/common/fab/components/fabBase/fabBase.d.ts +1 -1
- package/dist/types/common/fab/components/fabBase/fabBase.types.d.ts +3 -1
- package/dist/types/common/fab/fab.d.ts +1 -1
- package/dist/types/common/fab/fab.types.d.ts +6 -1
- package/dist/types/common/infiniteBlock/infiniteBlock.d.ts +1 -1
- package/dist/types/common/infiniteBlock/infiniteBlock.types.d.ts +2 -0
- package/dist/types/common/numberInput/numberInput.types.d.ts +6 -0
- package/dist/types/common/progressBar/progressBar.d.ts +1 -1
- package/dist/types/common/progressBar/progressBar.types.d.ts +6 -1
- package/dist/types/common/searchInput/searchInput.d.ts +1 -1
- package/dist/types/common/searchInput/searchInput.types.d.ts +13 -1
- package/docs/Card.md +2 -0
- package/docs/Chip.md +2 -0
- package/docs/ColorPicker.md +2 -0
- package/docs/DatePicker.md +4 -0
- package/docs/Drawer.md +2 -0
- package/docs/Dropdown.md +2 -0
- package/docs/EmptyState.md +2 -0
- package/docs/Fab.md +2 -0
- package/docs/NumberInput.md +4 -0
- package/docs/ProgressBar.md +2 -0
- package/docs/SearchInput.md +4 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dropdown.mjs","names":[],"sources":["../../../../package/common/dropdown/dropdown.tsx"],"sourcesContent":["import React, { useState, useEffect, useLayoutEffect, useRef } from 'react';\nimport cx from 'classnames';\nimport InfiniteBlock from 'components/common/infiniteBlock';\nimport BodyEnd from 'components/common/bodyEnd';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronDown as dropdownIcon } from '@fortawesome/free-solid-svg-icons';\nimport Downshift, { StateChangeOptions, DownshiftState } from 'downshift';\nimport Tooltip from 'components/common/tooltip';\n\nimport { useFloatingPosition } from '../floatingMenu/useFloatingPosition';\nimport { useComponentDefaults, usePresentationDefault } from '../configProvider';\nimport { useSheet } from '../hooks/useSheet';\nimport { useOverlay } from '../hooks/useOverlay';\nimport { Presentation } from '../floatingMenu/floatingMenu.types';\nimport styles from './dropdown.module.pcss';\nimport sheetStyles from '../styles/sheet.module.pcss';\nimport SheetBackdrop from '../sheetBackdrop';\nimport type { DropdownProps, DropdownItem } from './dropdown.types';\n\nfunction Dropdown(props: DropdownProps) {\n const {\n errorMessage = '',\n className = '',\n label = '',\n value = '',\n isValid = true,\n tabIndex = 0,\n isRequired = false,\n customProps = {},\n isRow = false,\n disabled = false,\n readOnly = false,\n onScroll = () => Promise.resolve(),\n onClick,\n onChange,\n onFocus: onFocusProp = null,\n onBlur: onBlurProp = null,\n onMouseEnter: onMouseEnterProp = null,\n onMouseLeave: onMouseLeaveProp = null,\n dropdownLabel: dropdownLabelProp,\n isDropdownListWider = false,\n inputClassName = '',\n dropDownIconClassName = '',\n dropDownClassName = '',\n formValidator = null,\n labelClassName = '',\n dropDownInputClassName = '',\n items = [],\n useMatLabelStyle = true,\n alwaysFloatLabel = false,\n placeholder = '',\n plainStyle = false,\n scrollToSelected = false,\n presentation: presentationProp,\n reactRef,\n ...rest\n } = useComponentDefaults('Dropdown', props);\n const [isFocused, setIsFocused] = useState(false);\n const [isHovered, setIsHovered] = useState(false);\n const [, forceUpdate] = useState(0);\n const fvRef = useRef(formValidator);\n const triggerRef = useRef<HTMLDivElement>(null);\n const anchorRef = useRef<HTMLDivElement>(null);\n const [menuIsOpen, setMenuIsOpen] = useState(false);\n\n // Explicit prop → this component's global default → the app-wide one → anchored.\n const appPresentation = usePresentationDefault();\n const presentation = presentationProp ?? appPresentation ?? Presentation.Default;\n const { isSheet, sheetStyle } = useSheet(presentation);\n\n useLayoutEffect(() => {\n const trigger = triggerRef.current;\n const menu = anchorRef.current;\n // A sheet spans the screen; only an anchored list matches the trigger's width.\n if (!menuIsOpen || isSheet || !trigger || !menu) return;\n const { width } = trigger.getBoundingClientRect();\n const { fontSize } = window.getComputedStyle(trigger);\n menu.style.width = `${width}px`;\n menu.style.fontSize = fontSize;\n }, [menuIsOpen, isSheet]);\n\n // A sheet isn't anchored to anything, so there's nothing to track, flip or clamp.\n useFloatingPosition({\n isOpen: menuIsOpen && !isSheet,\n triggerRef,\n anchorRef,\n panelRef: anchorRef,\n flipClassName: styles.dropdownInputListAnimFlip,\n });\n\n // Registered for as long as the list is open, not just as a sheet: it puts the list on the\n // overlay stack so an enclosing Modal doesn't also act on the Escape that closes it. No\n // `onEscape` of its own — Downshift already closes the list from the toggle button. Focus isn't\n // trapped: Downshift keeps focus on that button and drives the list from there.\n useOverlay({\n isActive: menuIsOpen,\n containerRef: anchorRef,\n trapFocus: false,\n lockScroll: isSheet,\n });\n\n useEffect(() => {\n fvRef.current = formValidator;\n if (!fvRef.current) return;\n fvRef.current.label = label ?? '';\n fvRef.current.validate();\n const listener = () => forceUpdate((n) => n + 1);\n fvRef.current.registerOnUpdateListener(listener);\n return () => fvRef.current?.removeOnUpdateListener(listener);\n }, [formValidator, label]);\n\n const onMouseEnterHandler = (event: React.MouseEvent) => {\n setIsHovered(true);\n onMouseEnterProp?.(event);\n };\n\n const onMouseLeaveHandler = (event: React.MouseEvent) => {\n setIsHovered(false);\n onMouseLeaveProp?.(event);\n };\n\n const onFocusHandler = (event: React.FocusEvent) => {\n setIsFocused(true);\n onFocusProp?.(event);\n };\n\n const onBlurHandler = (event: React.FocusEvent) => {\n if (fvRef.current) fvRef.current.touched = true;\n setIsFocused(false);\n onBlurProp?.(event);\n };\n\n const hasError = (): boolean => {\n return formValidator ? formValidator.touched && formValidator.hasError() : !isValid || !!errorMessage;\n };\n\n const computedIsRequired = (): boolean => {\n return !!(\n isRequired || formValidator?.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required')\n );\n };\n\n const onChangeHandler = (val: string | number): void => {\n if (formValidator) {\n formValidator.set(val);\n onChange?.(formValidator.value, customProps);\n } else {\n onChange?.(val, customProps);\n }\n };\n\n let dropdownLabel = dropdownLabelProp;\n const currentValue = formValidator ? (formValidator.value as string | number) : value;\n const hasErr = hasError();\n\n items.forEach((item: DropdownItem) => {\n if (item.key === currentValue) {\n dropdownLabel = item.label as string;\n }\n });\n\n const displayValue = dropdownLabel || currentValue;\n const isDisplayBlank = displayValue == null || (typeof displayValue === 'string' && displayValue.trim() === '');\n\n const requiredMark = computedIsRequired() ? <span className={styles.requiredMark}>*</span> : null;\n const editedLabel = label && computedIsRequired() ? `${label} *` : label;\n\n const showFocus = isFocused && !readOnly;\n\n return (\n <div\n {...rest}\n className={cx(className, styles.dropdownLabel, styles.dropdown, {\n [styles.dropdownLabelColumn]: !isRow,\n })}\n onMouseEnter={onMouseEnterHandler}\n onMouseLeave={onMouseLeaveHandler}\n >\n <Downshift\n {...(readOnly ? { isOpen: false } : {})}\n onChange={(selection: DropdownItem | null) => onChangeHandler((selection || { key: '' }).key)}\n itemToString={(item: DropdownItem | null) => (item ? String(item.key) : '')}\n selectedItem={items.find((el) => el.key === currentValue) ?? null}\n onStateChange={(changes: StateChangeOptions<DropdownItem>) => {\n if (typeof changes.isOpen === 'boolean') setMenuIsOpen(changes.isOpen);\n }}\n stateReducer={(state: DownshiftState<DropdownItem>, changes: StateChangeOptions<DropdownItem>) => {\n if (scrollToSelected && Object.prototype.hasOwnProperty.call(changes, 'isOpen')) {\n return {\n ...changes,\n highlightedIndex: changes.isOpen ? items.indexOf(state.selectedItem as DropdownItem) : null,\n };\n }\n return changes;\n }}\n >\n {({\n getItemProps,\n getMenuProps,\n getToggleButtonProps,\n closeMenu,\n isOpen,\n selectedItem,\n highlightedIndex,\n setHighlightedIndex,\n }) => (\n <div>\n <div\n ref={triggerRef}\n onFocus={onFocusHandler}\n onBlur={onBlurHandler}\n className={cx(\n styles.dropdownInput,\n {\n [styles.dropdownInputPlain]: plainStyle,\n [styles.dropdownInputReadOnly]: readOnly && !disabled,\n },\n dropDownInputClassName,\n )}\n >\n <button\n tabIndex={tabIndex}\n type='button'\n disabled={disabled}\n aria-readonly={readOnly || undefined}\n style={isDropdownListWider ? { padding: '6px', border: 'none' } : {}}\n className={cx(\n styles.dropdownInputButton,\n {\n [styles.dropdownInputButtonDisabled]: disabled,\n [styles.dropdownInputButtonReadOnly]: readOnly && !disabled,\n },\n inputClassName,\n )}\n {...getToggleButtonProps()}\n onClick={(e: React.MouseEvent<HTMLButtonElement>) => {\n getToggleButtonProps().onClick?.(e);\n onClick?.(e);\n }}\n ref={reactRef}\n >\n {/* Non-breaking space when blank (same text-node layout as a\n real value) so the button keeps a full line of height. */}\n {isDisplayBlank ? '\\u00A0' : displayValue}\n </button>\n <button\n type='button'\n tabIndex={-1}\n disabled={disabled}\n className={cx(styles.dropdownInputButtonIcon, dropDownIconClassName)}\n {...getToggleButtonProps()}\n onClick={(e: React.MouseEvent<HTMLButtonElement>) => {\n getToggleButtonProps().onClick?.(e);\n onClick?.(e);\n }}\n >\n {!disabled && !readOnly && (\n <FontAwesomeIcon\n icon={dropdownIcon}\n className={cx(styles.dropdownInputButtonIconArrow, {\n [styles.dropdownInputButtonIconArrowOpen]: isOpen,\n })}\n />\n )}\n </button>\n {!disabled && !readOnly && isOpen ? (\n <BodyEnd>\n {isSheet && <SheetBackdrop onClose={closeMenu} />}\n <div\n {...getMenuProps(\n {\n ref: (node: HTMLDivElement | null) => {\n anchorRef.current = node;\n },\n 'aria-label': label || placeholder || undefined,\n },\n { suppressRefError: true },\n )}\n style={isSheet ? sheetStyle : undefined}\n className={cx(styles.dropdownInputListAnim, {\n [sheetStyles.sheet]: isSheet,\n [styles.dropdownInputListAnimWider]: isDropdownListWider && !isSheet,\n })}\n >\n <InfiniteBlock\n onScroll={onScroll}\n className={cx(styles.dropdownInputList, isSheet && sheetStyles.sheetScroll)}\n setHighlightedIndex={setHighlightedIndex}\n >\n {items.map((item: DropdownItem, index: number) => (\n <div\n key={item.key}\n {...getItemProps({ index, item, disabled: !!item.disabled })}\n className={cx(styles.dropdownInputListItem, dropDownClassName, {\n [styles.dropdownInputListItemActive]: highlightedIndex === index,\n [styles.dropdownInputListItemSelected]: selectedItem === item,\n [styles.dropdownInputListItemDisabled]: !!item.disabled,\n })}\n >\n {item.label}\n </div>\n ))}\n </InfiniteBlock>\n </div>\n </BodyEnd>\n ) : null}\n <fieldset\n className={cx(styles.dropdownFieldset, {\n [styles.dropdownBorderPlain]: plainStyle,\n [styles.dropdownBorderFocused]: showFocus,\n [styles.dropdownBorderInvalid]: hasErr,\n [styles.dropdownBorderError]: hasErr,\n })}\n >\n <legend\n className={cx(styles.dropdownFieldsetLegend, {\n [styles.dropdownFieldsetLegendActive]:\n useMatLabelStyle && (alwaysFloatLabel || showFocus || !isDisplayBlank),\n })}\n >\n <span>{editedLabel}</span>\n </legend>\n </fieldset>\n {useMatLabelStyle && label && (\n <div\n className={cx(styles.dropdownMatLabel, {\n [styles.dropdownMatLabelFloating]: alwaysFloatLabel || showFocus || !isDisplayBlank,\n })}\n >\n {label}\n {requiredMark}\n </div>\n )}\n {useMatLabelStyle && placeholder && (\n <div\n className={cx(\n styles.dropdownPlaceholder,\n showFocus && !dropdownLabel && styles.dropdownPlaceholderShown,\n )}\n >\n {placeholder}\n </div>\n )}\n </div>\n </div>\n )}\n </Downshift>\n {label && !useMatLabelStyle && (\n <div className={cx(styles.dropdownLabelText, labelClassName)}>\n {label}\n {requiredMark}\n </div>\n )}\n <Tooltip\n variant='error'\n message={formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || ''}\n isVisible={hasErr && (isFocused || isHovered)}\n />\n </div>\n );\n}\n\nexport default Dropdown;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dropdown.mjs","names":[],"sources":["../../../../package/common/dropdown/dropdown.tsx"],"sourcesContent":["import React, { useState, useEffect, useLayoutEffect, useRef } from 'react';\nimport cx from 'classnames';\nimport InfiniteBlock from 'components/common/infiniteBlock';\nimport BodyEnd from 'components/common/bodyEnd';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronDown as dropdownIcon } from '@fortawesome/free-solid-svg-icons';\nimport Downshift, { StateChangeOptions, DownshiftState } from 'downshift';\nimport Tooltip from 'components/common/tooltip';\n\nimport { useFloatingPosition } from '../floatingMenu/useFloatingPosition';\nimport { useComponentDefaults, usePresentationDefault } from '../configProvider';\nimport { useSheet } from '../hooks/useSheet';\nimport { useOverlay } from '../hooks/useOverlay';\nimport { Presentation } from '../floatingMenu/floatingMenu.types';\nimport styles from './dropdown.module.pcss';\nimport sheetStyles from '../styles/sheet.module.pcss';\nimport SheetBackdrop from '../sheetBackdrop';\nimport type { DropdownProps, DropdownItem } from './dropdown.types';\n\nfunction Dropdown(props: DropdownProps) {\n const {\n errorMessage = '',\n className = '',\n style,\n label = '',\n value = '',\n isValid = true,\n tabIndex = 0,\n isRequired = false,\n customProps = {},\n isRow = false,\n disabled = false,\n readOnly = false,\n onScroll = () => Promise.resolve(),\n onClick,\n onChange,\n onFocus: onFocusProp = null,\n onBlur: onBlurProp = null,\n onMouseEnter: onMouseEnterProp = null,\n onMouseLeave: onMouseLeaveProp = null,\n dropdownLabel: dropdownLabelProp,\n isDropdownListWider = false,\n formValidator = null,\n classNames,\n styles: slotStyles,\n items = [],\n useMatLabelStyle = true,\n alwaysFloatLabel = false,\n placeholder = '',\n plainStyle = false,\n scrollToSelected = false,\n presentation: presentationProp,\n reactRef,\n ...rest\n } = useComponentDefaults('Dropdown', props);\n const [isFocused, setIsFocused] = useState(false);\n const [isHovered, setIsHovered] = useState(false);\n const [, forceUpdate] = useState(0);\n const fvRef = useRef(formValidator);\n const triggerRef = useRef<HTMLDivElement>(null);\n const anchorRef = useRef<HTMLDivElement>(null);\n const [menuIsOpen, setMenuIsOpen] = useState(false);\n\n // Explicit prop → this component's global default → the app-wide one → anchored.\n const appPresentation = usePresentationDefault();\n const presentation = presentationProp ?? appPresentation ?? Presentation.Default;\n const { isSheet, sheetStyle } = useSheet(presentation);\n\n useLayoutEffect(() => {\n const trigger = triggerRef.current;\n const menu = anchorRef.current;\n // A sheet spans the screen; only an anchored list matches the trigger's width.\n if (!menuIsOpen || isSheet || !trigger || !menu) return;\n const { width } = trigger.getBoundingClientRect();\n const { fontSize } = window.getComputedStyle(trigger);\n menu.style.width = `${width}px`;\n menu.style.fontSize = fontSize;\n }, [menuIsOpen, isSheet]);\n\n // A sheet isn't anchored to anything, so there's nothing to track, flip or clamp.\n useFloatingPosition({\n isOpen: menuIsOpen && !isSheet,\n triggerRef,\n anchorRef,\n panelRef: anchorRef,\n flipClassName: styles.dropdownInputListAnimFlip,\n });\n\n // Registered for as long as the list is open, not just as a sheet: it puts the list on the\n // overlay stack so an enclosing Modal doesn't also act on the Escape that closes it. No\n // `onEscape` of its own — Downshift already closes the list from the toggle button. Focus isn't\n // trapped: Downshift keeps focus on that button and drives the list from there.\n useOverlay({\n isActive: menuIsOpen,\n containerRef: anchorRef,\n trapFocus: false,\n lockScroll: isSheet,\n });\n\n useEffect(() => {\n fvRef.current = formValidator;\n if (!fvRef.current) return;\n fvRef.current.label = label ?? '';\n fvRef.current.validate();\n const listener = () => forceUpdate((n) => n + 1);\n fvRef.current.registerOnUpdateListener(listener);\n return () => fvRef.current?.removeOnUpdateListener(listener);\n }, [formValidator, label]);\n\n const onMouseEnterHandler = (event: React.MouseEvent) => {\n setIsHovered(true);\n onMouseEnterProp?.(event);\n };\n\n const onMouseLeaveHandler = (event: React.MouseEvent) => {\n setIsHovered(false);\n onMouseLeaveProp?.(event);\n };\n\n const onFocusHandler = (event: React.FocusEvent) => {\n setIsFocused(true);\n onFocusProp?.(event);\n };\n\n const onBlurHandler = (event: React.FocusEvent) => {\n if (fvRef.current) fvRef.current.touched = true;\n setIsFocused(false);\n onBlurProp?.(event);\n };\n\n const hasError = (): boolean => {\n return formValidator ? formValidator.touched && formValidator.hasError() : !isValid || !!errorMessage;\n };\n\n const computedIsRequired = (): boolean => {\n return !!(\n isRequired || formValidator?.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required')\n );\n };\n\n const onChangeHandler = (val: string | number): void => {\n if (formValidator) {\n formValidator.set(val);\n onChange?.(formValidator.value, customProps);\n } else {\n onChange?.(val, customProps);\n }\n };\n\n let dropdownLabel = dropdownLabelProp;\n const currentValue = formValidator ? (formValidator.value as string | number) : value;\n const hasErr = hasError();\n\n items.forEach((item: DropdownItem) => {\n if (item.key === currentValue) {\n dropdownLabel = item.label as string;\n }\n });\n\n const displayValue = dropdownLabel || currentValue;\n const isDisplayBlank = displayValue == null || (typeof displayValue === 'string' && displayValue.trim() === '');\n\n const requiredMark = computedIsRequired() ? <span className={styles.requiredMark}>*</span> : null;\n const editedLabel = label && computedIsRequired() ? `${label} *` : label;\n\n const showFocus = isFocused && !readOnly;\n\n return (\n <div\n {...rest}\n className={cx(className, classNames?.root, styles.dropdownLabel, styles.dropdown, {\n [styles.dropdownLabelColumn]: !isRow,\n })}\n style={{ ...style, ...slotStyles?.root }}\n onMouseEnter={onMouseEnterHandler}\n onMouseLeave={onMouseLeaveHandler}\n >\n <Downshift\n {...(readOnly ? { isOpen: false } : {})}\n onChange={(selection: DropdownItem | null) => onChangeHandler((selection || { key: '' }).key)}\n itemToString={(item: DropdownItem | null) => (item ? String(item.key) : '')}\n selectedItem={items.find((el) => el.key === currentValue) ?? null}\n onStateChange={(changes: StateChangeOptions<DropdownItem>) => {\n if (typeof changes.isOpen === 'boolean') setMenuIsOpen(changes.isOpen);\n }}\n stateReducer={(state: DownshiftState<DropdownItem>, changes: StateChangeOptions<DropdownItem>) => {\n if (scrollToSelected && Object.prototype.hasOwnProperty.call(changes, 'isOpen')) {\n return {\n ...changes,\n highlightedIndex: changes.isOpen ? items.indexOf(state.selectedItem as DropdownItem) : null,\n };\n }\n return changes;\n }}\n >\n {({\n getItemProps,\n getMenuProps,\n getToggleButtonProps,\n closeMenu,\n isOpen,\n selectedItem,\n highlightedIndex,\n setHighlightedIndex,\n }) => (\n <div>\n <div\n ref={triggerRef}\n onFocus={onFocusHandler}\n onBlur={onBlurHandler}\n className={cx(\n styles.dropdownInput,\n {\n [styles.dropdownInputPlain]: plainStyle,\n [styles.dropdownInputReadOnly]: readOnly && !disabled,\n },\n classNames?.field,\n )}\n style={slotStyles?.field}\n >\n <button\n tabIndex={tabIndex}\n type='button'\n disabled={disabled}\n aria-readonly={readOnly || undefined}\n className={cx(\n styles.dropdownInputButton,\n {\n [styles.dropdownInputButtonDisabled]: disabled,\n [styles.dropdownInputButtonReadOnly]: readOnly && !disabled,\n },\n classNames?.control,\n )}\n style={{ ...(isDropdownListWider ? { padding: '6px', border: 'none' } : {}), ...slotStyles?.control }}\n {...getToggleButtonProps()}\n onClick={(e: React.MouseEvent<HTMLButtonElement>) => {\n getToggleButtonProps().onClick?.(e);\n onClick?.(e);\n }}\n ref={reactRef}\n >\n {/* Non-breaking space when blank (same text-node layout as a\n real value) so the button keeps a full line of height. */}\n {isDisplayBlank ? '\\u00A0' : displayValue}\n </button>\n <button\n type='button'\n tabIndex={-1}\n disabled={disabled}\n className={cx(styles.dropdownInputButtonIcon, classNames?.icon)}\n style={slotStyles?.icon}\n {...getToggleButtonProps()}\n onClick={(e: React.MouseEvent<HTMLButtonElement>) => {\n getToggleButtonProps().onClick?.(e);\n onClick?.(e);\n }}\n >\n {!disabled && !readOnly && (\n <FontAwesomeIcon\n icon={dropdownIcon}\n className={cx(styles.dropdownInputButtonIconArrow, {\n [styles.dropdownInputButtonIconArrowOpen]: isOpen,\n })}\n />\n )}\n </button>\n {!disabled && !readOnly && isOpen ? (\n <BodyEnd>\n {isSheet && <SheetBackdrop onClose={closeMenu} />}\n <div\n {...getMenuProps(\n {\n ref: (node: HTMLDivElement | null) => {\n anchorRef.current = node;\n },\n 'aria-label': label || placeholder || undefined,\n },\n { suppressRefError: true },\n )}\n style={isSheet ? sheetStyle : undefined}\n className={cx(styles.dropdownInputListAnim, {\n [sheetStyles.sheet]: isSheet,\n [styles.dropdownInputListAnimWider]: isDropdownListWider && !isSheet,\n })}\n >\n <InfiniteBlock\n onScroll={onScroll}\n className={cx(styles.dropdownInputList, isSheet && sheetStyles.sheetScroll, classNames?.list)}\n style={slotStyles?.list}\n setHighlightedIndex={setHighlightedIndex}\n >\n {items.map((item: DropdownItem, index: number) => (\n <div\n key={item.key}\n {...getItemProps({ index, item, disabled: !!item.disabled })}\n style={slotStyles?.option}\n className={cx(styles.dropdownInputListItem, classNames?.option, {\n [styles.dropdownInputListItemActive]: highlightedIndex === index,\n [styles.dropdownInputListItemSelected]: selectedItem === item,\n [styles.dropdownInputListItemDisabled]: !!item.disabled,\n })}\n >\n {item.label}\n </div>\n ))}\n </InfiniteBlock>\n </div>\n </BodyEnd>\n ) : null}\n <fieldset\n className={cx(styles.dropdownFieldset, {\n [styles.dropdownBorderPlain]: plainStyle,\n [styles.dropdownBorderFocused]: showFocus,\n [styles.dropdownBorderInvalid]: hasErr,\n [styles.dropdownBorderError]: hasErr,\n })}\n >\n <legend\n className={cx(styles.dropdownFieldsetLegend, {\n [styles.dropdownFieldsetLegendActive]:\n useMatLabelStyle && (alwaysFloatLabel || showFocus || !isDisplayBlank),\n })}\n >\n <span>{editedLabel}</span>\n </legend>\n </fieldset>\n {useMatLabelStyle && label && (\n <div\n className={cx(\n styles.dropdownMatLabel,\n {\n [styles.dropdownMatLabelFloating]: alwaysFloatLabel || showFocus || !isDisplayBlank,\n },\n // The floating and the stacked label are two renderings of\n // the same thing, so one slot reaches whichever is in use.\n classNames?.label,\n )}\n style={slotStyles?.label}\n >\n {label}\n {requiredMark}\n </div>\n )}\n {useMatLabelStyle && placeholder && (\n <div\n className={cx(\n styles.dropdownPlaceholder,\n showFocus && !dropdownLabel && styles.dropdownPlaceholderShown,\n )}\n >\n {placeholder}\n </div>\n )}\n </div>\n </div>\n )}\n </Downshift>\n {label && !useMatLabelStyle && (\n <div className={cx(styles.dropdownLabelText, classNames?.label)} style={slotStyles?.label}>\n {label}\n {requiredMark}\n </div>\n )}\n <Tooltip\n variant='error'\n message={formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || ''}\n isVisible={hasErr && (isFocused || isHovered)}\n />\n </div>\n );\n}\n\nexport default Dropdown;\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{useLabels as e}from"../configProvider/useLabels.mjs";import{renderIcon as t}from"../utils/renderIcon.mjs";import n from"./emptyState.module.mjs";import r from"classnames";import{jsx as i,jsxs as a}from"react/jsx-runtime";var o={empty:`No entries available`};function s({className:s,icon:c,children:l,labels:u,...
|
|
1
|
+
import{useLabels as e}from"../configProvider/useLabels.mjs";import{renderIcon as t}from"../utils/renderIcon.mjs";import n from"./emptyState.module.mjs";import r from"classnames";import{jsx as i,jsxs as a}from"react/jsx-runtime";var o={empty:`No entries available`};function s({className:s,icon:c,children:l,labels:u,style:d,classNames:f,styles:p,...m}){let h=e(`EmptyState`,u,o);return a(`div`,{...m,className:r(n.emptyState,s,f?.root),style:{...d,...p?.root},children:[c?t(c,{className:f?.icon,style:p?.icon}):null,i(`div`,{className:f?.text,style:p?.text,children:l??h.empty})]})}export{s as default};
|
|
2
2
|
//# sourceMappingURL=emptyState.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emptyState.mjs","names":[],"sources":["../../../../package/common/emptyState/emptyState.tsx"],"sourcesContent":["import cx from 'classnames';\nimport type { ReactElement } from 'react';\nimport
|
|
1
|
+
{"version":3,"file":"emptyState.mjs","names":[],"sources":["../../../../package/common/emptyState/emptyState.tsx"],"sourcesContent":["import cx from 'classnames';\nimport type { ReactElement } from 'react';\n\nimport styles from './emptyState.module.pcss';\nimport { useLabels } from '../configProvider';\nimport { renderIcon } from '../utils/renderIcon';\nimport type { EmptyStateLabels, EmptyStateProps } from './emptyState.types';\n\nconst DEFAULT_LABELS: Required<EmptyStateLabels> = {\n empty: 'No entries available',\n};\n\nexport default function EmptyState({\n className,\n icon,\n children,\n labels,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n}: EmptyStateProps): ReactElement {\n const text = useLabels('EmptyState', labels, DEFAULT_LABELS);\n return (\n <div\n {...rest}\n className={cx(styles.emptyState, className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n >\n {icon ? renderIcon(icon, { className: classNames?.icon, style: slotStyles?.icon }) : null}\n <div className={classNames?.text} style={slotStyles?.text}>\n {children ?? text.empty}\n </div>\n </div>\n );\n}\n"],"mappings":"oOAQA,IAAM,EAA6C,CACjD,MAAO,sBACT,EAEA,SAAwB,EAAW,CACjC,YACA,OACA,WACA,SACA,QACA,aACA,OAAQ,EACR,GAAG,GAC6B,CAChC,IAAM,EAAO,EAAU,aAAc,EAAQ,CAAc,EAC3D,OACE,EAAC,MAAD,CACE,GAAI,EACJ,UAAW,EAAG,EAAO,WAAY,EAAW,GAAY,IAAI,EAC5D,MAAO,CAAE,GAAG,EAAO,GAAG,GAAY,IAAK,EAHzC,SAAA,CAKG,EAAO,EAAW,EAAM,CAAE,UAAW,GAAY,KAAM,MAAO,GAAY,IAAK,CAAC,EAAI,KACrF,EAAC,MAAD,CAAK,UAAW,GAAY,KAAM,MAAO,GAAY,KAClD,SAAA,GAAY,EAAK,KACf,CAAA,CACF,GAET"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{ButtonSize as e,ButtonStyleType as t}from"../../../button/button.types.mjs";import n from"../../../button/button.mjs";import r from"./fabBase.module.mjs";import i from"classnames";import{jsx as a}from"react/jsx-runtime";var o={default:null,mini:r.fabSizeMini};function s({children:
|
|
1
|
+
import{ButtonSize as e,ButtonStyleType as t}from"../../../button/button.types.mjs";import n from"../../../button/button.mjs";import r from"./fabBase.module.mjs";import i from"classnames";import{jsx as a}from"react/jsx-runtime";var o={default:null,mini:r.fabSizeMini};function s({style:s,children:c=null,size:l=`default`,className:u=``,styleType:d=t.Primary,onClick:f=()=>{}}){return a(n,{styleType:d,size:e.Icon,className:i(r.fab,o[l],u),style:s,onClick:f,children:a(`div`,{className:r.fabContentContainer,children:a(`div`,{className:r.fabContent,children:c})})})}export{s as default};
|
|
2
2
|
//# sourceMappingURL=fabBase.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fabBase.mjs","names":[],"sources":["../../../../../../package/common/fab/components/fabBase/fabBase.tsx"],"sourcesContent":["import cx from 'classnames';\n\nimport Button from 'components/common/button';\nimport { ButtonSize, ButtonStyleType } from 'components/common/button/button.types';\nimport type { FabBaseProps, FabBaseSize } from './fabBase.types';\n\nimport styles from './fabBase.module.pcss';\n\nconst sizeClass: Record<FabBaseSize, string | null> = {\n default: null,\n mini: styles.fabSizeMini,\n};\n\nfunction FabBase({\n children = null,\n size = 'default',\n className = '',\n styleType = ButtonStyleType.Primary,\n onClick = () => {},\n}: FabBaseProps) {\n return (\n /* Icon is the only size that adds no padding. Any other one pushes past the width set\n below through Button's `min-width: min-content`, and the circle becomes an ellipse. */\n <Button\n styleType={styleType}\n size={ButtonSize.Icon}\n className={cx(styles.fab, sizeClass[size], className)}\n onClick={onClick}\n >\n <div className={styles.fabContentContainer}>\n <div className={styles.fabContent}>{children}</div>\n </div>\n </Button>\n );\n}\n\nexport default FabBase;\n"],"mappings":"mOAQA,IAAM,EAAgD,CACpD,QAAS,KACT,KAAM,EAAO,WACf,EAEA,SAAS,EAAQ,CACf,WAAW,KACX,OAAO,UACP,YAAY,GACZ,YAAY,EAAgB,QAC5B,cAAgB,CAAC,GACF,CACf,OAGE,EAAC,EAAD,CACa,YACX,KAAM,EAAW,KACjB,UAAW,EAAG,EAAO,IAAK,EAAU,GAAO,CAAS,
|
|
1
|
+
{"version":3,"file":"fabBase.mjs","names":[],"sources":["../../../../../../package/common/fab/components/fabBase/fabBase.tsx"],"sourcesContent":["import cx from 'classnames';\n\nimport Button from 'components/common/button';\nimport { ButtonSize, ButtonStyleType } from 'components/common/button/button.types';\nimport type { FabBaseProps, FabBaseSize } from './fabBase.types';\n\nimport styles from './fabBase.module.pcss';\n\nconst sizeClass: Record<FabBaseSize, string | null> = {\n default: null,\n mini: styles.fabSizeMini,\n};\n\nfunction FabBase({\n style,\n children = null,\n size = 'default',\n className = '',\n styleType = ButtonStyleType.Primary,\n onClick = () => {},\n}: FabBaseProps) {\n return (\n /* Icon is the only size that adds no padding. Any other one pushes past the width set\n below through Button's `min-width: min-content`, and the circle becomes an ellipse. */\n <Button\n styleType={styleType}\n size={ButtonSize.Icon}\n className={cx(styles.fab, sizeClass[size], className)}\n style={style}\n onClick={onClick}\n >\n <div className={styles.fabContentContainer}>\n <div className={styles.fabContent}>{children}</div>\n </div>\n </Button>\n );\n}\n\nexport default FabBase;\n"],"mappings":"mOAQA,IAAM,EAAgD,CACpD,QAAS,KACT,KAAM,EAAO,WACf,EAEA,SAAS,EAAQ,CACf,QACA,WAAW,KACX,OAAO,UACP,YAAY,GACZ,YAAY,EAAgB,QAC5B,cAAgB,CAAC,GACF,CACf,OAGE,EAAC,EAAD,CACa,YACX,KAAM,EAAW,KACjB,UAAW,EAAG,EAAO,IAAK,EAAU,GAAO,CAAS,EAC7C,QACE,UAET,SAAA,EAAC,MAAD,CAAK,UAAW,EAAO,oBACrB,SAAA,EAAC,MAAD,CAAK,UAAW,EAAO,WAAa,UAAc,CAAA,CAC/C,CAAA,CACC,CAAA,CAEZ"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{ButtonStyleType as e}from"../button/button.types.mjs";import{renderIcon as t}from"../utils/renderIcon.mjs";import n from"./components/fabBase/fabBase.mjs";import r from"./fab.module.mjs";import{useState as i}from"react";import a from"classnames";import{FontAwesomeIcon as o}from"@fortawesome/react-fontawesome";import{faPlus as s}from"@fortawesome/free-solid-svg-icons";import{jsx as c,jsxs as l}from"react/jsx-runtime";function u({children:u=null,size:d=`default`,menuEntries:f=[],onMenuEntryClicked:p=()=>{},onClick:m=()=>{},closeMenuOnEntryClicked:h=!0,...
|
|
1
|
+
import{ButtonStyleType as e}from"../button/button.types.mjs";import{renderIcon as t}from"../utils/renderIcon.mjs";import n from"./components/fabBase/fabBase.mjs";import r from"./fab.module.mjs";import{useState as i}from"react";import a from"classnames";import{FontAwesomeIcon as o}from"@fortawesome/react-fontawesome";import{faPlus as s}from"@fortawesome/free-solid-svg-icons";import{jsx as c,jsxs as l}from"react/jsx-runtime";function u({children:u=null,size:d=`default`,menuEntries:f=[],onMenuEntryClicked:p=()=>{},onClick:m=()=>{},closeMenuOnEntryClicked:h=!0,className:g,style:_,classNames:v,styles:y,...b}){let[x,S]=i(!1);function C(){f.length>0&&S(e=>!e),m()}function w(e,t){e.stopPropagation(),h&&S(!1),p(t)}return l(`div`,{...b,className:a(r.fabContainer,g,v?.root),style:{..._,...y?.root},children:[c(`div`,{className:a(r.fabMenuBackdrop,x&&r.fabMenuBackdropOpen,v?.backdrop),style:y?.backdrop,onClick:e=>{e.stopPropagation(),S(!1)}}),c(`div`,{onClick:e=>e.stopPropagation(),children:c(n,{className:a(r.fab,v?.button),style:y?.button,size:d,onClick:C,children:l(`div`,{className:a(r.fabIcon,x&&r.fabIconMenuOpen,v?.icon),style:y?.icon,children:[c(`div`,{className:a(r.fabIconContent,!x&&r.fabIconContentShown),children:u}),c(`div`,{className:a(r.fabIconContent,x&&r.fabIconContentShown),children:c(o,{icon:s})})]})})}),c(`div`,{className:a(r.fabMenu,v?.menu),style:y?.menu,children:f.map(({id:i,label:o,icon:s})=>l(`div`,{className:a(r.fabMenuEntry,x&&r.fabMenuEntryShown,v?.menuEntry),style:y?.menuEntry,onClick:e=>w(e,i),children:[o&&c(`div`,{className:r.fabMenuEntryLabelContainer,children:c(`div`,{className:a(r.fabMenuEntryLabel,v?.menuEntryLabel),style:y?.menuEntryLabel,children:o})}),c(n,{size:`mini`,styleType:e.Default,children:t(s)})]},i))})]})}export{u as default};
|
|
2
2
|
//# sourceMappingURL=fab.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fab.mjs","names":[],"sources":["../../../../package/common/fab/fab.tsx"],"sourcesContent":["import { useState } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faPlus as addIcon } from '@fortawesome/free-solid-svg-icons';\nimport { ButtonStyleType } from '../button/button.types';\n\nimport FabBase from './components/fabBase';\nimport type { FabProps } from './fab.types';\n\nimport styles from './fab.module.pcss';\nimport { renderIcon } from '../utils/renderIcon';\n\nfunction Fab({\n children = null,\n size = 'default',\n menuEntries = [],\n onMenuEntryClicked = () => {},\n onClick = () => {},\n closeMenuOnEntryClicked = true,\n ...rest\n}: FabProps) {\n const [menuOpen, setMenuOpen] = useState(false);\n\n function handleFabClick() {\n if (menuEntries.length > 0) setMenuOpen((prev) => !prev);\n onClick();\n }\n\n function handleEntryClick(e: React.MouseEvent, id: string) {\n e.stopPropagation();\n if (closeMenuOnEntryClicked) setMenuOpen(false);\n onMenuEntryClicked(id);\n }\n\n return (\n <div
|
|
1
|
+
{"version":3,"file":"fab.mjs","names":[],"sources":["../../../../package/common/fab/fab.tsx"],"sourcesContent":["import { useState } from 'react';\nimport cx from 'classnames';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faPlus as addIcon } from '@fortawesome/free-solid-svg-icons';\nimport { ButtonStyleType } from '../button/button.types';\n\nimport FabBase from './components/fabBase';\nimport type { FabProps } from './fab.types';\n\nimport styles from './fab.module.pcss';\nimport { renderIcon } from '../utils/renderIcon';\n\nfunction Fab({\n children = null,\n size = 'default',\n menuEntries = [],\n onMenuEntryClicked = () => {},\n onClick = () => {},\n closeMenuOnEntryClicked = true,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n}: FabProps) {\n const [menuOpen, setMenuOpen] = useState(false);\n\n function handleFabClick() {\n if (menuEntries.length > 0) setMenuOpen((prev) => !prev);\n onClick();\n }\n\n function handleEntryClick(e: React.MouseEvent, id: string) {\n e.stopPropagation();\n if (closeMenuOnEntryClicked) setMenuOpen(false);\n onMenuEntryClicked(id);\n }\n\n return (\n <div\n {...rest}\n className={cx(styles.fabContainer, className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n >\n <div\n className={cx(styles.fabMenuBackdrop, menuOpen && styles.fabMenuBackdropOpen, classNames?.backdrop)}\n style={slotStyles?.backdrop}\n onClick={(e) => {\n e.stopPropagation();\n setMenuOpen(false);\n }}\n />\n <div onClick={(e) => e.stopPropagation()}>\n <FabBase\n className={cx(styles.fab, classNames?.button)}\n style={slotStyles?.button}\n size={size}\n onClick={handleFabClick}\n >\n <div\n className={cx(styles.fabIcon, menuOpen && styles.fabIconMenuOpen, classNames?.icon)}\n style={slotStyles?.icon}\n >\n <div className={cx(styles.fabIconContent, !menuOpen && styles.fabIconContentShown)}>{children}</div>\n <div className={cx(styles.fabIconContent, menuOpen && styles.fabIconContentShown)}>\n <FontAwesomeIcon icon={addIcon} />\n </div>\n </div>\n </FabBase>\n </div>\n <div className={cx(styles.fabMenu, classNames?.menu)} style={slotStyles?.menu}>\n {menuEntries.map(({ id, label, icon }) => (\n <div\n key={id}\n className={cx(styles.fabMenuEntry, menuOpen && styles.fabMenuEntryShown, classNames?.menuEntry)}\n style={slotStyles?.menuEntry}\n onClick={(e) => handleEntryClick(e, id)}\n >\n {label && (\n <div className={styles.fabMenuEntryLabelContainer}>\n <div\n className={cx(styles.fabMenuEntryLabel, classNames?.menuEntryLabel)}\n style={slotStyles?.menuEntryLabel}\n >\n {label}\n </div>\n </div>\n )}\n <FabBase size='mini' styleType={ButtonStyleType.Default}>\n {renderIcon(icon)}\n </FabBase>\n </div>\n ))}\n </div>\n </div>\n );\n}\n\nexport default Fab;\n"],"mappings":"2aAYA,SAAS,EAAI,CACX,WAAW,KACX,OAAO,UACP,cAAc,CAAC,EACf,yBAA2B,CAAC,EAC5B,cAAgB,CAAC,EACjB,0BAA0B,GAC1B,YACA,QACA,aACA,OAAQ,EACR,GAAG,GACQ,CACX,GAAM,CAAC,EAAU,GAAe,EAAS,EAAK,EAE9C,SAAS,GAAiB,CACpB,EAAY,OAAS,GAAG,EAAa,GAAS,CAAC,CAAI,EACvD,EAAQ,CACV,CAEA,SAAS,EAAiB,EAAqB,EAAY,CACzD,EAAE,gBAAgB,EACd,GAAyB,EAAY,EAAK,EAC9C,EAAmB,CAAE,CACvB,CAEA,OACE,EAAC,MAAD,CACE,GAAI,EACJ,UAAW,EAAG,EAAO,aAAc,EAAW,GAAY,IAAI,EAC9D,MAAO,CAAE,GAAG,EAAO,GAAG,GAAY,IAAK,EAHzC,SAAA,CAKE,EAAC,MAAD,CACE,UAAW,EAAG,EAAO,gBAAiB,GAAY,EAAO,oBAAqB,GAAY,QAAQ,EAClG,MAAO,GAAY,SACnB,QAAU,GAAM,CACd,EAAE,gBAAgB,EAClB,EAAY,EAAK,CACnB,CACD,CAAA,EACD,EAAC,MAAD,CAAK,QAAU,GAAM,EAAE,gBAAgB,EACrC,SAAA,EAAC,EAAD,CACE,UAAW,EAAG,EAAO,IAAK,GAAY,MAAM,EAC5C,MAAO,GAAY,OACb,OACN,QAAS,EAET,SAAA,EAAC,MAAD,CACE,UAAW,EAAG,EAAO,QAAS,GAAY,EAAO,gBAAiB,GAAY,IAAI,EAClF,MAAO,GAAY,KAFrB,SAAA,CAIE,EAAC,MAAD,CAAK,UAAW,EAAG,EAAO,eAAgB,CAAC,GAAY,EAAO,mBAAmB,EAAI,UAAc,CAAA,EACnG,EAAC,MAAD,CAAK,UAAW,EAAG,EAAO,eAAgB,GAAY,EAAO,mBAAmB,EAC9E,SAAA,EAAC,EAAD,CAAiB,KAAM,CAAU,CAAA,CAC9B,CAAA,CACF,GACE,CAAA,CACN,CAAA,EACL,EAAC,MAAD,CAAK,UAAW,EAAG,EAAO,QAAS,GAAY,IAAI,EAAG,MAAO,GAAY,KACtE,SAAA,EAAY,KAAK,CAAE,KAAI,QAAO,UAC7B,EAAC,MAAD,CAEE,UAAW,EAAG,EAAO,aAAc,GAAY,EAAO,kBAAmB,GAAY,SAAS,EAC9F,MAAO,GAAY,UACnB,QAAU,GAAM,EAAiB,EAAG,CAAE,EAJxC,SAAA,CAMG,GACC,EAAC,MAAD,CAAK,UAAW,EAAO,2BACrB,SAAA,EAAC,MAAD,CACE,UAAW,EAAG,EAAO,kBAAmB,GAAY,cAAc,EAClE,MAAO,GAAY,eAElB,SAAA,CACE,CAAA,CACF,CAAA,EAEP,EAAC,EAAD,CAAS,KAAK,OAAO,UAAW,EAAgB,QAC7C,SAAA,EAAW,CAAI,CACT,CAAA,CACN,CAlBE,EAAA,CAkBF,CACN,CACE,CAAA,CACF,GAET"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e from"../loading/spinnerLoading/index.mjs";import t from"./infiniteBlock.module.mjs";import n,{useCallback as r,useEffect as i,useRef as a,useState as o}from"react";import s from"classnames";import{Fragment as c,jsx as l,jsxs as u}from"react/jsx-runtime";function d(e){return e==null||typeof e==`boolean`?``:typeof e==`string`||typeof e==`number`?String(e):Array.isArray(e)?e.map(d).join(``):n.isValidElement(e)?d(e.props.children):``}function f(e,t,n){return e===-1?t>0?0:n-1:(e+t+n)%n}function p(e,t,n){let[r,s]=o(!1),c=a(!1),l=a(null),u=a(t);return i(()=>{u.current=t}),i(()=>{let t=e.current,r=l.current;if(n||!t||!r)return;let i=!1,a=()=>{c.current||(c.current=!0,s(!0),u.current().catch(()=>null).finally(()=>{i||(c.current=!1,s(!1),o.unobserve(r),o.observe(r))}))},o=new IntersectionObserver(([e])=>{e.isIntersecting&&a()},{root:t});return o.observe(r),()=>{i=!0,o.disconnect()}},[e,n]),{isLoading:r,sentinelRef:l}}function m(e,t){let i=a(null),o=a(-1),s=a(null);return{containerRef:i,onKeyDown:r(r=>{let a=i.current;if(!a)return;let c=n.Children.toArray(e);if(c.length===0)return;let l=o.current;if(r.key===`ArrowDown`)r.preventDefault(),l=f(l,1,c.length);else if(r.key===`ArrowUp`)r.preventDefault(),l=f(l,-1,c.length);else if(r.key.length===1){let e=r.key.toLowerCase(),t=s.current===r.key?l+1:0,n=t=>d(t).toLowerCase().startsWith(e),i=c.slice(t).findIndex(n),a=i===-1?c.slice(0,t).findIndex(n):t+i;if(s.current=r.key,a===-1)return;l=a}else return;s.current=r.key,o.current=l;let u=a.children[l];if(u){let e=u.offsetTop,t=e+u.offsetHeight;e<a.scrollTop?a.scrollTop=e:t>a.scrollTop+a.clientHeight&&(a.scrollTop=t-a.clientHeight)}t(l)},[e,t])}}function h({children:n,onScroll:r,className:i=``,isDataLoading:
|
|
1
|
+
import e from"../loading/spinnerLoading/index.mjs";import t from"./infiniteBlock.module.mjs";import n,{useCallback as r,useEffect as i,useRef as a,useState as o}from"react";import s from"classnames";import{Fragment as c,jsx as l,jsxs as u}from"react/jsx-runtime";function d(e){return e==null||typeof e==`boolean`?``:typeof e==`string`||typeof e==`number`?String(e):Array.isArray(e)?e.map(d).join(``):n.isValidElement(e)?d(e.props.children):``}function f(e,t,n){return e===-1?t>0?0:n-1:(e+t+n)%n}function p(e,t,n){let[r,s]=o(!1),c=a(!1),l=a(null),u=a(t);return i(()=>{u.current=t}),i(()=>{let t=e.current,r=l.current;if(n||!t||!r)return;let i=!1,a=()=>{c.current||(c.current=!0,s(!0),u.current().catch(()=>null).finally(()=>{i||(c.current=!1,s(!1),o.unobserve(r),o.observe(r))}))},o=new IntersectionObserver(([e])=>{e.isIntersecting&&a()},{root:t});return o.observe(r),()=>{i=!0,o.disconnect()}},[e,n]),{isLoading:r,sentinelRef:l}}function m(e,t){let i=a(null),o=a(-1),s=a(null);return{containerRef:i,onKeyDown:r(r=>{let a=i.current;if(!a)return;let c=n.Children.toArray(e);if(c.length===0)return;let l=o.current;if(r.key===`ArrowDown`)r.preventDefault(),l=f(l,1,c.length);else if(r.key===`ArrowUp`)r.preventDefault(),l=f(l,-1,c.length);else if(r.key.length===1){let e=r.key.toLowerCase(),t=s.current===r.key?l+1:0,n=t=>d(t).toLowerCase().startsWith(e),i=c.slice(t).findIndex(n),a=i===-1?c.slice(0,t).findIndex(n):t+i;if(s.current=r.key,a===-1)return;l=a}else return;s.current=r.key,o.current=l;let u=a.children[l];if(u){let e=u.offsetTop,t=e+u.offsetHeight;e<a.scrollTop?a.scrollTop=e:t>a.scrollTop+a.clientHeight&&(a.scrollTop=t-a.clientHeight)}t(l)},[e,t])}}function h({children:n,onScroll:r,className:i=``,style:a,isDataLoading:o=!1,setHighlightedIndex:d=()=>null}){let{containerRef:f,onKeyDown:h}=m(n,d),{isLoading:g,sentinelRef:_}=p(f,r,o);return l(`div`,{className:s(t.infiniteBlock,i),style:a,onKeyDown:h,tabIndex:0,ref:f,children:o?l(`div`,{className:t.spinnerWrapper,children:l(e,{className:t.spinner})}):u(c,{children:[n,l(`div`,{ref:_,className:t.sentinel,"aria-hidden":`true`}),g&&l(`div`,{className:t.spinnerWrapper,children:l(e,{className:t.spinner})})]})})}export{h as default};
|
|
2
2
|
//# sourceMappingURL=infiniteBlock.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"infiniteBlock.mjs","names":[],"sources":["../../../../package/common/infiniteBlock/infiniteBlock.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useRef, useState } from 'react';\nimport cx from 'classnames';\n\nimport styles from './infiniteBlock.module.pcss';\nimport SpinnerLoading from '../loading/spinnerLoading';\nimport type { InfiniteBlockProps } from './infiniteBlock.types';\n\n// Flattens a React node down to the text it renders, so keyboard type-ahead can match\n// against whatever a child actually displays — a plain string, a number, or something\n// wrapped in nested elements/fragments/arrays (e.g. an icon + label pair).\nfunction getNodeText(node: React.ReactNode): string {\n if (node === null || node === undefined || typeof node === 'boolean') return '';\n if (typeof node === 'string' || typeof node === 'number') return String(node);\n if (Array.isArray(node)) return node.map(getNodeText).join('');\n if (React.isValidElement<{ children?: React.ReactNode }>(node)) {\n return getNodeText(node.props.children);\n }\n return '';\n}\n\n// Cycles the index by `change`, wrapping directly from last to first (and first to last) —\n// no \"nothing highlighted\" stop in between, in either direction. `-1` means nothing is\n// highlighted yet: ArrowDown from there starts at the first item, ArrowUp at the last.\nfunction clampIndex(index: number, change: number, length: number): number {\n if (index === -1) return change > 0 ? 0 : length - 1;\n return (index + change + length) % length;\n}\n\n// Loads more items once a sentinel element at the end of the list scrolls into view.\n// IntersectionObserver only fires on a change of intersection state, so if a page of\n// freshly-loaded items still doesn't fill the container, the sentinel stays visible and\n// we have to re-arm the observer ourselves to pick up the next page.\nfunction useInfiniteScroll(\n containerRef: React.RefObject<HTMLDivElement | null>,\n onScroll: () => Promise<void>,\n disabled: boolean,\n) {\n const [isLoading, setIsLoading] = useState(false);\n const isLoadingRef = useRef(false);\n const sentinelRef = useRef<HTMLDivElement>(null);\n\n // Kept in a ref so a caller passing a fresh onScroll function each render doesn't\n // tear down and recreate the observer every time.\n const onScrollRef = useRef(onScroll);\n useEffect(() => {\n onScrollRef.current = onScroll;\n });\n\n useEffect(() => {\n const root = containerRef.current;\n const sentinel = sentinelRef.current;\n if (disabled || !root || !sentinel) return undefined;\n\n let cancelled = false;\n\n const loadMore = () => {\n if (isLoadingRef.current) return;\n isLoadingRef.current = true;\n setIsLoading(true);\n\n onScrollRef\n .current()\n .catch(() => null)\n .finally(() => {\n if (cancelled) return;\n isLoadingRef.current = false;\n setIsLoading(false);\n observer.unobserve(sentinel);\n observer.observe(sentinel);\n });\n };\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) loadMore();\n },\n { root },\n );\n\n observer.observe(sentinel);\n\n return () => {\n cancelled = true;\n observer.disconnect();\n };\n }, [containerRef, disabled]);\n\n return { isLoading, sentinelRef };\n}\n\n// Arrow-key navigation plus type-ahead: pressing a character key jumps to the next child\n// whose text starts with it, cycling through repeats and wrapping at the end of the list.\nfunction useTypeaheadNav(children: React.ReactNode, setHighlightedIndex: (index: number) => void) {\n const containerRef = useRef<HTMLDivElement>(null);\n const indexRef = useRef(-1);\n const lastKeyRef = useRef<string | null>(null);\n\n const onKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n const container = containerRef.current;\n if (!container) return;\n\n const childArray = React.Children.toArray(children);\n if (childArray.length === 0) return;\n\n let index = indexRef.current;\n\n if (event.key === 'ArrowDown') {\n // Without this, the browser's own native \"scroll a focused scrollable element on\n // arrow keys\" behavior fires alongside our scrollIntoView math below, stacking an\n // extra native scroll on top of ours on every press until the highlighted item is\n // pushed out of view entirely.\n event.preventDefault();\n index = clampIndex(index, 1, childArray.length);\n } else if (event.key === 'ArrowUp') {\n event.preventDefault();\n index = clampIndex(index, -1, childArray.length);\n } else if (event.key.length === 1) {\n const key = event.key.toLowerCase();\n const repeatingKey = lastKeyRef.current === event.key;\n const searchStart = repeatingKey ? index + 1 : 0;\n const startsWithKey = (child: React.ReactNode) => getNodeText(child).toLowerCase().startsWith(key);\n\n const afterCurrent = childArray.slice(searchStart).findIndex(startsWithKey);\n const match =\n afterCurrent !== -1 ? searchStart + afterCurrent : childArray.slice(0, searchStart).findIndex(startsWithKey);\n\n lastKeyRef.current = event.key;\n if (match === -1) return;\n index = match;\n } else {\n return;\n }\n\n lastKeyRef.current = event.key;\n indexRef.current = index;\n\n const child = container.children[index] as HTMLElement | undefined;\n if (child) {\n const childTop = child.offsetTop;\n const childBottom = childTop + child.offsetHeight;\n if (childTop < container.scrollTop) {\n container.scrollTop = childTop;\n } else if (childBottom > container.scrollTop + container.clientHeight) {\n container.scrollTop = childBottom - container.clientHeight;\n }\n }\n setHighlightedIndex(index);\n },\n [children, setHighlightedIndex],\n );\n\n return { containerRef, onKeyDown };\n}\n\nfunction InfiniteBlock({\n children,\n onScroll,\n className = '',\n isDataLoading = false,\n setHighlightedIndex = () => null,\n}: InfiniteBlockProps) {\n const { containerRef, onKeyDown } = useTypeaheadNav(children, setHighlightedIndex);\n const { isLoading: showScrollSpinner, sentinelRef } = useInfiniteScroll(containerRef, onScroll, isDataLoading);\n\n return (\n <div
|
|
1
|
+
{"version":3,"file":"infiniteBlock.mjs","names":[],"sources":["../../../../package/common/infiniteBlock/infiniteBlock.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useRef, useState } from 'react';\nimport cx from 'classnames';\n\nimport styles from './infiniteBlock.module.pcss';\nimport SpinnerLoading from '../loading/spinnerLoading';\nimport type { InfiniteBlockProps } from './infiniteBlock.types';\n\n// Flattens a React node down to the text it renders, so keyboard type-ahead can match\n// against whatever a child actually displays — a plain string, a number, or something\n// wrapped in nested elements/fragments/arrays (e.g. an icon + label pair).\nfunction getNodeText(node: React.ReactNode): string {\n if (node === null || node === undefined || typeof node === 'boolean') return '';\n if (typeof node === 'string' || typeof node === 'number') return String(node);\n if (Array.isArray(node)) return node.map(getNodeText).join('');\n if (React.isValidElement<{ children?: React.ReactNode }>(node)) {\n return getNodeText(node.props.children);\n }\n return '';\n}\n\n// Cycles the index by `change`, wrapping directly from last to first (and first to last) —\n// no \"nothing highlighted\" stop in between, in either direction. `-1` means nothing is\n// highlighted yet: ArrowDown from there starts at the first item, ArrowUp at the last.\nfunction clampIndex(index: number, change: number, length: number): number {\n if (index === -1) return change > 0 ? 0 : length - 1;\n return (index + change + length) % length;\n}\n\n// Loads more items once a sentinel element at the end of the list scrolls into view.\n// IntersectionObserver only fires on a change of intersection state, so if a page of\n// freshly-loaded items still doesn't fill the container, the sentinel stays visible and\n// we have to re-arm the observer ourselves to pick up the next page.\nfunction useInfiniteScroll(\n containerRef: React.RefObject<HTMLDivElement | null>,\n onScroll: () => Promise<void>,\n disabled: boolean,\n) {\n const [isLoading, setIsLoading] = useState(false);\n const isLoadingRef = useRef(false);\n const sentinelRef = useRef<HTMLDivElement>(null);\n\n // Kept in a ref so a caller passing a fresh onScroll function each render doesn't\n // tear down and recreate the observer every time.\n const onScrollRef = useRef(onScroll);\n useEffect(() => {\n onScrollRef.current = onScroll;\n });\n\n useEffect(() => {\n const root = containerRef.current;\n const sentinel = sentinelRef.current;\n if (disabled || !root || !sentinel) return undefined;\n\n let cancelled = false;\n\n const loadMore = () => {\n if (isLoadingRef.current) return;\n isLoadingRef.current = true;\n setIsLoading(true);\n\n onScrollRef\n .current()\n .catch(() => null)\n .finally(() => {\n if (cancelled) return;\n isLoadingRef.current = false;\n setIsLoading(false);\n observer.unobserve(sentinel);\n observer.observe(sentinel);\n });\n };\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) loadMore();\n },\n { root },\n );\n\n observer.observe(sentinel);\n\n return () => {\n cancelled = true;\n observer.disconnect();\n };\n }, [containerRef, disabled]);\n\n return { isLoading, sentinelRef };\n}\n\n// Arrow-key navigation plus type-ahead: pressing a character key jumps to the next child\n// whose text starts with it, cycling through repeats and wrapping at the end of the list.\nfunction useTypeaheadNav(children: React.ReactNode, setHighlightedIndex: (index: number) => void) {\n const containerRef = useRef<HTMLDivElement>(null);\n const indexRef = useRef(-1);\n const lastKeyRef = useRef<string | null>(null);\n\n const onKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n const container = containerRef.current;\n if (!container) return;\n\n const childArray = React.Children.toArray(children);\n if (childArray.length === 0) return;\n\n let index = indexRef.current;\n\n if (event.key === 'ArrowDown') {\n // Without this, the browser's own native \"scroll a focused scrollable element on\n // arrow keys\" behavior fires alongside our scrollIntoView math below, stacking an\n // extra native scroll on top of ours on every press until the highlighted item is\n // pushed out of view entirely.\n event.preventDefault();\n index = clampIndex(index, 1, childArray.length);\n } else if (event.key === 'ArrowUp') {\n event.preventDefault();\n index = clampIndex(index, -1, childArray.length);\n } else if (event.key.length === 1) {\n const key = event.key.toLowerCase();\n const repeatingKey = lastKeyRef.current === event.key;\n const searchStart = repeatingKey ? index + 1 : 0;\n const startsWithKey = (child: React.ReactNode) => getNodeText(child).toLowerCase().startsWith(key);\n\n const afterCurrent = childArray.slice(searchStart).findIndex(startsWithKey);\n const match =\n afterCurrent !== -1 ? searchStart + afterCurrent : childArray.slice(0, searchStart).findIndex(startsWithKey);\n\n lastKeyRef.current = event.key;\n if (match === -1) return;\n index = match;\n } else {\n return;\n }\n\n lastKeyRef.current = event.key;\n indexRef.current = index;\n\n const child = container.children[index] as HTMLElement | undefined;\n if (child) {\n const childTop = child.offsetTop;\n const childBottom = childTop + child.offsetHeight;\n if (childTop < container.scrollTop) {\n container.scrollTop = childTop;\n } else if (childBottom > container.scrollTop + container.clientHeight) {\n container.scrollTop = childBottom - container.clientHeight;\n }\n }\n setHighlightedIndex(index);\n },\n [children, setHighlightedIndex],\n );\n\n return { containerRef, onKeyDown };\n}\n\nfunction InfiniteBlock({\n children,\n onScroll,\n className = '',\n style,\n isDataLoading = false,\n setHighlightedIndex = () => null,\n}: InfiniteBlockProps) {\n const { containerRef, onKeyDown } = useTypeaheadNav(children, setHighlightedIndex);\n const { isLoading: showScrollSpinner, sentinelRef } = useInfiniteScroll(containerRef, onScroll, isDataLoading);\n\n return (\n <div\n className={cx(styles.infiniteBlock, className)}\n style={style}\n onKeyDown={onKeyDown}\n tabIndex={0}\n ref={containerRef}\n >\n {isDataLoading ? (\n <div className={styles.spinnerWrapper}>\n <SpinnerLoading className={styles.spinner} />\n </div>\n ) : (\n <>\n {children}\n <div ref={sentinelRef} className={styles.sentinel} aria-hidden='true' />\n {showScrollSpinner && (\n <div className={styles.spinnerWrapper}>\n <SpinnerLoading className={styles.spinner} />\n </div>\n )}\n </>\n )}\n </div>\n );\n}\n\nexport default InfiniteBlock;\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import{ButtonSize as n}from"../button/button.types.mjs";import r from"../button/button.mjs";import i from"../hooks/useCoarsePointer.mjs";import ee from"../buttonGroup/buttonGroup.mjs";import
|
|
1
|
+
import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import{ButtonSize as n}from"../button/button.types.mjs";import r from"../button/button.mjs";import i from"../hooks/useCoarsePointer.mjs";import ee from"../buttonGroup/buttonGroup.mjs";import a from"../input/input.mjs";import o from"./numberInput.module.mjs";import te from"./useStepRepeat.mjs";import s from"react";import c from"classnames";import{FontAwesomeIcon as l}from"@fortawesome/react-fontawesome";import{faChevronDown as ne,faChevronUp as re,faMinus as ie,faPlus as ae}from"@fortawesome/free-solid-svg-icons";import{jsx as u,jsxs as d}from"react/jsx-runtime";import{NumericFormat as oe}from"react-number-format";var se={increase:`Increase`,decrease:`Decrease`};function f({label:e,disabled:t,direction:n,onStart:r,onStop:i}){return{tabIndex:-1,disabled:t,"aria-label":e,onPointerDown:e=>{(e.button===0||e.pointerType!==`mouse`)&&(e.preventDefault(),r(n,e.shiftKey?10:1))},onPointerUp:i,onPointerCancel:i,onContextMenu:e=>e.preventDefault()}}function p(e){return u(`button`,{type:`button`,...f(e),className:c(o.arrowBtn,e.className),style:e.style,children:u(l,{icon:e.icon,"aria-hidden":`true`})})}function m(e){let{disabled:t,...i}=f(e);return u(r,{...i,className:c(o.splitBtn,e.className),style:e.style,size:n.Icon,disabled:t,icon:e.icon})}function h(n){let r=s.useRef(void 0),{value:l,onChange:f,formValidator:h,label:ce,error:g,className:_,style:v,classNames:y,styles:b,allowNegative:x=!1,decimalSeparator:S,thousandSeparator:C,prefix:le,suffix:ue,decimalScale:w,fixedDecimalScale:de=!0,placeholder:fe,sign:T,showArrows:pe=!1,step:E=1,min:D,max:O,arrowsLayout:k=`auto`,labels:me,readOnly:A=!1,disabled:j=!1,isRequired:he=!1,onKeyDown:M,...ge}=e(`NumberInput`,n),N=t(`NumberInput`,me,se),[,P]=s.useState(0);s.useEffect(()=>{if(r.current=h,!h)return;let e=()=>P(e=>e+1);return h.registerOnUpdateListener(e),()=>{h.removeOnUpdateListener(e)}},[h]);let F=he||!!h?.validators.find(e=>e.validatorId===`required`),I=h==null?l:h.value,L=T!=null&&I!=null?Math.abs(Number(I)):I,R=L==null?``:String(L);function z(e){let t=e;t!==void 0&&(T===`-`&&(t=-Math.abs(t)),T===`+`&&(t=Math.abs(t))),h?.set(t??null),f?.(t)}function B(e){let t=e;return!x&&T!==`-`&&(t=Math.max(0,t)),T===`-`&&(t=Math.min(0,t)),D!==void 0&&(t=Math.max(D,t)),O!==void 0&&(t=Math.min(O,t)),t}let V=I!=null&&I!==``?Number(I):0;function H(){if(A||j||I==null||I===``)return;let e=Number(I);if(!Number.isFinite(e))return;let t=B(e);t!==e&&z(t)}function U(e){if(A||j)return!1;let t=B(V+e);return t!==V&&(z(t),!0)}let{start:_e,stop:ve}=te(e=>U(e*E));function ye(e){if(e.key===`Enter`){H(),M?.(e);return}if(!G){M?.(e);return}if(e.key===`ArrowUp`){e.preventDefault(),U(e.shiftKey?E*10:E);return}if(e.key===`ArrowDown`){e.preventDefault(),U(e.shiftKey?-E*10:-E);return}M?.(e)}let be=T===`-`?`-`:T===`+`?`+`:le,W=i(),G=pe&&!A&&!j,K=k===`split`||k===`auto`&&W,q=B(V+E)!==V,J=B(V-E)!==V,Y={onStart:_e,onStop:ve},X=G&&!K?d(`div`,{className:c(o.arrows,y?.arrows),style:b?.arrows,children:[u(p,{className:y?.increaseButton,style:b?.increaseButton,label:N.increase,icon:re,disabled:!q,direction:1,...Y}),u(p,{className:y?.decreaseButton,style:b?.decreaseButton,label:N.decrease,icon:ne,disabled:!J,direction:-1,...Y})]}):null,xe=G?{role:`spinbutton`,"aria-valuenow":Number.isFinite(V)?V:void 0,"aria-valuemin":D,"aria-valuemax":O,"aria-valuetext":R||void 0}:{},Z=h?!h.touched||!h.hasError():!g,Q=G&&K,$=u(a,{label:ce,className:c(o.numberInput,{[o.numberInputSplit]:Q},Q?void 0:_,Q?void 0:y?.root,y?.input),style:{...Q?void 0:v,...Q?void 0:b?.root,...b?.input},value:R,onChange:()=>{},formValidator:null,isRequired:F,onBlur:()=>{H(),r.current&&(r.current.touched=!0)},isValid:Z,errorMessage:h?h.touched?h.getCurrentErrorMessage()??``:``:g??``,placeholder:fe,readOnly:A,disabled:j,suffix:X,classNames:X?{suffix:o.arrowSuffix}:void 0,inputMode:w===0?`numeric`:`decimal`,customInput:u(oe,{onValueChange:e=>z(e.floatValue),allowNegative:T==null&&x,decimalSeparator:S,thousandSeparator:C,decimalScale:w,fixedDecimalScale:de,prefix:be,suffix:ue,onKeyDown:ye,...xe,...ge})});return Q?d(ee,{className:c(o.split,{[o.splitInvalid]:!Z},_,y?.root),style:{...v,...b?.root},children:[u(m,{className:y?.decreaseButton,style:b?.decreaseButton,label:N.decrease,icon:ie,disabled:!J,direction:-1,...Y}),$,u(m,{className:y?.increaseButton,style:b?.increaseButton,label:N.increase,icon:ae,disabled:!q,direction:1,...Y})]}):$}export{h as default};
|
|
2
2
|
//# sourceMappingURL=numberInput.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"numberInput.mjs","names":[],"sources":["../../../../package/common/numberInput/numberInput.tsx"],"sourcesContent":["import type { ReactElement } from 'react';\nimport React from 'react';\nimport cx from 'classnames';\nimport { NumericFormat } from 'react-number-format';\nimport type { FormValidator } from 'services/formValidation';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronUp, faChevronDown, faMinus, faPlus } from '@fortawesome/free-solid-svg-icons';\n\nimport styles from './numberInput.module.pcss';\nimport Input from '../input';\nimport Button from '../button';\nimport ButtonGroup from '../buttonGroup';\nimport { ButtonSize } from '../button/button.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { useCoarsePointer } from '../hooks/useCoarsePointer';\nimport { useStepRepeat } from './useStepRepeat';\nimport type { NumberInputLabels, NumberInputProps } from './numberInput.types';\n\nexport type { NumberInputProps, NumberInputArrowsLayout } from './numberInput.types';\n\ninterface StepButtonProps {\n label: string;\n icon: typeof faPlus;\n disabled: boolean;\n direction: 1 | -1;\n onStart: (direction: 1 | -1, multiplier: number) => void;\n onStop: () => void;\n}\n\n/** Press-and-hold behaviour shared by both stepper layouts. */\nconst DEFAULT_LABELS: Required<NumberInputLabels> = {\n increase: 'Increase',\n decrease: 'Decrease',\n};\n\nfunction stepButtonProps({ label, disabled, direction, onStart, onStop }: StepButtonProps) {\n return {\n // The input keeps the tab stop — it carries role=\"spinbutton\", which announces the value\n // and the arrow-key affordance, so two more tab stops per field would be noise.\n tabIndex: -1,\n disabled,\n 'aria-label': label,\n // pointerdown (not click) keeps focus in the field and responds immediately on touch.\n // Deliberately no setPointerCapture: capturing retargets the following click to the captured\n // element, past the node Button's ripple listens on. useStepRepeat watches the window for\n // the release instead.\n onPointerDown: (e: React.PointerEvent<HTMLElement>) => {\n if (e.button !== 0 && e.pointerType === 'mouse') return;\n e.preventDefault();\n onStart(direction, e.shiftKey ? 10 : 1);\n },\n onPointerUp: onStop,\n onPointerCancel: onStop,\n onContextMenu: (e: React.SyntheticEvent) => e.preventDefault(),\n };\n}\n\n/** Stacked-layout chevron: borderless, sits inside the field's border. */\nfunction ChevronStepButton(props: StepButtonProps): ReactElement {\n return (\n <button type='button' {...stepButtonProps(props)} className={styles.arrowBtn}>\n <FontAwesomeIcon icon={props.icon} aria-hidden='true' />\n </button>\n );\n}\n\n/** Split-layout stepper: a standalone control, so a real Button. */\nfunction SplitStepButton(props: StepButtonProps): ReactElement {\n const { disabled, ...handlers } = stepButtonProps(props);\n return (\n <Button {...handlers} className={styles.splitBtn} size={ButtonSize.Icon} disabled={disabled} icon={props.icon} />\n );\n}\n\nfunction NumberInput(props: NumberInputProps): ReactElement {\n const fvRef = React.useRef<FormValidator | null | undefined>(undefined);\n const {\n value,\n onChange,\n formValidator,\n label,\n error,\n className,\n style,\n allowNegative = false,\n decimalSeparator,\n thousandSeparator,\n prefix,\n suffix,\n decimalScale,\n fixedDecimalScale = true,\n placeholder,\n sign,\n showArrows = false,\n step = 1,\n min,\n max,\n arrowsLayout = 'auto',\n labels,\n readOnly = false,\n disabled = false,\n isRequired = false,\n onKeyDown: onKeyDownProp,\n ...rest\n } = useComponentDefaults('NumberInput', props);\n const text = useLabels('NumberInput', labels, DEFAULT_LABELS);\n const [, forceUpdate] = React.useState(0);\n React.useEffect(() => {\n fvRef.current = formValidator;\n if (!formValidator) return;\n const listener = () => forceUpdate((n) => n + 1);\n formValidator.registerOnUpdateListener(listener);\n return () => {\n formValidator.removeOnUpdateListener(listener);\n };\n }, [formValidator]);\n const computedIsRequired =\n isRequired || !!formValidator?.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required');\n const rawValue = formValidator != null ? (formValidator.value as number | string) : value;\n // When a sign is forced, display the absolute value — the prefix character carries the sign visually.\n const displayValue = sign != null && rawValue != null ? Math.abs(Number(rawValue)) : rawValue;\n const controlledValue = displayValue != null ? String(displayValue) : '';\n\n function handleValueChange(floatValue: number | undefined) {\n let out = floatValue;\n if (out !== undefined) {\n if (sign === '-') out = -Math.abs(out);\n if (sign === '+') out = Math.abs(out);\n }\n if (formValidator != null) formValidator.set(out ?? null);\n onChange?.(out);\n }\n\n function clamp(next: number): number {\n let out = next;\n if (!allowNegative && sign !== '-') out = Math.max(0, out);\n if (sign === '-') out = Math.min(0, out);\n if (min !== undefined) out = Math.max(min, out);\n if (max !== undefined) out = Math.min(max, out);\n return out;\n }\n\n const currentNumber = rawValue != null && rawValue !== '' ? Number(rawValue) : 0;\n\n /**\n * Called on blur and Enter, never per keystroke: rejecting an out-of-bounds edit as it is\n * typed swallows keystrokes on their way somewhere valid (with `max: 10`, typing `1` in front\n * of `8` to reach `18`). An empty field is skipped so a `min` is never typed in for the user.\n */\n function clampToBounds() {\n if (readOnly || disabled) return;\n if (rawValue == null || rawValue === '') return;\n const n = Number(rawValue);\n if (!Number.isFinite(n)) return;\n const next = clamp(n);\n if (next !== n) handleValueChange(next);\n }\n\n /** Returns whether the value moved, which is how useStepRepeat knows to end a hold at a bound. */\n function handleStep(delta: number): boolean {\n if (readOnly || disabled) return false;\n const next = clamp(currentNumber + delta);\n if (next === currentNumber) return false;\n handleValueChange(next);\n return true;\n }\n\n const { start: startRepeat, stop: stopRepeat } = useStepRepeat((delta) => handleStep(delta * step));\n\n function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {\n // Enter submits the surrounding form without ever blurring, so the blur clamp would not run.\n // Before the stepper guard, since the bounds apply with or without steppers.\n if (e.key === 'Enter') {\n clampToBounds();\n onKeyDownProp?.(e);\n return;\n }\n if (!showStepper) {\n onKeyDownProp?.(e);\n return;\n }\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n handleStep(e.shiftKey ? step * 10 : step);\n return;\n }\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n handleStep(e.shiftKey ? -step * 10 : -step);\n return;\n }\n onKeyDownProp?.(e);\n }\n\n const resolvedPrefix = sign === '-' ? '-' : sign === '+' ? '+' : prefix;\n\n const isCoarsePointer = useCoarsePointer();\n const showStepper = showArrows && !readOnly && !disabled;\n const isSplit = arrowsLayout === 'split' || (arrowsLayout === 'auto' && isCoarsePointer);\n\n const canIncrease = clamp(currentNumber + step) !== currentNumber;\n const canDecrease = clamp(currentNumber - step) !== currentNumber;\n\n const stepperProps = { onStart: startRepeat, onStop: stopRepeat };\n\n const stackedArrows =\n showStepper && !isSplit ? (\n <div className={styles.arrows}>\n <ChevronStepButton\n label={text.increase}\n icon={faChevronUp}\n disabled={!canIncrease}\n direction={1}\n {...stepperProps}\n />\n <ChevronStepButton\n label={text.decrease}\n icon={faChevronDown}\n disabled={!canDecrease}\n direction={-1}\n {...stepperProps}\n />\n </div>\n ) : null;\n\n // aria-* is spread onto NumericFormat, which forwards unknown props to the DOM input.\n // Input's own cloneElement doesn't set these keys, so they survive the clone.\n const spinButtonProps = showStepper\n ? {\n role: 'spinbutton',\n 'aria-valuenow': Number.isFinite(currentNumber) ? currentNumber : undefined,\n 'aria-valuemin': min,\n 'aria-valuemax': max,\n 'aria-valuetext': controlledValue || undefined,\n }\n : {};\n\n // Shared with the split group, whose buttons have to mirror the field's invalid state — they\n // form one continuous border.\n const isValid = formValidator ? (formValidator.touched ? !formValidator.hasError() : true) : !error;\n\n // In the split layout the ButtonGroup is the root, so the consumer's className and style\n // belong there rather than on the Input.\n const isSplitGroup = showStepper && isSplit;\n\n const input = (\n <Input\n label={label}\n className={cx(\n styles.numberInput,\n { [styles.numberInputSplit]: isSplitGroup },\n isSplitGroup ? undefined : className,\n )}\n style={isSplitGroup ? undefined : style}\n value={controlledValue}\n onChange={() => {}}\n formValidator={null}\n isRequired={computedIsRequired}\n onBlur={() => {\n clampToBounds();\n if (fvRef.current) fvRef.current.touched = true;\n }}\n isValid={isValid}\n errorMessage={\n formValidator ? (formValidator.touched ? (formValidator.getCurrentErrorMessage() ?? '') : '') : (error ?? '')\n }\n placeholder={placeholder}\n readOnly={readOnly}\n disabled={disabled}\n suffix={stackedArrows}\n // Input centres its suffix wrapper; this lets the chevrons claim the field's full height.\n classNames={stackedArrows ? { suffix: styles.arrowSuffix } : undefined}\n inputMode={decimalScale === 0 ? 'numeric' : 'decimal'}\n customInput={\n <NumericFormat\n onValueChange={(vals) => handleValueChange(vals.floatValue)}\n allowNegative={sign != null ? false : allowNegative}\n decimalSeparator={decimalSeparator}\n thousandSeparator={thousandSeparator}\n decimalScale={decimalScale}\n fixedDecimalScale={fixedDecimalScale}\n prefix={resolvedPrefix}\n suffix={suffix}\n onKeyDown={handleKeyDown}\n {...spinButtonProps}\n {...rest}\n />\n }\n />\n );\n\n if (!isSplitGroup) return input;\n\n return (\n // ButtonGroup collapses the doubled borders and flattens the inner corners, so the three\n // read as one control.\n <ButtonGroup className={cx(styles.split, { [styles.splitInvalid]: !isValid }, className)} style={style}>\n <SplitStepButton label={text.decrease} icon={faMinus} disabled={!canDecrease} direction={-1} {...stepperProps} />\n {input}\n <SplitStepButton label={text.increase} icon={faPlus} disabled={!canIncrease} direction={1} {...stepperProps} />\n </ButtonGroup>\n );\n}\n\nexport default NumberInput;\n"],"mappings":"0vBA8BA,IAAM,GAA8C,CAClD,SAAU,WACV,SAAU,UACZ,EAEA,SAAS,EAAgB,CAAE,QAAO,WAAU,YAAW,UAAS,UAA2B,CACzF,MAAO,CAGL,SAAU,GACV,WACA,aAAc,EAKd,cAAgB,GAAuC,EACjD,EAAE,SAAW,GAAK,EAAE,cAAgB,WACxC,EAAE,eAAe,EACjB,EAAQ,EAAW,EAAE,SAAW,GAAK,CAAC,EACxC,EACA,YAAa,EACb,gBAAiB,EACjB,cAAgB,GAA4B,EAAE,eAAe,CAC/D,CACF,CAGA,SAAS,EAAkB,EAAsC,CAC/D,OACE,EAAC,SAAD,CAAQ,KAAK,SAAS,GAAI,EAAgB,CAAK,EAAG,UAAW,EAAO,SAClE,SAAA,EAAC,EAAD,CAAiB,KAAM,EAAM,KAAM,cAAY,MAAQ,CAAA,CACjD,CAAA,CAEZ,CAGA,SAAS,EAAgB,EAAsC,CAC7D,GAAM,CAAE,WAAU,GAAG,GAAa,EAAgB,CAAK,EACvD,OACE,EAAC,EAAD,CAAQ,GAAI,EAAU,UAAW,EAAO,SAAU,KAAM,EAAW,KAAgB,WAAU,KAAM,EAAM,IAAO,CAAA,CAEpH,CAEA,SAAS,EAAY,EAAuC,CAC1D,IAAM,EAAQ,EAAM,OAAyC,IAAA,EAAS,EAChE,CACJ,QACA,WACA,gBACA,SACA,QACA,YACA,QACA,gBAAgB,GAChB,mBACA,qBACA,UACA,SACA,eACA,oBAAoB,GACpB,eACA,OACA,cAAa,GACb,OAAO,EACP,MACA,MACA,eAAe,OACf,UACA,WAAW,GACX,WAAW,GACX,cAAa,GACb,UAAW,EACX,GAAG,GACD,EAAqB,cAAe,CAAK,EACvC,EAAO,EAAU,cAAe,GAAQ,EAAc,EACtD,EAAG,GAAe,EAAM,SAAS,CAAC,EACxC,EAAM,cAAgB,CAEpB,GADA,EAAM,QAAU,EACZ,CAAC,EAAe,OACpB,IAAM,MAAiB,EAAa,GAAM,EAAI,CAAC,EAE/C,OADA,EAAc,yBAAyB,CAAQ,MAClC,CACX,EAAc,uBAAuB,CAAQ,CAC/C,CACF,EAAG,CAAC,CAAa,CAAC,EAClB,IAAM,EACJ,IAAc,CAAC,CAAC,GAAe,WAAW,KAAM,GAAO,EAA+B,cAAgB,UAAU,EAC5G,EAAW,GAAiB,KAAkD,EAA1C,EAAc,MAElD,EAAe,GAAQ,MAAQ,GAAY,KAAO,KAAK,IAAI,OAAO,CAAQ,CAAC,EAAI,EAC/E,EAAkB,GAAgB,KAA8B,GAAvB,OAAO,CAAY,EAElE,SAAS,EAAkB,EAAgC,CACzD,IAAI,EAAM,EACN,IAAQ,IAAA,KACN,IAAS,MAAK,EAAM,CAAC,KAAK,IAAI,CAAG,GACjC,IAAS,MAAK,EAAM,KAAK,IAAI,CAAG,IAElC,GAAqC,IAAI,GAAO,IAAI,EACxD,IAAW,CAAG,CAChB,CAEA,SAAS,EAAM,EAAsB,CACnC,IAAI,EAAM,EAKV,MAJI,CAAC,GAAiB,IAAS,MAAK,EAAM,KAAK,IAAI,EAAG,CAAG,GACrD,IAAS,MAAK,EAAM,KAAK,IAAI,EAAG,CAAG,GACnC,IAAQ,IAAA,KAAW,EAAM,KAAK,IAAI,EAAK,CAAG,GAC1C,IAAQ,IAAA,KAAW,EAAM,KAAK,IAAI,EAAK,CAAG,GACvC,CACT,CAEA,IAAM,EAAgB,GAAY,MAAQ,IAAa,GAAK,OAAO,CAAQ,EAAI,EAO/E,SAAS,GAAgB,CAEvB,GADI,GAAY,GACZ,GAAY,MAAQ,IAAa,GAAI,OACzC,IAAM,EAAI,OAAO,CAAQ,EACzB,GAAI,CAAC,OAAO,SAAS,CAAC,EAAG,OACzB,IAAM,EAAO,EAAM,CAAC,EAChB,IAAS,GAAG,EAAkB,CAAI,CACxC,CAGA,SAAS,EAAW,EAAwB,CAC1C,GAAI,GAAY,EAAU,MAAO,GACjC,IAAM,EAAO,EAAM,EAAgB,CAAK,EAGxC,OAFI,IAAS,IACb,EAAkB,CAAI,EACf,GACT,CAEA,GAAM,CAAE,MAAO,GAAa,KAAM,IAAe,GAAe,GAAU,EAAW,EAAQ,CAAI,CAAC,EAElG,SAAS,GAAc,EAA0C,CAG/D,GAAI,EAAE,MAAQ,QAAS,CACrB,EAAc,EACd,IAAgB,CAAC,EACjB,MACF,CACA,GAAI,CAAC,EAAa,CAChB,IAAgB,CAAC,EACjB,MACF,CACA,GAAI,EAAE,MAAQ,UAAW,CACvB,EAAE,eAAe,EACjB,EAAW,EAAE,SAAW,EAAO,GAAK,CAAI,EACxC,MACF,CACA,GAAI,EAAE,MAAQ,YAAa,CACzB,EAAE,eAAe,EACjB,EAAW,EAAE,SAAW,CAAC,EAAO,GAAK,CAAC,CAAI,EAC1C,MACF,CACA,IAAgB,CAAC,CACnB,CAEA,IAAM,GAAiB,IAAS,IAAM,IAAM,IAAS,IAAM,IAAM,GAE3D,GAAkB,EAAiB,EACnC,EAAc,IAAc,CAAC,GAAY,CAAC,EAC1C,EAAU,IAAiB,SAAY,IAAiB,QAAU,GAElE,EAAc,EAAM,EAAgB,CAAI,IAAM,EAC9C,EAAc,EAAM,EAAgB,CAAI,IAAM,EAE9C,EAAe,CAAE,QAAS,GAAa,OAAQ,EAAW,EAE1D,EACJ,GAAe,CAAC,EACd,EAAC,MAAD,CAAK,UAAW,EAAO,OAAvB,SAAA,CACE,EAAC,EAAD,CACE,MAAO,EAAK,SACZ,KAAM,GACN,SAAU,CAAC,EACX,UAAW,EACX,GAAI,CACL,CAAA,EACD,EAAC,EAAD,CACE,MAAO,EAAK,SACZ,KAAM,EACN,SAAU,CAAC,EACX,UAAW,GACX,GAAI,CACL,CAAA,CACE,CACH,CAAA,EAAA,KAIA,GAAkB,EACpB,CACE,KAAM,aACN,gBAAiB,OAAO,SAAS,CAAa,EAAI,EAAgB,IAAA,GAClE,gBAAiB,EACjB,gBAAiB,EACjB,iBAAkB,GAAmB,IAAA,EACvC,EACA,CAAC,EAIC,EAAU,EAAiB,GAAc,SAAU,CAAC,EAAc,SAAS,EAAY,CAAC,EAIxF,EAAe,GAAe,EAE9B,EACJ,EAAC,GAAD,CACS,SACP,UAAW,EACT,EAAO,YACP,EAAG,EAAO,kBAAmB,CAAa,EAC1C,EAAe,IAAA,GAAY,CAC7B,EACA,MAAO,EAAe,IAAA,GAAY,EAClC,MAAO,EACP,aAAgB,CAAC,EACjB,cAAe,KACf,WAAY,EACZ,WAAc,CACZ,EAAc,EACV,EAAM,UAAS,EAAM,QAAQ,QAAU,GAC7C,EACS,UACT,aACE,EAAiB,EAAc,QAAW,EAAc,uBAAuB,GAAK,GAAM,GAAO,GAAS,GAE/F,eACH,WACA,WACV,OAAQ,EAER,WAAY,EAAgB,CAAE,OAAQ,EAAO,WAAY,EAAI,IAAA,GAC7D,UAAW,IAAiB,EAAI,UAAY,UAC5C,YACE,EAAC,GAAD,CACE,cAAgB,GAAS,EAAkB,EAAK,UAAU,EAC1D,cAAe,GAAQ,MAAe,EACpB,mBACC,qBACL,eACK,oBACnB,OAAQ,GACA,SACR,UAAW,GACX,GAAI,GACJ,GAAI,CACL,CAAA,CAEJ,CAAA,EAKH,OAFK,EAKH,EAAC,GAAD,CAAa,UAAW,EAAG,EAAO,MAAO,EAAG,EAAO,cAAe,CAAC,CAAQ,EAAG,CAAS,EAAU,QAAjG,SAAA,CACE,EAAC,EAAD,CAAiB,MAAO,EAAK,SAAU,KAAM,EAAS,SAAU,CAAC,EAAa,UAAW,GAAI,GAAI,CAAe,CAAA,EAC/G,EACD,EAAC,EAAD,CAAiB,MAAO,EAAK,SAAU,KAAM,GAAQ,SAAU,CAAC,EAAa,UAAW,EAAG,GAAI,CAAe,CAAA,CACnG,IATW,CAW5B"}
|
|
1
|
+
{"version":3,"file":"numberInput.mjs","names":[],"sources":["../../../../package/common/numberInput/numberInput.tsx"],"sourcesContent":["import type { CSSProperties, ReactElement } from 'react';\nimport React from 'react';\nimport cx from 'classnames';\nimport { NumericFormat } from 'react-number-format';\nimport type { FormValidator } from 'services/formValidation';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronUp, faChevronDown, faMinus, faPlus } from '@fortawesome/free-solid-svg-icons';\n\nimport styles from './numberInput.module.pcss';\nimport Input from '../input';\nimport Button from '../button';\nimport ButtonGroup from '../buttonGroup';\nimport { ButtonSize } from '../button/button.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { useCoarsePointer } from '../hooks/useCoarsePointer';\nimport { useStepRepeat } from './useStepRepeat';\nimport type { NumberInputLabels, NumberInputProps } from './numberInput.types';\n\nexport type { NumberInputProps, NumberInputArrowsLayout } from './numberInput.types';\n\ninterface StepButtonProps {\n label: string;\n className?: string;\n style?: CSSProperties;\n icon: typeof faPlus;\n disabled: boolean;\n direction: 1 | -1;\n onStart: (direction: 1 | -1, multiplier: number) => void;\n onStop: () => void;\n}\n\n/** Press-and-hold behaviour shared by both stepper layouts. */\nconst DEFAULT_LABELS: Required<NumberInputLabels> = {\n increase: 'Increase',\n decrease: 'Decrease',\n};\n\nfunction stepButtonProps({ label, disabled, direction, onStart, onStop }: StepButtonProps) {\n return {\n // The input keeps the tab stop — it carries role=\"spinbutton\", which announces the value\n // and the arrow-key affordance, so two more tab stops per field would be noise.\n tabIndex: -1,\n disabled,\n 'aria-label': label,\n // pointerdown (not click) keeps focus in the field and responds immediately on touch.\n // Deliberately no setPointerCapture: capturing retargets the following click to the captured\n // element, past the node Button's ripple listens on. useStepRepeat watches the window for\n // the release instead.\n onPointerDown: (e: React.PointerEvent<HTMLElement>) => {\n if (e.button !== 0 && e.pointerType === 'mouse') return;\n e.preventDefault();\n onStart(direction, e.shiftKey ? 10 : 1);\n },\n onPointerUp: onStop,\n onPointerCancel: onStop,\n onContextMenu: (e: React.SyntheticEvent) => e.preventDefault(),\n };\n}\n\n/** Stacked-layout chevron: borderless, sits inside the field's border. */\nfunction ChevronStepButton(props: StepButtonProps): ReactElement {\n return (\n <button\n type='button'\n {...stepButtonProps(props)}\n className={cx(styles.arrowBtn, props.className)}\n style={props.style}\n >\n <FontAwesomeIcon icon={props.icon} aria-hidden='true' />\n </button>\n );\n}\n\n/** Split-layout stepper: a standalone control, so a real Button. */\nfunction SplitStepButton(props: StepButtonProps): ReactElement {\n const { disabled, ...handlers } = stepButtonProps(props);\n return (\n <Button\n {...handlers}\n className={cx(styles.splitBtn, props.className)}\n style={props.style}\n size={ButtonSize.Icon}\n disabled={disabled}\n icon={props.icon}\n />\n );\n}\n\nfunction NumberInput(props: NumberInputProps): ReactElement {\n const fvRef = React.useRef<FormValidator | null | undefined>(undefined);\n const {\n value,\n onChange,\n formValidator,\n label,\n error,\n className,\n style,\n classNames,\n styles: slotStyles,\n allowNegative = false,\n decimalSeparator,\n thousandSeparator,\n prefix,\n suffix,\n decimalScale,\n fixedDecimalScale = true,\n placeholder,\n sign,\n showArrows = false,\n step = 1,\n min,\n max,\n arrowsLayout = 'auto',\n labels,\n readOnly = false,\n disabled = false,\n isRequired = false,\n onKeyDown: onKeyDownProp,\n ...rest\n } = useComponentDefaults('NumberInput', props);\n const text = useLabels('NumberInput', labels, DEFAULT_LABELS);\n const [, forceUpdate] = React.useState(0);\n React.useEffect(() => {\n fvRef.current = formValidator;\n if (!formValidator) return;\n const listener = () => forceUpdate((n) => n + 1);\n formValidator.registerOnUpdateListener(listener);\n return () => {\n formValidator.removeOnUpdateListener(listener);\n };\n }, [formValidator]);\n const computedIsRequired =\n isRequired || !!formValidator?.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required');\n const rawValue = formValidator != null ? (formValidator.value as number | string) : value;\n // When a sign is forced, display the absolute value — the prefix character carries the sign visually.\n const displayValue = sign != null && rawValue != null ? Math.abs(Number(rawValue)) : rawValue;\n const controlledValue = displayValue != null ? String(displayValue) : '';\n\n function handleValueChange(floatValue: number | undefined) {\n let out = floatValue;\n if (out !== undefined) {\n if (sign === '-') out = -Math.abs(out);\n if (sign === '+') out = Math.abs(out);\n }\n if (formValidator != null) formValidator.set(out ?? null);\n onChange?.(out);\n }\n\n function clamp(next: number): number {\n let out = next;\n if (!allowNegative && sign !== '-') out = Math.max(0, out);\n if (sign === '-') out = Math.min(0, out);\n if (min !== undefined) out = Math.max(min, out);\n if (max !== undefined) out = Math.min(max, out);\n return out;\n }\n\n const currentNumber = rawValue != null && rawValue !== '' ? Number(rawValue) : 0;\n\n /**\n * Called on blur and Enter, never per keystroke: rejecting an out-of-bounds edit as it is\n * typed swallows keystrokes on their way somewhere valid (with `max: 10`, typing `1` in front\n * of `8` to reach `18`). An empty field is skipped so a `min` is never typed in for the user.\n */\n function clampToBounds() {\n if (readOnly || disabled) return;\n if (rawValue == null || rawValue === '') return;\n const n = Number(rawValue);\n if (!Number.isFinite(n)) return;\n const next = clamp(n);\n if (next !== n) handleValueChange(next);\n }\n\n /** Returns whether the value moved, which is how useStepRepeat knows to end a hold at a bound. */\n function handleStep(delta: number): boolean {\n if (readOnly || disabled) return false;\n const next = clamp(currentNumber + delta);\n if (next === currentNumber) return false;\n handleValueChange(next);\n return true;\n }\n\n const { start: startRepeat, stop: stopRepeat } = useStepRepeat((delta) => handleStep(delta * step));\n\n function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {\n // Enter submits the surrounding form without ever blurring, so the blur clamp would not run.\n // Before the stepper guard, since the bounds apply with or without steppers.\n if (e.key === 'Enter') {\n clampToBounds();\n onKeyDownProp?.(e);\n return;\n }\n if (!showStepper) {\n onKeyDownProp?.(e);\n return;\n }\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n handleStep(e.shiftKey ? step * 10 : step);\n return;\n }\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n handleStep(e.shiftKey ? -step * 10 : -step);\n return;\n }\n onKeyDownProp?.(e);\n }\n\n const resolvedPrefix = sign === '-' ? '-' : sign === '+' ? '+' : prefix;\n\n const isCoarsePointer = useCoarsePointer();\n const showStepper = showArrows && !readOnly && !disabled;\n const isSplit = arrowsLayout === 'split' || (arrowsLayout === 'auto' && isCoarsePointer);\n\n const canIncrease = clamp(currentNumber + step) !== currentNumber;\n const canDecrease = clamp(currentNumber - step) !== currentNumber;\n\n const stepperProps = { onStart: startRepeat, onStop: stopRepeat };\n\n const stackedArrows =\n showStepper && !isSplit ? (\n <div className={cx(styles.arrows, classNames?.arrows)} style={slotStyles?.arrows}>\n <ChevronStepButton\n className={classNames?.increaseButton}\n style={slotStyles?.increaseButton}\n label={text.increase}\n icon={faChevronUp}\n disabled={!canIncrease}\n direction={1}\n {...stepperProps}\n />\n <ChevronStepButton\n className={classNames?.decreaseButton}\n style={slotStyles?.decreaseButton}\n label={text.decrease}\n icon={faChevronDown}\n disabled={!canDecrease}\n direction={-1}\n {...stepperProps}\n />\n </div>\n ) : null;\n\n // aria-* is spread onto NumericFormat, which forwards unknown props to the DOM input.\n // Input's own cloneElement doesn't set these keys, so they survive the clone.\n const spinButtonProps = showStepper\n ? {\n role: 'spinbutton',\n 'aria-valuenow': Number.isFinite(currentNumber) ? currentNumber : undefined,\n 'aria-valuemin': min,\n 'aria-valuemax': max,\n 'aria-valuetext': controlledValue || undefined,\n }\n : {};\n\n // Shared with the split group, whose buttons have to mirror the field's invalid state — they\n // form one continuous border.\n const isValid = formValidator ? (formValidator.touched ? !formValidator.hasError() : true) : !error;\n\n // In the split layout the ButtonGroup is the root, so the consumer's className and style\n // belong there rather than on the Input.\n const isSplitGroup = showStepper && isSplit;\n\n const input = (\n <Input\n label={label}\n className={cx(\n styles.numberInput,\n { [styles.numberInputSplit]: isSplitGroup },\n // In split mode the ButtonGroup around all three is the root, so the\n // consumer's root class belongs there rather than on the field.\n isSplitGroup ? undefined : className,\n isSplitGroup ? undefined : classNames?.root,\n classNames?.input,\n )}\n style={{\n ...(isSplitGroup ? undefined : style),\n ...(isSplitGroup ? undefined : slotStyles?.root),\n ...slotStyles?.input,\n }}\n value={controlledValue}\n onChange={() => {}}\n formValidator={null}\n isRequired={computedIsRequired}\n onBlur={() => {\n clampToBounds();\n if (fvRef.current) fvRef.current.touched = true;\n }}\n isValid={isValid}\n errorMessage={\n formValidator ? (formValidator.touched ? (formValidator.getCurrentErrorMessage() ?? '') : '') : (error ?? '')\n }\n placeholder={placeholder}\n readOnly={readOnly}\n disabled={disabled}\n suffix={stackedArrows}\n // Input centres its suffix wrapper; this lets the chevrons claim the field's full height.\n classNames={stackedArrows ? { suffix: styles.arrowSuffix } : undefined}\n inputMode={decimalScale === 0 ? 'numeric' : 'decimal'}\n customInput={\n <NumericFormat\n onValueChange={(vals) => handleValueChange(vals.floatValue)}\n allowNegative={sign != null ? false : allowNegative}\n decimalSeparator={decimalSeparator}\n thousandSeparator={thousandSeparator}\n decimalScale={decimalScale}\n fixedDecimalScale={fixedDecimalScale}\n prefix={resolvedPrefix}\n suffix={suffix}\n onKeyDown={handleKeyDown}\n {...spinButtonProps}\n {...rest}\n />\n }\n />\n );\n\n if (!isSplitGroup) return input;\n\n return (\n // ButtonGroup collapses the doubled borders and flattens the inner corners, so the three\n // read as one control.\n <ButtonGroup\n className={cx(styles.split, { [styles.splitInvalid]: !isValid }, className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n >\n <SplitStepButton\n className={classNames?.decreaseButton}\n style={slotStyles?.decreaseButton}\n label={text.decrease}\n icon={faMinus}\n disabled={!canDecrease}\n direction={-1}\n {...stepperProps}\n />\n {input}\n <SplitStepButton\n className={classNames?.increaseButton}\n style={slotStyles?.increaseButton}\n label={text.increase}\n icon={faPlus}\n disabled={!canIncrease}\n direction={1}\n {...stepperProps}\n />\n </ButtonGroup>\n );\n}\n\nexport default NumberInput;\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e from"./progressBar.module.mjs";import t from"classnames";import{jsx as n}from"react/jsx-runtime";function r(e){let t=e.replace(`#`,``),n=parseInt(t.substring(0,2),16),r=parseInt(t.substring(2,4),16),i=parseInt(t.substring(4,6),16);return(.299*n+.587*r+.114*i)/255>.5?`#000000`:`#ffffff`}function i({className:i=void 0,total:a=100,progress:o=0,style:s=void 0,...
|
|
1
|
+
import e from"./progressBar.module.mjs";import t from"classnames";import{jsx as n}from"react/jsx-runtime";function r(e){let t=e.replace(`#`,``),n=parseInt(t.substring(0,2),16),r=parseInt(t.substring(2,4),16),i=parseInt(t.substring(4,6),16);return(.299*n+.587*r+.114*i)/255>.5?`#000000`:`#ffffff`}function i({className:i=void 0,total:a=100,progress:o=0,style:s=void 0,classNames:c,styles:l,...u}){function d(){let i=[];return Array.isArray(o)?i.push(...o):typeof o==`object`?i.push(o):i.push({amount:o}),i.map((o,s)=>{let u=a?o.amount/a*100:0;return n(`div`,{className:t(e.progressBarFill,s===i.length-1&&e.progressBarFillEnd,o.class,c?.fill),style:{width:`${u}%`,background:o.color||void 0,...l?.fill},title:o.label?`${o.label}: ${o.amount}`:void 0,children:o.label&&n(`div`,{className:t(e.progressBarLabel,c?.label),style:{...o.color?{color:r(o.color)}:{},...l?.label},children:o.label})},s)})}return n(`div`,{...u,className:t(e.progressBar,i,c?.root),style:{...s,...l?.root},children:d()})}export{i as default,r as getContrastColor};
|
|
2
2
|
//# sourceMappingURL=progressBar.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progressBar.mjs","names":[],"sources":["../../../../package/common/progressBar/progressBar.tsx"],"sourcesContent":["import type { ReactElement } from 'react';\nimport cx from 'classnames';\nimport { Progress, ProgressBarProps } from './progressBar.types';\n\nimport styles from './progressBar.module.pcss';\n\nexport function getContrastColor(hexColor: string): string {\n const hex = hexColor.replace('#', '');\n const r = parseInt(hex.substring(0, 2), 16);\n const g = parseInt(hex.substring(2, 4), 16);\n const b = parseInt(hex.substring(4, 6), 16);\n\n const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;\n return luminance > 0.5 ? '#000000' : '#ffffff';\n}\n\nfunction ProgressBar({\n className = undefined,\n total = 100,\n progress = 0,\n style = undefined,\n ...rest\n}: ProgressBarProps): ReactElement {\n function renderProgressBars() {\n const progressArray: Progress[] = [];\n\n if (Array.isArray(progress)) progressArray.push(...progress);\n else if (typeof progress === 'object') progressArray.push(progress);\n else progressArray.push({ amount: progress });\n\n return progressArray.map((prog, index) => {\n const widthPercentage = !total ? 0 : (prog.amount / total) * 100;\n\n return (\n <div\n key={index}\n className={cx(\n styles.progressBarFill,\n index === progressArray.length - 1 && styles.progressBarFillEnd,\n prog.class,\n )}\n style={{\n width: `${widthPercentage}%`,\n background: prog.color || undefined,\n ...
|
|
1
|
+
{"version":3,"file":"progressBar.mjs","names":[],"sources":["../../../../package/common/progressBar/progressBar.tsx"],"sourcesContent":["import type { ReactElement } from 'react';\nimport cx from 'classnames';\nimport { Progress, ProgressBarProps } from './progressBar.types';\n\nimport styles from './progressBar.module.pcss';\n\nexport function getContrastColor(hexColor: string): string {\n const hex = hexColor.replace('#', '');\n const r = parseInt(hex.substring(0, 2), 16);\n const g = parseInt(hex.substring(2, 4), 16);\n const b = parseInt(hex.substring(4, 6), 16);\n\n const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;\n return luminance > 0.5 ? '#000000' : '#ffffff';\n}\n\nfunction ProgressBar({\n className = undefined,\n total = 100,\n progress = 0,\n style = undefined,\n classNames,\n styles: slotStyles,\n ...rest\n}: ProgressBarProps): ReactElement {\n function renderProgressBars() {\n const progressArray: Progress[] = [];\n\n if (Array.isArray(progress)) progressArray.push(...progress);\n else if (typeof progress === 'object') progressArray.push(progress);\n else progressArray.push({ amount: progress });\n\n return progressArray.map((prog, index) => {\n const widthPercentage = !total ? 0 : (prog.amount / total) * 100;\n\n return (\n <div\n key={index}\n className={cx(\n styles.progressBarFill,\n index === progressArray.length - 1 && styles.progressBarFillEnd,\n prog.class,\n classNames?.fill,\n )}\n style={{\n width: `${widthPercentage}%`,\n background: prog.color || undefined,\n ...slotStyles?.fill,\n }}\n title={prog.label ? `${prog.label}: ${prog.amount}` : undefined}\n >\n {prog.label && (\n <div\n className={cx(styles.progressBarLabel, classNames?.label)}\n style={{ ...(prog.color ? { color: getContrastColor(prog.color) } : {}), ...slotStyles?.label }}\n >\n {prog.label}\n </div>\n )}\n </div>\n );\n });\n }\n\n return (\n <div\n {...rest}\n className={cx(styles.progressBar, className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n >\n {renderProgressBars()}\n </div>\n );\n}\n\nexport default ProgressBar;\n"],"mappings":"0GAMA,SAAgB,EAAiB,EAA0B,CACzD,IAAM,EAAM,EAAS,QAAQ,IAAK,EAAE,EAC9B,EAAI,SAAS,EAAI,UAAU,EAAG,CAAC,EAAG,EAAE,EACpC,EAAI,SAAS,EAAI,UAAU,EAAG,CAAC,EAAG,EAAE,EACpC,EAAI,SAAS,EAAI,UAAU,EAAG,CAAC,EAAG,EAAE,EAG1C,OADmB,KAAQ,EAAI,KAAQ,EAAI,KAAQ,GAAK,IACrC,GAAM,UAAY,SACvC,CAEA,SAAS,EAAY,CACnB,YAAY,IAAA,GACZ,QAAQ,IACR,WAAW,EACX,QAAQ,IAAA,GACR,aACA,OAAQ,EACR,GAAG,GAC8B,CACjC,SAAS,GAAqB,CAC5B,IAAM,EAA4B,CAAC,EAMnC,OAJI,MAAM,QAAQ,CAAQ,EAAG,EAAc,KAAK,GAAG,CAAQ,EAClD,OAAO,GAAa,SAAU,EAAc,KAAK,CAAQ,EAC7D,EAAc,KAAK,CAAE,OAAQ,CAAS,CAAC,EAErC,EAAc,KAAK,EAAM,IAAU,CACxC,IAAM,EAAmB,EAAa,EAAK,OAAS,EAAS,IAA5B,EAEjC,OACE,EAAC,MAAD,CAEE,UAAW,EACT,EAAO,gBACP,IAAU,EAAc,OAAS,GAAK,EAAO,mBAC7C,EAAK,MACL,GAAY,IACd,EACA,MAAO,CACL,MAAO,GAAG,EAAgB,GAC1B,WAAY,EAAK,OAAS,IAAA,GAC1B,GAAG,GAAY,IACjB,EACA,MAAO,EAAK,MAAQ,GAAG,EAAK,MAAM,IAAI,EAAK,SAAW,IAAA,GAErD,SAAA,EAAK,OACJ,EAAC,MAAD,CACE,UAAW,EAAG,EAAO,iBAAkB,GAAY,KAAK,EACxD,MAAO,CAAE,GAAI,EAAK,MAAQ,CAAE,MAAO,EAAiB,EAAK,KAAK,CAAE,EAAI,CAAC,EAAI,GAAG,GAAY,KAAM,EAE7F,SAAA,EAAK,KACH,CAAA,CAEJ,EAtBE,CAsBF,CAET,CAAC,CACH,CAEA,OACE,EAAC,MAAD,CACE,GAAI,EACJ,UAAW,EAAG,EAAO,YAAa,EAAW,GAAY,IAAI,EAC7D,MAAO,CAAE,GAAG,EAAO,GAAG,GAAY,IAAK,EAEtC,SAAA,EAAmB,CACjB,CAAA,CAET"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{useLabels as e}from"../configProvider/useLabels.mjs";import{InputType as t}from"../input/input.types.mjs";import n from"../input/input.mjs";import r from"./searchInput.module.mjs";import{useEffect as i,useRef as a}from"react";import o from"classnames";import{jsx as s,jsxs as c}from"react/jsx-runtime";var l={results:e=>`${e} ${e===1?`result`:`results`}`};function u({value:u,onChange:d,onDebounce:f,placeholder:p,label:m,debounceMs:h=500,totalAmount:g,showAmount:_=!0,className:v,width:y,style:b,classNames:x,styles:S,labels:C,...w}){let T=e(`SearchInput`,C,l),E=a(null);i(()=>()=>{E.current!==null&&clearTimeout(E.current)},[]);function D(e){let t=e==null?``:String(e),n=t.trim()===``?``:t;d(n),E.current!==null&&clearTimeout(E.current),E.current=setTimeout(()=>{f?.(n)},h)}let O=_&&typeof g==`number`&&!Number.isNaN(g);return c(`div`,{...w,className:o(v,x?.root),style:{...y==null?void 0:{width:y},...b,...S?.root},children:[s(n,{autoComplete:`off`,className:x?.input,style:S?.input,type:t.Search,label:m,placeholder:p,value:u,onChange:D}),O&&s(`p`,{className:o(r.resultCount,x?.resultCount),style:S?.resultCount,children:T.results(g)})]})}export{u as default};
|
|
2
2
|
//# sourceMappingURL=searchInput.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"searchInput.mjs","names":[],"sources":["../../../../package/common/searchInput/searchInput.tsx"],"sourcesContent":["import { useEffect, useRef, type ReactElement } from 'react';\nimport Input from 'components/common/input';\nimport { InputType } from 'components/common/input/input.types';\nimport styles from './searchInput.module.pcss';\nimport type { SearchInputProps } from './searchInput.types';\n\nexport default function SearchInput({\n value,\n onChange,\n onDebounce,\n placeholder,\n label,\n debounceMs = 500,\n totalAmount,\n showAmount = true,\n className,\n width,\n ...rest\n}: SearchInputProps): ReactElement {\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(\n () => () => {\n if (timerRef.current !== null) clearTimeout(timerRef.current);\n },\n [],\n );\n\n function handleChange(val: unknown) {\n const str = val != null ? String(val) : '';\n const trimmed = str.trim() === '' ? '' : str;\n onChange(trimmed);\n if (timerRef.current !== null) clearTimeout(timerRef.current);\n timerRef.current = setTimeout(() => {\n onDebounce?.(trimmed);\n }, debounceMs);\n }\n\n const showCount = showAmount && typeof totalAmount === 'number' && !Number.isNaN(totalAmount);\n\n return (\n <div
|
|
1
|
+
{"version":3,"file":"searchInput.mjs","names":[],"sources":["../../../../package/common/searchInput/searchInput.tsx"],"sourcesContent":["import { useEffect, useRef, type ReactElement } from 'react';\nimport cx from 'classnames';\nimport Input from 'components/common/input';\nimport { InputType } from 'components/common/input/input.types';\nimport styles from './searchInput.module.pcss';\nimport { useLabels } from '../configProvider';\nimport type { SearchInputLabels, SearchInputProps } from './searchInput.types';\n\nconst DEFAULT_LABELS: Required<SearchInputLabels> = {\n results: (count) => `${count} ${count === 1 ? 'result' : 'results'}`,\n};\n\nexport default function SearchInput({\n value,\n onChange,\n onDebounce,\n placeholder,\n label,\n debounceMs = 500,\n totalAmount,\n showAmount = true,\n className,\n width,\n style,\n classNames,\n styles: slotStyles,\n labels,\n ...rest\n}: SearchInputProps): ReactElement {\n const text = useLabels('SearchInput', labels, DEFAULT_LABELS);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(\n () => () => {\n if (timerRef.current !== null) clearTimeout(timerRef.current);\n },\n [],\n );\n\n function handleChange(val: unknown) {\n const str = val != null ? String(val) : '';\n const trimmed = str.trim() === '' ? '' : str;\n onChange(trimmed);\n if (timerRef.current !== null) clearTimeout(timerRef.current);\n timerRef.current = setTimeout(() => {\n onDebounce?.(trimmed);\n }, debounceMs);\n }\n\n const showCount = showAmount && typeof totalAmount === 'number' && !Number.isNaN(totalAmount);\n\n return (\n <div\n {...rest}\n className={cx(className, classNames?.root)}\n style={{ ...(width != null ? { width } : undefined), ...style, ...slotStyles?.root }}\n >\n <Input\n autoComplete='off'\n className={classNames?.input}\n style={slotStyles?.input}\n type={InputType.Search}\n label={label}\n placeholder={placeholder}\n value={value}\n onChange={handleChange}\n />\n {showCount && (\n <p className={cx(styles.resultCount, classNames?.resultCount)} style={slotStyles?.resultCount}>\n {text.results(totalAmount)}\n </p>\n )}\n </div>\n );\n}\n"],"mappings":"qTAQA,IAAM,EAA8C,CAClD,QAAU,GAAU,GAAG,EAAM,GAAG,IAAU,EAAI,SAAW,WAC3D,EAEA,SAAwB,EAAY,CAClC,QACA,WACA,aACA,cACA,QACA,aAAa,IACb,cACA,aAAa,GACb,YACA,QACA,QACA,aACA,OAAQ,EACR,SACA,GAAG,GAC8B,CACjC,IAAM,EAAO,EAAU,cAAe,EAAQ,CAAc,EACtD,EAAW,EAA6C,IAAI,EAElE,UACc,CACN,EAAS,UAAY,MAAM,aAAa,EAAS,OAAO,CAC9D,EACA,CAAC,CACH,EAEA,SAAS,EAAa,EAAc,CAClC,IAAM,EAAM,GAAO,KAAqB,GAAd,OAAO,CAAG,EAC9B,EAAU,EAAI,KAAK,IAAM,GAAK,GAAK,EACzC,EAAS,CAAO,EACZ,EAAS,UAAY,MAAM,aAAa,EAAS,OAAO,EAC5D,EAAS,QAAU,eAAiB,CAClC,IAAa,CAAO,CACtB,EAAG,CAAU,CACf,CAEA,IAAM,EAAY,GAAc,OAAO,GAAgB,UAAY,CAAC,OAAO,MAAM,CAAW,EAE5F,OACE,EAAC,MAAD,CACE,GAAI,EACJ,UAAW,EAAG,EAAW,GAAY,IAAI,EACzC,MAAO,CAAE,GAAI,GAAS,KAAmB,IAAA,GAAZ,CAAE,OAAM,EAAgB,GAAG,EAAO,GAAG,GAAY,IAAK,EAHrF,SAAA,CAKE,EAAC,EAAD,CACE,aAAa,MACb,UAAW,GAAY,MACvB,MAAO,GAAY,MACnB,KAAM,EAAU,OACT,QACM,cACN,QACP,SAAU,CACX,CAAA,EACA,GACC,EAAC,IAAD,CAAG,UAAW,EAAG,EAAO,YAAa,GAAY,WAAW,EAAG,MAAO,GAAY,YAC/E,SAAA,EAAK,QAAQ,CAAW,CACxB,CAAA,CAEF,GAET"}
|