UNPKG

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