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

532 lines (531 loc) • 16.7 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 = (y) => /* @__PURE__ */ t.createElement("span", { className: "k-input-value-text" }, y.children), Ne = (y) => y.split("_").map((D) => parseInt(D, 10)), Nt = (y, D) => { const { validationMessage: j, valid: r, required: a } = y; 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((y, D) => { const j = !ot(gt, { component: "DropDownTree" }), r = at(Ot, y), a = { ...ie, ...r }, Ve = rt(), le = a.id || Ve, { data: M, dataItemKey: N, popupSettings: b = {}, style: O, opened: u, disabled: F, onOpen: V = U, onClose: m = U, placeholder: A, label: I, name: We, selectField: H, subItemsField: W, 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), h = 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, L = ht(v), Y = L ? bt(v, a.textField) : "", ee = Nt({ validationMessage: _, valid: Le, required: G }, L), ce = t.useContext(Ie), Be = ce ? ce + Rt : Tt, qe = t.useCallback(() => f.current && f.current.focus(), []); t.useImperativeHandle(i, () => ({ props: a, element: f.current, focus: qe })), t.useImperativeHandle(D, () => i.current); const k = lt(f, a.dir), Ae = { width: Et( f, ie, { ...ie.popupSettings, ...b }, O ), ...k !== void 0 ? { direction: k } : {} }, [He, ue] = t.useState(!1), l = u !== void 0 ? u : He, [d, te] = t.useState(!1), [ne, _e] = t.useState(), [de, Ze] = t.useState(""), pe = st(), E = !!(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 (V) { const o = { ...e }; V.call(void 0, o); } u === void 0 && ue(!0); } }, [l, u, V] ), x = t.useCallback( (e) => { if (l) { if (m) { const o = { ...e }; m.call(void 0, o); } u === void 0 && (ue(!1), E && setTimeout(() => { var o; g((o = Q.current) == null ? void 0 : o.element); }, 300)); } }, [l, u, m, E] ), 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, V, 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, p = h.current && h.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 (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 && ((De = S.current) != null && De.element)) { const B = Array.from(p.querySelectorAll(".k-treeview-item")), Fe = [...B].reverse().find((q) => !!(q && q.querySelector(".k-focus"))); if (Fe && B.indexOf(Fe) === 0) return T(() => { var q; g((q = S.current) == null ? void 0 : q.element); }); } T(U); } else o === c.down && T(() => { var B; g(((B = S.current) == null ? void 0 : B.element) || p); }); else s && o === c.down ? (e.preventDefault(), me(P)) : l || o === c.esc && he(e); }, [l, u, V, 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); } : () => { g(h.current && h.current.element); } )); }, []), g = t.useCallback((e) => { e && T(() => e.focus()); }, []), Xe = t.useCallback(() => { var e; !d && l && !u ? x({ 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 && !$.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 && !$.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); } E || x(o); } }, [d, r.onBlur, l, u, m] ), Qe = t.useCallback(() => { d && T(U), E && setTimeout(() => { var e; g((e = Q.current) == null ? void 0 : e.element); }, 300); }, [d, E]), ge = t.useCallback( (e, o, s) => { if (r.onChange) { const p = { value: o, level: s ? Ne(s) : [], ...e }; r.onChange.call(void 0, p); } C || ze(o); }, [r.onChange, C] ), be = t.useCallback( (e) => { if (Re(e.item, v, N) || !i.current) return; const { item: o, itemHierarchicalIndex: s, nativeEvent: p, syntheticEvent: P } = e, w = { syntheticEvent: P, nativeEvent: p, target: i.current }; ge(w, o, s), x(w); }, [C, v, r.onChange, N, 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: p, syntheticEvent: P } = e, w = { level: Ne(s), item: o, nativeEvent: p, 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: K, fillMode: z, placeholder: A } ) : 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, M.length > 0 ? /* @__PURE__ */ t.createElement( Te, { ref: h, tabIndex: J, data: Ce, focusIdField: N, textField: a.textField, selectField: H, expandField: a.expandField, childrenField: W, 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 || !L ? M : ut( M, W, (e) => dt(e, W, { [H]: Re(e, v, N) }) ), [M, v, C, L, H, W]), ke = a.listNoData || yt, tt = a.valueHolder || Mt, oe = vt(), xe = !Ke || ee.valid, { size: R, rounded: K, fillMode: z } = 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[K] || K}`]: K, [`k-picker-${z}`]: z, "k-focus": d, "k-invalid": !xe, "k-loading": a.loading, "k-required": G, "k-disabled": a.disabled }), tabIndex: J, accessKey: a.accessKey, id: le, style: I ? { ...O, width: void 0 } : O, dir: k, ref: f, onKeyDown: F ? void 0 : Ue, onMouseDown: Qe, onClick: F ? void 0 : fe, onFocus: E ? (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 || A) && /* @__PURE__ */ t.createElement(tt, { item: v }, Y || A)), a.loading && /* @__PURE__ */ t.createElement(Se, { className: "k-input-loading-icon", name: "loading" }), L && !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: z, 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 }) ), !E && /* @__PURE__ */ t.createElement(Ie.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( Me, { value: a.filter === void 0 ? de : a.filter, ref: S, onChange: ye, onKeyDown: ve, size: R, rounded: K, fillMode: z, renderListFilterWrapper: !0, renderPrefixSeparator: !0 } ), M.length > 0 ? /* @__PURE__ */ t.createElement( Te, { style: { height: b.height }, ref: h, tabIndex: J, data: Ce, focusIdField: N, textField: a.textField, selectField: H, expandField: a.expandField, childrenField: W, 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) )) ), E && Ye()); return I ? /* @__PURE__ */ t.createElement( Ct, { label: I, editorValue: Y, editorPlaceholder: A, editorValid: xe, editorDisabled: F, editorId: le, style: { width: O ? O.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 };