@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
307 lines (306 loc) • 13 kB
JavaScript
"use strict";
"use client";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = _interopRequireWildcard(require("react"));
var _reactDom = require("react-dom");
var _clsx = _interopRequireDefault(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 _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
function SegmentedField(props) {
const fallbackId = (0, _useId.default)(props === null || props === void 0 ? void 0 : 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 === null || optionsEnhancer === void 0 || 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 === null || selection === void 0 || selection.removeAllRanges();
inputs.forEach(({
id
}) => {
var _section$textContent$, _section$textContent;
const inputId = String(id);
const section = sectionRefs.current[inputId];
const length = (_section$textContent$ = section === null || section === void 0 || (_section$textContent = section.textContent) === null || _section$textContent === void 0 ? void 0 : _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 === null || selection === void 0 || selection.removeAllRanges();
selection === null || selection === void 0 || selection.addRange(range);
sectionSelectionModeRef.current[inputId] = 'all';
caretPositionsRef.current[inputId] = 0;
}, []);
const setSectionCaret = (0, _react.useCallback)((inputId, position) => {
var _section$textContent$2, _section$textContent2;
const section = sectionRefs.current[inputId];
if (!section) {
return;
}
const safePosition = Math.max(0, Math.min(position, (_section$textContent$2 = (_section$textContent2 = section.textContent) === null || _section$textContent2 === void 0 ? void 0 : _section$textContent2.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 === null || selection === void 0 || selection.removeAllRanges();
selection === null || selection === void 0 || selection.addRange(range);
sectionSelectionModeRef.current[inputId] = 'caret';
caretPositionsRef.current[inputId] = safePosition;
}, []);
const selectWholeGroup = (0, _react.useCallback)(targetInputId => {
var _lastTextNode$textCon, _lastTextNode$textCon2;
const currentSection = sectionRefs.current[targetInputId];
const currentGroup = currentSection === null || currentSection === void 0 ? void 0 : 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$textCon2 = lastTextNode.textContent) === null || _lastTextNode$textCon2 === void 0 ? void 0 : _lastTextNode$textCon2.length) !== null && _lastTextNode$textCon !== void 0 ? _lastTextNode$textCon : 0);
selection === null || selection === void 0 || selection.removeAllRanges();
selection === null || selection === void 0 || 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$textContent3;
const section = sectionRefs.current[inputId];
if (!section) {
return;
}
section.focus();
if (mode === 'all') {
selectSection(inputId);
return;
}
const displayValue = (_section$textContent3 = section.textContent) !== null && _section$textContent3 !== void 0 ? _section$textContent3 : '';
setSectionCaret(inputId, mode === 'end' ? displayValue.length : 0);
}, [selectSection, setSectionCaret]);
const focusFirstSection = (0, _react.useCallback)(event => {
var _inputs$;
const firstId = (_inputs$ = inputs[0]) === null || _inputs$ === void 0 ? void 0 : _inputs$.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 === null || onFocus === void 0 || onFocus(valuesRef.current);
}
areInputsInFocus.current = true;
},
onGroupBlur: event => {
var _event$relatedTarget;
if (!((_event$relatedTarget = event.relatedTarget) !== null && _event$relatedTarget !== void 0 && (_event$relatedTarget = _event$relatedTarget.id) !== null && _event$relatedTarget !== void 0 && _event$relatedTarget.startsWith(`${id}-`))) {
const run = () => onBlur === null || onBlur === void 0 ? void 0 : onBlur(valuesRef.current);
window.requestAnimationFrame(run);
areInputsInFocus.current = false;
clearGroupSelection();
clearSectionSelection();
}
},
...rest
}, String(inputId));
})
}), (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.applySpacing)(rest, {
ref: element => {
if (!hasExternalScopeRef && !scopeRef.current) {
scopeRef.current = element;
}
},
className: '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.default)('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