UNPKG

@dnb/eufemia

Version:

DNB Eufemia Design System UI Library

329 lines 12.6 kB
import _extends from "@babel/runtime/helpers/esm/extends"; var _em, _em2; import React, { useMemo, useCallback } from 'react'; import classnames from 'classnames'; import { convertJsxToString, makeUniqueId } from "../../../../shared/component-helper.js"; import { ToggleButton, Dropdown, Radio, Autocomplete, HelpButton } from "../../../../components/index.js"; import OptionField from "../Option/index.js"; import { useFieldProps } from "../../hooks/index.js"; import { checkForError } from "../../hooks/useFieldProps.js"; import { pickSpacingProps } from "../../../../components/flex/utils.js"; import FieldBlock from "../../FieldBlock/index.js"; import { convertCamelCasePropsToSnakeCase } from "../../../../shared/helpers/withCamelCaseProps.js"; import useDataValue from "../../hooks/useDataValue.js"; const validDrawerListProps = ['default_value', 'triangle_position', 'label_direction', 'prevent_selection', 'prevent_close', 'keep_open', 'independent_width', 'fixed_position', 'enable_body_lock', 'skip_keysearch', 'ignore_events', 'align_drawer', 'list_class', 'portal_class', 'no_scroll_animation', 'no_animation', 'skip_portal', 'min_height', 'max_height', 'page_offset', 'observer_element', 'cache_hash', 'wrapper_element', 'options_render', 'on_pre_change', 'on_change', 'on_select', 'on_show', 'on_hide']; const validAutocompleteProps = ['align_autocomplete', 'aria_live_options', 'disable_filter', 'disable_highlighting', 'disable_reorder', 'drawer_class', 'icon_position', 'icon_size', 'indicator_label', 'input_element', 'input_icon', 'input_ref', 'input_value', 'keep_selection', 'keep_value', 'keep_value_and_selection', 'label_direction', 'label_sr_only', 'no_options', 'open_on_focus', 'prevent_selection', 'search_in_word_index', 'search_numbers', 'selected_sr', 'show_all', 'show_clear_button', 'show_options_sr', 'show_submit_button', 'skip_portal', 'status_props', 'status_state', 'submit_button_icon', 'submit_button_title', 'submit_element', 'triangle_position', 'on_type', 'on_focus', 'on_blur', 'on_change', 'on_select', 'on_show', 'on_hide']; export const listOfValidAutocompleteProps = [...validAutocompleteProps, ...validDrawerListProps]; const validDropdownProps = ['icon_size', 'icon_position', 'triangle_position', 'open_on_focus', 'prevent_selection', 'action_menu', 'more_menu', 'align_dropdown', 'independent_width', 'skip_portal', 'status_state', 'status_props', 'label_direction', 'label_sr_only', 'trigger_element', 'on_change', 'on_select', 'on_show', 'on_hide']; export const listOfValidDropdownProps = [...validDropdownProps, ...validDrawerListProps]; function Selection(props) { const clearValue = useMemo(() => `clear-option-${makeUniqueId()}`, []); const { id, className, variant = 'dropdown', layout = 'vertical', optionsLayout = 'vertical', placeholder, value, info, warning, error, hasError, disabled, size, emptyValue, width, htmlAttributes, setHasFocus, handleChange, setDisplayValue, transformSelection, data, groups, dataPath, children, additionalArgs, autocompleteProps, dropdownProps } = useFieldProps(props); const { getValueByPath } = useDataValue(); let dataList = data; if (dataPath) { dataList = getValueByPath(dataPath); } const hasRenderPropChildren = typeof children === 'function'; const renderedChildren = useMemo(() => { return resolveChildren(children, value, dataList); }, [children, dataList, value]); const handleDrawerListChange = useCallback(({ data, value }) => { var _data$selectedKey; const selectedKey = (_data$selectedKey = data?.selectedKey) !== null && _data$selectedKey !== void 0 ? _data$selectedKey : value; handleChange?.(!selectedKey || selectedKey === clearValue ? emptyValue : selectedKey, { data }); }, [handleChange, emptyValue, clearValue]); const onChangeHandler = useCallback(({ value }) => { handleChange?.(value === undefined ? emptyValue : value); }, [handleChange, emptyValue]); const handleShow = useCallback(({ data }) => { setHasFocus(true, data?.selectedKey); }, [setHasFocus]); const handleHide = useCallback(({ data }) => { setHasFocus(false, data?.selectedKey); }, [setHasFocus]); const cn = classnames(`dnb-forms-field-selection dnb-forms-field-selection__variant--${variant} dnb-forms-field-selection--layout-${layout} dnb-forms-field-selection--options-layout--${optionsLayout}`, className); const fieldBlockProps = { forId: id, className: cn, disableStatusSummary: true, ...pickSpacingProps(props) }; const onType = props?.autocompleteProps?.onType; const onTypeAutocompleteHandler = useCallback(event => { if (typeof onType === 'function') { const { value } = event; onType({ ...event, ...additionalArgs, value: value === '' ? emptyValue : value }); } }, [additionalArgs, emptyValue, onType]); switch (variant) { case 'radio': case 'radio-list': case 'button': { const Component = variant === 'radio' || variant === 'radio-list' ? Radio : ToggleButton; const items = renderRadioItems({ id, value, variant, info, warning, htmlAttributes, children: renderedChildren, dataList: hasRenderPropChildren ? undefined : dataList, hasError, iterateOverItems: ({ value: v, label }) => { if (v === value) { setDisplayValue(label); } } }); const additionalFieldBlockProps = { asFieldset: hasRenderPropChildren || React.Children.count(items) > 1, fieldsetRole: variant === 'radio' || variant === 'radio-list' ? 'radiogroup' : 'group' }; if (!size) { additionalFieldBlockProps.labelHeight = 'small'; } if (width) { additionalFieldBlockProps.contentWidth = width; } return React.createElement(FieldBlock, _extends({}, fieldBlockProps, additionalFieldBlockProps), React.createElement(Component.Group, { size: size, className: cn, layout_direction: optionsLayout === 'horizontal' ? 'row' : 'column', disabled: disabled, on_change: onChangeHandler, value: String(value !== null && value !== void 0 ? value : '') }, items)); } case 'autocomplete': case 'dropdown': { const data = renderDropdownItems(hasRenderPropChildren ? undefined : dataList, transformSelection).concat(makeOptions(renderedChildren, transformSelection)).filter(Boolean); const displayValue = data.find(item => item.selectedKey === value)?.content; setDisplayValue(displayValue); const sharedProps = { id, list_class: 'dnb-forms-field-selection__list', portal_class: 'dnb-forms-field-selection__portal', title: placeholder, value: String(value !== null && value !== void 0 ? value : ''), status: (hasError || checkForError([error, info, warning])) && 'error', disabled, ...htmlAttributes, data, groups, size, on_change: handleDrawerListChange, on_show: handleShow, on_hide: handleHide, stretch: true }; const specificFieldBlockProps = { contentWidth: width !== null && width !== void 0 ? width : 'large' }; return React.createElement(FieldBlock, _extends({}, fieldBlockProps, specificFieldBlockProps), variant === 'autocomplete' ? React.createElement(Autocomplete, _extends({}, sharedProps, autocompleteProps ? convertCamelCasePropsToSnakeCase(Object.freeze(autocompleteProps), listOfValidAutocompleteProps) : null, { value: autocompleteProps?.preventSelection ? undefined : value, on_type: onTypeAutocompleteHandler, data: !props.data && !props.dataPath && autocompleteProps?.mode === 'async' ? undefined : data, selectall: true })) : React.createElement(Dropdown, _extends({}, sharedProps, dropdownProps ? convertCamelCasePropsToSnakeCase(dropdownProps, listOfValidDropdownProps) : null))); } } } function resolveChildren(children, value, options) { if (typeof children === 'function') { return children({ value, options }); } return children; } function renderRadioItems({ id, value: valueProp, variant, info, warning, htmlAttributes, children, dataList, hasError, iterateOverItems }) { const optionsCount = countOptions(children) + (dataList?.length || 0); const createOption = (props, i) => { const { value, title, children, error, help, size, ...rest } = props; const label = title !== null && title !== void 0 ? title : children; const suffix = help ? React.createElement(HelpButton, { size: "small", title: convertJsxToString(help.title) }, help.content) : undefined; iterateOverItems?.({ value, label }); const Component = variant === 'radio' || variant === 'radio-list' ? Radio : ToggleButton; return React.createElement(Component, _extends({ id: optionsCount === 1 ? id : undefined, key: `option-${i}-${id}`, label: variant === 'radio' || variant === 'radio-list' ? label : undefined, text: variant === 'button' ? label : undefined, role: "radio", value: String(value !== null && value !== void 0 ? value : valueProp) || undefined, status: (hasError || checkForError([error, info, warning])) && 'error', suffix: suffix, size: size }, htmlAttributes, rest)); }; return [...(dataList || []).map((props, i) => { return createOption(props, i); }), ...(mapOptions(children, { createOption }) || [])].filter(Boolean); } export function countOptions(children) { let count = 0; React.Children.forEach(children, child => { if (React.isValidElement(child)) { if (child.type === OptionField) { count++; } else if (child.props.children) { count += countOptions(child.props.children); } } }); return count; } export function mapOptions(children, { createOption }) { return React.Children.map(children, (child, i) => { if (React.isValidElement(child)) { if (child.type === OptionField) { return createOption(child.props, i); } if (child.props.children) { const nestedChildren = mapOptions(child.props.children, { createOption }); return React.cloneElement(child, child.props, nestedChildren); } } return child; }); } export function makeOptions(children, transformSelection) { return React.Children.map(children, child => { if (child?.['props']?.children?.type === OptionField) { child = child['props'].children; } if (React.isValidElement(child) && child.type === OptionField) { var _ref, _props$title, _props$value; const props = child.props; const title = (_ref = (_props$title = props.title) !== null && _props$title !== void 0 ? _props$title : props.children) !== null && _ref !== void 0 ? _ref : _em || (_em = React.createElement("em", null, "Untitled")); const content = props.text ? [title, props.text] : title; const selected_value = transformSelection ? transformSelection(props) : undefined; const selectedKey = String((_props$value = props.value) !== null && _props$value !== void 0 ? _props$value : ''); const disabled = props.disabled; const style = props.style; const groupIndex = props.groupIndex; return { selectedKey, selected_value, content, disabled, style, groupIndex }; } if (child) { return { content: child }; } }); } function renderDropdownItems(data, transformSelection) { return data?.map(props => { const { value, title, text, disabled, style, ...rest } = props; return { selectedKey: value, content: (text ? [title, text] : title) || _em2 || (_em2 = React.createElement("em", null, "Untitled")), selected_value: transformSelection ? transformSelection(props) : undefined, disabled, style, ...rest }; }) || []; } Selection._supportsSpacingProps = true; export default Selection; //# sourceMappingURL=Selection.js.map