@progress/kendo-react-buttons
Version:
All you need in React Button in one package: disabled/enabled states, built-in styles and more. KendoReact Buttons package
294 lines (293 loc) • 10.7 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 p from "react";
import s from "prop-types";
import { Button as m } from "../Button.mjs";
import { Keys as a, dispatchEvent as l, getActiveElement as c, validatePackage as C, svgIconPropType as k, classNames as d, kendoThemeMaps as f, WatermarkOverlay as b } from "@progress/kendo-react-common";
import { ButtonItem as y } from "./ButtonItem.mjs";
import { SplitButtonItem as D } from "./SplitButtonItem.mjs";
import { navigate as I } from "./utils/navigation.mjs";
import { Popup as w } from "@progress/kendo-react-popup";
import { getAnchorAlign as S, getPopupAlign as v } from "./utils/popup.mjs";
import { packageMetadata as O } from "../package-metadata.mjs";
import { caretAltDownIcon as E } from "@progress/kendo-svg-icons";
const u = class u extends p.Component {
constructor(i) {
super(i), this.state = {
focused: !1,
focusedIndex: -1,
opened: !1
}, this.wrapper = null, this.mainButton = null, this.list = null, this.skipFocus = !1, this.buttonsData = [], this.showLicenseWatermark = !1, this.onKeyDown = (t) => {
const { focusedIndex: e } = this.state;
if (t.altKey) {
!this.opened && t.keyCode === a.down ? (t.preventDefault(), this.setState({ focusedIndex: 0 }), this.setOpen(!0, t)) : this.opened && t.keyCode === a.up && (t.preventDefault(), this.setState({ focusedIndex: -1 }), this.setOpen(!1, t));
return;
}
let o;
if (t.keyCode === a.enter || t.keyCode === a.space) {
if (t.preventDefault(), this.dispatchClickEvent(t, e), e >= 0) {
o = {
focusedIndex: this.opened ? -1 : 0
};
const n = !this.opened;
this.setOpen(n, t);
}
} else if (this.opened && t.keyCode === a.esc) {
this.setState({ focusedIndex: -1 }), this.setOpen(!1, t);
return;
}
if (this.opened) {
const n = I(e, t.keyCode, t.altKey, this.buttonsData.length);
n !== e && (o = o || {}, o.focusedIndex = n);
const h = t.keyCode === a.up || t.keyCode === a.down || t.keyCode === a.left || t.keyCode === a.right;
!t.altKey && (h || t.keyCode === a.home || t.keyCode === a.end) && t.preventDefault();
}
o && this.setState(o);
}, this.switchFocus = (t) => {
this.skipFocus = !0, t(), window.setTimeout(() => this.skipFocus = !1, 0);
}, this.onFocus = (t) => {
this.skipFocus || (l(this.props.onFocus, t, this, void 0), this.setState({ focused: !0 }));
}, this.setOpen = (t, e) => {
this.opened !== t && (this.openedDuringOnChange = t, this.setState({ opened: t }), e ? this.dispatchPopupEvent(e, t) : this.openedDuringOnChange = void 0);
}, this.onItemClick = (t, e) => {
this.opened && this.setState({ focusedIndex: 0 }), this.dispatchClickEvent(t, e), this.setOpen(!1, t);
}, this.onBlur = (t) => {
this.skipFocus || (this.setState({
focused: !1,
focusedIndex: -1
}), l(this.props.onBlur, t, this, void 0), setTimeout(() => {
this.setOpen(!1, t);
}, 0));
}, this.onPopupClose = (t) => {
var o;
const e = c(document);
this.element && this.element.removeAttribute("tabindex"), (e === this.list || (o = this.list) != null && o.contains(e)) && this.switchFocus(() => {
this.element && this.element.focus({ preventScroll: !0 });
}), this.props.popupSettings && this.props.popupSettings.onClose && this.props.popupSettings.onClose.call(void 0, t);
}, this.listRef = (t) => {
this.list = t, t && this.state.focused && this.switchFocus(() => {
t.focus({ preventScroll: !0 }), this.element && (this.element.tabIndex = -1);
});
}, this.onSplitPartClick = (t) => {
if (this.buttonsData.length) {
const e = !this.opened;
this.setState({
focusedIndex: e ? 0 : -1,
focused: !0
}), this.setOpen(e, t);
}
}, this.onDownSplitPart = (t) => {
t.preventDefault();
const e = c(document);
this.element && e !== this.element && e !== this.list && this.element.focus();
}, this.onItemDown = (t) => {
c(document) === this.list && t.preventDefault();
}, this.dispatchPopupEvent = (t, e) => {
l(e ? this.props.onOpen : this.props.onClose, t, this, void 0), this.openedDuringOnChange = void 0;
}, this.showLicenseWatermark = !C(O, { component: "SplitButton" });
}
get guid() {
return this.props.id ? this.props.id + "-accessibility-id" : this.props.id;
}
get opened() {
return this.openedDuringOnChange !== void 0 ? this.openedDuringOnChange : this.props.opened === void 0 ? this.state.opened : this.props.opened;
}
/**
* @hidden
*/
render() {
this.buttonsData = this.props.items || p.Children.toArray(this.props.children).filter((r) => r && r.type === D).map((r) => r.props);
const i = this.isRtl(), t = i ? "rtl" : void 0, { id: e, style: o, tabIndex: n, disabled: h } = this.props;
return /* @__PURE__ */ p.createElement(p.Fragment, null, /* @__PURE__ */ p.createElement(
"div",
{
id: e,
style: o,
className: d(
"k-split-button",
"k-button-group",
{
"k-focus": this.state.focused
},
`k-rounded-${f.roundedMap[this.props.rounded || "medium"]}`,
this.props.className
),
onKeyDown: this.onKeyDown,
onFocus: this.onFocus,
onBlur: this.onBlur,
dir: t,
ref: (r) => this.wrapper = r
},
/* @__PURE__ */ p.createElement(
m,
{
ref: (r) => this.mainButton = r && r.element,
type: "button",
size: this.props.size,
rounded: this.props.rounded,
fillMode: this.props.fillMode,
themeColor: this.props.themeColor,
onClick: (r) => this.onItemClick(r, -1),
disabled: h || void 0,
tabIndex: n,
accessKey: this.props.accessKey,
className: this.props.buttonClass,
icon: this.props.icon,
svgIcon: this.props.svgIcon,
iconClass: this.props.iconClass,
startIcon: this.props.startIcon,
endIcon: this.props.endIcon,
imageUrl: this.props.imageUrl,
dir: t,
"aria-disabled": h,
"aria-haspopup": !0,
"aria-expanded": this.opened || void 0,
"aria-label": this.props.ariaLabel,
"aria-controls": this.opened ? this.guid : void 0,
id: "button-" + this.guid,
title: this.props.title
},
this.props.text
),
/* @__PURE__ */ p.createElement(
m,
{
type: "button",
size: this.props.size,
rounded: this.props.rounded,
fillMode: this.props.fillMode,
themeColor: this.props.themeColor,
icon: "caret-alt-down",
svgIcon: E,
className: "k-split-button-arrow",
disabled: h || void 0,
tabIndex: -1,
onClick: this.onSplitPartClick,
onMouseDown: this.onDownSplitPart,
onPointerDown: this.onDownSplitPart,
dir: t,
"aria-label": "menu toggling button"
}
),
this.renderPopup(i)
), this.showLicenseWatermark && /* @__PURE__ */ p.createElement(b, null));
}
/**
* @hidden
*/
componentDidMount() {
(this.props.dir === void 0 && this.isRtl() || this.opened) && this.forceUpdate();
}
/**
* The DOM element of main button.
*/
get element() {
return this.mainButton;
}
dispatchClickEvent(i, t) {
this.isItemDisabled(t) || (t === -1 ? l(this.props.onButtonClick, i, this, void 0) : l(this.props.onItemClick, i, this, {
item: this.buttonsData[t],
itemIndex: t
}));
}
renderPopup(i) {
const { popupSettings: t = {} } = this.props, { focusedIndex: e } = this.state;
return /* @__PURE__ */ p.createElement(
w,
{
anchor: this.wrapper,
show: this.opened,
animate: t.animate,
popupClass: d("k-menu-popup", t.popupClass),
anchorAlign: t.anchorAlign || S(i),
popupAlign: t.popupAlign || v(i),
style: i ? { direction: "rtl" } : void 0,
onClose: this.onPopupClose
},
/* @__PURE__ */ p.createElement(
"ul",
{
role: "menu",
id: this.guid,
"aria-labelledby": "button-" + this.guid,
tabIndex: -1,
ref: this.listRef,
"aria-activedescendant": e >= 0 ? `${this.guid}-${e}` : void 0,
className: d("k-menu-group", {
[`k-menu-group-${f.sizeMap[this.props.size] || this.props.size}`]: this.props.size
})
},
this.renderChildItems()
)
);
}
renderChildItems() {
const { item: i, itemRender: t, textField: e } = this.props;
return this.buttonsData.length > 0 ? this.buttonsData.map((o, n) => /* @__PURE__ */ p.createElement(
y,
{
className: d(
"k-menu-item",
{ "k-first": n === 0 },
{ "k-last": n === this.buttonsData.length - 1 }
),
dataItem: o,
textField: e,
focused: this.state.focusedIndex === n,
onClick: this.onItemClick,
onDown: this.onItemDown,
render: t,
item: i,
key: n,
index: n,
id: `${this.guid}-${n}`
}
)) : null;
}
isItemDisabled(i) {
return this.buttonsData[i] ? this.buttonsData[i].disabled : this.props.disabled;
}
isRtl() {
return this.props.dir !== void 0 ? this.props.dir === "rtl" : !!this.wrapper && getComputedStyle(this.wrapper).direction === "rtl";
}
};
u.propTypes = {
accessKey: s.string,
ariaLabel: s.string,
title: s.string,
onButtonClick: s.func,
onFocus: s.func,
onBlur: s.func,
onItemClick: s.func,
onOpen: s.func,
onClose: s.func,
text: s.string,
items: s.arrayOf(s.any),
textField: s.string,
tabIndex: s.number,
disabled: s.bool,
icon: s.string,
svgIcon: k,
iconClass: s.string,
imageUrl: s.string,
popupSettings: s.object,
itemRender: s.any,
item: s.func,
className: s.string,
buttonClass: s.string,
dir: s.string
}, u.defaultProps = {
size: "medium",
rounded: "medium",
fillMode: "solid",
themeColor: "base"
};
let g = u;
export {
g as SplitButton
};