@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
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 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
};