mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 04:18:54 +00:00
149 lines
4.5 KiB
TypeScript
149 lines
4.5 KiB
TypeScript
import type {
|
|
AffineLegacyCloudWorkspace,
|
|
LocalWorkspace,
|
|
} from '@affine/workspace/type';
|
|
import { ExportIcon, PublishIcon, ShareIcon } from '@blocksuite/icons';
|
|
import type { Page } from '@blocksuite/store';
|
|
import { useBlockSuiteWorkspacePageIsPublic } from '@toeverything/hooks/use-block-suite-workspace-page-is-public';
|
|
import type { FC } from 'react';
|
|
import { useRef } from 'react';
|
|
import { useCallback, useState } from 'react';
|
|
|
|
import { Menu } from '../..';
|
|
import { Export } from './export';
|
|
import { containerStyle, indicatorContainerStyle, tabStyle } from './index.css';
|
|
import { SharePage } from './share-page';
|
|
import { ShareWorkspace } from './share-workspace';
|
|
import { StyledIndicator, StyledShareButton, TabItem } from './styles';
|
|
type SharePanel = 'SharePage' | 'Export' | 'ShareWorkspace';
|
|
const MenuItems: Record<SharePanel, FC<ShareMenuProps>> = {
|
|
SharePage: SharePage,
|
|
Export: Export,
|
|
ShareWorkspace: ShareWorkspace,
|
|
};
|
|
const tabIcons = {
|
|
SharePage: <ShareIcon />,
|
|
Export: <ExportIcon />,
|
|
ShareWorkspace: <PublishIcon />,
|
|
};
|
|
export type ShareMenuProps<
|
|
Workspace extends AffineLegacyCloudWorkspace | LocalWorkspace =
|
|
| AffineLegacyCloudWorkspace
|
|
| LocalWorkspace
|
|
> = {
|
|
workspace: Workspace;
|
|
currentPage: Page;
|
|
onEnableAffineCloud: (workspace: LocalWorkspace) => void;
|
|
onOpenWorkspaceSettings: (workspace: Workspace) => void;
|
|
togglePagePublic: (page: Page, isPublic: boolean) => Promise<void>;
|
|
toggleWorkspacePublish: (
|
|
workspace: Workspace,
|
|
publish: boolean
|
|
) => Promise<void>;
|
|
};
|
|
|
|
function assertInstanceOf<T, U extends T>(
|
|
obj: T,
|
|
type: new (...args: any[]) => U
|
|
): asserts obj is U {
|
|
if (!(obj instanceof type)) {
|
|
throw new Error('Object is not instance of type');
|
|
}
|
|
}
|
|
|
|
export const ShareMenu: FC<ShareMenuProps> = props => {
|
|
const [activeItem, setActiveItem] = useState<SharePanel>('SharePage');
|
|
const [isPublic] = useBlockSuiteWorkspacePageIsPublic(props.currentPage);
|
|
const [open, setOpen] = useState(false);
|
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
const indicatorRef = useRef<HTMLDivElement | null>(null);
|
|
const startTransaction = useCallback(() => {
|
|
if (indicatorRef.current && containerRef.current) {
|
|
const indicator = indicatorRef.current;
|
|
const activeTabElement = containerRef.current.querySelector(
|
|
`[data-tab-key="${activeItem}"]`
|
|
);
|
|
assertInstanceOf(activeTabElement, HTMLElement);
|
|
requestAnimationFrame(() => {
|
|
indicator.style.left = `${activeTabElement.offsetLeft}px`;
|
|
indicator.style.width = `${activeTabElement.offsetWidth}px`;
|
|
});
|
|
}
|
|
}, [activeItem]);
|
|
const handleMenuChange = useCallback(
|
|
(selectedItem: SharePanel) => {
|
|
setActiveItem(selectedItem);
|
|
startTransaction();
|
|
},
|
|
[setActiveItem, startTransaction]
|
|
);
|
|
|
|
const ActiveComponent = MenuItems[activeItem];
|
|
interface ShareMenuProps {
|
|
activeItem: SharePanel;
|
|
onChangeTab: (selectedItem: SharePanel) => void;
|
|
}
|
|
const ShareMenu: FC<ShareMenuProps> = ({ activeItem, onChangeTab }) => {
|
|
const handleButtonClick = (itemName: SharePanel) => {
|
|
onChangeTab(itemName);
|
|
setActiveItem(itemName);
|
|
};
|
|
|
|
return (
|
|
<div className={tabStyle} ref={containerRef}>
|
|
{Object.keys(MenuItems).map(item => (
|
|
<TabItem
|
|
isActive={activeItem === item}
|
|
key={item}
|
|
data-tab-key={item}
|
|
onClick={() => handleButtonClick(item as SharePanel)}
|
|
>
|
|
{tabIcons[item as SharePanel]}
|
|
{isPublic ? (item === 'SharePage' ? 'SharedPage' : item) : item}
|
|
</TabItem>
|
|
))}
|
|
</div>
|
|
);
|
|
};
|
|
const Share = (
|
|
<>
|
|
<ShareMenu activeItem={activeItem} onChangeTab={handleMenuChange} />
|
|
<div className={indicatorContainerStyle}>
|
|
<StyledIndicator
|
|
ref={(ref: HTMLDivElement | null) => {
|
|
indicatorRef.current = ref;
|
|
startTransaction();
|
|
}}
|
|
/>
|
|
</div>
|
|
|
|
<div className={containerStyle}>
|
|
<ActiveComponent {...props} />
|
|
</div>
|
|
</>
|
|
);
|
|
return (
|
|
<Menu
|
|
content={Share}
|
|
visible={open}
|
|
placement="bottom"
|
|
trigger={['click']}
|
|
width={439}
|
|
disablePortal={true}
|
|
onClickAway={() => {
|
|
setOpen(false);
|
|
}}
|
|
>
|
|
<StyledShareButton
|
|
data-testid="share-menu-button"
|
|
onClick={() => {
|
|
setOpen(!open);
|
|
}}
|
|
isShared={isPublic}
|
|
>
|
|
<div>{isPublic ? 'Shared' : 'Share'}</div>
|
|
</StyledShareButton>
|
|
</Menu>
|
|
);
|
|
};
|