Skip to content

Commit

Permalink
feat: ability to add hidden action to toolbar (#144)
Browse files Browse the repository at this point in the history
  • Loading branch information
smsochneg authored Oct 26, 2023
1 parent 7313f10 commit d100d9b
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 36 deletions.
34 changes: 29 additions & 5 deletions src/toolbar/FlexToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ import {ToolbarListButton} from './ToolbarListButton';
import {shrinkToolbarData} from './flexible';
import {logger} from '../logger';
import {useRenderTime} from '../react-utils/hooks';
import {ToolbarDataType, ToolbarItemData} from './types';

import './FlexToolbar.scss';

const b = cn('flex-toolbar');

export type FlexToolbarProps<E> = ToolbarProps<E> & {
dotsTitle: string | (() => string);
hiddenActions?: ToolbarItemData<E>[];
};

export function FlexToolbar<E>(props: FlexToolbarProps<E>) {
Expand All @@ -26,12 +28,32 @@ export function FlexToolbar<E>(props: FlexToolbarProps<E>) {
});
});

const {data, className} = props;
const {data, className, hiddenActions} = props;

const [ref, {width}] = useMeasure<HTMLDivElement>();
const {data: items, dots} = React.useMemo(
() => shrinkToolbarData({data, availableWidth: width}),
[data, width],
);
const {data: items, dots} = React.useMemo(() => {
const toolbarButtonIds = data.reduce((a: string[], toolbarGroup) => {
return [
...a,
...toolbarGroup
.map((toolbarButton) => {
if (toolbarButton.type === ToolbarDataType.ListButton) {
return toolbarButton.data.map((v) => v.id);
}
return toolbarButton.id;
})
.flat(),
];
}, []);

// Finding only actions tha are not present in the main toolbar config
const filteredHiddenAction = hiddenActions?.filter((a) => !toolbarButtonIds.includes(a.id));
return shrinkToolbarData({
data,
availableWidth: width,
hiddenActions: filteredHiddenAction,
});
}, [data, width, hiddenActions]);

return (
<div ref={ref} className={b(null, [className])}>
Expand All @@ -46,6 +68,8 @@ export function FlexToolbar<E>(props: FlexToolbarProps<E>) {
focus={props.focus}
onClick={props.onClick}
className={b('dots')}
alwaysActive={true}
hideDisabled={true}
/>
)}
</div>
Expand Down
70 changes: 40 additions & 30 deletions src/toolbar/ToolbarListButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export type ToolbarListButtonData<E> = {
title: string | (() => string);
withArrow?: boolean;
data: ToolbarItemData<E>[];
alwaysActive?: boolean;
hideDisabled?: boolean;
};

export type ToolbarListButtonProps<E> = ToolbarBaseProps<E> & ToolbarListButtonData<E>;
Expand All @@ -31,12 +33,14 @@ export function ToolbarListButton<E>({
title,
withArrow,
data,
alwaysActive,
hideDisabled,
}: ToolbarListButtonProps<E>) {
const buttonRef = React.useRef<HTMLButtonElement>(null);
const [open, , hide, toggleOpen] = useBooleanState(false);

const someActive = data.some((item) => item.isActive(editor));
const everyDisabled = data.every((item) => !item.isEnable(editor));
const someActive = alwaysActive ? false : data.some((item) => item.isActive(editor));
const everyDisabled = alwaysActive ? false : data.every((item) => !item.isEnable(editor));

const popupOpen = everyDisabled ? false : open;
const shouldForceHide = open && !popupOpen;
Expand Down Expand Up @@ -78,35 +82,41 @@ export function ToolbarListButton<E>({
</Tooltip>
<Popup anchorRef={buttonRef} open={popupOpen} onClose={hide}>
<Menu size="l" className={b('menu')}>
{data.map(({id, title, icon, hotkey, isActive, isEnable, exec, hint}) => {
const titleText = isFunction(title) ? title() : title;
const hintText = isFunction(hint) ? hint() : hint;
return (
<Menu.Item
key={id}
active={isActive(editor)}
disabled={!isEnable(editor)}
onClick={() => {
hide();
focus();
exec(editor);
onClick?.(id);
}}
icon={<Icon data={icon.data} size={icon.size ?? 16} />}
extraProps={{'aria-label': titleText}}
>
<div className={b('item')}>
{titleText}
<div className={b('extra')}>
{hotkey && <Hotkey value={hotkey} />}
{hintText && (
<HelpPopover className={b('hint')} content={hintText} />
)}
{data
.map(({id, title, icon, hotkey, isActive, isEnable, exec, hint}) => {
const titleText = isFunction(title) ? title() : title;
const hintText = isFunction(hint) ? hint() : hint;
const disabled = !isEnable(editor);
return hideDisabled && disabled ? null : (
<Menu.Item
key={id}
active={isActive(editor)}
disabled={!isEnable(editor)}
onClick={() => {
hide();
focus();
exec(editor);
onClick?.(id);
}}
icon={<Icon data={icon.data} size={icon.size ?? 16} />}
extraProps={{'aria-label': titleText}}
>
<div className={b('item')}>
{titleText}
<div className={b('extra')}>
{hotkey && <Hotkey value={hotkey} />}
{hintText && (
<HelpPopover
className={b('hint')}
content={hintText}
/>
)}
</div>
</div>
</div>
</Menu.Item>
);
})}
</Menu.Item>
);
})
.filter(Boolean)}
</Menu>
</Popup>
</>
Expand Down
9 changes: 8 additions & 1 deletion src/toolbar/flexible.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,14 @@ const dotsWidth = B_DOTS_LEFT_GAP + B_DOTS_WIDTH;
export type ShrinkToolbarDataParams<E> = {
data: ToolbarData<E>;
availableWidth: number;
hiddenActions?: ToolbarItemData<E>[];
};

export function shrinkToolbarData<E>({availableWidth, data}: ShrinkToolbarDataParams<E>): {
export function shrinkToolbarData<E>({
availableWidth,
data,
hiddenActions = [],
}: ShrinkToolbarDataParams<E>): {
data: ToolbarData<E>;
dots?: ToolbarItemData<E>[];
} {
Expand Down Expand Up @@ -64,6 +69,8 @@ export function shrinkToolbarData<E>({availableWidth, data}: ShrinkToolbarDataPa
if (newGroup.length) fittingData.push(newGroup);
});

dotsData.push(...hiddenActions);

return {data: fittingData, dots: dotsData.length ? dotsData : undefined};
}

Expand Down

0 comments on commit d100d9b

Please sign in to comment.