Merge to master (#435)

* Fix/venus spanish (#423)

fix: update venus spanish language

Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com>

* fix: can not convert url text to link after paste

* fix: double link icon size error

* feat(code): enhance markdown parse code

* fix(code): add robust

* fix: remove special menu

* chore: clean code

* fix: ime with command menu

* fix(code): langs[lang] is not a function

* fix: can't add image and delete more action button (#430)

* feat: add zh_Hant for venus (#431)

* fix: lang select in  code block is insanity

* GitHub Doc Updates (#421)

* Update types-of-contributions.md

* Update README.md

Tidy up links section

* fix: inline menu position (#433)

Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com>
Co-authored-by: QiShaoXuan <qishaoxuan777@gmail.com>
Co-authored-by: tzhangchi <terry.zhangchi@outlook.com>
Co-authored-by: lawvs <18554747+lawvs@users.noreply.github.com>
Co-authored-by: DiamondThree <diamond.shx@gmail.com>
Co-authored-by: CJSS <CJSS@users.noreply.github.com>
Co-authored-by: Qi <474021214@qq.com>
This commit is contained in:
zuomeng wang
2022-09-19 15:57:48 +08:00
committed by GitHub
parent 8984bedec9
commit c010e05023
24 changed files with 614 additions and 528 deletions
+4 -14
View File
@@ -59,7 +59,10 @@ See https://github.com/all-?/all-contributors/issues/361#issuecomment-637166066
[![affine.pro](https://img.shields.io/static/v1?label=Try%20it%20Online&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAhpJREFUWEdjZEACtnl3MxgY/0YzMjAaMzAwcCLLUYH9/T/D/7MM/5mXHp6kPANmHiOI4Zx9Xfg3C+tKBob/zlSwiAgjGPey/vkdvneq5luwA+zy7+yhn+Vwv+89NFHFhREU7IyM/6YT4WyqK/n/nymT0Tb/1mFGBkYbqptOhIH/Gf4fYbTLv/2NBgmOCOvBSr6DHPCfWNW0UEe2A2x1uRlakiXBbtpx6jND+7KXZLmPbAdURokzeJjxwi31rrzH8OX7P5IdQbYDtnUoMXBzMMEt7Fj2imH7qU/0cQBy8MNsPHL5K0P13Of0cQB68MNsJScaSI4CHk4mhq3tSnCf3n36k0FZmh3Mn7L+DcPqgx9ICgWSHeBpxsdQESUGtgRk+eqDH+H8O09/MiR3P6atA1qTJRlsdLnhPgYlPOQQCW96wPDi3R+iHUFSCKAHP8wydEeREg0kOQA9+JOgwR1qL8CQEygC9jWp0UCSA+aVysIT3JqDHxgmr38DtlRCiIVhZZ0CPNhB6QDkEGIA0Q4gZAkuxxFyBNEOQA7ml+/+MIQ1PUAxG1kelAhB6YMYQLQDCPmQUAjhcgxRDiDWcEKOxOYIohyQGyjCEGIvANaPLfhhBiNHA6hmBBXNhABRDgCV/aBQAAFQpYMrn4PUgNTCACiXEMoNRDmAkC8okR8UDhjYRumAN8sHvGMCSkAD2jUDOWDAO6ewbDQQ3XMAy/oxKownQR0AAAAASUVORK5CYII=&message=%E2%86%92&style=for-the-badge)](https://affine.pro) No installation or registration required! Head over to our website and try it out now.
Want to deploy it yourself? AFFiNE can run just about anywhere. <br />
You can refer to our documentation which can be found from the [useful links](#useful-links) section - where you will also find ways to help contribute to the project and join our communities.
[AFFiNE Documentation](https://docs.affine.pro/affine/) - More detailed documentation on how to use and develop with AFFiNE
AFFiNE Communities: [Discord](https://discord.gg/yz6tGVsf5p) | [Telegram](https://t.me/affineworkos) | [Twitter](https://twitter.com/AffineOfficial)
<br /><br />
⚠️ Please note that AFFiNE is still under active development and is not yet ready for production use. ⚠️
@@ -80,19 +83,6 @@ And when it comes to collaboration, these features are not just necessarily for
![c9e1ff46-cec2-411b-b89d-6727a5e6f6c3](https://user-images.githubusercontent.com/79301703/182366602-08e44d28-a031-4097-9904-52fb9b1e9e17.gif)
# Useful Links
- [AFFiNE Documentation](https://docs.affine.pro/affine/) - More detailed documentation on how to use and develop with AFFiNE
- [Feature Roadmap](https://github.com/toeverything/AFFiNE/issues/293) - Looking for a feature? It might already be planned for release - you can check here
- [Release Notes](https://github.com/toeverything/AFFiNE/wiki) - Find out what changes we are making and how we are improving AFFiNE
- [Contributing Guide](/docs/CONTRIBUTING.md) - Want to help improve AFFiNE? You might not even need to write a line of code. Find out how you can contribute.
- [Code of Conduct](/docs/CODE_OF_CONDUCT.md) - How we promote and maintain a harassment-free experience for everyone in our community.
- AFFiNE Communities: [Discord](https://discord.gg/yz6tGVsf5p) | [Telegram](https://t.me/affineworkos) | [Twitter](https://twitter.com/AffineOfficial) |
[Medium](https://medium.com/@affineworkos) | [AFFiNE Blog](https://blog.affine.pro)
# Contact Us
Feel free to send us an email: contact@toeverything.info
@@ -1,6 +1,7 @@
import en from './en.json';
import es from './es.json';
import zh_Hans from './zh.json';
import zh_Hant from './zh-Hant.json';
export const LOCALES = [
{
@@ -15,6 +16,12 @@ export const LOCALES = [
originalName: '简体中文',
res: zh_Hans,
},
{
name: 'Traditional Chinese',
tag: 'zh-Hant',
originalName: '繁體中文',
res: zh_Hant,
},
{
name: 'Spanish',
tag: 'es',
@@ -0,0 +1,39 @@
{
"Blog": "博客",
"AboutUs": "關於我們",
"Open Source": "開源",
"Docs": "文檔",
"Feedback": "反饋",
"ContactUs": "聯繫我們",
"Privacy First": "隱私第一",
"Alternative": "的另一種選擇",
"Check GitHub": "在 Github 中查看",
"GitHub": "GitHub",
"Try it Online": "在線嘗試",
"language": "語言",
"description1": {
"part1": "Affine 是面向專業用戶的下一代協同知識庫",
"part2": "Affine 不僅僅集合着文檔、白板和表格。",
"part3": "隨心所欲切換構塊模式。",
"part4": "告別冗余,存儲數據一次就好,放在任何您想要的位置。"
},
"description2": {
"part1": "塑形您的頁面",
"part2": "隨時隨地,文檔、看板、數據庫都完全可用。真正“所見即所得”的數據處理環境。",
"part3": "所有頁面都配備文檔(紙張模式)和白板(無邊緣模式)視圖。"
},
"description3": {
"part1": "計畫您的任務",
"part2": "多視圖管理不再混亂。",
"part3": "既可使用 Markdown 設置 TODO,又可無縫在看板視圖中編輯。",
"part4": "管理多維表格本當易如反掌——攜手Affine,确实易如反掌"
},
"description4": {
"part1": "隱私第一,合作無間,毫不妥協。",
"part2": "我們不喜歡被軟件綁架,您也是。我們的任何努力雖建立在尊重用戶隱私基礎上,也游刃有餘。這解釋了為什麼我們毫不妥協。",
"part3": "您創造的數據屬於您,安全永久存儲在本地供您隨時調用。同時您也能盡享協同的便利,無需雲設置就能實時編輯、共享給他人。"
},
"BuildFor": "構建一個開放和語義化的未來",
"KeepUpdated": "持續更新在",
"Join": "加入我們的社群"
}
+1 -3
View File
@@ -26,6 +26,4 @@ You may be able to find additional help and information on our social media plat
### :earth_asia: Translations
AFFiNE is internationalized and available in multiple languages. The source content in this repository is written in English. We integrate with an external localization platform to work with the community in localizing the English content.
**We do not currently offer this feature**, but we hope to in the future.
AFFiNE is internationalized and available in multiple languages. The source content in this repository is written in English. We integrate with an external localization platform to work with the community in localizing the English content. You can find more info in our [internationalization docs](https://docs.affine.pro/affine/internationalization/welcome).
@@ -38,13 +38,13 @@ export const createEditor = (
views: {
[Protocol.Block.Type.page]: new PageBlock(),
[Protocol.Block.Type.reference]: new RefLinkBlock(),
[Protocol.Block.Type.code]: new CodeBlock(),
[Protocol.Block.Type.text]: new TextBlock(),
[Protocol.Block.Type.heading1]: new Heading1Block(),
[Protocol.Block.Type.heading2]: new Heading2Block(),
[Protocol.Block.Type.heading3]: new Heading3Block(),
[Protocol.Block.Type.quote]: new QuoteBlock(),
[Protocol.Block.Type.todo]: new TodoBlock(),
[Protocol.Block.Type.code]: new CodeBlock(),
// [Protocol.Block.Type.toc]: new TocBlock(),
[Protocol.Block.Type.file]: new FileBlock(),
[Protocol.Block.Type.image]: new ImageBlock(),
+2 -10
View File
@@ -39,14 +39,6 @@ type MenuItemsProps = {
export const CommonList = (props: MenuItemsProps) => {
const { items, currentItem, setCurrentItem, onSelected } = props;
// const JSONUnsupportedBlockTypes = useFlag('JSONUnsupportedBlockTypes', [
// 'page',
// ]);
// TODO Insert bidirectional link to be developed
const JSONUnsupportedBlockTypes = ['page'];
const usedItems = items.filter(item => {
return !JSONUnsupportedBlockTypes.includes(item?.content?.id);
});
return (
<div className={clsx(styles('root_container'), props.className)}>
<div
@@ -55,8 +47,8 @@ export const CommonList = (props: MenuItemsProps) => {
commonListContainer,
])}
>
{usedItems?.length ? (
usedItems.map((item, idx) => {
{items?.length ? (
items.map((item, idx) => {
if (item.renderCustom) {
return item.renderCustom(item);
} else if (item.block) {
@@ -3,6 +3,7 @@ import React, { useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { Descendant } from 'slate';
import { RenderElementProps } from 'slate-react';
import { styled } from '@toeverything/components/ui';
export type DoubleLinkElement = {
type: 'link';
@@ -13,6 +14,10 @@ export type DoubleLinkElement = {
id: string;
};
const StyledLink = styled('a')({
cursor: 'pointer',
});
export const DoubleLinkComponent = (props: RenderElementProps) => {
const { attributes, children, element } = props;
const doubleLinkElement = element as DoubleLinkElement;
@@ -32,15 +37,20 @@ export const DoubleLinkComponent = (props: RenderElementProps) => {
return (
<span onClick={handleClickLinkText}>
<a {...attributes} style={{ cursor: 'pointer' }}>
<StyledLink {...attributes}>
<PagesIcon
style={{ verticalAlign: 'middle', height: '20px' }}
style={{
verticalAlign: 'middle',
height: '1em',
fontSize: 'inherit',
marginBottom: '.2em',
}}
/>
<span>
{children}
{displayValue}
</span>
</a>
</StyledLink>
</span>
);
};
+2 -1
View File
@@ -38,7 +38,8 @@
"react-window": "^1.8.7",
"slate": "^0.81.1",
"slate-react": "^0.81.0",
"style9": "^0.14.0"
"style9": "^0.14.0",
"html-escaper": "^3.0.3"
},
"devDependencies": {
"@types/codemirror": "^5.60.5",
@@ -116,7 +116,7 @@ const CodeBlock = styled('div')(({ theme }) => ({
flexWrap: 'wrap',
justifyContent: 'space-between',
opacity: 0,
transition: 'opacity 1.5s',
transition: 'opacity .15s',
},
'.copy-block': {
padding: '0px 10px',
@@ -139,10 +139,21 @@ const CodeBlock = styled('div')(({ theme }) => ({
outline: 'none !important',
},
}));
const StyledOperationalPanel = styled('div')<{ show: boolean }>(({ show }) => {
return {
display: 'flex',
flexWrap: 'wrap',
justifyContent: 'space-between',
opacity: show ? 1 : 0,
transition: 'opacity .15s',
};
});
export const CodeView = ({ block, editor }: CreateCodeView) => {
const initValue: string = block.getProperty('text')?.value?.[0]?.text;
const langType: string = block.getProperty('lang');
const [extensions, setExtensions] = useState<Extension[]>();
const [showOperationPanel, setShowOperationPanel] = useState(false);
const isSelecting = useRef(false);
const codeMirror = useRef<ReactCodeMirrorRef>();
const focusCode = () => {
if (codeMirror.current) {
@@ -160,7 +171,7 @@ export const CodeView = ({ block, editor }: CreateCodeView) => {
};
const handleLangChange = (lang: string) => {
block.setProperty('lang', lang);
setExtensions([langs[lang]()]);
langs[lang] && setExtensions([langs[lang]()]);
};
useEffect(() => {
handleLangChange(langType ? langType : DEFAULT_LANG);
@@ -181,8 +192,15 @@ export const CodeView = ({ block, editor }: CreateCodeView) => {
onKeyDown={e => {
e.stopPropagation();
}}
onMouseEnter={() => {
isSelecting.current = false;
setShowOperationPanel(true);
}}
onMouseLeave={() => {
!isSelecting.current && setShowOperationPanel(false);
}}
>
<div className="operation">
<StyledOperationalPanel show={showOperationPanel}>
<div className="select">
<Select
width={128}
@@ -192,6 +210,9 @@ export const CodeView = ({ block, editor }: CreateCodeView) => {
onChange={(selectedValue: string) => {
handleLangChange(selectedValue);
}}
onListboxOpenChange={() => {
isSelecting.current = true;
}}
>
{Object.keys(langs).map(item => {
return (
@@ -208,7 +229,7 @@ export const CodeView = ({ block, editor }: CreateCodeView) => {
Copy
</div>
</div>
</div>
</StyledOperationalPanel>
<CodeMirror
ref={codeMirror}
@@ -5,10 +5,10 @@ import {
BlockEditor,
HTML2BlockResult,
} from '@toeverything/framework/virgo';
import { unescape } from 'html-escaper';
import {
Block2HtmlProps,
commonBlock2HtmlContent,
commonHTML2block,
} from '../../utils/commonBlockClip';
import { CodeView } from './CodeView';
@@ -34,12 +34,30 @@ export class CodeBlock extends BaseView {
element: Element;
editor: BlockEditor;
}): Promise<HTML2BlockResult> {
return commonHTML2block({
element,
editor,
type: this.type,
tagName: 'CODE',
});
// debugger;
if (element.tagName === 'CODE') {
return [
{
type: this.type,
properties: {
text: {
value: [
{
text: unescape(
(element as HTMLElement).innerText
),
},
],
},
lang:
element.classList[0] &&
element.classList[0].substr(9),
},
children: [],
},
];
}
return null;
}
override async block2html(props: Block2HtmlProps) {
@@ -168,7 +168,9 @@ export const ImageView = ({ block, editor }: ImageView) => {
<BlockPendantProvider editor={editor} block={block}>
<ImageBlock>
<div style={{ position: 'relative' }} ref={resizeBox}>
{!isSelect ? <ImageShade onClick={handleClick} /> : null}
{!isSelect && imgUrl ? (
<ImageShade onClick={handleClick} />
) : null}
{imgUrl ? (
<div
onMouseDown={e => {
@@ -1,17 +1,17 @@
import {
BaseView,
CreateView,
AsyncBlock,
HTML2BlockResult,
BlockEditor,
} from '@toeverything/framework/virgo';
import { Protocol } from '@toeverything/datasource/db-service';
import { TextView } from './TextView';
import {
AsyncBlock,
BaseView,
BlockEditor,
CreateView,
HTML2BlockResult,
} from '@toeverything/framework/virgo';
import {
Block2HtmlProps,
commonBlock2HtmlContent,
commonHTML2block,
} from '../../utils/commonBlockClip';
import { TextView } from './TextView';
export class TextBlock extends BaseView {
type = Protocol.Block.Type.text;
@@ -41,12 +41,12 @@ export class TextBlock extends BaseView {
tagName: [
'DIV',
'P',
'PRE',
// 'PRE',
'B',
'A',
'EM',
'U',
'CODE',
// 'CODE',
'S',
'DEL',
],
@@ -1,5 +1,3 @@
import { useState } from 'react';
import { CustomText, TextProps } from '@toeverything/components/common';
import {
BlockPendantProvider,
@@ -11,9 +9,11 @@ import {
import { styled } from '@toeverything/components/ui';
import { Protocol } from '@toeverything/datasource/db-service';
import { CreateView } from '@toeverything/framework/virgo';
import { useState } from 'react';
import { BlockContainer } from '../../components/BlockContainer';
import { TextManage } from '../../components/text-manage';
import { dedentBlock, tabBlock } from '../../utils/indent';
interface CreateTextView extends CreateView {
// TODO: need to optimize
containerClassName?: string;
@@ -247,6 +247,9 @@ export const TextView = ({
await block.setProperty('text', {
value: options?.['text'] as CustomText[],
});
setTimeout(async () => {
await editor.selectionManager.activeNodeByNodeId(block.id);
}, 100);
block.firstCreateFlag = true;
};
const onTab: TextProps['handleTab'] = async ({ isShiftKey }) => {
@@ -1,10 +1,15 @@
import { Protocol } from '@toeverything/datasource/db-service';
import { AsyncBlock } from '../block';
import { Editor } from '../editor';
import { SelectBlock, SelectInfo } from '../selection';
import { AsyncBlock } from '../block';
import { ClipBlockInfo, OFFICE_CLIPBOARD_MIMETYPE } from './types';
import { Clip } from './clip';
import { commonHTML2Block, commonHTML2Text } from './utils';
import { ClipBlockInfo, OFFICE_CLIPBOARD_MIMETYPE } from './types';
import {
commonHTML2Block,
commonHTML2Text,
getIsTextLink,
linkText2Block,
} from './utils';
export class ClipboardUtils {
private _editor: Editor;
constructor(editor: Editor) {
@@ -156,12 +161,37 @@ export class ClipboardUtils {
return this.convertHtml2Blocks(htmlEl);
}
async convertHtml2Blocks(element: Element): Promise<ClipBlockInfo[]> {
const editableViews = this._editor.getEditableViews();
// const editableViews = this._editor.getEditableViews();
// 如果block能够捕捉htmlElement则返回block的html2block
const CONVERT_SORT_LIST = [
Protocol.Block.Type.page,
Protocol.Block.Type.reference,
Protocol.Block.Type.code,
Protocol.Block.Type.text,
Protocol.Block.Type.heading1,
Protocol.Block.Type.heading2,
Protocol.Block.Type.heading3,
Protocol.Block.Type.quote,
Protocol.Block.Type.todo,
Protocol.Block.Type.file,
Protocol.Block.Type.image,
Protocol.Block.Type.divider,
Protocol.Block.Type.callout,
Protocol.Block.Type.youtube,
Protocol.Block.Type.figma,
Protocol.Block.Type.group,
Protocol.Block.Type.embedLink,
Protocol.Block.Type.numbered,
Protocol.Block.Type.bullet,
Protocol.Block.Type.grid,
Protocol.Block.Type.gridItem,
Protocol.Block.Type.groupDivider,
];
const [clipBlockInfos] = (
await Promise.all(
editableViews.map(editableView => {
return editableView?.html2block?.({
CONVERT_SORT_LIST.map(type => {
return this._editor.getView(type)?.html2block?.({
editor: this._editor,
element: element,
});
@@ -197,6 +227,11 @@ export class ClipboardUtils {
textToBlock(clipData = ''): ClipBlockInfo[] {
return clipData.split('\n').map((str: string) => {
const isLink = getIsTextLink(str);
if (isLink) {
return linkText2Block(clipData);
}
return {
type: 'text',
properties: {
@@ -5,6 +5,34 @@ import { ClipBlockInfo } from './types';
const getIsLink = (htmlElement: HTMLElement) => {
return ['A', 'IMG'].includes(htmlElement.tagName);
};
export const getIsTextLink = (str: string) => {
const regex = new RegExp(
/https?:\/\/(www\.)?[-a-zA-Z\d@:%._+~#=]{1,256}\.[a-zA-Z\d()]{1,6}\b([-a-zA-Z\d()@:%_+.~#?&//=]*)/gi
);
return regex.test(str);
};
export const linkText2Block = (url: string) => {
return {
type: 'text',
properties: {
text: {
value: [
{
children: [
{
text: url,
},
],
type: 'link',
url: url,
id: getRandomString('link'),
},
],
},
},
children: [],
} as unknown as ClipBlockInfo;
};
const getTextStyle = (htmlElement: HTMLElement) => {
const tagName = htmlElement.tagName;
const textStyle: { [key: string]: any } = {};
@@ -1,9 +1,8 @@
import { StrictMode } from 'react';
import { createRoot, type Root } from 'react-dom/client';
import { BasePlugin } from '../../base-plugin';
import { CommandMenu } from './Menu';
import { PluginRenderRoot } from '../../utils';
import { CommandMenu } from './Menu';
const PLUGIN_NAME = 'command-menu';
@@ -1,28 +1,24 @@
import React, {
useEffect,
useState,
useMemo,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import style9 from 'style9';
import { BlockFlavorKeys } from '@toeverything/datasource/db-service';
import { Virgo, PluginHooks, HookType } from '@toeverything/framework/virgo';
import {
CommonList,
CommonListItem,
commonListContainer,
CommonListItem,
} from '@toeverything/components/common';
import { BlockFlavorKeys } from '@toeverything/datasource/db-service';
import { HookType, PluginHooks, Virgo } from '@toeverything/framework/virgo';
import { domToRect } from '@toeverything/utils';
import { MenuCategories } from './Categories';
import { menuItemsMap, CommandMenuCategories } from './config';
import { styled } from '@toeverything/components/ui';
import { QueryResult } from '../../search';
import {
MuiClickAwayListener as ClickAwayListener,
styled,
} from '@toeverything/components/ui';
import { MenuCategories } from './Categories';
import { CommandMenuCategories, menuItemsMap } from './config';
const RootContainer = styled('div')(({ theme }) => {
return {
@@ -134,59 +130,75 @@ export const CommandMenuContainer = ({
const handleClickUp = useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => {
if (isShow && types && event.code === 'ArrowUp') {
event.preventDefault();
if (!currentItem && types.length) {
setCurrentItem(types[types.length - 1]);
}
if (currentItem) {
const idx = types.indexOf(currentItem);
if (idx > 0) {
setNeedCheckIntoView(true);
setCurrentItem(types[idx - 1]);
}
event.preventDefault();
if (!currentItem && types.length) {
setCurrentItem(types[types.length - 1]);
}
if (currentItem) {
const idx = types.indexOf(currentItem);
if (idx > 0) {
setNeedCheckIntoView(true);
setCurrentItem(types[idx - 1]);
}
}
},
[isShow, types, currentItem]
[types, currentItem]
);
const handleClickDown = useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => {
if (isShow && types && event.code === 'ArrowDown') {
event.preventDefault();
if (!currentItem && types.length) {
setCurrentItem(types[0]);
}
if (currentItem) {
const idx = types.indexOf(currentItem);
if (idx < types.length - 1) {
setNeedCheckIntoView(true);
setCurrentItem(types[idx + 1]);
}
event.preventDefault();
if (!currentItem && types.length) {
setCurrentItem(types[0]);
}
if (currentItem) {
const idx = types.indexOf(currentItem);
if (idx < types.length - 1) {
setNeedCheckIntoView(true);
setCurrentItem(types[idx + 1]);
}
}
},
[isShow, types, currentItem]
[types, currentItem]
);
const handleClickEnter = useCallback(
async (event: React.KeyboardEvent<HTMLDivElement>) => {
if (isShow && event.code === 'Enter' && currentItem) {
event.preventDefault();
onSelected && onSelected(currentItem);
}
event.preventDefault();
onSelected && onSelected(currentItem);
},
[isShow, currentItem, onSelected]
[currentItem, onSelected]
);
const handleKeyDown = useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => {
handleClickUp(event);
handleClickDown(event);
handleClickEnter(event);
if (!isShow) {
return;
}
if (event.nativeEvent.isComposing) {
return;
}
if (types && event.code === 'ArrowUp') {
handleClickUp(event);
return;
}
if (types && event.code === 'ArrowDown') {
handleClickDown(event);
return;
}
if (event.code === 'Enter' && currentItem) {
handleClickEnter(event);
return;
}
},
[handleClickUp, handleClickDown, handleClickEnter]
[
isShow,
types,
currentItem,
handleClickUp,
handleClickDown,
handleClickEnter,
]
);
useEffect(() => {
@@ -212,7 +212,7 @@ export const CommandMenu = ({ editor, hooks, style }: CommandMenuProps) => {
const handleSelected = async (type: BlockFlavorKeys | string) => {
const text = await editor.commands.textCommands.getBlockText(blockId);
const block = await editor.getBlockById(blockId);
let textValue = block.getProperty('text').value;
const textValue = block.getProperty('text').value;
editor.blockHelper.removeSearchSlash(blockId, true);
if (type.startsWith('Virgo')) {
const handler =
@@ -261,23 +261,21 @@ export const CommandMenu = ({ editor, hooks, style }: CommandMenuProps) => {
>
{show ? (
<MuiClickAwayListener onClickAway={handleClickAway}>
<div>
<CommandMenuContainer
editor={editor}
hooks={hooks}
style={{
...commandMenuPosition,
...style,
}}
isShow={show}
blockId={blockId}
onSelected={handleSelected}
onclose={handleClose}
searchBlocks={searchBlocks}
types={types}
categories={categories}
/>
</div>
<CommandMenuContainer
editor={editor}
hooks={hooks}
style={{
...commandMenuPosition,
...style,
}}
isShow={show}
blockId={blockId}
onSelected={handleSelected}
onclose={handleClose}
searchBlocks={searchBlocks}
types={types}
categories={categories}
/>
</MuiClickAwayListener>
) : (
<></>
@@ -1,25 +1,24 @@
import {
HeadingOneIcon,
HeadingTwoIcon,
HeadingThreeIcon,
ToDoIcon,
NumberIcon,
BulletIcon,
CodeIcon,
TextIcon,
PagesIcon,
ImageIcon,
FileIcon,
QuoteIcon,
CalloutIcon,
CodeIcon,
DividerIcon,
FigmaIcon,
YoutubeIcon,
EmbedIcon,
FigmaIcon,
FileIcon,
HeadingOneIcon,
HeadingThreeIcon,
HeadingTwoIcon,
ImageIcon,
NumberIcon,
QuoteIcon,
TextIcon,
ToDoIcon,
YoutubeIcon,
} from '@toeverything/components/icons';
import { SvgIconProps } from '@toeverything/components/ui';
import { Virgo } from '@toeverything/framework/virgo';
import { BlockFlavorKeys, Protocol } from '@toeverything/datasource/db-service';
import { Virgo } from '@toeverything/framework/virgo';
import { without } from '@toeverything/utils';
export enum CommandMenuCategories {
@@ -68,11 +67,12 @@ export const menuItemsMap: {
type: Protocol.Block.Type.text,
icon: TextIcon,
},
{
text: 'Page',
type: 'page',
icon: PagesIcon,
},
// the Page block should not be inserted
// {
// text: 'Page',
// type: Protocol.Block.Type.page,
// icon: PagesIcon,
// },
{
text: 'Heading 1',
type: Protocol.Block.Type.heading1,
@@ -91,7 +91,7 @@ export const menuItemsMap: {
],
[CommandMenuCategories.lists]: [
{
text: 'To do',
text: 'Todo',
type: Protocol.Block.Type.todo,
icon: ToDoIcon,
},
@@ -1,12 +1,7 @@
import { useState, useEffect } from 'react';
import { MuiGrow as Grow, styled } from '@toeverything/components/ui';
import { Protocol } from '@toeverything/datasource/db-service';
import {
MuiClickAwayListener as ClickAwayListener,
MuiGrow as Grow,
styled,
} from '@toeverything/components/ui';
import { Virgo, SelectionInfo } from '@toeverything/framework/virgo';
import { SelectionInfo, Virgo } from '@toeverything/framework/virgo';
import { useEffect, useState } from 'react';
import { InlineMenuToolbar } from './Toolbar';
export type InlineMenuContainerProps = {
@@ -47,11 +42,18 @@ export const InlineMenuContainer = ({ editor }: InlineMenuContainerProps) => {
// This is relative to window
const rect = browserSelection.getRangeAt(0).getBoundingClientRect();
const { top, left } = editor.container.getBoundingClientRect();
const { top, left, right } =
editor.container.getBoundingClientRect();
let menuLeft = rect.left - left;
if (right - rect.right < 500) {
// If the inline-menu is further away from the right than the button itself, a scroll bar will appear
menuLeft -= 500;
}
setSelectionInfo(info);
setShowMenu(true);
setContainerStyle({
left: rect.left - left,
left: menuLeft,
top: rect.top - top - 64,
});
});
@@ -89,6 +91,6 @@ const ToolbarContainer = styled('div')(({ theme }) => ({
alignItems: 'center',
padding: '0 12px',
borderRadius: '10px',
boxShadow: theme.affine.shadows.shadow1,
boxShadow: theme.affine.shadows.shadow3,
backgroundColor: '#fff',
}));
@@ -22,7 +22,6 @@ import {
HeadingTwoIcon,
ImageIcon,
LinkIcon,
MoreIcon,
NumberIcon,
PagesIcon,
QuoteIcon,
@@ -827,13 +826,13 @@ export const getInlineMenuData = ({
nameKey: inlineMenuNamesKeys.backlinks,
onClick: common_handler_for_inline_menu,
},
{
type: INLINE_MENU_UI_TYPES.icon,
icon: MoreIcon,
name: inlineMenuNames.moreActions,
nameKey: inlineMenuNamesKeys.moreActions,
onClick: common_handler_for_inline_menu,
},
// {
// type: INLINE_MENU_UI_TYPES.icon,
// icon: MoreIcon,
// name: inlineMenuNames.moreActions,
// nameKey: inlineMenuNamesKeys.moreActions,
// onClick: common_handler_for_inline_menu,
// },
];
return inlineMenuData.filter(item => Boolean(item));
+14 -6
View File
@@ -41,7 +41,14 @@ export const Select = forwardRef(function CustomSelect<TValue>(
ref: ForwardedRef<HTMLUListElement>
) {
const [isOpen, setIsOpen] = useState(false);
const { width = '100%', style, listboxStyle, placeholder } = props;
const {
width = '100%',
style,
listboxStyle,
placeholder,
onListboxOpenChange,
onChange,
} = props;
const components: SelectUnstyledProps<TValue>['components'] = {
// Root: generateStyledRoot({ width, ...style }),
Root: forwardRef((rootProps, rootRef) => {
@@ -80,14 +87,15 @@ export const Select = forwardRef(function CustomSelect<TValue>(
return (
<SelectUnstyled
listboxOpen={isOpen}
onListboxOpenChange={() => {
setIsOpen(true);
}}
{...props}
listboxOpen={isOpen}
onListboxOpenChange={open => {
setIsOpen(open);
onListboxOpenChange?.(open);
}}
onChange={v => {
setIsOpen(false);
props.onChange && props.onChange(v);
onChange?.(v);
}}
ref={ref}
components={components}
+2
View File
@@ -237,6 +237,8 @@ export const Theme = {
'0px 1px 10px -6px rgba(24, 39, 75, 0.08), 0px 3px 16px -6px rgba(24, 39, 75, 0.04)',
shadow2:
'0px 6px 16px -8px rgba(0,0,0,0.08), 0px 9px 14px 0px rgba(0,0,0,0.05), 0px 12px 24px 16px rgba(0,0,0,0.03)',
shadow3:
'0px 1px 10px -6px rgb(24 39 75 / 50%), 0px 3px 16px -6px rgb(24 39 75 / 30%)',
},
border: ['none'],
spacing: {
+276 -354
View File
File diff suppressed because it is too large Load Diff