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

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