refactor(uni): refactor uni mode with tailwindcss (#5075)

This commit is contained in:
Wenzhao Hu
2025-04-25 11:08:07 +08:00
committed by GitHub
parent dc61cd9a9c
commit 732cd6d2cf
25 changed files with 284 additions and 1321 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
## Introduction
> TODO: Introduction
This package provides a UI controller in replacement of the `@univerjs/docs-ui` package.
## Usage
@@ -16,7 +16,20 @@
import { ICommandService, IConfigService, Inject, Injector, IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
import { DOCS_IMAGE_MENU_ID } from '@univerjs/docs-drawing-ui';
import { BulletListCommand, DocCreateTableOperation, DocUIController, OrderListCommand, SetInlineFormatBoldCommand, SetInlineFormatFontFamilyCommand, SetInlineFormatFontSizeCommand, SetInlineFormatItalicCommand, SetInlineFormatStrikethroughCommand, SetInlineFormatTextBackgroundColorCommand, SetInlineFormatTextColorCommand, SetInlineFormatUnderlineCommand } from '@univerjs/docs-ui';
import {
BulletListCommand,
DocCreateTableOperation,
DocUIController,
OrderListCommand,
SetInlineFormatBoldCommand,
SetInlineFormatFontFamilyCommand,
SetInlineFormatFontSizeCommand,
SetInlineFormatItalicCommand,
SetInlineFormatStrikethroughCommand,
SetInlineFormatTextBackgroundColorCommand,
SetInlineFormatTextColorCommand,
SetInlineFormatUnderlineCommand,
} from '@univerjs/docs-ui';
import { ComponentManager, ILayoutService, IMenuManagerService, IShortcutService, IUIPartsService } from '@univerjs/ui';
import { BuiltinUniToolbarItemId, generateCloneMutation, UniToolbarService } from '@univerjs/uniui';
import { DOC_BOLD_MUTATION_ID, DOC_ITALIC_MUTATION_ID, DOC_STRIKE_MUTATION_ID, DOC_TABLE_MUTATION_ID, DOC_UNDERLINE_MUTATION_ID } from './menu';
@@ -46,6 +59,7 @@ export class UniDocsUIController extends DocUIController {
shortcutService,
configService
);
this._initUniMenus();
this._initMutations();
}
@@ -1,2 +0,0 @@
@tailwind components;
@tailwind utilities;
@@ -14,6 +14,4 @@
* limitations under the License.
*/
import './global.css';
export { UniDocsUIController } from './controllers/uni-docs-ui.controller';
@@ -16,16 +16,24 @@
import type { IDocumentData, Nullable } from '@univerjs/core';
import type { IUniFormulaPopupInfo } from '../../services/formula-popup.service';
import { BooleanNumber, createInternalEditorID, DEFAULT_EMPTY_DOCUMENT_VALUE, DocumentFlavor, HorizontalAlign, ICommandService, LocaleService, VerticalAlign, WrapStrategy } from '@univerjs/core';
import { clsx } from '@univerjs/design';
// import { TextEditor } from '@univerjs/docs-ui';
import {
BooleanNumber,
createInternalEditorID,
DEFAULT_EMPTY_DOCUMENT_VALUE,
DocumentFlavor,
HorizontalAlign,
ICommandService,
LocaleService,
VerticalAlign,
WrapStrategy,
} from '@univerjs/core';
import { CheckMarkSingle, CloseSingle } from '@univerjs/icons';
import { useDependency, useObservable } from '@univerjs/ui';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { CloseFormulaPopupOperation, ConfirmFormulaPopupCommand } from '../../commands/operations/operation';
import { DOC_FORMULA_POPUP_KEY, UniFormulaPopupService } from '../../services/formula-popup.service';
import styles from './index.module.less';
export const UNI_FORMULA_EDITOR_ID = createInternalEditorID('UNI_FORMULA');
@@ -122,7 +130,17 @@ function DocFormula(props: { popupInfo: IUniFormulaPopupInfo }) {
}, [handleEscKey]);
return (
<div className={styles.docUiFormulaPopup} onMouseEnter={() => onHovered(true)} onMouseLeave={() => onHovered(false)}>
<div
className={`
univer-box-border univer-flex univer-h-12 univer-w-[482px] univer-items-center univer-gap-x-2
univer-overflow-hidden univer-rounded-lg univer-border univer-border-solid univer-border-gray-200
univer-bg-white univer-p-2 univer-shadow-lg
dark:univer-bg-gray-900
`}
onMouseEnter={() => onHovered(true)}
onMouseLeave={() => onHovered(false)}
>
{/* TODO@wzhudev: fix DocFormulaFloat */}
{/* <TextEditor
id={UNI_FORMULA_EDITOR_ID}
className={clsx(styles.docUiFormulaPopupEditor, focused && styles.docUiFormulaPopupEditorActivated)}
@@ -139,16 +157,15 @@ function DocFormula(props: { popupInfo: IUniFormulaPopupInfo }) {
}}
onBlur={() => setFocused(false)}
/> */}
<div className={clsx(styles.formulaIcon, { [styles.formulaIconDisable]: !formulaString })}>
<div className="univer-flex">
<span
className={clsx(styles.iconContainer, styles.iconContainerError)}
className="univer-flex univer-items-center univer-gap-2 univer-rounded-lg univer-p-1"
onClick={onCancel}
>
<CloseSingle />
</span>
<span
className={clsx(styles.iconContainer, styles.iconContainerSuccess)}
className="univer-flex univer-items-center univer-gap-2 univer-rounded-lg univer-p-1"
onClick={onConfirm}
>
<CheckMarkSingle />
@@ -1,67 +0,0 @@
.doc-ui-formula-popup {
overflow: hidden;
width: 482px;
display: flex;
border-radius: 10px;
border: 1px solid var(--, #ecedef);
background: #fff;
height: 48px;
padding: 8px;
align-items: center;
box-sizing: border-box;
gap: 8px;
box-shadow: 0px 2px 4px 0px rgba(30, 34, 43, 0.06);
&-title {
font-size: var(--font-size-xs);
}
&-editor {
position: relative;
width: 100%;
height: 100%;
color: var(---Hyacinth-Blue-500, #274fee);
font-size: 13px;
font-style: normal;
font-weight: 500;
line-height: 20px; /* 153.846% */
user-select: none;
transition: all 0.15s;
border-radius: var(--radius-m, 6px);
border: 1px solid var(---Hyacinth-Blue-500, #274fee);
background: #f0f3fe;
box-shadow: 0px 0px 4px 0px rgba(39, 79, 238, 0.4);
// &-activated {
// border-color: rgb(var(--primary-color));
// }
}
&-button-grp {
margin-top: var(--margin-xs);
text-align: right;
text-wrap: nowrap;
button:not(:last-of-type) {
margin-right: var(--margin-xxs);
}
}
.formula-icon {
display: flex;
}
.icon-container {
display: flex;
padding: var(--radius-m, 6px);
align-items: center;
gap: 10px;
border-radius: 8px;
&:hover {
background: var(--hyacinth-blue-5007, rgba(39, 79, 238, 0.07));
color: #274fee;
}
}
}
@@ -15,7 +15,7 @@
*/
export { UniverDocUniFormulaPlugin } from './uni-formula.plugin';
export { IUniFormulaService, DumbUniFormulaService } from './services/uni-formula.service';
export { DumbUniFormulaService, IUniFormulaService } from './services/uni-formula.service';
export { UNI_FORMULA_PLUGIN_NAME as DOC_FORMULA_PLUGIN_NAME } from './const';
export type { IDocFormulaCache, IDocFormulaData, IDocFormulaReference } from './models/doc-formula';
export type { ISlideFormulaCache, ISlideFormulaData, ISlideFormulaReference } from './models/slide-formula';
@@ -23,10 +23,10 @@ export type { ISlideFormulaCache, ISlideFormulaData, ISlideFormulaReference } fr
// #region - all commands
export {
AddDocUniFormulaMutation,
type IAddDocUniFormulaMutationParams,
type IRemoveDocUniFormulaMutationParams,
type IUpdateDocUniFormulaMutationParams,
AddDocUniFormulaMutation,
RemoveDocUniFormulaMutation,
UpdateDocUniFormulaMutation,
} from './commands/mutations/doc-formula.mutation';
@@ -35,21 +35,17 @@ export interface IUniFormulaService {
updateSlideFormulaResults(unitId: string, pageId: string, elementId: string, formulaId: string, v: IDocFormulaCache): boolean;
// #region doc
hasDocFormula(unitId: string, formulaId: string): boolean;
getDocFormula(unitId: string, rangeId: string): Nullable<IDocFormulaReference>;
registerDocFormula(unitId: string, rangeId: string, f: string, v: ICellData['v'], t: ICellData['t']): IDisposable;
unregisterDocFormula(unitId: string, rangeId: string): void;
// #endregion
// #region slide
hasSlideFormula(unitId: string, pageId: string, elementId: string, formulaId: string): boolean;
getSlideFormula(unitId: string, pageId: string, elementId: string, formulaId: string): Nullable<ISlideFormulaReference>;
registerSlideFormula(unitId: string, pageId: string, elementId: string, f: string, v: ICellData['v'], t: ICellData['t']): IDisposable;
unregisterSlideFormula(unitId: string, pageId: string, elementId: string, formulaId: string): void;
// #endregion
}
@@ -15,10 +15,10 @@
*/
import type { Dependency } from '@univerjs/core';
import { Inject, Injector, Plugin, UniverInstanceType } from '@univerjs/core';
import { DumbUniFormulaService, IUniFormulaService } from './services/uni-formula.service';
import { Inject, Injector, Plugin, registerDependencies, touchDependencies, UniverInstanceType } from '@univerjs/core';
import { UNI_FORMULA_PLUGIN_NAME } from './const';
import { UniFormulaController } from './controller/uni-formula.controller';
import { DumbUniFormulaService, IUniFormulaService } from './services/uni-formula.service';
export class UniverDocUniFormulaPlugin extends Plugin {
static override pluginName: string = UNI_FORMULA_PLUGIN_NAME;
@@ -34,12 +34,12 @@ export class UniverDocUniFormulaPlugin extends Plugin {
}
override onStarting(): void {
this._injector.add([UniFormulaController]);
this._injector.get(UniFormulaController);
const dependencies: Dependency[] = [[UniFormulaController]];
if (this._config?.playDumb) {
this._injector.add([IUniFormulaService, { useClass: DumbUniFormulaService }] as Dependency);
this._injector.get(IUniFormulaService);
dependencies.push([IUniFormulaService, { useClass: DumbUniFormulaService }]);
}
registerDependencies(this._injector, dependencies);
touchDependencies(this._injector, dependencies.map((d) => [d[0]]));
}
}
@@ -56,7 +56,6 @@ export class UniSheetsUIController extends SheetUIController {
const uiController = this._uiPartsService;
const injector = this._injector;
// this.disposeWithMe(uiController.registerComponent(BuiltInUIPart.HEADER, () => connectInjector(RenderSheetHeader, injector)));
this.disposeWithMe(uiController.registerComponent(UniUIPart.OUTLINE, () => connectInjector(RenderOutline, injector)));
this.disposeWithMe(uiController.registerComponent(BuiltInUIPart.CONTENT, () => connectInjector(RenderSheetContent, injector)));
}
@@ -91,6 +90,7 @@ function RenderOutline() {
const univerInstanceService = useDependency(IUniverInstanceService);
const focused = useObservable(univerInstanceService.focused$);
const workbook = useObservable(() => univerInstanceService.getCurrentTypeOfUnit$<Workbook>(UniverInstanceType.UNIVER_SHEET), null, false, []);
if (!workbook || focused !== workbook?.getUnitId()) return null;
return (
@@ -23,7 +23,6 @@ import { InsertSheetCommand, InsertSheetMutation, RemoveSheetMutation, SetTabCol
import { useActiveWorkbook } from '@univerjs/sheets-ui';
import { useDependency } from '@univerjs/ui';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import styles from './index.module.less';
export function UniSheetBar() {
const [sheetList, setSheetList] = useState<IBaseSheetBarProps[]>([]);
@@ -102,15 +101,15 @@ export function UniSheetBar() {
});
return (
<div className={styles.uniSheetBar} ref={sheetBarRef}>
<div className={styles.sheetTab} style={{ height: `${barHeight}px` }}>
<div className={styles.sheetItems}>
<div className="univer-flex univer-h-full univer-select-none univer-flex-col univer-justify-between" ref={sheetBarRef}>
<div className="univer-overflow-y-auto univer-scrollbar-thin" style={{ height: `${barHeight}px` }}>
<div className="univer-flex univer-flex-col univer-gap-1 univer-text-sm univer-font-medium">
{sheetList.map((item, index) => (
<div
key={index}
className={`
${styles.sheetItem}
${activeKey === item.sheetId ? styles.active : ''}
"univer-flex univer-h-8 univer-items-center univer-px-1 univer-py-0 univer-rounded-lg"
${activeKey === item.sheetId ? 'univer-text-teal-500' : ''}
`}
onClick={() => handleSheetActiveChange(item.sheetId)}
>
@@ -126,8 +125,19 @@ export function UniSheetBar() {
</div>
</div>
<button className={styles.newSheetButton} onClick={handleSheetAdd}>
<IncreaseSingle className={styles.newSheetButtonIcon} />
<button
type="button"
className={`
univer-relative univer-mt-4 univer-flex univer-h-8 univer-items-center univer-justify-center
univer-rounded-lg univer-border-0 univer-bg-transparent univer-text-primary-600
before:absolute before:left-0 before:top-0 before:h-full before:w-full before:bg-gray-100
before:content-['']
hover:univer-cursor-pointer hover:univer-bg-gray-100
`}
onClick={handleSheetAdd}
>
<IncreaseSingle className="univer-mr-1 univer-size-4" />
<span>New sheet</span>
</button>
</div>
@@ -1,80 +0,0 @@
.uni-sheet-bar {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
user-select: none;
.sheet-tab {
overflow-y: auto;
scrollbar-width: thin;
.sheet-items {
display: flex;
flex-direction: column;
gap: var(--margin-xxs);
font-size: var(--font-size-xs);
font-weight: 500;
.sheet-item {
display: flex;
align-items: center;
height: 32px;
padding: 0 var(--margin-xs);
border-radius: var(--border-radius-lg);
> span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 142px;
}
&:hover {
background-color: rgb(var(--grey-100));
cursor: pointer;
}
&.active {
color: rgba(var(--hyacinth-500));
background-color: rgb(var(--hyacinth-500), 0.07);
}
}
}
}
.new-sheet-button {
position: relative;
height: 32px;
margin-top: 16px;
padding: 0 var(--margin-xs);
border-radius: var(--border-radius-lg);
color: rgba(var(--hyacinth-500));
background-color: transparent;
border: 0;
display: flex;
align-items: center;
justify-content: center;
&-icon {
width: 16px;
height: 16px;
margin-right: 5px;
}
&:hover {
background-color: rgb(var(--grey-100));
cursor: pointer;
}
&::before {
content: '';
position: absolute;
left: 0;
right: 0;
top: -10px;
height: 1px;
background-color: #ecedef;
}
}
}
@@ -23,8 +23,6 @@ import { ActivateSlidePageOperation, AppendSlideOperation, SetSlidePageThumbOper
import { useDependency, useObservable } from '@univerjs/ui';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import styles from './index.module.less';
/**
* This components works as the root component of the left Sidebar of Slide.
*/
@@ -66,7 +64,6 @@ export function UniSlideSideBar() {
return () => {
subscriber?.unsubscribe();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
@@ -100,23 +97,45 @@ export function UniSlideSideBar() {
}, [commandService, currentSlide]);
return (
<div className={styles.uniSlideBar} ref={slideBarRef}>
<div className={styles.uniSlideBarContent} style={{ height: `${barHeight}px` }}>
<div className="univer-flex univer-h-full univer-select-none univer-flex-col" ref={slideBarRef}>
<div className="univer-overflow-y-auto univer-scrollbar-thin" style={{ height: `${barHeight}px` }}>
{slideList.map((item, index) => (
<div
key={item.id}
className={clsx(styles.uniSlideBarItem, {
[styles.uniSlideBarItemActive]: item.id === activatePageId,
})}
className={clsx(`
univer-relative univer-my-4 univer-box-border univer-flex univer-w-[160px]
univer-overflow-hidden univer-rounded-lg univer-border univer-border-solid
univer-border-gray-200
hover:univer-border-primary-500
`, item.id === activatePageId && 'univer-border-primary-500')}
onClick={() => activatePage(item.id)}
>
<span>{index + 1}</span>
<div ref={divRefs[index]} className={styles.uniSlideBarBox} />
<span
className={`
univer-absolute univer-bottom-1 univer-left-1 univer-z-10 univer-inline-flex univer-size-5
univer-items-center univer-justify-center univer-overflow-hidden univer-rounded
univer-text-xs univer-text-white
`}
>
{index + 1}
</span>
<div ref={divRefs[index]} className="univer-relative univer-h-[90px] univer-w-[160px]" />
</div>
))}
</div>
<button type="button" className={styles.newSlideButton} onClick={handleAppendSlide}>
<IncreaseSingle className={styles.newSlideButtonIcon} />
<button
type="button"
className={`
univer-relative univer-mt-4 univer-flex univer-h-8 univer-cursor-pointer univer-items-center
univer-justify-center univer-rounded-lg univer-border-0 univer-bg-transparent univer-px-1
univer-text-primary-500
before:absolute before:left-0 before:top-0 before:h-[1px] before:content-['']
before:univer-bg-gray-200
hover:univer-bg-gray-100
`}
onClick={handleAppendSlide}
>
<IncreaseSingle className="univer-mr-1 univer-size-4" />
<span>{localeService.t('slide.append')}</span>
</button>
</div>
@@ -1,112 +0,0 @@
.uni-slide-bar {
display: flex;
flex-direction: column;
height: 100%;
user-select: none;
&-content {
overflow-y: auto;
scrollbar-width: thin;
.uni-slide-bar-item {
box-sizing: border-box;
position: relative;
display: flex;
overflow: hidden;
margin: 15px 0;
width: 160px;
border-radius: 8px;
border: 1px solid #ecedef;
span {
position: absolute;
width: 15px;
font-size: 12px;
left: 6px;
bottom: 6px;
width: 20px;
height: 20px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 4px;
overflow: hidden;
background: rgba(20, 34, 43, 0.4);
z-index: 10;
color: white;
}
.uni-slide-bar-box {
position: relative;
width: 160px;
height: 90px;
}
&-active {
border-color: rgb(var(--blue-500));
}
&:hover {
border-color: rgb(var(--blue-500));
}
}
&-header {
padding-top: var(--padding-lg);
display: flex;
justify-content: center;
a {
width: 212px;
padding: var(--padding-xs) 0;
font-size: var(--font-size-sm);
background: rgb(var(--bg-color-secondary));
text-align: center;
border: 1px solid rgb(var(--border-color));
border-radius: var(--border-radius-base);
display: block;
transition: all 0.25s;
cursor: pointer;
&:hover {
background: rgb(var(--bg-color-hover));
}
}
}
}
.new-slide-button {
position: relative;
height: 32px;
margin-top: 16px;
padding: 0 var(--margin-xs);
border-radius: var(--border-radius-lg);
color: rgba(var(--hyacinth-500));
background-color: transparent;
border: 0;
display: flex;
align-items: center;
justify-content: center;
&-icon {
width: 16px;
height: 16px;
margin-right: 5px;
}
&:hover {
background-color: rgb(var(--grey-100));
cursor: pointer;
}
&::before {
content: '';
position: absolute;
left: 0;
right: 0;
top: -10px;
height: 1px;
background-color: #ecedef;
}
}
}
@@ -1,2 +1,8 @@
@tailwind components;
@tailwind utilities;
@layer utilities {
.scrollbar-gutter-auto {
scrollbar-gutter: auto;
}
}
@@ -22,13 +22,20 @@ 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';
import styles from './index.module.less';
export const UniControlButton = (props: { tooltips: string; children?: React.ReactElement; onClick: () => void; style?: React.CSSProperties }) => {
const { children, onClick, style, tooltips } = props;
return (
<Tooltip title={tooltips}>
<div className={styles.uniControlButton} onClick={onClick} style={style}>
<div
className={`
univer-box-border univer-flex univer-h-8 univer-w-8 univer-items-center univer-justify-center
univer-rounded-lg univer-p-1.5
hover:univer-bg-gray-800
`}
onClick={onClick}
style={style}
>
{children}
</div>
</Tooltip>
@@ -47,6 +54,8 @@ export enum UniControlItem {
AI = 'AI',
}
// The component on the bottom right corner of the screen, providing zoom in, zoom out, fit view,
// and AI button and other shared functionalities.
export const UniControls = ({ zoom, onItemClick }: { zoom: number; onItemClick?: (key: UniControlItem) => void }) => {
const unitGridService = useDependency(IUnitGridService);
const { zoomIn, zoomOut, fitView, getNodes, setCenter, getZoom } = useReactFlow();
@@ -117,7 +126,14 @@ export const UniControls = ({ zoom, onItemClick }: { zoom: number; onItemClick?:
}, [fitView]);
return (
<div className={styles.uniControls} style={{ right: `${rightPadding}px` }}>
<div
className={`
univer-fixed univer-bottom-3 univer-right-3 univer-flex univer-w-fit univer-items-center univer-gap-3
univer-rounded-[10px] univer-border univer-border-solid univer-border-gray-200 univer-bg-white univer-p-2
univer-shadow
`}
style={{ right: `${rightPadding}px` }}
>
<UniControlButton tooltips="Full screen" onClick={onFullscreenHandler}>
<FullscreenSingle />
</UniControlButton>
@@ -126,18 +142,38 @@ export const UniControls = ({ zoom, onItemClick }: { zoom: number; onItemClick?:
</UniControlButton>
<Dropdown
overlay={(
<div className={clsx(styles.sliderShortcuts, 'univer-rounded-lg univer-p-4')}>
<div
className={clsx(
`
univer-box-border univer-grid univer-w-32 univer-items-center univer-gap-1
univer-rounded-lg univer-border univer-border-gray-300 univer-bg-white univer-p-4
univer-text-xs univer-shadow-lg
`
)}
>
{shortcuts?.map((item) => (
<a
key={item}
className={clsx(
styles.sliderShortcut,
item === zoomPercent ? styles.sliderShortcutActive : ''
`
univer-relative univer-box-border univer-cursor-pointer univer-py-1 univer-pl-9
univer-text-gray-900
`,
`
univer-rounded univer-no-underline univer-transition-colors univer-duration-200
hover:univer-bg-gray-100
`,
item === zoomPercent ? 'univer-bg-gray-100' : ''
)}
onClick={() => onZoomMenuChange(item)}
>
{item === zoomPercent && (
<span className={styles.sliderShortcutIcon}>
<span
className={`
univer-absolute univer-left-1 univer-top-0 univer-flex univer-h-full
univer-items-center univer-text-green-600
`}
>
<CheckMarkSingle />
</span>
)}
@@ -149,7 +185,17 @@ export const UniControls = ({ zoom, onItemClick }: { zoom: number; onItemClick?:
</div>
)}
>
<a className={styles.sliderValue}>
<a
className={clsx(
`
univer-h-[28px] univer-w-[55px] univer-cursor-pointer univer-rounded univer-text-center
univer-text-xs univer-leading-loose univer-text-gray-700 univer-no-underline
univer-transition-all univer-duration-200
group-data-[open=true]:univer-bg-gray-200
hover:univer-bg-gray-200
`
)}
>
{zoomPercent}
%
</a>
@@ -1,234 +0,0 @@
.uni-controls {
display: flex;
padding: 8px;
align-items: center;
gap: 12px;
border-radius: 10px;
border: 1px solid var(--, #ecedef);
background: #fff;
box-shadow: 0px 2px 4px 0px rgba(30, 34, 43, 0.06);
width: fit-content;
position: fixed;
right: 12px;
bottom: 12px;
}
.uni-control-button {
display: flex;
padding: var(--radius-m, 6px);
align-items: center;
justify-content: center;
border-radius: 8px;
box-sizing: border-box;
width: 32px;
height: 32px;
&:hover {
background: rgba(30, 34, 43, 0.05);
}
}
@slider-bg: rgb(var(--text-color-secondary));
@slider-width: 116px;
@slider-height: 2px;
@slider-radius: 15px;
@slider-handle-size: 12px;
@slider-value-width: 55px;
@slider-value-height: 28px;
@slider-parts-hover-color: rgb(var(--border-color));
:export {
--slider-width: unit(@slider-width);
--slider-handle-size: unit(@slider-handle-size);
}
.slider {
user-select: none;
display: flex;
gap: var(--margin-xxs);
align-items: center;
&-disabled {
cursor: not-allowed;
.slider {
&-rail {
background-color: rgb(var(--grey-300));
}
&-handle {
background-color: rgb(var(--grey-300));
box-shadow: none;
&:hover {
cursor: not-allowed;
background-color: rgb(var(--grey-300));
}
}
&-reset-point {
cursor: not-allowed;
}
&-value {
&:hover {
cursor: not-allowed;
background-color: rgb(var(--grey-300));
}
}
&-value {
pointer-events: none;
color: rgb(var(--grey-300));
&:hover {
background-color: transparent;
}
}
}
}
&-rail {
@padding: (@slider-handle-size / 2);
position: relative;
width: @slider-width;
height: @slider-height;
padding: 0 @padding;
background-color: @slider-bg;
border-radius: @slider-radius;
}
&-inner-rail {
position: relative;
height: @slider-height;
}
&-handle {
cursor: pointer;
position: absolute;
top: calc(50% - @slider-handle-size / 2);
transform: translateX(-50%);
width: @slider-handle-size;
height: @slider-handle-size;
background-color: rgb(var(--color-white));
border-radius: 50%;
box-shadow: 0 0 6px 0 rgb(0 0 0 / 10%);
transition: background 0.2s;
&:hover {
background-color: @slider-parts-hover-color;
box-shadow: none;
}
}
&-reset-point {
cursor: pointer;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
box-sizing: border-box;
width: @slider-height * 2;
height: @slider-height;
background-color: rgb(var(--color-white));
border-radius: 50%;
}
&-value {
cursor: pointer;
width: @slider-value-width;
height: @slider-value-height;
font-size: var(--font-size-xs);
line-height: 2;
color: rgb(var(--text-color-secondary-darker));
text-align: center;
border-radius: var(--border-radius-base);
transition: all 0.2s;
text-decoration: none;
&:hover {
background-color: @slider-parts-hover-color;
text-decoration: inherit;
color: inherit;
}
&.dropdown-open {
background-color: @slider-parts-hover-color;
}
}
&-shortcuts {
transform: translateX(-(@slider-value-width / 2));
display: grid;
gap: var(--margin-xxs);
align-items: center;
box-sizing: border-box;
width: 128px;
padding: var(--padding-sm);
font-size: var(--font-size-xs);
background-color: rgb(var(--color-white));
border: 1px solid rgb(var(--border-color));
border-radius: var(--border-radius-lg);
box-shadow: var(--box-shadow-lg);
}
&-shortcut {
cursor: pointer;
position: relative;
box-sizing: border-box;
padding: var(--padding-xs) 0 var(--padding-xs) 36px;
color: rgb(var(--text-color));
border-radius: var(--border-radius-base);
transition: background 0.2s;
text-decoration: none;
&:hover {
background-color: rgb(var(--bg-color-hover));
color: inherit;
text-decoration: inherit;
}
}
&-shortcut-icon {
position: absolute;
top: 0;
left: var(--padding-xs);
height: 100%;
color: rgb(var(--success-color));
display: flex;
align-items: center;
}
&-shortcut-active {
background-color: rgb(var(--bg-color-hover));
}
}
@@ -19,7 +19,6 @@ import { clsx } from '@univerjs/design';
import { CloseSingle } from '@univerjs/icons';
import { CustomLabel, ILeftSidebarService, ISidebarService, useDependency, useObservable } from '@univerjs/ui';
import React, { useEffect, useMemo, useRef } from 'react';
import styles from './index.module.less';
export interface IUniSidebarProps {
position: 'left' | 'right';
@@ -82,14 +81,8 @@ export function UniSidebar(props: IUniSidebarProps) {
};
}, [sidebarService]);
const rootClassName = clsx(styles.uniSidebar, {
[styles.uniSidebarOpen]: options?.visible,
[styles.uniSidebarLeft]: position === 'left',
[styles.uniSidebarRight]: position === 'right',
});
const width = useMemo(() => {
if (!options?.visible) return 0;
if (!options?.visible) return '0px';
if (typeof options.width === 'number') {
return `${options.width}px`;
@@ -108,21 +101,51 @@ export function UniSidebar(props: IUniSidebarProps) {
options?.onClose?.();
}
return (
<aside className={rootClassName} style={{ width }}>
<section className={styles.uniSidebarContainer} ref={scrollRef}>
<aside
className={clsx(`
univer-pointer-events-auto univer-fixed univer-bottom-3 univer-z-20 univer-box-border univer-translate-x-0
univer-overflow-hidden univer-rounded-lg univer-border univer-border-solid univer-border-gray-200
univer-shadow-lg univer-transition-all
`, position === 'left' && 'univer-left-3 univer-top-12 univer-w-[180px]', position === 'left' && options?.visible && `
univer-translate-x-0
`, position === 'left' && options?.visible === false && '-univer-translate-x-4', position === 'right' && `
univer-min-w-[280px] univer-max-w[400px] univer-right-3 univer-translate-x-4
`, options?.visible && 'univer-translate-x-0')}
style={{ width }}
>
<section
className={`
univer-m-auto univer-box-border univer-grid univer-h-0 univer-min-h-full univer-w-full
univer-overflow-hidden univer-overflow-y-auto univer-grid-rows-auto univer-bg-white
univer-scrollbar-thin univer-scrollbar-gutter-auto univer-scrollbar-track-slate-700
`}
ref={scrollRef}
>
{ showClose && (
<header className={styles.uniSidebarHeader}>
<header
className={`
univer-sticky univer-top-0 univer-z-10 univer-box-border univer-flex univer-h-[44px]
univer-content-between univer-items-center univer-bg-black univer-p-4 univer-pb-0
univer-text-lg univer-font-medium
`}
>
{options?.header}
<a className={styles.uniSidebarHeaderClose} onClick={handleClose}>
<a className="univer-cursor-pointer" onClick={handleClose}>
<CloseSingle />
</a>
</header>
)}
<section className={styles.uniSidebarBody}>{options?.children}</section>
{options?.footer && <footer className={styles.uniSidebarFooter}>{options.footer}</footer>}
<section
className={`
univer-box-border univer-p-2
*:univer-h-full
`}
>
{options?.children}
</section>
{options?.footer && <footer className="univer-sticky univer-bottom-0 univer-box-border univer-p-4">{options.footer}</footer>}
</section>
</aside>
);
@@ -1,95 +0,0 @@
.uni-sidebar {
position: fixed;
border: 1px solid rgb(var(--border-color));
border-radius: 10px;
overflow: hidden;
box-sizing: border-box;
box-shadow: var(--box-shadow-lg);
top: 72px;
bottom: 12px;
z-index: 20;
background-color: rgb(var(--bg-color-secondary));
color: rgb(var(--text-color));
transition: all 0.15s;
&-left {
left: 12px;
transform: translateX(calc(-100% - 12px));
width: 180px;
}
&-right {
min-width: 280px;
max-width: 400px;
right: 12px;
transform: translateX(calc(100% + 12px));
}
&&-open {
transform: translateX(0);
}
&-container {
overflow-y: auto;
display: grid;
grid-template-rows: auto;
// grid-template-rows: auto 1fr auto;
box-sizing: border-box;
width: 100%;
height: 0;
min-height: 100%;
margin: auto;
overflow: hidden;
background-color: rgb(var(--bg-color-secondary));
scrollbar-color: rgba(var(--scrollbar-color), 0.7) transparent;
scrollbar-gutter: auto;
scrollbar-width: thin;
}
&-header {
position: sticky;
top: 0;
display: flex;
align-items: center;
justify-content: space-between;
height: 44px;
padding: var(--padding-xl) var(--padding-xl) 0;
font-size: var(--font-size-lg);
font-weight: 500;
background: rgb(var(--bg-color-secondary));
box-sizing: border-box;
z-index: 10;
&-close {
cursor: pointer;
color: rgb(var(--text-color-secondary));
}
}
&-body {
padding: var(--padding-sm);
box-sizing: border-box;
& > div {
height: 100%;
}
}
&-footer {
position: sticky;
bottom: 0;
padding: var(--padding-xl);
background: rgb(var(--bg-color-secondary));
box-sizing: border-box;
}
}
@@ -22,7 +22,6 @@ import { IUniverInstanceService } from '@univerjs/core';
import { ComponentContainer, IMenuManagerService, ToolbarItem, useComponentsOfPart, useDependency, useObservable } from '@univerjs/ui';
import React, { useEffect, useImperativeHandle, useState } from 'react';
import { DELETE_MENU_ID, DOWNLOAD_MENU_ID, LOCK_MENU_ID, PRINT_MENU_ID, SHARE_MENU_ID, UNI_MENU_POSITIONS, ZEN_MENU_ID } from '../../controllers/menu';
import styles from './index.module.less';
export interface IFloatingToolbarRef {
update: () => void;
@@ -86,18 +85,24 @@ export const UniFloatingToolbar = React.forwardRef<IFloatingToolbarRef, { node:
return (
<div
className={styles.uniFloatingToolbar}
className={`
univer-flex univer-items-center univer-gap-2 univer-rounded-md univer-bg-white univer-text-sm
univer-text-primary-600
dark:univer-bg-gray-900
`}
ref={setFloating}
style={{ position: strategy, top: y ?? 0, left: x ?? 0 }}
>
<div className={styles.uniToolbar}>
<div
className="univer-inline-flex univer-flex-shrink-0 univer-items-center univer-gap-2 univer-p-2"
>
<ComponentContainer
key="name"
components={toolbarNameComponents}
fallback={<UnitName unitId={node.unitId} />}
/>
<UniDiv />
<div className={styles.toolbarGroup}>
<div className="univer-flex">
{uniVisibleItems.map((subItem) => <ToolbarItem key={subItem.id} {...subItem} />)}
</div>
</div>
@@ -106,7 +111,7 @@ export const UniFloatingToolbar = React.forwardRef<IFloatingToolbarRef, { node:
});
export function UniDiv() {
return <div className={styles.uniDiv} />;
return <div className="univer-h-6 univer-w-[1px] univer-bg-gray-100" />;
}
export function UnitName({ unitId }: { unitId: string }) {
@@ -114,7 +119,12 @@ export function UnitName({ unitId }: { unitId: string }) {
const unit = instanceService.getUnit(unitId);
const name = useObservable(unit?.name$);
return (
<div className={styles.unitName}>
<div
className={`
univer-flex univer-h-6 univer-items-center univer-gap-1 univer-rounded-md univer-bg-white univer-px-1
univer-py-0 univer-text-sm univer-text-gray-900
`}
>
{name}
</div>
);
@@ -17,16 +17,13 @@
import type { Nullable, Workbook } from '@univerjs/core';
import { BooleanNumber, DEFAULT_EMPTY_DOCUMENT_VALUE, DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY, DocumentFlavor, HorizontalAlign, ICommandService, IPermissionService, IUniverInstanceService, Rectangle, ThemeService, UniverInstanceType, VerticalAlign, WrapStrategy } from '@univerjs/core';
import { clsx } from '@univerjs/design';
// import { TextEditor } from '@univerjs/docs-ui';
import { DeviceInputEventType } from '@univerjs/engine-render';
import { CheckMarkSingle, CloseSingle, FxSingle } from '@univerjs/icons';
import { RangeProtectionPermissionEditPoint, RangeProtectionRuleModel, SheetsSelectionsService, WorkbookEditablePermission, WorksheetEditPermission, WorksheetProtectionRuleModel, WorksheetSetCellValuePermission } from '@univerjs/sheets';
import { IEditorBridgeService, IFormulaEditorManagerService, SetCellEditVisibleOperation, useActiveWorkbook } from '@univerjs/sheets-ui';
// import { TextEditor } from '@univerjs/docs-ui';
import { KeyCode, useDependency, useObservable } from '@univerjs/ui';
import React, { useCallback, useLayoutEffect, useState } from 'react';
import { EMPTY, merge, switchMap } from 'rxjs';
import styles from './index.module.less';
export const UniFormulaBar = () => {
const editorBridgeService = useDependency(IEditorBridgeService);
@@ -50,19 +47,24 @@ export const UniFormulaBar = () => {
return (
<>
<div
className={clsx(styles.uniFormulaBar, {
[styles.uniFormulaBarDisable]: !focusedId,
className={clsx(`
univer-flex univer-items-center univer-gap-2 univer-rounded-md univer-px-3 univer-py-1.5
univer-text-sm univer-text-primary-600
`, {
'univer-opacity-30': !focusedId,
})}
onClick={() => handleOpenWrite()}
>
<FxSingle />
<span className={styles.uniFormulaBarText}>
<span className="univer-whitespace-nowrap univer-text-xs">
Write formula
</span>
</div>
{visible && (
<div
className={styles.uniFormulaBarFullInput}
className={`
univer-absolute univer-left-0 univer-top-0 univer-z-10 univer-h-full univer-w-full univer-bg-white
`}
>
<FormulaBar />
</div>
@@ -71,6 +73,8 @@ export const UniFormulaBar = () => {
);
};
// This copies the FormulaBar component from the sheets-ui package.
// FIXME@wzhudev: it is not appropriate to copy the component here.
export function FormulaBar() {
const formulaEditorManagerService = useDependency(IFormulaEditorManagerService);
const editorBridgeService = useDependency(IEditorBridgeService);
@@ -208,7 +212,11 @@ export function FormulaBar() {
}
return (
<div className={styles.uniFormulaBox}>
<div
className={`
univer-relative univer-box-border univer-flex univer-items-center unvier-gap-2 univer-h-full univer-p-2
`}
>
{/* <TextEditor
id={DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY}
isSheetEditor
@@ -219,16 +227,22 @@ export function FormulaBar() {
snapshot={INITIAL_SNAPSHOT}
isSingle
/> */}
<div className={clsx(styles.formulaIcon, { [styles.formulaIconDisable]: disable })}>
<div className={clsx('univer-py-1.5', { 'univer-cursor-not-allowed univer-text-gray-200': disable })}>
<span
className={clsx(styles.iconContainer, styles.iconContainerError)}
className={`
univer-flex univer-items-center univer-justify-center univer-rounded univer-p-1 univer-text-lg
univer-text-red-600
`}
onClick={handleCloseBtnClick}
>
<CloseSingle />
</span>
<span
className={clsx(styles.iconContainer, styles.iconContainerSuccess)}
className={`
univer-flex univer-items-center univer-justify-center univer-rounded univer-p-1 univer-text-lg
univer-text-green-600
`}
onClick={handleConfirmBtnClick}
>
<CheckMarkSingle />
@@ -22,7 +22,6 @@ import { IMenuManagerService, ToolbarItem, useDependency, useObservable } from '
import React, { useEffect, useState } from 'react';
import { UNI_MENU_POSITIONS } from '../../controllers/menu';
import { UniToolbarService } from '../../services/toolbar/uni-toolbar-service';
import styles from './index.module.less';
import { UniFormulaBar } from './UniFormulaBar';
export interface IToolbarProps {
@@ -71,9 +70,15 @@ export function UniToolbar() {
}, [isMenuChange]);
return (
<div className={styles.uniToolbar}>
<div
className={`
univer-flex univer-p-2 univer-items-center univer-gap-2 univer-rounded-lg univer-border
univer-border-gray-300 univer-overflow-hidden univer-shadow-lg univer-select-none univer-relative
univer-box-border univer-text-sm univer-bg-white
`}
>
{hasWorkbooks && <UniFormulaBar />}
<div className={styles.toolbarGroup}>
<div className="univer-flex univer-gap-1 univer-items-center univer-shrink-0">
{uniVisibleItems.map((subItem) => subItem && <ToolbarItem key={subItem.id} {...subItem} />)}
</div>
</div>
@@ -1,477 +0,0 @@
@toolbar-btn-size: 24px;
.headerbar {
user-select: none;
position: relative;
> .header-menu {
position: absolute;
top: 0;
right: 0;
display: flex;
box-sizing: border-box;
height: 100%;
> div {
user-select: none;
display: inline-flex;
align-items: center;
height: 28px;
margin-left: 4px;
padding: 2px 4px;
text-align: center;
border-radius: 4px;
&:hover {
background-color: rgb(var(--grey-300));
}
}
}
}
.menubar {
display: flex;
gap: var(--margin-xs);
align-items: center;
justify-content: center;
height: 32px;
&-item {
cursor: pointer;
display: block;
padding: var(--padding-xs) var(--padding-sm);
font-size: var(--font-size-xs);
font-weight: 400;
border-radius: var(--border-radius-base);
transition: all 0.1s ease-in-out;
&:hover:not(&-active) {
background: rgb(var(--grey-300));
}
&-active {
color: rgb(var(--color-white));
background: rgb(var(--primary-color));
}
}
}
.uni-toolbar {
display: flex;
height: 48px;
padding: 8px;
align-items: center;
gap: 8px;
border-radius: 10px;
border: 1px solid var(--, #ecedef);
overflow: hidden;
box-shadow: 0px 2px 4px 0px rgba(30, 34, 43, 0.06);
user-select: none;
position: relative;
box-sizing: border-box;
font-size: var(--font-size-base);
color: rgb(var(--text-color));
background-color: rgb(var(--bg-color-secondary));
&-container {
padding: 0 var(--padding-lg);
display: flex;
gap: var(--margin-xs);
align-items: center;
justify-content: center;
box-sizing: border-box;
height: 100%;
margin: 0 auto;
max-width: 100%;
overflow: hidden;
}
&-more {
max-width: 80%;
&-container {
display: grid;
box-sizing: border-box;
padding: var(--padding-sm);
background-color: rgb(var(--bg-color-secondary));
border-radius: var(--border-radius-base);
box-shadow: var(--box-shadow-base);
.toolbar-group {
flex-wrap: wrap;
&:not(:last-child) {
&::after {
content: initial;
}
> * {
margin-bottom: var(--margin-xs);
}
}
}
}
}
&-group {
display: flex;
gap: var(--margin-xs);
align-items: center;
flex-shrink: 0;
> * {
flex-shrink: 0;
}
&:not(:last-child, :empty) {
&::after {
content: '';
display: block;
width: 1px;
height: 20px;
background-color: rgb(var(--border-color));
flex-shrink: 0;
}
}
}
&-item {
&-select-button {
cursor: pointer;
position: relative;
overflow: hidden;
display: flex;
gap: var(--margin-xs);
align-items: center;
height: @toolbar-btn-size;
padding-right: 18px;
font-size: var(--font-size-lg);
border-radius: var(--border-radius-base);
&-disabled {
cursor: not-allowed;
color: rgb(var(--grey-200));
&.toolbar-item-select-button-activated {
color: rgba(var(--text-color), 0.25);
.toolbar-item-select-button {
&-label,
&-arrow {
&:hover {
background-color: rgb(var(--grey-100)) !important;
}
}
}
}
}
&-activated {
background-color: rgb(var(--grey-100));
}
&:hover {
background-color: rgb(var(--grey-100));
.toolbar {
&-item {
&-select-button {
&-label {
background-color: rgb(var(--grey-100));
}
}
}
}
}
&-label {
position: relative;
z-index: 1;
display: flex;
align-items: center;
height: 100%;
padding: 0 var(--padding-xs);
&:hover {
background-color: rgb(var(--grey-300)) !important;
}
}
&-arrow {
position: absolute;
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: flex-end;
box-sizing: border-box;
width: 100%;
height: 100%;
padding-right: var(--padding-xs);
color: rgb(var(--grey-400));
&:hover {
background-color: rgb(var(--grey-300));
}
}
}
&-select {
cursor: pointer;
position: relative;
display: flex;
gap: var(--margin-xs);
align-items: center;
height: @toolbar-btn-size;
padding: 0 var(--padding-xs);
font-size: var(--font-size-lg);
border-radius: var(--border-radius-base);
&-disabled {
cursor: not-allowed;
color: rgb(var(--grey-200));
&.toolbar-item-select-activated {
color: rgba(var(--text-color), 0.25);
&:hover {
background-color: rgb(var(--grey-100));
}
}
}
&-activated {
background-color: rgb(var(--grey-100));
}
&:hover {
background-color: rgb(var(--grey-100));
border-radius: var(--border-radius-base);
}
&-arrow {
display: flex;
align-items: center;
height: 100%;
color: rgb(var(--grey-400));
&-disabled {
cursor: not-allowed;
color: rgb(var(--grey-200));
&.toolbar-item-select-arrow-activated {
color: rgba(var(--text-color), 0.25);
&:hover {
background-color: rgb(var(--grey-100));
}
}
}
&-activated {
background-color: rgb(var(--grey-100));
}
}
}
}
.ui-plugin-sheets-font-family {
width: 60px;
}
}
.uni-formula-bar {
display: flex;
width: 133px;
padding: var(--radius-m, 6px) 12px;
align-items: center;
gap: 8px;
border-radius: var(--radius-m, 6px);
background: #f0f3fe;
color: var(---Hyacinth-Blue-500, #274fee);
font-style: normal;
font-weight: 500;
line-height: 20px; /* 153.846% */
&-text {
white-space: nowrap;
font-size: 13px;
}
&-disable {
opacity: 0.3;
}
}
.uni-floating-toolbar {
display: inline-flex;
height: 48px;
padding: 8px;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
.unit-name {
display: flex;
height: 24px;
padding: 0px 4px;
align-items: center;
gap: 4px;
border-radius: var(--radius-m, 6px);
background: var(---White, #fff);
color: #1e222b;
font-size: 13px;
font-weight: 400;
}
.uni-div {
width: 1px;
height: 24px;
background: #ecedef;
}
.uni-formula-bar-full-input {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(---White, #fff);
z-index: 10;
.uni-formula-box {
position: relative;
display: flex;
align-items: center;
gap: 8px;
height: 100%;
box-sizing: border-box;
padding: 8px;
}
.uni-formula-input {
position: relative;
flex: 1;
background: #f0f3fe;
overflow: hidden;
border-radius: var(--radius-m, 6px);
border: 1px solid var(---Hyacinth-Blue-500, #274fee);
background: #f0f3fe;
box-shadow: 0px 0px 4px 0px rgba(39, 79, 238, 0.4);
height: 100%;
}
.formula-icon {
padding: 6px 0;
&-disable {
color: rgb(var(--grey-200));
cursor: not-allowed;
}
&-wrapper {
position: relative;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
width: 80px;
height: 100%;
font-size: var(--font-size-xs);
border-right: 1px solid rgb(var(--border-color));
.icon-container {
display: flex;
align-items: center;
justify-content: center;
padding: var(--padding-xs);
font-size: var(--font-size-lg);
border-radius: var(--border-radius-base);
&-success {
&.formula-active {
cursor: pointer;
color: rgb(var(--success-color));
&:hover {
background-color: rgb(var(--grey-50));
}
}
}
&-error {
&.formula-active {
cursor: pointer;
color: rgb(var(--error-color));
&:hover {
background-color: rgb(var(--grey-50));
}
}
}
&-fx {
cursor: pointer;
&:hover {
background-color: rgb(var(--grey-50));
}
}
}
}
}
.formula-icon {
display: flex;
}
.icon-container {
display: flex;
padding: var(--radius-m, 6px);
align-items: center;
gap: 10px;
border-radius: 8px;
&:hover {
background: var(--hyacinth-blue-5007, rgba(39, 79, 238, 0.07));
color: #274fee;
}
}
}
@@ -55,7 +55,7 @@ import { DEFAULT_ZOOM, MAX_ZOOM, MIN_ZOOM, UniControlItem, UniControls } from '.
import { LeftSidebar, RightSidebar } from '../uni-sidebar/UniSidebar';
import { UniFloatingToolbar } from '../uni-toolbar/UniFloatToolbar';
import { UniToolbar } from '../uni-toolbar/UniToolbar';
import styles from './workbench.module.less';
import '@xyflow/react/dist/style.css';
// Refer to packages/ui/src/views/workbench/Workbench.tsx
@@ -189,11 +189,15 @@ export function UniWorkbench(props: IUniWorkbenchProps) {
* bubbled to this element and refocus the input element.
*/}
<ReactFlowProvider>
<div data-u-comp="workbench-layout" className={styles.workbenchLayout} tabIndex={-1} onBlur={(e) => e.stopPropagation()}>
<div className={styles.flowLayer}>
<div
data-u-comp="workbench-layout"
className="univer-relative univer-flex univer-h-full univer-min-h-0 univer-flex-col"
tabIndex={-1}
onBlur={(e) => e.stopPropagation()}
>
<div className="univer-absolute univer-left-0 univer-top-0 univer-h-full univer-w-full">
<section
className={styles.workbenchContainerCanvasContainer}
className="univer-relative univer-flex univer-h-full univer-w-full"
ref={contentRef}
data-range-selector
onContextMenu={(e) => e.preventDefault()}
@@ -243,11 +247,22 @@ export function UniWorkbench(props: IUniWorkbenchProps) {
<ComponentContainer key="content" components={contentComponents} />
</section>
</div>
<div className={styles.floatLayer}>
<div
className={`
univer-pointer-events-none univer-absolute univer-left-0 univer-top-0 univer-h-full
univer-w-full
[&>*]:univer-pointer-events-auto
`}
>
{/* header */}
{header && (
<div className={styles.workbenchContainerHeader}>
<div className={styles.workbenchToolbarWrapper}>
<div
className={`
univer-relative univer-z-10 univer-flex univer-w-full univer-items-center
univer-justify-center univer-pt-3
`}
>
<div className="univer-pointer-events-auto univer-max-w-[calc(100%-650px)]">
<UniToolbar />
<ComponentContainer key="header" components={headerComponents} />
</div>
@@ -298,18 +313,23 @@ function UnitNode({ data }: IUnitNodeProps) {
}, [disableChangingUnitFocusing, focused, unitId, commandService]);
return (
<div className={styles.uniNodeContainer} onPointerDownCapture={focus}>
<div className="univer-relative univer-flex univer-h-full univer-w-full" onPointerDownCapture={focus}>
<NodeResizer isVisible={focused} minWidth={180} minHeight={100} />
<UnitRenderer
key={data.unitId}
{...data}
/>
<div className={styles.uniNodeDragHandle}>
<div
className={`
univer-absolute -univer-left-8 univer-flex univer-h-[18px] univer-w-[18px] univer-items-center
univer-justify-center univer-rounded univer-p-1 univer-shadow-sm
`}
>
<MenuSingle />
</div>
<div className={styles.uniNodeTitle}>
<div className="univer-absolute -univer-top-6 univer-left-0 univer-text-sm univer-text-gray-600">
{title}
</div>
<ComponentContainer key="unit" components={unitComponents} sharedProps={{ unitId }} />
@@ -342,8 +362,11 @@ function UnitRenderer(props: IUnitRendererProps) {
return (
<div
className={clsx(styles.workbenchContainerCanvas, {
[styles.workbenchContainerCanvasFocused]: focused,
className={clsx(`
univer-relative univer-flex-1 univer-overflow-hidden univer-rounded-lg univer-borer univer-border-solid
univer-border-transparent
`, {
'univer-border-primary-500': focused,
})}
ref={mountRef}
// We bind these focusing events on capture phrase so the
@@ -1,151 +0,0 @@
.workbench {
&-layout {
display: flex;
flex-direction: column;
height: 100%;
background-color: rgb(var(--bg-color));
min-height: 0;
position: relative;
}
&-header {
// height: 32px;
}
&-toolbar-wrapper {
max-width: calc(100% - 650px);
pointer-events: auto;
}
&-container {
position: relative;
display: flex;
flex: 1;
flex-direction: column;
min-height: 0;
&-header {
position: relative;
z-index: 10;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
padding-top: 12px;
}
&-wrapper {
display: grid;
grid-template-columns: auto 1fr auto;
grid-template-rows: 100%;
height: 100%;
overflow: hidden;
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
}
&-content {
position: relative;
overflow: hidden;
display: grid;
grid-template-rows: auto 1fr;
flex: 1;
background-color: rgb(var(--bg-color-secondary));
border-bottom: 1px solid rgb(var(--border-color));
}
&-canvas-container {
position: relative;
display: flex;
height: 100%;
width: 100%;
}
&-canvas {
flex: 1;
position: relative;
overflow: hidden;
border-radius: 8px;
border: 1px solid transparent;
&-focused {
border: 1px solid rgb(var(--primary-color));
}
}
&-left-sidebar {
width: 340px;
}
}
&-footer {
position: absolute;
bottom: 20px;
left: 20px;
width: calc(100% - 700px);
background-color: rgb(var(--bg-color-secondary));
border: 2px solid rgb(var(--border-color));
border-radius: 8px;
pointer-events: auto;
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
}
}
:global {
.react-flow__attribution {
display: none !important;
}
}
.float-layer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
> * {
pointer-events: all;
}
}
.flow-layer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.uni-node {
&-title {
position: absolute;
top: -24px;
left: 0;
font-size: 11px;
color: rgb(var(--grey-600));
}
&-container {
width: 100%;
height: 100%;
position: relative;
display: flex;
}
&-drag-handle {
display: flex;
border-radius: 4px;
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
background-color: rgb(var(--bg-color-secondary));
position: absolute;
left: -32px;
padding: 4px;
height: 18px;
width: 18px;
justify-content: center;
align-items: center;
}
}