mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:48:45 +08:00
Merge pull request #11028 from 2betop/fix-diff-editor
fix: DiffEditor 分装成ui 组件,解决可能不显示最新值的问题
This commit is contained in:
commit
0528169ba9
@ -112,7 +112,7 @@ export const LocaleContext = React.createContext('');
|
||||
|
||||
export function localeable<
|
||||
T extends React.ComponentType<React.ComponentProps<T> & LocaleProps>
|
||||
>(ComposedComponent: T) {
|
||||
>(ComposedComponent: T, methods?: Array<string>) {
|
||||
type OuterProps = JSX.LibraryManagedAttributes<
|
||||
T,
|
||||
Omit<React.ComponentProps<T>, keyof LocaleProps>
|
||||
@ -189,6 +189,17 @@ export function localeable<
|
||||
ComposedComponent
|
||||
);
|
||||
|
||||
if (Array.isArray(methods)) {
|
||||
methods.forEach(method => {
|
||||
if (ComposedComponent.prototype[method]) {
|
||||
(result as any).prototype[method] = function () {
|
||||
const fn = this.ref?.[method];
|
||||
return fn ? fn.apply(this.ref, arguments) : undefined;
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return result as typeof result & {
|
||||
ComposedComponent: T;
|
||||
};
|
||||
|
@ -131,7 +131,7 @@ export function themeable<
|
||||
T extends React.ComponentType<React.ComponentProps<T> & ThemeProps> & {
|
||||
themeKey?: string;
|
||||
}
|
||||
>(ComposedComponent: T) {
|
||||
>(ComposedComponent: T, methods?: Array<string>) {
|
||||
type OuterProps = JSX.LibraryManagedAttributes<
|
||||
T,
|
||||
Omit<React.ComponentProps<T>, keyof ThemeProps>
|
||||
@ -211,6 +211,17 @@ export function themeable<
|
||||
ComposedComponent
|
||||
);
|
||||
|
||||
if (Array.isArray(methods)) {
|
||||
methods.forEach(method => {
|
||||
if (ComposedComponent.prototype[method]) {
|
||||
(result as any).prototype[method] = function () {
|
||||
const fn = this.ref?.[method];
|
||||
return fn ? fn.apply(this.ref, arguments) : undefined;
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return result as typeof result & {
|
||||
ComposedComponent: T;
|
||||
};
|
||||
|
107
packages/amis-ui/src/components/DiffEditor.tsx
Normal file
107
packages/amis-ui/src/components/DiffEditor.tsx
Normal file
@ -0,0 +1,107 @@
|
||||
import React from 'react';
|
||||
import Editor, {EditorBaseProps} from './Editor';
|
||||
import {autobind} from 'amis-core';
|
||||
|
||||
export interface DiffEditorProps extends EditorBaseProps {
|
||||
originValue?: string;
|
||||
}
|
||||
|
||||
export default class DiffEditor extends React.Component<DiffEditorProps> {
|
||||
editor: any;
|
||||
monaco: any;
|
||||
originalEditor: any;
|
||||
modifiedEditor: any;
|
||||
toDispose: Array<Function> = [];
|
||||
domRef = React.createRef<any>();
|
||||
|
||||
componentDidUpdate(prevProps: any) {
|
||||
const {value, originValue} = this.props;
|
||||
|
||||
if (this.originalEditor && originValue !== prevProps.originValue) {
|
||||
this.originalEditor.getModel().setValue(originValue || '');
|
||||
}
|
||||
|
||||
if (this.modifiedEditor && value !== prevProps.value) {
|
||||
this.modifiedEditor.getModel().setValue(value || '');
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.toDispose.forEach(fn => fn());
|
||||
}
|
||||
|
||||
prevHeight = 0;
|
||||
@autobind
|
||||
updateContainerSize(editor: any, monaco: any) {
|
||||
const dom = this.domRef.current?.getDom();
|
||||
if (!dom) {
|
||||
return;
|
||||
}
|
||||
|
||||
const lineHeight = editor.getOption(monaco.editor.EditorOption.lineHeight);
|
||||
const lineCount = editor.getModel()?.getLineCount() || 1;
|
||||
const height = editor.getTopForLineNumber(lineCount + 1) + lineHeight;
|
||||
|
||||
if (this.prevHeight !== height && dom.parentElement) {
|
||||
this.prevHeight = height;
|
||||
dom.parentElement.style.height = `${height}px`;
|
||||
editor.layout();
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
editorFactory(containerElement: any, monaco: any, options: any) {
|
||||
if (this.props.editorFactory) {
|
||||
return this.props.editorFactory(containerElement, monaco, options);
|
||||
}
|
||||
return monaco.editor.createDiffEditor(containerElement, options);
|
||||
}
|
||||
|
||||
@autobind
|
||||
editorDidMount(editor: any, monaco: any) {
|
||||
const {value, originValue, language, onFocus, onBlur, editorDidMount} =
|
||||
this.props;
|
||||
editorDidMount?.(editor, monaco);
|
||||
|
||||
this.monaco = monaco;
|
||||
this.editor = editor;
|
||||
this.modifiedEditor = editor.getModifiedEditor();
|
||||
this.originalEditor = editor.getOriginalEditor();
|
||||
|
||||
onFocus &&
|
||||
this.toDispose.push(
|
||||
this.modifiedEditor.onDidFocusEditorWidget(onFocus).dispose
|
||||
);
|
||||
onBlur &&
|
||||
this.toDispose.push(
|
||||
this.modifiedEditor.onDidBlurEditorWidget(onBlur).dispose
|
||||
);
|
||||
|
||||
this.toDispose.push(
|
||||
this.modifiedEditor.onDidChangeModelDecorations(() => {
|
||||
this.updateContainerSize(this.modifiedEditor, monaco); // typing
|
||||
requestAnimationFrame(
|
||||
this.updateContainerSize.bind(this, this.modifiedEditor, monaco)
|
||||
); // folding
|
||||
}).dispose
|
||||
);
|
||||
|
||||
this.editor.setModel({
|
||||
original: this.monaco.editor.createModel(originValue || '', language),
|
||||
modified: this.monaco.editor.createModel(value, language)
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const {value, originValue, options, ...rest} = this.props;
|
||||
return (
|
||||
<Editor
|
||||
{...rest}
|
||||
ref={this.domRef}
|
||||
editorDidMount={this.editorDidMount}
|
||||
editorFactory={this.editorFactory}
|
||||
isDiffEditor
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
import {ClassNamesFn, themeable} from 'amis-core';
|
||||
import {ClassNamesFn, themeable, ThemeProps} from 'amis-core';
|
||||
import {autobind} from 'amis-core';
|
||||
import {Icon} from './icons';
|
||||
import {LocaleProps, localeable} from 'amis-core';
|
||||
@ -67,33 +67,32 @@ export function monacoFactory(
|
||||
});
|
||||
}
|
||||
|
||||
export interface EditorProps extends LocaleProps {
|
||||
export interface EditorBaseProps {
|
||||
value?: string;
|
||||
defaultValue?: string;
|
||||
width?: number | string;
|
||||
height?: number | string;
|
||||
onChange?: (value: string, event: any) => void;
|
||||
disabled?: boolean;
|
||||
language?: string;
|
||||
editorTheme?: string;
|
||||
allowFullscreen?: boolean;
|
||||
options: {
|
||||
[propName: string]: any;
|
||||
};
|
||||
classPrefix: string;
|
||||
className?: string;
|
||||
classnames: ClassNamesFn;
|
||||
context?: any;
|
||||
style?: any;
|
||||
isDiffEditor?: boolean;
|
||||
placeholder?: string;
|
||||
onFocus?: () => void;
|
||||
onBlur?: () => void;
|
||||
onFocus?: (e: any) => void;
|
||||
onBlur?: (e: any) => void;
|
||||
editorDidMount?: (editor: any, monaco: any) => void;
|
||||
editorWillMount?: (monaco: any) => void;
|
||||
editorWillUnmount?: (editor: any, monaco: any) => void;
|
||||
editorFactory?: (conatainer: HTMLElement, monaco: any, options: any) => any;
|
||||
}
|
||||
|
||||
export interface EditorProps extends EditorBaseProps, LocaleProps, ThemeProps {}
|
||||
|
||||
export interface EditorState {
|
||||
isFullscreen?: boolean;
|
||||
innerWidth?: any;
|
||||
@ -124,6 +123,7 @@ export class Editor extends React.Component<EditorProps, EditorState> {
|
||||
super(props);
|
||||
|
||||
this.wrapperRef = this.wrapperRef.bind(this);
|
||||
this.getDom = this.getDom.bind(this);
|
||||
this.currentValue = props.value;
|
||||
}
|
||||
|
||||
@ -200,6 +200,10 @@ export class Editor extends React.Component<EditorProps, EditorState> {
|
||||
}
|
||||
}
|
||||
|
||||
getDom() {
|
||||
return this.container;
|
||||
}
|
||||
|
||||
loadMonaco() {
|
||||
// 由于 require.config({'vs/nls': { availableLanguages: { '*': 'xxxx' }}}) 只能在初始化之前设置有用,所以这里只能用全局变量的方式来设置。
|
||||
// 另外此方式只是针对 jssdk 和平台有效,对于其他方式还需要再想想。
|
||||
@ -234,6 +238,7 @@ export class Editor extends React.Component<EditorProps, EditorState> {
|
||||
const factory = editorFactory || monacoFactory;
|
||||
this.editor = factory(containerElement, monaco, {
|
||||
...options,
|
||||
readOnly: this.props.disabled,
|
||||
automaticLayout: true,
|
||||
value,
|
||||
language,
|
||||
@ -272,14 +277,14 @@ export class Editor extends React.Component<EditorProps, EditorState> {
|
||||
if (!this.preventTriggerChangeEvent && onChange) {
|
||||
onChange(value, event);
|
||||
}
|
||||
})
|
||||
}).dispose
|
||||
);
|
||||
onFocus &&
|
||||
editor.onDidFocusEditorWidget &&
|
||||
this.disposes.push(editor.onDidFocusEditorWidget(onFocus));
|
||||
this.disposes.push(editor.onDidFocusEditorWidget(onFocus).dispose);
|
||||
onBlur &&
|
||||
editor.onDidBlurEditorWidget &&
|
||||
this.disposes.push(editor.onDidBlurEditorWidget(onBlur));
|
||||
this.disposes.push(editor.onDidBlurEditorWidget(onBlur).dispose);
|
||||
|
||||
const {width = 'auto', height = 'auto'} =
|
||||
this?.editor?._configuration?._elementSizeObserver ?? {};
|
||||
@ -353,4 +358,4 @@ export class Editor extends React.Component<EditorProps, EditorState> {
|
||||
}
|
||||
}
|
||||
|
||||
export default themeable(localeable(Editor));
|
||||
export default themeable(localeable(Editor, ['getDom']), ['getDom']);
|
||||
|
@ -30,6 +30,7 @@ import Drawer from './Drawer';
|
||||
import Tabs from './Tabs';
|
||||
import Tab from './Tab';
|
||||
import Editor from './Editor';
|
||||
import DiffEditor from './DiffEditor';
|
||||
import Html from './Html';
|
||||
export * from './icons';
|
||||
import * as Icons from './icons';
|
||||
@ -167,6 +168,7 @@ export {
|
||||
Tabs,
|
||||
Tab,
|
||||
Editor,
|
||||
DiffEditor,
|
||||
Html,
|
||||
Icons,
|
||||
Layout,
|
||||
|
@ -6,10 +6,9 @@ import {
|
||||
resolveEventData,
|
||||
getVariable
|
||||
} from 'amis-core';
|
||||
import {LazyComponent} from 'amis-core';
|
||||
import {DiffEditor} from 'amis-ui';
|
||||
import {isPureVariable, resolveVariableAndFilter} from 'amis-core';
|
||||
import {FormBaseControlSchema, SchemaTokenizeableString} from '../../Schema';
|
||||
import {autobind} from 'amis-core';
|
||||
|
||||
import type {Position} from 'monaco-editor';
|
||||
import type {ListenerAction} from 'amis-core';
|
||||
@ -42,10 +41,6 @@ export interface DiffControlSchema extends FormBaseControlSchema {
|
||||
|
||||
export type DiffEditorRendererEvent = 'blur' | 'focus';
|
||||
|
||||
function loadComponent(): Promise<any> {
|
||||
return import('amis-ui/lib/components/Editor').then(item => item.default);
|
||||
}
|
||||
|
||||
export interface DiffEditorProps
|
||||
extends FormControlProps,
|
||||
Omit<
|
||||
@ -71,7 +66,14 @@ function normalizeValue(value: any, language?: string) {
|
||||
return value || '';
|
||||
}
|
||||
|
||||
export class DiffEditor extends React.Component<DiffEditorProps, any> {
|
||||
export interface DiffEditorState {
|
||||
focused: boolean;
|
||||
}
|
||||
|
||||
export class DiffEditorRenderer extends React.Component<
|
||||
DiffEditorProps,
|
||||
DiffEditorState
|
||||
> {
|
||||
static defaultProps: Partial<DiffEditorProps> = {
|
||||
language: 'javascript',
|
||||
editorTheme: 'vs',
|
||||
@ -92,25 +94,13 @@ export class DiffEditor extends React.Component<DiffEditorProps, any> {
|
||||
};
|
||||
|
||||
editor: any;
|
||||
monaco: any;
|
||||
originalEditor: any;
|
||||
modifiedEditor: any;
|
||||
toDispose: Array<Function> = [];
|
||||
divRef = React.createRef<HTMLDivElement>();
|
||||
|
||||
constructor(props: DiffEditorProps) {
|
||||
super(props);
|
||||
|
||||
this.handleFocus = this.handleFocus.bind(this);
|
||||
this.handleBlur = this.handleBlur.bind(this);
|
||||
this.editorFactory = this.editorFactory.bind(this);
|
||||
this.handleEditorMounted = this.handleEditorMounted.bind(this);
|
||||
this.handleModifiedEditorChange =
|
||||
this.handleModifiedEditorChange.bind(this);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.toDispose.forEach(fn => fn());
|
||||
}
|
||||
|
||||
doAction(
|
||||
@ -134,7 +124,7 @@ export class DiffEditor extends React.Component<DiffEditorProps, any> {
|
||||
}
|
||||
|
||||
focus() {
|
||||
this.editor.focus();
|
||||
this.editor?.focus();
|
||||
this.setState({focused: true});
|
||||
|
||||
// 最近一次光标位置
|
||||
@ -180,97 +170,8 @@ export class DiffEditor extends React.Component<DiffEditorProps, any> {
|
||||
onBlur?.(e);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: any) {
|
||||
const {data, value, diffValue, language} = this.props;
|
||||
|
||||
if (
|
||||
this.originalEditor &&
|
||||
(diffValue !== prevProps.diffValue || data !== prevProps.data)
|
||||
) {
|
||||
this.originalEditor.getModel().setValue(
|
||||
isPureVariable(diffValue as string)
|
||||
? normalizeValue(
|
||||
resolveVariableAndFilter(
|
||||
diffValue || '',
|
||||
data,
|
||||
'| raw',
|
||||
() => ''
|
||||
),
|
||||
language
|
||||
)
|
||||
: normalizeValue(diffValue, language)
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
this.modifiedEditor &&
|
||||
value !== prevProps.value &&
|
||||
!this.state.focused
|
||||
) {
|
||||
this.modifiedEditor.getModel().setValue(
|
||||
isPureVariable(value as string)
|
||||
? normalizeValue(
|
||||
resolveVariableAndFilter(value || '', data, '| raw', () => ''),
|
||||
language
|
||||
)
|
||||
: normalizeValue(value, language)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
editorFactory(containerElement: any, monaco: any, options: any) {
|
||||
return monaco.editor.createDiffEditor(containerElement, options);
|
||||
}
|
||||
|
||||
handleEditorMounted(editor: any, monaco: any) {
|
||||
const {value, data, language, diffValue} = this.props;
|
||||
|
||||
this.monaco = monaco;
|
||||
this.editor = editor;
|
||||
this.modifiedEditor = editor.getModifiedEditor();
|
||||
this.originalEditor = editor.getOriginalEditor();
|
||||
|
||||
this.toDispose.push(
|
||||
this.modifiedEditor.onDidFocusEditorWidget(this.handleFocus).dispose
|
||||
);
|
||||
this.toDispose.push(
|
||||
this.modifiedEditor.onDidBlurEditorWidget(this.handleBlur).dispose
|
||||
);
|
||||
this.toDispose.push(
|
||||
this.modifiedEditor.onDidChangeModelContent(
|
||||
this.handleModifiedEditorChange
|
||||
).dispose
|
||||
);
|
||||
|
||||
this.toDispose.push(
|
||||
this.modifiedEditor.onDidChangeModelDecorations(() => {
|
||||
this.updateContainerSize(this.modifiedEditor, monaco); // typing
|
||||
requestAnimationFrame(
|
||||
this.updateContainerSize.bind(this, this.modifiedEditor, monaco)
|
||||
); // folding
|
||||
}).dispose
|
||||
);
|
||||
|
||||
this.editor.setModel({
|
||||
original: this.monaco.editor.createModel(
|
||||
isPureVariable(diffValue as string)
|
||||
? normalizeValue(
|
||||
resolveVariableAndFilter(diffValue || '', data, '| raw'),
|
||||
language
|
||||
)
|
||||
: normalizeValue(diffValue, language),
|
||||
language
|
||||
),
|
||||
modified: this.monaco.editor.createModel(
|
||||
normalizeValue(value, language),
|
||||
language
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
async handleModifiedEditorChange() {
|
||||
async handleChange(value: any) {
|
||||
const {onChange, dispatchEvent} = this.props;
|
||||
const value = this.modifiedEditor.getModel().getValue();
|
||||
|
||||
const rendererEvent = await dispatchEvent(
|
||||
'change',
|
||||
@ -284,22 +185,8 @@ export class DiffEditor extends React.Component<DiffEditorProps, any> {
|
||||
onChange && onChange(value);
|
||||
}
|
||||
|
||||
prevHeight = 0;
|
||||
@autobind
|
||||
updateContainerSize(editor: any, monaco: any) {
|
||||
if (!this.divRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const lineHeight = editor.getOption(monaco.editor.EditorOption.lineHeight);
|
||||
const lineCount = editor.getModel()?.getLineCount() || 1;
|
||||
const height = editor.getTopForLineNumber(lineCount + 1) + lineHeight;
|
||||
|
||||
if (this.prevHeight !== height) {
|
||||
this.prevHeight = height;
|
||||
this.divRef.current.style.height = `${height}px`;
|
||||
editor.layout();
|
||||
}
|
||||
handleEditorMounted(editor: any) {
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -313,12 +200,22 @@ export class DiffEditor extends React.Component<DiffEditorProps, any> {
|
||||
options,
|
||||
language,
|
||||
editorTheme,
|
||||
classnames: cx
|
||||
diffValue,
|
||||
classnames: cx,
|
||||
data
|
||||
} = this.props;
|
||||
|
||||
const originValue = isPureVariable(diffValue as string)
|
||||
? normalizeValue(
|
||||
resolveVariableAndFilter(diffValue || '', data, '| raw'),
|
||||
language
|
||||
)
|
||||
: normalizeValue(diffValue, language);
|
||||
|
||||
const finalValue = normalizeValue(value, language);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={this.divRef}
|
||||
className={cx(
|
||||
'EditorControl',
|
||||
size ? `EditorControl--${size}` : '',
|
||||
@ -328,20 +225,17 @@ export class DiffEditor extends React.Component<DiffEditorProps, any> {
|
||||
}
|
||||
)}
|
||||
>
|
||||
<LazyComponent
|
||||
getComponent={loadComponent}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
<DiffEditor
|
||||
value={finalValue}
|
||||
originValue={originValue}
|
||||
onChange={this.handleChange}
|
||||
disabled={disabled}
|
||||
language={language}
|
||||
editorTheme={editorTheme}
|
||||
options={options}
|
||||
onFocus={this.handleFocus}
|
||||
onBlur={this.handleBlur}
|
||||
editorDidMount={this.handleEditorMounted}
|
||||
editorFactory={this.editorFactory}
|
||||
options={{
|
||||
...options,
|
||||
readOnly: disabled
|
||||
}}
|
||||
isDiffEditor
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@ -352,9 +246,9 @@ export class DiffEditor extends React.Component<DiffEditorProps, any> {
|
||||
type: `diff-editor`,
|
||||
sizeMutable: false
|
||||
})
|
||||
export class DiffEditorControlRenderer extends DiffEditor {
|
||||
export class DiffEditorControlRenderer extends DiffEditorRenderer {
|
||||
static defaultProps = {
|
||||
...DiffEditor.defaultProps
|
||||
...DiffEditorRenderer.defaultProps
|
||||
};
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user