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

585 lines (584 loc) • 19.2 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 it, usePropsContext as ot, validatePackage as lt, useId as st, getTabIndex as ct, useRtl as ut, useAdaptiveModeContext as dt, canUseDOM as pt, noop as Z, Keys as o, classNames as ee, IconWrap as ft, WatermarkOverlay as mt, kendoThemeMaps as gt } from "@progress/kendo-react-common"; import { Popup as vt } from "@progress/kendo-react-popup"; import { useLocalization as ht } from "@progress/kendo-react-intl"; import { TreeView as De } from "@progress/kendo-react-treeview"; import { packageMetadata as bt } from "../package-metadata.mjs"; import { getItemValue as yt, areSame as Ct, matchTags as Me } from "../common/utils.mjs"; import { useDropdownWidth as kt } from "../DropDownTree/useDropdownWidth.mjs"; import { ListNoData as Et } from "../DropDownTree/ListNoData.mjs"; import { nodata as te, messages as ne, adaptiveModeFooterCancel as Re, adaptiveModeFooterApply as Ne } from "../messages/index.mjs"; import { FloatingLabel as Ft } from "@progress/kendo-react-labels"; import xt from "../MultiSelect/TagList.mjs"; import St from "../common/ClearButton.mjs"; import Pe from "../common/ListFilter.mjs"; import { AdaptiveMode as wt } from "../common/AdaptiveMode.mjs"; import { ActionSheetContent as It } from "@progress/kendo-react-layout"; const Tt = "Please select a value from the list!", { sizeMap: Oe, roundedMap: Dt } = gt, Mt = (P) => P.split("_").map((D) => parseInt(D, 10)), Rt = (P, D) => { const { validationMessage: a, valid: V, required: ae } = P; return { customError: a !== void 0, valid: !!(V !== void 0 ? V : !ae || D), valueMissing: !D }; }, y = { checkField: "checkField", checkIndeterminateField: "checkIndeterminateField", subItemsField: "items", popupSettings: { animate: !0, width: "200px", height: "200px" }, size: "medium", rounded: "medium", fillMode: "solid", required: !1, validityStyles: !0 }, Nt = it(), Le = t.forwardRef( (P, D) => { const a = ot(Nt, P), V = !lt(bt, { component: "MultiSelectTree" }), ae = st(), O = a.id || ae, { data: W = [], dataItemKey: x, style: L = {}, placeholder: pe, label: B, name: Be, validationMessage: q, valid: Ke, tags: fe, value: m, opened: S, disabled: k, popupSettings: M = y.popupSettings, checkField: me = y.checkField, checkIndeterminateField: ge = y.checkIndeterminateField, subItemsField: ve = y.subItemsField, size: v = y.size, rounded: K = y.rounded, fillMode: A = y.fillMode, required: he = y.required, validityStyles: Ve = y.validityStyles, onOpen: re = Z, onClose: z = Z } = a, H = ct(a.tabIndex, k), l = t.useRef(null), C = t.useRef(null), w = t.useRef(null), ie = t.useRef(null), $ = t.useRef(null), E = t.useRef(null), G = t.useRef(!1), _ = t.useRef([]), j = t.useRef(null), [R, We] = t.useState(), [qe, be] = t.useState(!1), [d, oe] = t.useState(!1), [le, Ae] = t.useState(), [ze, He] = t.useState([]), [ye, $e] = t.useState(""), s = S !== void 0 ? S : qe, se = !!(Array.isArray(m) && m.length), ce = Rt({ validationMessage: q, valid: Ke, required: he }, se), I = ut(C, a.dir), Ge = { width: kt(C, y, M, L), ...I !== void 0 ? { direction: I } : {} }, _e = t.useCallback(() => C.current && C.current.focus(), []), Ce = dt(), N = !!(le && Ce && le <= Ce.medium && a.adaptive); _.current = fe === void 0 ? (m || []).map((e) => ({ text: yt(e, a.textField), data: [e] })) : [...fe], t.useImperativeHandle(l, () => ({ props: a, element: C.current, focus: _e })), t.useImperativeHandle( D, () => l.current ); const je = t.useCallback(() => { $.current && $.current.setCustomValidity && $.current.setCustomValidity( ce.valid ? "" : q === void 0 ? Tt : q ); }, [q, ce]); t.useEffect(je), t.useEffect(() => { const e = pt && window.ResizeObserver && new window.ResizeObserver(at.bind(void 0)); return document != null && document.body && e && e.observe(document.body), () => { e && e.disconnect(); }; }, []), t.useEffect(() => { j.current && s && j.current.setPosition(j.current.element); }, [m, s]); const F = t.useCallback( (e, r, i) => { if (a.onChange) { const b = { items: r, operation: i, ...e }; a.onChange.call(void 0, b); } }, [a.onChange] ), U = t.useCallback( (e) => { if (Ct(e.item, m, x) || !l.current) return; const { item: r, nativeEvent: i, syntheticEvent: b } = e, T = { syntheticEvent: b, nativeEvent: i, target: l.current }; F(T, [r], "toggle"); }, [m, x, F] ), J = t.useCallback( (e) => { if (!s) { if (re) { const r = { ...e }; re.call(void 0, r); } S === void 0 && (be(!0), He(m || []), N && setTimeout(() => { var r; u((r = ie.current) == null ? void 0 : r.element); }, 300)); } }, [s, S, re, N, ie] ), g = t.useCallback( (e) => { if (s) { if (z) { const r = { ...e }; z.call(void 0, r); } S === void 0 && be(!1); } }, [s, S, z] ), h = t.useCallback((e) => { G.current = !0, e(), window.setTimeout(() => G.current = !1, 0); }, []), u = t.useCallback( (e) => { e && h(() => e.focus()); }, [h] ), Ue = t.useCallback(() => { var e; !d && s ? g({ target: l.current }) : a.filterable ? u((e = w.current) == null ? void 0 : e.element) : u(E.current && E.current.element); }, [d, s, u, g, a.filterable]), Je = t.useCallback(() => { d && u(C.current); }, [d, u]), Qe = t.useCallback( (e) => { if (!d && !G.current && (oe(!0), a.onFocus && l.current)) { const r = { syntheticEvent: e, nativeEvent: e.nativeEvent, target: l.current }; a.onFocus.call(void 0, r); } }, [d, a.onFocus] ), Xe = t.useCallback( (e) => { if (d && !G.current && l.current) { oe(!1); const r = { syntheticEvent: e, nativeEvent: e.nativeEvent, target: l.current }; if (a.onBlur) { const i = { ...r }; a.onBlur.call(void 0, i); } N || g(r); } }, [d, a.onBlur, s, S, z] ), Ye = t.useCallback(() => { d && h(Z); }, [d, h]), Ze = t.useCallback( (e) => { if (!e.isDefaultPrevented() && l.current) { oe(!0); const r = { syntheticEvent: e, nativeEvent: e.nativeEvent, target: l.current }; s || J(r); } }, [s, J] ), et = t.useCallback( (e) => { var Y, Te; const { keyCode: r, altKey: i } = e, b = E.current && E.current.element; if (!l.current || e.isDefaultPrevented() && ((Y = w.current) == null ? void 0 : Y.element) === e.target) return; const T = { syntheticEvent: e, nativeEvent: e.nativeEvent, target: l.current }; if (m && m.length > 0 && (r === o.left || r === o.right || r === o.home || r === o.end || r === o.delete)) { const c = _.current; let p = R ? c.findIndex((rt) => Me(rt, R, x)) : -1, f; const de = p !== -1; r === o.left ? (de ? p = Math.max(0, p - 1) : p = c.length - 1, f = c[p]) : r === o.right ? p === c.length - 1 ? f = void 0 : de && (p = Math.min(c.length - 1, p + 1), f = c[p]) : r === o.home ? f = c[0] : r === o.end ? f = c[c.length - 1] : r === o.delete && de && F(T, c[p].data, "delete"), f !== R && We(f); } if (s) if (r === o.esc || i && r === o.up) e.preventDefault(), g(T); else if (b && b.querySelector(".k-focus") && (r === o.up || r === o.down || r === o.left || r === o.right || r === o.home || r === o.end)) { if (r === o.up && ((Te = w.current) != null && Te.element)) { const c = Array.from(b.querySelectorAll(".k-treeview-item")), p = [...c].reverse().find((f) => !!(f && f.querySelector(".k-focus"))); if (p && c.indexOf(p) === 0) return h(() => { var f; u((f = w.current) == null ? void 0 : f.element); }); } h(Z); } else r === o.down && h(() => { var c; u(((c = w.current) == null ? void 0 : c.element) || b); }); else i && r === o.down && (e.preventDefault(), J(T)); }, [s, g, u, h, J, R, x, m, F] ), ke = t.useCallback( (e) => { const { keyCode: r, altKey: i } = e; i || r !== o.up && r !== o.down || (e.preventDefault(), h( r === o.up ? () => { u(C.current); } : () => { u(E.current && E.current.element); } )); }, [u, h] ), ue = t.useCallback( (e) => { if (!l.current) return; const r = { syntheticEvent: e, nativeEvent: e.nativeEvent, target: l.current }; F(r, [], "clear"), g(r), e.preventDefault(); }, [F, g] ), tt = t.useCallback( (e, r) => { var i; g({ target: l.current }), d || u((i = w.current) == null ? void 0 : i.element), F( { syntheticEvent: r, nativeEvent: r.nativeEvent, target: l.current }, e, "delete" ); }, [F, g, u, d] ), Ee = t.useCallback( (e) => { if (a.onExpandChange && l.current) { const { item: r, itemHierarchicalIndex: i, nativeEvent: b, syntheticEvent: T } = e, Y = { level: Mt(i), item: r, nativeEvent: b, syntheticEvent: T, target: l.current }; a.onExpandChange.call(void 0, Y); } }, [a.onExpandChange] ), Fe = t.useCallback( (e) => { if (a.onFilterChange && l.current) { const i = { filter: { field: a.textField, operator: "contains", value: e.target.value }, syntheticEvent: e.syntheticEvent, nativeEvent: e.nativeEvent, target: l.current }; a.onFilterChange.call(void 0, i), a.filter === void 0 && $e(e.target.value); } }, [a.onFilterChange, a.filter, a.textField] ), xe = t.useCallback( (e) => { if (!l.current) return; const r = { syntheticEvent: e, nativeEvent: e.nativeEvent, target: l.current }; a.onCancel && a.onCancel.call(void 0, r), g(r), e.preventDefault(); }, [a.onCancel, g] ), nt = () => { const e = a.filterable ? /* @__PURE__ */ t.createElement( Pe, { value: a.filter === void 0 ? ye : a.filter, ref: ie, onChange: Fe, onKeyDown: ke, size: v, rounded: K, fillMode: A } ) : null, r = { title: a.adaptiveTitle, expand: s, onClose: (i) => a.onCancel ? xe(i) : ue(i), windowWidth: le, mobileFilter: e, footer: { cancelText: Q.toLanguageString( Re, ne[Re] ), onCancel: (i) => a.onCancel ? xe(i) : ue(i), applyText: Q.toLanguageString( Ne, ne[Ne] ), onApply: (i) => g(i) } }; return /* @__PURE__ */ t.createElement(wt, { ...r }, /* @__PURE__ */ t.createElement(It, { overflowHidden: !0 }, /* @__PURE__ */ t.createElement("div", { className: "k-list-container" }, /* @__PURE__ */ t.createElement("div", { className: "k-list k-list-lg" }, W.length > 0 ? /* @__PURE__ */ t.createElement( De, { ref: E, tabIndex: H, data: W, focusIdField: x, textField: a.textField, checkField: me, checkIndeterminateField: ge, expandField: a.expandField, childrenField: ve, expandIcons: !0, onItemClick: U, onCheckChange: U, onExpandChange: Ee, checkboxes: !0, size: v, item: a.item, dir: I } ) : /* @__PURE__ */ t.createElement(Se, null, Q.toLanguageString(te, ne[te])))))); }, at = t.useCallback((e) => { for (const r of e) Ae(r.target.clientWidth); }, []), Se = a.listNoData || Et, Q = ht(), we = !Ve || ce.valid, X = N && s ? ze : _.current, Ie = /* @__PURE__ */ t.createElement(t.Fragment, null, /* @__PURE__ */ t.createElement( "span", { className: ee("k-multiselecttree k-input", a.className, { [`k-input-${Oe[v] || v}`]: v, [`k-rounded-${Dt[K] || K}`]: K, [`k-input-${A}`]: A, "k-focus": d && !k, "k-invalid": !we, "k-disabled": k, "k-loading": a.loading, "k-required": he }), tabIndex: H, accessKey: a.accessKey, id: O, style: B ? { ...L, width: void 0 } : L, dir: I, ref: C, onKeyDown: k ? void 0 : et, onMouseDown: Ye, onFocus: Qe, onBlur: Xe, role: "combobox", "aria-haspopup": "tree", "aria-expanded": s, "aria-disabled": k, "aria-label": B, "aria-labelledby": a.ariaLabelledBy, "aria-describedby": a.ariaDescribedBy ? a.ariaDescribedBy : "tagslist-" + O, "aria-required": a.required, onClick: k ? void 0 : Ze }, /* @__PURE__ */ t.createElement( "div", { id: "tagslist-" + O, className: ee("k-input-values k-chip-list", { [`k-chip-list-${Oe[v] || v}`]: v }) }, X.length > 0 && /* @__PURE__ */ t.createElement( xt, { tag: a.tag, onTagDelete: tt, data: X, guid: O, focused: R ? _.current.find( (e) => Me(e, R, x) ) : void 0, size: v } ) ), /* @__PURE__ */ t.createElement("span", { className: "k-input-inner" }, X.length === 0 && /* @__PURE__ */ t.createElement("span", { className: "k-input-value-text" }, pe)), a.loading && /* @__PURE__ */ t.createElement(ft, { className: "k-input-loading-icon", name: "loading" }), se && !k && X.length > 0 && /* @__PURE__ */ t.createElement(St, { onClick: ue }), /* @__PURE__ */ t.createElement( "select", { name: Be, ref: $, tabIndex: -1, "aria-hidden": !0, title: B, 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, m) : m }) ), !N && /* @__PURE__ */ t.createElement( vt, { ...M, popupClass: ee(M.popupClass, "k-multiselecttree-popup"), className: ee(M.className, { "k-rtl": I === "rtl" }), style: Ge, anchor: M.anchor || C.current, show: s, onOpen: Ue, onClose: Je, ref: j }, a.filterable && /* @__PURE__ */ t.createElement( Pe, { value: a.filter === void 0 ? ye : a.filter, ref: w, onChange: Fe, onKeyDown: ke, tabIndex: H, size: v, rounded: K, fillMode: A, renderListFilterWrapper: !0, renderPrefixSeparator: !0 } ), W.length > 0 ? /* @__PURE__ */ t.createElement( De, { style: { height: M.height }, ref: E, tabIndex: H, data: W, focusIdField: x, textField: a.textField, checkField: me, checkIndeterminateField: ge, expandField: a.expandField, childrenField: ve, expandIcons: !0, onItemClick: U, onCheckChange: U, onExpandChange: Ee, checkboxes: !0, size: v, item: a.item, dir: I } ) : /* @__PURE__ */ t.createElement(Se, null, Q.toLanguageString(te, ne[te])), V && /* @__PURE__ */ t.createElement(mt, null) ) ), N && nt()); return B ? /* @__PURE__ */ t.createElement( Ft, { label: B, editorValue: se, editorPlaceholder: pe, editorValid: we, editorDisabled: k, editorId: O, style: { width: L ? L.width : void 0 }, children: Ie, dir: I } ) : Ie; } ), Pt = { 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, checkField: n.string, checkIndeterminateField: 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, tags: n.arrayOf( n.shape({ text: n.string, data: n.arrayOf(n.any) }) ), 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, onCancel: n.func, item: n.func, listNoData: n.func, adaptiveTitle: n.string, adaptive: n.bool }; Le.displayName = "KendoReactMultiSelectTree"; Le.propTypes = Pt; export { Le as MultiSelectTree, Nt as MultiSelectTreePropsContext };