Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3079,6 +3079,7 @@ possible breaking changes to any direct/custom use of AG Grid APIs and props wit

### 🎁 New Features

* New `SplitButton` component added to button component collection.
* GridModel `groupSortFn` now accepts `null` to turn off sorting of group rows.
* `DockViewModel` now supports optional `width`, `height` and `collapsedWidth` configs.
* The `appMenuButton.extraItems` prop now accepts `MenuItem` configs (as before) but also React
Expand Down
4 changes: 2 additions & 2 deletions desktop/cmp/button/Button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,12 @@
border-radius: 0;
&.xh-button--standard,
&.xh-button--outlined {
&:first-child {
&:first-child:not(.xh-split-button__trigger-right) {
border-bottom-left-radius: var(--xh-button-border-radius-px);
border-top-left-radius: var(--xh-button-border-radius-px);
}

&:last-child {
&:last-child:not(.xh-split-button__trigger-left) {
border-bottom-right-radius: var(--xh-button-border-radius-px);
border-top-right-radius: var(--xh-button-border-radius-px);
}
Expand Down
6 changes: 6 additions & 0 deletions desktop/cmp/button/SplitButton.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.xh-split-button {
&__trigger {
width: 22px;
min-width: 22px;
}
}
110 changes: 110 additions & 0 deletions desktop/cmp/button/SplitButton.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import {hoistCmp, MenuItemLike} from '@xh/hoist/core';
import {button, buttonGroup, ButtonProps} from '@xh/hoist/desktop/cmp/button';
import {Icon} from '@xh/hoist/icon';
import {menu, menuItem, popover} from '@xh/hoist/kit/blueprint';
import {wait} from '@xh/hoist/promise';
import classNames from 'classnames';
import {castArray} from 'lodash';
import './SplitButton.scss';

/**
* A split button combines a primary action button with an integrated menu of secondary actions.
*/

export interface SplitButtonProps extends ButtonProps {
menuItems: MenuItemLike[];
menuSide?: 'left' | 'right';
}
export const [SplitButton, splitButton] = hoistCmp.withFactory<SplitButtonProps>({
displayName: 'SplitButton',
model: false,
memo: false,
observer: false,

render({
menuItems = [],
menuSide = 'right',
disabled,
intent,
minimal = false,
className,
...rest
}) {
menuItems = castArray(menuItems);
const noMenu = !menuItems.length;

return buttonGroup({
className: classNames(className, 'xh-split-button'),
items: [
menuTriggerButton({
omit: noMenu || menuSide == 'right',
menuItems,
menuSide,
className,
disabled,
intent,
minimal
}),
primaryButton({disabled, intent, minimal, ...rest}),
menuTriggerButton({
omit: noMenu || menuSide == 'left',
menuItems,
menuSide,
className,
disabled,
intent,
minimal
})
]
});
}
});

const primaryButton = hoistCmp.factory<ButtonProps>({
displayName: 'SplitButtonPrimary',
model: false,
memo: false,
observer: false,

render(props) {
return button({
className: 'xh-split-button__primary',
...props
});
}
});

const menuTriggerButton = hoistCmp.factory({
displayName: 'SplitButtonTrigger',
model: false,
memo: false,
observer: false,

render({menuItems, menuSide, className, disabled, intent, minimal}) {
return popover({
position: `bottom-${menuSide}`,
minimal: true,
disabled,
target: button({
icon: Icon.chevronDown({prefix: 'fas'}),
className: `xh-split-button__trigger-${menuSide}`,
disabled,
intent,
minimal
}),
content: menu({
className: classNames(className, 'xh-split-button__menu'),
items: menuItems.map((item, idx) => {
const {actionFn, ...rest} = item;

return menuItem({
key: idx,
className: classNames(item.className, 'xh-split-button__menu-item'),
onClick: actionFn ? () => wait().then(actionFn) : null, // do async to allow menu to close
...rest
});
})
})
});
}
});
1 change: 1 addition & 0 deletions desktop/cmp/button/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ export * from './ModalToggleButton';
export * from './OptionsButton';
export * from './RefreshButton';
export * from './RestoreDefaultsButton';
export * from './SplitButton';
export * from './ThemeToggleButton';