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