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

580 lines (579 loc) • 18.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, usePropsContext as at, validatePackage as it, useId as rt, getTabIndex as ot, useRtl as lt, useAdaptiveModeContext as st, canUseDOM as ct, noop as Y, Keys as r, classNames as Z, IconWrap as ut, WatermarkOverlay as dt, kendoThemeMaps as pt } from "@progress/kendo-react-common"; import { Popup as ft } from "@progress/kendo-react-popup"; import { useLocalization as mt } from "@progress/kendo-react-intl"; import { TreeView as Ie } from "@progress/kendo-react-treeview"; import { packageMetadata as gt } from "../package-metadata.mjs"; import { getItemValue as vt, areSame as ht, matchTags as Te } from "../common/utils.mjs"; import { useDropdownWidth as bt } from "../DropDownTree/useDropdownWidth.mjs"; import { ListNoData as yt } from "../DropDownTree/ListNoData.mjs"; import { nodata as ee, messages as De } from "../messages/index.mjs"; import { FloatingLabel as Ct } from "@progress/kendo-react-labels"; import Et from "../MultiSelect/TagList.mjs"; import kt from "../common/ClearButton.mjs"; import Re from "../common/ListFilter.mjs"; import { AdaptiveMode as Ft } from "../common/AdaptiveMode.mjs"; import { ActionSheetContent as xt } from "@progress/kendo-react-layout"; const St = "Please select a value from the list!", { sizeMap: Me, roundedMap: wt } = pt, It = (N) => N.split("_").map((R) => parseInt(R, 10)), Tt = (N, R) => { const { validationMessage: a, valid: V, required: te } = N; return { customError: a !== void 0, valid: !!(V !== void 0 ? V : !te || R), valueMissing: !R }; }, b = { checkField: "checkField", checkIndeterminateField: "checkIndeterminateField", subItemsField: "items", popupSettings: { animate: !0, width: "200px", height: "200px" }, size: "medium", rounded: "medium", fillMode: "solid", required: !1, validityStyles: !0 }, Dt = nt(), Pe = t.forwardRef( (N, R) => { const a = at(Dt, N), V = !it(gt, { component: "MultiSelectTree" }), te = rt(), O = a.id || te, { data: W = [], dataItemKey: x, style: B = {}, placeholder: ne, label: K, name: Ne, validationMessage: q, valid: Oe, tags: ue, value: m, opened: S, disabled: E, popupSettings: M = b.popupSettings, checkField: de = b.checkField, checkIndeterminateField: pe = b.checkIndeterminateField, subItemsField: fe = b.subItemsField, size: y = b.size, rounded: L = b.rounded, fillMode: z = b.fillMode, required: me = b.required, validityStyles: Be = b.validityStyles, onOpen: ae = Y, onClose: A = Y } = a, $ = ot(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, Ke] = t.useState(), [Le, ge] = t.useState(!1), [d, re] = t.useState(!1), [oe, Ve] = t.useState(), [We, qe] = t.useState([]), [ve, ze] = t.useState(""), s = S !== void 0 ? S : Le, le = !!(Array.isArray(m) && m.length), se = Tt({ validationMessage: q, valid: Oe, required: me }, le), I = lt(C, a.dir), Ae = { width: bt(C, b, M, B), ...I !== void 0 ? { direction: I } : {} }, $e = t.useCallback(() => C.current && C.current.focus(), []), he = st(), T = !!(oe && he && oe <= he.medium && a.adaptive); _.current = ue === void 0 ? (m || []).map((e) => ({ text: vt(e, a.textField), data: [e] })) : [...ue], t.useImperativeHandle(o, () => ({ props: a, element: C.current, focus: $e })), t.useImperativeHandle( R, () => o.current ); const He = t.useCallback(() => { H.current && H.current.setCustomValidity && H.current.setCustomValidity( se.valid ? "" : q === void 0 ? St : q ); }, [q, se]); t.useEffect(He), t.useEffect(() => { const e = ct && window.ResizeObserver && new window.ResizeObserver(et.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 (ht(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), qe(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] ), Ge = 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]), _e = 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] ), Ue = 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] ), Je = 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] ), Qe = 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((tt) => Te(tt, 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 && Ke(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] ), Xe = 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] ), Ye = 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: It(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 && ze(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 Ze = () => { 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: L, 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(Ft, { ...i }, /* @__PURE__ */ t.createElement(xt, null, W.length > 0 ? /* @__PURE__ */ t.createElement( Ie, { 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: "large", item: a.item, dir: I } ) : /* @__PURE__ */ t.createElement(ke, null, Fe.toLanguageString(ee, De[ee])))); }, et = t.useCallback((e) => { for (const i of e) Ve(i.target.clientWidth); }, []), ke = a.listNoData || yt, Fe = mt(), xe = !Be || se.valid, Q = T && s ? We : _.current, Se = /* @__PURE__ */ t.createElement(t.Fragment, null, /* @__PURE__ */ t.createElement( "span", { className: Z("k-multiselecttree k-input", a.className, { [`k-input-${Me[y] || y}`]: y, [`k-rounded-${wt[L] || L}`]: L, [`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: K ? { ...B, width: void 0 } : B, dir: I, ref: C, onKeyDown: E ? void 0 : Qe, onMouseDown: Je, onFocus: (e) => { T ? be(e) : je(e); }, onBlur: Ue, role: "combobox", "aria-haspopup": "tree", "aria-expanded": s, "aria-disabled": E, "aria-label": K, "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-${Me[y] || y}`]: y }) }, Q.length > 0 && /* @__PURE__ */ t.createElement( Et, { tag: a.tag, onTagDelete: Ye, data: Q, guid: O, focused: P ? _.current.find( (e) => Te(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(ut, { className: "k-input-loading-icon", name: "loading" }), le && !E && Q.length > 0 && /* @__PURE__ */ t.createElement(kt, { onClick: Xe }), /* @__PURE__ */ t.createElement( "select", { name: Ne, ref: H, tabIndex: -1, "aria-hidden": !0, title: K, 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( ft, { ...M, popupClass: Z(M.popupClass, "k-multiselecttree-popup"), className: Z(M.className, { "k-rtl": I === "rtl" }), style: Ae, anchor: M.anchor || C.current, show: s, onOpen: Ge, onClose: _e, 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: L, fillMode: z, renderListFilterWrapper: !0, renderPrefixSeparator: !0 } ), W.length > 0 ? /* @__PURE__ */ t.createElement( Ie, { style: { height: M.height }, 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, De[ee])), V && /* @__PURE__ */ t.createElement(dt, null) ) ), T && Ze()); return K ? /* @__PURE__ */ t.createElement( Ct, { label: K, editorValue: le, editorPlaceholder: ne, editorValid: xe, editorDisabled: E, editorId: O, style: { width: B ? B.width : void 0 }, children: Se, dir: I } ) : Se; } ), Rt = { 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 }; Pe.displayName = "KendoReactMultiSelectTree"; Pe.propTypes = Rt; export { Pe as MultiSelectTree, Dt as MultiSelectTreePropsContext };