mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-23 04:51:49 +08:00
@@ -3,6 +3,7 @@ import React, { useCallback } from 'react';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Descendant } from 'slate';
|
import { Descendant } from 'slate';
|
||||||
import { RenderElementProps } from 'slate-react';
|
import { RenderElementProps } from 'slate-react';
|
||||||
|
import { styled } from '@toeverything/components/ui';
|
||||||
|
|
||||||
export type DoubleLinkElement = {
|
export type DoubleLinkElement = {
|
||||||
type: 'link';
|
type: 'link';
|
||||||
@@ -13,6 +14,10 @@ export type DoubleLinkElement = {
|
|||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const StyledLink = styled('a')({
|
||||||
|
cursor: 'pointer',
|
||||||
|
});
|
||||||
|
|
||||||
export const DoubleLinkComponent = (props: RenderElementProps) => {
|
export const DoubleLinkComponent = (props: RenderElementProps) => {
|
||||||
const { attributes, children, element } = props;
|
const { attributes, children, element } = props;
|
||||||
const doubleLinkElement = element as DoubleLinkElement;
|
const doubleLinkElement = element as DoubleLinkElement;
|
||||||
@@ -32,15 +37,20 @@ export const DoubleLinkComponent = (props: RenderElementProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<span onClick={handleClickLinkText}>
|
<span onClick={handleClickLinkText}>
|
||||||
<a {...attributes} style={{ cursor: 'pointer' }}>
|
<StyledLink {...attributes}>
|
||||||
<PagesIcon
|
<PagesIcon
|
||||||
style={{ verticalAlign: 'middle', height: '20px' }}
|
style={{
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
height: '1em',
|
||||||
|
fontSize: 'inherit',
|
||||||
|
marginBottom: '.2em',
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<span>
|
<span>
|
||||||
{children}
|
{children}
|
||||||
{displayValue}
|
{displayValue}
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</StyledLink>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ const CodeBlock = styled('div')(({ theme }) => ({
|
|||||||
flexWrap: 'wrap',
|
flexWrap: 'wrap',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
transition: 'opacity 1.5s',
|
transition: 'opacity .15s',
|
||||||
},
|
},
|
||||||
'.copy-block': {
|
'.copy-block': {
|
||||||
padding: '0px 10px',
|
padding: '0px 10px',
|
||||||
@@ -139,10 +139,21 @@ const CodeBlock = styled('div')(({ theme }) => ({
|
|||||||
outline: 'none !important',
|
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) => {
|
export const CodeView = ({ block, editor }: CreateCodeView) => {
|
||||||
const initValue: string = block.getProperty('text')?.value?.[0]?.text;
|
const initValue: string = block.getProperty('text')?.value?.[0]?.text;
|
||||||
const langType: string = block.getProperty('lang');
|
const langType: string = block.getProperty('lang');
|
||||||
const [extensions, setExtensions] = useState<Extension[]>();
|
const [extensions, setExtensions] = useState<Extension[]>();
|
||||||
|
const [showOperationPanel, setShowOperationPanel] = useState(false);
|
||||||
|
const isSelecting = useRef(false);
|
||||||
const codeMirror = useRef<ReactCodeMirrorRef>();
|
const codeMirror = useRef<ReactCodeMirrorRef>();
|
||||||
const focusCode = () => {
|
const focusCode = () => {
|
||||||
if (codeMirror.current) {
|
if (codeMirror.current) {
|
||||||
@@ -181,8 +192,15 @@ export const CodeView = ({ block, editor }: CreateCodeView) => {
|
|||||||
onKeyDown={e => {
|
onKeyDown={e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}}
|
}}
|
||||||
|
onMouseEnter={() => {
|
||||||
|
isSelecting.current = false;
|
||||||
|
setShowOperationPanel(true);
|
||||||
|
}}
|
||||||
|
onMouseLeave={() => {
|
||||||
|
!isSelecting.current && setShowOperationPanel(false);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div className="operation">
|
<StyledOperationalPanel show={showOperationPanel}>
|
||||||
<div className="select">
|
<div className="select">
|
||||||
<Select
|
<Select
|
||||||
width={128}
|
width={128}
|
||||||
@@ -192,6 +210,9 @@ export const CodeView = ({ block, editor }: CreateCodeView) => {
|
|||||||
onChange={(selectedValue: string) => {
|
onChange={(selectedValue: string) => {
|
||||||
handleLangChange(selectedValue);
|
handleLangChange(selectedValue);
|
||||||
}}
|
}}
|
||||||
|
onListboxOpenChange={() => {
|
||||||
|
isSelecting.current = true;
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{Object.keys(langs).map(item => {
|
{Object.keys(langs).map(item => {
|
||||||
return (
|
return (
|
||||||
@@ -208,7 +229,7 @@ export const CodeView = ({ block, editor }: CreateCodeView) => {
|
|||||||
Copy
|
Copy
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</StyledOperationalPanel>
|
||||||
|
|
||||||
<CodeMirror
|
<CodeMirror
|
||||||
ref={codeMirror}
|
ref={codeMirror}
|
||||||
|
|||||||
@@ -4,7 +4,12 @@ import { Editor } from '../editor';
|
|||||||
import { SelectBlock, SelectInfo } from '../selection';
|
import { SelectBlock, SelectInfo } from '../selection';
|
||||||
import { Clip } from './clip';
|
import { Clip } from './clip';
|
||||||
import { ClipBlockInfo, OFFICE_CLIPBOARD_MIMETYPE } from './types';
|
import { ClipBlockInfo, OFFICE_CLIPBOARD_MIMETYPE } from './types';
|
||||||
import { commonHTML2Block, commonHTML2Text } from './utils';
|
import {
|
||||||
|
commonHTML2Block,
|
||||||
|
commonHTML2Text,
|
||||||
|
getIsTextLink,
|
||||||
|
linkText2Block,
|
||||||
|
} from './utils';
|
||||||
export class ClipboardUtils {
|
export class ClipboardUtils {
|
||||||
private _editor: Editor;
|
private _editor: Editor;
|
||||||
constructor(editor: Editor) {
|
constructor(editor: Editor) {
|
||||||
@@ -222,6 +227,11 @@ export class ClipboardUtils {
|
|||||||
|
|
||||||
textToBlock(clipData = ''): ClipBlockInfo[] {
|
textToBlock(clipData = ''): ClipBlockInfo[] {
|
||||||
return clipData.split('\n').map((str: string) => {
|
return clipData.split('\n').map((str: string) => {
|
||||||
|
const isLink = getIsTextLink(str);
|
||||||
|
if (isLink) {
|
||||||
|
return linkText2Block(clipData);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'text',
|
type: 'text',
|
||||||
properties: {
|
properties: {
|
||||||
|
|||||||
@@ -5,6 +5,34 @@ import { ClipBlockInfo } from './types';
|
|||||||
const getIsLink = (htmlElement: HTMLElement) => {
|
const getIsLink = (htmlElement: HTMLElement) => {
|
||||||
return ['A', 'IMG'].includes(htmlElement.tagName);
|
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 getTextStyle = (htmlElement: HTMLElement) => {
|
||||||
const tagName = htmlElement.tagName;
|
const tagName = htmlElement.tagName;
|
||||||
const textStyle: { [key: string]: any } = {};
|
const textStyle: { [key: string]: any } = {};
|
||||||
|
|||||||
@@ -41,7 +41,14 @@ export const Select = forwardRef(function CustomSelect<TValue>(
|
|||||||
ref: ForwardedRef<HTMLUListElement>
|
ref: ForwardedRef<HTMLUListElement>
|
||||||
) {
|
) {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
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'] = {
|
const components: SelectUnstyledProps<TValue>['components'] = {
|
||||||
// Root: generateStyledRoot({ width, ...style }),
|
// Root: generateStyledRoot({ width, ...style }),
|
||||||
Root: forwardRef((rootProps, rootRef) => {
|
Root: forwardRef((rootProps, rootRef) => {
|
||||||
@@ -80,14 +87,15 @@ export const Select = forwardRef(function CustomSelect<TValue>(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SelectUnstyled
|
<SelectUnstyled
|
||||||
listboxOpen={isOpen}
|
|
||||||
onListboxOpenChange={() => {
|
|
||||||
setIsOpen(true);
|
|
||||||
}}
|
|
||||||
{...props}
|
{...props}
|
||||||
|
listboxOpen={isOpen}
|
||||||
|
onListboxOpenChange={open => {
|
||||||
|
setIsOpen(open);
|
||||||
|
onListboxOpenChange?.(open);
|
||||||
|
}}
|
||||||
onChange={v => {
|
onChange={v => {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
props.onChange && props.onChange(v);
|
onChange?.(v);
|
||||||
}}
|
}}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
components={components}
|
components={components}
|
||||||
|
|||||||
Reference in New Issue
Block a user