UNPKG

@progress/kendo-react-dropdowns

Version:

React DropDowns offer an interface for users to select different items from a list and more. KendoReact Dropdowns package

529 lines (528 loc) • 16.8 kB
/** * @license *------------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the package root for more information *------------------------------------------------------------------------------------------- */ import * as t from "react"; import n from "prop-types"; import { createPropsContext as nt, validatePackage as ot, usePropsContext as at, useId as rt, getTabIndex as it, ZIndexContext as Fe, useRtl as lt, useAdaptiveModeContext as st, canUseDOM as ct, Keys as c, noop as $, mapTree as ut, extendDataItem as dt, classNames as ae, IconWrap as Ie, WatermarkOverlay as pt, kendoThemeMaps as mt } from "@progress/kendo-react-common"; import { Popup as ft } from "@progress/kendo-react-popup"; import { useLocalization as vt } from "@progress/kendo-react-intl"; import { TreeView as Se } from "@progress/kendo-react-treeview"; import { packageMetadata as gt } from "../package-metadata.mjs"; import { getItemValue as bt, areSame as Re, isPresent as ht } from "../common/utils.mjs"; import { useDropdownWidth as Et } from "./useDropdownWidth.mjs"; import { ListNoData as yt } from "./ListNoData.mjs"; import { clear as Te, messages as re, nodata as U } from "../messages/index.mjs"; import { FloatingLabel as Ct } from "@progress/kendo-react-labels"; import Ne from "../common/ListFilter.mjs"; import { Button as kt } from "@progress/kendo-react-buttons"; import { xIcon as xt, caretAltDownIcon as wt } from "@progress/kendo-svg-icons"; import { AdaptiveMode as Dt } from "../common/AdaptiveMode.mjs"; import { ActionSheetContent as Ft } from "@progress/kendo-react-layout"; const { sizeMap: It, roundedMap: St } = mt, Rt = 12e3, Tt = 2e3, Nt = "Please select a value from the list!", Pt = (y) => /* @__PURE__ */ t.createElement("span", { className: "k-input-value-text" }, y.children), Pe = (y) => y.split("_").map((F) => parseInt(F, 10)), Mt = (y, F) => { const { validationMessage: X, valid: r, required: a } = y; return { customError: X !== void 0, valid: !!(r !== void 0 ? r : !a || F), valueMissing: !F }; }, ie = { selectField: "selected", subItemsField: "items", popupSettings: { animate: !0, width: "200px", height: "200px" }, data: [], required: !1, style: {}, validityStyles: !0, size: "medium", rounded: "medium", fillMode: "solid" }, Ot = nt(), Me = t.forwardRef((y, F) => { const X = !ot(gt, { component: "DropDownTree" }), r = at(Ot, y), a = { ...ie, ...r }, Oe = rt(), le = a.id || Oe, { data: N, dataItemKey: P, popupSettings: b = {}, style: M, opened: u, disabled: I, onOpen: O = $, onClose: m = $, placeholder: j, label: V, name: Ve, selectField: z, subItemsField: W, validationMessage: H, valid: We, required: G, validityStyles: Le } = a, J = it(a.tabIndex, I), i = t.useRef(null), f = t.useRef(null), S = t.useRef(null), Q = t.useRef(null), _ = t.useRef(null), h = t.useRef(null), Z = t.useRef(!1), [se, Ke] = t.useState(void 0), C = a.value !== void 0, v = C ? a.value : se !== void 0 ? se : a.defaultValue, L = ht(v), Y = L ? bt(v, a.textField) : "", ee = Mt({ validationMessage: H, valid: We, required: G }, L), ce = t.useContext(Fe), Be = ce ? ce + Tt : Rt, qe = t.useCallback(() => f.current && f.current.focus(), []); t.useImperativeHandle(i, () => ({ props: a, element: f.current, focus: qe })), t.useImperativeHandle(F, () => i.current); const k = lt(f, a.dir), Ae = { width: Et( f, ie, { ...ie.popupSettings, ...b }, M ), ...k !== void 0 ? { direction: k } : {} }, [ze, ue] = t.useState(!1), l = u !== void 0 ? u : ze, [d, te] = t.useState(!1), [ne, He] = t.useState(), [de, _e] = t.useState(""), pe = st(), x = !!(ne && pe && ne <= pe.medium && a.adaptive), Ze = t.useCallback(() => { _.current && _.current.setCustomValidity && _.current.setCustomValidity( ee.valid ? "" : H === void 0 ? Nt : H ); }, [H, ee]); t.useEffect(Ze), t.useEffect(() => { const e = ct && window.ResizeObserver && new window.ResizeObserver(et.bind(void 0)); return document != null && document.body && e && e.observe(document.body), () => { document != null && document.body && e && e.disconnect(); }; }, []); const me = t.useCallback( (e) => { if (!l) { if (O) { const o = { ...e }; O.call(void 0, o); } u === void 0 && ue(!0); } }, [l, u, O] ), w = t.useCallback( (e) => { if (l) { if (m) { const o = { ...e }; m.call(void 0, o); } u === void 0 && (ue(!1), x && setTimeout(() => { var o; g((o = Q.current) == null ? void 0 : o.element); }, 300)); } }, [l, u, m, x] ), $e = t.useCallback( (e) => { if (!e.isDefaultPrevented() && i.current) { te(!0); const o = { syntheticEvent: e, nativeEvent: e.nativeEvent, target: i.current }; (l ? w : me)(o); } }, [l, u, O, m] ), R = t.useCallback((e) => { Z.current = !0, e(), window.setTimeout(() => Z.current = !1, 0); }, []), Ue = t.useCallback( (e) => { var D, we; const { keyCode: o, altKey: s } = e, p = h.current && h.current.element; if (!i.current || e.isDefaultPrevented() && ((D = S.current) == null ? void 0 : D.element) === e.target) return; const T = { syntheticEvent: e, nativeEvent: e.nativeEvent, target: i.current }; if (l) if (o === c.esc || s && o === c.up) e.preventDefault(), w(T); else if (p && p.querySelector(".k-focus") && (o === c.up || o === c.down || o === c.left || o === c.right || o === c.home || o === c.end)) { if (o === c.up && ((we = S.current) != null && we.element)) { const q = Array.from(p.querySelectorAll(".k-treeview-item")), De = [...q].reverse().find((A) => !!(A && A.querySelector(".k-focus"))); if (De && q.indexOf(De) === 0) return R(() => { var A; g((A = S.current) == null ? void 0 : A.element); }); } R($); } else o === c.down && R(() => { var q; g(((q = S.current) == null ? void 0 : q.element) || p); }); else s && o === c.down ? (e.preventDefault(), me(T)) : l || o === c.esc && be(e); }, [l, u, O, m] ), fe = t.useCallback((e) => { const { keyCode: o, altKey: s } = e; s || o !== c.up && o !== c.down || (e.preventDefault(), R( o === c.up ? () => { g(f.current); } : () => { g(h.current && h.current.element); } )); }, []), g = t.useCallback((e) => { e && R(() => e.focus()); }, []), Xe = t.useCallback(() => { var e; !d && l && !u ? w({ target: i.current }) : r.filterable ? g((e = S.current) == null ? void 0 : e.element) : g(h.current && h.current.element); }, [m, r.filterable, d, u, l]), je = t.useCallback(() => { d && g(f.current); }, [d]), Ge = t.useCallback( (e) => { if (!d && !Z.current && (te(!0), r.onFocus && i.current)) { const o = { syntheticEvent: e, nativeEvent: e.nativeEvent, target: i.current }; r.onFocus.call(void 0, o); } }, [d, r.onFocus] ), Je = t.useCallback( (e) => { if (d && !Z.current && i.current) { te(!1); const o = { syntheticEvent: e, nativeEvent: e.nativeEvent, target: i.current }; if (r.onBlur) { const s = { ...o }; r.onBlur.call(void 0, s); } x || w(o); } }, [d, r.onBlur, l, u, m] ), Qe = t.useCallback(() => { d && R($), x && setTimeout(() => { var e; g((e = Q.current) == null ? void 0 : e.element); }, 300); }, [d, x]), ve = t.useCallback( (e, o, s) => { if (r.onChange) { const p = { value: o, level: s ? Pe(s) : [], ...e }; r.onChange.call(void 0, p); } C || Ke(o); }, [r.onChange, C] ), ge = t.useCallback( (e) => { if (Re(e.item, v, P) || !i.current) return; const { item: o, itemHierarchicalIndex: s, nativeEvent: p, syntheticEvent: T } = e, D = { syntheticEvent: T, nativeEvent: p, target: i.current }; ve(D, o, s), w(D); }, [C, v, r.onChange, P, l, u, m] ), be = t.useCallback( (e) => { if (!i.current) return; const o = { syntheticEvent: e, nativeEvent: e.nativeEvent, target: i.current }; ve(o, null), w(o), e.preventDefault(); }, [C, r.onChange, l, u, m] ), he = t.useCallback( (e) => { if (e.syntheticEvent.stopPropagation(), r.onExpandChange && i.current) { const { item: o, itemHierarchicalIndex: s, nativeEvent: p, syntheticEvent: T } = e, D = { level: Pe(s), item: o, nativeEvent: p, syntheticEvent: T, target: i.current }; r.onExpandChange.call(void 0, D); } }, [r.onExpandChange] ), Ee = t.useCallback( (e) => { if (r.onFilterChange && i.current) { const s = { filter: { field: r.textField, operator: "contains", value: e.target.value }, syntheticEvent: e.syntheticEvent, nativeEvent: e.nativeEvent, target: i.current }; r.onFilterChange.call(void 0, s), r.filter === void 0 && _e(e.target.value); } }, [r.onFilterChange, r.filter, r.textField] ), Ye = () => { const e = a.filterable ? /* @__PURE__ */ t.createElement( Ne, { value: a.filter === void 0 ? de : a.filter, ref: Q, onChange: Ee, onKeyDown: fe, size: E, rounded: K, fillMode: B } ) : null, o = { title: a.adaptiveTitle, expand: l, onClose: (s) => w(s), windowWidth: ne, mobileFilter: e }; return /* @__PURE__ */ t.createElement(Dt, { ...o }, /* @__PURE__ */ t.createElement(Ft, { overflowHidden: !0 }, /* @__PURE__ */ t.createElement("div", { className: "k-list-container" }, /* @__PURE__ */ t.createElement("div", { className: "k-list k-list-lg" }, N.length > 0 ? /* @__PURE__ */ t.createElement( Se, { ref: h, tabIndex: J, data: ye, focusIdField: P, textField: a.textField, selectField: z, expandField: a.expandField, childrenField: W, expandIcons: !0, onItemClick: ge, onExpandChange: he, size: E, item: a.item, dir: k, animate: b.animate } ) : /* @__PURE__ */ t.createElement(Ce, null, oe.toLanguageString(U, re[U])))))); }, et = t.useCallback((e) => { for (const o of e) He(o.target.clientWidth); }, []), ye = t.useMemo(() => C || !L ? N : ut( N, W, (e) => dt(e, W, { [z]: Re(e, v, P) }) ), [N, v, C, L, z, W]), Ce = a.listNoData || yt, tt = a.valueHolder || Pt, oe = vt(), ke = !Le || ee.valid, { size: E, rounded: K, fillMode: B } = a, xe = /* @__PURE__ */ t.createElement(t.Fragment, null, /* @__PURE__ */ t.createElement( "span", { className: ae("k-dropdowntree k-picker", a.className, { [`k-picker-${It[E] || E}`]: E, [`k-rounded-${St[K] || K}`]: K, [`k-picker-${B}`]: B, "k-focus": d, "k-invalid": !ke, "k-loading": a.loading, "k-required": G, "k-disabled": a.disabled }), tabIndex: J, accessKey: a.accessKey, id: le, style: V ? { ...M, width: void 0 } : M, dir: k, ref: f, onKeyDown: I ? void 0 : Ue, onMouseDown: Qe, onClick: I ? void 0 : $e, onFocus: Ge, onBlur: Je, role: "combobox", "aria-haspopup": "tree", "aria-expanded": l, "aria-disabled": I, "aria-label": V, "aria-labelledby": a.ariaLabelledBy, "aria-describedby": a.ariaDescribedBy, "aria-required": G }, /* @__PURE__ */ t.createElement("span", { className: "k-input-inner" }, (Y || j) && /* @__PURE__ */ t.createElement(tt, { item: v }, Y || j)), a.loading && /* @__PURE__ */ t.createElement(Ie, { className: "k-input-loading-icon", name: "loading" }), L && !I && /* @__PURE__ */ t.createElement( "span", { onClick: be, className: "k-clear-value", title: oe.toLanguageString(Te, re[Te]), role: "button", tabIndex: -1, onMouseDown: (e) => e.preventDefault() }, /* @__PURE__ */ t.createElement(Ie, { name: "x", icon: xt }) ), /* @__PURE__ */ t.createElement( kt, { tabIndex: -1, type: "button", "aria-label": "select", className: "k-input-button", size: E, fillMode: B, themeColor: "base", rounded: null, icon: "caret-alt-down", svgIcon: wt } ), /* @__PURE__ */ t.createElement( "select", { name: Ve, ref: _, tabIndex: -1, "aria-hidden": !0, title: V, style: { opacity: 0, width: 1, border: 0, zIndex: -1, position: "absolute", left: "50%" } }, /* @__PURE__ */ t.createElement("option", { value: a.valueMap ? a.valueMap.call(void 0, v) : v }) ), !x && /* @__PURE__ */ t.createElement(Fe.Provider, { value: Be }, /* @__PURE__ */ t.createElement( ft, { ...b, className: ae(b.className, { "k-rtl": k === "rtl" }), popupClass: ae(b.popupClass, "k-dropdowntree-popup k-list-container"), style: Ae, anchor: b.anchor || f.current, show: l, onOpen: Xe, onClose: je }, a.filterable && /* @__PURE__ */ t.createElement( Ne, { value: a.filter === void 0 ? de : a.filter, ref: S, onChange: Ee, onKeyDown: fe, size: E, rounded: K, fillMode: B, renderListFilterWrapper: !0, renderPrefixSeparator: !0 } ), N.length > 0 ? /* @__PURE__ */ t.createElement( Se, { style: { height: b.height }, ref: h, tabIndex: J, data: ye, focusIdField: P, textField: a.textField, selectField: z, expandField: a.expandField, childrenField: W, expandIcons: !0, onItemClick: ge, onExpandChange: he, size: E, item: a.item, dir: k, animate: b.animate } ) : /* @__PURE__ */ t.createElement(Ce, null, oe.toLanguageString(U, re[U])), X && /* @__PURE__ */ t.createElement(pt, null) )) ), x && Ye()); return V ? /* @__PURE__ */ t.createElement( Ct, { label: V, editorValue: Y, editorPlaceholder: j, editorValid: ke, editorDisabled: I, editorId: le, style: { width: M ? M.width : void 0 }, children: xe, dir: k } ) : xe; }), Vt = { opened: n.bool, disabled: n.bool, dir: n.string, tabIndex: n.number, accessKey: n.string, data: n.array, value: n.any, valueMap: n.func, placeholder: n.string, dataItemKey: n.string.isRequired, textField: n.string.isRequired, selectField: n.string, expandField: n.string, subItemsField: n.string, className: n.string, style: n.object, label: n.string, validationMessage: n.string, validityStyles: n.bool, valid: n.bool, required: n.bool, name: n.string, id: n.string, ariaLabelledBy: n.string, ariaDescribedBy: n.string, filterable: n.bool, filter: n.string, loading: n.bool, popupSettings: n.shape({ animate: n.oneOfType([ n.bool, n.shape({ openDuration: n.number, closeDuration: n.number }) ]), popupClass: n.string, className: n.string, appendTo: n.any, width: n.oneOfType([n.string, n.number]), height: n.oneOfType([n.string, n.number]) }), onOpen: n.func, onClose: n.func, onFocus: n.func, onBlur: n.func, onChange: n.func, onFilterChange: n.func, onExpandChange: n.func, item: n.func, valueHolder: n.func, listNoData: n.func, adaptiveTitle: n.string, adaptive: n.bool }; Me.displayName = "KendoReactDropDownTree"; Me.propTypes = Vt; export { Me as DropDownTree, Ot as DropDownTreePropsContext };