UNPKG

@dnb/eufemia

Version:

DNB Eufemia Design System UI Library

308 lines (307 loc) 11.2 kB
"use strict"; "use client"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = require("react"); var _reactDom = require("react-dom"); var _clsx = require("clsx"); var _withComponentMarkers = _interopRequireDefault(require("../../../shared/helpers/withComponentMarkers.js")); var _useId = _interopRequireDefault(require("../../../shared/helpers/useId.js")); var _Input = _interopRequireDefault(require("../../Input.js")); var _FormLabel = _interopRequireDefault(require("../../FormLabel.js")); var _SpacingUtils = require("../../space/SpacingUtils.js"); var _useSegmentedFieldValues = require("../hooks/useSegmentedFieldValues.js"); var _SegmentedFieldSection = _interopRequireDefault(require("./SegmentedFieldSection.js")); var _dom = require("./dom.js"); var _utils = require("./utils.js"); var _jsxRuntime = require("react/jsx-runtime"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function SegmentedField(props) { const fallbackId = (0, _useId.default)(props?.id); const fallbackFieldsetRef = (0, _react.useRef)(null); const { id = fallbackId, label, labelDirection = 'vertical', inputs, delimiter, onChange: onChangeExternal, disabled, status, statusState, values: defaultValues, className, stretch, _omitInputShellClass, scopeRef = fallbackFieldsetRef, size, suffix, onBlur, onFocus, overwriteMode = 'shift', optionsEnhancer, ...rest } = props; const hasExternalScopeRef = Boolean(props.scopeRef); const [values, onChangeBase] = (0, _useSegmentedFieldValues.useSegmentedFieldValues)({ inputs, defaultValues }); const valuesRef = (0, _react.useRef)(values); const sectionRefs = (0, _react.useRef)({}); const caretPositionsRef = (0, _react.useRef)({}); const sectionSelectionModeRef = (0, _react.useRef)({}); const areInputsInFocus = (0, _react.useRef)(false); const [wholeGroupSelectionUi, setWholeGroupSelectionUi] = (0, _react.useState)(false); const wholeGroupSelectionUiRef = (0, _react.useRef)(false); wholeGroupSelectionUiRef.current = wholeGroupSelectionUi; valuesRef.current = values; (0, _react.useEffect)(() => { optionsEnhancer?.({ overwriteMode }); }, [optionsEnhancer, overwriteMode]); const onChange = (0, _react.useCallback)((inputId, value) => { const updatedValues = { ...valuesRef.current, [inputId]: value }; valuesRef.current = updatedValues; onChangeBase(updatedValues); if (typeof onChangeExternal === 'function') { onChangeExternal(updatedValues); } }, [onChangeBase, onChangeExternal]); const clearGroupSelection = (0, _react.useCallback)(() => { if (wholeGroupSelectionUiRef.current) { (0, _reactDom.flushSync)(() => { setWholeGroupSelectionUi(false); }); } }, []); const clearSectionSelection = (0, _react.useCallback)(() => { const selection = window.getSelection(); selection?.removeAllRanges(); inputs.forEach(({ id }) => { var _section$textContent$; const inputId = String(id); const section = sectionRefs.current[inputId]; const length = (_section$textContent$ = section?.textContent?.length) !== null && _section$textContent$ !== void 0 ? _section$textContent$ : 0; sectionSelectionModeRef.current[inputId] = 'caret'; caretPositionsRef.current[inputId] = length; }); }, [inputs]); const selectSection = (0, _react.useCallback)(inputId => { const section = sectionRefs.current[inputId]; if (!section) { return; } const selection = window.getSelection(); const range = document.createRange(); range.selectNodeContents(section); selection?.removeAllRanges(); selection?.addRange(range); sectionSelectionModeRef.current[inputId] = 'all'; caretPositionsRef.current[inputId] = 0; }, []); const setSectionCaret = (0, _react.useCallback)((inputId, position) => { var _section$textContent$2; const section = sectionRefs.current[inputId]; if (!section) { return; } const safePosition = Math.max(0, Math.min(position, (_section$textContent$2 = section.textContent?.length) !== null && _section$textContent$2 !== void 0 ? _section$textContent$2 : 0)); const textNode = (0, _dom.ensureTextNode)(section); if (!textNode) { return; } const selection = window.getSelection(); const range = document.createRange(); range.setStart(textNode, safePosition); range.collapse(true); selection?.removeAllRanges(); selection?.addRange(range); sectionSelectionModeRef.current[inputId] = 'caret'; caretPositionsRef.current[inputId] = safePosition; }, []); const selectWholeGroup = (0, _react.useCallback)(targetInputId => { var _lastTextNode$textCon; const currentSection = sectionRefs.current[targetInputId]; const currentGroup = currentSection?.closest('.dnb-segmented-field__group'); const sections = (0, _dom.listAllSections)(currentGroup || undefined); if (sections.length === 0) { return; } const firstSection = sections[0]; const lastSection = sections[sections.length - 1]; if (!firstSection || !lastSection) { return; } (0, _reactDom.flushSync)(() => { setWholeGroupSelectionUi(true); }); const firstTextNode = (0, _dom.ensureTextNode)(firstSection); const lastTextNode = (0, _dom.ensureTextNode)(lastSection); if (!firstTextNode || !lastTextNode) { clearGroupSelection(); return; } const selection = window.getSelection(); const range = document.createRange(); range.setStart(firstTextNode, 0); range.setEnd(lastTextNode, (_lastTextNode$textCon = lastTextNode.textContent?.length) !== null && _lastTextNode$textCon !== void 0 ? _lastTextNode$textCon : 0); selection?.removeAllRanges(); selection?.addRange(range); sections.forEach(section => { const sectionId = section.dataset.segmentedInputId; if (!sectionId) { return; } sectionSelectionModeRef.current[sectionId] = 'all'; caretPositionsRef.current[sectionId] = 0; }); }, [clearGroupSelection, caretPositionsRef, sectionRefs, sectionSelectionModeRef]); const focusSection = (0, _react.useCallback)((inputId, mode) => { var _section$textContent; const section = sectionRefs.current[inputId]; if (!section) { return; } section.focus(); if (mode === 'all') { selectSection(inputId); return; } const displayValue = (_section$textContent = section.textContent) !== null && _section$textContent !== void 0 ? _section$textContent : ''; setSectionCaret(inputId, mode === 'end' ? displayValue.length : 0); }, [selectSection, setSectionCaret]); const focusFirstSection = (0, _react.useCallback)(event => { const firstId = inputs[0]?.id; if (disabled || !firstId) { return; } focusSection(String(firstId), 'all'); }, [disabled, focusSection, inputs]); const onLegendClick = (0, _react.useCallback)(() => { focusFirstSection(); }, [focusFirstSection]); const WrapperElement = label ? 'fieldset' : 'div'; const hiddenInputValue = (0, _utils.joinValues)(values, delimiter); const inputElement = (0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, { children: [(0, _jsxRuntime.jsx)("div", { className: "dnb-segmented-field__group", role: "group", "data-segmented-selection": wholeGroupSelectionUi ? 'all' : undefined, children: inputs.map(({ id: inputId, onFocus: _a, onBlur: _b, ...itemProps }, index) => { var _values$inputId; return (0, _jsxRuntime.jsx)(_SegmentedFieldSection.default, { groupId: id, inputId: String(inputId), itemProps: itemProps, value: String((_values$inputId = values[inputId]) !== null && _values$inputId !== void 0 ? _values$inputId : ''), overwriteMode: overwriteMode, delimiter: index !== inputs.length - 1 ? delimiter : undefined, groupDelimiter: delimiter, disabled: Boolean(disabled), valuesRef: valuesRef, inputs: inputs.map(({ id, mask }) => ({ id: String(id), mask })), scopeRef: scopeRef, sectionRefs: sectionRefs, caretPositionsRef: caretPositionsRef, sectionSelectionModeRef: sectionSelectionModeRef, wholeGroupSelectionUi: wholeGroupSelectionUi, clearGroupSelection: clearGroupSelection, clearSectionSelection: clearSectionSelection, selectWholeGroup: selectWholeGroup, selectSection: selectSection, setSectionCaret: setSectionCaret, focusSection: focusSection, onChange: onChange, onGroupFocus: () => { if (!areInputsInFocus.current) { onFocus?.(valuesRef.current); } areInputsInFocus.current = true; }, onGroupBlur: event => { if (!event.relatedTarget?.id?.startsWith(`${id}-`)) { const run = () => onBlur?.(valuesRef.current); window.requestAnimationFrame(run); areInputsInFocus.current = false; clearGroupSelection(); clearSectionSelection(); } }, ...rest }, String(inputId)); }) }), disabled ? (0, _jsxRuntime.jsx)("span", { id: id, className: "dnb-segmented-field__hidden-input dnb-sr-only", "aria-hidden": true }) : (0, _jsxRuntime.jsx)("input", { id: id, className: "dnb-segmented-field__hidden-input dnb-sr-only", value: hiddenInputValue, onFocus: focusFirstSection, readOnly: true, tabIndex: -1, "aria-hidden": true })] }); const labelElement = label && (0, _jsxRuntime.jsx)(_FormLabel.default, { element: "legend", forId: id, disabled: disabled, labelDirection: labelDirection, onClick: onLegendClick, children: label }); const wrapperProps = (0, _SpacingUtils.useSpacing)(rest, { ref: element => { if (!hasExternalScopeRef && !scopeRef.current) { scopeRef.current = element; } }, className: (0, _clsx.clsx)('dnb-segmented-field__fieldset', labelDirection === 'horizontal' && 'dnb-segmented-field__fieldset--horizontal') }); return (0, _jsxRuntime.jsx)(WrapperElement, { ...wrapperProps, children: (0, _jsxRuntime.jsx)(_Input.default, { ...rest, id: id, label: labelElement, className: (0, _clsx.clsx)('dnb-segmented-field', className), size: size, labelDirection: labelDirection, disabled: disabled, status: status, statusState: statusState, suffix: suffix, stretch: stretch, inputElement: inputElement, _omitInputShellClass: _omitInputShellClass }) }); } var _default = exports.default = SegmentedField; (0, _withComponentMarkers.default)(SegmentedField, { _formElement: true, _supportsSpacingProps: true }); //# sourceMappingURL=SegmentedField.js.map