Skip to content

Commit

Permalink
fix(ui): fix toolbar responsiveness on small screens (#1716)
Browse files Browse the repository at this point in the history
* fix(ui): fix toolbar responsiveness on small screens

* fix: fix lint

* feat(ui): update toolbar item width calculation
  • Loading branch information
jikkai committed Mar 27, 2024
1 parent 02e9647 commit a9755e8
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 70 deletions.
6 changes: 6 additions & 0 deletions packages/design/src/components/dropdown/Dropdown.tsx
Expand Up @@ -36,6 +36,12 @@ export interface IDropdownProps {
*/
visible?: boolean;

/**
* Whether to force render the dropdown
* @default false
*/
forceRender?: boolean;

/**
* The trigger mode which executes the dropdown action
* @default ['click']
Expand Down
Expand Up @@ -155,7 +155,7 @@ export function SearchFunction() {
});
}

return (
return searchList.length > 0 && (
<Popup visible={visible} offset={offset}>
<ul className={styles.formulaSearchFunction} ref={ulRef}>
{searchList.map((item, index) => (
Expand Down
2 changes: 1 addition & 1 deletion packages/sheets-ui/src/views/formula-bar/index.module.less
Expand Up @@ -13,8 +13,8 @@
box-sizing: border-box;
width: 80px;
height: 100%;
// padding: 6px 0;
position: relative;
flex-shrink: 0;

&-input {
height: 100%;
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/locale/en-US.ts
Expand Up @@ -25,6 +25,7 @@ const locale: typeof zhCN = {
view: 'View',
others: 'Others',
},
more: 'More',
fontFamily: {
TimesNewRoman: 'Times New Roman',
Arial: 'Arial',
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/locale/zh-CN.ts
Expand Up @@ -23,6 +23,7 @@ const locale = {
view: '视图',
others: '其他',
},
more: '更多',
fontFamily: {
TimesNewRoman: 'Times New Roman',
Arial: 'Arial',
Expand Down
22 changes: 12 additions & 10 deletions packages/ui/src/views/components/context-menu/ContextMenu.tsx
Expand Up @@ -63,16 +63,18 @@ export function ContextMenu() {
return (
<Popup visible={visible} offset={offset}>
<section onPointerDown={(e) => e.stopPropagation()}>
<Menu
menuType={[menuType]}
onOptionSelect={(params) => {
const { label: commandId, value } = params;
commandService && commandService.executeCommand(commandId as string, { value });
const textSelectionRenderManager = injector.get(ITextSelectionRenderManager);
textSelectionRenderManager.focus();
setVisible(false);
}}
/>
{menuType && (
<Menu
menuType={[menuType]}
onOptionSelect={(params) => {
const { label: commandId, value } = params;
commandService && commandService.executeCommand(commandId as string, { value });
const textSelectionRenderManager = injector.get(ITextSelectionRenderManager);
textSelectionRenderManager.focus();
setVisible(false);
}}
/>
)}
</section>
</Popup>
);
Expand Down
137 changes: 82 additions & 55 deletions packages/ui/src/views/components/doc-bars/Toolbar.tsx
Expand Up @@ -22,6 +22,7 @@ import clsx from 'clsx';
import type { ComponentType } from 'react';
import React, { useEffect, useMemo, useRef, useState } from 'react';

import { combineLatest, map } from 'rxjs';
import type { IDisplayMenuItem, IMenuItem } from '../../../services/menu/menu';
import { MenuGroup, MenuPosition } from '../../../services/menu/menu';
import { IMenuService } from '../../../services/menu/menu.service';
Expand Down Expand Up @@ -55,7 +56,6 @@ export function Toolbar(props: IToolbarProps) {
const [position, setPosition] = useState<MenuPosition>(MenuPosition.TOOLBAR_START);

const toolbarRef = useRef<HTMLDivElement>(null);
const toolbarContainerRef = useRef<HTMLDivElement>(null);
const toolbarItemRefs = useRef<
Record<
string,
Expand All @@ -68,6 +68,24 @@ export function Toolbar(props: IToolbarProps) {

const [group, setGroup] = useState<IMenuGroup[]>([]);
const [collapsedId, setCollapsedId] = useState<string[]>([]);
const [visibleItems, setVisibleItems] = useState<IDisplayMenuItem<IMenuItem>[]>([]);

useEffect(() => {
const activeItems = group.find((g) => g.name === position)?.menuItems ?? [];
const filteredItems$ = combineLatest(
activeItems.map((item) => item.hidden$).filter((item) => !!item)
).pipe(
map((hiddenValues) => activeItems.filter((_, index) => !hiddenValues[index]))
);

const subscribe = filteredItems$.subscribe((items) => {
setVisibleItems(items);
});

return () => {
subscribe.unsubscribe();
};
}, [group]);

useEffect(() => {
const listener = menuService.menuChanged$.subscribe(() => {
Expand All @@ -94,29 +112,31 @@ export function Toolbar(props: IToolbarProps) {
useEffect(() => {
function resize() {
const wrapperWidth = toolbarRef.current?.clientWidth ?? 0;
const containerWidth = toolbarContainerRef.current?.clientWidth ?? 0;
const GAP = 8;

const itemWidths = Object.entries(toolbarItemRefs.current).map(([key, ref]) => {
return {
key: ref.key,
width: ref.el?.clientWidth,
};
});
const itemWidths = Object.entries(toolbarItemRefs.current)
.filter(([_, ref]) => {
return ref.el && ref.key && visibleItems.find((item) => item.id === ref.key);
})
.map(([_, ref]) => {
return {
key: ref.key,
width: ref.el?.clientWidth + GAP,
};
});

const gap = 8;
const collapsedId: string[] = [];

const collapsedIds: string[] = [];
let currentWidth = 168;
for (const item of itemWidths) {
currentWidth += item.width;

let currentWidth = containerWidth + 120;
for (let i = itemWidths.length - 1; i >= 0; i--) {
const item = itemWidths[i];
currentWidth -= item.width + gap;
if (currentWidth >= wrapperWidth) {
collapsedIds.push(item.key);
if (currentWidth > wrapperWidth) {
collapsedId.push(item.key);
}
}

setCollapsedId(collapsedIds);
setCollapsedId(collapsedId);
}

resize();
Expand All @@ -127,25 +147,23 @@ export function Toolbar(props: IToolbarProps) {
return () => {
observer.unobserve(document.body);
};
}, [group]);
}, [visibleItems, group]);

const toolbarGroups = useMemo(() => {
return (
group
.find((g) => g.name === position)
?.menuItems.reduce(
(acc, item) => {
const key = item.group ?? MenuGroup.TOOLBAR_OTHERS;
if (!acc[key]) {
acc[key] = [];
}

acc[key].push(item);
return acc;
},
{} as Record<MenuGroup, Array<IDisplayMenuItem<IMenuItem>>>
) ?? ({} as Record<MenuGroup, Array<IDisplayMenuItem<IMenuItem>>>)
);
return group
.find((g) => g.name === position)
?.menuItems.reduce(
(acc, item) => {
const key = item.group ?? MenuGroup.TOOLBAR_OTHERS;
if (!acc[key]) {
acc[key] = [];
}

acc[key].push(item);
return acc;
},
{} as Record<MenuGroup, Array<IDisplayMenuItem<IMenuItem>>>
) ?? ({} as Record<MenuGroup, Array<IDisplayMenuItem<IMenuItem>>>);
}, [group]);

const hasHeaderMenu = useMemo(() => (headerMenuComponents && headerMenuComponents.size > 0) || group.length > 1, [headerMenuComponents, group]);
Expand All @@ -157,17 +175,19 @@ export function Toolbar(props: IToolbarProps) {
<header className={styles.headerbar}>
<div className={styles.menubar}>
{group.length > 1 &&
group.map((item, index) => (
<a
key={index}
className={clsx(styles.menubarItem, {
[styles.menubarItemActive]: item.name === position,
})}
onClick={() => setPosition(item.name)}
>
{localeService.t(item.name)}
</a>
))}
group.map((item, index) => (
<a
key={index}
className={clsx(styles.menubarItem, {
[styles.menubarItemActive]: item.name === position,
})}
onClick={() => {
setPosition(item.name);
}}
>
{localeService.t(item.name)}
</a>
))}
</div>
<div className={styles.headerMenu}>
<ComponentContainer components={headerMenuComponents} />
Expand All @@ -178,18 +198,26 @@ export function Toolbar(props: IToolbarProps) {

<div ref={toolbarRef} className={styles.toolbar}>
<div className={styles.toolbarContainer}>
{Object.entries(toolbarGroups).map(([key, item]) => (
<div key={key} className={styles.toolbarGroup}>
{item.map(
(subItem) =>
!collapsedId.includes(subItem.id) && <ToolbarItem key={subItem.id} {...subItem} />
)}
</div>
))}
{Object.entries(toolbarGroups)
.filter(([_, item]) => {
const count = item.filter((subItem) => !collapsedId.includes(subItem.id)).length;
return count;
})
.map(([key, item]) => (
<div key={key} className={styles.toolbarGroup}>
{item.map(
(subItem) =>
!collapsedId.includes(subItem.id) && (
<ToolbarItem key={subItem.id} {...subItem} />
)
)}
</div>
))}

{collapsedId.length > 0 && (
<Tooltip title="更多" placement="bottom">
<Tooltip title={localeService.t('more')} placement="bottom">
<Dropdown
forceRender
className={styles.toolbarMore}
overlay={(
<div className={styles.toolbarMoreContainer} onClick={(e) => e.stopPropagation()}>
Expand Down Expand Up @@ -217,7 +245,6 @@ export function Toolbar(props: IToolbarProps) {

{/* fake toolbar for calculating overflow width */}
<div
ref={toolbarContainerRef}
className={styles.toolbarContainer}
style={{
position: 'absolute',
Expand Down
4 changes: 1 addition & 3 deletions packages/ui/src/views/components/doc-bars/ToolbarItem.tsx
Expand Up @@ -267,9 +267,7 @@ export const ToolbarItem = forwardRef((props: IDisplayMenuItem<IMenuItem>, ref:

// ref component
return hidden
? (
<></>
)
? null
: (
<Tooltip
ref={ref}
Expand Down
9 changes: 9 additions & 0 deletions packages/ui/src/views/components/doc-bars/index.module.less
Expand Up @@ -86,6 +86,7 @@
border-bottom: 1px solid rgb(var(--border-color));

&-container {
padding: 0 var(--padding-lg);
display: flex;
gap: var(--margin-xs);
align-items: center;
Expand All @@ -94,6 +95,8 @@
box-sizing: border-box;
height: 100%;
margin: 0 auto;
max-width: 100%;
overflow: hidden;
}

&-more {
Expand Down Expand Up @@ -124,6 +127,11 @@
display: flex;
gap: var(--margin-xs);
align-items: center;
flex-shrink: 0;

> * {
flex-shrink: 0;
}

&:not(:last-child, :empty) {
&::after {
Expand All @@ -135,6 +143,7 @@
height: 20px;

background-color: rgb(var(--border-color));
flex-shrink: 0;
}
}
}
Expand Down

0 comments on commit a9755e8

Please sign in to comment.