Merge pull request #303 from toeverything/develop

chore: v0.0.1-alpha.0.4.2
This commit is contained in:
DarkSky
2022-08-21 15:14:22 +08:00
committed by GitHub
442 changed files with 4352 additions and 3348 deletions
+74
View File
@@ -10,6 +10,7 @@
"commit": false,
"commitConvention": "angular",
"contributorsPerLine": 7,
"badgeTemplate": "\n[all-contributors-badge]: https://img.shields.io/badge/all_contributors-<%= contributors.length %>-orange.svg?style=flat-square\n",
"contributors": [
{
"login": "darkskygit",
@@ -229,6 +230,79 @@
"contributions": [
"doc"
]
},
{
"login": "liby",
"name": "Bryan Lee",
"avatar_url": "https://avatars.githubusercontent.com/u/38807139?v=4",
"profile": "https://liby.github.io/notes",
"contributions": [
"code"
]
},
{
"login": "chenmoonmo",
"name": "Simon Li",
"avatar_url": "https://avatars.githubusercontent.com/u/36295999?v=4",
"profile": "https://github.com/chenmoonmo",
"contributions": [
"code"
]
},
{
"login": "githbq",
"name": "Bob Hu",
"avatar_url": "https://avatars.githubusercontent.com/u/10009709?v=4",
"profile": "https://github.com/githbq",
"contributions": [
"code"
]
},
{
"login": "lucky-chap",
"name": "Quavo",
"avatar_url": "https://avatars.githubusercontent.com/u/67266933?v=4",
"profile": "https://quavo.vercel.app/",
"contributions": [
"doc"
]
},
{
"login": "LuciNyan",
"name": "子瞻 Luci",
"avatar_url": "https://avatars.githubusercontent.com/u/22126563?v=4",
"profile": "https://github.com/LuciNyan",
"contributions": [
"code"
]
},
{
"login": "m1911star",
"name": "Horus",
"avatar_url": "https://avatars.githubusercontent.com/u/4948120?v=4",
"profile": "http://blog.ipili.me/",
"contributions": [
"code",
"platform"
]
},
{
"login": "fanshyiis",
"name": "Super.x",
"avatar_url": "https://avatars.githubusercontent.com/u/15103283?v=4",
"profile": "https://segmentfault.com/u/qzuser_584786517d31a",
"contributions": [
"code"
]
},
{
"login": "wangyu-1999",
"name": "Wang Yu",
"avatar_url": "https://avatars.githubusercontent.com/u/80874770?v=4",
"profile": "https://wangyu-1999.github.io/",
"contributions": [
"code"
]
}
]
}
+1 -1
View File
@@ -26,7 +26,7 @@ module.exports = {
],
scopes: [
{ name: 'selection' },
{ name: 'whiteboard' },
{ name: 'edgeless' },
{ name: 'point' },
{ name: 'group' },
{ name: 'page' },
+1
View File
@@ -1,4 +1,5 @@
# use for download icon from figma
FIGMA_TOKEN
NODE_ENV
AFFINE_FEATURE_FLAG_TOKEN
+6
View File
@@ -260,6 +260,12 @@
]
}
},
{
"files": ["index.tsx", "index.*.tsx"],
"rules": {
"filename-rules/match": "off"
}
},
{
"files": ["*.ts", "*.tsx"],
"extends": ["plugin:@nrwl/nx/typescript"],
-2
View File
@@ -3,6 +3,4 @@
# npx lint-staged
pnpm run lint:with-cache
pnpm run check
pnpm run format:ci
+1 -1
View File
@@ -11,4 +11,4 @@ if test $current_branch != $default_branch; then
exit 0
fi
npm run type:check
pnpm run build:check
+1
View File
@@ -2,6 +2,7 @@
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.formatOnSaveMode": "file",
"editor.codeActionsOnSave": ["source.fixAll", "source.organizeImports"],
"prettier.prettierPath": "./node_modules/prettier",
"cSpell.words": [
"AUTOINCREMENT",
+23 -7
View File
@@ -18,7 +18,7 @@ See https://github.com/all-?/all-contributors/issues/361#issuecomment-637166066
-->
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[all-contributors-badge]: https://img.shields.io/badge/all_contributors-23-orange.svg?style=flat-square
[all-contributors-badge]: https://img.shields.io/badge/all_contributors-31-orange.svg?style=flat-square
<!-- ALL-CONTRIBUTORS-BADGE:END -->
@@ -33,13 +33,19 @@ See https://github.com/all-?/all-contributors/issues/361#issuecomment-637166066
</div>
<p align="center">
<a href="http://affine.pro"><b>Website</b></a>
<a href="https://discord.com/invite/yz6tGVsf5p"><b>Discord</b></a> •
<a href="https://twitter.com/AffineOfficial"><b>Twitter</b></a>
<a href="https://medium.com/@affineworkos"><b>Medium</b></a> •
<a href="https://t.me/affineworkos"><b>Telegram</b></a>
<a href="http://affine.pro"><img src="https://img.shields.io/badge/-AFFiNE-06449d?style=social&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEsAAABLCAMAAAAPkIrYAAAAP1BMVEU8b9w8b9w+b947cNw7b9w6b908b909b9w8b9w7b9w8b9w7cN08b9w7b908b9w7b9w8b907cNw8b9w8b91HcEx3NJCJAAAAFXRSTlP/3QWSgA+lHPlu6Di4XtIrxk/xRADGudUoAAAB9UlEQVR42tWYwbKjIBREG0GJKkRj/v9bZ1ZvRC99rzib11tTB9qqnKoW3/+X38vy7ifzQ1b/wk/8Q1bCv3y6Z6wFh2x2llIRGB6xRhzz6p+wVhRJD1gRZZYHrADYSyqsjFPGZtYbuFESesUysZXlcMnYyJpxTW5keQh5N7G6CUJCE2uHFNfEGiBmbmB1H4jxDawNcqbuPmtAJTtj6RZ0lpIwiR5jNmgfNtHHwLXPWfFYcS2NMdxkjac/dNaNCJPo3yf9pFuseHbDrBsRFguGs8te8Q4rXzTjVSPCIHp3FePKWbzi30xE+4zlBMmoJaGLfpLUmAmLiN4Xyibahy76WZRQMLJ2WX27on2oFvQVac8yi4p+J2forA0V8W1c++AVS1f1H6p9KKLHxk9RWKmsyB+VLC76gV65DLjokdg5KmsEMXsiDwXWSmTc9ezSoKJHoi9zUVihbMHfQOSsXB7Mrz1S1huKPde69sEsiKgNt8hYTjiWlAyENeu7IFe1D15RSEBN+yCiXw17K1RZm/w7UtJVWYN8f1ZyLlkVb2bT4vIVVrINH1dqX2YttkHmIWsfVWs646wcRFYis6fIVGpfYq1kjpGSW8kSRD+xYSmXRM0Ang9eSZioVdy/5pWaLqzIRyIpuVxYozvGf1m67I7pf/s3UXv+AP61NI2Y+BbSAAAAAElFTkSuQmCC" height=25></a>
&nbsp;
<a href="https://discord.com/invite/yz6tGVsf5p"><img src="https://img.shields.io/badge/-Discord-424549?style=social&logo=discord" height=25></a>
&nbsp;
<a href="https://t.me/affineworkos"><img src="https://img.shields.io/badge/-Telegram-red?style=social&logo=telegram" height=25></a>
&nbsp;
<a href="https://twitter.com/AffineOfficial"><img src="https://img.shields.io/badge/-Twitter-red?style=social&logo=twitter" height=25></a>
&nbsp;
<a href="https://medium.com/@affineworkos"><img src="https://img.shields.io/badge/-Medium-red?style=social&logo=medium" height=25></a>
</p>
<br />
<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 and Support Us
@@ -117,7 +123,7 @@ Please view the path Contribute-to-AFFiNE/Software-Contributions/Quick-Start in
# Roadmap
Coming Soon...
Yes! Permanent storage, collaboration, stable release is planned! Check it [here](https://github.com/toeverything/AFFiNE/issues/293)
# Releases
@@ -218,6 +224,16 @@ For help, discussion about best practices, or any other conversation that would
<tr>
<td align="center"><a href="https://github.com/westongraham"><img src="https://avatars.githubusercontent.com/u/89493023?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Weston Graham</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=westongraham" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/pointmax"><img src="https://avatars.githubusercontent.com/u/49361135?v=4?s=50" width="50px;" alt=""/><br /><sub><b>pointmax</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=pointmax" title="Documentation">📖</a></td>
<td align="center"><a href="https://liby.github.io/notes"><img src="https://avatars.githubusercontent.com/u/38807139?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Bryan Lee</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=liby" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/chenmoonmo"><img src="https://avatars.githubusercontent.com/u/36295999?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Simon Li</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=chenmoonmo" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/githbq"><img src="https://avatars.githubusercontent.com/u/10009709?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Bob Hu</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=githbq" title="Code">💻</a></td>
<td align="center"><a href="https://quavo.vercel.app/"><img src="https://avatars.githubusercontent.com/u/67266933?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Quavo</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=lucky-chap" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/LuciNyan"><img src="https://avatars.githubusercontent.com/u/22126563?v=4?s=50" width="50px;" alt=""/><br /><sub><b>子瞻 Luci</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=LuciNyan" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="http://blog.ipili.me/"><img src="https://avatars.githubusercontent.com/u/4948120?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Horus</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=m1911star" title="Code">💻</a> <a href="#platform-m1911star" title="Packaging/porting to new platform">📦</a></td>
<td align="center"><a href="https://segmentfault.com/u/qzuser_584786517d31a"><img src="https://avatars.githubusercontent.com/u/15103283?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Super.x</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=fanshyiis" title="Code">💻</a></td>
<td align="center"><a href="https://wangyu-1999.github.io/"><img src="https://avatars.githubusercontent.com/u/80874770?v=4?s=50" width="50px;" alt=""/><br /><sub><b>Wang Yu</b></sub></a><br /><a href="https://github.com/toeverything/AFFiNE/commits?author=wangyu-1999" title="Code">💻</a></td>
</tr>
</table>
+1 -1
View File
@@ -7,8 +7,8 @@ import firebaseAuth = require('firebase-admin/auth');
import LRUCache = require('lru-cache');
import nanoid = require('nanoid');
import { handleConnection } from './utils';
import { URL } from 'url';
import { handleConnection } from './utils';
if (process.env.NODE_ENV !== 'development') {
firebaseApp.initializeApp({
@@ -1,10 +1,10 @@
import { getTitle, getBoard } from '../support/app.po';
import { getBoard, getTitle } from '../support/app.po';
describe('ligo-virgo', () => {
beforeEach(() => cy.visit('/'));
it('basic load check', () => {
getTitle().contains('👋 Get Started with AFFINE');
getTitle().contains('👋 Get Started with AFFiNE');
cy.get('.block_container').contains('The Essentials');
+4 -4
View File
@@ -12,14 +12,14 @@
declare namespace Cypress {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface Chainable<Subject> {
login(email: string, password: string): void;
// login(email: string, password: string): void;
}
}
//
// -- This is a parent command --
Cypress.Commands.add('login', (email, password) => {
console.log('Custom command example: Login', email, password);
});
// Cypress.Commands.add('login', (email, password) => {
// console.log('Custom command example: Login', email, password);
// });
//
// -- This is a child command --
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
-1
View File
@@ -1,4 +1,3 @@
/* eslint-disable filename-rules/match */
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
+14 -11
View File
@@ -1,11 +1,20 @@
import { Outlet } from 'react-router-dom';
import { css, Global } from '@emotion/react';
import { LayoutHeader, SettingsSidebar } from '@toeverything/components/layout';
import { styled } from '@toeverything/components/ui';
import { SettingsSidebar, LayoutHeader } from '@toeverything/components/layout';
import { Outlet } from 'react-router-dom';
export function LigoVirgoRootContainer() {
return (
<StyledRootContainer id="idAppRoot">
<>
<Global
styles={css`
#root {
display: flex;
flex-direction: row;
height: 100vh;
}
`}
/>
<StyledContentContainer>
<LayoutHeader />
<StyledMainContainer>
@@ -13,7 +22,7 @@ export function LigoVirgoRootContainer() {
</StyledMainContainer>
</StyledContentContainer>
<SettingsSidebar />
</StyledRootContainer>
</>
);
}
@@ -23,12 +32,6 @@ const StyledMainContainer = styled('div')({
overflowY: 'hidden',
});
const StyledRootContainer = styled('div')({
display: 'flex',
flexDirection: 'row',
height: '100vh',
});
const StyledContentContainer = styled('div')({
flex: 'auto',
display: 'flex',
+6 -6
View File
@@ -1,20 +1,20 @@
import { Routes, Route, Navigate } from 'react-router-dom';
import { Navigate, Route, Routes } from 'react-router-dom';
import { Login } from './account';
import Agenda from './agenda';
import { WorkspaceContainer } from './workspace';
import { LigoVirgoRootContainer } from './AppContainer';
import Recent from './recent';
import { RoutePrivate } from './RoutePrivate';
import { RoutePublicAutoLogin } from './RoutePublicAutoLogin';
import Search from './search';
import Settings from './settings';
import Shared from './shared';
import Starred from './starred';
import { Login } from './account';
import { PageNotFound } from './status/page-not-found';
import { WorkspaceNotFound } from './status/workspace-not-found';
import { RoutePrivate } from './RoutePrivate';
import { RoutePublicAutoLogin } from './RoutePublicAutoLogin';
import { Tools } from './tools';
import { LigoVirgoRootContainer } from './AppContainer';
import { UIPage } from './ui';
import { WorkspaceContainer } from './workspace';
export function LigoVirgoRoutes() {
return (
+1 -1
View File
@@ -1,6 +1,6 @@
import { Navigate, useLocation } from 'react-router-dom';
import { PageLoading, Error } from '@toeverything/components/account';
import { PageLoading } from '@toeverything/components/account';
import { useUserAndSpaces } from '@toeverything/datasource/state';
export type RoutePrivateProps = {
+3 -3
View File
@@ -1,10 +1,10 @@
import { Routes, Route } from 'react-router-dom';
import { Route, Routes } from 'react-router-dom';
import Container from './container';
import Calendar from './calendar';
import Container from './container';
import Home from './home';
import Tasks from './tasks';
import Today from './today';
import Home from './home';
export default function AgendaContainer() {
return (
@@ -1,6 +1,6 @@
import { type ComponentType, useRef } from 'react';
import * as uiIcons from '@toeverything/components/icons';
import { message, styled } from '@toeverything/components/ui';
import { useRef, type ComponentType } from 'react';
import { copy } from './copy';
const IconBooth = ({
+1 -1
View File
@@ -1,4 +1,4 @@
import { Routes, Route } from 'react-router-dom';
import { Route, Routes } from 'react-router-dom';
import { Container } from './container';
import { Icons } from './icons';
-2
View File
@@ -1,5 +1,3 @@
import React from 'react';
export const UIPage = () => {
return (
<div className="">
@@ -2,18 +2,18 @@ import { memo, useEffect } from 'react';
import { useParams } from 'react-router';
import { AffineBoard } from '@toeverything/components/affine-board';
import { useUserAndSpaces } from '@toeverything/datasource/state';
import { services } from '@toeverything/datasource/db-service';
import { useUserAndSpaces } from '@toeverything/datasource/state';
const MemoAffineBoard = memo(AffineBoard, (prev, next) => {
return prev.rootBlockId === next.rootBlockId;
});
type WhiteboardProps = {
type EdgelessProps = {
workspace: string;
};
export const Whiteboard = (props: WhiteboardProps) => {
export const Edgeless = (props: EdgelessProps) => {
const { page_id } = useParams();
const { user } = useUserAndSpaces();
+8 -4
View File
@@ -1,7 +1,7 @@
import { services, TemplateFactory } from '@toeverything/datasource/db-service';
import { useUserAndSpaces } from '@toeverything/datasource/state';
import { useEffect } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { useUserAndSpaces } from '@toeverything/datasource/state';
import { services, TemplateFactory } from '@toeverything/datasource/db-service';
export function WorkspaceHome() {
const navigate = useNavigate();
@@ -22,8 +22,12 @@ export function WorkspaceHome() {
workspace_id,
user_initial_page_id,
TemplateFactory.generatePageTemplateByGroupKeys({
name: '👋 Get Started with AFFINE',
groupKeys: ['getStartedGroup0', 'getStartedGroup1'],
name: '👋 Get Started with AFFiNE',
groupKeys: [
'getStartedGroup0',
'getStartedGroup1',
'getStartedGroup2',
],
})
);
}
@@ -1,14 +1,13 @@
/* eslint-disable filename-rules/match */
import { Routes, Route, useParams, Navigate } from 'react-router';
import { Route, Routes, useParams } from 'react-router';
import { useUserAndSpaces } from '@toeverything/datasource/state';
import { WorkspaceRootContainer } from './Container';
import { Page } from './docs';
import { Edgeless } from './Edgeless';
import { WorkspaceHome } from './Home';
import Labels from './labels';
import Pages from './pages';
import { Whiteboard } from './Whiteboard';
export function WorkspaceContainer() {
const { workspace_id } = useParams();
@@ -27,8 +26,8 @@ export function WorkspaceContainer() {
<Route path="/labels" element={<Labels />} />
<Route path="/pages" element={<Pages />} />
<Route
path="/:page_id/whiteboard"
element={<Whiteboard workspace={workspace_id} />}
path="/:page_id/edgeless"
element={<Edgeless workspace={workspace_id} />}
/>
<Route
path="/:page_id"
@@ -1,29 +1,28 @@
/* eslint-disable filename-rules/match */
import { useEffect, useRef, type UIEvent, useState } from 'react';
import { useEffect, useRef, useState, type UIEvent } from 'react';
import { useParams } from 'react-router';
import { AffineEditor } from '@toeverything/components/affine-editor';
import {
CalendarHeatmap,
PageTree,
Activities,
} from '@toeverything/components/layout';
import { CollapsibleTitle } from '@toeverything/components/common';
import {
useShowSpaceSidebar,
usePageClientWidth,
} from '@toeverything/datasource/state';
Activities,
CalendarHeatmap,
PageTree,
} from '@toeverything/components/layout';
import {
MuiBox as Box,
MuiCircularProgress as CircularProgress,
styled,
} from '@toeverything/components/ui';
import {
usePageClientWidth,
useShowSpaceSidebar,
} from '@toeverything/datasource/state';
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 { useFlag } from '@toeverything/datasource/feature-flags';
import { CollapsiblePageTree } from './collapsible-page-tree';
import { Tabs } from './components/tabs';
import { WorkspaceName } from './workspace-name';
type PageProps = {
workspace: string;
};
@@ -1,8 +1,4 @@
import {
AddIcon,
ArrowDropDownIcon,
ArrowRightIcon,
} from '@toeverything/components/icons';
import { AddIcon } from '@toeverything/components/icons';
import {
useCalendarHeatmap,
usePageTree,
@@ -1,6 +1,6 @@
import { useState } from 'react';
import { styled } from '@toeverything/components/ui';
import type { ValueOf } from '@toeverything/utils';
import { useState } from 'react';
const StyledTabs = styled('div')(({ theme }) => {
return {
@@ -1,4 +1,3 @@
/* eslint-disable filename-rules/match */
import { render } from '@testing-library/react';
import { Page } from './index';
@@ -1,17 +1,17 @@
import { styled, Input } from '@toeverything/components/ui';
import { PinIcon } from '@toeverything/components/icons';
import { Input, styled } from '@toeverything/components/ui';
import { services } from '@toeverything/datasource/db-service';
import {
useUserAndSpaces,
useShowSpaceSidebar,
useUserAndSpaces,
} from '@toeverything/datasource/state';
import React, {
import {
ChangeEvent,
KeyboardEvent,
useCallback,
useEffect,
useState,
} from 'react';
import { services } from '@toeverything/datasource/db-service';
import { Logo } from './components/logo/Logo';
const WorkspaceContainer = styled('div')({
+2 -2
View File
@@ -73,9 +73,9 @@ module.exports = function (webpackConfig) {
priority: -5,
chunks: 'all',
},
whiteboard: {
edgeless: {
test: /(libs\/components\/board-|[\\/]node_modules[\\/]@tldraw)/,
name: 'whiteboard',
name: 'edgeless',
priority: -7,
chunks: 'all',
},
+5 -3
View File
@@ -7,16 +7,18 @@
"keywords": [],
"author": "DarkSky <darksky2048@gmail.com>",
"dependencies": {
"@mui/joy": "^5.0.0-alpha.39",
"@emotion/react": "^11.10.0",
"@emotion/styled": "^11.10.0",
"lozad": "^1.16.0"
"@mui/joy": "^5.0.0-alpha.39",
"i18next": "^21.9.1",
"lozad": "^1.16.0",
"react-i18next": "^11.18.4"
},
"devDependencies": {
"mini-css-extract-plugin": "^2.6.1",
"image-minimizer-webpack-plugin": "^3.2.3",
"imagemin": "^8.0.1",
"imagemin-optipng": "^8.0.0",
"mini-css-extract-plugin": "^2.6.1",
"webpack": "^5.74.0"
}
}
+25
View File
@@ -0,0 +1,25 @@
import i18next from 'i18next';
import { initReactI18next } from 'react-i18next';
import en_US from './resources/en.json';
import zh_CN from './resources/zh.json';
const resources = {
en: en_US,
zh: zh_CN,
} as const;
i18next.use(initReactI18next).init({
lng: 'en',
fallbackLng: 'en',
resources,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
});
export const options = [
{ value: 'en', text: 'English' },
{ value: 'zh', text: '简体中文' },
];
export { i18next };
+34
View File
@@ -0,0 +1,34 @@
{
"translation": {
"Open Source": "Open Source",
"Privacy First": "Privacy First",
"Alternative": "Alternative",
"Check GitHub": "Check GitHub",
"Try it Online": "Try it Online",
"description1": {
"part1": "Affine is the next-generation collaborative knowledge base for professionals.",
"part2": "It's not just a collection of Docs, whiteboard, and tables.",
"part3": "Transform any building block as you like.",
"part4": "Say goodbye to redundancy. Store your data once, and keep your data as you like it."
},
"description2": {
"part1": "Shape Your Page",
"part2": "Docs, Kanbans, and Databases are all fully functional anywhere, anytime. A truly what-you-see-is-what-you-get environment for your data.",
"part3": "All pages come with a document (Paper Mode) and whiteboard (Edgeless Mode) view."
},
"description3": {
"part1": "Plan Your Task",
"part2": "No more chaos managing multiple views.",
"part3": "Set a TODO with Markdown, and seamlessly edit it within a Kanban.",
"part4": "Managing multi-dimensional tables should be this simple - and now it is."
},
"description4": {
"part1": "Privacy-first, and collaborative. No compromises whatsoever.",
"part2": "We don't like being locked-in, and neither should you. Privacy is at the foundation of everything we do, but it should not limit us that's why there are no compromises.",
"part3": "Your data is yours;it is always locally stored and secured - available to you always. While still being able to enjoy collaboration features such as real-time editing and sharing with others, without any cloud setup."
},
"BuildFor": "Build for an open and semantic future",
"KeepUpdated": "Keep Updated on",
"Join": "Join Our Community"
}
}
+34
View File
@@ -0,0 +1,34 @@
{
"translation": {
"Open Source": "开源",
"Privacy First": "隐私第一",
"Alternative": "的另一种选择",
"Check GitHub": "GitHub中查看",
"Try it Online": "在线上试试",
"description1": {
"part1": "Affine是面向专业人士的下一代协同知识库",
"part2": "它不仅仅是一个文档、白板和表格的集合。",
"part3": "可以根据需要转换任何构建块。",
"part4": "向冗余说再见吧。将数据存储一次,并保留您喜欢的数据。"
},
"description2": {
"part1": "塑造您的页面",
"part2": "文档、看板和数据库在任何地方、任何时候都是完全可用的,一个真正的“所见即所得”的数据环境。",
"part3": "所有页面都有一个文档(纸张模式)和白板(无边缘模式)视图。"
},
"description3": {
"part1": "计划您的任务",
"part2": "不再混乱地管理多个视图。",
"part3": "使用 Markdown 设置 TODO,并在看板中无缝地编辑它。",
"part4": "管理多维表格本应该就这么简单——现在的它就是这么简单。"
},
"description4": {
"part1": "隐私第一,合作无间,绝不妥协。",
"part2": "我们不喜欢被关起来,您也不应该。隐私是我们做任何事情的基础,但它不应该限制我们,这就是为什么我们决不妥协。",
"part3": "您的数据是您自己的,它总是安全的在本地存储并随时供您使用。您不需要任何云设置就可以享受协作功能,即时编辑和与他人共享。"
},
"BuildFor": "构建一个开放和语义化的未来",
"KeepUpdated": "持续更新在",
"Join": "加入我们的社区"
}
}
+43 -46
View File
@@ -1,20 +1,22 @@
/* eslint-disable max-lines */
/* eslint-disable @typescript-eslint/naming-convention */
/* eslint-disable filename-rules/match */
import { useEffect, useMemo, useState } from 'react';
import clsx from 'clsx';
import { useEffect, useMemo, useState } from 'react';
import { CssVarsProvider, styled } from '@mui/joy/styles';
import { Box, Button, Container, Grid, SvgIcon, Typography } from '@mui/joy';
import GitHubIcon from '@mui/icons-material/GitHub';
import RedditIcon from '@mui/icons-material/Reddit';
import TelegramIcon from '@mui/icons-material/Telegram';
import { Box, Button, Container, Grid, SvgIcon, Typography } from '@mui/joy';
import Option from '@mui/joy/Option';
import Select from '@mui/joy/Select';
import { CssVarsProvider, styled } from '@mui/joy/styles';
import { LogoIcon } from '@toeverything/components/icons';
// eslint-disable-next-line no-restricted-imports
import { useMediaQuery } from '@mui/material';
import LogoImage from './logo.png';
import { useTranslation } from 'react-i18next';
import CollaborationImage from './collaboration.png';
import { options } from './i18n';
import LogoImage from './logo.png';
import PageImage from './page.png';
import ShapeImage from './shape.png';
import TaskImage from './task.png';
@@ -200,6 +202,7 @@ const AffineImage = styled('img')({
const GitHub = (props: { center?: boolean; flat?: boolean }) => {
const matches = useMediaQuery('(max-width: 1024px)');
const { t } = useTranslation();
return (
<Button
@@ -235,14 +238,14 @@ const GitHub = (props: { center?: boolean; flat?: boolean }) => {
startIcon={<GitHubIcon />}
size="lg"
>
{props.center ? 'Check ' : ''}GitHub
{props.center ? t('Check GitHub') : t('GitHub')}
</Button>
);
};
const AFFiNEOnline = (props: { center?: boolean; flat?: boolean }) => {
const matches = useMediaQuery('(max-width: 1024px)');
const { t } = useTranslation();
return (
<Button
onClick={() => {
@@ -278,14 +281,18 @@ const AFFiNEOnline = (props: { center?: boolean; flat?: boolean }) => {
startIcon={<LogoIcon />}
size="lg"
>
Try it Online
{t('Try it Online')}
</Button>
);
};
export function App() {
const matches = useMediaQuery('(max-width: 1024px)');
const { t, i18n } = useTranslation();
const changeLanguage = (event: any) => {
i18n.changeLanguage(event);
};
return (
<CssVarsProvider>
<VenusContainer
@@ -343,6 +350,13 @@ export function App() {
>
Blog
</Button>
<Select defaultValue="en" onChange={changeLanguage}>
{options.map(option => (
<Option key={option.value} value={option.value}>
{option.text}
</Option>
))}
</Select>
</Grid>
</Grid>
<Grid
@@ -370,7 +384,7 @@ export function App() {
},
}}
>
Open Source,
{t('Open Source')},
</Typography>
<Typography
fontSize="96px"
@@ -381,7 +395,7 @@ export function App() {
},
}}
>
Privacy First
{t('Privacy First')}
</Typography>
</Box>
</Grid>
@@ -414,7 +428,7 @@ export function App() {
},
}}
>
Alternative
{t('Alternative')}
</Typography>
</Box>
</Grid>
@@ -433,8 +447,7 @@ export function App() {
fontWeight={'400'}
sx={{ color: '#888' }}
>
Affine is the next-generation collaborative
knowledge base for professionals.
{t('description1.part1')}
</Typography>
</Box>
</Grid>
@@ -489,8 +502,7 @@ export function App() {
level={matches ? 'h2' : 'h1'}
fontWeight={'bold'}
>
Its not just a collection of Docs, whiteboard, and
tables.
{t('description1.part2')}
</Typography>
</Box>
</Grid>
@@ -507,11 +519,10 @@ export function App() {
}}
>
<Typography fontSize="1.2em">
Transform any building block as you like.
{t('description1.part3')}
</Typography>
<Typography fontSize="1.2em">
Say goodbye to redundancy. Store your data once, and
keep your data as you like it.
{t('description1.part4')}
</Typography>
</Box>
</Grid>
@@ -548,23 +559,19 @@ export function App() {
fontWeight={'bold'}
style={{ marginBottom: '0.5em' }}
>
Shape Your Page
{t('description2.part1')}
</Typography>
<Typography
fontSize="1.2em"
style={{ marginBottom: '0.25em' }}
>
Docs, Kanbans, and Databases are all fully
functional anywhere, anytime. A truly
what-you-see-is-what-you-get environment for
your data.
{t('description2.part2')}
</Typography>
<Typography
fontSize="1.2em"
style={{ marginBottom: '0.25em' }}
>
All pages come with a document (Paper Mode) and
whiteboard (Edgeless Mode) view.
{t('description2.part3')}
</Typography>
</Box>
</Grid>
@@ -626,27 +633,25 @@ export function App() {
fontWeight={'bold'}
style={{ marginBottom: '0.5em' }}
>
Plan Your Task
{t('description3.part1')}
</Typography>
<Typography
fontSize="1.2em"
style={{ marginBottom: '0.25em' }}
>
No more chaos managing multiple views.
{t('description3.part2')}
</Typography>
<Typography
fontSize="1.2em"
style={{ marginBottom: '0.25em' }}
>
Set a TODO with Markdown, and seamlessly edit it
within a Kanban.
{t('description3.part3')}
</Typography>
<Typography
fontSize="1.2em"
style={{ marginBottom: '0.25em' }}
>
Managing multi-dimensional tables should be this
simple and now it is.
{t('description3.part4')}
</Typography>
</Box>
</Grid>
@@ -698,27 +703,19 @@ export function App() {
fontWeight={'bold'}
style={{ marginBottom: '0.5em' }}
>
Privacy-first, and collaborative. No compromises
whatsoever.
{t('description4.part1')}
</Typography>
<Typography
fontSize="1.2em"
style={{ marginBottom: '0.25em' }}
>
We dont like being locked-in, and neither should
you. Privacy is at the foundation of everything we
do, but it should not limit us thats+ why there are
no compromises.
{t('description4.part2')}
</Typography>
<Typography
fontSize="1.2em"
style={{ marginBottom: '0.25em' }}
>
Your data is yours; it is always locally stored and
secured - available to you always. While still being
able enjoy collaboration features such as real-time
editing and sharing with others, without any cloud
setup.
{t('description4.part3')}
</Typography>
</Box>
</Grid>
@@ -763,7 +760,7 @@ export function App() {
}}
>
<Typography fontSize={'1.5em'}>
Build for an open and semantic future
{t('BuildFor')}
</Typography>
</Box>
</Grid>
@@ -779,7 +776,7 @@ export function App() {
>
<Typography level="h3" sx={{ display: 'flex' }}>
<span style={{ alignSelf: 'center' }}>
Keep Updated on
{t('KeepUpdated')}
</span>
<GitHub />
</Typography>
@@ -796,7 +793,7 @@ export function App() {
}}
>
<Typography level="h2" sx={{ display: 'flex' }}>
Join Our Community
{t('Join')}
</Typography>
</Box>
</Grid>
+1 -1
View File
@@ -1,7 +1,7 @@
/* eslint-disable filename-rules/match */
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import './app/i18n';
import App from './app';
-13
View File
@@ -1,13 +0,0 @@
# AFFiNE CONTRIBUTING
Contributions are **welcome** and will be fully **credited**.
## **Requirements**
If the project maintainer has any additional requirements, you will find them listed here.
- Code Style [AFFiNE Code Guideline](./affine-code-guideline.md)
- Git Rules [AFFiNE Git Guideline ](./affine-git-guideline.md)
- • **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.
**Happy coding**!
-22
View File
@@ -1,22 +0,0 @@
# AFFiNE Code Guideline
| Item | Specification | Example |
| ----------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------- |
| [Packages/Paths]() | aaa-bbb-ccc | ligo-virgo, editor-todo |
| [.tsx]() | PascalCase | AddPage.tsx |
| [.ts]() | kebab-case | file-export.ts |
| [.json]() | kebab-case | file-export.ts |
| [Domain File]() | OpenRules | xx._d.ts_ &#124; _tsconfig.xx_.json &#124; xx.spec .ts &#124; .env.xx &#124; yy-ds.ts |
| [Types]() | UpperCamelCase | WebEvent |
| [Enum variants]() | UpperCamelCase | Status{ Todo,Completed } |
| [Functions]() | lowerCamelCase | |
| [React Funciton Compoment]() | UpperCamelCase | function DocShare(){} |
| [React HOC]() | UpperCamelCase | function BussinessText(){} |
| [Function Parameter]() | lowerCamelCase | function searchByIdOrName(idOrname){ } |
| [Methods for external access]() | lowerCamelCase | public sayHello(){ }; |
| [Externally Accessible Variables (Variables)]() | lowerCamelCase | animal.sleepCount |
| [General constructors]() | constructor or with_more_details | |
| [Local variables]() | lowerCamelCase | const tableCollection = []; |
| [Statics]() | SCREAMING_SNAKE_CASE | GLOBAL_MESSAGES |
| [Constants](b) | SCREAMING_SNAKE_CASE | GLOBAL_CONFIG |
| [Type parameters]() | UpperCamelCase , usually a single capital letter: T | let a: Animal = new Animal() |
-91
View File
@@ -1,91 +0,0 @@
# AFFiNE Git Guideline
# 1. Git Branch Name
- fix/
- feat/
# 2. **Commit message guidelines**
AFFiNE uses [semantic-release](https://github.com/semantic-release/semantic-release) for automated version management and package publishing. For that to work, commitmessages need to be in the right format.
### **Atomic commits**
If possible, make [atomic commits](https://en.wikipedia.org/wiki/Atomic_commit), which means:
- a commit should contain exactly one self-contained functional change
- a functional change should be contained in exactly one commit
- a commit should not create an inconsistent state (such as test errors, linting errors, partial fix, feature with documentation etc...)
A complex feature can be broken down into multiple commits as long as each one keep a consistent state and consist of a self-contained change.
### **Commit message format**
Each commit message consists of a **header**, a **body** and a **footer**. The header has a special format that includes a **type**, a **scope** and a **subject**:
`<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>`
The **header** is mandatory and the **scope** of the header is optional.
The **footer** can contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages).
### **Revert**
If the commit reverts a previous commit, it should begin with `revert:` , followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
### **Type**
The type must be one of the following:
| Type | Description |
| ----------- | ----------- |
| build | Changes that affect the build system or external | dependencies (example scopes: gulp, broccoli, npm) |
| ci | Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs) |
| docs | Documentation only changes |
| feat | A new feature |
| fix | A bug fix |
| perf | A code change that improves performance |
| refactor | A code change that neither fixes a bug nor adds a feature |
| style | Changes that do not affect the meaning of the code(white-space, formatting, missing semi-colons, etc) |
| test | Adding missing tests or correcting existing tests |
| chore | Changes to the build process or auxiliary tools | |
### **Subject**
The subject contains succinct description of the change:
- use the imperative, present tense: "change" not "changed" nor "changes"
- don't capitalize first letter
- no dot (.) at the end
### **Body**
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes". The body should include the motivation for the change and contrast this with previous behavior.
### **Footer**
The footer should contain any information about **Breaking Changes** and is also the place to reference GitHub issues that this commit **Closes**.
**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
### **Examples**
`fix(pencil): stop graphite breaking when too much pressure applied`
``feat(pencil): add 'graphiteWidth' option`
Fix #42`
`perf(pencil): remove graphiteWidth option`
BREAKING CHANGE: The graphiteWidth option has been removed.
The default graphite width of 10mm is always used for performance reasons.`
# 3. tracking-your-work-with-issues
[https://docs.github.com/en/issues/tracking-your-work-with-issues/about-issues](https://docs.github.com/en/issues/tracking-your-work-with-issues/about-issues)
-7
View File
@@ -1,7 +0,0 @@
## AFFiNE Icons
> Abundant and colorful icon resource for free free use
Website: [http://localhost:4200/tools/icons](http://localhost:4200/tools/icons)
Figma: [Figma AFFiNE Icons](https://www.figma.com/file/7pyx5gMz6CN0qSRADmScQ7/AFFINE?node-id=665%3A1734)
-53
View File
@@ -1,53 +0,0 @@
# Tutorial
AFFiNE defines a new component development specification in Figma, extends AFFiNE UI Components based on MUI BASE and MUI SYSTEM, and supplements as needed https://github.com/toeverything/AFFiNE/tree/master/libs/components/ui , eg `src/libs/components/ui/src/button/base-button.ts`
```jsx
import ButtonUnstyled, {
buttonUnstyledClasses,
} from '@mui/base/ButtonUnstyled';
import { styled } from '../styled';
/* eslint-disable @typescript-eslint/naming-convention */
const blue = {
500: '#007FFF',
600: '#0072E5',
700: '#0059B2',
};
/* eslint-enable @typescript-eslint/naming-convention */
export const BaseButton = styled(ButtonUnstyled)`
font-family: IBM Plex Sans, sans-serif;
font-weight: bold;
font-size: 0.875rem;
background-color: ${blue[500]};
border-radius: 8px;
padding: 4px 8px;
color: white;
transition: all 150ms ease;
cursor: pointer;
border: none;
&:hover {
background-color: ${blue[600]};
}
&.${buttonUnstyledClasses.active} {
background-color: ${blue[700]};
}
&.${buttonUnstyledClasses.focusVisible} {
box-shadow: 0 4px 20px 0 rgba(61, 71, 82, 0.1), 0 0 0 5px rgba(0, 127, 255, 0.5);
outline: none;
}
&.${buttonUnstyledClasses.disabled} {
opacity: 0.5;
cursor: not-allowed;
}
`;
```
```jsx
export { BaseButton } from './base-button';
```
@@ -1,13 +0,0 @@
Create or edit the `.env.local` file in the project root directory, add `FIGMA_TOKEN`, you can refer to Generate ACCESS_TOKEN: https://www.figma.com/developers/api#access-tokens
```
FIGMA_TOKEN=your-figma-token
```
Execute the command `nx run components-icons:figmaRes` to synchronize figma resources
figma icon resource address: https://www.figma.com/file/7pyx5gMz6CN0qSRADmScQ7/AFFINE?node-id=665%3A1734
### Icon Supplementary Style
Some icons downloaded directly have incorrect styles. You can add supplementary styles in `tools/executors/figmaRes/patch-styles.js`. The key is the name of the icon kebab-case.
-3
View File
@@ -1,3 +0,0 @@
## Single Component Rollup Config
If styles are used in a Component, `rollupConfig` under `project.json` needs to be modified to `libs/rollup.config.cjs`
-103
View File
@@ -1,103 +0,0 @@
[toc]
# Tutorial
1. MUI styled
```jsx
import type { MouseEventHandler, ReactNode } from 'react';
import { styled } from '@toeverything/components/ui';
const CardContainer = styled('div')({
display: 'flex',
flexDirection: 'column',
backgroundColor: '#fff',
border: '1px solid #E2E7ED',
borderRadius: '5px',
});
const CardContent = styled('div')({
margin: '23px 52px 24px 19px',
});
const CardActions = styled('div')({
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
width: '100%',
height: '29px',
background: 'rgba(152, 172, 189, 0.1)',
borderRadius: '0px 0px 5px 5px',
padding: '6px 0 6px 19px',
fontSize: '12px',
fontWeight: '300',
color: '#98ACBD',
});
const PlusIcon = styled('div')({
marginRight: '9px',
fontWeight: '500',
lineHeight: 0,
'::before': {
content: '"+"',
},
});
export const Card = ({
children,
onAddItem,
}: {
children?: ReactNode,
onAddItem?: MouseEventHandler<HTMLDivElement>,
}) => {
return (
<CardContainer>
<CardContent>{children}</CardContent>
<CardActions onClick={onAddItem}>
<PlusIcon />
Add item
</CardActions>
</CardContainer>
);
};
```
## 2. import `*.scss`
```jsx
import styles from './tree-item.module.scss';
export const TreeItem = forwardRef<HTMLDivElement, TreeItemProps>(
() => {
return (
<li
ref={wrapperRef}
className={cx(
styles['Wrapper'],
clone && styles['clone'],
ghost && styles['ghost'],
indicator && styles['indicator']
)}
style={
{
'--spacing': `${indentationWidth * depth}px`
} as CSSProperties
}
{...props}
>
</li>
);
}
);
```
## 3. import `*.css`,
```js
import 'codemirror/lib/codemirror.css';
import 'codemirror/lib/codemirror';
```
+1 -4
View File
@@ -1,10 +1,7 @@
import { Link } from 'react-router-dom';
import style9 from 'style9';
import { getAuth, signOut } from 'firebase/auth';
import { MuiBox as Box } from '@toeverything/components/ui';
import { keyframes } from '@emotion/react';
import { LOGOUT_LOCAL_STORAGE, LOGOUT_COOKIES } from '@toeverything/utils';
import { MuiBox as Box } from '@toeverything/components/ui';
const styles = style9.create({
container: {
+1 -1
View File
@@ -1,3 +1,3 @@
export { Login } from './login';
export { Error } from './error';
export { PageLoading } from './loading';
export { Login } from './login';
+1 -1
View File
@@ -1,5 +1,5 @@
import style9 from 'style9';
import { MuiCircularProgress as CircularProgress } from '@toeverything/components/ui';
import style9 from 'style9';
const styles = style9.create({
container: {
@@ -1,5 +1,4 @@
/* eslint-disable filename-rules/match */
import { useEffect, useMemo } from 'react';
import { useMemo } from 'react';
import { LogoIcon } from '@toeverything/components/icons';
import { MuiButton } from '@toeverything/components/ui';
@@ -1,12 +1,11 @@
/* eslint-disable filename-rules/match */
import { useCallback, useMemo } from 'react';
import { initializeApp } from 'firebase/app';
import {
GoogleAuthProvider,
getAuth,
signInWithPopup,
browserLocalPersistence,
getAuth,
GoogleAuthProvider,
signInWithPopup,
} from 'firebase/auth';
import { useCallback, useMemo } from 'react';
import { MuiButton } from '@toeverything/components/ui';
@@ -1,78 +0,0 @@
import { useCallback, useEffect, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import style9 from 'style9';
import { MuiBox as Box } from '@toeverything/components/ui';
import { Guard as AuthingGuard } from '@authing/react-ui-components';
import type { User as AuthingUser } from 'authing-js-sdk';
import {
AUTHING_APP_ID_US,
AUTHING_APP_ID_CN,
AUTHING_APP_HOST_US,
} from '@toeverything/utils';
import { useUserAndSpaces } from '@toeverything/datasource/state';
import '@authing/react-ui-components/lib/index.min.css';
export function Authing() {
const navigate = useNavigate();
const { handleUserLoginSuccess, currentSpaceId } = useUserAndSpaces();
/**
* Refer to Authing documentation https://docs.authing.cn/v2/reference/guard/react.html
*/
const handle_login_success = useCallback(
async (authingUser: AuthingUser) => {
handleUserLoginSuccess(authingUser);
},
[handleUserLoginSuccess]
);
const authing_config = useMemo(() => {
return {
host: AUTHING_APP_HOST_US,
};
}, []);
useEffect(() => {
if (currentSpaceId) {
const targetRoute = `/${currentSpaceId}`;
navigate(targetRoute);
}
}, [currentSpaceId, navigate]);
return (
<div className={styles('loginContainer')}>
<Box
sx={{
'.g2-view-header': {
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
},
'.authing-g2-render-module': {
boxShadow: '0 2px 10px 0 rgb(57 106 255 / 20%)',
},
}}
>
<AuthingGuard
onLogin={handle_login_success}
appId={AUTHING_APP_ID_US}
config={authing_config}
/>
<Box sx={{ height: '56px' }} />
</Box>
</div>
);
}
const styles = style9.create({
loginContainer: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
width: '100%',
height: 'calc( 100vh - 64px )',
},
});
+4 -5
View File
@@ -1,12 +1,11 @@
/* eslint-disable filename-rules/match */
import { useCallback, useState } from 'react';
import { LogoImg } from '@toeverything/components/common';
import { MuiBox, MuiGrid, MuiSnackbar } from '@toeverything/components/ui';
import { useCallback, useState } from 'react';
// import { Authing } from './authing';
import { Firebase } from './firebase';
import { FileSystem } from './fs';
import { Error } from './../error';
// import { Authing } from './Authing';
import { FileSystem } from './FileSystem';
import { Firebase } from './Firebase';
export function Login() {
const [error, setError] = useState(false);
+78 -17
View File
@@ -1,24 +1,30 @@
import { useEffect, useState } from 'react';
import { Tldraw } from '@toeverything/components/board-draw';
import { tools } from '@toeverything/components/board-tools';
import { getSession } from '@toeverything/components/board-sessions';
import * as commands from '@toeverything/components/board-commands';
import { TldrawApp, deepCopy } from '@toeverything/components/board-state';
import { TDShapeType } from '@toeverything/components/board-types';
import { services } from '@toeverything/datasource/db-service';
import { useShapes } from './hooks';
import { RecastBlockProvider } from '@toeverything/components/editor-core';
import { createEditor } from '@toeverything/components/affine-editor';
import * as commands from '@toeverything/components/board-commands';
import { Tldraw } from '@toeverything/components/board-draw';
import { getSession } from '@toeverything/components/board-sessions';
import { deepCopy, TldrawApp } from '@toeverything/components/board-state';
import { tools } from '@toeverything/components/board-tools';
import { TDShapeType } from '@toeverything/components/board-types';
import {
getClipDataOfBlocksById,
RecastBlockProvider,
} from '@toeverything/components/editor-core';
import { services } from '@toeverything/datasource/db-service';
import { AsyncBlock, BlockEditor } from '@toeverything/framework/virgo';
import { useEffect, useState } from 'react';
import { useShapes } from './hooks';
interface AffineBoardProps {
workspace: string;
rootBlockId: string;
}
const AffineBoard = ({ workspace, rootBlockId }: AffineBoardProps) => {
const AffineBoard = ({
workspace,
rootBlockId,
editor,
}: AffineBoardProps & { editor: BlockEditor }) => {
const [app, set_app] = useState<TldrawApp>();
const [document] = useState(() => {
return {
...deepCopy(TldrawApp.default_document),
@@ -45,10 +51,10 @@ const AffineBoard = ({ workspace, rootBlockId }: AffineBoardProps) => {
};
});
const shapes = useShapes(workspace, rootBlockId);
const { shapes, bindings } = useShapes(workspace, rootBlockId);
useEffect(() => {
if (app) {
app.replacePageContent(shapes || {}, {}, {});
app.replacePageContent(shapes || {}, bindings, {});
}
}, [app, shapes]);
@@ -62,7 +68,19 @@ const AffineBoard = ({ workspace, rootBlockId }: AffineBoardProps) => {
onMount(app) {
set_app(app);
},
onChangePage(app, shapes, bindings, assets) {
async onCopy(e, groupIds) {
const clip = await getClipDataOfBlocksById(
editor,
groupIds
);
e.clipboardData?.setData(
clip.getMimeType(),
clip.getData()
);
},
async onChangePage(app, shapes, bindings, assets) {
Promise.all(
Object.entries(shapes).map(async ([id, shape]) => {
if (shape === undefined) {
@@ -91,7 +109,20 @@ const AffineBoard = ({ workspace, rootBlockId }: AffineBoardProps) => {
});
}
shape.affineId = block.id;
return services.api.editorBlock.update({
Object.keys(bindings).forEach(bilingKey => {
if (
bindings[bilingKey]?.fromId === shape.id
) {
bindings[bilingKey].fromId = block.id;
}
if (
bindings[bilingKey]?.toId === shape.id
) {
bindings[bilingKey].toId = block.id;
}
});
return await services.api.editorBlock.update({
workspace: shape.workspace,
id: block.id,
properties: {
@@ -103,6 +134,32 @@ const AffineBoard = ({ workspace, rootBlockId }: AffineBoardProps) => {
}
})
);
let pageBindingsString = (
await services.api.editorBlock.get({
workspace: workspace,
ids: [rootBlockId],
})
)?.[0].properties.bindings?.value;
console.log(123123123);
let pageBindings = JSON.parse(pageBindingsString ?? '{}');
console.log(pageBindings, 3333, bindings);
Object.keys(bindings).forEach(bindingsKey => {
console.log(345345345345345);
if (!bindings[bindingsKey]) {
delete pageBindings[bindingsKey];
} else {
Object.assign(pageBindings, bindings);
}
});
services.api.editorBlock.update({
workspace: workspace,
id: rootBlockId,
properties: {
bindings: {
value: JSON.stringify(pageBindings),
},
},
});
},
}}
/>
@@ -130,7 +187,11 @@ export const AffineBoardWitchContext = ({
}, [editor, rootBlockId]);
return page ? (
<RecastBlockProvider block={page}>
<AffineBoard workspace={workspace} rootBlockId={rootBlockId} />
<AffineBoard
workspace={workspace}
rootBlockId={rootBlockId}
editor={editor}
/>
</RecastBlockProvider>
) : null;
};
@@ -1,36 +1,60 @@
import { useEffect, useState } from 'react';
import { services } from '@toeverything/datasource/db-service';
import type { ReturnEditorBlock } from '@toeverything/datasource/db-service';
import type { TDShape } from '@toeverything/components/board-types';
import { Editor } from '@toeverything/components/board-shapes';
import type { TDShape } from '@toeverything/components/board-types';
import type { ReturnEditorBlock } from '@toeverything/datasource/db-service';
import { services } from '@toeverything/datasource/db-service';
import { usePageClientWidth } from '@toeverything/datasource/state';
import { useEffect, useState } from 'react';
const getBindings = (workspace: string, rootBlockId: string) => {
return services.api.editorBlock
.get({
workspace: workspace,
ids: [rootBlockId],
})
.then(blcoks => {
return blcoks[0].properties.bindings?.value;
});
};
export const useShapes = (workspace: string, rootBlockId: string) => {
const { pageClientWidth } = usePageClientWidth();
// page padding left and right total 300px
const editorShapeInitSize = pageClientWidth - 300;
const [blocks, setBlocks] = useState<ReturnEditorBlock[]>();
const [blocks, setBlocks] = useState<{
shapes: [ReturnEditorBlock[]];
bindings: string;
}>();
useEffect(() => {
services.api.editorBlock
.get({ workspace, ids: [rootBlockId] })
.then(async blockData => {
const shapes = await Promise.all(
(blockData?.[0]?.children || []).map(async childId => {
const childBlock = (
await services.api.editorBlock.get({
workspace,
ids: [childId],
})
)?.[0];
return childBlock;
})
);
setBlocks(shapes);
Promise.all([
services.api.editorBlock
.get({ workspace, ids: [rootBlockId] })
.then(async blockData => {
const shapes = await Promise.all(
(blockData?.[0]?.children || []).map(async childId => {
const childBlock = (
await services.api.editorBlock.get({
workspace,
ids: [childId],
})
)?.[0];
return childBlock;
})
);
return shapes;
}),
]).then(shapes => {
getBindings(workspace, rootBlockId).then(bindings => {
setBlocks({
shapes,
bindings: bindings,
});
});
});
let unobserve: () => void;
services.api.editorBlock
.observe({ workspace, id: rootBlockId }, async blockData => {
const shapes = await Promise.all(
Promise.all(
(blockData?.children || []).map(async childId => {
const childBlock = (
await services.api.editorBlock.get({
@@ -40,8 +64,14 @@ export const useShapes = (workspace: string, rootBlockId: string) => {
)?.[0];
return childBlock;
})
);
setBlocks(shapes);
).then(shapes => {
getBindings(workspace, rootBlockId).then(bindings => {
setBlocks({
shapes: [shapes],
bindings: bindings,
});
});
});
})
.then(cb => {
unobserve = cb;
@@ -53,8 +83,7 @@ export const useShapes = (workspace: string, rootBlockId: string) => {
}, [workspace, rootBlockId]);
let groupCount = 0;
return blocks?.reduce((acc, block) => {
let blocksShapes = blocks?.shapes[0]?.reduce((acc, block) => {
const shapeProps = block.properties.shapeProps?.value
? JSON.parse(block.properties.shapeProps.value)
: {};
@@ -75,4 +104,8 @@ export const useShapes = (workspace: string, rootBlockId: string) => {
return acc;
}, {} as Record<string, TDShape>);
return {
shapes: blocksShapes,
bindings: JSON.parse(blocks?.bindings ?? '{}'),
};
};
+8 -12
View File
@@ -1,8 +1,8 @@
import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react';
import {
RenderRoot,
RenderBlock,
RenderRoot,
type BlockEditor,
} from '@toeverything/components/editor-core';
import { useCurrentEditors } from '@toeverything/datasource/state';
@@ -17,7 +17,7 @@ interface AffineEditorProps {
*/
scrollBlank?: boolean;
isWhiteboard?: boolean;
isEdgeless?: boolean;
scrollContainer?: HTMLElement;
scrollController?: {
@@ -29,12 +29,12 @@ interface AffineEditorProps {
function _useConstantWithDispose(
workspace: string,
rootBlockId: string,
isWhiteboard: boolean
isEdgeless: boolean
) {
const ref = useRef<{ data: BlockEditor; onInit: boolean }>(null);
const { setCurrentEditors } = useCurrentEditors();
ref.current ??= {
data: createEditor(workspace, rootBlockId, isWhiteboard),
data: createEditor(workspace, rootBlockId, isEdgeless),
onInit: true,
};
@@ -42,18 +42,14 @@ function _useConstantWithDispose(
if (ref.current.onInit) {
ref.current.onInit = false;
} else {
ref.current.data = createEditor(
workspace,
rootBlockId,
isWhiteboard
);
ref.current.data = createEditor(workspace, rootBlockId, isEdgeless);
}
setCurrentEditors(prev => ({
...prev,
[rootBlockId]: ref.current.data,
}));
return () => ref.current.data.dispose();
}, [workspace, rootBlockId, isWhiteboard, setCurrentEditors]);
}, [workspace, rootBlockId, isEdgeless, setCurrentEditors]);
return ref.current.data;
}
@@ -64,7 +60,7 @@ export const AffineEditor = forwardRef<BlockEditor, AffineEditorProps>(
workspace,
rootBlockId,
scrollBlank = true,
isWhiteboard,
isEdgeless,
scrollController,
scrollContainer,
},
@@ -73,7 +69,7 @@ export const AffineEditor = forwardRef<BlockEditor, AffineEditorProps>(
const editor = _useConstantWithDispose(
workspace,
rootBlockId,
isWhiteboard
isEdgeless
);
useEffect(() => {
@@ -1,36 +1,36 @@
import { BlockEditor } from '@toeverything/framework/virgo';
import {
PageBlock,
RefLinkBlock,
TextBlock,
BulletBlock,
CalloutBlock,
CodeBlock,
DividerBlock,
EmbedLinkBlock,
FigmaBlock,
// TocBlock,
FileBlock,
GridBlock,
GridItemBlock,
GroupBlock,
GroupDividerBlock,
Heading1Block,
Heading2Block,
Heading3Block,
QuoteBlock,
TodoBlock,
CodeBlock,
// TocBlock,
FileBlock,
ImageBlock,
DividerBlock,
CalloutBlock,
YoutubeBlock,
FigmaBlock,
GroupBlock,
EmbedLinkBlock,
BulletBlock,
NumberedBlock,
GridBlock,
GridItemBlock,
GroupDividerBlock,
PageBlock,
QuoteBlock,
RefLinkBlock,
TextBlock,
TodoBlock,
YoutubeBlock,
} from '@toeverything/components/editor-blocks';
import { Protocol } from '@toeverything/datasource/db-service';
import { plugins } from '@toeverything/components/editor-plugins';
import { Protocol } from '@toeverything/datasource/db-service';
import { BlockEditor } from '@toeverything/framework/virgo';
export const createEditor = (
workspace: string,
rootBlockId: string,
isWhiteboard?: boolean
isEdgeless?: boolean
) => {
const blockEditor = new BlockEditor({
workspace,
@@ -61,7 +61,7 @@ export const createEditor = (
[Protocol.Block.Type.groupDivider]: new GroupDividerBlock(),
},
plugins,
isWhiteboard,
isEdgeless,
});
return blockEditor;
+1 -1
View File
@@ -1,2 +1,2 @@
export { AffineEditor } from './Editor';
export { createEditor } from './create-editor';
export { AffineEditor } from './Editor';
@@ -1,13 +1,13 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { Vec } from '@tldraw/vec';
import { Utils } from '@tldraw/core';
import { Vec } from '@tldraw/vec';
import type { TldrawApp } from '@toeverything/components/board-state';
import { TLDR } from '@toeverything/components/board-state';
import {
AlignType,
TldrawCommand,
TDShapeType,
TldrawCommand,
} from '@toeverything/components/board-types';
import { TLDR } from '@toeverything/components/board-state';
import type { TldrawApp } from '@toeverything/components/board-state';
export function alignShapes(
app: TldrawApp,
@@ -1,5 +1,5 @@
import type { TldrawCommand } from '@toeverything/components/board-types';
import type { TldrawApp } from '@toeverything/components/board-state';
import type { TldrawCommand } from '@toeverything/components/board-types';
export function changePage(app: TldrawApp, pageId: string): TldrawCommand {
return {
@@ -1,9 +1,9 @@
import type {
TldrawCommand,
TDPage,
} from '@toeverything/components/board-types';
import { Utils, TLPageState } from '@tldraw/core';
import { TLPageState, Utils } from '@tldraw/core';
import type { TldrawApp } from '@toeverything/components/board-state';
import type {
TDPage,
TldrawCommand,
} from '@toeverything/components/board-types';
export function createPage(
app: TldrawApp,
@@ -1,10 +1,10 @@
import type { TldrawApp } from '@toeverything/components/board-state';
import type {
Patch,
TDBinding,
TDShape,
TldrawCommand,
TDBinding,
} from '@toeverything/components/board-types';
import type { TldrawApp } from '@toeverything/components/board-state';
export function createShapes(
app: TldrawApp,
@@ -1,5 +1,5 @@
import type { TldrawCommand } from '@toeverything/components/board-types';
import type { TldrawApp } from '@toeverything/components/board-state';
import type { TldrawCommand } from '@toeverything/components/board-types';
export function deletePage(app: TldrawApp, pageId: string): TldrawCommand {
const {
@@ -1,9 +1,9 @@
import type { TldrawApp } from '@toeverything/components/board-state';
import type {
TDAsset,
TDAssets,
TldrawCommand,
} from '@toeverything/components/board-types';
import type { TldrawApp } from '@toeverything/components/board-state';
import { removeShapesFromPage } from './shared/remove-shapes-from-page';
const removeAssetsFromDocument = (assets: TDAssets, idsToRemove: string[]) => {
@@ -1,14 +1,14 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { Utils } from '@tldraw/core';
import Vec from '@tldraw/vec';
import type { TldrawApp } from '@toeverything/components/board-state';
import { TLDR } from '@toeverything/components/board-state';
import {
DistributeType,
TDShape,
TldrawCommand,
TDShapeType,
TldrawCommand,
} from '@toeverything/components/board-types';
import { TLDR } from '@toeverything/components/board-state';
import Vec from '@tldraw/vec';
import type { TldrawApp } from '@toeverything/components/board-state';
export function distributeShapes(
app: TldrawApp,
@@ -1,6 +1,6 @@
import type { TldrawCommand } from '@toeverything/components/board-types';
import { Utils } from '@tldraw/core';
import type { TldrawApp } from '@toeverything/components/board-state';
import type { TldrawCommand } from '@toeverything/components/board-types';
export function duplicatePage(app: TldrawApp, pageId: string): TldrawCommand {
const newId = Utils.uniqueId();
@@ -1,13 +1,13 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { Utils } from '@tldraw/core';
import { Vec } from '@tldraw/vec';
import type { TldrawApp } from '@toeverything/components/board-state';
import { TLDR } from '@toeverything/components/board-state';
import type {
PagePartial,
TldrawCommand,
TDShape,
TldrawCommand,
} from '@toeverything/components/board-types';
import type { TldrawApp } from '@toeverything/components/board-state';
export function duplicateShapes(
app: TldrawApp,
@@ -1,8 +1,8 @@
import { FlipType } from '@toeverything/components/board-types';
import { TLBoundsCorner, Utils } from '@tldraw/core';
import type { TldrawCommand } from '@toeverything/components/board-types';
import type { TldrawApp } from '@toeverything/components/board-state';
import { TLDR } from '@toeverything/components/board-state';
import type { TldrawCommand } from '@toeverything/components/board-types';
import { FlipType } from '@toeverything/components/board-types';
export function flipShapes(
app: TldrawApp,
@@ -1,12 +1,12 @@
import { TDShape, TDShapeType } from '@toeverything/components/board-types';
import { Utils } from '@tldraw/core';
import type {
Patch,
TldrawCommand,
TDBinding,
} from '@toeverything/components/board-types';
import type { TldrawApp } from '@toeverything/components/board-state';
import { TLDR } from '@toeverything/components/board-state';
import type {
Patch,
TDBinding,
TldrawCommand,
} from '@toeverything/components/board-types';
import { TDShape, TDShapeType } from '@toeverything/components/board-types';
export function groupShapes(
app: TldrawApp,
@@ -100,6 +100,7 @@ export function groupShapes(
afterShapes[groupId] = TLDR.get_shape_util(TDShapeType.Group).create({
id: groupId,
affineId: groupId,
childIndex: groupChildIndex,
parentId: groupParentId,
point: [groupBounds.minX, groupBounds.minY],
@@ -217,7 +218,6 @@ export function groupShapes(
}
}
});
return {
id: 'group',
before: {
+3 -3
View File
@@ -10,10 +10,12 @@ export * from './duplicate-shapes';
export * from './flip-shapes';
export * from './group-shapes';
export * from './move-shapes-to-page';
export * from './reorder-shapes';
export * from './rename-page';
export * from './reorder-shapes';
export * from './reset-bounds';
export * from './rotate-shapes';
export * from './set-shapes-lock-status';
export * from './set-shapes-props';
export * from './stretch-shapes';
export * from './style-shapes';
export * from './toggle-shapes-decoration';
@@ -21,5 +23,3 @@ export * from './toggle-shapes-prop';
export * from './translate-shapes';
export * from './ungroup-shapes';
export * from './update-shapes';
export * from './set-shapes-props';
export * from './set-shapes-lock-status';
@@ -1,14 +1,14 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { TLBounds, Utils } from '@tldraw/core';
import { Vec } from '@tldraw/vec';
import type { TldrawApp } from '@toeverything/components/board-state';
import { TLDR } from '@toeverything/components/board-state';
import type {
ArrowShape,
PagePartial,
TldrawCommand,
TDShape,
TldrawCommand,
} from '@toeverything/components/board-types';
import type { TldrawApp } from '@toeverything/components/board-state';
import { TLDR } from '@toeverything/components/board-state';
import { Utils, TLBounds } from '@tldraw/core';
import { Vec } from '@tldraw/vec';
export function moveShapesToPage(
app: TldrawApp,
@@ -1,5 +1,5 @@
import type { TldrawCommand } from '@toeverything/components/board-types';
import type { TldrawApp } from '@toeverything/components/board-state';
import type { TldrawCommand } from '@toeverything/components/board-types';
export function renamePage(
app: TldrawApp,
@@ -1,10 +1,10 @@
import type { TldrawApp } from '@toeverything/components/board-state';
import { TLDR } from '@toeverything/components/board-state';
import {
MoveType,
TDShape,
TldrawCommand,
} from '@toeverything/components/board-types';
import { TLDR } from '@toeverything/components/board-state';
import type { TldrawApp } from '@toeverything/components/board-state';
export function reorderShapes(
app: TldrawApp,
@@ -1,6 +1,6 @@
import type { TldrawCommand } from '@toeverything/components/board-types';
import { TLDR } from '@toeverything/components/board-state';
import type { TldrawApp } from '@toeverything/components/board-state';
import { TLDR } from '@toeverything/components/board-state';
import type { TldrawCommand } from '@toeverything/components/board-types';
export function resetBounds(
app: TldrawApp,
@@ -1,10 +1,10 @@
import { Utils } from '@tldraw/core';
import type {
TldrawCommand,
TDShape,
} from '@toeverything/components/board-types';
import { TLDR } from '@toeverything/components/board-state';
import type { TldrawApp } from '@toeverything/components/board-state';
import { TLDR } from '@toeverything/components/board-state';
import type {
TDShape,
TldrawCommand,
} from '@toeverything/components/board-types';
const PI2 = Math.PI * 2;
@@ -1,8 +1,8 @@
import type { TldrawApp } from '@toeverything/components/board-state';
import type {
TDShape,
TldrawCommand,
} from '@toeverything/components/board-types';
import type { TldrawApp } from '@toeverything/components/board-state';
export function setShapesLockStatus<T extends TDShape>(
app: TldrawApp,
@@ -1,8 +1,8 @@
import type { TldrawApp } from '@toeverything/components/board-state';
import type {
TDShape,
TldrawCommand,
} from '@toeverything/components/board-types';
import type { TldrawApp } from '@toeverything/components/board-state';
export function setShapesProps<T extends TDShape>(
app: TldrawApp,
@@ -1,9 +1,9 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { TLBoundsCorner, Utils } from '@tldraw/core';
import { StretchType, TDShapeType } from '@toeverything/components/board-types';
import type { TldrawCommand } from '@toeverything/components/board-types';
import { TLDR } from '@toeverything/components/board-state';
import type { TldrawApp } from '@toeverything/components/board-state';
import { TLDR } from '@toeverything/components/board-state';
import type { TldrawCommand } from '@toeverything/components/board-types';
import { StretchType, TDShapeType } from '@toeverything/components/board-types';
export function stretchShapes(
app: TldrawApp,
@@ -1,14 +1,11 @@
import type { TldrawApp } from '@toeverything/components/board-state';
import { TLDR } from '@toeverything/components/board-state';
import {
Patch,
ShapeStyles,
TldrawCommand,
TDShape,
// TDShapeType,
// TextShape
TldrawCommand,
} from '@toeverything/components/board-types';
import { TLDR } from '@toeverything/components/board-state';
import { Vec } from '@tldraw/vec';
import type { TldrawApp } from '@toeverything/components/board-state';
export function styleShapes(
app: TldrawApp,
@@ -1,10 +1,10 @@
import { Decoration } from '@toeverything/components/board-types';
import type { TldrawApp } from '@toeverything/components/board-state';
import type {
Patch,
ArrowShape,
Patch,
TldrawCommand,
} from '@toeverything/components/board-types';
import type { TldrawApp } from '@toeverything/components/board-state';
import { Decoration } from '@toeverything/components/board-types';
export function toggleShapesDecoration(
app: TldrawApp,
@@ -1,8 +1,8 @@
import type { TldrawApp } from '@toeverything/components/board-state';
import type {
TDShape,
TldrawCommand,
} from '@toeverything/components/board-types';
import type { TldrawApp } from '@toeverything/components/board-state';
export function toggleShapesProp(
app: TldrawApp,
@@ -1,10 +1,10 @@
import { Vec } from '@tldraw/vec';
import type { TldrawApp } from '@toeverything/components/board-state';
import { TLDR } from '@toeverything/components/board-state';
import type {
TldrawCommand,
PagePartial,
TldrawCommand,
} from '@toeverything/components/board-types';
import type { TldrawApp } from '@toeverything/components/board-state';
export function translateShapes(
app: TldrawApp,
@@ -1,12 +1,12 @@
import type { TldrawApp } from '@toeverything/components/board-state';
import { TLDR } from '@toeverything/components/board-state';
import type {
Patch,
GroupShape,
Patch,
TDBinding,
TDShape,
TldrawCommand,
} from '@toeverything/components/board-types';
import type { TldrawApp } from '@toeverything/components/board-state';
export function ungroupShapes(
app: TldrawApp,
@@ -1,9 +1,9 @@
import type {
TldrawCommand,
TDShape,
} from '@toeverything/components/board-types';
import { TLDR } from '@toeverything/components/board-state';
import type { TldrawApp } from '@toeverything/components/board-state';
import { TLDR } from '@toeverything/components/board-state';
import type {
TDShape,
TldrawCommand,
} from '@toeverything/components/board-types';
export function updateShapes(
app: TldrawApp,
+27 -28
View File
@@ -1,43 +1,43 @@
/* eslint-disable max-lines */
import { Renderer } from '@tldraw/core';
import { shapeUtils } from '@toeverything/components/board-shapes';
import {
TLDR,
TldrawApp,
TldrawAppCtorProps,
} from '@toeverything/components/board-state';
import {
GRID_SIZE,
TDDocument,
TDMeta,
TDStatus,
} from '@toeverything/components/board-types';
import { styled } from '@toeverything/components/ui';
// import { FocusButton } from '~components/FocusButton';
import { usePageClientWidth } from '@toeverything/datasource/state';
import {
memo,
useEffect,
useLayoutEffect,
useRef,
useMemo,
useRef,
useState,
type RefObject,
} from 'react';
import { Renderer } from '@tldraw/core';
import { styled } from '@toeverything/components/ui';
import {
TDDocument,
TDStatus,
GRID_SIZE,
TDMeta,
} from '@toeverything/components/board-types';
import {
TldrawApp,
TldrawAppCtorProps,
TLDR,
} from '@toeverything/components/board-state';
import {
TldrawContext,
useStylesheet,
useKeyboardShortcuts,
useTldrawApp,
} from './hooks';
import { shapeUtils } from '@toeverything/components/board-shapes';
import { ToolsPanel } from './components/tools-panel';
import { ErrorBoundary } from 'react-error-boundary';
import { CommandPanel } from './components/command-panel';
// import { TopPanel } from '~components/TopPanel';
import { ContextMenu } from './components/context-menu';
// import { FocusButton } from '~components/FocusButton';
import { Loading } from './components/loading';
import { ErrorBoundary } from 'react-error-boundary';
import { ErrorFallback } from './components/error-fallback';
import { Loading } from './components/loading';
import { ToolsPanel } from './components/tools-panel';
import { ZoomBar } from './components/zoom-bar';
import { CommandPanel } from './components/command-panel';
import { usePageClientWidth } from '@toeverything/datasource/state';
import {
TldrawContext,
useKeyboardShortcuts,
useStylesheet,
useTldrawApp,
} from './hooks';
export interface TldrawProps extends TldrawAppCtorProps {
/**
@@ -295,7 +295,6 @@ const InnerTldraw = memo(function InnerTldraw({
const pageState = document.pageStates[page.id];
const assets = document.assets;
const { selectedIds } = pageState;
const isHideBoundsShape =
selectedIds.length === 1 &&
page.shapes[selectedIds[0]] &&
@@ -0,0 +1,66 @@
import { styled, Tooltip } from '@toeverything/components/ui';
import { AlignType } from '../command-panel/AlignOperation';
interface AlignObject {
name?: string;
/**
* color: none means no color
*/
title: string;
icon?: JSX.Element;
}
/**
* ColorValue : none means no color
*/
interface AlignProps {
alignOptions: AlignObject[];
selected?: string;
onSelect?: (alginType: AlignType) => void;
}
export const AlignPanel = ({
alignOptions,
selected,
onSelect,
}: AlignProps) => {
return (
<Container>
{alignOptions.map(alignOption => {
const option = alignOption.name as AlignType;
// const selected = color;
return (
<Tooltip key={option} content={alignOption.title}>
<SelectableContainer
onClick={() => {
onSelect?.(option);
}}
>
{alignOption.icon}
</SelectableContainer>
</Tooltip>
);
})}
</Container>
);
};
const Container = styled('div')({
width: '170px',
display: 'flex',
flexWrap: 'wrap',
});
const SelectableContainer = styled('div')<{ selected?: boolean }>(
({ selected, theme }) => ({
color: theme.affine.palette.icons,
borderRadius: '5px',
overflow: 'hidden',
margin: '5px',
padding: '3px',
cursor: 'pointer',
boxSizing: 'border-box',
'&:hover': {
backgroundColor: theme.affine.palette.hover,
},
})
);
@@ -0,0 +1,108 @@
import type { TldrawApp } from '@toeverything/components/board-state';
import { DistributeType, TDShape } from '@toeverything/components/board-types';
import {
AlignIcon,
ShapesAlignBottomIcon,
ShapesAlignHorizontalCenterIcon,
ShapesAlignLeftIcon,
ShapesAlignRightIcon,
ShapesAlignTopIcon,
ShapesAlignVerticalCenterIcon,
ShapesDistributeHorizontalIcon,
ShapesDistributeVerticalIcon,
} from '@toeverything/components/icons';
import { IconButton, Popover, Tooltip } from '@toeverything/components/ui';
import { AlignPanel } from '../align-panel';
interface BorderColorConfigProps {
app: TldrawApp;
shapes: TDShape[];
}
export enum AlignType {
Top = 'top',
CenterVertical = 'centerVertical',
Bottom = 'bottom',
Left = 'left',
CenterHorizontal = 'centerHorizontal',
Right = 'right',
}
let AlignPanelArr = [
{
name: 'left',
title: 'Align left',
icon: <ShapesAlignLeftIcon></ShapesAlignLeftIcon>,
},
{
name: 'centerVertical',
title: 'Align Center Vertical',
icon: <ShapesAlignVerticalCenterIcon></ShapesAlignVerticalCenterIcon>,
},
{
name: 'right',
title: 'Align right',
icon: <ShapesAlignRightIcon></ShapesAlignRightIcon>,
},
{
name: 'top',
title: 'Align top',
icon: <ShapesAlignTopIcon></ShapesAlignTopIcon>,
},
{
name: 'bottom',
title: 'Align bottom',
icon: <ShapesAlignBottomIcon></ShapesAlignBottomIcon>,
},
{
name: 'centerHorizontal',
title: 'Align centerHorizontal',
icon: (
<ShapesAlignHorizontalCenterIcon></ShapesAlignHorizontalCenterIcon>
),
},
{
name: 'distributeCenterHorizontal',
title: 'Align distribute center horizontal',
icon: <ShapesDistributeHorizontalIcon></ShapesDistributeHorizontalIcon>,
},
{
name: 'distributeCenterVertical',
title: 'Align distribute center horizontal',
icon: <ShapesDistributeVerticalIcon></ShapesDistributeVerticalIcon>,
},
];
export const AlignOperation = ({ app, shapes }: BorderColorConfigProps) => {
const setAlign = (alginType: string) => {
switch (alginType) {
case 'distributeCenterHorizontal':
app.distribute(DistributeType.Horizontal);
break;
case 'distributeCenterVertical':
app.distribute(DistributeType.Vertical);
break;
default:
app.align(alginType as AlignType);
break;
}
};
return (
<Popover
trigger="hover"
placement="bottom-start"
content={
<AlignPanel
alignOptions={AlignPanelArr}
onSelect={setAlign}
></AlignPanel>
}
>
<Tooltip content="Align" placement="top-start">
<IconButton>
<AlignIcon />
</IconButton>
</Tooltip>
</Popover>
);
};
@@ -0,0 +1,78 @@
import type { TldrawApp } from '@toeverything/components/board-state';
import type {
ArrowBinding,
TDShape,
} from '@toeverything/components/board-types';
import { ConnectorIcon } from '@toeverything/components/icons';
import { IconButton, Popover, Tooltip } from '@toeverything/components/ui';
import { useEffect, useState } from 'react';
import { ListItemContainer, ListItemTitle } from './FontSizeConfig';
interface GroupAndUnGroupProps {
app: TldrawApp;
shapes: TDShape[];
}
export const ArrowTo = ({ app, shapes }: GroupAndUnGroupProps) => {
const [arrowToArr, setarrowToArr] = useState([]);
useEffect(() => {
let allShape = app.shapes;
let bindings = app.page.bindings;
let activeShape = shapes[0];
let toNextShapBindings: ArrowBinding[] = [];
let bindingId = '';
Object.keys(bindings).forEach(key => {
if (bindings[key].toId === activeShape.id) {
bindingId = bindings[key].fromId;
}
});
Object.keys(bindings).forEach(key => {
if (bindings[key].fromId === bindingId) {
toNextShapBindings.push(bindings[key]);
}
});
let ArrowToArr: TDShape[] = [];
toNextShapBindings.forEach(binding => {
if (binding.toId !== activeShape.id) {
allShape.forEach(item => {
console.log(item);
if (item.id === binding.toId) {
ArrowToArr.push(item);
}
});
}
});
setarrowToArr(ArrowToArr);
return () => {};
}, [app.page.bindings, app.shapes]);
const jumpToNextShap = (shape: TDShape) => {
app.zoomToShapes([shape]);
};
return (
<Popover
trigger="hover"
placement="bottom-start"
content={
<div>
{arrowToArr.map((arrow: any) => {
return (
<ListItemContainer
key={arrow.id}
onClick={() => jumpToNextShap(arrow)}
>
<ListItemTitle>{arrow.name}</ListItemTitle>
</ListItemContainer>
);
})}
</div>
}
>
<Tooltip content="Font Size" placement="top-start">
<IconButton>
<ConnectorIcon></ConnectorIcon>
</IconButton>
</Tooltip>
</Popover>
);
};
@@ -1,13 +1,13 @@
import type { TldrawApp } from '@toeverything/components/board-state';
import type { TDShape } from '@toeverything/components/board-types';
import { Popover, Tooltip, IconButton } from '@toeverything/components/ui';
import {
BorderColorNoneIcon,
BorderColorDuotoneIcon,
BorderColorNoneIcon,
} from '@toeverything/components/icons';
import { IconButton, Popover, Tooltip } from '@toeverything/components/ui';
import { countBy, maxBy } from '@toeverything/utils';
import { getShapeIds } from './utils';
import { Palette } from '../palette';
import { getShapeIds } from './utils';
interface BorderColorConfigProps {
app: TldrawApp;
@@ -1,16 +1,18 @@
import { TLDR, TldrawApp } from '@toeverything/components/board-state';
import { Divider, Popover, styled } from '@toeverything/components/ui';
import { Fragment } from 'react';
import { Vec } from '@tldraw/vec';
import { TldrawApp, TLDR } from '@toeverything/components/board-state';
import { Popover, styled, Divider } from '@toeverything/components/ui';
import { getAnchor, useConfig } from './utils';
import { AlignOperation } from './AlignOperation';
import { ArrowTo } from './ArrowTo';
import { BorderColorConfig } from './BorderColorConfig';
import { DeleteShapes } from './DeleteOperation';
import { FillColorConfig } from './FillColorConfig';
import { FontSizeConfig } from './FontSizeConfig';
import { StrokeLineStyleConfig } from './stroke-line-style-config';
import { Group, UnGroup } from './GroupOperation';
import { DeleteShapes } from './DeleteOperation';
import { Lock, Unlock } from './LockOperation';
import { FrameFillColorConfig } from './FrameFillColorConfig';
import { Group, UnGroup } from './GroupOperation';
import { Lock, Unlock } from './LockOperation';
import { MoveCoverageConfig } from './MoveCoverage';
import { StrokeLineStyleConfig } from './stroke-line-style-config';
import { getAnchor, useConfig } from './utils';
export const CommandPanel = ({ app }: { app: TldrawApp }) => {
const state = app.useStore();
@@ -92,6 +94,25 @@ export const CommandPanel = ({ app }: { app: TldrawApp }) => {
shapes={config.deleteShapes.selectedShapes}
/>
),
moveCoverageConfig: (
<MoveCoverageConfig
key="deleteShapes1"
app={app}
shapes={config.deleteShapes.selectedShapes}
/>
),
alginOperation: config.group.selectedShapes.length ? (
<AlignOperation
app={app}
shapes={config.deleteShapes.selectedShapes}
></AlignOperation>
) : null,
toNextShap: (
<ArrowTo
app={app}
shapes={config.deleteShapes.selectedShapes}
></ArrowTo>
),
};
const nodes = Object.entries(configNodes).filter(([key, node]) => !!node);
@@ -1,7 +1,7 @@
import type { TldrawApp } from '@toeverything/components/board-state';
import type { TDShape } from '@toeverything/components/board-types';
import { IconButton, Tooltip } from '@toeverything/components/ui';
import { DeleteCashBinIcon } from '@toeverything/components/icons';
import { IconButton, Tooltip } from '@toeverything/components/ui';
import { getShapeIds } from './utils';
interface DeleteShapesProps {
@@ -1,18 +1,18 @@
import type { TldrawApp } from '@toeverything/components/board-state';
import type { TDShape } from '@toeverything/components/board-types';
import {
ShapeColorDuotoneIcon,
ShapeColorNoneIcon,
} from '@toeverything/components/icons';
import {
IconButton,
Popover,
Tooltip,
IconButton,
useTheme,
} from '@toeverything/components/ui';
import {
ShapeColorNoneIcon,
ShapeColorDuotoneIcon,
} from '@toeverything/components/icons';
import { countBy, maxBy } from '@toeverything/utils';
import { getShapeIds } from './utils';
import { Palette } from '../palette';
import { getShapeIds } from './utils';
interface BorderColorConfigProps {
app: TldrawApp;
@@ -2,17 +2,17 @@ import type { TldrawApp } from '@toeverything/components/board-state';
import type { TDShape } from '@toeverything/components/board-types';
import { FontSizeStyle } from '@toeverything/components/board-types';
import {
Popover,
Tooltip,
IconButton,
styled,
} from '@toeverything/components/ui';
import {
TextFontIcon,
HeadingOneIcon,
HeadingTwoIcon,
HeadingThreeIcon,
HeadingTwoIcon,
TextFontIcon,
} from '@toeverything/components/icons';
import {
IconButton,
Popover,
styled,
Tooltip,
} from '@toeverything/components/ui';
import { countBy, maxBy } from '@toeverything/utils';
import { getShapeIds } from './utils';
@@ -86,7 +86,7 @@ export const FontSizeConfig = ({ app, shapes }: FontSizeConfigProps) => {
);
};
const ListItemContainer = styled('div')(({ theme }) => ({
export const ListItemContainer = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'center',
cursor: 'pointer',
@@ -100,7 +100,7 @@ const ListItemContainer = styled('div')(({ theme }) => ({
},
}));
const ListItemTitle = styled('span')(({ theme }) => ({
export const ListItemTitle = styled('span')(({ theme }) => ({
marginLeft: '12px',
color: theme.affine.palette.primaryText,
}));
@@ -1,18 +1,18 @@
import type { TldrawApp } from '@toeverything/components/board-state';
import type { TDShape } from '@toeverything/components/board-types';
import {
ShapeColorDuotoneIcon,
ShapeColorNoneIcon,
} from '@toeverything/components/icons';
import {
IconButton,
Popover,
Tooltip,
IconButton,
useTheme,
} from '@toeverything/components/ui';
import {
ShapeColorNoneIcon,
ShapeColorDuotoneIcon,
} from '@toeverything/components/icons';
import { countBy, maxBy } from '@toeverything/utils';
import { getShapeIds } from './utils';
import { Palette } from '../palette';
import { getShapeIds } from './utils';
interface BorderColorConfigProps {
app: TldrawApp;
@@ -1,7 +1,8 @@
import type { TldrawApp } from '@toeverything/components/board-state';
import type { TDShape } from '@toeverything/components/board-types';
import { IconButton, Tooltip } from '@toeverything/components/ui';
import { TLDR, TldrawApp } from '@toeverything/components/board-state';
import { TDShape, TDShapeType } from '@toeverything/components/board-types';
import { GroupIcon, UngroupIcon } from '@toeverything/components/icons';
import { IconButton, Tooltip } from '@toeverything/components/ui';
import { services } from '@toeverything/datasource/db-service';
import { getShapeIds } from './utils';
interface GroupAndUnGroupProps {
@@ -10,8 +11,38 @@ interface GroupAndUnGroupProps {
}
export const Group = ({ app, shapes }: GroupAndUnGroupProps) => {
const group = () => {
app.group(getShapeIds(shapes));
const group = async () => {
let groupShape = await services.api.editorBlock.create({
workspace: app.document.id,
parentId: app.appState.currentPageId,
type: 'shape',
});
await services.api.editorBlock.update({
workspace: groupShape.workspace,
id: groupShape.id,
properties: {
shapeProps: {
value: JSON.stringify(
TLDR.get_shape_util(TDShapeType.Group).create({
id: groupShape.id,
affineId: groupShape.id,
childIndex: 1,
parentId: app.appState.currentPageId,
point: [0, 0],
size: [0, 0],
children: getShapeIds(shapes),
workspace: app.document.id,
})
),
},
},
});
app.group(
getShapeIds(shapes),
groupShape.id,
app.appState.currentPageId
);
};
return (
<Tooltip content="Group">
@@ -1,7 +1,7 @@
import type { TldrawApp } from '@toeverything/components/board-state';
import type { TDShape } from '@toeverything/components/board-types';
import { IconButton, Tooltip } from '@toeverything/components/ui';
import { LockIcon, UnlockIcon } from '@toeverything/components/icons';
import { IconButton, Tooltip } from '@toeverything/components/ui';
import { getShapeIds } from './utils';
interface GroupAndUnGroupProps {
@@ -0,0 +1,77 @@
import type { TldrawApp } from '@toeverything/components/board-state';
import type { TDShape } from '@toeverything/components/board-types';
import {
BringForwardIcon,
BringToFrontIcon,
LayersIcon,
SendBackwardIcon,
SendToBackIcon,
} from '@toeverything/components/icons';
import { IconButton, Popover, Tooltip } from '@toeverything/components/ui';
import { AlignPanel } from '../align-panel';
interface FontSizeConfigProps {
app: TldrawApp;
shapes: TDShape[];
}
const AlignPanelArr = [
{
title: 'To Front',
name: 'tofront',
icon: <BringToFrontIcon />,
},
{
title: 'Forward',
name: 'forward',
icon: <BringForwardIcon />,
},
{
title: 'Backward',
name: 'backward',
icon: <SendBackwardIcon />,
},
{
title: 'To Back',
name: 'toback',
icon: <SendToBackIcon />,
},
];
export const MoveCoverageConfig = ({ app, shapes }: FontSizeConfigProps) => {
const moveCoverage = (type: string) => {
switch (type) {
case 'toback':
app.moveToBack();
break;
case 'backward':
app.moveBackward();
break;
case 'forward':
app.moveForward();
break;
case 'tofront':
app.moveToFront();
break;
}
};
return (
<Popover
trigger="hover"
placement="bottom-start"
content={
<AlignPanel
alignOptions={AlignPanelArr}
onSelect={moveCoverage}
></AlignPanel>
}
>
<Tooltip content="Layers" placement="top-start">
<IconButton>
<LayersIcon />
</IconButton>
</Tooltip>
</Popover>
);
};
@@ -1,15 +1,15 @@
import { DashStyle, StrokeWidth } from '@toeverything/components/board-types';
import {
LineNoneIcon,
DashLineIcon,
SolidLineIcon,
BrushIcon,
DashLineIcon,
LineNoneIcon,
SolidLineIcon,
} from '@toeverything/components/icons';
import {
IconButton,
Slider,
styled,
Tooltip,
Slider,
} from '@toeverything/components/ui';
export const lineStyles = [
@@ -1,8 +1,8 @@
import type { TldrawApp } from '@toeverything/components/board-state';
import { DashStyle, StrokeWidth } from '@toeverything/components/board-types';
import type { TDShape } from '@toeverything/components/board-types';
import { Popover, IconButton, Tooltip } from '@toeverything/components/ui';
import { DashStyle, StrokeWidth } from '@toeverything/components/board-types';
import { BrushIcon } from '@toeverything/components/icons';
import { IconButton, Popover, Tooltip } from '@toeverything/components/ui';
import { countBy, maxBy } from '@toeverything/utils';
import { getShapeIds } from '../utils';
import { LineStyle, lineStyles } from './LineStyle';
@@ -1,2 +1,2 @@
export { getAnchor } from './get-anchor';
export { useConfig, getShapeIds } from './use-config';
export { getShapeIds, useConfig } from './use-config';
@@ -1,7 +1,7 @@
import type { TldrawApp } from '@toeverything/components/board-state';
import { TLDR } from '@toeverything/components/board-state';
import type { TDShape } from '@toeverything/components/board-types';
import { TDShapeType } from '@toeverything/components/board-types';
import { TLDR } from '@toeverything/components/board-state';
interface Config {
type:
@@ -1,7 +1,6 @@
import * as React from 'react';
import { styled } from '@toeverything/components/ui';
import { FallbackProps } from 'react-error-boundary';
import { useTldrawApp } from '../../hooks';
import { styled } from '@toeverything/components/ui';
export function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
const app = useTldrawApp();
@@ -1,7 +1,6 @@
import * as React from 'react';
import { useTldrawApp } from '../../hooks';
import { styled } from '@toeverything/components/ui';
import type { TDSnapshot } from '@toeverything/components/board-types';
import { styled } from '@toeverything/components/ui';
import { useTldrawApp } from '../../hooks';
const loadingSelector = (s: TDSnapshot) => s.appState.isLoading;
@@ -1,7 +1,6 @@
import type { PropsWithChildren } from 'react';
import { useMemo } from 'react';
import { styled, Tooltip } from '@toeverything/components/ui';
import { ShapeColorNoneIcon } from '@toeverything/components/icons';
import { styled, Tooltip } from '@toeverything/components/ui';
import { useMemo } from 'react';
interface ColorObject {
name?: string;

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