amis-saas-6892 fix: 长文本公式输入框支持全屏

Change-Id: Ia0998f0baeaa4c9e4f359e6b87b77fbb4226d953
This commit is contained in:
igrowp 2022-10-26 16:37:19 +08:00
parent fa51bef367
commit 82749c63c5
3 changed files with 97 additions and 35 deletions

View File

@ -7,7 +7,6 @@ import {autobind, FormControlProps} from 'amis-core';
import cx from 'classnames';
import isEqual from 'lodash/isEqual';
import {FormItem, Button, Icon} from 'amis';
import {renderFormulaValue} from './textarea-formula/TextareaFormulaControl';
import FormulaPicker from './textarea-formula/FormulaPicker';
import {FormulaEditor} from 'amis-ui/lib/components/formula/Editor';
import type {VariableItem} from 'amis-ui/lib/components/formula/Editor';
@ -83,6 +82,13 @@ export default class ExpressionFormulaControl extends React.Component<
});
}
@autobind
renderFormulaValue(item: any) {
const html = {__html: item.html};
// bca-disable-next-line
return <span dangerouslySetInnerHTML={html}></span>;
}
async resolveVariablesFromScope() {
const {node, manager} = this.props.formProps || this.props;
await manager?.getContextSchemas(node);
@ -140,7 +146,7 @@ export default class ExpressionFormulaControl extends React.Component<
tooltip={{
placement: 'bottom',
tooltipClassName: 'btn-configured-tooltip',
children: () => renderFormulaValue(highlightValue)
children: () => this.renderFormulaValue(highlightValue)
}}
onClick={this.openFormulaPickerModal}
>

View File

@ -6,6 +6,7 @@ import React from 'react';
import isEqual from 'lodash/isEqual';
import cx from 'classnames';
import {
Icon,
render as amisRender,
FormItem
} from 'amis';
@ -39,6 +40,8 @@ interface TextareaFormulaControlState {
formulaPickerValue: string; // 公式编辑器内容
expressionBrace?: Array<CodeMirror.Position>; // 表达式所在位置
isFullscreen: boolean; //是否全屏
}
export class TextareaFormulaControl extends React.Component<
@ -46,7 +49,8 @@ export class TextareaFormulaControl extends React.Component<
TextareaFormulaControlState
> {
static defaultProps: Partial<TextareaFormulaControlProps> = {
variableMode: 'tabs'
variableMode: 'tabs',
height: 100
};
isUnmount: boolean;
@ -62,7 +66,8 @@ export class TextareaFormulaControl extends React.Component<
variables: [],
menusList: [],
formulaPickerOpen: false,
formulaPickerValue: ''
formulaPickerValue: '',
isFullscreen: false
};
}
@ -121,7 +126,7 @@ export class TextareaFormulaControl extends React.Component<
}
@autobind
onExpressionClick(expression: string, brace: Array<CodeMirror.Position>) {
onExpressionClick(expression: string, brace?: Array<CodeMirror.Position>) {
this.setState({
formulaPickerValue: expression,
formulaPickerOpen: true,
@ -137,7 +142,7 @@ export class TextareaFormulaControl extends React.Component<
@autobind
handleConfirm(value: any) {
const {expressionBrace} = this.state;
// // 去除可能包裹的最外层的${}
// 去除可能包裹的最外层的${}
value = value.replace(/^\$\{(.*)\}$/, (match: string, p1: string) => p1);
value = value ? `\${${value}}` : value;
this.editorPlugin?.insertContent(value, 'expression', expressionBrace);
@ -164,6 +169,13 @@ export class TextareaFormulaControl extends React.Component<
this.editorPlugin = new FormulaPlugin(editor, cm, () => ({...this.props, variables}), this.onExpressionClick);
}
@autobind
handleFullscreenModeChange() {
this.setState({
isFullscreen: !this.state.isFullscreen
});
}
render() {
const {
className,
@ -173,7 +185,7 @@ export class TextareaFormulaControl extends React.Component<
height,
...rest
} = this.props;
const {value, menusList, formulaPickerOpen, formulaPickerValue} = this.state;
const {value, menusList, formulaPickerOpen, formulaPickerValue, isFullscreen} = this.state;
const variables = rest.variables || this.state.variables || [];
@ -184,32 +196,49 @@ export class TextareaFormulaControl extends React.Component<
}
return (
<div className={cx('ae-TextareaFormulaControl')} ref={(ref: any) => this.wrapRef = ref}>
<div className='ae-TextareaResultBox' style={resultBoxStyle}>
<div className="ae-TextareaResultBox-content">
<CodeMirrorEditor
className="ae-TextareaResultBox-editor"
value={value}
onChange={this.handleOnChange}
editorFactory={this.editorFactory}
editorDidMount={this.handleEditorMounted}
/>
{amisRender({
type: 'dropdown-button',
className: 'ae-TextareaResultBox-dropdown',
menuClassName: 'ae-TextareaResultBox-menus',
popOverContainer: this.wrapRef,
label: '',
level: 'link',
size: 'md',
icon: 'fa fa-plus',
placement: 'top',
trigger: 'hover',
closeOnClick: true,
closeOnOutside: true,
hideCaret: true,
buttons: menusList
})}
<div className={cx('ae-TextareaFormulaControl', {'is-fullscreen': this.state.isFullscreen})} ref={(ref: any) => this.wrapRef = ref}>
<div
className={cx('ae-TextareaResultBox')}
style={resultBoxStyle}
>
<CodeMirrorEditor
className="ae-TextareaResultBox-editor"
value={value}
onChange={this.handleOnChange}
editorFactory={this.editorFactory}
editorDidMount={this.handleEditorMounted}
/>
{amisRender({
type: 'dropdown-button',
className: 'ae-TextareaResultBox-dropdown',
menuClassName: 'ae-TextareaResultBox-menus',
popOverContainer: this.wrapRef,
label: '',
level: 'link',
size: 'md',
icon: 'fa fa-plus',
trigger: 'hover',
closeOnClick: true,
closeOnOutside: true,
hideCaret: true,
buttons: menusList
})}
<div className="ae-TextareaResultBox-fullscreen">
<a
className={cx('Modal-fullscreen')}
data-tooltip={
isFullscreen
? '退出全屏'
: '全屏'
}
data-position="left"
onClick={this.handleFullscreenModeChange}
>
<Icon
icon={isFullscreen ? 'compress-alt' : 'expand-alt'}
className="icon"
/>
</a>
</div>
</div>
{formulaPickerOpen ? (

View File

@ -22,7 +22,7 @@ export class FormulaPlugin {
readonly editor: CodeMirror.Editor,
readonly cm: typeof CodeMirror,
readonly getProps: () => TextareaFormulaControlProps,
readonly onExpressionClick: (expression: string, brace: Array<CodeMirror.Position>) => any
readonly onExpressionClick: (expression: string, brace?: Array<CodeMirror.Position>) => any
) {
const {value} = this.getProps();
if (value) {
@ -57,6 +57,32 @@ export class FormulaPlugin {
}
}
// 找到表达式所在的位置
getExpressionBrace(expression: string) {
const editor = this.editor;
const lines = editor.lineCount();
for (let line = 0; line < lines; line++) {
const content = editor.getLine(line);
const braces = this.computedBracesPosition(content);
for (let i = 0; i < braces.length; i++) {
// 替换每个公式表达式中的内容
const start = braces[i].begin;
const end = braces[i].end;
if (expression === content.slice(start, end)) {
return [{
line: line,
ch: start - 2
},
{
line: line,
ch: end + 1
}];
}
}
}
return undefined;
}
// 计算 `${`、`}` 括号的位置,如 ${a}+${b}, 结果是 [ { from: 0, to: 3 }, { from: 5, to: 8 } ]
computedBracesPosition(exp: string) {
const braces: {begin: number; end: number}[] = [];
@ -155,7 +181,8 @@ export class FormulaPlugin {
text.innerText = '表达式';
text.setAttribute('data-expression', expression);
text.onclick = () => {
this.onExpressionClick(expression, [from, to]);
const brace = this.getExpressionBrace(expression);
this.onExpressionClick(expression, brace);
}
const {variables} = this.getProps();
const highlightValue = FormulaEditor.highlightValue(expression, variables) || {