chore: replace all cn comments of fe to en version by volc api (#320)
This commit is contained in:
@@ -31,7 +31,7 @@ interface ExpressionEditorCounterProps {
|
||||
}
|
||||
|
||||
/**
|
||||
* 长度计数器
|
||||
* length counter
|
||||
*/
|
||||
export const ExpressionEditorCounter: FC<
|
||||
ExpressionEditorCounterProps
|
||||
|
||||
@@ -36,7 +36,7 @@ interface ExpressionEditorRenderProps {
|
||||
}
|
||||
|
||||
/**
|
||||
* 应当只包含编辑器逻辑,业务无关
|
||||
* It should only contain editor logic, business-independent
|
||||
*/
|
||||
export const ExpressionEditorRender: React.FC<
|
||||
ExpressionEditorRenderProps
|
||||
@@ -57,7 +57,7 @@ export const ExpressionEditorRender: React.FC<
|
||||
editor={model.editor}
|
||||
initialValue={model.lines}
|
||||
onChange={value => {
|
||||
// eslint-disable-next-line @typescript-eslint/require-await -- 防止阻塞 slate 渲染
|
||||
// eslint-disable-next-line @typescript-eslint/require-await -- prevent blocking slate rendering
|
||||
const asyncOnChange = async () => {
|
||||
const lines = value as ExpressionEditorLine[];
|
||||
model.change(lines);
|
||||
|
||||
@@ -45,9 +45,9 @@ import {
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
/** 内置函数 */
|
||||
/** built-in function */
|
||||
namespace SuggestionViewUtils {
|
||||
/** 编辑器选中事件处理 */
|
||||
/** Editor selected event handling */
|
||||
export const editorSelectHandler = (params: {
|
||||
reducer: SuggestionReducer;
|
||||
payload: ExpressionEditorEventParams<ExpressionEditorEvent.Select>;
|
||||
@@ -55,7 +55,7 @@ namespace SuggestionViewUtils {
|
||||
const { reducer, payload } = params;
|
||||
const [state, dispatch] = reducer;
|
||||
|
||||
// 设置解析数据
|
||||
// Set up parse data
|
||||
const parseData = ExpressionEditorParser.parse({
|
||||
lineContent: payload.content,
|
||||
lineOffset: payload.offset,
|
||||
@@ -83,7 +83,7 @@ namespace SuggestionViewUtils {
|
||||
},
|
||||
});
|
||||
|
||||
// 重置UI组件内部状态
|
||||
// Reset UI component internal state
|
||||
const shouldRefresh = parseData.content.reachable === '';
|
||||
if (shouldRefresh) {
|
||||
dispatch({
|
||||
@@ -91,7 +91,7 @@ namespace SuggestionViewUtils {
|
||||
});
|
||||
}
|
||||
|
||||
// 设置选中值
|
||||
// Set the selected value
|
||||
const selected = SuggestionViewUtils.computeSelected({
|
||||
model: state.model,
|
||||
parseData,
|
||||
@@ -101,7 +101,7 @@ namespace SuggestionViewUtils {
|
||||
payload: selected,
|
||||
});
|
||||
|
||||
// 设置可见变量树
|
||||
// Set the visible variable tree
|
||||
const variableTree = SuggestionViewUtils.computeVariableTree({
|
||||
model: state.model,
|
||||
parseData,
|
||||
@@ -111,7 +111,7 @@ namespace SuggestionViewUtils {
|
||||
payload: variableTree,
|
||||
});
|
||||
|
||||
// 设置匹配树枝
|
||||
// Set matching branches
|
||||
const matchTreeBranch: ExpressionEditorTreeNode[] | undefined =
|
||||
ExpressionEditorTreeHelper.matchTreeBranch({
|
||||
tree: state.model.variableTree,
|
||||
@@ -122,7 +122,7 @@ namespace SuggestionViewUtils {
|
||||
payload: matchTreeBranch,
|
||||
});
|
||||
|
||||
// 设置空内容
|
||||
// Set empty content
|
||||
const emptyContent = SuggestionViewUtils.computeEmptyContent({
|
||||
parseData,
|
||||
fullVariableTree: state.model.variableTree,
|
||||
@@ -134,7 +134,7 @@ namespace SuggestionViewUtils {
|
||||
payload: emptyContent,
|
||||
});
|
||||
|
||||
// 设置UI相对坐标
|
||||
// Set UI relative coordinates
|
||||
const rect = SuggestionViewUtils.computeRect(state);
|
||||
dispatch({
|
||||
type: SuggestionActionType.SetRect,
|
||||
@@ -147,9 +147,9 @@ namespace SuggestionViewUtils {
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: 设置搜索值,很hack的逻辑,后面建议重构不用semi组件,自己写一个
|
||||
// FIXME: Set the search value, very hacked logic. Later, it is recommended to refactor without semi components, and write one yourself.
|
||||
if (!state.ref.tree.current) {
|
||||
// 不设为可见获取不到ref
|
||||
// Not set to visible can't get ref
|
||||
dispatch({
|
||||
type: SuggestionActionType.SetVisible,
|
||||
payload: true,
|
||||
@@ -168,7 +168,7 @@ namespace SuggestionViewUtils {
|
||||
return 1;
|
||||
};
|
||||
|
||||
/** 计算可见时相对父容器坐标 */
|
||||
/** Calculate relative parent container coordinates when visible */
|
||||
export const computeRect = (
|
||||
state: SuggestionState,
|
||||
):
|
||||
@@ -194,12 +194,12 @@ namespace SuggestionViewUtils {
|
||||
left: (rect.left - containerRect.left) / getFinalScale(state),
|
||||
};
|
||||
} catch (e) {
|
||||
// slate DOM 计算报错可忽略
|
||||
// Slate DOM calculation error can be ignored
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
/** 计算当前选中变量 */
|
||||
/** Calculate the currently selected variable */
|
||||
export const computeSelected = (params: {
|
||||
model: ExpressionEditorModel;
|
||||
parseData: ExpressionEditorParseData;
|
||||
@@ -218,7 +218,7 @@ namespace SuggestionViewUtils {
|
||||
return treeBrach[treeBrach.length - 1];
|
||||
};
|
||||
|
||||
/** 计算当前搜索值 */
|
||||
/** Calculate the current search value */
|
||||
export const computeSearch = (
|
||||
parseData: ExpressionEditorParseData,
|
||||
): string => {
|
||||
@@ -229,7 +229,7 @@ namespace SuggestionViewUtils {
|
||||
const lastSegment =
|
||||
segments[segments.length - 1].type ===
|
||||
ExpressionEditorSegmentType.ArrayIndex
|
||||
? segments[segments.length - 2] // 数组索引属于上一层级,需要去除防止影响到搜索值
|
||||
? segments[segments.length - 2] // The array index belongs to the previous level and needs to be removed to prevent it from affecting the search value
|
||||
: segments[segments.length - 1];
|
||||
if (
|
||||
!lastSegment ||
|
||||
@@ -240,7 +240,7 @@ namespace SuggestionViewUtils {
|
||||
return lastSegment.objectKey;
|
||||
};
|
||||
|
||||
/** 计算裁剪层级的变量树 */
|
||||
/** Calculate the variable tree of the clipping level */
|
||||
export const computeVariableTree = (params: {
|
||||
model: ExpressionEditorModel;
|
||||
parseData: ExpressionEditorParseData;
|
||||
@@ -293,7 +293,7 @@ namespace SuggestionViewUtils {
|
||||
export const keyboardSelectedClassName = () =>
|
||||
styles['expression-editor-suggestion-keyboard-selected'];
|
||||
|
||||
/** 将选中项设为高亮 */
|
||||
/** Highlight the selected item */
|
||||
export const setUIOptionSelected = (uiOption: Element): void => {
|
||||
if (
|
||||
!uiOption?.classList?.add ||
|
||||
@@ -305,7 +305,7 @@ namespace SuggestionViewUtils {
|
||||
uiOption.classList.add(SuggestionViewUtils.keyboardSelectedClassName());
|
||||
};
|
||||
|
||||
/** 获取所有选项UI元素 */
|
||||
/** Get all options UI elements */
|
||||
export const computeUIOptions = (
|
||||
state: SuggestionState,
|
||||
):
|
||||
@@ -315,14 +315,14 @@ namespace SuggestionViewUtils {
|
||||
selectedOption?: Element;
|
||||
}
|
||||
| undefined => {
|
||||
// 获取所有的选项元素
|
||||
// Get all option elements
|
||||
const optionListDom =
|
||||
state.ref.suggestion.current?.children?.[0]?.children?.[1]?.children;
|
||||
if (!optionListDom) {
|
||||
return;
|
||||
}
|
||||
const optionList = Array.from(optionListDom);
|
||||
// 找到当前高亮的选项
|
||||
// Find the currently highlighted option
|
||||
const selectedIndex = optionList.findIndex(element =>
|
||||
element.classList.contains(keyboardSelectedClassName()),
|
||||
);
|
||||
@@ -333,7 +333,7 @@ namespace SuggestionViewUtils {
|
||||
};
|
||||
};
|
||||
|
||||
/** 禁止变更 visible 防止 ui 抖动 */
|
||||
/** Disable visible changes Prevent UI jitter */
|
||||
export const preventVisibleJitter = (
|
||||
reducer: SuggestionReducer,
|
||||
time = 150,
|
||||
@@ -354,18 +354,18 @@ namespace SuggestionViewUtils {
|
||||
}, time);
|
||||
};
|
||||
|
||||
/** 清空键盘UI选项 */
|
||||
/** Clear keyboard UI options */
|
||||
export const clearSelectedUIOption = (state: SuggestionState) => {
|
||||
const uiOptions = SuggestionViewUtils.computeUIOptions(state);
|
||||
if (uiOptions?.selectedOption) {
|
||||
// 清空键盘选中状态
|
||||
// Clear keyboard selection
|
||||
uiOptions.selectedOption.classList.remove(
|
||||
SuggestionViewUtils.keyboardSelectedClassName(),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/** 默认键盘UI选项为第一项 */
|
||||
/** The default keyboard UI option is the first item */
|
||||
export const selectFirstUIOption = (state: SuggestionState) => {
|
||||
const uiOptions = SuggestionViewUtils.computeUIOptions(state);
|
||||
if (!uiOptions?.optionList) {
|
||||
@@ -375,12 +375,12 @@ namespace SuggestionViewUtils {
|
||||
if (!uiOptions?.optionList?.[0]?.classList?.add) {
|
||||
return;
|
||||
}
|
||||
// 默认首项高亮
|
||||
// Default first item highlighting
|
||||
SuggestionViewUtils.setUIOptionSelected(uiOptions.optionList[0]);
|
||||
};
|
||||
}
|
||||
|
||||
/** 选中节点 */
|
||||
/** selected node */
|
||||
export const useSelectNode = (reducer: SuggestionReducer) => {
|
||||
const [state] = reducer;
|
||||
return useCallback(
|
||||
@@ -403,7 +403,7 @@ export const useSelectNode = (reducer: SuggestionReducer) => {
|
||||
},
|
||||
};
|
||||
const insertText = `${ExpressionEditorToken.FullStart}${fullPath}${ExpressionEditorToken.FullEnd}`;
|
||||
// 替换文本
|
||||
// replacement text
|
||||
Transforms.insertText(state.model.editor, insertText, {
|
||||
at: selection,
|
||||
});
|
||||
@@ -412,12 +412,12 @@ export const useSelectNode = (reducer: SuggestionReducer) => {
|
||||
);
|
||||
};
|
||||
|
||||
/** 挂载监听器 */
|
||||
/** mount the listener */
|
||||
export const useListeners = (reducer: SuggestionReducer) => {
|
||||
const [state, dispatch] = reducer;
|
||||
|
||||
useEffect(() => {
|
||||
// 挂载监听: 鼠标点击事件
|
||||
// Mount Listening: Mouse Click Events
|
||||
const mouseHandler = (e: MouseEvent) => {
|
||||
if (!state.visible || !state.ref.suggestion.current) {
|
||||
return;
|
||||
@@ -439,13 +439,13 @@ export const useListeners = (reducer: SuggestionReducer) => {
|
||||
window.removeEventListener('mousedown', mouseHandler);
|
||||
};
|
||||
return () => {
|
||||
// 销毁时卸载监听器防止内存泄露
|
||||
// Prevent memory leaks by uninstalling listeners when destroyed
|
||||
mouseDisposer();
|
||||
};
|
||||
}, [state]);
|
||||
|
||||
useEffect(() => {
|
||||
// 挂载监听: 编辑器选择事件
|
||||
// Mount Listening: Editor Selection Event
|
||||
const editorSelectDisposer = state.model.on<ExpressionEditorEvent.Select>(
|
||||
ExpressionEditorEvent.Select,
|
||||
payload =>
|
||||
@@ -455,13 +455,13 @@ export const useListeners = (reducer: SuggestionReducer) => {
|
||||
}),
|
||||
);
|
||||
return () => {
|
||||
// 销毁时卸载监听器防止内存泄露
|
||||
// Prevent memory leaks by uninstalling listeners when destroyed
|
||||
editorSelectDisposer();
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// 挂载监听: 编辑器拼音输入事件
|
||||
// Mount Monitor: Editor Pinyin Input Event
|
||||
const compositionStartDisposer =
|
||||
state.model.on<ExpressionEditorEvent.CompositionStart>(
|
||||
ExpressionEditorEvent.CompositionStart,
|
||||
@@ -472,13 +472,13 @@ export const useListeners = (reducer: SuggestionReducer) => {
|
||||
}),
|
||||
);
|
||||
return () => {
|
||||
// 销毁时卸载监听器防止内存泄露
|
||||
// Prevent memory leaks by uninstalling listeners when destroyed
|
||||
compositionStartDisposer();
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// 初始化前首次渲染激活DOM
|
||||
// First render activation DOM before initialization
|
||||
if (state.initialized) {
|
||||
return;
|
||||
}
|
||||
@@ -489,7 +489,7 @@ export const useListeners = (reducer: SuggestionReducer) => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// 初始化前监听到DOM激活后隐藏
|
||||
// Listen to DOM activation before initialization and hide after activation
|
||||
if (state.initialized || state.visible) {
|
||||
return;
|
||||
}
|
||||
@@ -503,14 +503,14 @@ export const useListeners = (reducer: SuggestionReducer) => {
|
||||
}, [state]);
|
||||
};
|
||||
|
||||
/** 键盘上下回车键选中节点 */
|
||||
/** Keyboard Enter up and down to select the node */
|
||||
export const useKeyboardSelect = (
|
||||
reducer: SuggestionReducer,
|
||||
selectNode: (node: ExpressionEditorTreeNode) => void,
|
||||
) => {
|
||||
const [state, dispatch] = reducer;
|
||||
|
||||
// 键盘上下
|
||||
// Keyboard up and down
|
||||
useEffect(() => {
|
||||
const keyboardArrowHandler = event => {
|
||||
if (
|
||||
@@ -526,34 +526,34 @@ export const useKeyboardSelect = (
|
||||
}
|
||||
const { optionList, selectedIndex } = uiOptions;
|
||||
if (optionList.length === 1) {
|
||||
// 仅有一项可选项的时候不做处理
|
||||
// Do not deal with when there is only one option
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
let newIndex = selectedIndex;
|
||||
if (event.key === 'ArrowDown') {
|
||||
// 如果当前没有高亮的选项或者是最后一个选项,则高亮第一个选项
|
||||
// If there is currently no highlighted option or the last option, highlight the first option
|
||||
newIndex =
|
||||
selectedIndex === -1 || selectedIndex === optionList.length - 1
|
||||
? 0
|
||||
: selectedIndex + 1;
|
||||
} else if (event.key === 'ArrowUp') {
|
||||
// 如果当前没有高亮的选项或者是第一个选项,则高亮最后一个选项
|
||||
// If there is currently no highlighted option or the first option, highlight the last option
|
||||
newIndex =
|
||||
selectedIndex <= 0 ? optionList.length - 1 : selectedIndex - 1;
|
||||
}
|
||||
const selectedOption = optionList[newIndex];
|
||||
// 更新高亮选项
|
||||
// Update highlighting options
|
||||
if (selectedIndex !== -1) {
|
||||
optionList[selectedIndex].classList.remove(
|
||||
SuggestionViewUtils.keyboardSelectedClassName(),
|
||||
);
|
||||
}
|
||||
SuggestionViewUtils.setUIOptionSelected(selectedOption);
|
||||
// 将新选中的选项滚动到视图中
|
||||
// Scroll the newly selected option into view
|
||||
selectedOption.scrollIntoView({
|
||||
behavior: 'smooth', // 平滑滚动
|
||||
block: 'nearest', // 最接近的视图边界,可能是顶部或底部
|
||||
behavior: 'smooth', // Smooth scrolling
|
||||
block: 'nearest', // The closest view boundary, possibly the top or bottom
|
||||
});
|
||||
};
|
||||
document.addEventListener('keydown', keyboardArrowHandler);
|
||||
@@ -562,7 +562,7 @@ export const useKeyboardSelect = (
|
||||
};
|
||||
}, [state]);
|
||||
|
||||
// 键盘回车
|
||||
// Keyboard Enter
|
||||
useEffect(() => {
|
||||
const keyboardEnterHandler = event => {
|
||||
if (
|
||||
@@ -596,13 +596,13 @@ export const useKeyboardSelect = (
|
||||
!variableTreeNode.variable?.children ||
|
||||
variableTreeNode.variable?.children.length === 0
|
||||
) {
|
||||
// 叶子节点
|
||||
// leaf node
|
||||
return;
|
||||
}
|
||||
// 非叶子节点,光标向前移动两格
|
||||
// Non-leaf node, move the cursor forward two spaces
|
||||
const { selection } = state.model.editor;
|
||||
if (selection && Range.isCollapsed(selection)) {
|
||||
// 向前移动两个字符的光标
|
||||
// Move the cursor two characters forward
|
||||
Transforms.move(state.model.editor, { distance: 2, reverse: true });
|
||||
}
|
||||
SuggestionViewUtils.preventVisibleJitter(reducer);
|
||||
@@ -613,7 +613,7 @@ export const useKeyboardSelect = (
|
||||
};
|
||||
}, [state]);
|
||||
|
||||
// 键盘 ESC 取消弹窗
|
||||
// Keyboard ESC cancel pop-up window
|
||||
useEffect(() => {
|
||||
const keyboardESCHandler = event => {
|
||||
if (
|
||||
@@ -635,17 +635,17 @@ export const useKeyboardSelect = (
|
||||
};
|
||||
}, [state]);
|
||||
|
||||
// 默认选中首项
|
||||
// First item selected by default
|
||||
useEffect(() => {
|
||||
SuggestionViewUtils.selectFirstUIOption(state);
|
||||
}, [state]);
|
||||
};
|
||||
|
||||
/** 等待semi组件数据更新后的副作用 */
|
||||
/** Side effects of waiting for semi component data to be updated */
|
||||
export const useRenderEffect = (reducer: SuggestionReducer) => {
|
||||
const [state, dispatch] = reducer;
|
||||
|
||||
// 组件树状数据更新后设置搜索值
|
||||
// Set the search value after the component tree data is updated
|
||||
useEffect(() => {
|
||||
if (!state.renderEffect.search || !state.parseData) {
|
||||
return;
|
||||
@@ -667,7 +667,7 @@ export const useRenderEffect = (reducer: SuggestionReducer) => {
|
||||
});
|
||||
}, [state]);
|
||||
|
||||
// 搜索过滤后是否为空
|
||||
// Is it empty after searching for filters?
|
||||
useEffect(() => {
|
||||
if (!state.renderEffect.filtered) {
|
||||
return;
|
||||
|
||||
@@ -45,7 +45,7 @@ interface ExpressionEditorSuggestionProps {
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动提示
|
||||
* autoprompt
|
||||
*/
|
||||
export const ExpressionEditorSuggestion: FC<
|
||||
ExpressionEditorSuggestionProps
|
||||
|
||||
@@ -85,7 +85,7 @@ export const suggestionReducer = (
|
||||
action.payload as SuggestionActionPayload<SuggestionActionType.SetVisible>;
|
||||
if (state.entities.selectorBoxConfig) {
|
||||
if (visible) {
|
||||
state.entities.selectorBoxConfig.disabled = true; // 防止鼠标拖选不触发点击
|
||||
state.entities.selectorBoxConfig.disabled = true; // Prevent mouse dragging from triggering clicks
|
||||
}
|
||||
if (!visible) {
|
||||
state.entities.selectorBoxConfig.disabled = false;
|
||||
@@ -166,7 +166,7 @@ export const suggestionReducer = (
|
||||
return state;
|
||||
};
|
||||
|
||||
/** 获取状态 */
|
||||
/** Get status */
|
||||
export const useSuggestionReducer = (
|
||||
initialState: Omit<
|
||||
SuggestionState,
|
||||
@@ -182,15 +182,15 @@ export const useSuggestionReducer = (
|
||||
): SuggestionReducer => {
|
||||
const [state, dispatch]: SuggestionReducer = useReducer(suggestionReducer, {
|
||||
...initialState,
|
||||
initialized: false, // 初始化
|
||||
version: 0, // 更新状态计数
|
||||
key: 0, // 用于触发 react 重新渲染组件
|
||||
variableTree: [], // 用于展示的组件树
|
||||
visible: true, // 默认显示,让ref能访问到DOM
|
||||
hiddenDOM: true, // 默认隐藏,让用户看不到UI
|
||||
allowVisibleChange: true, // 允许visible变更
|
||||
initialized: false, // initialization
|
||||
version: 0, // update status count
|
||||
key: 0, // Used to trigger react to re-render components
|
||||
variableTree: [], // Component tree for presentation
|
||||
visible: true, // Default display, allowing ref to access the DOM
|
||||
hiddenDOM: true, // Hidden by default, so that users cannot see the UI.
|
||||
allowVisibleChange: true, // Allow visible changes
|
||||
renderEffect: {
|
||||
// 渲染副作用
|
||||
// rendering side effects
|
||||
search: false,
|
||||
filtered: false,
|
||||
},
|
||||
|
||||
@@ -60,27 +60,27 @@ export class ExpressionEditorModel {
|
||||
this.innerLines = ExpressionEditorParser.deserialize(initialValue);
|
||||
}
|
||||
|
||||
/** 设置变量树 */
|
||||
/** Set variable tree */
|
||||
public setVariableTree(variableTree: ExpressionEditorTreeNode[]): void {
|
||||
this.innerVariableTree = variableTree;
|
||||
}
|
||||
|
||||
/** 获取变量树 */
|
||||
/** Get variable tree */
|
||||
public get variableTree(): ExpressionEditorTreeNode[] {
|
||||
return this.innerVariableTree;
|
||||
}
|
||||
|
||||
/** 获取行数据 */
|
||||
/** Get row data */
|
||||
public get lines(): ExpressionEditorLine[] {
|
||||
return this.innerLines;
|
||||
}
|
||||
|
||||
/** 获取序列化值 */
|
||||
/** Get Serialized Value */
|
||||
public get value(): string {
|
||||
return this.innerValue;
|
||||
}
|
||||
|
||||
/** 外部设置模型值 */
|
||||
/** External setting model values */
|
||||
public setValue(value: string): void {
|
||||
if (value === this.innerValue) {
|
||||
return;
|
||||
@@ -90,22 +90,22 @@ export class ExpressionEditorModel {
|
||||
this.syncEditorValue();
|
||||
}
|
||||
|
||||
/** 同步选中状态 */
|
||||
/** Synchronize selected state */
|
||||
public setFocus(focus: boolean): void {
|
||||
if (this.innerFocus === focus) {
|
||||
return;
|
||||
}
|
||||
this.innerFocus = focus;
|
||||
if (focus) {
|
||||
// 首次选中时主动触发选区事件,主动触发变量推荐
|
||||
// Active trigger selection event when first selected, active trigger variable recommendation
|
||||
this.select(this.lines);
|
||||
} else if (this.innerValue !== '' && this.editor.children.length !== 0) {
|
||||
// 触发失焦且编辑器内容不为空,则重置选区
|
||||
// Trigger out of focus and editor content is not empty, reset the selection
|
||||
Transforms.select(this.editor, Editor.start(this.editor, []));
|
||||
}
|
||||
}
|
||||
|
||||
/** 注册事件 */
|
||||
/** Register an event */
|
||||
public on<T extends ExpressionEditorEvent>(
|
||||
event: T,
|
||||
callback: (params: ExpressionEditorEventParams<T>) => void,
|
||||
@@ -116,7 +116,7 @@ export class ExpressionEditorModel {
|
||||
};
|
||||
}
|
||||
|
||||
/** 数据变更事件 */
|
||||
/** data change event */
|
||||
public change(lines: ExpressionEditorLine[]): void {
|
||||
const isAstChange = this.editor.operations.some(
|
||||
op => 'set_selection' !== op.type,
|
||||
@@ -132,7 +132,7 @@ export class ExpressionEditorModel {
|
||||
});
|
||||
}
|
||||
|
||||
/** 选中事件 */
|
||||
/** selected event */
|
||||
public select(lines: ExpressionEditorLine[]): void {
|
||||
const { selection } = this.editor;
|
||||
if (!selection || !Range.isCollapsed(selection)) {
|
||||
@@ -143,7 +143,7 @@ export class ExpressionEditorModel {
|
||||
selection.anchor.path[0] !== selection.focus.path[0] ||
|
||||
selection.anchor.path[1] !== selection.focus.path[1]
|
||||
) {
|
||||
// 框选
|
||||
// box selection
|
||||
this.emitter.emit(ExpressionEditorEvent.Select, {
|
||||
content: '',
|
||||
offset: -1,
|
||||
@@ -169,7 +169,7 @@ export class ExpressionEditorModel {
|
||||
});
|
||||
}
|
||||
|
||||
/** 键盘事件 */
|
||||
/** keyboard event */
|
||||
public keydown(
|
||||
event: Parameters<KeyboardEventHandler<HTMLDivElement>>[0],
|
||||
): void {
|
||||
@@ -184,7 +184,7 @@ export class ExpressionEditorModel {
|
||||
reverse: true,
|
||||
});
|
||||
setTimeout(() => {
|
||||
// slate UI 渲染滞后
|
||||
// Slate UI Rendering
|
||||
this.select(this.innerLines);
|
||||
}, 0);
|
||||
}
|
||||
@@ -203,7 +203,7 @@ export class ExpressionEditorModel {
|
||||
}
|
||||
}
|
||||
|
||||
/** 开始输入拼音 */
|
||||
/** Start typing pinyin */
|
||||
public compositionStart(
|
||||
event: CompositionEventHandler<HTMLDivElement>,
|
||||
): void {
|
||||
@@ -212,7 +212,7 @@ export class ExpressionEditorModel {
|
||||
});
|
||||
}
|
||||
|
||||
/** 装饰叶子节点 */
|
||||
/** Decorative leaf node */
|
||||
public get decorate(): ([node, path]: NodeEntry) => ExpressionEditorRange[] {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const self = this;
|
||||
@@ -221,7 +221,7 @@ export class ExpressionEditorModel {
|
||||
if (!Text.isText(node)) {
|
||||
return ranges;
|
||||
}
|
||||
// 计算表达式合法/非法
|
||||
// Evaluation expressions are legal/illegal
|
||||
const validateList = ExpressionEditorValidator.lineTextValidate({
|
||||
lineText: node.text,
|
||||
tree: self.innerVariableTree,
|
||||
@@ -247,7 +247,7 @@ export class ExpressionEditorModel {
|
||||
if (!this.innerFocus) {
|
||||
return ranges;
|
||||
}
|
||||
// 以下是计算当前选中表达式逻辑
|
||||
// The following is the logic for evaluating the currently selected expression
|
||||
const selectedItem = self.isValidateSelectPath([node, path]);
|
||||
const selectedValidItem = validateList.find(
|
||||
validateData =>
|
||||
@@ -274,17 +274,17 @@ export class ExpressionEditorModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步编辑器实例内容
|
||||
* > **NOTICE:** *为确保不影响性能,应仅在外部值变更导致编辑器值与模型值不一致时调用*
|
||||
* Synchronize editor instance content
|
||||
* > ** NOTICE: ** * To ensure that performance is not affected, it should only be called when an external value change causes the editor value to be inconsistent with the model value.
|
||||
*/
|
||||
private syncEditorValue(): void {
|
||||
// 删除编辑器内所有行
|
||||
// Delete all lines in the editor
|
||||
this.editor.children.forEach((line, index) => {
|
||||
Transforms.removeNodes(this.editor, {
|
||||
at: [index],
|
||||
});
|
||||
});
|
||||
// 重新在编辑器插入当前行内容
|
||||
// Reinsert the current line content in the editor
|
||||
this.lines.forEach((line, index) => {
|
||||
Transforms.insertNodes(this.editor, line, {
|
||||
at: [this.editor.children.length],
|
||||
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
} from '../constant';
|
||||
|
||||
export namespace ExpressionEditorParserBuiltin {
|
||||
/** 计算开始和结束标识的序号 */
|
||||
/** Calculate the serial number of the start and end tags */
|
||||
export const tokenOffset = (line: {
|
||||
lineContent: string;
|
||||
lineOffset: number;
|
||||
@@ -52,7 +52,7 @@ export namespace ExpressionEditorParserBuiltin {
|
||||
firstEndTokenOffset + 2,
|
||||
);
|
||||
if (endChars !== ExpressionEditorToken.FullEnd) {
|
||||
// 结束符号 "}}" 不完整
|
||||
// End symbol "}}" is incomplete
|
||||
return;
|
||||
}
|
||||
const lastStartTokenOffset = content.lastIndexOf(
|
||||
@@ -64,7 +64,7 @@ export namespace ExpressionEditorParserBuiltin {
|
||||
lastStartTokenOffset + 1,
|
||||
);
|
||||
if (startChars !== ExpressionEditorToken.FullStart) {
|
||||
// 开始符号 "{{" 不完整
|
||||
// The opening symbol "{{" is incomplete
|
||||
return;
|
||||
}
|
||||
return {
|
||||
@@ -73,7 +73,7 @@ export namespace ExpressionEditorParserBuiltin {
|
||||
};
|
||||
};
|
||||
|
||||
/** 从行内容提取内容 */
|
||||
/** Extract content from line content */
|
||||
export const extractContent = (params: {
|
||||
lineContent: string;
|
||||
lineOffset: number;
|
||||
@@ -102,7 +102,7 @@ export namespace ExpressionEditorParserBuiltin {
|
||||
};
|
||||
};
|
||||
|
||||
/** 根据 offset 将文本内容切分为可用与不可用 */
|
||||
/** Split text content into available and unavailable by offset */
|
||||
export const sliceReachable = (params: {
|
||||
content: string;
|
||||
offset: number;
|
||||
@@ -119,29 +119,29 @@ export namespace ExpressionEditorParserBuiltin {
|
||||
};
|
||||
};
|
||||
|
||||
/** 切分文本 */
|
||||
/** Split text */
|
||||
export const splitText = (pathString: string): string[] => {
|
||||
// 得到的分割数组,初始为原字符串以"."分割的结果
|
||||
// The obtained split array, initially the result of splitting the original string with "."
|
||||
const segments = pathString.split(ExpressionEditorToken.Separator);
|
||||
|
||||
// 定义结果数组,并处理连续的"."导致的空字符串
|
||||
// Define the result array and handle empty strings resulting from consecutive "."
|
||||
const result: string[] = [];
|
||||
|
||||
segments.forEach(segment => {
|
||||
if (!segment.match(/\[\d+\]/)) {
|
||||
// 如果不是数组索引,直接加入结果数组,即使是空字符串也加入以保持正确的分割
|
||||
// If it is not an array index, add the result array directly, even if it is an empty string, to maintain the correct segmentation
|
||||
result.push(segment);
|
||||
return;
|
||||
}
|
||||
// 如果当前段是数组索引,将前面的字符串和当前数组索引分别加入结果数组
|
||||
// If the current segment is an array index, add the previous string and the current array index to the result array, respectively
|
||||
const lastSegmentIndex = segment.lastIndexOf(
|
||||
ExpressionEditorToken.ArrayStart,
|
||||
);
|
||||
const key = segment.substring(0, lastSegmentIndex);
|
||||
const index = segment.substring(lastSegmentIndex);
|
||||
// {{array[0]}} 中的 array
|
||||
// Array in {{array [0]}}
|
||||
result.push(key);
|
||||
// {{array[0]}} 中的 [0]
|
||||
// [0] in {{array [0]}}
|
||||
result.push(index);
|
||||
return;
|
||||
});
|
||||
@@ -149,14 +149,14 @@ export namespace ExpressionEditorParserBuiltin {
|
||||
return result;
|
||||
};
|
||||
|
||||
/** 字符串解析为路径 */
|
||||
/** String parsed as path */
|
||||
export const toSegments = (
|
||||
text: string,
|
||||
): ExpressionEditorSegment[] | undefined => {
|
||||
const textSegments = ExpressionEditorParserBuiltin.splitText(text);
|
||||
const segments: ExpressionEditorSegment[] = [];
|
||||
const validate = textSegments.every((textSegment, index) => {
|
||||
// 数组下标
|
||||
// array subscript
|
||||
if (
|
||||
textSegment.startsWith(ExpressionEditorToken.ArrayStart) &&
|
||||
textSegment.endsWith(ExpressionEditorToken.ArrayEnd)
|
||||
@@ -164,7 +164,7 @@ export namespace ExpressionEditorParserBuiltin {
|
||||
const arrayIndexString = textSegment.slice(1, -1);
|
||||
const arrayIndex = Number(arrayIndexString);
|
||||
if (arrayIndexString === '' || Number.isNaN(arrayIndex)) {
|
||||
// index 必须是数字
|
||||
// Index must be a number
|
||||
return false;
|
||||
}
|
||||
const lastSegment = segments[segments.length - 1];
|
||||
@@ -172,7 +172,7 @@ export namespace ExpressionEditorParserBuiltin {
|
||||
!lastSegment ||
|
||||
lastSegment.type !== ExpressionEditorSegmentType.ObjectKey
|
||||
) {
|
||||
// 数组索引必须在 key 之后
|
||||
// The array index must be after the key
|
||||
return false;
|
||||
}
|
||||
segments.push({
|
||||
@@ -181,7 +181,7 @@ export namespace ExpressionEditorParserBuiltin {
|
||||
arrayIndex,
|
||||
});
|
||||
}
|
||||
// 最后一行空文本
|
||||
// The last empty line of text
|
||||
else if (index === textSegments.length - 1 && textSegment === '') {
|
||||
segments.push({
|
||||
type: ExpressionEditorSegmentType.EndEmpty,
|
||||
@@ -207,11 +207,11 @@ export namespace ExpressionEditorParserBuiltin {
|
||||
}
|
||||
|
||||
export namespace ExpressionEditorParser {
|
||||
/** 序列化 */
|
||||
/** Serialization */
|
||||
export const serialize = (value: ExpressionEditorLine[]) =>
|
||||
value.map(n => Node.string(n)).join('\n');
|
||||
|
||||
/** 反序列化 */
|
||||
/** deserialization */
|
||||
export const deserialize = (text: string): ExpressionEditorLine[] => {
|
||||
const lines = text.split('\n');
|
||||
return lines.map(line => ({
|
||||
|
||||
@@ -165,7 +165,7 @@ export namespace ExpressionEditorTreeHelper {
|
||||
const lastSegment = segments[segments.length - 1];
|
||||
const segmentsRemovedLast =
|
||||
lastSegment.type === ExpressionEditorSegmentType.ArrayIndex
|
||||
? segments.slice(0, segments.length - 2) // 数组索引属于上一层级,需要去除两层
|
||||
? segments.slice(0, segments.length - 2) // The array index belongs to the previous level, and two layers need to be removed.
|
||||
: segments.slice(0, segments.length - 1);
|
||||
let treeLayer = tree;
|
||||
segmentsRemovedLast.forEach(segment => {
|
||||
@@ -193,7 +193,7 @@ export namespace ExpressionEditorTreeHelper {
|
||||
const pathList: { objectKey: string; arrayIndex?: number }[] = [];
|
||||
while (current) {
|
||||
if (current.variable?.type === ViewVariableType.ArrayObject) {
|
||||
// 默认第0个
|
||||
// Default 0th
|
||||
pathList.unshift({
|
||||
objectKey: current.label,
|
||||
arrayIndex: 0,
|
||||
@@ -213,7 +213,7 @@ export namespace ExpressionEditorTreeHelper {
|
||||
const pathItem = pathList[pathIndex];
|
||||
pathIndex++;
|
||||
if (pathItem.objectKey !== segment.objectKey) {
|
||||
// 退出循环
|
||||
// exit the loop
|
||||
return true;
|
||||
}
|
||||
const nextSegment = segments[index + 1];
|
||||
@@ -253,7 +253,7 @@ export namespace ExpressionEditorTreeHelper {
|
||||
return false;
|
||||
};
|
||||
const beforeTreeNode = treeBranch[treeBranch.length - 1];
|
||||
// 确认非法情况:是否对非数组类型使用数组索引
|
||||
// Verify Illegal Case: Whether to Use Array Indexing for Non-Array Types
|
||||
if (
|
||||
segment.type === ExpressionEditorSegmentType.ArrayIndex &&
|
||||
beforeTreeNode &&
|
||||
@@ -262,7 +262,7 @@ export namespace ExpressionEditorTreeHelper {
|
||||
) {
|
||||
return itemInvalid();
|
||||
}
|
||||
// 确认非法情况:数组只能跟随数组下标
|
||||
// Confirm illegal condition: Array can only follow array subscript
|
||||
if (
|
||||
beforeTreeNode?.variable?.type &&
|
||||
ViewVariableType.isArrayType(beforeTreeNode.variable.type) &&
|
||||
@@ -270,12 +270,12 @@ export namespace ExpressionEditorTreeHelper {
|
||||
) {
|
||||
return itemInvalid();
|
||||
}
|
||||
// 忽略
|
||||
// ignore
|
||||
if (segment.type !== ExpressionEditorSegmentType.ObjectKey) {
|
||||
return itemValid();
|
||||
}
|
||||
const treeNode = treeLayer.find(node => node.label === segment.objectKey);
|
||||
// 确认非法情况:每一个 object key 必须对应一个 variable node
|
||||
// Verify illegal condition: each object key must correspond to a variable node
|
||||
if (!treeNode) {
|
||||
return itemInvalid();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user