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

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