@progress/kendo-react-dropdowns
Version:
React DropDowns offer an interface for users to select different items from a list and more. KendoReact Dropdowns package
539 lines (538 loc) • 16.9 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 at, validatePackage as rt, getLicenseMessage as it, usePropsContext as lt, useId as st, getTabIndex as ct, ZIndexContext as Se, useRtl as ut, useAdaptiveModeContext as dt, canUseDOM as pt, Keys as c, noop as X, mapTree as ft, extendDataItem as mt, classNames as re, IconWrap as Te, WatermarkOverlay as vt, kendoThemeMaps as gt } from "@progress/kendo-react-common";
import { Popup as bt } from "@progress/kendo-react-popup";
import { useLocalization as ht } from "@progress/kendo-react-intl";
import { TreeView as Re } from "@progress/kendo-react-treeview";
import { packageMetadata as Me } from "../package-metadata.mjs";
import { getItemValue as Et, areSame as Pe, isPresent as yt } from "../common/utils.mjs";
import { useDropdownWidth as Ct } from "./useDropdownWidth.mjs";
import { ListNoData as kt } from "./ListNoData.mjs";
import { clear as Ne, messages as ie, nodata as j } from "../messages/index.mjs";
import { FloatingLabel as xt } from "@progress/kendo-react-labels";
import Oe from "../common/ListFilter.mjs";
import { Button as wt } from "@progress/kendo-react-buttons";
import { xIcon as Dt, caretAltDownIcon as Ft } from "@progress/kendo-svg-icons";
import { AdaptiveMode as It } from "../common/AdaptiveMode.mjs";
import { ActionSheetContent as St } from "@progress/kendo-react-layout";
const { sizeMap: Tt, roundedMap: Rt } = gt, Mt = 12e3, Pt = 2e3, Nt = "Please select a value from the list!", Ot = (E) => /* @__PURE__ */ t.createElement("span", { className: "k-input-value-text" }, E.children), Ve = (E) => E.split("_").map((D) => parseInt(D, 10)), Vt = (E, D) => {
const { validationMessage: G, valid: B, required: r } = E;
return {
customError: G !== void 0,
valid: !!(B !== void 0 ? B : !r || D),
valueMissing: !D
};
}, le = {
selectField: "selected",
subItemsField: "items",
popupSettings: {
animate: !0,
width: "200px",
height: "200px"
},
data: [],
required: !1,
style: {},
validityStyles: !0,
size: "medium",
rounded: "medium",
fillMode: "solid"
}, Lt = at(), Le = t.forwardRef((E, D) => {
const G = !rt(Me, { component: "DropDownTree" }), B = it(Me), r = lt(Lt, E), a = {
...le,
...r
}, We = st(), se = a.id || We, {
data: N,
dataItemKey: O,
popupSettings: f = {},
style: V,
opened: u,
disabled: F,
onOpen: L = X,
onClose: m = X,
placeholder: H,
label: I,
name: Ke,
selectField: _,
subItemsField: W,
validationMessage: Z,
valid: ze,
required: J,
validityStyles: qe
} = a, Q = ct(a.tabIndex, F), i = t.useRef(null), v = t.useRef(null), S = t.useRef(null), Y = t.useRef(null), $ = t.useRef(null), y = t.useRef(null), U = t.useRef(!1), [ce, Ae] = t.useState(void 0), C = a.value !== void 0, g = C ? a.value : ce !== void 0 ? ce : a.defaultValue, K = yt(g), ee = K ? Et(g, a.textField) : "", te = Vt({ validationMessage: Z, valid: ze, required: J }, K), ue = t.useContext(Se), Be = ue ? ue + Pt : Mt, He = t.useCallback(() => v.current && v.current.focus(), []);
t.useImperativeHandle(i, () => ({
props: a,
element: v.current,
focus: He
})), t.useImperativeHandle(D, () => i.current);
const k = ut(v, a.dir), _e = {
width: Ct(
v,
le,
{ ...le.popupSettings, ...f },
V
),
...k !== void 0 ? { direction: k } : {}
}, [Ze, de] = t.useState(!1), l = u !== void 0 ? u : Ze, [p, ne] = t.useState(!1), [oe, $e] = t.useState(), [pe, Ue] = t.useState(""), fe = dt(), h = !!(oe && fe && oe <= fe.medium && a.adaptive), Xe = t.useCallback(() => {
$.current && $.current.setCustomValidity && $.current.setCustomValidity(
te.valid ? "" : Z === void 0 ? Nt : Z
);
}, [Z, te]);
t.useEffect(Xe), t.useEffect(() => {
const e = pt && window.ResizeObserver && new window.ResizeObserver(nt.bind(void 0));
return document != null && document.body && e && e.observe(document.body), () => {
document != null && document.body && e && e.disconnect();
};
}, []);
const me = t.useCallback(
(e) => {
if (!l) {
if (L) {
const o = { ...e };
L.call(void 0, o);
}
u === void 0 && de(!0);
}
},
[l, u, L]
), x = t.useCallback(
(e) => {
if (l) {
if (m) {
const o = { ...e };
m.call(void 0, o);
}
u === void 0 && (de(!1), h && setTimeout(() => {
var o;
b((o = Y.current) == null ? void 0 : o.element);
}, 300));
}
},
[l, u, m, h]
), ve = t.useCallback(
(e) => {
if (!e.isDefaultPrevented() && i.current) {
ne(!0);
const o = {
syntheticEvent: e,
nativeEvent: e.nativeEvent,
target: i.current
};
(l ? x : me)(o);
}
},
[l, u, L, m]
), T = t.useCallback((e) => {
U.current = !0, e(), window.setTimeout(() => U.current = !1, 0);
}, []), je = t.useCallback(
(e) => {
var w, Fe;
const { keyCode: o, altKey: s } = e, d = y.current && y.current.element;
if (!i.current || e.isDefaultPrevented() && ((w = S.current) == null ? void 0 : w.element) === e.target)
return;
const M = {
syntheticEvent: e,
nativeEvent: e.nativeEvent,
target: i.current
};
if (l)
if (o === c.esc || s && o === c.up)
e.preventDefault(), x(M);
else if (d && d.querySelector(".k-focus") && (o === c.up || o === c.down || o === c.left || o === c.right || o === c.home || o === c.end)) {
if (o === c.up && ((Fe = S.current) != null && Fe.element)) {
const A = Array.from(d.querySelectorAll(".k-treeview-item")), Ie = [...A].reverse().find((P) => P == null ? void 0 : P.querySelector(".k-focus"));
if (Ie && A.indexOf(Ie) === 0)
return T(() => {
var P;
b((P = S.current) == null ? void 0 : P.element);
});
}
T(X);
} else o === c.down && T(() => {
var A;
b(((A = S.current) == null ? void 0 : A.element) || d);
});
else s && o === c.down ? (e.preventDefault(), me(M)) : l || o === c.esc && Ee(e);
},
[l, u, L, m]
), ge = t.useCallback((e) => {
const { keyCode: o, altKey: s } = e;
s || o !== c.up && o !== c.down || (e.preventDefault(), T(
o === c.up ? () => {
b(v.current);
} : () => {
var d;
b((d = y.current) == null ? void 0 : d.element);
}
));
}, []), b = t.useCallback((e) => {
e && T(() => e.focus());
}, []), Ge = t.useCallback(
(e) => {
var o;
f.onOpen && f.onOpen(e), !p && l && !u ? x({ target: i.current }) : r.filterable ? b((o = S.current) == null ? void 0 : o.element) : b(y.current && y.current.element);
},
[m, r.filterable, p, u, l]
), Je = t.useCallback(
(e) => {
f.onClose && f.onClose(e), p && b(v.current);
},
[p]
), Qe = t.useCallback(
(e) => {
if (!p && !U.current && (ne(!0), r.onFocus && i.current)) {
const o = {
syntheticEvent: e,
nativeEvent: e.nativeEvent,
target: i.current
};
r.onFocus.call(void 0, o);
}
},
[p, r.onFocus]
), Ye = t.useCallback(
(e) => {
if (p && !U.current && i.current) {
ne(!1);
const o = {
syntheticEvent: e,
nativeEvent: e.nativeEvent,
target: i.current
};
if (r.onBlur) {
const s = { ...o };
r.onBlur.call(void 0, s);
}
h || x(o);
}
},
[p, r.onBlur, l, u, m]
), et = t.useCallback(() => {
p && T(X), h && setTimeout(() => {
var e;
b((e = Y.current) == null ? void 0 : e.element);
}, 300);
}, [p, h]), be = t.useCallback(
(e, o, s) => {
if (r.onChange) {
const d = {
value: o,
level: s ? Ve(s) : [],
...e
};
r.onChange.call(void 0, d);
}
C || Ae(o);
},
[r.onChange, C]
), he = t.useCallback(
(e) => {
if (Pe(e.item, g, O) || !i.current)
return;
const { item: o, itemHierarchicalIndex: s, nativeEvent: d, syntheticEvent: M } = e, w = {
syntheticEvent: M,
nativeEvent: d,
target: i.current
};
be(w, o, s), x(w);
},
[C, g, r.onChange, O, l, u, m]
), Ee = t.useCallback(
(e) => {
if (!i.current)
return;
const o = {
syntheticEvent: e,
nativeEvent: e.nativeEvent,
target: i.current
};
be(o, null), x(o), e.preventDefault();
},
[C, r.onChange, l, u, m]
), ye = t.useCallback(
(e) => {
if (e.syntheticEvent.stopPropagation(), r.onExpandChange && i.current) {
const { item: o, itemHierarchicalIndex: s, nativeEvent: d, syntheticEvent: M } = e, w = {
level: Ve(s),
item: o,
nativeEvent: d,
syntheticEvent: M,
target: i.current
};
r.onExpandChange.call(void 0, w);
}
},
[r.onExpandChange]
), Ce = t.useCallback(
(e) => {
if (r.onFilterChange && i.current) {
const s = {
filter: {
field: r.textField,
operator: "contains",
value: e.target.value
},
syntheticEvent: e.syntheticEvent,
nativeEvent: e.nativeEvent,
target: i.current
};
r.onFilterChange.call(void 0, s), r.filter === void 0 && Ue(e.target.value);
}
},
[r.onFilterChange, r.filter, r.textField]
), tt = () => {
const e = a.filterable ? /* @__PURE__ */ t.createElement(
Oe,
{
value: a.filter === void 0 ? pe : a.filter,
ref: Y,
onChange: Ce,
onKeyDown: ge,
size: "large",
rounded: z,
fillMode: q,
placeholder: H
}
) : null, o = {
title: a.adaptiveTitle || I,
subTitle: a.adaptiveSubtitle,
expand: l,
onClose: (s) => x(s),
windowWidth: oe,
mobileFilter: e
};
return /* @__PURE__ */ t.createElement(It, { ...o }, /* @__PURE__ */ t.createElement(St, null, N.length > 0 ? /* @__PURE__ */ t.createElement(
Re,
{
ref: y,
tabIndex: Q,
data: ke,
focusIdField: O,
textField: a.textField,
selectField: _,
expandField: a.expandField,
childrenField: W,
expandIcons: !0,
onItemClick: he,
onExpandChange: ye,
size: "large",
item: a.item,
dir: k,
animate: !1
}
) : /* @__PURE__ */ t.createElement(xe, null, ae.toLanguageString(j, ie[j]))));
}, nt = t.useCallback((e) => {
for (const o of e)
$e(o.target.clientWidth);
}, []), ke = t.useMemo(() => C || !K ? N : ft(
N,
W,
(e) => mt(e, W, { [_]: Pe(e, g, O) })
), [N, g, C, K, _, W]), xe = a.listNoData || kt, ot = a.valueHolder || Ot, ae = ht(), we = !qe || te.valid, { size: R, rounded: z, fillMode: q } = a, De = /* @__PURE__ */ t.createElement(t.Fragment, null, /* @__PURE__ */ t.createElement(
"span",
{
className: re("k-dropdowntree k-picker", a.className, {
[`k-picker-${Tt[R] || R}`]: R,
[`k-rounded-${Rt[z] || z}`]: z,
[`k-picker-${q}`]: q,
"k-focus": p,
"k-invalid": !we,
"k-loading": a.loading,
"k-required": J,
"k-disabled": a.disabled
}),
tabIndex: Q,
accessKey: a.accessKey,
id: se,
style: I ? { ...V, width: void 0 } : V,
dir: k,
ref: v,
onKeyDown: F ? void 0 : je,
onMouseDown: et,
onClick: F ? void 0 : ve,
onFocus: h ? (e) => ve(e) : Qe,
onBlur: Ye,
role: "combobox",
"aria-haspopup": "tree",
"aria-expanded": l,
"aria-disabled": F,
"aria-label": I,
"aria-labelledby": a.ariaLabelledBy,
"aria-describedby": a.ariaDescribedBy,
"aria-required": J
},
/* @__PURE__ */ t.createElement("span", { className: "k-input-inner" }, (ee || H) && /* @__PURE__ */ t.createElement(ot, { item: g }, ee || H)),
a.loading && /* @__PURE__ */ t.createElement(Te, { className: "k-input-loading-icon", name: "loading" }),
K && !F && /* @__PURE__ */ t.createElement(
"span",
{
onClick: Ee,
className: "k-clear-value",
title: ae.toLanguageString(Ne, ie[Ne]),
role: "button",
tabIndex: -1,
onMouseDown: (e) => e.preventDefault()
},
/* @__PURE__ */ t.createElement(Te, { name: "x", icon: Dt })
),
/* @__PURE__ */ t.createElement(
wt,
{
tabIndex: -1,
type: "button",
"aria-label": "select",
className: "k-input-button",
size: R,
fillMode: q,
themeColor: "base",
rounded: null,
icon: "caret-alt-down",
svgIcon: Ft
}
),
/* @__PURE__ */ t.createElement(
"select",
{
name: Ke,
ref: $,
tabIndex: -1,
"aria-hidden": !0,
title: I,
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, g) : g })
),
!h && /* @__PURE__ */ t.createElement(Se.Provider, { value: Be }, /* @__PURE__ */ t.createElement(
bt,
{
...f,
className: re(f.className, { "k-rtl": k === "rtl" }),
popupClass: re(f.popupClass, "k-dropdowntree-popup k-list-container"),
style: _e,
anchor: f.anchor || v.current,
show: l,
onOpen: Ge,
onClose: Je
},
a.filterable && /* @__PURE__ */ t.createElement(
Oe,
{
value: a.filter === void 0 ? pe : a.filter,
ref: S,
onChange: Ce,
onKeyDown: ge,
size: R,
rounded: z,
fillMode: q,
renderListFilterWrapper: !0,
renderPrefixSeparator: !0
}
),
N.length > 0 ? /* @__PURE__ */ t.createElement(
Re,
{
style: { height: f.height },
ref: y,
tabIndex: Q,
data: ke,
focusIdField: O,
textField: a.textField,
selectField: _,
expandField: a.expandField,
childrenField: W,
expandIcons: !0,
onItemClick: he,
onExpandChange: ye,
size: R,
item: a.item,
dir: k,
animate: !1
}
) : /* @__PURE__ */ t.createElement(xe, null, ae.toLanguageString(j, ie[j])),
G && /* @__PURE__ */ t.createElement(vt, { message: B })
))
), h && tt());
return I ? /* @__PURE__ */ t.createElement(
xt,
{
label: I,
editorValue: ee,
editorPlaceholder: H,
editorValid: we,
editorDisabled: F,
editorId: se,
style: { width: V ? V.width : void 0 },
children: De,
dir: k
}
) : De;
}), Wt = {
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,
selectField: 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,
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,
item: n.func,
valueHolder: n.func,
listNoData: n.func,
adaptiveTitle: n.string,
adaptiveSubtitle: n.string,
adaptive: n.bool
};
Le.displayName = "KendoReactDropDownTree";
Le.propTypes = Wt;
export {
Le as DropDownTree,
Lt as DropDownTreePropsContext
};