feat(*): improve styles in sheets-ui (#4704)

This commit is contained in:
白熱
2025-02-23 00:59:00 +08:00
committed by GitHub
parent 568f8bc069
commit b752a7fcf8
111 changed files with 2291 additions and 1998 deletions
@@ -13,6 +13,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
token: ${{ secrets.GH_TOKEN }}
- name: Setup Node.js
uses: ./.github/actions/setup-node
+3
View File
@@ -131,6 +131,9 @@ const config: StorybookConfig = {
dynamicImport: true,
},
transform: {
react: {
runtime: 'automatic',
},
legacyDecorator: true,
decoratorMetadata: true,
},
+1 -2
View File
@@ -36,7 +36,6 @@ import {
LocaleType,
LocalUndoRedoService,
PermissionService,
RediContext,
ResourceManagerService,
ThemeService,
UniverInstanceService,
@@ -44,7 +43,7 @@ import {
import { ConfigProvider, defaultTheme, greenTheme, themeInstance } from '@univerjs/design';
import enUS from '@univerjs/design/locale/en-US';
import zhCN from '@univerjs/design/locale/zh-CN';
import { DesktopLocalStorageService } from '@univerjs/ui';
import { DesktopLocalStorageService, RediContext } from '@univerjs/ui';
import React, { useMemo } from 'react';
import './global.css';
+1 -1
View File
@@ -21,7 +21,7 @@
"@storybook/addon-interactions": "^8.5.8",
"@storybook/addon-links": "^8.5.8",
"@storybook/addon-styling-webpack": "^1.0.1",
"@storybook/addon-webpack5-compiler-swc": "^1.0.5",
"@storybook/addon-webpack5-compiler-swc": "^2.1.0",
"@storybook/blocks": "^8.5.8",
"@storybook/icons": "^1.3.2",
"@storybook/react": "^8.5.8",
-3
View File
@@ -1,9 +1,6 @@
{
"extends": "@univerjs-infra/shared/tsconfigs/base",
"compilerOptions": {
"target": "ESNext",
"jsx": "react",
"lib": ["ESNext", "dom", "dom.iterable"],
"useDefineForClassFields": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 77 KiB

+2 -1
View File
@@ -34,6 +34,7 @@
"@univerjs/engine-formula": "workspace:*",
"@univerjs/engine-render": "workspace:*",
"@univerjs/find-replace": "workspace:*",
"@univerjs/icons": "0.2.20",
"@univerjs/mockdata": "workspace:*",
"@univerjs/rpc": "workspace:*",
"@univerjs/rpc-node": "workspace:*",
@@ -84,7 +85,7 @@
"@univerjs-infra/shared": "workspace:*",
"cross-env": "^7.0.3",
"detect-port": "^2.1.0",
"esbuild": "^0.24.2",
"esbuild": "^0.25.0",
"esbuild-plugin-clean": "^1.0.1",
"esbuild-plugin-copy": "^2.1.1",
"esbuild-plugin-vue3": "^0.4.2",
@@ -0,0 +1,36 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { IAccessor, ICommand } from '@univerjs/core';
import { CommandType } from '@univerjs/core';
export const DropdownListFirstItemOperation: ICommand = {
id: 'custom-menu.operation.dropdown-list-first-item',
type: CommandType.OPERATION,
handler: async (_accessor: IAccessor) => {
// alert('Dropdown list first item operation')
return true;
},
};
export const DropdownListSecondItemOperation: ICommand = {
id: 'custom-menu.operation.dropdown-list-second-item',
type: CommandType.OPERATION,
handler: async (_accessor: IAccessor) => {
// alert('Dropdown list second item operation')
return true;
},
};
@@ -14,23 +14,14 @@
* limitations under the License.
*/
import type { RefObject } from 'react';
import { createContext, useContext } from 'react';
import type { IAccessor, ICommand } from '@univerjs/core';
import { CommandType } from '@univerjs/core';
interface IDropdownContextType {
show: boolean;
updateShow: (value: boolean) => void;
disabled: boolean;
overlayRef: RefObject<HTMLDivElement>;
triggerRef: RefObject<HTMLElement>;
}
export const DropdownContext = createContext<IDropdownContextType | null>(null);
export const useDropdown = () => {
const context = useContext(DropdownContext);
if (!context) {
throw new Error('useDropdown must be used within a DropdownProvider');
}
return context;
export const SingleButtonOperation: ICommand = {
id: 'custom-menu.operation.single-button',
type: CommandType.OPERATION,
handler: async (_accessor: IAccessor) => {
// alert('Single button operation')
return true;
},
};
@@ -0,0 +1,118 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Disposable, ICommandService, Inject, Injector } from '@univerjs/core';
import { AiSingle, BarChartSingle, CommentSingle } from '@univerjs/icons';
import { ComponentManager, ContextMenuGroup, ContextMenuPosition, IMenuManagerService, MenuItemType, RibbonStartGroup } from '@univerjs/ui';
import { DropdownListFirstItemOperation, DropdownListSecondItemOperation } from '../commands/operations/dropdown-list.operation';
import { SingleButtonOperation } from '../commands/operations/single-button.operation';
import { CUSTOM_MENU_DROPDOWN_LIST_OPERATION_ID, CustomMenuItemDropdownListFirstItemFactory, CustomMenuItemDropdownListMainButtonFactory, CustomMenuItemDropdownListSecondItemFactory } from './menu/dropdown-list.menu';
import { CustomMenuItemSingleButtonFactory } from './menu/single-button.menu';
export class CustomMenuController extends Disposable {
constructor(
@Inject(Injector) private readonly _injector: Injector,
@ICommandService private readonly _commandService: ICommandService,
@IMenuManagerService private readonly _menuManagerService: IMenuManagerService,
@Inject(ComponentManager) private readonly _componentManager: ComponentManager
) {
super();
this._initCommands();
this._registerComponents();
this._initMenus();
}
/**
* register commands
*/
private _initCommands(): void {
[
SingleButtonOperation,
DropdownListFirstItemOperation,
DropdownListSecondItemOperation,
].forEach((c) => {
this.disposeWithMe(this._commandService.registerCommand(c));
});
}
/**
* register icon components
*/
private _registerComponents(): void {
this.disposeWithMe(this._componentManager.register('ButtonIcon', AiSingle));
this.disposeWithMe(this._componentManager.register('ItemIcon', BarChartSingle));
this.disposeWithMe(this._componentManager.register('MainButtonIcon', CommentSingle));
}
/**
* register menu items
*/
private _initMenus(): void {
const largeDropdownList = Array.from({ length: 15 }, (_, i) => ({
order: 20 + i,
menuItemFactory: () => {
return {
id: `${DropdownListSecondItemOperation.id}-${i}`,
type: MenuItemType.BUTTON,
title: 'customMenu.itemTwo',
icon: 'ItemIcon',
};
},
}));
this._menuManagerService.mergeMenu({
[RibbonStartGroup.OTHERS]: {
[SingleButtonOperation.id]: {
order: 10,
menuItemFactory: CustomMenuItemSingleButtonFactory,
},
[CUSTOM_MENU_DROPDOWN_LIST_OPERATION_ID]: {
order: 11,
menuItemFactory: CustomMenuItemDropdownListMainButtonFactory,
[DropdownListFirstItemOperation.id]: {
order: 0,
menuItemFactory: CustomMenuItemDropdownListFirstItemFactory,
},
[DropdownListSecondItemOperation.id]: {
order: 1,
menuItemFactory: CustomMenuItemDropdownListSecondItemFactory,
},
...largeDropdownList,
},
},
[ContextMenuPosition.MAIN_AREA]: {
[ContextMenuGroup.OTHERS]: {
[SingleButtonOperation.id]: {
order: 12,
menuItemFactory: CustomMenuItemSingleButtonFactory,
},
[CUSTOM_MENU_DROPDOWN_LIST_OPERATION_ID]: {
order: 9,
menuItemFactory: CustomMenuItemDropdownListMainButtonFactory,
[DropdownListFirstItemOperation.id]: {
order: 0,
menuItemFactory: CustomMenuItemDropdownListFirstItemFactory,
},
[DropdownListSecondItemOperation.id]: {
order: 1,
menuItemFactory: CustomMenuItemDropdownListSecondItemFactory,
},
},
},
},
});
}
}
@@ -0,0 +1,51 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { IMenuButtonItem, IMenuSelectorItem } from '@univerjs/ui';
import { MenuItemType } from '@univerjs/ui';
import { DropdownListFirstItemOperation, DropdownListSecondItemOperation } from '../../commands/operations/dropdown-list.operation';
export const CUSTOM_MENU_DROPDOWN_LIST_OPERATION_ID = 'custom-menu.operation.dropdown-list';
export function CustomMenuItemDropdownListMainButtonFactory(): IMenuSelectorItem<string> {
return {
// When type is MenuItemType.SUBITEMS, this factory serves as a container for the drop-down list, and you can set any unique id
id: CUSTOM_MENU_DROPDOWN_LIST_OPERATION_ID,
// The type of the menu item, in this case, it is a subitems
type: MenuItemType.SUBITEMS,
icon: 'MainButtonIcon',
tooltip: 'customMenu.dropdownList',
title: 'customMenu.dropdown',
};
}
export function CustomMenuItemDropdownListFirstItemFactory(): IMenuButtonItem<string> {
return {
id: DropdownListFirstItemOperation.id,
type: MenuItemType.BUTTON,
title: 'customMenu.itemOne',
icon: 'ItemIcon',
};
}
export function CustomMenuItemDropdownListSecondItemFactory(): IMenuButtonItem<string> {
return {
id: DropdownListSecondItemOperation.id,
type: MenuItemType.BUTTON,
title: 'customMenu.itemTwo',
icon: 'ItemIcon',
};
}
@@ -0,0 +1,34 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { IMenuButtonItem } from '@univerjs/ui';
import { MenuItemType } from '@univerjs/ui';
import { SingleButtonOperation } from '../../commands/operations/single-button.operation';
export function CustomMenuItemSingleButtonFactory(): IMenuButtonItem<string> {
return {
// Bind the command id, clicking the button will trigger this command
id: SingleButtonOperation.id,
// The type of the menu item, in this case, it is a button
type: MenuItemType.BUTTON,
// The icon of the button, which needs to be registered in ComponentManager
icon: 'ButtonIcon',
// The tooltip of the button. Prioritize matching internationalization. If no match is found, the original string will be displayed
tooltip: 'customMenu.singleButton',
// The title of the button. Prioritize matching internationalization. If no match is found, the original string will be displayed
title: 'customMenu.button',
};
}
+17
View File
@@ -0,0 +1,17 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { UniverSheetsCustomMenuPlugin } from './plugin';
@@ -0,0 +1,26 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export default {
customMenu: {
button: 'Button',
singleButton: 'Single button',
dropdown: 'Dropdown',
dropdownList: 'Dropdown list',
itemOne: 'Item 1',
itemTwo: 'Item 2',
},
};
@@ -0,0 +1,26 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export default {
customMenu: {
button: '按钮',
singleButton: '单个按钮',
dropdown: '下拉',
dropdownList: '下拉列表',
itemOne: '项目一',
itemTwo: '项目二',
},
};
+52
View File
@@ -0,0 +1,52 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Dependency } from '@univerjs/core';
import { Inject, Injector, LocaleService, Plugin, touchDependencies, UniverInstanceType } from '@univerjs/core';
import { CustomMenuController } from './controllers/custom-menu.controller';
import enUS from './locale/en-US';
import zhCN from './locale/zh-CN';
const SHEET_CUSTOM_MENU_PLUGIN = 'SHEET_CUSTOM_MENU_PLUGIN';
export class UniverSheetsCustomMenuPlugin extends Plugin {
static override type = UniverInstanceType.UNIVER_SHEET;
static override pluginName = SHEET_CUSTOM_MENU_PLUGIN;
constructor(
@Inject(Injector) protected readonly _injector: Injector,
@Inject(LocaleService) private readonly _localeService: LocaleService
) {
super();
this._localeService.load({
enUS,
zhCN,
});
}
override onStarting(): void {
([
[CustomMenuController],
] as Dependency[]).forEach((d) => this._injector.add(d));
}
override onRendered(): void {
touchDependencies(this._injector, [
[CustomMenuController],
]);
}
}
+71 -69
View File
@@ -43,9 +43,10 @@ import { UniverSheetsUIPlugin } from '@univerjs/sheets-ui';
import { UniverSheetsZenEditorPlugin } from '@univerjs/sheets-zen-editor';
import { UniverThreadCommentUIPlugin } from '@univerjs/thread-comment-ui';
import { UniverUIPlugin } from '@univerjs/ui';
import { enUS, faIR, frFR, ruRU, viVN, zhCN, zhTW } from '../locales';
import { UniverSheetsCustomMenuPlugin } from './custom-menu';
import '@univerjs/sheets/facade';
import '@univerjs/ui/facade';
import '@univerjs/docs-ui/facade';
@@ -127,6 +128,7 @@ function createNewInstance() {
univer.registerPlugin(UniverSheetsThreadCommentPlugin);
univer.registerPlugin(UniverSheetsThreadCommentUIPlugin);
univer.registerPlugin(UniverSheetsBindingSourcePlugin);
univer.registerPlugin(UniverSheetsCustomMenuPlugin);
// If we are running in e2e platform, we should immediately register the debugger plugin.
if (IS_E2E) {
@@ -182,7 +184,7 @@ declare global {
function initEvent() {
const logService = window.univer!.__getInjector().get(ILogService);
// Workbook Events
// Workbook Events
window.univerAPI?.addEvent(window.univerAPI.Event.WorkbookCreated, (params) => {
logService.log('===WorkbookCreated', params);
});
@@ -191,132 +193,132 @@ function initEvent() {
logService.log('===WorkbookDisposed', params);
});
// Sheet Lifecycle Events
// checked
// Sheet Lifecycle Events
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.BeforeSheetCreate, (params) => {
logService.log('===BeforeSheetCreate', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.SheetCreated, (params) => {
logService.log('===SheetCreated', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.BeforeSheetDelete, (params) => {
logService.log('===BeforeSheetDelete', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.SheetDeleted, (params) => {
logService.log('===SheetDeleted', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.BeforeSheetMove, (params) => {
logService.log('===BeforeSheetMove', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.SheetMoved, (params) => {
logService.log('===SheetMoved', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.BeforeSheetNameChange, (params) => {
logService.log('===BeforeSheetNameChange', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.SheetNameChanged, (params) => {
logService.log('===SheetNameChanged', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.BeforeSheetHideChange, (params) => {
logService.log('===BeforeSheetHideChange', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.SheetHideChanged, (params) => {
logService.log('===SheetHideChanged', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.CellClicked, (params) => {
logService.log('===CellClicked', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.CellHover, (params) => {
logService.log('===CellHover', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.CellPointerDown, (params) => {
logService.log('===CellPointerDown', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.CellPointerUp, (params) => {
logService.log('===CellPointerUp', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.CellPointerMove, (params) => {
logService.log('===CellPointerMove', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.SelectionChanged, (params) => {
logService.log('===SelectionChanged', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.SelectionMoveStart, (params) => {
logService.log('===SelectionMoveStart', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.SelectionMoveEnd, (params) => {
logService.log('===SelectionMoveEnd', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.SelectionMoving, (params) => {
logService.log('===SelectionMoving', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.BeforeSheetEditStart, (params) => {
logService.log('===BeforeSheetEditStart', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.SheetEditStarted, (params) => {
logService.log('===SheetEditStarted', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.SheetEditChanging, (params) => {
logService.log('===SheetEditChanging', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.BeforeSheetEditEnd, (params) => {
logService.log('===BeforeSheetEditEnd', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.SheetEditEnded, (params) => {
logService.log('===SheetEditEnded', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.SheetValueChanged, (params) => {
logService.log('===SheetValueChanged', params);
});
// Clipboard Events
// Clipboard Events
window.univerAPI?.addEvent(window.univerAPI.Event.BeforeClipboardChange, (params) => {
logService.log('===BeforeClipboardChange', params);
});
@@ -333,72 +335,72 @@ function initEvent() {
logService.log('===ClipboardPasted', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.BeforeGridlineEnableChange, (params) => {
logService.log('===BeforeGridlineEnableChange', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.BeforeGridlineColorChange, (params) => {
logService.log('===BeforeGridlineColorChange', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.GridlineChanged, (params) => {
logService.log('===GridlineChanged', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.BeforeSheetZoomChange, (params) => {
logService.log('===BeforeSheetZoomChange', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.SheetZoomChanged, (params) => {
logService.log('===SheetZoomChanged', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.Scroll, (params) => {
logService.log('===Scroll', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.SheetSkeletonChanged, (params) => {
logService.log('===SheetSkeletonChanged', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.BeforeSheetDataValidationAdd, (params) => {
logService.log('===BeforeSheetDataValidationAdd', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.SheetDataValidationChanged, (params) => {
logService.log('===SheetDataValidationChanged', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.SheetDataValidatorStatusChanged, (params) => {
logService.log('===SheetDataValidatorStatusChanged', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.SheetBeforeRangeFilter, (params) => {
logService.log('===SheetBeforeRangeFilter', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.SheetRangeFiltered, (params) => {
logService.log('===SheetRangeFiltered', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.SheetBeforeRangeSort, (params) => {
logService.log('===SheetBeforeRangeSort', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.SheetRangeSorted, (params) => {
logService.log('===SheetRangeSorted', params);
});
@@ -411,7 +413,7 @@ function initEvent() {
logService.log('===SheetRangeFilterCleared', params);
});
// Image Events
// Image Events
window.univerAPI?.addEvent(window.univerAPI.Event.BeforeOverGridImageInsert, (params) => {
logService.log('===BeforeOverGridImageInsert', params);
});
@@ -444,102 +446,102 @@ function initEvent() {
logService.log('===OverGridImageSelected', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.BeforeCommentAdd, (params) => {
logService.log('===BeforeCommentAdd', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.CommentAdded, (params) => {
logService.log('===CommentAdded', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.BeforeCommentUpdate, (params) => {
logService.log('===BeforeCommentUpdate', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.CommentUpdated, (params) => {
logService.log('===CommentUpdated', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.BeforeCommentDeleted, (params) => {
logService.log('===BeforeCommentDeleted', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.CommentDeleted, (params) => {
logService.log('===CommentDeleted', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.BeforeCommentResolve, (params) => {
logService.log('===BeforeCommentResolve', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.CommentResolved, (params) => {
logService.log('===CommentResolved', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.RowHeaderClick, (params) => {
logService.log('===RowHeaderClick', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.RowHeaderHover, (params) => {
logService.log('===RowHeaderHover', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.RowHeaderPointerDown, (params) => {
logService.log('===RowHeaderPointerDown', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.RowHeaderPointerUp, (params) => {
logService.log('===RowHeaderPointerUp', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.ColumnHeaderClick, (params) => {
logService.log('===ColumnHeaderClick', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.ColumnHeaderHover, (params) => {
logService.log('===ColumnHeaderHover', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.ColumnHeaderPointerDown, (params) => {
logService.log('===ColumnHeaderPointerDown', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.ColumnHeaderPointerUp, (params) => {
logService.log('===ColumnHeaderPointerUp', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.BeforeSheetLinkAdd, (params) => {
logService.log('===BeforeSheetLinkAdd', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.BeforeSheetLinkUpdate, (params) => {
logService.log('===BeforeSheetLinkUpdate', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.BeforeSheetLinkCancel, (params) => {
logService.log('===BeforeSheetLinkCancel', params);
});
// Drag and Drop Events
// Drag and Drop Events
window.univerAPI?.addEvent(window.univerAPI.Event.DragOver, (params) => {
logService.log('===DragOver', params);
});
@@ -548,17 +550,17 @@ function initEvent() {
logService.log('===Drop', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.CrosshairHighlightColorChanged, (params) => {
logService.log('===CrosshairHighlightColorChanged', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.CrosshairHighlightEnabledChanged, (params) => {
logService.log('===CrosshairHighlightEnabledChanged', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.BeforeSheetEditStart, (params) => {
const { row, column } = params;
if (row === 0 && column === 0) {
@@ -566,12 +568,12 @@ function initEvent() {
}
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.ActiveSheetChanged, (params) => {
logService.log('===active sheet changed', params);
});
// checked
// checked
window.univerAPI?.addEvent(window.univerAPI.Event.SheetEditChanging, (params) => {
logService.log('===SheetEditChanging', params);
});
+5 -5
View File
@@ -19,7 +19,7 @@
"url": "https://github.com/dream-num/univer/issues"
},
"engines": {
"node": ">=18 || <=22",
"node": ">=20 || <=22",
"pnpm": ">=10"
},
"scripts": {
@@ -47,18 +47,18 @@
"@antfu/eslint-config": "4.3.0",
"@commitlint/cli": "^19.7.1",
"@commitlint/config-conventional": "^19.7.1",
"@eslint-react/eslint-plugin": "^1.26.2",
"@eslint-react/eslint-plugin": "^1.27.0",
"@playwright/test": "^1.50.1",
"@release-it-plugins/workspaces": "^4.2.0",
"@release-it/conventional-changelog": "^9.0.4",
"@storybook/react": "8.5.8",
"@types/node": "^22.13.4",
"@types/node": "^22.13.5",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"@univerjs-infra/shared": "workspace:*",
"@univerjs/design": "workspace:*",
"@vitejs/plugin-react": "^4.3.4",
"eslint": "9.20.1",
"eslint": "9.21.0",
"eslint-plugin-format": "^1.0.1",
"eslint-plugin-header": "^3.1.1",
"eslint-plugin-jsdoc": "^50.6.3",
@@ -73,7 +73,7 @@
"react-dom": "19.0.0",
"release-it": "^17.11.0",
"serve": "^14.2.4",
"tsx": "^4.19.2",
"tsx": "^4.19.3",
"turbo": "^2.4.2",
"typescript": "^5.7.3",
"vitest": "^3.0.6"
@@ -16,10 +16,9 @@
import type { ITextWatermarkConfig } from '@univerjs/engine-render';
import { LocaleService } from '@univerjs/core';
import { Checkbox, ColorPicker, DropdownLegacy, Input, InputNumber, Select } from '@univerjs/design';
import { Checkbox, clsx, ColorPicker, Dropdown, Input, InputNumber, Select } from '@univerjs/design';
import { BoldSingle, FontColor, ItalicSingle } from '@univerjs/icons';
import { useDependency } from '@univerjs/ui';
import clsx from 'clsx';
import React from 'react';
import styles from './index.module.less';
@@ -38,73 +37,77 @@ export const WatermarkTextSetting: React.FC<IWatermarkTextSettingProps> = (props
<div className={styles.watermarkTextSetting}>
<div className={styles.watermarkTextSettingHeader}>{localeService.t('univer-watermark.style')}</div>
<div className={styles.watermarkTextSettingFontContent}>
<div className="univer-mb-4 univer-grid univer-gap-1">
<div>{localeService.t('univer-watermark.content')}</div>
<Input
value={config.content}
onChange={(val) => onChange({ ...config, content: val })}
className={styles.watermarkInputContent}
placeholder={localeService.t('univer-watermark.textPlaceholder')}
/>
</div>
<div className={styles.watermarkTextSettingFontStyle}>
<div className={styles.watermarkTextSettingFontStylePart}>
<div>
<div className={clsx(styles.watermarkTextSettingFontStyle, 'univer-grid univer-gap-4')}>
<div className={clsx(styles.watermarkTextSettingFontStylePart, 'univer-flex univer-gap-2')}>
<div className="univer-grid univer-gap-1">
<div>{localeService.t('univer-watermark.fontSize')}</div>
<InputNumber
className="univer-box-border univer-h-7"
value={config.fontSize}
max={72}
min={12}
onChange={(val) => {
if (val != null) {
onChange({ ...config, fontSize: Number.parseInt(val.toString()) });
}
}}
max={72}
min={12}
className={styles.watermarkInput}
/>
</div>
<div style={{ margin: '0 8px' }}>
<div className="univer-grid univer-gap-1">
<div>{localeService.t('univer-watermark.direction')}</div>
<Select
className="univer-box-border univer-h-7"
value={config.direction}
onChange={(v) => onChange({ ...config, direction: v as 'ltr' | 'rtl' })}
options={[
{ label: localeService.t('univer-watermark.ltr'), value: 'ltr' },
{ label: localeService.t('univer-watermark.rtl'), value: 'rtl' },
]}
onChange={(v) => onChange({ ...config, direction: v as 'ltr' | 'rtl' })}
/>
</div>
<div>
<div className="univer-grid univer-gap-1">
<div>{localeService.t('univer-watermark.opacity')}</div>
<InputNumber
className="univer-box-border univer-h-7"
max={1}
min={0}
step={0.05}
value={config.opacity}
onChange={(val) => {
if (val != null) {
onChange({ ...config, opacity: Number.parseFloat(val.toString()) });
}
}}
max={1}
min={0}
step={0.05}
className={styles.watermarkInput}
/>
</div>
</div>
<div className={styles.watermarkTextSettingFontStylePart}>
<div
className={clsx(styles.watermarkTextSettingFontStylePart, `
univer-flex univer-justify-around univer-gap-4
`)}
>
<div className={styles.watermarkIconWrapper}>
<DropdownLegacy
<Dropdown
overlay={(
<div className={styles.watermarkColorPickerWrapper}>
<div className="univer-rounded-lg univer-p-4">
<ColorPicker value={config.color} onChange={(val) => onChange({ ...config, color: val })} />
</div>
)}
>
<FontColor className={styles.watermarkIcon} extend={{ colorChannel1: config.color ?? 'rgb(var(--primary-color))' }} />
</DropdownLegacy>
</Dropdown>
</div>
<div className={clsx(styles.watermarkIconWrapper, { [styles.watermarkIconWrapperSelect]: config.bold })} onClick={() => { onChange({ ...config, bold: !config.bold }); }}>
<BoldSingle className={styles.watermarkIcon} />
@@ -115,94 +118,94 @@ export const WatermarkTextSetting: React.FC<IWatermarkTextSettingProps> = (props
</div>
</div>
{/* Layout */}
<div className={styles.watermarkTextSettingHeader}>{localeService.t('univer-watermark.layout')}</div>
<div className={styles.watermarkTextSettingLayout}>
<div className={styles.watermarkTextSettingFontStylePart}>
<div className={styles.watermarkTextSettingLayoutFontWrapper}>
<div className="univer-grid univer-gap-2 univer-text-center">
<div className="univer-flex univer-gap-2">
<div className="univer-grid univer-flex-1 univer-gap-1">
<div>{localeService.t('univer-watermark.rotate')}</div>
<InputNumber
className="univer-box-border univer-h-7"
value={config.rotate}
max={360}
min={-360}
onChange={(val) => {
if (val != null) {
onChange({ ...config, rotate: Number.parseInt(val.toString()) });
}
}}
max={360}
min={-360}
className={styles.watermarkInput}
/>
</div>
<div className={styles.watermarkTextSettingLayoutFontWrapper}>
<div className="univer-grid univer-flex-1 univer-gap-1">
<div>{localeService.t('univer-watermark.repeat')}</div>
<Checkbox
className="univer-justify-center univer-self-baseline"
checked={config.repeat}
onChange={(val) => onChange({ ...config, repeat: val as boolean })}
/>
</div>
</div>
<div className={styles.watermarkTextSettingFontStylePart}>
<div className={styles.watermarkTextSettingLayoutFontWrapper}>
<div className="univer-flex univer-gap-2">
<div className="univer-grid univer-gap-1">
<div>{localeService.t('univer-watermark.spacingX')}</div>
<InputNumber
className="univer-box-border univer-h-7"
value={config.spacingX}
min={0}
onChange={(val) => {
if (val != null) {
onChange({ ...config, spacingX: Number.parseInt(val.toString()) });
}
}}
min={0}
className={styles.watermarkInput}
/>
</div>
<div className={styles.watermarkTextSettingLayoutFontWrapper}>
<div className="univer-grid univer-gap-1">
<div>{localeService.t('univer-watermark.spacingY')}</div>
<InputNumber
className="univer-box-border univer-h-7"
value={config.spacingY}
min={0}
onChange={(val) => {
if (val != null) {
onChange({ ...config, spacingY: Number.parseInt(val.toString()) });
}
}}
min={0}
className={styles.watermarkInput}
/>
</div>
</div>
<div className={styles.watermarkTextSettingFontStylePart}>
<div className={styles.watermarkTextSettingLayoutFontWrapper}>
<div className="univer-flex univer-gap-2">
<div className="univer-grid univer-gap-1">
<div>{localeService.t('univer-watermark.startX')}</div>
<InputNumber
className="univer-box-border univer-h-7"
value={config.x}
min={0}
onChange={(val) => {
if (val != null) {
onChange({ ...config, x: Number.parseInt(val.toString()) });
}
}}
min={0}
className={styles.watermarkInput}
/>
</div>
<div className={styles.watermarkTextSettingLayoutFontWrapper}>
<div>{localeService.t('univer-watermark.startY')}</div>
<InputNumber
className="univer-box-border univer-h-7"
value={config.y}
min={0}
onChange={(val) => {
if (val != null) {
onChange({ ...config, y: Number.parseInt(val.toString()) });
}
}}
min={0}
className={styles.watermarkInput}
/>
</div>
</div>
</div>
</div>
);
};
@@ -46,49 +46,12 @@
color: rgb(var(--text-color-secondary));
margin: 16px 0;
}
&-font {
&-content {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
&-style-part {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 16px;
&:first-child {
margin-top: 0;
}
}
}
&-layout {
&-font-wrapper {
width: calc(50% - 8px);
align-self: flex-start;
white-space: nowrap;
}
}
}
&-image-setting {
font-size: 14px;
}
&-input {
height: 28px;
box-sizing: border-box;
&-content {
width: 220px;
}
}
&-icon {
width: 18px;
height: 18px;
@@ -117,11 +80,4 @@
}
}
}
&-color-picker-wrapper {
padding: 16px;
border-radius: 8px;
background-color: #fff;
border: 1px solid rgb(var(--grey-100));
}
}
@@ -15,13 +15,10 @@
*/
import type { IProjectNode } from '../../services/unit-grid/unit-grid.service';
import { DropdownLegacy, Tooltip } from '@univerjs/design';
import { clsx, Dropdown, Tooltip } from '@univerjs/design';
import { CheckMarkSingle, FullscreenSingle, IncreaseSingle, ZoomReduceSingle } from '@univerjs/icons';
import { ISidebarService, useDependency } from '@univerjs/ui';
import {
useReactFlow,
} from '@xyflow/react';
import clsx from 'clsx';
import { useReactFlow } from '@xyflow/react';
import React, { useCallback, useEffect, useLayoutEffect } from 'react';
import { IUnitGridService } from '../../services/unit-grid/unit-grid.service';
import { UniDiv } from '../uni-toolbar/UniFloatToolbar';
@@ -127,10 +124,9 @@ export const UniControls = ({ zoom, onItemClick }: { zoom: number; onItemClick?:
<UniControlButton tooltips="Zoom in" onClick={onZoomInHandler}>
<IncreaseSingle />
</UniControlButton>
<DropdownLegacy
placement="topLeft"
<Dropdown
overlay={(
<div className={styles.sliderShortcuts}>
<div className={clsx(styles.sliderShortcuts, 'univer-rounded-lg univer-p-4')}>
{shortcuts?.map((item) => (
<a
key={item}
@@ -157,7 +153,7 @@ export const UniControls = ({ zoom, onItemClick }: { zoom: number; onItemClick?:
{zoomPercent}
%
</a>
</DropdownLegacy>
</Dropdown>
<UniControlButton tooltips="Zoom out" onClick={onZoomOutHandler}>
<ZoomReduceSingle />
</UniControlButton>
+1 -1
View File
@@ -74,7 +74,7 @@
"fast-diff": "1.3.0",
"kdbush": "^4.0.2",
"lodash-es": "^4.17.21",
"nanoid": "5.0.9",
"nanoid": "5.1.0",
"numeral": "^2.0.6",
"numfmt": "^2.5.2",
"ot-json1": "^1.0.2",
+3 -3
View File
@@ -14,9 +14,9 @@
* limitations under the License.
*/
import { LocaleType } from '../types/enum/locale-type';
import { version } from '../../package.json';
import type { IWorkbookData } from './typedef';
import pkg from '../../package.json';
import { LocaleType } from '../types/enum/locale-type';
export function getEmptySnapshot(
unitID = '',
@@ -27,7 +27,7 @@ export function getEmptySnapshot(
id: unitID,
sheetOrder: [],
name,
appVersion: version,
appVersion: pkg.version,
locale,
styles: {},
sheets: {},
+1
View File
@@ -68,6 +68,7 @@
"react-dom": ">=16.9.0 || >=17 || >=18 || >=19"
},
"dependencies": {
"@radix-ui/react-popover": "^1.1.6",
"@rc-component/color-picker": "^2.0.1",
"@rc-component/trigger": "^2.2.6",
"@univerjs/icons": "^0.2.20",
@@ -15,7 +15,6 @@
*/
import type { Meta } from '@storybook/react';
import React from 'react';
import { Avatar } from './Avatar';
@@ -15,7 +15,6 @@
*/
import type { Meta } from '@storybook/react';
import React from 'react';
import { Button } from './Button';
@@ -16,9 +16,7 @@
import { ChevronSortSingle } from '@univerjs/icons';
import React, { useEffect, useMemo, useState } from 'react';
import { DropdownOverlay } from '../dropdown/DropdownOverlay';
import { DropdownProvider } from '../dropdown/DropdownProvider';
import { DropdownTrigger } from '../dropdown/DropdownTrigger';
import { Dropdown } from '../dropdown';
import { hexToHsv, hslToHsv, hsvToHex, hsvToHsl, hsvToRgb, rgbToHsv } from './color-conversion';
interface IColorInputProps {
@@ -310,6 +308,7 @@ function AlphaInput({ alpha, onChange }: IAlphaInputProps) {
export function ColorInput({ hsv, alpha, showAlpha, onChangeColor, onChangeAlpha }: IColorInputProps) {
const [format, setFormat] = useState<ColorFormat>('hex');
const [formatPanelVisible, setFormatPanelVisible] = useState(false);
function renderInput(format: ColorFormat) {
switch (format) {
@@ -324,6 +323,11 @@ export function ColorInput({ hsv, alpha, showAlpha, onChangeColor, onChangeAlpha
}
}
function handleSelectFormat(format: ColorFormat) {
setFormatPanelVisible(false);
setFormat(format);
}
return (
<div
className={`
@@ -335,36 +339,39 @@ export function ColorInput({ hsv, alpha, showAlpha, onChangeColor, onChangeAlpha
dark:[&_input]:univer-border-gray-600 dark:[&_input]:univer-text-white
`}
>
<DropdownProvider>
<DropdownTrigger>
<a
className={`
univer-box-border univer-flex univer-h-8 univer-flex-1 univer-cursor-pointer
univer-items-center univer-justify-between univer-gap-1 univer-rounded univer-border
univer-border-solid univer-border-gray-200 univer-px-1.5 univer-text-sm univer-text-gray-700
dark:univer-border-gray-600 dark:univer-text-white
`}
>
<span>{format.toUpperCase()}</span>
<ChevronSortSingle className="univer-size-5 univer-text-gray-400" />
</a>
</DropdownTrigger>
<DropdownOverlay>
<Dropdown
align="start"
overlay={(
<div
className={`
univer-grid univer-w-16
univer-grid univer-w-32
[&>a]:univer-cursor-pointer [&>a]:univer-px-2 [&>a]:univer-py-1 [&>a]:univer-text-sm
dark:univer-text-white
focus-visible:univer-outline-none
`}
>
<a onClick={() => setFormat('hex')}>HEX</a>
<a onClick={() => setFormat('hsl')}>HSL</a>
<a onClick={() => setFormat('rgb')}>RGB</a>
<a onClick={() => handleSelectFormat('hex')}>HEX</a>
<a onClick={() => handleSelectFormat('hsl')}>HSL</a>
<a onClick={() => handleSelectFormat('rgb')}>RGB</a>
</div>
</DropdownOverlay>
</DropdownProvider>
)}
open={formatPanelVisible}
onOpenChange={setFormatPanelVisible}
>
<a
className={`
univer-box-border univer-flex univer-h-8 univer-w-32 univer-cursor-pointer univer-items-center
univer-justify-between univer-gap-1 univer-rounded univer-border univer-border-solid
univer-border-gray-200 univer-px-1.5 univer-text-sm univer-text-gray-700
dark:univer-border-gray-600 dark:univer-text-white
`}
>
<span>{format.toUpperCase()}</span>
<ChevronSortSingle className="univer-size-5 univer-text-gray-400" />
</a>
</Dropdown>
<div className="univer-relative univer-flex univer-gap-2">
<div className="univer-relative univer-flex univer-flex-1 univer-gap-2">
{renderInput(format)}
</div>
@@ -33,7 +33,7 @@ export default meta;
export const Playground = {
render() {
const [value, setValue] = useState('rgba(100,150,200,0.8)');
const [value, setValue] = useState('#FF0000');
const [format] = useState<'hex' | 'rgb' | 'hsl'>('rgb');
return (
@@ -53,12 +53,18 @@ exports[`ColorPicker > should render correctly 1`] = `
"
>
<a
aria-controls="radix-:r0:"
aria-expanded="false"
aria-haspopup="dialog"
class="
univer-box-border univer-flex univer-h-8 univer-flex-1 univer-cursor-pointer
univer-items-center univer-justify-between univer-gap-1 univer-rounded univer-border
univer-border-solid univer-border-gray-200 univer-px-1.5 univer-text-sm univer-text-gray-700
dark:univer-border-gray-600 dark:univer-text-white
"
univer-box-border univer-flex univer-h-8 univer-w-32 univer-cursor-pointer univer-items-center
univer-justify-between univer-gap-1 univer-rounded univer-border univer-border-solid
univer-border-gray-200 univer-px-1.5 univer-text-sm univer-text-gray-700
dark:univer-border-gray-600 dark:univer-text-white
"
data-slot="popover-trigger"
data-state="closed"
type="button"
>
<span>
HEX
@@ -79,7 +85,7 @@ exports[`ColorPicker > should render correctly 1`] = `
</svg>
</a>
<div
class="univer-relative univer-flex univer-gap-2"
class="univer-relative univer-flex univer-flex-1 univer-gap-2"
>
<input
class="
@@ -429,12 +435,18 @@ exports[`ColorPicker > should render with showAlpha 1`] = `
"
>
<a
aria-controls="radix-:r2:"
aria-expanded="false"
aria-haspopup="dialog"
class="
univer-box-border univer-flex univer-h-8 univer-flex-1 univer-cursor-pointer
univer-items-center univer-justify-between univer-gap-1 univer-rounded univer-border
univer-border-solid univer-border-gray-200 univer-px-1.5 univer-text-sm univer-text-gray-700
dark:univer-border-gray-600 dark:univer-text-white
"
univer-box-border univer-flex univer-h-8 univer-w-32 univer-cursor-pointer univer-items-center
univer-justify-between univer-gap-1 univer-rounded univer-border univer-border-solid
univer-border-gray-200 univer-px-1.5 univer-text-sm univer-text-gray-700
dark:univer-border-gray-600 dark:univer-text-white
"
data-slot="popover-trigger"
data-state="closed"
type="button"
>
<span>
HEX
@@ -455,7 +467,7 @@ exports[`ColorPicker > should render with showAlpha 1`] = `
</svg>
</a>
<div
class="univer-relative univer-flex univer-gap-2"
class="univer-relative univer-flex univer-flex-1 univer-gap-2"
>
<input
class="
@@ -801,12 +813,18 @@ exports[`ColorPicker > should render with value 1`] = `
"
>
<a
aria-controls="radix-:r1:"
aria-expanded="false"
aria-haspopup="dialog"
class="
univer-box-border univer-flex univer-h-8 univer-flex-1 univer-cursor-pointer
univer-items-center univer-justify-between univer-gap-1 univer-rounded univer-border
univer-border-solid univer-border-gray-200 univer-px-1.5 univer-text-sm univer-text-gray-700
dark:univer-border-gray-600 dark:univer-text-white
"
univer-box-border univer-flex univer-h-8 univer-w-32 univer-cursor-pointer univer-items-center
univer-justify-between univer-gap-1 univer-rounded univer-border univer-border-solid
univer-border-gray-200 univer-px-1.5 univer-text-sm univer-text-gray-700
dark:univer-border-gray-600 dark:univer-text-white
"
data-slot="popover-trigger"
data-state="closed"
type="button"
>
<span>
HEX
@@ -827,7 +845,7 @@ exports[`ColorPicker > should render with value 1`] = `
</svg>
</a>
<div
class="univer-relative univer-flex univer-gap-2"
class="univer-relative univer-flex univer-flex-1 univer-gap-2"
>
<input
class="
@@ -15,7 +15,6 @@
*/
import type { Meta, StoryObj } from '@storybook/react';
import React from 'react';
import { defaultTheme, greenTheme } from '../../themes';
@@ -15,7 +15,6 @@
*/
import type { Meta } from '@storybook/react';
import React from 'react';
import { Button } from '../button/Button';
import { DropdownLegacy } from './DropdownLegacy';
@@ -79,6 +79,7 @@ export interface IDropdownLegacyProps {
disabled?: boolean;
}
/** @deprecated */
export function DropdownLegacy(props: IDropdownLegacyProps) {
const {
className,
@@ -15,15 +15,12 @@
*/
import type { Meta } from '@storybook/react';
import React from 'react';
import { DropdownOverlay } from './DropdownOverlay';
import { DropdownProvider } from './DropdownProvider';
import { DropdownTrigger } from './DropdownTrigger';
import { Dropdown } from './Dropdown';
const meta: Meta<typeof DropdownProvider> = {
const meta: Meta<typeof Dropdown> = {
title: 'Components / Dropdown',
component: DropdownProvider,
component: Dropdown,
parameters: {
layout: 'centered',
},
@@ -36,31 +33,29 @@ export const Playground = {
render() {
return (
<div className="univer-relative">
<DropdownProvider>
<DropdownTrigger>
<a className="univer-cursor-pointer univer-border univer-rounded-lg univer-border-gray-200 univer-px-4 univer-py-2 univer-border-solid hover:univer-bg-gray-100 univer-transition-all">
Click me
</a>
</DropdownTrigger>
<DropdownOverlay>
<Dropdown
overlay={(
<div className="univer-text-blue-500">
Hello Univer
<DropdownProvider>
<DropdownTrigger>
<a>Nested Dropdown</a>
</DropdownTrigger>
<DropdownOverlay>
<Dropdown
overlay={(
<div className="univer-text-cyan-700">
<div>Nested Content</div>
<div>Nested Content</div>
<div>Nested Content</div>
<div>Nested Content</div>
</div>
</DropdownOverlay>
</DropdownProvider>
)}
>
<a>Nested Popover</a>
</Dropdown>
</div>
</DropdownOverlay>
</DropdownProvider>
)}
>
<a className="univer-cursor-pointer univer-border univer-rounded-lg univer-border-gray-200 univer-px-4 univer-py-2 univer-border-solid hover:univer-bg-gray-100 univer-transition-all">
Click me
</a>
</Dropdown>
</div>
);
},
@@ -0,0 +1,65 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Content } from '@radix-ui/react-popover';
import type { ComponentProps, ReactNode } from 'react';
import { useState } from 'react';
import { PopoverContent, PopoverPrimitive, PopoverTrigger } from './Popover';
interface IDropdownProps {
children: ReactNode;
overlay: ReactNode;
disabled?: boolean;
open?: boolean;
onOpenChange?: (open: boolean) => void;
}
export function Dropdown(props: IDropdownProps & ComponentProps<typeof Content>) {
const {
children,
overlay,
disabled,
open: controlledOpen,
onOpenChange: controlledOnOpenChange,
...restProps
} = props;
const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
const isControlled = controlledOpen !== undefined;
const open = isControlled ? controlledOpen : uncontrolledOpen;
function handleChangeOpen(newOpen: boolean) {
if (disabled) return;
if (!isControlled) {
setUncontrolledOpen(newOpen);
}
controlledOnOpenChange?.(newOpen);
}
return (
<PopoverPrimitive open={open} onOpenChange={handleChangeOpen}>
<PopoverTrigger asChild>
{children}
</PopoverTrigger>
<PopoverContent {...restProps}>
{overlay}
</PopoverContent>
</PopoverPrimitive>
);
}
@@ -1,109 +0,0 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import canUseDom from 'rc-util/lib/Dom/canUseDom';
import React, { useCallback, useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import { clsx } from '../../helper/clsx';
import { useDropdown } from './DropdownContext';
export interface IDropdownOverlayProps {
children: React.ReactNode;
className?: string;
offset?: {
x?: number;
y?: number;
};
anchor?: {
x: 'left' | 'right';
y: 'top' | 'bottom';
};
}
export function DropdownOverlay({ children, className, offset, anchor: anchorPos = { x: 'left', y: 'bottom' } }: IDropdownOverlayProps) {
if (!canUseDom) {
return null;
}
const { show, updateShow, overlayRef, triggerRef } = useDropdown();
const [mounted, setMounted] = useState(false);
const [position, setPosition] = useState({ top: 0, left: 0 });
useEffect(() => {
if (show && triggerRef.current && overlayRef.current) {
const triggerRect = triggerRef.current.getBoundingClientRect();
const overlayRect = overlayRef.current.getBoundingClientRect();
const viewportWidth = document.documentElement.clientWidth;
const viewportHeight = document.documentElement.clientHeight;
let top = triggerRect[anchorPos.y] + window.scrollY;
let left = triggerRect[anchorPos.x] + window.scrollX;
if (top + overlayRect.height > viewportHeight + window.scrollY) {
top = triggerRect.top - overlayRect.height + window.scrollY;
}
if (left + overlayRect.width > viewportWidth + window.scrollX) {
left = triggerRect.right - overlayRect.width + window.scrollX;
}
top = Math.max(top, window.scrollY);
left = Math.max(left, window.scrollX);
setPosition({
top: top + (offset?.y ?? 0),
left: left + (offset?.x ?? 0),
});
requestAnimationFrame(() => {
setMounted(true);
});
}
}, [show, offset?.x, offset?.y, overlayRef, triggerRef]);
const hideAfterClick = useCallback((e: React.MouseEvent) => {
e.stopPropagation();
updateShow(false);
}, []);
if (!show) return null;
return createPortal(
<div
ref={overlayRef}
className={clsx(
`
univer-fixed univer-z-[1071] univer-overflow-hidden univer-rounded-md univer-border univer-bg-white
univer-opacity-0 univer-shadow-md
dark:univer-bg-gray-700
`,
{
'univer-opacity-100 univer-animate-in univer-slide-in-from-top-2': mounted,
},
className
)}
style={{
top: position.top,
left: position.left,
}}
onClick={hideAfterClick}
onPointerUpCapture={(e) => e.stopPropagation()}
>
{children}
</div>,
document.body
);
}
@@ -1,73 +0,0 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { ReactNode } from 'react';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { DropdownContext } from './DropdownContext';
interface IDropdownProviderProps {
visible?: boolean;
children: ReactNode;
disabled?: boolean;
onVisibleChange?: (visible: boolean) => void;
}
export function DropdownProvider({ visible, children, disabled = false, onVisibleChange }: IDropdownProviderProps) {
const triggerRef = useRef<HTMLDivElement>(null!);
const overlayRef = useRef<HTMLDivElement>(null!);
const [internalShow, setInternalShow] = useState(false);
const isControlled = visible !== undefined;
const show = isControlled ? visible : internalShow;
const updateShow = useCallback((newShow: boolean) => {
if (disabled) return;
if (!isControlled) {
setInternalShow(newShow);
}
onVisibleChange?.(newShow);
}, [disabled, isControlled, onVisibleChange]);
useEffect(() => {
// If the Dropdown is not visible, we do not add event listeners to boost performance.
if (!show) return;
const handleClickOutside = (event: MouseEvent) => {
const target = event.target as Node;
if (!triggerRef.current?.contains(target) && !overlayRef.current?.contains(target)) {
// As this function listens to mousedown event, while the Button components listens to
// click event, we can anticapte this event callback will be triggered first.
// If we hide the Dropdown in a synchronous way, the button's callback will never be triggered.
// TODO: @jikkai: This is a temp fix. A more appropriate fix would be detecting if the target is
// in a nested dropdown.
updateShow(false);
}
};
window.addEventListener('pointerup', handleClickOutside);
return () => window.removeEventListener('pointerup', handleClickOutside);
}, [show, updateShow]);
const contextValue = useMemo(() => ({ show, updateShow, disabled, triggerRef, overlayRef }), [show, disabled]);
return (
<DropdownContext.Provider value={contextValue}>
{children}
</DropdownContext.Provider>
);
}
@@ -1,43 +0,0 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { ReactElement } from 'react';
import { cloneElement, isValidElement } from 'react';
import { useDropdown } from './DropdownContext';
interface IDropdownTriggerProps {
children: ReactElement;
}
export function DropdownTrigger({ children }: IDropdownTriggerProps) {
const { show, updateShow, triggerRef } = useDropdown();
if (!isValidElement(children)) {
throw new Error('DropdownTrigger children must be a valid React element');
}
// eslint-disable-next-line react/no-clone-element
return cloneElement(children, {
ref: triggerRef,
onClick: (e: React.MouseEvent) => {
e.stopPropagation();
updateShow(!show);
// eslint-disable-next-line ts/no-explicit-any
(children.props as React.HTMLProps<HTMLElement>)?.onClick?.(e as any);
},
// eslint-disable-next-line ts/no-explicit-any
} as any);
}
@@ -0,0 +1,72 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { ComponentProps } from 'react';
import { Anchor, Content, Portal, Root, Trigger } from '@radix-ui/react-popover';
import { clsx } from '../../helper/clsx';
function PopoverPrimitive({
...props
}: ComponentProps<typeof Root>) {
return <Root data-slot="popover" {...props} />;
}
function PopoverTrigger({
...props
}: ComponentProps<typeof Trigger>) {
return <Trigger data-slot="popover-trigger" {...props} />;
}
function PopoverContent({
className,
align = 'center',
sideOffset = 4,
...props
}: ComponentProps<typeof Content>) {
return (
<Portal>
<Content
data-slot="popover-content"
align={align}
sideOffset={sideOffset}
className={clsx(
`
univer-z-[1080] univer-rounded-md univer-border univer-border-solid univer-bg-white
univer-text-gray-900 univer-shadow-md univer-outline-hidden univer-border-gray-200
data-[side=bottom]:univer-slide-in-from-top-2
data-[side=left]:univer-slide-in-from-right-2
data-[side=right]:univer-slide-in-from-left-2
data-[side=top]:univer-slide-in-from-bottom-2
data-[state=closed]:univer-animate-out data-[state=closed]:univer-fade-out-0
data-[state=closed]:univer-zoom-out-95
data-[state=open]:univer-animate-in data-[state=open]:univer-fade-in-0
data-[state=open]:univer-zoom-in-95
`,
className
)}
{...props}
/>
</Portal>
);
}
function PopoverAnchor({
...props
}: ComponentProps<typeof Anchor>) {
return <Anchor data-slot="popover-anchor" {...props} />;
}
export { PopoverAnchor, PopoverContent, PopoverPrimitive, PopoverTrigger };
@@ -14,6 +14,4 @@
* limitations under the License.
*/
export { DropdownOverlay, type IDropdownOverlayProps } from './DropdownOverlay';
export { DropdownProvider } from './DropdownProvider';
export { DropdownTrigger } from './DropdownTrigger';
export { Dropdown } from './Dropdown';
@@ -15,7 +15,6 @@
*/
import type { Meta } from '@storybook/react';
import React from 'react';
import { Input } from '../input';
import { Select } from '../select';
@@ -15,7 +15,6 @@
*/
import type { Meta } from '@storybook/react';
import React from 'react';
import { Button } from '../button/Button';
@@ -15,7 +15,6 @@
*/
import type { Meta } from '@storybook/react';
import React from 'react';
import { InputNumber } from './InputNumber';
@@ -15,7 +15,6 @@
*/
import type { Meta } from '@storybook/react';
import React from 'react';
import { Input } from './Input';
+8 -10
View File
@@ -108,17 +108,15 @@ export const Input = ({
<button
type="button"
onClick={handleClear}
className={clsx(
'univer-absolute univer-right-2 univer-rounded-full univer-p-1',
`
univer-text-gray-400
hover:univer-text-gray-500
`,
'univer-transition-colors univer-duration-200',
'focus:univer-outline-none focus:univer-ring-2 focus:univer-ring-blue-500/20'
)}
className={`
univer-absolute univer-right-2 univer-flex univer-items-center univer-rounded-full
univer-border-none univer-bg-transparent univer-p-1 univer-text-gray-400 univer-transition-colors
univer-duration-200
focus:univer-outline-none
hover:univer-text-gray-500
`}
>
<CloseSingle className="univer-h-4 univer-w-4" />
<CloseSingle className="univer-size-4" />
</button>
)}
</div>
@@ -15,7 +15,6 @@
*/
import type { Meta } from '@storybook/react';
import React from 'react';
import { Pager } from '../pager';
import { InputWithSlot } from './InputWithSlot';
+2 -2
View File
@@ -17,13 +17,13 @@
import type { MenuItemGroupProps, MenuItemProps, MenuProps, MenuRef, SubMenuProps } from 'rc-menu';
import RcMenu, { MenuItem as RcMenuItem, MenuItemGroup as RcMenuItemGroup, SubMenu as RcSubMenu } from 'rc-menu';
import React, { useContext } from 'react';
import { clsx } from '../../helper/clsx';
import { ConfigContext } from '../config-provider/ConfigProvider';
import styles from './index.module.less';
export const Menu = React.forwardRef<MenuRef, MenuProps>((props, ref) => {
const { mountContainer } = useContext(ConfigContext);
return mountContainer && React.cloneElement(<RcMenu ref={ref} prefixCls={styles.menu} getPopupContainer={() => mountContainer} />, {
return mountContainer && React.cloneElement(<RcMenu ref={ref} prefixCls={clsx(styles.menu, props.className)} getPopupContainer={() => mountContainer} />, {
...props,
});
});
@@ -7,7 +7,6 @@
list-style: none;
background-color: rgb(var(--bg-color-secondary));
border: 1px solid rgb(var(--border-color));
border-radius: var(--border-radius-lg);
outline: none;
box-shadow: var(--box-shadow-base);
@@ -15,7 +15,6 @@
*/
import type { Meta } from '@storybook/react';
import React from 'react';
import { Button } from '../button/Button';
import { message, Messager, MessageType } from './Message';
@@ -15,7 +15,6 @@
*/
import type { Meta } from '@storybook/react';
import React from 'react';
import { Radio } from './Radio';
@@ -15,7 +15,6 @@
*/
import type { Meta, StoryObj } from '@storybook/react';
import React from 'react';
import { Scrollbar } from './Scrollbar';
@@ -14,7 +14,6 @@
* limitations under the License.
*/
import React from 'react';
import type { Meta } from '@storybook/react';
import { Segmented } from './Segmented';
@@ -33,10 +32,11 @@ export const SegmentedBasic = {
render() {
return (
<div style={{ width: 280 }}>
<Segmented options={[
{ label: 'By Values', value: 'Values' },
{ label: 'By Conditions', value: 'Conditions' },
]}
<Segmented
options={[
{ label: 'By Values', value: 'Values' },
{ label: 'By Conditions', value: 'Conditions' },
]}
/>
</div>
);
@@ -1,89 +0,0 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { fireEvent, render, screen } from '@testing-library/react';
import React, { useState } from 'react';
import { describe, expect, it } from 'vitest';
import { Slider } from '../Slider';
describe('Slider', () => {
it('renders correctly', () => {
const { container } = render(<Slider min={0} max={100} value={90} />);
expect(container);
});
it('renders correctly with resetPoint', () => {
let value = 90;
function handleChange(point: number) {
value = point;
}
const { container } = render(<Slider value={value} resetPoint={30} onChange={handleChange} />);
fireEvent.click(container.querySelector('[role=button]')!);
expect(value).toBe(30);
});
it('renders correctly with steps', () => {
let result = 10;
function Demo() {
const [value, setValue] = useState(result);
function handleChange(point: number) {
setValue(point);
result = point;
}
return <Slider max={20} value={value} onChange={handleChange} />;
}
const { container } = render(<Demo />);
const [prev, next] = container.querySelectorAll('button');
fireEvent.click(prev);
fireEvent.click(prev);
fireEvent.click(next);
fireEvent.click(next);
fireEvent.click(next);
expect(result).toBe(20);
});
it('renders correctly with shortcuts', () => {
let result = 10;
function Demo() {
const [value, setValue] = useState(result);
function handleChange(point: number) {
setValue(point);
result = point;
}
return <Slider value={value} shortcuts={[10, 50]} onChange={handleChange} />;
}
const { container } = render(<Demo />);
fireEvent.click(screen.getByText('10%'));
fireEvent.click(screen.getByText('50%'));
expect(result).toBe(50);
});
});
@@ -15,7 +15,6 @@
*/
import type { Meta } from '@storybook/react';
import React from 'react';
import { Button } from '../button/Button';
import { Tooltip } from './Tooltip';
+1 -2
View File
@@ -27,7 +27,7 @@ export { Confirm, type IConfirmProps } from './components/confirm';
export { DatePanel, DatePicker, type IDatePanelProps, type IDatePickerProps } from './components/date-picker';
export { Dialog, type IDialogProps } from './components/dialog';
export { DropdownLegacy, type IDropdownLegacyProps } from './components/dropdown-legacy';
export { DropdownOverlay, DropdownProvider, DropdownTrigger, type IDropdownOverlayProps } from './components/dropdown';
export { Dropdown } from './components/dropdown';
export { FormDualColumnLayout, FormLayout, type IFormDualColumnLayoutProps, type IFormLayoutProps } from './components/form-layout';
export { type IInputProps, type IInputWithSlotProps, Input, InputWithSlot } from './components/input';
export { type IInputNumberProps, InputNumber } from './components/input-number';
@@ -41,7 +41,6 @@ export { type IScrollbarProps, Scrollbar } from './components/scrollbar';
export { type ISelectProps, Select } from './components/select';
export { type ISelectListProps, SelectList } from './components/select-list';
export { type ISegmentedProps, Segmented } from './components/segmented';
export { type ISliderProps, Slider } from './components/slider';
export { type ITooltipProps, Tooltip } from './components/tooltip';
export { filterLeafNode, findNodePathFromTree, findSubTreeFromPath, type ITreeNodeProps, type ITreeProps, mergeTreeSelected, Tree, TreeSelectionMode } from './components/tree';
export { Switch } from './components/switch';
-3
View File
@@ -24,9 +24,6 @@ const locale: ILocale = {
cancel: 'cancel',
confirm: 'ok',
},
Slider: {
resetTo: 'Reset to',
},
Picker: {
...dajsEnUS,
...PickerEnUs,
-3
View File
@@ -24,9 +24,6 @@ const locale: ILocale = {
cancel: 'لغو',
confirm: 'باشه',
},
Slider: {
resetTo: 'بازنشانی به',
},
Picker: {
...dajsEnUS,
...PickerEnUs,
-3
View File
@@ -24,9 +24,6 @@ const locale: ILocale = {
cancel: 'annuler',
confirm: 'ok',
},
Slider: {
resetTo: 'Réinitialiser à',
},
Picker: {
...dajsFrFR,
...PickerFrFR,
-1
View File
@@ -19,7 +19,6 @@ import type PickerZhCN from 'rc-picker/lib/locale/zh_CN';
export interface ILocale {
design: {
Confirm: Record<string, any>;
Slider: Record<string, any>;
Picker: typeof PickerZhCN;
CascaderList: Record<string, any>;
Select: Record<string, any>;
-3
View File
@@ -24,9 +24,6 @@ const locale: ILocale = {
cancel: 'отмена',
confirm: 'ок',
},
Slider: {
resetTo: 'Сбросить до',
},
Picker: {
...dajsRuRU,
...PickerRuRU,
-3
View File
@@ -24,9 +24,6 @@ const locale: ILocale = {
cancel: 'Hủy bỏ',
confirm: 'Xác nhận',
},
Slider: {
resetTo: 'Khôi phục đến',
},
Picker: {
...dajsViVN,
...PickerViVN,
-3
View File
@@ -24,9 +24,6 @@ const locale: ILocale = {
cancel: '取消',
confirm: '确定',
},
Slider: {
resetTo: '恢复至',
},
Picker: {
...dajsZhCN,
...PickerZhCN,
-3
View File
@@ -24,9 +24,6 @@ const locale: ILocale = {
cancel: '取消',
confirm: '確定',
},
Slider: {
resetTo: '恢復至',
},
Picker: {
...dajsZhTw,
...PickerZhTw,
@@ -15,14 +15,9 @@
*/
import type { DocumentDataModel } from '@univerjs/core';
import {
ICommandService,
IUniverInstanceService,
UniverInstanceType,
} from '@univerjs/core';
import { Slider } from '@univerjs/design';
import { useDependency, useObservable } from '@univerjs/ui';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { ICommandService, IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
import { Slider, useDependency, useObservable } from '@univerjs/ui';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { SetDocZoomRatioOperation } from '../../commands/operations/set-doc-zoom-ratio.operation';
const ZOOM_MAP = [50, 80, 100, 130, 150, 170, 200, 400];
@@ -16,10 +16,9 @@
import type { IDrawingSearch } from '@univerjs/core';
import { ICommandService, LocaleService } from '@univerjs/core';
import { DropdownLegacy } from '@univerjs/design';
import { clsx, Dropdown } from '@univerjs/design';
import { Autofill, MoreDownSingle } from '@univerjs/icons';
import { useDependency } from '@univerjs/ui';
import clsx from 'clsx';
import React, { useState } from 'react';
import styles from './index.module.less';
@@ -80,11 +79,14 @@ export const ImagePopupMenu: React.FC<IImagePopupMenuProps> = (props: IImagePopu
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<DropdownLegacy
placement="bottomLeft"
trigger={['click']}
<Dropdown
align="start"
overlay={(
<ul className={styles.imagePopupMenu}>
<ul
className={clsx(styles.imagePopupMenu, `
univer-box-border univer-p-2 univer-text-sm univer-theme
`)}
>
{availableMenu.map((item) => (
<li
key={item.index}
@@ -96,8 +98,8 @@ export const ImagePopupMenu: React.FC<IImagePopupMenuProps> = (props: IImagePopu
))}
</ul>
)}
visible={visible}
onVisibleChange={onVisibleChange}
open={visible}
onOpenChange={onVisibleChange}
>
<div
className={clsx(styles.btnContainer, {
@@ -110,7 +112,7 @@ export const ImagePopupMenu: React.FC<IImagePopupMenuProps> = (props: IImagePopu
/>
{showMore && <MoreDownSingle style={{ color: '#CCCCCC', fontSize: '8px', marginLeft: '8px' }} />}
</div>
</DropdownLegacy>
</Dropdown>
</div>
);
};
@@ -9,13 +9,6 @@
margin-bottom: 0;
padding: var(--padding-sm);
font-size: var(--font-size-base);
background-color: rgb(var(--bg-color-secondary));
border: 1px solid rgb(var(--border-color));
border-radius: var(--border-radius-lg);
box-shadow: var(--box-shadow-lg);
li {
list-style: none;
}
@@ -31,7 +24,6 @@
box-sizing: border-box;
height: 28px;
font-size: var(--font-size-base);
font-style: normal;
line-height: 20px; /* 153.846% */
text-align: left;
@@ -15,7 +15,7 @@
*/
import { ColorKit } from '@univerjs/core';
import { DropdownLegacy, ColorPicker as OriginColorPicker } from '@univerjs/design';
import { Dropdown, ColorPicker as OriginColorPicker } from '@univerjs/design';
import { MoreDownSingle } from '@univerjs/icons';
import { ComponentManager, useDependency } from '@univerjs/ui';
import React, { useMemo } from 'react';
@@ -40,26 +40,23 @@ export const ColorPicker = (props: IColorPickerProps) => {
return Icon && (!disable
? (
<DropdownLegacy
<Dropdown
overlay={(
<div className={`
${styles.cfColorPicker}
`}
>
<div className="univer-rounded-lg univer-p-4">
<OriginColorPicker value={color} onChange={onChange} />
</div>
)}
>
<span className={`
${styles.cfColorPickerIcon}
${className}
`}
<span
className={`
${styles.cfColorPickerIcon}
${className}
`}
>
<Icon extend={{ colorChannel1: colorKit.isValid ? color : 'rgb(var(--primary-color))' }} />
{isNeedDropdownIcon && <MoreDownSingle className={styles.iconDropdown} />}
</span>
</DropdownLegacy>
</Dropdown>
)
: <Icon className={className} extend={{ colorChannel1: colorKit.isValid ? color : 'rgb(var(--primary-color))' }} />);
};
@@ -18,7 +18,7 @@ import type { Workbook } from '@univerjs/core';
import type { IColorScale, IConditionalFormattingRuleConfig } from '@univerjs/sheets-conditional-formatting';
import type { IStyleEditorProps } from './type';
import { IUniverInstanceService, LocaleService, UniverInstanceType } from '@univerjs/core';
import { InputNumber, Select } from '@univerjs/design';
import { clsx, InputNumber, Select } from '@univerjs/design';
import { CFRuleType, CFValueType, createDefaultValueByValueType } from '@univerjs/sheets-conditional-formatting';
import { FormulaEditor } from '@univerjs/sheets-formula-ui';
import { useDependency, useSidebarClick } from '@univerjs/ui';
@@ -222,17 +222,19 @@ export const ColorScaleStyleEditor = (props: IStyleEditorProps) => {
return (
<div>
<div className={stylesBase.title}>{localeService.t('sheet.cf.panel.styleRule')}</div>
<div className={`
${styles.cfPreviewWrap}
`}
<div
className={`
${styles.cfPreviewWrap}
`}
>
<Preview rule={getResult({ minType, medianType, maxType, minValue, medianValue, maxValue, minColor, medianColor, maxColor }) as IConditionalFormattingRuleConfig} />
</div>
<div className={stylesBase.label}>{localeService.t('sheet.cf.valueType.min')}</div>
<div className={`
${stylesBase.labelContainer}
${stylesBase.mTSm}
`}
<div
className={clsx(`
${stylesBase.labelContainer}
${stylesBase.mTSm}
`, 'univer-box-border univer-h-7')}
>
<Select
style={{ flexShrink: 0 }}
@@ -267,10 +269,11 @@ export const ColorScaleStyleEditor = (props: IStyleEditorProps) => {
/>
</div>
<div className={stylesBase.label}>{localeService.t('sheet.cf.panel.medianValue')}</div>
<div className={`
${stylesBase.labelContainer}
${stylesBase.mTSm}
`}
<div
className={clsx(`
${stylesBase.labelContainer}
${stylesBase.mTSm}
`, 'univer-box-border univer-h-7')}
>
<Select
style={{ flexShrink: 0 }}
@@ -309,10 +312,11 @@ export const ColorScaleStyleEditor = (props: IStyleEditorProps) => {
</div>
<div className={stylesBase.label}>{localeService.t('sheet.cf.valueType.max')}</div>
<div className={`
${stylesBase.labelContainer}
${stylesBase.mTSm}
`}
<div
className={clsx(`
${stylesBase.labelContainer}
${stylesBase.mTSm}
`, 'univer-box-border univer-h-7')}
>
<Select
style={{ flexShrink: 0 }}
@@ -19,7 +19,7 @@ import type { IIconSet, IIconType } from '@univerjs/sheets-conditional-formattin
import type { IStyleEditorProps } from './type';
import { get, IUniverInstanceService, LocaleService, set, Tools, UniverInstanceType } from '@univerjs/core';
import { Checkbox, DropdownLegacy, InputNumber, Select } from '@univerjs/design';
import { Checkbox, clsx, Dropdown, InputNumber, Select } from '@univerjs/design';
import { MoreDownSingle, SlashSingle } from '@univerjs/icons';
import {
CFNumberOperator,
@@ -285,12 +285,18 @@ const IconSetRuleEdit = (props: {
${styles.width45}
`}
>
<DropdownLegacy overlay={<IconItemList onClick={handleIconClick} iconId={item.iconId} iconType={item.iconType} />}>
<div className={styles.dropdownIcon}>
<Dropdown
overlay={(
<div className="univer-rounded-lg univer-p-4">
<IconItemList onClick={handleIconClick} iconId={item.iconId} iconType={item.iconType} />
</div>
)}
>
<div className={clsx(styles.dropdownIcon, 'univer-box-border univer-h-7')}>
{icon ? <img src={icon} className={styles.icon} /> : <SlashSingle className={styles.icon} />}
<MoreDownSingle />
</div>
</DropdownLegacy>
</Dropdown>
</div>
{!isEnd
@@ -560,24 +566,24 @@ export const IconSet = (props: IStyleEditorProps<unknown, IIconSet>) => {
${stylesBase.mTSm}
`}
>
<DropdownLegacy
placement="bottomLeft"
<Dropdown
overlay={(
<IconGroupList
ref={(el) => {
!iconGroupListEl && el && setIconGroupListEl(el);
}}
iconType={currentIconType}
onClick={handleClickIconList}
/>
<div className="univer-rounded-lg univer-p-4">
<IconGroupList
ref={(el) => {
!iconGroupListEl && el && setIconGroupListEl(el);
}}
iconType={currentIconType}
onClick={handleClickIconList}
/>
</div>
)}
>
<div className={styles.dropdownIcon} style={{ width: 'unset' }}>
<div className={clsx(styles.dropdownIcon, 'univer-box-border univer-h-7 univer-w-auto')}>
{previewIcon}
<MoreDownSingle />
</div>
{/* <div>{previewIcon}</div> */}
</DropdownLegacy>
</Dropdown>
</div>
<div
className={`
@@ -75,7 +75,7 @@
},
"devDependencies": {
"@univerjs-infra/shared": "workspace:*",
"@univerjs/icons-svg": "^0.2.15",
"@univerjs/icons-svg": "^0.2.20",
"rxjs": "^7.8.1",
"typescript": "^5.7.3",
"vite": "^6.1.1",
@@ -156,7 +156,7 @@ export function SelectFunction(props: ISelectFunctionProps) {
onKeyDown={handleSelectListKeyDown}
value={searchText}
onChange={handleSearchInputChange}
size="large"
size="small"
allowClear
/>
</div>
@@ -200,9 +200,9 @@ export function SelectFunction(props: ISelectFunctionProps) {
/>
{functionInfo.functionParameter &&
functionInfo.functionParameter.map((item: IFunctionParam, i: number) => (
functionInfo.functionParameter.map((item: IFunctionParam) => (
<FunctionParams
key={i}
key={item.name}
title={item.name}
value={`${item.require ? required : optional} ${item.detail}`}
/>
@@ -17,7 +17,7 @@
import type { IBorderInfo } from '@univerjs/sheets';
import type { IBorderPanelProps } from './interface';
import { BorderStyleTypes } from '@univerjs/core';
import { ColorPicker, DropdownLegacy, Menu, MenuItem } from '@univerjs/design';
import { ColorPicker, Dropdown, Menu, MenuItem } from '@univerjs/design';
import { MoreDownSingle, PaintBucket } from '@univerjs/icons';
import { BorderStyleManagerService } from '@univerjs/sheets';
import { ComponentManager, useDependency } from '@univerjs/ui';
@@ -121,14 +121,11 @@ export function BorderPanel(props: IBorderPanelProps) {
<div className={styles.uiPluginSheetsBorderPanelStyles}>
<div>
<DropdownLegacy
align={{
offset: [0, 18],
}}
<Dropdown
overlay={(
<section className={styles.uiPluginSheetsBorderPanelBoard} onClick={stopPropagation}>
<div className="univer-rounded-lg univer-p-4" onClick={stopPropagation}>
<ColorPicker onChange={(value) => handleClick(value, 'color')} />
</section>
</div>
)}
>
<a className={styles.uiPluginSheetsBorderPanelButton} onClick={stopPropagation}>
@@ -137,16 +134,13 @@ export function BorderPanel(props: IBorderPanelProps) {
<MoreDownSingle />
</span>
</a>
</DropdownLegacy>
</Dropdown>
</div>
<div>
<DropdownLegacy
align={{
offset: [0, 18],
}}
<Dropdown
overlay={(
<section onClick={stopPropagation}>
<section className="univer-rounded-lg univer-p-4" onClick={stopPropagation}>
<Menu>
{BORDER_SIZE_CHILDREN.map((item) => (
<MenuItem
@@ -167,7 +161,7 @@ export function BorderPanel(props: IBorderPanelProps) {
<MoreDownSingle />
</span>
</a>
</DropdownLegacy>
</Dropdown>
</div>
</div>
</section>
@@ -41,16 +41,6 @@
justify-content: space-around;
}
&-board {
box-sizing: border-box;
padding: var(--padding-base);
background-color: rgb(var(--bg-color-secondary));
border: 1px solid rgb(var(--border-color));
border-radius: var(--border-radius-lg);
box-shadow: var(--box-shadow-base);
}
&-button {
cursor: pointer;
@@ -22,7 +22,7 @@ import { DOCS_COMPONENT_MAIN_LAYER_INDEX, VIEWPORT_KEY } from '@univerjs/docs-ui
import { convertTextRotation, fixLineWidthByScale, IRenderManagerService, Rect, ScrollBar } from '@univerjs/engine-render';
import { ILayoutService } from '@univerjs/ui';
import { getEditorObject } from '../../basics/editor/get-editor-object';
import styles from '../../views/sheet-container/index.module.less';
import { SHEET_FOOTER_BAR_HEIGHT } from '../../views/sheet-container/SheetContainer';
import { IEditorBridgeService } from '../editor-bridge.service';
import { SheetSkeletonManagerService } from '../sheet-skeleton-manager.service';
import { ICellEditorManagerService } from './cell-editor-manager.service';
@@ -217,7 +217,7 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM
const clientHeight =
document.body.clientHeight -
startY -
Number.parseFloat(styles.sheetFooterBarHeight) -
SHEET_FOOTER_BAR_HEIGHT -
canvasOffset.top -
EDITOR_BORDER_SIZE * 2;
@@ -17,15 +17,14 @@
import type { IDiscreteRange } from '../../controllers/utils/range-tools';
import type { IPasteHookKeyType } from '../../services/clipboard/type';
import { ICommandService, IUniverInstanceService, LocaleService } from '@univerjs/core';
import { DropdownOverlay, DropdownProvider, DropdownTrigger } from '@univerjs/design';
import { clsx, Dropdown } from '@univerjs/design';
import { convertTransformToOffsetX, convertTransformToOffsetY, IRenderManagerService } from '@univerjs/engine-render';
import { CheckMarkSingle, MoreDownSingle, PasteSpecial } from '@univerjs/icons';
import { useDependency, useObservable } from '@univerjs/ui';
import clsx from 'clsx';
import { useState } from 'react';
import { SheetOptionalPasteCommand } from '../../commands/commands/clipboard.command';
import { useActiveWorkbook } from '../../components/hook';
import { SheetClipboardController } from '../../controllers/clipboard/clipboard.controller';
// import { SheetClipboardController } from '../../controllers/clipboard/clipboard.controller';
import { getSheetObject } from '../../controllers/utils/component-tools';
import { ISheetClipboardService, PREDEFINED_HOOK_NAME } from '../../services/clipboard/clipboard.service';
import { ISheetSelectionRenderService } from '../../services/selection/base-selection-render.service';
@@ -112,7 +111,7 @@ const useMenuPosition = (range?: IDiscreteRange) => {
export const ClipboardPopupMenu = () => {
const clipboardService = useDependency(ISheetClipboardService);
const showMenu = useObservable(clipboardService.showMenu$, false);
const clipboardController = useDependency(SheetClipboardController);
// const clipboardController = useDependency(SheetClipboardController);
const pasteOptionsCache = useObservable(clipboardService.pasteOptionsCache$, null);
const localeService = useDependency(LocaleService);
const commandService = useDependency(ICommandService);
@@ -120,7 +119,7 @@ export const ClipboardPopupMenu = () => {
const [menuHovered, setMenuHovered] = useState(false);
const [menuVisible, setMenuVisible] = useState(false);
const version = useObservable(clipboardController.refreshOptionalPaste$, Math.random());
// const version = useObservable(clipboardController.refreshOptionalPaste$, Math.random());
const range = pasteOptionsCache?.target.pastedRange;
@@ -133,6 +132,7 @@ export const ClipboardPopupMenu = () => {
const iconVisible = menuHovered || menuVisible;
const handleClick = (type: string) => {
setMenuVisible(false);
commandService.executeCommand(SheetOptionalPasteCommand.id, { type });
};
@@ -146,52 +146,58 @@ export const ClipboardPopupMenu = () => {
onMouseEnter={() => setMenuHovered(true)}
onMouseLeave={() => setMenuHovered(false)}
>
<DropdownProvider
visible={menuVisible}
onVisibleChange={(visible) => {
setMenuVisible(visible);
}}
>
<DropdownTrigger>
<Dropdown
overlay={(
<div
className={styles.sheetPasteOptionsIconWrapper}
onClick={() => {
setMenuVisible(!menuVisible);
}}
className={clsx(styles.sheetPasteOptionsMenu, `
univer-border univer-border-solid univer-border-gray-200 univer-opacity-100
`)}
>
<PasteSpecial
style={{ color: '#35322B' }}
extend={{ colorChannel1: 'rgb(var(--green-700))' }}
/>
{iconVisible && <MoreDownSingle />}
<ul>
{SheetPasteOptions.map((item) => {
const itemType = PREDEFINED_HOOK_NAME[item.value as IPasteHookKeyType];
const selected = pasteOptionsCache?.pasteType === itemType;
return (
<li
key={item.value}
className={clsx(styles.sheetPasteOptionsMenuItem, 'hover:univer-bg-neutral-100')}
onClick={() => handleClick(item.value)}
>
<span>
{selected && (
<CheckMarkSingle className={styles.sheetPasteOptionsMenuItemIcon} style={{ color: 'rgb(var(--green-700, #409f11))' }} />
)}
</span>
<div
className={clsx(styles.sheetPasteOptionsMenuItemTitle, `
univer-text-gray-700
`)}
>
{localeService.t(item.label)}
</div>
</li>
);
})}
</ul>
</div>
</DropdownTrigger>
<DropdownOverlay className={clsx(styles.sheetPasteOptionsMenu, `
univer-border univer-border-solid univer-border-gray-200 univer-opacity-100
`)}
)}
open={menuVisible}
onOpenChange={setMenuVisible}
>
<div
className={styles.sheetPasteOptionsIconWrapper}
onClick={() => {
setMenuVisible(!menuVisible);
}}
>
<ul>
{SheetPasteOptions.map((item) => {
const itemType = PREDEFINED_HOOK_NAME[item.value as IPasteHookKeyType];
const selected = pasteOptionsCache?.pasteType === itemType;
return (
<li
key={item.value}
onClick={() => handleClick(item.value)}
className={clsx(styles.sheetPasteOptionsMenuItem, 'hover:univer-bg-neutral-100')}
>
<span>
{selected && (
<CheckMarkSingle className={styles.sheetPasteOptionsMenuItemIcon} style={{ color: 'rgb(var(--green-700, #409f11))' }} />
)}
</span>
<div className={clsx(styles.sheetPasteOptionsMenuItemTitle, 'univer-text-gray-700')}>{localeService.t(item.label)}</div>
</li>
);
})}
</ul>
</DropdownOverlay>
</DropdownProvider>
<PasteSpecial
style={{ color: '#35322B' }}
extend={{ colorChannel1: 'rgb(var(--green-700))' }}
/>
{iconVisible && <MoreDownSingle />}
</div>
</Dropdown>
</div>
);
};
@@ -21,10 +21,9 @@ import {
IUniverInstanceService,
UniverInstanceType,
} from '@univerjs/core';
import { Slider } from '@univerjs/design';
import { SetWorksheetActiveOperation } from '@univerjs/sheets';
import { useDependency, useObservable } from '@univerjs/ui';
import React, { useCallback, useEffect, useState } from 'react';
import { Slider, useDependency, useObservable } from '@univerjs/ui';
import { useCallback, useEffect, useState } from 'react';
import { SetZoomRatioCommand } from '../../commands/commands/set-zoom-ratio.command';
import { SetZoomRatioOperation } from '../../commands/operations/set-zoom-ratio.operation';
@@ -14,8 +14,7 @@
* limitations under the License.
*/
import { DropdownOverlay, DropdownProvider, DropdownTrigger } from '@univerjs/design';
import { Dropdown } from '@univerjs/design';
import { IDefinedNamesService } from '@univerjs/engine-formula';
import { MoreDownSingle } from '@univerjs/icons';
import { useDependency } from '@univerjs/ui';
@@ -45,11 +44,16 @@ export function DefinedName({ disable }: { disable: boolean }) {
}
return (
<div className={styles.definedName}>
<div
className={`
univer-relative univer-box-border univer-flex univer-h-full univer-w-24 univer-border-r-gray-200
univer-py-1.5 univer-transition-all
`}
>
<input
className={clsx(`
univer-absolute univer-box-border univer-h-full univer-w-full univer-appearance-none
univer-border-none univer-px-1.5
univer-box-border univer-h-full univer-w-full univer-appearance-none univer-border-0 univer-border-r
univer-border-r-gray-200 univer-px-1.5
focus:univer-outline-none
`, {
[styles.defineNameInputDisable]: disable,
@@ -59,27 +63,26 @@ export function DefinedName({ disable }: { disable: boolean }) {
onChange={handleChangeSelection}
/>
<DropdownProvider>
<DropdownTrigger>
<a
className={clsx(`
univer-absolute univer-right-0 univer-flex univer-h-full univer-cursor-pointer
univer-items-center univer-justify-center univer-px-1 univer-transition-colors
univer-duration-200
hover:univer-bg-gray-100
`,
{
'univer-cursor-not-allowed univer-text-gray-300 hover:univer-bg-transparent': disable,
})}
>
<MoreDownSingle />
</a>
</DropdownTrigger>
<DropdownOverlay className="univer-z-[1001]" offset={{ x: -75, y: 2 }}>
<DefinedNameOverlay />
</DropdownOverlay>
</DropdownProvider>
<Dropdown
overlay={(
<div className="univer-z-[1001]">
<DefinedNameOverlay />
</div>
)}
>
<a
className={clsx(`
univer-absolute univer-right-0 univer-top-0 univer-flex univer-h-full univer-cursor-pointer
univer-items-center univer-justify-center univer-px-1 univer-transition-colors univer-duration-200
hover:univer-bg-gray-100
`,
{
'univer-cursor-not-allowed univer-text-gray-300 hover:univer-bg-transparent': disable,
})}
>
<MoreDownSingle />
</a>
</Dropdown>
</div>
);
}
@@ -1,18 +1,4 @@
.defined-name {
display: flex;
position: relative;
transition: height 0.15s;
border-right: 1px solid rgb(var(--border-color));
height: 100%;
width: 100px;
box-sizing: border-box;
&-overlay {
position: relative;
width: 300px;
@@ -160,12 +160,14 @@ export function FormulaBar(props: IProps) {
}, [editorBridgeService.currentEditCellState$]);
useEffect(() => {
if (ref.current) {
const handleResize = () => {
const editorRect = ref.current!.getBoundingClientRect();
formulaEditorManagerService.setPosition(editorRect);
};
const handleResize = () => {
if (!ref.current) return;
const editorRect = ref.current.getBoundingClientRect();
formulaEditorManagerService.setPosition(editorRect);
};
if (ref.current) {
handleResize();
const a = new ResizeObserver(handleResize);
@@ -16,11 +16,10 @@
import type { ICommandInfo, IExecutionOptions } from '@univerjs/core';
import { ICommandService, IUniverInstanceService, LocaleService, toDisposable } from '@univerjs/core';
import { DropdownLegacy } from '@univerjs/design';
import { clsx, Dropdown } from '@univerjs/design';
import { convertTransformToOffsetX, convertTransformToOffsetY, IRenderManagerService } from '@univerjs/engine-render';
import { Autofill, CheckMarkSingle, MoreDownSingle } from '@univerjs/icons';
import { useDependency } from '@univerjs/ui';
import clsx from 'clsx';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { RefillCommand } from '../../commands/commands/refill.command';
import { SetScrollOperation } from '../../commands/operations/scroll.operation';
@@ -186,16 +185,20 @@ export const AutoFillPopupMenu: React.FC<{}> = () => {
onMouseLeave={handleMouseLeave}
style={{ left: `${relativeX + 2}px`, top: `${relativeY + 2}px`, position: 'absolute' }}
>
<DropdownLegacy
placement="bottomLeft"
trigger={['click']}
<Dropdown
align="start"
overlay={(
<ul className={styles.autoFillPopupMenu}>
<ul
className={`
univer-rounded-lg univer-p-2 univer-theme univer-m-0 univer-box-border univer-grid
univer-list-none univer-gap-1
`}
>
{availableMenu.map((item) => (
<li
key={item.index}
onClick={() => handleClick(item)}
className={styles.autoFillPopupMenuItem}
onClick={() => handleClick(item)}
>
<span className={styles.autoFillPopupMenuItemIcon}>
{item.value === selected && (
@@ -207,8 +210,8 @@ export const AutoFillPopupMenu: React.FC<{}> = () => {
))}
</ul>
)}
visible={visible}
onVisibleChange={onVisibleChange}
open={visible}
onOpenChange={onVisibleChange}
>
<div
className={clsx(styles.btnContainer, {
@@ -221,7 +224,7 @@ export const AutoFillPopupMenu: React.FC<{}> = () => {
/>
{showMore && <MoreDownSingle style={{ color: '#CCCCCC', fontSize: '8px', marginLeft: '8px' }} />}
</div>
</DropdownLegacy>
</Dropdown>
</div>
);
};
@@ -7,31 +7,9 @@
width: 0;
height: 0;
}
@item-padding-left: 36px;
@auto-fill-popup-menu-min-width: 128px;
@item-padding-left: 24px;
.auto-fill-popup-menu {
display: grid;
gap: var(--margin-xxs);
align-items: center;
box-sizing: border-box;
min-width: @auto-fill-popup-menu-min-width;
margin-top: 0;
margin-bottom: 0;
padding: var(--padding-sm);
font-size: var(--font-size-base);
background-color: rgb(var(--bg-color-secondary));
border: 1px solid rgb(var(--border-color));
border-radius: var(--border-radius-lg);
box-shadow: var(--box-shadow-lg);
li {
list-style: none;
}
&-item {
cursor: pointer;
@@ -41,7 +19,6 @@
align-items: center;
box-sizing: border-box;
width: 134px;
height: 28px;
padding: var(--padding-xs) 0 var(--padding-xs) @item-padding-left;
@@ -16,10 +16,11 @@
import type { IScrollState } from './sheet-bar-tabs/utils/slide-tab-bar';
import { ICommandService, IPermissionService } from '@univerjs/core';
import { clsx } from '@univerjs/design';
import { IncreaseSingle, MoreSingle } from '@univerjs/icons';
import { InsertSheetCommand, WorkbookCreateSheetPermission, WorkbookEditablePermission } from '@univerjs/sheets';
import { useDependency, useObservable } from '@univerjs/ui';
import { useDependency, useObservable } from '@univerjs/ui';
import React, { useEffect, useState } from 'react';
import { useActiveWorkbook } from '../../components/hook';
import { ISheetBarService } from '../../services/sheet-bar/sheet-bar.service';
@@ -77,7 +78,7 @@ export const SheetBar = () => {
};
return (
<div className={styles.sheetBar}>
<div className={clsx(styles.sheetBar, 'univer-relative univer-flex univer-h-full univer-flex-1')}>
<div className={styles.sheetBarOptions}>
{/* Add sheet button */}
<SheetBarButton onClick={addSheet} disabled={!(workbookCreateSheetPermission?.value && workbookEditablePermission?.value)}>
@@ -92,10 +93,11 @@ export const SheetBar = () => {
{/* Scroll arrows */}
{(!leftScrollState || !rightScrollState) && (
<div className={`
${styles.sheetBarOptions}
${styles.sheetBarOptionsDivider}
`}
<div
className={`
${styles.sheetBarOptions}
${styles.sheetBarOptionsDivider}
`}
>
<SheetBarButton disabled={leftScrollState} onClick={handleScrollLeft}>
<MoreSingle style={{ transform: 'rotateZ(180deg)' }} />
@@ -4,17 +4,8 @@
@margin-right: -4px;
.sheet-bar {
position: relative;
display: flex;
flex: 1 1 0px;
flex-direction: row;
min-width: calc(100% - @zoom-slider-width - @status-bar-max-width);
max-width: calc(100% - @zoom-slider-width);
height: 100%;
background-color: rgb(var(--bg-color-secondary));
&-options {
display: flex;
@@ -1,10 +1,8 @@
@import '../../sheet-container/index.module.less';
@padding-left-right: 24px;
@line-height: 20px;
.sheet-bar-menu {
max-height: calc(100vh - @sheet-footer-bar-height);
max-height: calc(100vh - 36px);
display: grid;
gap: var(--margin-xxs);
align-items: center;
@@ -17,8 +17,7 @@
import type { Workbook } from '@univerjs/core';
import { IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
import { ContextMenuPosition, IMenuManagerService, ToolbarItem, useDependency, useObservable } from '@univerjs/ui';
import React, { useMemo } from 'react';
import { useMemo } from 'react';
import { useActiveWorkbook } from '../../components/hook';
import { CountBar } from '../count-bar/CountBar';
import { EditorContainer } from '../editor-container/EditorContainer';
@@ -26,7 +25,8 @@ import { FormulaBar } from '../formula-bar/FormulaBar';
import { OperateContainer } from '../operate-container/OperateContainer';
import { SheetBar } from '../sheet-bar/SheetBar';
import { StatusBar } from '../status-bar/StatusBar';
import styles from './index.module.less';
export const SHEET_FOOTER_BAR_HEIGHT = 36;
export function RenderSheetFooter() {
const menuManagerService = useDependency(IMenuManagerService);
@@ -37,20 +37,33 @@ export function RenderSheetFooter() {
const footerMenus = menuManagerService.getMenuByPositionKey(ContextMenuPosition.FOOTER_MENU);
return (
<section className={styles.sheetContainer} data-range-selector>
<section
className={`
univer-box-border univer-flex univer-items-center univer-justify-between univer-bg-white univer-px-5
univer-text-gray-900
dark:univer-bg-gray-900 dark:univer-text-gray-200
`}
style={{
height: SHEET_FOOTER_BAR_HEIGHT,
}}
data-range-selector
>
<SheetBar />
<StatusBar />
{footerMenus.map((item) => item.children?.map((child) => (
child?.item && (
<ToolbarItem
key={child.key}
align={{
offset: [-32, 18],
}}
{...child.item}
/>
)
)))}
{footerMenus.length && (
<div className="univer-mr-2 univer-flex univer-gap-2">
{footerMenus.map((item) => item.children?.map((child) => (
child?.item && (
<ToolbarItem
key={child.key}
{...child.item}
/>
)
)))}
</div>
)}
<CountBar />
</section>
);
@@ -1,21 +0,0 @@
@sheet-footer-bar-height: 36px;
:export {
--sheet-footer-bar-height: unit(@sheet-footer-bar-height);
}
.sheet-container {
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
height: @sheet-footer-bar-height;
padding: 0 var(--padding-xl);
background-color: rgb(var(--bg-color-secondary));
> span:not(:last-of-type) {
margin-right: 4px;
}
}
@@ -190,7 +190,7 @@ export const StatusBar = () => {
[styles.singleMode]: isSingle,
})}
>
<div className={clsx(styles.statisticList)}>{renderContent}</div>
<div className="univer-flex">{renderContent}</div>
<div className={styles.statusBarDiv} />
</div>
)
@@ -18,14 +18,6 @@
}
.statistic {
&-list {
display: flex;
}
&-list-column {
flex-direction: column;
}
&-list-column &-item {
font-size: 12px;
line-height: 140%;
@@ -16,7 +16,7 @@
import type { IDrawingSearch } from '@univerjs/core';
import { ICommandService, LocaleService } from '@univerjs/core';
import { DropdownLegacy } from '@univerjs/design';
import { Dropdown } from '@univerjs/design';
import { Autofill, MoreDownSingle } from '@univerjs/icons';
import { useDependency } from '@univerjs/ui';
import clsx from 'clsx';
@@ -80,11 +80,14 @@ export const SlideImagePopupMenu: React.FC<IImagePopupMenuProps> = (props: IImag
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<DropdownLegacy
placement="bottomLeft"
trigger={['click']}
<Dropdown
align="start"
overlay={(
<ul className={styles.imagePopupMenu}>
<ul
className={clsx(styles.imagePopupMenu, `
univer-box-border univer-p-2 univer-text-sm univer-theme
`)}
>
{availableMenu.map((item) => (
<li
key={item.index}
@@ -96,8 +99,8 @@ export const SlideImagePopupMenu: React.FC<IImagePopupMenuProps> = (props: IImag
))}
</ul>
)}
visible={visible}
onVisibleChange={onVisibleChange}
open={visible}
onOpenChange={onVisibleChange}
>
<div
className={clsx(styles.btnContainer, {
@@ -110,7 +113,7 @@ export const SlideImagePopupMenu: React.FC<IImagePopupMenuProps> = (props: IImag
/>
{showMore && <MoreDownSingle style={{ color: '#CCCCCC', fontSize: '8px', marginLeft: '8px' }} />}
</div>
</DropdownLegacy>
</Dropdown>
</div>
);
};
@@ -18,7 +18,7 @@ import type { Nullable } from '@univerjs/core';
import type { Rect } from '@univerjs/engine-render';
import { ICommandService, LocaleService } from '@univerjs/core';
import { ColorPicker, DropdownLegacy } from '@univerjs/design';
import { ColorPicker, Dropdown } from '@univerjs/design';
import { MoreDownSingle, PaintBucket } from '@univerjs/icons';
import { useDependency } from '@univerjs/ui';
import clsx from 'clsx';
@@ -93,17 +93,14 @@ export default function ArrangePanel(props: IProps) {
</div>
<div className={styles.imageCommonPanelRow}>
<div className={clsx(styles.imageCommonPanelColumn, styles.imageCommonPanelSpan2)}>
<DropdownLegacy
align={{
offset: [0, 18],
}}
<Dropdown
overlay={(
<section className={styles.slidePanelColorPicker}>
<div className="univer-rounded-lg univer-p-4">
<ColorPicker
value="#fff"
onChange={handleChangeColor}
/>
</section>
</div>
)}
>
<a className={styles.uiPluginSheetsBorderPanelButton}>
@@ -112,7 +109,7 @@ export default function ArrangePanel(props: IProps) {
<MoreDownSingle />
</span>
</a>
</DropdownLegacy>
</Dropdown>
</div>
</div>
</div>
@@ -18,7 +18,7 @@ import type { IUser, UniverInstanceType } from '@univerjs/core';
import type { IAddCommentCommandParams, IThreadComment, IUpdateCommentCommandParams } from '@univerjs/thread-comment';
import type { IThreadCommentEditorInstance } from '../thread-comment-editor';
import { generateRandomId, ICommandService, LocaleService, UserManagerService } from '@univerjs/core';
import { DropdownLegacy, Menu, MenuItem, Tooltip } from '@univerjs/design';
import { Dropdown, Tooltip } from '@univerjs/design';
import { DeleteSingle, MoreHorizontalSingle, ReplyToCommentSingle, ResolvedSingle, SolveSingle } from '@univerjs/icons';
import { AddCommentCommand, DeleteCommentCommand, DeleteCommentTreeCommand, getDT, ResolveCommentCommand, ThreadCommentModel, UpdateCommentCommand } from '@univerjs/thread-comment';
import { useDependency, useObservable } from '@univerjs/ui';
@@ -118,18 +118,40 @@ const ThreadCommentItem = (props: IThreadCommentItemProps) => {
)}
{isCommentBySelf && !isMock && !resolved
? (
<DropdownLegacy
<Dropdown
overlay={(
<Menu>
<MenuItem key="edit" onClick={() => onEditingChange?.(true)}>{localeService.t('threadCommentUI.item.edit')}</MenuItem>
<MenuItem key="delete" onClick={handleDeleteItem}>{localeService.t('threadCommentUI.item.delete')}</MenuItem>
</Menu>
<div className="univer-rounded-lg univer-p-4 univer-theme">
<ul
className={`
univer-m-0 univer-grid univer-list-none univer-gap-2 univer-p-0
univer-text-sm
[&_a]:univer-cursor-pointer [&_a]:univer-rounded [&_a]:univer-p-1
`}
>
<li>
<a
className="hover:univer-bg-gray-200"
onClick={() => onEditingChange?.(true)}
>
{localeService.t('threadCommentUI.item.edit')}
</a>
</li>
<li>
<a
className="hover:univer-bg-gray-200"
onClick={handleDeleteItem}
>
{localeService.t('threadCommentUI.item.delete')}
</a>
</li>
</ul>
</div>
)}
>
<div className={styles.threadCommentIcon}>
<MoreHorizontalSingle />
</div>
</DropdownLegacy>
</Dropdown>
)
: null}
</div>
+7 -7
View File
@@ -14,11 +14,11 @@
* limitations under the License.
*/
import canUseDom from 'rc-util/lib/Dom/canUseDom';
import { useEffect } from 'react';
import type { Nullable } from '@univerjs/core';
import { resizeObserverCtor } from '@univerjs/design';
import canUseDom from 'rc-util/lib/Dom/canUseDom';
import { useEffect } from 'react';
import { useEvent } from './event';
/**
* These hooks are used for browser layout
@@ -40,12 +40,12 @@ export function useScrollYOverContainer(element: Nullable<HTMLElement>, containe
const elStyle = element.style;
const elRect = element.getBoundingClientRect();
const { y } = elRect;
const { y, height } = elRect;
const containerRect = container.getBoundingClientRect();
const scrolled = element.scrollHeight > elRect.height;
const scrolled = element.scrollHeight > height;
const isOverViewport = y < 0 || (y + elRect.height > containerRect.height);
const isOverViewport = y < 0 || (y + height > containerRect.height);
if (!isOverViewport && !scrolled) {
elStyle.overflowY = '';
@@ -54,7 +54,7 @@ export function useScrollYOverContainer(element: Nullable<HTMLElement>, containe
}
if (isOverViewport) {
elStyle.overflowY = 'scroll';
elStyle.overflowY = 'auto';
elStyle.maxHeight = y < 0 ? `${element.scrollHeight + y}px` : `${containerRect.height - y}px`;
}
});
+1
View File
@@ -18,3 +18,4 @@ export * from './custom-label';
export * from './font-family';
export * from './font-size';
export { useScrollYOverContainer } from './hooks/layout';
export { type ISliderProps, Slider } from './slider';
@@ -14,6 +14,8 @@
* limitations under the License.
*/
import type {
MenuRef } from '@univerjs/design';
import type {
IDisplayMenuItem,
IMenuButtonItem,
@@ -186,9 +188,15 @@ export const Menu = (props: IBaseMenuProps) => {
useScrollYOverContainer(overViewport === 'scroll' ? menuEl : null, layoutService.rootContainerElement);
function handleSetMenuEl(ref: MenuRef | null) {
if (ref) {
setMenuEl(ref.list);
}
}
return (
<DesignMenu
ref={(ref) => ref?.list && setMenuEl(ref.list)}
ref={handleSetMenuEl}
selectable={false}
>
<MenuOptionsWrapper {...restProps} />
@@ -16,7 +16,6 @@
import type { Meta } from '@storybook/react';
import { Button } from '@univerjs/design';
import React from 'react';
import { Notification, notification } from './Notification';
@@ -14,16 +14,11 @@
* limitations under the License.
*/
import { LocaleService } from '@univerjs/core';
import { Button, clsx, Dropdown, Tooltip } from '@univerjs/design';
import { CheckMarkSingle, IncreaseSingle, ReduceSingle } from '@univerjs/icons';
import clsx from 'clsx';
import React, { useContext, useMemo, useRef } from 'react';
import { Button } from '../button/Button';
import { ConfigContext } from '../config-provider/ConfigProvider';
import { DropdownOverlay } from '../dropdown/DropdownOverlay';
import { DropdownProvider } from '../dropdown/DropdownProvider';
import { DropdownTrigger } from '../dropdown/DropdownTrigger';
import { Tooltip } from '../tooltip/Tooltip';
import { useDependency } from '@wendellhu/redi/react-bindings';
import React, { useMemo, useRef, useState } from 'react';
import styles from './index.module.less';
export interface ISliderProps {
@@ -65,11 +60,12 @@ export interface ISliderProps {
* Slider Component
*/
export function Slider(props: ISliderProps) {
const localeService = useDependency(LocaleService);
const { value, min = 0, max = 400, disabled = false, resetPoint = 100, shortcuts, onChange } = props;
const sliderInnerRailRef = useRef<HTMLDivElement>(null);
const { locale } = useContext(ConfigContext);
const [zoomListVisible, setZoomListVisible] = useState(false);
function handleReset() {
if (disabled) return;
@@ -147,6 +143,13 @@ export function Slider(props: ISliderProps) {
window.addEventListener('pointerout', onMouseOut);
}
function handleSelectZoomLevel(value: number) {
if (disabled) return;
setZoomListVisible(false);
onChange && onChange(value);
}
return (
<div
className={clsx(styles.slider, {
@@ -159,13 +162,13 @@ export function Slider(props: ISliderProps) {
<div className={styles.sliderRail}>
<div ref={sliderInnerRailRef} role="track" className={styles.sliderInnerRail}>
<Tooltip title={`${locale?.Slider.resetTo} ${resetPoint}%`} placement="top" asChild>
<Tooltip title={`${localeService.t('zoom-slider.resetTo')} ${resetPoint}%`} placement="top" asChild>
<a
key="reset-button"
className={`
univer-cursor-pointer univer-absolute univer-left-1/2 univer-top-1/2
-univer-translate-x-1/2 -univer-translate-y-1/2 univer-box-border univer-w-0.5
univer-h-0.5 univer-bg-white univer-rounded-full univer-block
univer-absolute univer-left-1/2 univer-top-1/2 univer-box-border univer-block univer-h-0.5
univer-w-0.5 -univer-translate-x-1/2 -univer-translate-y-1/2 univer-cursor-pointer
univer-rounded-full univer-bg-white
`}
role="button"
onClick={handleReset}
@@ -187,44 +190,32 @@ export function Slider(props: ISliderProps) {
<IncreaseSingle />
</Button>
<DropdownProvider>
<DropdownTrigger>
<a
className={`
univer-text-gray-800 univer-h-7 univer-text-sm univer-flex univer-items-center
univer-cursor-pointer univer-rounded univer-transition-all univer-w-[55px]
univer-justify-center
hover:univer-bg-gray-100
`}
>
{value}
%
</a>
</DropdownTrigger>
<DropdownOverlay offset={{ y: -20 }}>
<Dropdown
align="end"
overlay={(
<div
className={`
univer-gap-1 univer-items-center univer-w-32 univer-p-2 univer-text-sm univer-text-gray-800
univer-box-border univer-grid
univer-box-border univer-grid univer-w-32 univer-items-center univer-gap-1 univer-p-2
univer-text-sm univer-text-gray-800
`}
>
{shortcuts?.map((item) => (
<a
key={item}
className={clsx(`
univer-cursor-pointer univer-rounded-md univer-transition-colors univer-duration-200
univer-px-2 univer-py-1 univer-relative univer-text-center
univer-relative univer-cursor-pointer univer-rounded-md univer-px-2 univer-py-1
univer-text-center univer-transition-colors univer-duration-200
hover:univer-bg-gray-100
`, {
'univer-bg-gray-100': item === value,
})}
onClick={() => onChange && onChange(item)}
onClick={() => handleSelectZoomLevel(item)}
>
{item === value && (
<span
className={`
univer-text-green-500 univer-absolute univer-left-2 univer-top-0 univer-h-full
univer-items-center univer-flex
univer-absolute univer-left-2 univer-top-0 univer-flex univer-h-full
univer-items-center univer-text-green-500
`}
>
<CheckMarkSingle />
@@ -237,8 +228,21 @@ export function Slider(props: ISliderProps) {
</a>
))}
</div>
</DropdownOverlay>
</DropdownProvider>
)}
open={zoomListVisible}
onOpenChange={setZoomListVisible}
>
<a
className={`
univer-flex univer-h-7 univer-w-[55px] univer-cursor-pointer univer-items-center
univer-justify-center univer-rounded univer-text-sm univer-text-gray-800 univer-transition-all
hover:univer-bg-gray-100
`}
>
{value}
%
</a>
</Dropdown>
</div>
);
}

Some files were not shown because too many files have changed in this diff Show More