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

533 lines (532 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 Ie, useRtl as lt, useAdaptiveModeContext as st, canUseDOM as ct, Keys as c, noop as U, mapTree as ut, extendDataItem as dt, classNames as ae, IconWrap as Se, 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 Te } 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 Pe, messages as re, nodata as X } from "../messages/index.mjs"; import { FloatingLabel as Ct } from "@progress/kendo-react-labels"; import Me 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, Tt = 12e3, Rt = 2e3, Pt = "Please select a value from the list!", Mt = (E) => /* @__PURE__ */ t.createElement("span", { className: "k-input-value-text" }, E.children), Ne = (E) => E.split("_").map((D) => parseInt(D, 10)), Nt = (E, D) => { const { validationMessage: j, valid: r, required: a } = E; return { customError: j !== void 0, valid: !!(r !== void 0 ? r : !a || D), valueMissing: !D }; }, 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(), Oe = t.forwardRef((E, D) => { const j = !ot(gt, { component: "DropDownTree" }), r = at(Ot, E), a = { ...ie, ...r }, Ve = rt(), le = a.id || Ve, { data: N, dataItemKey: O, popupSettings: b = {}, style: V, opened: u, disabled: F, onOpen: W = U, onClose: m = U, placeholder: B, label: I, name: We, selectField: H, subItemsField: L, validationMessage: _, valid: Le, required: G, validityStyles: Ke } = a, J = it(a.tabIndex, F), i = t.useRef(null), f = t.useRef(null), S = t.useRef(null), Q = t.useRef(null), Z = t.useRef(null), y = t.useRef(null), $ = t.useRef(!1), [se, ze] = t.useState(void 0), C = a.value !== void 0, v = C ? a.value : se !== void 0 ? se : a.defaultValue, K = ht(v), Y = K ? bt(v, a.textField) : "", ee = Nt({ validationMessage: _, valid: Le, required: G }, K), ce = t.useContext(Ie), qe = ce ? ce + Rt : Tt, Ae = t.useCallback(() => f.current && f.current.focus(), []); t.useImperativeHandle(i, () => ({ props: a, element: f.current, focus: Ae })), t.useImperativeHandle(D, () => i.current); const k = lt(f, a.dir), Be = { width: Et( f, ie, { ...ie.popupSettings, ...b }, V ), ...k !== void 0 ? { direction: k } : {} }, [He, ue] = t.useState(!1), l = u !== void 0 ? u : He, [p, te] = t.useState(!1), [ne, _e] = t.useState(), [de, Ze] = t.useState(""), pe = st(), h = !!(ne && pe && ne <= pe.medium && a.adaptive), $e = t.useCallback(() => { Z.current && Z.current.setCustomValidity && Z.current.setCustomValidity( ee.valid ? "" : _ === void 0 ? Pt : _ ); }, [_, ee]); t.useEffect($e), 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 (W) { const o = { ...e }; W.call(void 0, o); } u === void 0 && ue(!0); } }, [l, u, W] ), x = t.useCallback( (e) => { if (l) { if (m) { const o = { ...e }; m.call(void 0, o); } u === void 0 && (ue(!1), h && setTimeout(() => { var o; g((o = Q.current) == null ? void 0 : o.element); }, 300)); } }, [l, u, m, h] ), fe = t.useCallback( (e) => { if (!e.isDefaultPrevented() && i.current) { te(!0); const o = { syntheticEvent: e, nativeEvent: e.nativeEvent, target: i.current }; (l ? x : me)(o); } }, [l, u, W, m] ), T = t.useCallback((e) => { $.current = !0, e(), window.setTimeout(() => $.current = !1, 0); }, []), Ue = t.useCallback( (e) => { var w, De; const { keyCode: o, altKey: s } = e, d = y.current && y.current.element; if (!i.current || e.isDefaultPrevented() && ((w = S.current) == null ? void 0 : w.element) === e.target) return; const P = { syntheticEvent: e, nativeEvent: e.nativeEvent, target: i.current }; if (l) if (o === c.esc || s && o === c.up) e.preventDefault(), x(P); else if (d && d.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 && ((De = S.current) != null && De.element)) { const A = Array.from(d.querySelectorAll(".k-treeview-item")), Fe = [...A].reverse().find((M) => M == null ? void 0 : M.querySelector(".k-focus")); if (Fe && A.indexOf(Fe) === 0) return T(() => { var M; g((M = S.current) == null ? void 0 : M.element); }); } T(U); } else o === c.down && T(() => { var A; g(((A = S.current) == null ? void 0 : A.element) || d); }); else s && o === c.down ? (e.preventDefault(), me(P)) : l || o === c.esc && he(e); }, [l, u, W, m] ), ve = t.useCallback((e) => { const { keyCode: o, altKey: s } = e; s || o !== c.up && o !== c.down || (e.preventDefault(), T( o === c.up ? () => { g(f.current); } : () => { var d; g((d = y.current) == null ? void 0 : d.element); } )); }, []), g = t.useCallback((e) => { e && T(() => e.focus()); }, []), Xe = t.useCallback(() => { var e; !p && l && !u ? x({ target: i.current }) : r.filterable ? g((e = S.current) == null ? void 0 : e.element) : g(y.current && y.current.element); }, [m, r.filterable, p, u, l]), je = t.useCallback(() => { p && g(f.current); }, [p]), Ge = t.useCallback( (e) => { if (!p && !$.current && (te(!0), r.onFocus && i.current)) { const o = { syntheticEvent: e, nativeEvent: e.nativeEvent, target: i.current }; r.onFocus.call(void 0, o); } }, [p, r.onFocus] ), Je = t.useCallback( (e) => { if (p && !$.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); } h || x(o); } }, [p, r.onBlur, l, u, m] ), Qe = t.useCallback(() => { p && T(U), h && setTimeout(() => { var e; g((e = Q.current) == null ? void 0 : e.element); }, 300); }, [p, h]), ge = t.useCallback( (e, o, s) => { if (r.onChange) { const d = { value: o, level: s ? Ne(s) : [], ...e }; r.onChange.call(void 0, d); } C || ze(o); }, [r.onChange, C] ), be = t.useCallback( (e) => { if (Re(e.item, v, O) || !i.current) return; const { item: o, itemHierarchicalIndex: s, nativeEvent: d, syntheticEvent: P } = e, w = { syntheticEvent: P, nativeEvent: d, target: i.current }; ge(w, o, s), x(w); }, [C, v, r.onChange, O, l, u, m] ), he = t.useCallback( (e) => { if (!i.current) return; const o = { syntheticEvent: e, nativeEvent: e.nativeEvent, target: i.current }; ge(o, null), x(o), e.preventDefault(); }, [C, r.onChange, l, u, m] ), Ee = t.useCallback( (e) => { if (e.syntheticEvent.stopPropagation(), r.onExpandChange && i.current) { const { item: o, itemHierarchicalIndex: s, nativeEvent: d, syntheticEvent: P } = e, w = { level: Ne(s), item: o, nativeEvent: d, syntheticEvent: P, target: i.current }; r.onExpandChange.call(void 0, w); } }, [r.onExpandChange] ), ye = 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 && Ze(e.target.value); } }, [r.onFilterChange, r.filter, r.textField] ), Ye = () => { const e = a.filterable ? /* @__PURE__ */ t.createElement( Me, { value: a.filter === void 0 ? de : a.filter, ref: Q, onChange: ye, onKeyDown: ve, size: "large", rounded: z, fillMode: q, placeholder: B } ) : null, o = { title: a.adaptiveTitle || I, subTitle: a.adaptiveSubtitle, expand: l, onClose: (s) => x(s), windowWidth: ne, mobileFilter: e }; return /* @__PURE__ */ t.createElement(Dt, { ...o }, /* @__PURE__ */ t.createElement(Ft, null, N.length > 0 ? /* @__PURE__ */ t.createElement( Te, { ref: y, tabIndex: J, data: Ce, focusIdField: O, textField: a.textField, selectField: H, expandField: a.expandField, childrenField: L, expandIcons: !0, onItemClick: be, onExpandChange: Ee, size: "large", item: a.item, dir: k, animate: b.animate } ) : /* @__PURE__ */ t.createElement(ke, null, oe.toLanguageString(X, re[X])))); }, et = t.useCallback((e) => { for (const o of e) _e(o.target.clientWidth); }, []), Ce = t.useMemo(() => C || !K ? N : ut( N, L, (e) => dt(e, L, { [H]: Re(e, v, O) }) ), [N, v, C, K, H, L]), ke = a.listNoData || yt, tt = a.valueHolder || Mt, oe = vt(), xe = !Ke || ee.valid, { size: R, rounded: z, fillMode: q } = a, we = /* @__PURE__ */ t.createElement(t.Fragment, null, /* @__PURE__ */ t.createElement( "span", { className: ae("k-dropdowntree k-picker", a.className, { [`k-picker-${It[R] || R}`]: R, [`k-rounded-${St[z] || z}`]: z, [`k-picker-${q}`]: q, "k-focus": p, "k-invalid": !xe, "k-loading": a.loading, "k-required": G, "k-disabled": a.disabled }), tabIndex: J, accessKey: a.accessKey, id: le, style: I ? { ...V, width: void 0 } : V, dir: k, ref: f, onKeyDown: F ? void 0 : Ue, onMouseDown: Qe, onClick: F ? void 0 : fe, onFocus: h ? (e) => fe(e) : Ge, onBlur: Je, role: "combobox", "aria-haspopup": "tree", "aria-expanded": l, "aria-disabled": F, "aria-label": I, "aria-labelledby": a.ariaLabelledBy, "aria-describedby": a.ariaDescribedBy, "aria-required": G }, /* @__PURE__ */ t.createElement("span", { className: "k-input-inner" }, (Y || B) && /* @__PURE__ */ t.createElement(tt, { item: v }, Y || B)), a.loading && /* @__PURE__ */ t.createElement(Se, { className: "k-input-loading-icon", name: "loading" }), K && !F && /* @__PURE__ */ t.createElement( "span", { onClick: he, className: "k-clear-value", title: oe.toLanguageString(Pe, re[Pe]), role: "button", tabIndex: -1, onMouseDown: (e) => e.preventDefault() }, /* @__PURE__ */ t.createElement(Se, { name: "x", icon: xt }) ), /* @__PURE__ */ t.createElement( kt, { tabIndex: -1, type: "button", "aria-label": "select", className: "k-input-button", size: R, fillMode: q, themeColor: "base", rounded: null, icon: "caret-alt-down", svgIcon: wt } ), /* @__PURE__ */ t.createElement( "select", { name: We, ref: Z, tabIndex: -1, "aria-hidden": !0, title: I, 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 }) ), !h && /* @__PURE__ */ t.createElement(Ie.Provider, { value: qe }, /* @__PURE__ */ t.createElement( ft, { ...b, className: ae(b.className, { "k-rtl": k === "rtl" }), popupClass: ae(b.popupClass, "k-dropdowntree-popup k-list-container"), style: Be, anchor: b.anchor || f.current, show: l, onOpen: Xe, onClose: je }, a.filterable && /* @__PURE__ */ t.createElement( Me, { value: a.filter === void 0 ? de : a.filter, ref: S, onChange: ye, onKeyDown: ve, size: R, rounded: z, fillMode: q, renderListFilterWrapper: !0, renderPrefixSeparator: !0 } ), N.length > 0 ? /* @__PURE__ */ t.createElement( Te, { style: { height: b.height }, ref: y, tabIndex: J, data: Ce, focusIdField: O, textField: a.textField, selectField: H, expandField: a.expandField, childrenField: L, expandIcons: !0, onItemClick: be, onExpandChange: Ee, size: R, item: a.item, dir: k, animate: b.animate } ) : /* @__PURE__ */ t.createElement(ke, null, oe.toLanguageString(X, re[X])), j && /* @__PURE__ */ t.createElement(pt, null) )) ), h && Ye()); return I ? /* @__PURE__ */ t.createElement( Ct, { label: I, editorValue: Y, editorPlaceholder: B, editorValid: xe, editorDisabled: F, editorId: le, style: { width: V ? V.width : void 0 }, children: we, dir: k } ) : we; }), 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, adaptiveSubtitle: n.string, adaptive: n.bool }; Oe.displayName = "KendoReactDropDownTree"; Oe.propTypes = Vt; export { Oe as DropDownTree, Ot as DropDownTreePropsContext };