mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-11 07:06:28 +08:00
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
@@ -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}
|
||||
|
||||
@@ -4,7 +4,12 @@ import { Editor } from '../editor';
|
||||
import { SelectBlock, SelectInfo } from '../selection';
|
||||
import { Clip } from './clip';
|
||||
import { ClipBlockInfo, OFFICE_CLIPBOARD_MIMETYPE } from './types';
|
||||
import { commonHTML2Block, commonHTML2Text } from './utils';
|
||||
import {
|
||||
commonHTML2Block,
|
||||
commonHTML2Text,
|
||||
getIsTextLink,
|
||||
linkText2Block,
|
||||
} from './utils';
|
||||
export class ClipboardUtils {
|
||||
private _editor: Editor;
|
||||
constructor(editor: Editor) {
|
||||
@@ -222,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 } = {};
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user