@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
JavaScript
/**
* @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
};