Merge branch 'master' into bugfix/iframe
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"projectName": "Ligo-Virgo",
|
||||
"projectName": "toeverything",
|
||||
"projectOwner": "toeverything",
|
||||
"repoType": "github",
|
||||
"repoHost": "https://github.com",
|
||||
@@ -25,7 +25,7 @@
|
||||
"login": "tzhangchi",
|
||||
"name": "Chi Zhang",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/5910926?v=4",
|
||||
"profile": "https://zhangchi.blog.csdn.net/",
|
||||
"profile": "http://zhangchi.page/",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
@@ -33,7 +33,7 @@
|
||||
},
|
||||
{
|
||||
"login": "alt1o",
|
||||
"name": "alt1o",
|
||||
"name": "wang xinglong",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/21084335?v=4",
|
||||
"profile": "https://github.com/alt1o",
|
||||
"contributions": [
|
||||
@@ -43,7 +43,7 @@
|
||||
},
|
||||
{
|
||||
"login": "DiamondThree",
|
||||
"name": "Diamond",
|
||||
"name": "DiamondThree",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/24630517?v=4",
|
||||
"profile": "https://github.com/DiamondThree",
|
||||
"contributions": [
|
||||
@@ -63,7 +63,7 @@
|
||||
},
|
||||
{
|
||||
"login": "zuoxiaodong0815",
|
||||
"name": "zuoxiaodong0815",
|
||||
"name": "xiaodong zuo",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/53252747?v=4",
|
||||
"profile": "https://github.com/zuoxiaodong0815",
|
||||
"contributions": [
|
||||
@@ -73,7 +73,7 @@
|
||||
},
|
||||
{
|
||||
"login": "SaikaSakura",
|
||||
"name": "SaikaSakura",
|
||||
"name": "MingLIang Wang",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/11530942?v=4",
|
||||
"profile": "https://github.com/SaikaSakura",
|
||||
"contributions": [
|
||||
@@ -92,10 +92,10 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "tuluffy",
|
||||
"name": "tuluffy",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/26808339?v=4",
|
||||
"profile": "https://tuluffy.github.io/angular.github.io/",
|
||||
"login": "mitsuhatu",
|
||||
"name": "mitsuhatu",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/110213079?v=4",
|
||||
"profile": "https://github.com/mitsuhatu",
|
||||
"contributions": [
|
||||
"code",
|
||||
"doc"
|
||||
|
||||
@@ -14,7 +14,7 @@ Planning, Sorting and Creating all Together. Open-source, Privacy-First, and Fre
|
||||
|
||||
<!--
|
||||
Make New Badge Pattern badges inline
|
||||
See https://github.com/all-contributors/all-contributors/issues/361#issuecomment-637166066
|
||||
See https://github.com/all-?/all-contributors/issues/361#issuecomment-637166066
|
||||
-->
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
|
||||
@@ -38,7 +38,7 @@ See https://github.com/all-contributors/all-contributors/issues/361#issuecomment
|
||||
<a href="https://t.me/affineworkos"><b>Telegram</b></a>
|
||||
</p>
|
||||
|
||||
<p align="center"><img width="1920" alt="affine_screen" src="https://user-images.githubusercontent.com/79301703/182363099-48b479c3-dc26-4fc3-8f9b-45f9cf358f9a.png"><p/>
|
||||
<p align="center"><img width="1920" alt="affine_screen" src="https://user-images.githubusercontent.com/21084335/182552060-972cac0e-6258-4ccb-85bd-3bb466c30ccd.png"><p/>
|
||||
|
||||
# Stay Up-to-Date
|
||||
|
||||
@@ -52,7 +52,8 @@ See https://github.com/all-contributors/all-contributors/issues/361#issuecomment
|
||||
- [Plan your task](#plan-your-task)
|
||||
- [Sort your knowledge](#sort-your-knowledge)
|
||||
- [Create your story](#create-your-story)
|
||||
- [Getting Started with development](#getting-started-with-development)
|
||||
- [Documentation](#documentation)
|
||||
- [Getting Started with development](#getting-started-with-development)
|
||||
- [Roadmap](#roadmap)
|
||||
- [Releases](#releases)
|
||||
- [Feature requests](#feature-requests)
|
||||
@@ -60,6 +61,7 @@ See https://github.com/all-contributors/all-contributors/issues/361#issuecomment
|
||||
- [The Philosophy of AFFiNE](#the-philosophy-of-affine)
|
||||
- [Community](#community)
|
||||
- [Contributors](#contributors)
|
||||
- [Acknowledgments](#acknowledgments)
|
||||
- [License](#license)
|
||||
|
||||
## Shape your page
|
||||
@@ -80,9 +82,13 @@ We want your data always to be yours, and we don't want to make any sacrifice to
|
||||
Collaboration isn't only necessary for teams -- you may take and insert pics on your phone, then edit them on your desktop, and share them with your collaborators.
|
||||
Affine is fully built with web technologies so that consistency and accessibility are always guaranteed on Mac, Windows and Linux. The local file system support will be available when version 0.0.1beta is released.
|
||||
|
||||
# Getting Started with development
|
||||
# Documentation
|
||||
|
||||
Please view the [documentation](https://affine.gitbook.io/affine/) in Contribute-to-AFFiNE/Software-Contributions/Environment-setup.
|
||||
Please view the [documentation](https://affine.gitbook.io/affine/)
|
||||
|
||||
## Getting Started with development
|
||||
|
||||
Please view the path Contribute-to-AFFiNE/Software-Contributions/Quick-Start in documentation.
|
||||
|
||||
# Roadmap
|
||||
|
||||
@@ -134,9 +140,17 @@ We would like to give special thanks to the innovators and pioneers who greatly
|
||||
|
||||
We would also like to give thanks to open-source projects that make affine possible:
|
||||
|
||||
- Yjs & Yrs
|
||||
- React
|
||||
- Rust
|
||||
- [Yjs](https://github.com/yjs/yjs) & [Yrs](https://github.com/y-crdt/y-crdt) -- Fundamental support of CRDTs for our implements on state management and data sync.
|
||||
- [React](https://github.com/facebook/react) -- View layer support and web GUI framework.
|
||||
- [Rust](https://github.com/rust-lang/rust) -- High performance language that extends the ability and availability of our real-time backend, JWST.
|
||||
- [Fossil](https://www2.fossil-scm.org/home/doc/trunk/www/index.wiki) -- Source code management tool made with CRDTs which inspired our design on block data structure.
|
||||
- [slatejs](https://github.com/ianstormtaylor/slate) -- Customizable rich-text editor.
|
||||
- [Jotai](https://github.com/pmndrs/jotai) -- Minimal state management tool for frontend.
|
||||
- [Tldraw](https://github.com/tldraw/tldraw) -- Excellent drawing board.
|
||||
- [MUI](https://github.com/mui/material-ui) -- Our most used graphic UI component library.
|
||||
- Other [dependancies](https://github.com/toeverything/AFFiNE/network/dependencies)
|
||||
|
||||
Thanks a lot to the community for providing such powerful and simple libraries, so that we can focus more on the implementation of the product logic, and we hope that in the future our projects will also provide a more easy-to-use knowledge base for everyone.
|
||||
|
||||
# Community
|
||||
|
||||
@@ -152,16 +166,16 @@ For help, discussion about best practices, or any other conversation that would
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center"><a href="https://darksky.eu.org/"><img src="https://avatars.githubusercontent.com/u/25152247?v=4?s=100" width="100px;" alt=""/><br /><sub><b>DarkSky</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=darkskygit" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=darkskygit" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/tzhangchi"><img src="https://avatars.githubusercontent.com/u/5910926?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Chi Zhang</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=tzhangchi" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=tzhangchi" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/alt1o"><img src="https://avatars.githubusercontent.com/u/21084335?v=4?s=100" width="100px;" alt=""/><br /><sub><b>alt1o</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=alt1o" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=alt1o" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/DiamondThree"><img src="https://avatars.githubusercontent.com/u/24630517?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Diamond</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=DiamondThree" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=DiamondThree" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://zhangchi.page/"><img src="https://avatars.githubusercontent.com/u/5910926?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Chi Zhang</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=tzhangchi" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=tzhangchi" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/alt1o"><img src="https://avatars.githubusercontent.com/u/21084335?v=4?s=100" width="100px;" alt=""/><br /><sub><b>wang xinglong</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=alt1o" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=alt1o" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/DiamondThree"><img src="https://avatars.githubusercontent.com/u/24630517?v=4?s=100" width="100px;" alt=""/><br /><sub><b>DiamondThree</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=DiamondThree" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=DiamondThree" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://lawvs.github.io/profile/"><img src="https://avatars.githubusercontent.com/u/18554747?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Whitewater</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=lawvs" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=lawvs" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/zuoxiaodong0815"><img src="https://avatars.githubusercontent.com/u/53252747?v=4?s=100" width="100px;" alt=""/><br /><sub><b>zuoxiaodong0815</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=zuoxiaodong0815" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=zuoxiaodong0815" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/SaikaSakura"><img src="https://avatars.githubusercontent.com/u/11530942?v=4?s=100" width="100px;" alt=""/><br /><sub><b>SaikaSakura</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=SaikaSakura" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=SaikaSakura" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/zuoxiaodong0815"><img src="https://avatars.githubusercontent.com/u/53252747?v=4?s=100" width="100px;" alt=""/><br /><sub><b>xiaodong zuo</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=zuoxiaodong0815" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=zuoxiaodong0815" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/SaikaSakura"><img src="https://avatars.githubusercontent.com/u/11530942?v=4?s=100" width="100px;" alt=""/><br /><sub><b>MingLIang Wang</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=SaikaSakura" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=SaikaSakura" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/QiShaoXuan"><img src="https://avatars.githubusercontent.com/u/22772830?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Qi</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=QiShaoXuan" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=QiShaoXuan" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://tuluffy.github.io/angular.github.io/"><img src="https://avatars.githubusercontent.com/u/26808339?v=4?s=100" width="100px;" alt=""/><br /><sub><b>tuluffy</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=tuluffy" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=tuluffy" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/mitsuhatu"><img src="https://avatars.githubusercontent.com/u/110213079?v=4?s=100" width="100px;" alt=""/><br /><sub><b>mitsuhatu</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=mitsuhatu" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=mitsuhatu" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://shockwave.me/"><img src="https://avatars.githubusercontent.com/u/15013925?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Austaras</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=Austaras" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=Austaras" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/uptonking?tab=repositories&type=source"><img src="https://avatars.githubusercontent.com/u/11391549?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jin Yao</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=uptonking" title="Code">💻</a> <a href="https://github.com/toeverything/AFFiNE/commits?author=uptonking" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
|
||||
@@ -31,7 +31,7 @@ import { WorkspaceName } from './workspace-name';
|
||||
import { CollapsiblePageTree } from './collapsible-page-tree';
|
||||
import { useFlag } from '@toeverything/datasource/feature-flags';
|
||||
import { type BlockEditor } from '@toeverything/components/editor-core';
|
||||
|
||||
import { Tabs } from './components/tabs';
|
||||
type PageProps = {
|
||||
workspace: string;
|
||||
};
|
||||
@@ -80,7 +80,9 @@ export function Page(props: PageProps) {
|
||||
onMouseLeave={() => setSpaceSidebarVisible(false)}
|
||||
>
|
||||
<WorkspaceName />
|
||||
<Divider light={true} sx={{ my: 1, margin: '6px 0px' }} />
|
||||
|
||||
<Tabs />
|
||||
|
||||
<WorkspaceSidebarContent>
|
||||
<div>
|
||||
{dailyNotesFlag && (
|
||||
@@ -219,4 +221,5 @@ const WorkspaceSidebar = styled('div')(({ hidden }) => ({
|
||||
const WorkspaceSidebarContent = styled('div')({
|
||||
flex: 'auto',
|
||||
overflow: 'hidden auto',
|
||||
marginTop: '18px',
|
||||
});
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
export const Logo = ({ color, style, ...props }) => {
|
||||
return (
|
||||
<svg
|
||||
width="52"
|
||||
height="52"
|
||||
viewBox="0 0 52 52"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect width="52" height="52" rx="10" fill="#3E6FDB" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M24.2189 11.4392L14.6321 38.7943H20.2472L26.3453 19.8747L32.4461 38.7943H38.0423L28.454 11.4392H24.2189Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { Logo } from './Logo';
|
||||
@@ -0,0 +1,61 @@
|
||||
import { useState } from 'react';
|
||||
import { MuiDivider as Divider, styled } from '@toeverything/components/ui';
|
||||
import type { ValueOf } from '@toeverything/utils';
|
||||
|
||||
const StyledTabs = styled('div')({
|
||||
width: '100%',
|
||||
height: '12px',
|
||||
marginTop: '12px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
cursor: 'pointer',
|
||||
});
|
||||
|
||||
const StyledDivider = styled(Divider)<{ isActive?: boolean }>(
|
||||
({ isActive }) => {
|
||||
return {
|
||||
flex: 1,
|
||||
backgroundColor: isActive ? '#3E6FDB' : '#ECF1FB',
|
||||
borderWidth: '2px',
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const TAB_TITLE = {
|
||||
PAGES: 'pages',
|
||||
GALLERY: 'gallery',
|
||||
TOC: 'toc',
|
||||
} as const;
|
||||
|
||||
const TabMap = new Map<TabKey, TabValue>([
|
||||
['PAGES', 'pages'],
|
||||
['GALLERY', 'gallery'],
|
||||
['TOC', 'toc'],
|
||||
]);
|
||||
|
||||
type TabKey = keyof typeof TAB_TITLE;
|
||||
type TabValue = ValueOf<typeof TAB_TITLE>;
|
||||
|
||||
const Tabs = () => {
|
||||
const [activeTab, setActiveTab] = useState<TabValue>(TAB_TITLE.PAGES);
|
||||
|
||||
const onClick = (v: TabValue) => {
|
||||
setActiveTab(v);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledTabs>
|
||||
{[...TabMap.entries()].map(([k, v]) => {
|
||||
return (
|
||||
<StyledDivider
|
||||
key={v}
|
||||
isActive={v === activeTab}
|
||||
onClick={() => onClick(v)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</StyledTabs>
|
||||
);
|
||||
};
|
||||
|
||||
export { Tabs };
|
||||
@@ -0,0 +1 @@
|
||||
export { Tabs } from './Tabs';
|
||||
@@ -1,27 +1,28 @@
|
||||
import {
|
||||
MuiButton as Button,
|
||||
Switch,
|
||||
styled,
|
||||
MuiOutlinedInput as OutlinedInput,
|
||||
} from '@toeverything/components/ui';
|
||||
import { LogoIcon } from '@toeverything/components/icons';
|
||||
import { PinIcon } from '@toeverything/components/icons';
|
||||
import {
|
||||
useUserAndSpaces,
|
||||
useShowSpaceSidebar,
|
||||
} from '@toeverything/datasource/state';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { services } from '@toeverything/datasource/db-service';
|
||||
import { Logo } from './components/logo/Logo';
|
||||
|
||||
const WorkspaceContainer = styled('div')({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
minHeight: 60,
|
||||
padding: '12px 0px',
|
||||
flexDirection: 'column',
|
||||
paddingBottom: '12px',
|
||||
color: '#566B7D',
|
||||
});
|
||||
const LeftContainer = styled('div')({
|
||||
flex: 'auto',
|
||||
display: 'flex',
|
||||
height: '52px',
|
||||
alignItems: 'center',
|
||||
margin: '0 12px',
|
||||
});
|
||||
|
||||
const LogoContainer = styled('div')({
|
||||
@@ -32,20 +33,36 @@ const LogoContainer = styled('div')({
|
||||
minWidth: 24,
|
||||
});
|
||||
|
||||
const StyledLogoIcon = styled(LogoIcon)(({ theme }) => {
|
||||
return {
|
||||
color: theme.affine.palette.primary,
|
||||
width: '16px !important',
|
||||
height: '16px !important',
|
||||
};
|
||||
const StyledPin = styled('div')({
|
||||
display: 'flex',
|
||||
justifyContent: 'end',
|
||||
alignItems: 'center',
|
||||
});
|
||||
|
||||
const StyledWorkspace = styled('div')({
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-between',
|
||||
marginLeft: '12px',
|
||||
paddingLeft: '12px',
|
||||
});
|
||||
|
||||
const StyledWorkspaceDesc = styled('div')({
|
||||
fontSize: '12px',
|
||||
color: '#98ACBD',
|
||||
height: '18px',
|
||||
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
});
|
||||
|
||||
const WorkspaceNameContainer = styled('div')({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flex: 'auto',
|
||||
width: '100px',
|
||||
marginRight: '10px',
|
||||
width: '165px',
|
||||
height: '34px',
|
||||
input: {
|
||||
padding: '5px 10px',
|
||||
},
|
||||
@@ -58,21 +75,15 @@ const WorkspaceNameContainer = styled('div')({
|
||||
});
|
||||
|
||||
const WorkspaceReNameContainer = styled('div')({
|
||||
marginRight: '10px',
|
||||
height: '34px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
|
||||
input: {
|
||||
padding: '5px 10px',
|
||||
},
|
||||
});
|
||||
|
||||
const ToggleDisplayContainer = styled('div')({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
fontSize: 12,
|
||||
color: '#3E6FDB',
|
||||
padding: 6,
|
||||
minWidth: 64,
|
||||
});
|
||||
|
||||
export const WorkspaceName = () => {
|
||||
const { currentSpaceId } = useUserAndSpaces();
|
||||
const { fixedDisplay, toggleSpaceSidebar } = useShowSpaceSidebar();
|
||||
@@ -139,35 +150,46 @@ export const WorkspaceName = () => {
|
||||
|
||||
return (
|
||||
<WorkspaceContainer>
|
||||
<StyledPin>
|
||||
<PinIcon
|
||||
style={{
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
color: fixedDisplay ? '#3E6FDB' : '',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
onClick={toggleSpaceSidebar}
|
||||
/>
|
||||
</StyledPin>
|
||||
<LeftContainer>
|
||||
<LogoContainer>
|
||||
<StyledLogoIcon />
|
||||
<Logo color={undefined} style={undefined} />
|
||||
</LogoContainer>
|
||||
|
||||
{inRename ? (
|
||||
<WorkspaceReNameContainer>
|
||||
<OutlinedInput
|
||||
value={workspaceName}
|
||||
onChange={handleChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
onMouseLeave={() => setInRename(false)}
|
||||
/>
|
||||
</WorkspaceReNameContainer>
|
||||
) : (
|
||||
<WorkspaceNameContainer>
|
||||
<span onClick={() => setInRename(true)}>
|
||||
{workspaceName || workspaceId}
|
||||
</span>
|
||||
</WorkspaceNameContainer>
|
||||
)}
|
||||
<StyledWorkspace>
|
||||
{inRename ? (
|
||||
<WorkspaceReNameContainer>
|
||||
<OutlinedInput
|
||||
style={{ width: '140px', height: '28px' }}
|
||||
value={workspaceName}
|
||||
onChange={handleChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
onMouseLeave={() => setInRename(false)}
|
||||
/>
|
||||
</WorkspaceReNameContainer>
|
||||
) : (
|
||||
<WorkspaceNameContainer>
|
||||
<span onClick={() => setInRename(true)}>
|
||||
{workspaceName || workspaceId}
|
||||
</span>
|
||||
</WorkspaceNameContainer>
|
||||
)}
|
||||
|
||||
<StyledWorkspaceDesc>
|
||||
To shape, Not to Adapt.
|
||||
</StyledWorkspaceDesc>
|
||||
</StyledWorkspace>
|
||||
</LeftContainer>
|
||||
<ToggleDisplayContainer onClick={toggleSpaceSidebar}>
|
||||
<Switch
|
||||
checked={fixedDisplay}
|
||||
checkedLabel="ON"
|
||||
uncheckedLabel="OFF"
|
||||
/>
|
||||
</ToggleDisplayContainer>
|
||||
</WorkspaceContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -20,12 +20,12 @@ const DiscordIcon = (props: any) => {
|
||||
width="71"
|
||||
height="55"
|
||||
viewBox="0 0 71 55"
|
||||
fill="none"
|
||||
fill="currentcolor"
|
||||
>
|
||||
<g clip-path="url(#clip0)">
|
||||
<path
|
||||
d="M60.1045 4.8978C55.5792 2.8214 50.7265 1.2916 45.6527 0.41542C45.5603 0.39851 45.468 0.440769 45.4204 0.525289C44.7963 1.6353 44.105 3.0834 43.6209 4.2216C38.1637 3.4046 32.7345 3.4046 27.3892 4.2216C26.905 3.0581 26.1886 1.6353 25.5617 0.525289C25.5141 0.443589 25.4218 0.40133 25.3294 0.41542C20.2584 1.2888 15.4057 2.8186 10.8776 4.8978C10.8384 4.9147 10.8048 4.9429 10.7825 4.9795C1.57795 18.7309 -0.943561 32.1443 0.293408 45.3914C0.299005 45.4562 0.335386 45.5182 0.385761 45.5576C6.45866 50.0174 12.3413 52.7249 18.1147 54.5195C18.2071 54.5477 18.305 54.5139 18.3638 54.4378C19.7295 52.5728 20.9469 50.6063 21.9907 48.5383C22.0523 48.4172 21.9935 48.2735 21.8676 48.2256C19.9366 47.4931 18.0979 46.6 16.3292 45.5858C16.1893 45.5041 16.1781 45.304 16.3068 45.2082C16.679 44.9293 17.0513 44.6391 17.4067 44.3461C17.471 44.2926 17.5606 44.2813 17.6362 44.3151C29.2558 49.6202 41.8354 49.6202 53.3179 44.3151C53.3935 44.2785 53.4831 44.2898 53.5502 44.3433C53.9057 44.6363 54.2779 44.9293 54.6529 45.2082C54.7816 45.304 54.7732 45.5041 54.6333 45.5858C52.8646 46.6197 51.0259 47.4931 49.0921 48.2228C48.9662 48.2707 48.9102 48.4172 48.9718 48.5383C50.038 50.6034 51.2554 52.5699 52.5959 54.435C52.6519 54.5139 52.7526 54.5477 52.845 54.5195C58.6464 52.7249 64.529 50.0174 70.6019 45.5576C70.6551 45.5182 70.6887 45.459 70.6943 45.3942C72.1747 30.0791 68.2147 16.7757 60.1968 4.9823C60.1772 4.9429 60.1437 4.9147 60.1045 4.8978ZM23.7259 37.3253C20.2276 37.3253 17.3451 34.1136 17.3451 30.1693C17.3451 26.225 20.1717 23.0133 23.7259 23.0133C27.308 23.0133 30.1626 26.2532 30.1066 30.1693C30.1066 34.1136 27.28 37.3253 23.7259 37.3253ZM47.3178 37.3253C43.8196 37.3253 40.9371 34.1136 40.9371 30.1693C40.9371 26.225 43.7636 23.0133 47.3178 23.0133C50.9 23.0133 53.7545 26.2532 53.6986 30.1693C53.6986 34.1136 50.9 37.3253 47.3178 37.3253Z"
|
||||
fill="#23272A"
|
||||
fill="currentcolor"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
@@ -515,10 +515,12 @@ export function App() {
|
||||
justifyContent: 'left',
|
||||
textAlign: 'left',
|
||||
transition: 'all .5s',
|
||||
// boxShadow: '2px 2px 40px #08f2',
|
||||
// ':hover': {
|
||||
// boxShadow: '2px 2px 40px #08f4',
|
||||
// },
|
||||
transform: 'scale(0.98)',
|
||||
boxShadow: '2px 2px 40px #0002',
|
||||
':hover': {
|
||||
transform: 'scale(1)',
|
||||
boxShadow: '2px 2px 40px #0004',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<AffineImage
|
||||
@@ -537,10 +539,10 @@ export function App() {
|
||||
}}
|
||||
>
|
||||
<Grid
|
||||
xs={matches ? 12 : 4}
|
||||
xs={matches ? 12 : 6}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
...(matches ? {} : { marginRight: '4em' }),
|
||||
...(matches ? {} : { marginLeft: '4em' }),
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
@@ -584,7 +586,7 @@ export function App() {
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid
|
||||
xs={matches ? 12 : 8}
|
||||
xs={matches ? 12 : 6}
|
||||
sx={{ display: 'flex', width: '100%' }}
|
||||
>
|
||||
<Box
|
||||
@@ -595,10 +597,12 @@ export function App() {
|
||||
justifyContent: 'left',
|
||||
textAlign: 'left',
|
||||
transition: 'all .5s',
|
||||
// boxShadow: '2px 2px 40px #08f2',
|
||||
// ':hover': {
|
||||
// boxShadow: '2px 2px 40px #08f4',
|
||||
// },
|
||||
transform: 'scale(0.98)',
|
||||
boxShadow: '2px 2px 40px #0002',
|
||||
':hover': {
|
||||
transform: 'scale(1)',
|
||||
boxShadow: '2px 2px 40px #0004',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<AffineImage
|
||||
@@ -656,10 +660,10 @@ export function App() {
|
||||
justifyContent: 'center',
|
||||
margin: 'auto',
|
||||
transition: 'all .5s',
|
||||
// boxShadow: '2px 2px 40px #08f2',
|
||||
// ':hover': {
|
||||
// boxShadow: '2px 2px 40px #08f4',
|
||||
// },
|
||||
transform: 'scale(0.98)',
|
||||
':hover': {
|
||||
transform: 'scale(1)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<AffineImage
|
||||
@@ -738,13 +742,20 @@ export function App() {
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: 'flex', width: '100%' }}>
|
||||
<Card
|
||||
<Button
|
||||
variant="plain"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
margin: 'auto',
|
||||
minWidth: '4em',
|
||||
'--Card-padding': '4px',
|
||||
'--Card-radius': '0',
|
||||
padding: '1em',
|
||||
minWidth: '6em',
|
||||
}}
|
||||
onClick={() =>
|
||||
window.open(
|
||||
'https://github.com/toeverything/AFFiNE/'
|
||||
)
|
||||
}
|
||||
>
|
||||
<Grid
|
||||
xs={12}
|
||||
@@ -767,21 +778,30 @@ export function App() {
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{ display: 'flex', color: '#888' }}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
color: '#888',
|
||||
fontSize: '0.5em',
|
||||
}}
|
||||
>
|
||||
GitHub
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Card>
|
||||
</Button>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', width: '100%' }}>
|
||||
<Card
|
||||
<Button
|
||||
variant="plain"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
margin: 'auto',
|
||||
minWidth: '4em',
|
||||
'--Card-padding': '4px',
|
||||
'--Card-radius': '0',
|
||||
padding: '1em',
|
||||
minWidth: '6em',
|
||||
}}
|
||||
onClick={() =>
|
||||
window.open('https://www.reddit.com/r/Affine/')
|
||||
}
|
||||
>
|
||||
<Grid
|
||||
xs={12}
|
||||
@@ -804,12 +824,16 @@ export function App() {
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{ display: 'flex', color: '#888' }}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
color: '#888',
|
||||
fontSize: '0.5em',
|
||||
}}
|
||||
>
|
||||
Reddit
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Card>
|
||||
</Button>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
@@ -817,13 +841,18 @@ export function App() {
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Card
|
||||
<Button
|
||||
variant="plain"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
margin: 'auto',
|
||||
minWidth: '4em',
|
||||
'--Card-padding': '4px',
|
||||
'--Card-radius': '0',
|
||||
padding: '1em',
|
||||
minWidth: '6em',
|
||||
}}
|
||||
onClick={() =>
|
||||
window.open('https://t.me/affineworkos')
|
||||
}
|
||||
>
|
||||
<Grid
|
||||
xs={12}
|
||||
@@ -846,21 +875,30 @@ export function App() {
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{ display: 'flex', color: '#888' }}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
color: '#888',
|
||||
fontSize: '0.5em',
|
||||
}}
|
||||
>
|
||||
Telegram
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Card>
|
||||
</Button>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', width: '100%' }}>
|
||||
<Card
|
||||
<Button
|
||||
variant="plain"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
margin: 'auto',
|
||||
minWidth: '4em',
|
||||
'--Card-padding': '4px',
|
||||
'--Card-radius': '0',
|
||||
padding: '1em',
|
||||
minWidth: '6em',
|
||||
}}
|
||||
onClick={() =>
|
||||
window.open('https://discord.gg/yz6tGVsf5p')
|
||||
}
|
||||
>
|
||||
<Grid
|
||||
xs={12}
|
||||
@@ -870,7 +908,11 @@ export function App() {
|
||||
}}
|
||||
>
|
||||
<DiscordIcon
|
||||
sx={{ width: '36px', height: '36px' }}
|
||||
sx={{
|
||||
width: '36px',
|
||||
height: '36px',
|
||||
color: '#09449d',
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
@@ -883,12 +925,16 @@ export function App() {
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{ display: 'flex', color: '#888' }}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
color: '#888',
|
||||
fontSize: '0.5em',
|
||||
}}
|
||||
>
|
||||
Discord
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Card>
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
<Grid xs={12} sx={{ display: 'flex', marginBottom: '2em' }}>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.5 MiB |
@@ -1,10 +0,0 @@
|
||||
<svg width="71" height="55" viewBox="0 0 71 55" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<path d="M60.1045 4.8978C55.5792 2.8214 50.7265 1.2916 45.6527 0.41542C45.5603 0.39851 45.468 0.440769 45.4204 0.525289C44.7963 1.6353 44.105 3.0834 43.6209 4.2216C38.1637 3.4046 32.7345 3.4046 27.3892 4.2216C26.905 3.0581 26.1886 1.6353 25.5617 0.525289C25.5141 0.443589 25.4218 0.40133 25.3294 0.41542C20.2584 1.2888 15.4057 2.8186 10.8776 4.8978C10.8384 4.9147 10.8048 4.9429 10.7825 4.9795C1.57795 18.7309 -0.943561 32.1443 0.293408 45.3914C0.299005 45.4562 0.335386 45.5182 0.385761 45.5576C6.45866 50.0174 12.3413 52.7249 18.1147 54.5195C18.2071 54.5477 18.305 54.5139 18.3638 54.4378C19.7295 52.5728 20.9469 50.6063 21.9907 48.5383C22.0523 48.4172 21.9935 48.2735 21.8676 48.2256C19.9366 47.4931 18.0979 46.6 16.3292 45.5858C16.1893 45.5041 16.1781 45.304 16.3068 45.2082C16.679 44.9293 17.0513 44.6391 17.4067 44.3461C17.471 44.2926 17.5606 44.2813 17.6362 44.3151C29.2558 49.6202 41.8354 49.6202 53.3179 44.3151C53.3935 44.2785 53.4831 44.2898 53.5502 44.3433C53.9057 44.6363 54.2779 44.9293 54.6529 45.2082C54.7816 45.304 54.7732 45.5041 54.6333 45.5858C52.8646 46.6197 51.0259 47.4931 49.0921 48.2228C48.9662 48.2707 48.9102 48.4172 48.9718 48.5383C50.038 50.6034 51.2554 52.5699 52.5959 54.435C52.6519 54.5139 52.7526 54.5477 52.845 54.5195C58.6464 52.7249 64.529 50.0174 70.6019 45.5576C70.6551 45.5182 70.6887 45.459 70.6943 45.3942C72.1747 30.0791 68.2147 16.7757 60.1968 4.9823C60.1772 4.9429 60.1437 4.9147 60.1045 4.8978ZM23.7259 37.3253C20.2276 37.3253 17.3451 34.1136 17.3451 30.1693C17.3451 26.225 20.1717 23.0133 23.7259 23.0133C27.308 23.0133 30.1626 26.2532 30.1066 30.1693C30.1066 34.1136 27.28 37.3253 23.7259 37.3253ZM47.3178 37.3253C43.8196 37.3253 40.9371 34.1136 40.9371 30.1693C40.9371 26.225 43.7636 23.0133 47.3178 23.0133C50.9 23.0133 53.7545 26.2532 53.6986 30.1693C53.6986 34.1136 50.9 37.3253 47.3178 37.3253Z" fill="#23272A"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="71" height="55" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 936 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.0 MiB |
@@ -12,6 +12,8 @@ type GridHandleProps = {
|
||||
blockId: string;
|
||||
enabledAddItem: boolean;
|
||||
draggable: boolean;
|
||||
alertHandleId: string;
|
||||
onMouseEnter?: React.MouseEventHandler<HTMLDivElement>;
|
||||
};
|
||||
|
||||
export const GridHandle: FC<GridHandleProps> = function ({
|
||||
@@ -21,6 +23,8 @@ export const GridHandle: FC<GridHandleProps> = function ({
|
||||
onDrag,
|
||||
onMouseDown,
|
||||
draggable,
|
||||
alertHandleId,
|
||||
onMouseEnter,
|
||||
}) {
|
||||
const [isMouseDown, setIsMouseDown] = useState<boolean>(false);
|
||||
const handleMouseDown: React.MouseEventHandler<HTMLDivElement> = e => {
|
||||
@@ -44,16 +48,17 @@ export const GridHandle: FC<GridHandleProps> = function ({
|
||||
editor.selectionManager.setActivatedNodeId(textBlock.id);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseEnter: React.MouseEventHandler<HTMLDivElement> = e => {
|
||||
onMouseEnter && onMouseEnter(e);
|
||||
};
|
||||
|
||||
return (
|
||||
<GridHandleContainer
|
||||
style={
|
||||
isMouseDown
|
||||
? {
|
||||
backgroundColor: '#3E6FDB',
|
||||
}
|
||||
: {}
|
||||
}
|
||||
isMouseDown={isMouseDown}
|
||||
onMouseDown={handleMouseDown}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
isAlert={alertHandleId === blockId}
|
||||
>
|
||||
{enabledAddItem ? (
|
||||
<AddGridHandle
|
||||
@@ -67,7 +72,10 @@ export const GridHandle: FC<GridHandleProps> = function ({
|
||||
);
|
||||
};
|
||||
|
||||
const GridHandleContainer = styled('div')(({ theme }) => ({
|
||||
const GridHandleContainer = styled('div')<{
|
||||
isMouseDown: boolean;
|
||||
isAlert: boolean;
|
||||
}>(({ theme, isMouseDown, isAlert }) => ({
|
||||
position: 'relative',
|
||||
width: '10px',
|
||||
flexGrow: '0',
|
||||
@@ -78,11 +86,15 @@ const GridHandleContainer = styled('div')(({ theme }) => ({
|
||||
borderRadius: '1px',
|
||||
backgroundClip: 'content-box',
|
||||
' &:hover': {
|
||||
backgroundColor: theme.affine.palette.primary,
|
||||
backgroundColor: isAlert ? 'red' : theme.affine.palette.primary,
|
||||
[`.${GRID_ADD_HANDLE_NAME}`]: {
|
||||
display: 'block',
|
||||
},
|
||||
},
|
||||
...(isMouseDown &&
|
||||
(isAlert
|
||||
? { backgroundColor: 'red' }
|
||||
: { backgroundColor: theme.affine.palette.primary })),
|
||||
}));
|
||||
|
||||
const AddGridHandle = styled('div')(({ theme }) => ({
|
||||
|
||||
@@ -31,6 +31,7 @@ export const Grid: FC<CreateView> = function (props) {
|
||||
const gridItemCountRef = useRef<number>();
|
||||
const originalLeftWidth = useRef<number>(GRID_ITEM_MIN_WIDTH);
|
||||
const originalRightWidth = useRef<number>(GRID_ITEM_MIN_WIDTH);
|
||||
const [alertHandleId, setAlertHandleId] = useState<string>(null);
|
||||
|
||||
const getLeftRightGridItemDomByIndex = (index: number) => {
|
||||
const gridItems = Array.from(gridContainerRef.current?.children).filter(
|
||||
@@ -117,7 +118,7 @@ export const Grid: FC<CreateView> = function (props) {
|
||||
itemDom.style.width = width;
|
||||
};
|
||||
|
||||
const handleDragGrid = (e: MouseEvent, index: number) => {
|
||||
const handleDragGrid = async (e: MouseEvent, index: number) => {
|
||||
setIsOnDrag(true);
|
||||
window.getSelection().removeAllRanges();
|
||||
if (!isSetMouseUp.current) {
|
||||
@@ -165,39 +166,47 @@ export const Grid: FC<CreateView> = function (props) {
|
||||
setItemWidth(leftGrid, newLeft);
|
||||
setItemWidth(rightGrid, newRight);
|
||||
updateDbWidth(leftBlockId, newLeft, rightBlockId, newRight);
|
||||
[leftBlockId, rightBlockId].forEach(async blockId => {
|
||||
if (await checkGridItemHasOverflow(blockId)) {
|
||||
setAlertHandleId(leftBlockId);
|
||||
} else {
|
||||
setAlertHandleId(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const children = (
|
||||
<>
|
||||
{block.childrenIds.map((id, i) => {
|
||||
return (
|
||||
<GridItem
|
||||
style={{
|
||||
transition: isOnDrag
|
||||
? 'none'
|
||||
: 'all 0.2s ease-in-out',
|
||||
}}
|
||||
key={id}
|
||||
className={GRID_ITEM_CLASS_NAME}
|
||||
>
|
||||
<RenderBlock hasContainer={false} blockId={id} />
|
||||
<GridHandle
|
||||
onDrag={event => handleDragGrid(event, i)}
|
||||
editor={editor}
|
||||
onMouseDown={event => handleMouseDown(event, i)}
|
||||
blockId={id}
|
||||
enabledAddItem={
|
||||
block.childrenIds.length < MAX_ITEM_COUNT
|
||||
}
|
||||
draggable={i !== block.childrenIds.length - 1}
|
||||
/>
|
||||
</GridItem>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
const checkGridItemHasOverflow = async (blockId: string) => {
|
||||
let isOverflow = false;
|
||||
const block = await editor.getBlockById(blockId);
|
||||
if (block) {
|
||||
const blockDom = block.dom;
|
||||
if (blockDom) {
|
||||
block.dom.style.overflow = 'scroll';
|
||||
if (block.dom.clientWidth !== block.dom.scrollWidth) {
|
||||
isOverflow = true;
|
||||
}
|
||||
blockDom.style.overflow = 'visible';
|
||||
}
|
||||
}
|
||||
return isOverflow;
|
||||
};
|
||||
|
||||
const handleHandleMouseEnter = (
|
||||
e: React.MouseEvent<HTMLDivElement>,
|
||||
index: number
|
||||
) => {
|
||||
const leftBlockId = block.childrenIds[index];
|
||||
const rightBlockId = block.childrenIds[index + 1];
|
||||
[leftBlockId, rightBlockId].forEach(async blockId => {
|
||||
if (await checkGridItemHasOverflow(blockId)) {
|
||||
setAlertHandleId(leftBlockId);
|
||||
} else {
|
||||
setAlertHandleId(null);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -206,7 +215,35 @@ export const Grid: FC<CreateView> = function (props) {
|
||||
ref={gridContainerRef}
|
||||
isOnDrag={isOnDrag}
|
||||
>
|
||||
{children}
|
||||
{block.childrenIds.map((id, i) => {
|
||||
return (
|
||||
<GridItem
|
||||
style={{
|
||||
transition: isOnDrag
|
||||
? 'none'
|
||||
: 'all 0.2s ease-in-out',
|
||||
}}
|
||||
key={id}
|
||||
className={GRID_ITEM_CLASS_NAME}
|
||||
>
|
||||
<RenderBlock hasContainer={false} blockId={id} />
|
||||
<GridHandle
|
||||
onDrag={event => handleDragGrid(event, i)}
|
||||
editor={editor}
|
||||
onMouseDown={event => handleMouseDown(event, i)}
|
||||
blockId={id}
|
||||
enabledAddItem={
|
||||
block.childrenIds.length < MAX_ITEM_COUNT
|
||||
}
|
||||
onMouseEnter={event =>
|
||||
handleHandleMouseEnter(event, i)
|
||||
}
|
||||
alertHandleId={alertHandleId}
|
||||
draggable={i !== block.childrenIds.length - 1}
|
||||
/>
|
||||
</GridItem>
|
||||
);
|
||||
})}
|
||||
</GridContainer>
|
||||
{isOnDrag
|
||||
? ReactDOM.createPortal(<GridMask />, window.document.body)
|
||||
|
||||
@@ -60,7 +60,7 @@ const GroupContainer = styled('div')<{ isSelect?: boolean }>(
|
||||
({ isSelect, theme }) => ({
|
||||
background: theme.affine.palette.white,
|
||||
border: '2px solid rgba(236,241,251,.5)',
|
||||
padding: '15px 12px',
|
||||
padding: `15px 16px 0 16px`,
|
||||
borderRadius: '10px',
|
||||
...(isSelect
|
||||
? {
|
||||
|
||||
@@ -9,6 +9,7 @@ type BlockContentWrapperProps = {
|
||||
children: ReactElement | null;
|
||||
};
|
||||
|
||||
// TODO: remove
|
||||
export const WrapperWithPendantAndDragDrop: FC<BlockContentWrapperProps> =
|
||||
function ({ block, children, editor }) {
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { FC, PropsWithChildren } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { styled } from '@toeverything/components/ui';
|
||||
import type { AsyncBlock } from '../editor';
|
||||
import { PendantPopover } from './pendant-popover';
|
||||
@@ -11,74 +10,68 @@ interface BlockTagProps {
|
||||
block: AsyncBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Need to be refactored
|
||||
*/
|
||||
export const BlockPendantProvider: FC<PropsWithChildren<BlockTagProps>> = ({
|
||||
block,
|
||||
children,
|
||||
}) => {
|
||||
const [container, setContainer] = useState<HTMLElement>(null);
|
||||
const [isHover, setIsHover] = useState(false);
|
||||
return (
|
||||
<Container ref={(dom: HTMLElement) => setContainer(dom)}>
|
||||
<Container>
|
||||
{children}
|
||||
{container && (
|
||||
<PendantPopover
|
||||
block={block}
|
||||
container={container}
|
||||
onVisibleChange={visible => {
|
||||
setIsHover(visible);
|
||||
}}
|
||||
>
|
||||
<StyledTriggerLine
|
||||
className="triggerLine"
|
||||
isHover={isHover}
|
||||
/>
|
||||
</PendantPopover>
|
||||
)}
|
||||
|
||||
<PendantPopover block={block}>
|
||||
<StyledTriggerLine />
|
||||
</PendantPopover>
|
||||
|
||||
<PendantRender block={block} />
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
const Container = styled('div')({
|
||||
export const LINE_GAP = 16;
|
||||
const TAG_GAP = 4;
|
||||
|
||||
const StyledTriggerLine = styled('div')({
|
||||
padding: `${TAG_GAP}px 0`,
|
||||
width: '100px',
|
||||
cursor: 'default',
|
||||
display: 'flex',
|
||||
alignItems: 'flex-end',
|
||||
position: 'relative',
|
||||
padding: '4px',
|
||||
'&:hover .triggerLine::before': {
|
||||
display: 'flex',
|
||||
|
||||
'::before': {
|
||||
content: "''",
|
||||
width: '100%',
|
||||
height: '2px',
|
||||
background: '#dadada',
|
||||
display: 'none',
|
||||
position: 'absolute',
|
||||
left: '0',
|
||||
top: '4px',
|
||||
},
|
||||
'::after': {
|
||||
content: "''",
|
||||
width: '0',
|
||||
height: '2px',
|
||||
background: '#aac4d5',
|
||||
display: 'block',
|
||||
position: 'absolute',
|
||||
left: '0',
|
||||
top: '4px',
|
||||
transition: 'width .3s',
|
||||
},
|
||||
});
|
||||
|
||||
const StyledTriggerLine = styled('div')<{ isHover: boolean }>(({ isHover }) => {
|
||||
return {
|
||||
padding: '4px 0',
|
||||
width: '100px',
|
||||
cursor: 'default',
|
||||
display: 'flex',
|
||||
alignItems: 'flex-end',
|
||||
position: 'relative',
|
||||
|
||||
'::before': {
|
||||
content: "''",
|
||||
width: '100%',
|
||||
height: '2px',
|
||||
background: '#dadada',
|
||||
display: 'none',
|
||||
position: 'absolute',
|
||||
left: '0',
|
||||
top: '4px',
|
||||
const Container = styled('div')({
|
||||
position: 'relative',
|
||||
paddingBottom: `${LINE_GAP - TAG_GAP * 2}px`,
|
||||
'&:hover': {
|
||||
[StyledTriggerLine.toString()]: {
|
||||
'&::before': {
|
||||
display: 'flex',
|
||||
},
|
||||
'&::after': {
|
||||
width: '100%',
|
||||
},
|
||||
},
|
||||
'::after': {
|
||||
content: "''",
|
||||
width: isHover ? '100%' : '0',
|
||||
height: '2px',
|
||||
background: '#aac4d5',
|
||||
display: 'block',
|
||||
position: 'absolute',
|
||||
left: '0',
|
||||
top: '4px',
|
||||
transition: 'width .3s',
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { FC, useRef } from 'react';
|
||||
import { FC, useRef } from 'react';
|
||||
import { AsyncBlock } from '../../editor';
|
||||
import { PendantHistoryPanel } from '../pendant-history-panel';
|
||||
import {
|
||||
@@ -21,8 +21,6 @@ export const PendantPopover: FC<
|
||||
pointerEnterDelay={300}
|
||||
pointerLeaveDelay={200}
|
||||
placement="bottom-start"
|
||||
// visible={true}
|
||||
// trigger="click"
|
||||
content={
|
||||
<PendantHistoryPanel
|
||||
block={block}
|
||||
|
||||
@@ -12,6 +12,7 @@ enum DragType {
|
||||
}
|
||||
|
||||
const DRAG_STATE_CHANGE_EVENT_KEY = 'dragStateChange';
|
||||
const MAX_GRID_BLOCK_FLOOR = 3;
|
||||
export class DragDropManager {
|
||||
private _editor: Editor;
|
||||
private _enabled: boolean;
|
||||
@@ -231,6 +232,17 @@ export class DragDropManager {
|
||||
if (!(await this._canBeDrop(event))) {
|
||||
direction = BlockDropPlacement.none;
|
||||
}
|
||||
if (
|
||||
direction === BlockDropPlacement.left ||
|
||||
direction === BlockDropPlacement.right
|
||||
) {
|
||||
const path = await this._editor.getBlockPath(blockId);
|
||||
const gridBlocks = path.filter(block => block.type === 'grid');
|
||||
// limit grid block floor counts
|
||||
if (gridBlocks.length >= MAX_GRID_BLOCK_FLOOR) {
|
||||
direction = BlockDropPlacement.none;
|
||||
}
|
||||
}
|
||||
this._setBlockDragDirection(direction);
|
||||
return direction;
|
||||
}
|
||||
|
||||
@@ -340,7 +340,20 @@ export class Editor implements Virgo {
|
||||
const rootBlockId = this.getRootBlockId();
|
||||
const rootBlock = await this.getBlockById(rootBlockId);
|
||||
const blockList: Array<AsyncBlock> = rootBlock ? [rootBlock] : [];
|
||||
const children = (await rootBlock?.children()) || [];
|
||||
return [...blockList, ...(await this.getOffspring(rootBlockId))];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* get all offspring of block
|
||||
* @param {string} id
|
||||
* @return {*}
|
||||
* @memberof Editor
|
||||
*/
|
||||
async getOffspring(id: string) {
|
||||
const block = await this.getBlockById(id);
|
||||
const blockList: Array<AsyncBlock> = [];
|
||||
const children = (await block?.children()) || [];
|
||||
for (const block of children) {
|
||||
if (!block) {
|
||||
continue;
|
||||
@@ -379,6 +392,20 @@ export class Editor implements Virgo {
|
||||
return lastBlock;
|
||||
}
|
||||
|
||||
async getBlockPath(id: string) {
|
||||
const block = await this.getBlockById(id);
|
||||
if (!block) {
|
||||
return [];
|
||||
}
|
||||
const path = [block];
|
||||
let parent = await block.parent();
|
||||
while (parent) {
|
||||
path.unshift(parent);
|
||||
parent = await parent.parent();
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
async getBlockByPoint(point: Point) {
|
||||
const blockList = await this.getBlockList();
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HandleParentIcon } from '@toeverything/components/icons';
|
||||
import { styled } from '@toeverything/components/ui';
|
||||
import { Point } from '@toeverything/utils';
|
||||
|
||||
export const ICON_WIDTH = 24;
|
||||
export const ICON_WIDTH = 16;
|
||||
|
||||
type DragItemProps = {
|
||||
isShow: boolean;
|
||||
@@ -30,17 +30,21 @@ export const DragItem = function ({
|
||||
);
|
||||
};
|
||||
|
||||
const StyledDiv = styled('div')({
|
||||
const StyledDiv = styled('div')(({ theme }) => ({
|
||||
padding: '0',
|
||||
display: 'inlineFlex',
|
||||
display: 'inline-flex',
|
||||
width: `${ICON_WIDTH}px`,
|
||||
height: `${ICON_WIDTH}px`,
|
||||
height: '20px',
|
||||
cursor: 'grab',
|
||||
'& svg': {
|
||||
fontSize: '20px',
|
||||
marginLeft: '-2px',
|
||||
},
|
||||
':hover': {
|
||||
backgroundColor: '#edeef0',
|
||||
backgroundColor: '#F5F7F8',
|
||||
borderRadius: '4px',
|
||||
},
|
||||
});
|
||||
}));
|
||||
|
||||
const StyledButton = styled('div')({
|
||||
padding: '0',
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
import { useState, useEffect, FC } from 'react';
|
||||
import {
|
||||
useState,
|
||||
useEffect,
|
||||
FC,
|
||||
type MouseEvent,
|
||||
type DragEvent,
|
||||
type ReactNode,
|
||||
type CSSProperties,
|
||||
} from 'react';
|
||||
|
||||
import {
|
||||
Virgo,
|
||||
BlockDomInfo,
|
||||
PluginHooks,
|
||||
BlockDropPlacement,
|
||||
LINE_GAP,
|
||||
} from '@toeverything/framework/virgo';
|
||||
import { Button } from '@toeverything/components/common';
|
||||
import { styled } from '@toeverything/components/ui';
|
||||
@@ -14,7 +23,7 @@ import { distinctUntilChanged, Subject } from 'rxjs';
|
||||
import { HandleChildIcon } from '@toeverything/components/icons';
|
||||
import { MENU_WIDTH } from './menu-config';
|
||||
|
||||
const MENU_BUTTON_OFFSET = 12;
|
||||
const MENU_BUTTON_OFFSET = 4;
|
||||
|
||||
export type LineInfoSubject = Subject<
|
||||
| {
|
||||
@@ -64,13 +73,13 @@ function Line(props: { lineInfo: LineInfo; rootRect: DOMRect }) {
|
||||
};
|
||||
const bottomLineStyle = {
|
||||
...horizontalLineStyle,
|
||||
top: intersectionRect.bottom + 1 - rootRect.y,
|
||||
top: intersectionRect.bottom + 1 - rootRect.y - LINE_GAP,
|
||||
};
|
||||
|
||||
const verticalLineStyle = {
|
||||
...lineStyle,
|
||||
width: 2,
|
||||
height: intersectionRect.height,
|
||||
height: intersectionRect.height - LINE_GAP,
|
||||
top: intersectionRect.y - rootRect.y,
|
||||
};
|
||||
const leftLineStyle = {
|
||||
@@ -93,10 +102,10 @@ function Line(props: { lineInfo: LineInfo; rootRect: DOMRect }) {
|
||||
}
|
||||
|
||||
function DragComponent(props: {
|
||||
children: React.ReactNode;
|
||||
style: React.CSSProperties;
|
||||
onDragStart: (event: React.DragEvent<Element>) => void;
|
||||
onDragEnd: (event: React.DragEvent<Element>) => void;
|
||||
children: ReactNode;
|
||||
style: CSSProperties;
|
||||
onDragStart: (event: DragEvent<Element>) => void;
|
||||
onDragEnd: (event: DragEvent<Element>) => void;
|
||||
}) {
|
||||
const { style, children, onDragStart, onDragEnd } = props;
|
||||
return (
|
||||
@@ -122,7 +131,7 @@ export const LeftMenuDraggable: FC<LeftMenuProps> = props => {
|
||||
const [block, setBlock] = useState<BlockDomInfo | undefined>();
|
||||
const [line, setLine] = useState<LineInfo | undefined>(undefined);
|
||||
|
||||
const handleDragStart = async (event: React.DragEvent<Element>) => {
|
||||
const handleDragStart = async (event: DragEvent<Element>) => {
|
||||
event.stopPropagation();
|
||||
setVisible(false);
|
||||
|
||||
@@ -138,12 +147,12 @@ export const LeftMenuDraggable: FC<LeftMenuProps> = props => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleDragEnd = (event: React.DragEvent<Element>) => {
|
||||
const handleDragEnd = (event: DragEvent<Element>) => {
|
||||
event.preventDefault();
|
||||
setLine(undefined);
|
||||
};
|
||||
|
||||
const onClick = (event: React.MouseEvent) => {
|
||||
const onClick = (event: MouseEvent<Element>) => {
|
||||
if (block == null) return;
|
||||
const currentTarget = event.currentTarget;
|
||||
editor.selection.setSelectedNodesIds([block.blockId]);
|
||||
@@ -200,11 +209,10 @@ export const LeftMenuDraggable: FC<LeftMenuProps> = props => {
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left:
|
||||
Math.min(
|
||||
block.rect.left -
|
||||
MENU_WIDTH -
|
||||
MENU_BUTTON_OFFSET
|
||||
) - rootRect.left,
|
||||
block.rect.left -
|
||||
MENU_WIDTH -
|
||||
MENU_BUTTON_OFFSET -
|
||||
rootRect.left,
|
||||
top: block.rect.top - rootRect.top,
|
||||
opacity: visible ? 1 : 0,
|
||||
zIndex: 1,
|
||||
@@ -239,15 +247,19 @@ const Draggable = styled(Button)({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: 'transparent',
|
||||
width: '24px',
|
||||
height: '24px',
|
||||
width: '16px',
|
||||
height: '22px',
|
||||
'& svg': {
|
||||
fontSize: '20px',
|
||||
marginLeft: '-2px',
|
||||
},
|
||||
':hover': {
|
||||
backgroundColor: '#edeef0',
|
||||
backgroundColor: '#F5F7F8',
|
||||
borderRadius: '4px',
|
||||
},
|
||||
});
|
||||
|
||||
const LigoLeftMenu = styled('div')({
|
||||
backgroundColor: 'transparent',
|
||||
marginRight: '4px',
|
||||
// marginRight: '4px',
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BlockDomInfo, HookType } from '@toeverything/framework/virgo';
|
||||
import React, { StrictMode } from 'react';
|
||||
import { StrictMode } from 'react';
|
||||
import { BasePlugin } from '../../base-plugin';
|
||||
import { ignoreBlockTypes } from './menu-config';
|
||||
import { LineInfoSubject, LeftMenuDraggable } from './LeftMenuDraggable';
|
||||
|
||||
@@ -141,8 +141,7 @@ export const PlaceholderPanel = (props: PlaceholderPanelProps) => {
|
||||
setOpen(false);
|
||||
props.onClickTips();
|
||||
};
|
||||
const templateList: Array<TemplateMeta> =
|
||||
TemplateFactory.defaultTemplateList;
|
||||
const templateList = TemplateFactory.defaultTemplateList;
|
||||
const handleNewFromTemplate = async (template: TemplateMeta) => {
|
||||
const pageId = await editor.getRootBlockId();
|
||||
const newPage = await services.api.editorBlock.getBlock(
|
||||
@@ -162,33 +161,30 @@ export const PlaceholderPanel = (props: PlaceholderPanelProps) => {
|
||||
setOpen(false);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<PlaceholderPanelContainer
|
||||
style={{
|
||||
top: point.y + 'px',
|
||||
left: point.x + 'px',
|
||||
display: open ? 'block' : 'none',
|
||||
}}
|
||||
>
|
||||
<EmptyPageTipContainer onClick={handleClickEmptyPage}>
|
||||
Press Enter to continue with an empty page, or pick a
|
||||
template
|
||||
</EmptyPageTipContainer>
|
||||
<div style={{ marginTop: '4px', marginLeft: '-8px' }}>
|
||||
{templateList.map((template, index) => {
|
||||
return (
|
||||
<TemplateItemContainer
|
||||
key={index}
|
||||
onClick={() => {
|
||||
handleNewFromTemplate(template);
|
||||
}}
|
||||
>
|
||||
<BaseButton>{template.name}</BaseButton>
|
||||
</TemplateItemContainer>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</PlaceholderPanelContainer>
|
||||
</>
|
||||
<PlaceholderPanelContainer
|
||||
style={{
|
||||
top: point.y + 'px',
|
||||
left: point.x + 'px',
|
||||
display: open ? 'block' : 'none',
|
||||
}}
|
||||
>
|
||||
<EmptyPageTipContainer onClick={handleClickEmptyPage}>
|
||||
Press Enter to continue with an empty page, or pick a template
|
||||
</EmptyPageTipContainer>
|
||||
<div style={{ marginTop: '4px', marginLeft: '-8px' }}>
|
||||
{templateList.map((template, index) => {
|
||||
return (
|
||||
<TemplateItemContainer
|
||||
key={index}
|
||||
onClick={() => {
|
||||
handleNewFromTemplate(template);
|
||||
}}
|
||||
>
|
||||
<BaseButton>{template.name}</BaseButton>
|
||||
</TemplateItemContainer>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</PlaceholderPanelContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -15,11 +15,11 @@ export const PopoverContainer = styled('div')<
|
||||
const shadow = theme.affine.shadows.shadowSxDownLg;
|
||||
const white = theme.affine.palette.white;
|
||||
|
||||
const border_radius =
|
||||
const borderRadius =
|
||||
border_radius_map[direction] || border_radius_map['left-top'];
|
||||
return {
|
||||
boxShadow: shadow,
|
||||
borderRadius: border_radius,
|
||||
borderRadius: borderRadius,
|
||||
padding: '8px 4px',
|
||||
backgroundColor: white,
|
||||
...style,
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { MuiPopperPlacementType as PopperPlacementType } from '../mui';
|
||||
import React, { forwardRef, type PropsWithChildren } from 'react';
|
||||
import { type PopperHandler, Popper } from '../popper';
|
||||
import { PopoverContainer } from './container';
|
||||
import { PopoverContainer } from './Container';
|
||||
import type { PopoverProps, PopoverDirection } from './interface';
|
||||
|
||||
export const placementToContainerDirection: Record<
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export * from './Popover';
|
||||
export * from './interface';
|
||||
export { PopoverContainer } from './container';
|
||||
export { PopoverContainer } from './Container';
|
||||
@@ -13,7 +13,7 @@ const groupTemplateMap = {
|
||||
grid: gridTemplate,
|
||||
} as GroupTemplateMap;
|
||||
|
||||
const defaultTemplateList = [
|
||||
const defaultTemplateList: Array<TemplateMeta> = [
|
||||
{
|
||||
name: 'New From Quick Start',
|
||||
groupKeys: ['todolist'],
|
||||
@@ -22,10 +22,10 @@ const defaultTemplateList = [
|
||||
{ name: 'New From Blog', groupKeys: ['blog'] },
|
||||
{ name: ' New Todolist', groupKeys: ['todolist'] },
|
||||
{ name: ' New Empty Page', groupKeys: ['empty'] },
|
||||
] as const;
|
||||
];
|
||||
|
||||
const TemplateFactory = {
|
||||
defaultTemplateList: defaultTemplateList,
|
||||
defaultTemplateList,
|
||||
generatePageTemplateByGroupKeys(props: TemplateMeta): Template {
|
||||
const newTitle = props.name || 'Get Started with AFFiNE';
|
||||
const keys: GroupTemplateKeys[] = props.groupKeys || [];
|
||||
|
||||
@@ -541,6 +541,10 @@ export class BlockClient<
|
||||
this.set_page(abstract_block);
|
||||
});
|
||||
this.#block_caches.set(abstract_block.id, abstract_block);
|
||||
|
||||
if (root && abstract_block.flavor === BlockFlavors.page) {
|
||||
root.insertChildren(abstract_block);
|
||||
}
|
||||
return abstract_block;
|
||||
}
|
||||
}
|
||||
|
||||