mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-04-06 03:57:56 +08:00
feat(editor): 优化依赖收集体验,减小收集任务粒度,修改配置时识别是否需要触发重新收集
This commit is contained in:
parent
9f7d67b17b
commit
b4136c91c2
@ -57,6 +57,7 @@
|
|||||||
"@tmagic/utils": "workspace:*",
|
"@tmagic/utils": "workspace:*",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"color": "^3.1.3",
|
"color": "^3.1.3",
|
||||||
|
"deep-object-diff": "^1.1.9",
|
||||||
"emmet-monaco-es": "^5.3.0",
|
"emmet-monaco-es": "^5.3.0",
|
||||||
"events": "^3.3.0",
|
"events": "^3.3.0",
|
||||||
"gesto": "^1.19.1",
|
"gesto": "^1.19.1",
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export * from './use-code-block-edit';
|
export * from './use-code-block-edit';
|
||||||
export * from './use-data-source-method';
|
|
||||||
export * from './use-stage';
|
export * from './use-stage';
|
||||||
export * from './use-float-box';
|
export * from './use-float-box';
|
||||||
export * from './use-window-rect';
|
export * from './use-window-rect';
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
import type { DataSourceSchema } from '@tmagic/core';
|
import type { DataSourceSchema } from '@tmagic/core';
|
||||||
|
import type { ContainerChangeEventData } from '@tmagic/form';
|
||||||
|
|
||||||
import DataSourceConfigPanel from '@editor/layouts/sidebar/data-source/DataSourceConfigPanel.vue';
|
import DataSourceConfigPanel from '@editor/layouts/sidebar/data-source/DataSourceConfigPanel.vue';
|
||||||
import type { DataSourceService } from '@editor/services/dataSource';
|
import type { DataSourceService } from '@editor/services/dataSource';
|
||||||
@ -24,9 +25,9 @@ export const useDataSourceEdit = (dataSourceService?: DataSourceService) => {
|
|||||||
editDialog.value.show();
|
editDialog.value.show();
|
||||||
};
|
};
|
||||||
|
|
||||||
const submitDataSourceHandler = (value: DataSourceSchema) => {
|
const submitDataSourceHandler = (value: DataSourceSchema, eventData: ContainerChangeEventData) => {
|
||||||
if (value.id) {
|
if (value.id) {
|
||||||
dataSourceService?.update(value);
|
dataSourceService?.update(value, { changeRecords: eventData.changeRecords });
|
||||||
} else {
|
} else {
|
||||||
dataSourceService?.add(value);
|
dataSourceService?.add(value);
|
||||||
}
|
}
|
||||||
|
@ -1,100 +0,0 @@
|
|||||||
import { nextTick, ref } from 'vue';
|
|
||||||
import { cloneDeep } from 'lodash-es';
|
|
||||||
|
|
||||||
import type { CodeBlockContent, DataSourceSchema } from '@tmagic/core';
|
|
||||||
import { tMagicMessage } from '@tmagic/design';
|
|
||||||
|
|
||||||
import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue';
|
|
||||||
import { getEditorConfig } from '@editor/utils/config';
|
|
||||||
|
|
||||||
export const useDataSourceMethod = () => {
|
|
||||||
const codeConfig = ref<CodeBlockContent>();
|
|
||||||
const codeBlockEditor = ref<InstanceType<typeof CodeBlockEditor>>();
|
|
||||||
|
|
||||||
const dataSource = ref<DataSourceSchema>();
|
|
||||||
const dataSourceMethod = ref('');
|
|
||||||
|
|
||||||
return {
|
|
||||||
codeConfig,
|
|
||||||
codeBlockEditor,
|
|
||||||
|
|
||||||
createCode: async (model: DataSourceSchema) => {
|
|
||||||
codeConfig.value = {
|
|
||||||
name: '',
|
|
||||||
content: `({ params, dataSource, app, flowState }) => {\n // place your code here\n}`,
|
|
||||||
params: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
await nextTick();
|
|
||||||
|
|
||||||
dataSource.value = model;
|
|
||||||
dataSourceMethod.value = '';
|
|
||||||
|
|
||||||
codeBlockEditor.value?.show();
|
|
||||||
},
|
|
||||||
|
|
||||||
editCode: async (model: DataSourceSchema, methodName: string) => {
|
|
||||||
const method = model.methods?.find((method) => method.name === methodName);
|
|
||||||
|
|
||||||
if (!method) {
|
|
||||||
tMagicMessage.error('获取数据源方法失败');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let codeContent = method.content || `({ params, dataSource, app }) => {\n // place your code here\n}`;
|
|
||||||
|
|
||||||
if (typeof codeContent !== 'string') {
|
|
||||||
codeContent = codeContent.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
codeConfig.value = {
|
|
||||||
...cloneDeep(method),
|
|
||||||
content: codeContent,
|
|
||||||
};
|
|
||||||
|
|
||||||
await nextTick();
|
|
||||||
|
|
||||||
dataSource.value = model;
|
|
||||||
dataSourceMethod.value = methodName;
|
|
||||||
|
|
||||||
codeBlockEditor.value?.show();
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteCode: async (model: DataSourceSchema, methodName: string) => {
|
|
||||||
if (!model.methods) return;
|
|
||||||
|
|
||||||
const index = model.methods.findIndex((method) => method.name === methodName);
|
|
||||||
|
|
||||||
if (index === -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
model.methods.splice(index, 1);
|
|
||||||
},
|
|
||||||
|
|
||||||
submitCode: (values: CodeBlockContent) => {
|
|
||||||
if (!dataSource.value) return;
|
|
||||||
|
|
||||||
if (!dataSource.value.methods) {
|
|
||||||
dataSource.value.methods = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (values.content) {
|
|
||||||
// 在保存的时候转换代码内容
|
|
||||||
const parseDSL = getEditorConfig('parseDSL');
|
|
||||||
if (typeof values.content === 'string') {
|
|
||||||
values.content = parseDSL<(...args: any[]) => any>(values.content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dataSourceMethod.value) {
|
|
||||||
const index = dataSource.value.methods.findIndex((method) => method.name === dataSourceMethod.value);
|
|
||||||
dataSource.value.methods.splice(index, 1, values);
|
|
||||||
} else {
|
|
||||||
dataSource.value.methods.push(values);
|
|
||||||
}
|
|
||||||
|
|
||||||
codeBlockEditor.value?.hide();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
@ -17,11 +17,14 @@ import {
|
|||||||
createDataSourceMethodTarget,
|
createDataSourceMethodTarget,
|
||||||
createDataSourceTarget,
|
createDataSourceTarget,
|
||||||
DepTargetType,
|
DepTargetType,
|
||||||
|
NODE_CONDS_KEY,
|
||||||
Target,
|
Target,
|
||||||
} from '@tmagic/core';
|
} from '@tmagic/core';
|
||||||
|
import { ChangeRecord } from '@tmagic/form';
|
||||||
import { getNodes, isPage, traverseNode } from '@tmagic/utils';
|
import { getNodes, isPage, traverseNode } from '@tmagic/utils';
|
||||||
|
|
||||||
import PropsPanel from './layouts/PropsPanel.vue';
|
import PropsPanel from './layouts/PropsPanel.vue';
|
||||||
|
import { isIncludeDataSource, isValueIncludeDataSource } from './utils/editor';
|
||||||
import { EditorProps } from './editorProps';
|
import { EditorProps } from './editorProps';
|
||||||
import { Services } from './type';
|
import { Services } from './type';
|
||||||
|
|
||||||
@ -219,6 +222,7 @@ export const initServiceEvents = (
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (Array.isArray(value.items)) {
|
if (Array.isArray(value.items)) {
|
||||||
|
depService.clearIdleTasks();
|
||||||
collectIdle(value.items, true);
|
collectIdle(value.items, true);
|
||||||
} else {
|
} else {
|
||||||
depService.clear();
|
depService.clear();
|
||||||
@ -302,10 +306,6 @@ export const initServiceEvents = (
|
|||||||
const root = editorService.get('root');
|
const root = editorService.get('root');
|
||||||
if (!root) return;
|
if (!root) return;
|
||||||
const stage = editorService.get('stage');
|
const stage = editorService.get('stage');
|
||||||
const app = getApp();
|
|
||||||
if (app?.dsl) {
|
|
||||||
app.dsl.dataSourceDeps = root.dataSourceDeps;
|
|
||||||
}
|
|
||||||
for (const node of nodes) {
|
for (const node of nodes) {
|
||||||
stage?.update({
|
stage?.update({
|
||||||
config: cloneDeep(node),
|
config: cloneDeep(node),
|
||||||
@ -348,8 +348,18 @@ export const initServiceEvents = (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const depCollectedHandler = () => {
|
||||||
|
const root = editorService.get('root');
|
||||||
|
if (!root) return;
|
||||||
|
const app = getApp();
|
||||||
|
if (app?.dsl) {
|
||||||
|
app.dsl.dataSourceDeps = root.dataSourceDeps;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
depService.on('add-target', targetAddHandler);
|
depService.on('add-target', targetAddHandler);
|
||||||
depService.on('remove-target', targetRemoveHandler);
|
depService.on('remove-target', targetRemoveHandler);
|
||||||
|
depService.on('collected', depCollectedHandler);
|
||||||
|
|
||||||
const initDataSourceDepTarget = (ds: DataSourceSchema) => {
|
const initDataSourceDepTarget = (ds: DataSourceSchema) => {
|
||||||
depService.addTarget(createDataSourceTarget(ds, reactive({})));
|
depService.addTarget(createDataSourceTarget(ds, reactive({})));
|
||||||
@ -380,10 +390,39 @@ export const initServiceEvents = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 节点更新,收集依赖
|
// 节点更新,收集依赖
|
||||||
const nodeUpdateHandler = (nodes: MNode[]) => {
|
// 仅当修改到数据源相关的才收集
|
||||||
collectIdle(nodes, true).then(() => {
|
const nodeUpdateHandler = (data: { newNode: MNode; oldNode: MNode; changeRecords?: ChangeRecord[] }[]) => {
|
||||||
afterUpdateNodes(nodes);
|
const needRecollectNodes: MNode[] = [];
|
||||||
|
const normalNodes: MNode[] = [];
|
||||||
|
data.forEach(({ newNode, oldNode, changeRecords }) => {
|
||||||
|
if (changeRecords?.length) {
|
||||||
|
for (const record of changeRecords) {
|
||||||
|
// NODE_CONDS_KEY为显示条件key
|
||||||
|
if (
|
||||||
|
!record.propPath ||
|
||||||
|
new RegExp(`${NODE_CONDS_KEY}.(\\d)+.cond`).test(record.propPath) ||
|
||||||
|
new RegExp(`${NODE_CONDS_KEY}.(\\d)+.cond.(\\d)+.value`).test(record.propPath) ||
|
||||||
|
record.propPath === NODE_CONDS_KEY ||
|
||||||
|
isValueIncludeDataSource(record.value)
|
||||||
|
) {
|
||||||
|
needRecollectNodes.push(newNode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (isIncludeDataSource(newNode, oldNode)) {
|
||||||
|
needRecollectNodes.push(newNode);
|
||||||
|
} else {
|
||||||
|
normalNodes.push(newNode);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (needRecollectNodes.length) {
|
||||||
|
collectIdle(needRecollectNodes, true).then(() => {
|
||||||
|
afterUpdateNodes(needRecollectNodes);
|
||||||
|
});
|
||||||
|
} else if (normalNodes.length) {
|
||||||
|
afterUpdateNodes(normalNodes);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 节点删除,清除对齐的依赖收集
|
// 节点删除,清除对齐的依赖收集
|
||||||
@ -425,14 +464,41 @@ export const initServiceEvents = (
|
|||||||
getApp()?.dataSourceManager?.addDataSource(config);
|
getApp()?.dataSourceManager?.addDataSource(config);
|
||||||
};
|
};
|
||||||
|
|
||||||
const dataSourceUpdateHandler = (config: DataSourceSchema) => {
|
const dataSourceUpdateHandler = (config: DataSourceSchema, { changeRecords }: { changeRecords: ChangeRecord[] }) => {
|
||||||
const root = editorService.get('root');
|
let needRecollectDep = false;
|
||||||
removeDataSourceTarget(config.id);
|
for (const changeRecord of changeRecords) {
|
||||||
initDataSourceDepTarget(config);
|
if (!changeRecord.propPath) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
collectIdle(root?.items || [], true).then(() => {
|
needRecollectDep =
|
||||||
updateDataSourceSchema(root?.items || [], true);
|
changeRecord.propPath === 'fields' ||
|
||||||
});
|
changeRecord.propPath === 'methods' ||
|
||||||
|
/fields.(\d)+.name/.test(changeRecord.propPath) ||
|
||||||
|
/fields.(\d)+$/.test(changeRecord.propPath) ||
|
||||||
|
/methods.(\d)+.name/.test(changeRecord.propPath) ||
|
||||||
|
/methods.(\d)+$/.test(changeRecord.propPath);
|
||||||
|
|
||||||
|
if (needRecollectDep) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const root = editorService.get('root');
|
||||||
|
if (needRecollectDep) {
|
||||||
|
if (Array.isArray(root?.items)) {
|
||||||
|
depService.clearIdleTasks();
|
||||||
|
|
||||||
|
removeDataSourceTarget(config.id);
|
||||||
|
initDataSourceDepTarget(config);
|
||||||
|
|
||||||
|
collectIdle(root.items, true).then(() => {
|
||||||
|
updateDataSourceSchema(root?.items || [], true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (root?.dataSources) {
|
||||||
|
getApp()?.dataSourceManager?.updateSchema(root.dataSources);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeDataSourceTarget = (id: string) => {
|
const removeDataSourceTarget = (id: string) => {
|
||||||
@ -459,6 +525,7 @@ export const initServiceEvents = (
|
|||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
depService.off('add-target', targetAddHandler);
|
depService.off('add-target', targetAddHandler);
|
||||||
depService.off('remove-target', targetRemoveHandler);
|
depService.off('remove-target', targetRemoveHandler);
|
||||||
|
depService.off('collected', depCollectedHandler);
|
||||||
|
|
||||||
editorService.off('history-change', historyChangeHandler);
|
editorService.off('history-change', historyChangeHandler);
|
||||||
editorService.off('root-change', rootChangeHandler);
|
editorService.off('root-change', rootChangeHandler);
|
||||||
|
@ -11,8 +11,6 @@
|
|||||||
</slot>
|
</slot>
|
||||||
|
|
||||||
<SplitView
|
<SplitView
|
||||||
v-loading="stageLoading"
|
|
||||||
element-loading-text="Runtime 加载中..."
|
|
||||||
v-else
|
v-else
|
||||||
ref="splitView"
|
ref="splitView"
|
||||||
class="m-editor-content"
|
class="m-editor-content"
|
||||||
@ -102,7 +100,6 @@ const root = computed(() => editorService?.get('root'));
|
|||||||
const page = computed(() => editorService?.get('page'));
|
const page = computed(() => editorService?.get('page'));
|
||||||
|
|
||||||
const pageLength = computed(() => editorService?.get('pageLength') || 0);
|
const pageLength = computed(() => editorService?.get('pageLength') || 0);
|
||||||
const stageLoading = computed(() => editorService?.get('stageLoading') || false);
|
|
||||||
const showSrc = computed(() => uiService?.get('showSrc'));
|
const showSrc = computed(() => uiService?.get('showSrc'));
|
||||||
|
|
||||||
const LEFT_COLUMN_WIDTH_STORAGE_KEY = '$MagicEditorLeftColumnWidthData';
|
const LEFT_COLUMN_WIDTH_STORAGE_KEY = '$MagicEditorLeftColumnWidthData';
|
||||||
|
@ -43,7 +43,7 @@ import { Document as DocumentIcon } from '@element-plus/icons-vue';
|
|||||||
|
|
||||||
import type { MNode } from '@tmagic/core';
|
import type { MNode } from '@tmagic/core';
|
||||||
import { TMagicButton } from '@tmagic/design';
|
import { TMagicButton } from '@tmagic/design';
|
||||||
import type { FormState, FormValue } from '@tmagic/form';
|
import type { ContainerChangeEventData, FormState, FormValue } from '@tmagic/form';
|
||||||
import { MForm } from '@tmagic/form';
|
import { MForm } from '@tmagic/form';
|
||||||
|
|
||||||
import MIcon from '@editor/components/Icon.vue';
|
import MIcon from '@editor/components/Icon.vue';
|
||||||
@ -110,10 +110,10 @@ watchEffect(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const submit = async () => {
|
const submit = async (v: FormValue, eventData: ContainerChangeEventData) => {
|
||||||
try {
|
try {
|
||||||
const values = await configForm.value?.submitForm();
|
const values = await configForm.value?.submitForm();
|
||||||
services?.editorService.update(values);
|
services?.editorService.update(values, { changeRecords: eventData.changeRecords });
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
emit('submit-error', e);
|
emit('submit-error', e);
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="m-editor-sidebar-content"
|
class="m-editor-sidebar-content"
|
||||||
|
:class="{ 'm-editor-dep-collecting': collecting }"
|
||||||
v-for="(config, index) in sideBarItems"
|
v-for="(config, index) in sideBarItems"
|
||||||
:key="config.$key ?? index"
|
:key="config.$key ?? index"
|
||||||
v-show="[config.text, config.$key, `${index}`].includes(activeTabName)"
|
v-show="[config.text, config.$key, `${index}`].includes(activeTabName)"
|
||||||
@ -197,6 +198,8 @@ const props = withDefaults(
|
|||||||
|
|
||||||
const services = inject<Services>('services');
|
const services = inject<Services>('services');
|
||||||
|
|
||||||
|
const collecting = computed(() => services?.depService.get('collecting'));
|
||||||
|
|
||||||
const columnLeftWidth = computed(() => services?.uiService.get('columnWidth')[ColumnLayout.LEFT] || 0);
|
const columnLeftWidth = computed(() => services?.uiService.get('columnWidth')[ColumnLayout.LEFT] || 0);
|
||||||
const { height: editorContentHeight } = useEditorContentHeight();
|
const { height: editorContentHeight } = useEditorContentHeight();
|
||||||
const columnLeftHeight = ref(0);
|
const columnLeftHeight = ref(0);
|
||||||
|
@ -24,9 +24,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { inject, Ref, ref, watchEffect } from 'vue';
|
import { inject, Ref, ref, watchEffect } from 'vue';
|
||||||
|
|
||||||
import { DataSourceSchema } from '@tmagic/core';
|
import type { DataSourceSchema } from '@tmagic/core';
|
||||||
import { tMagicMessage } from '@tmagic/design';
|
import { tMagicMessage } from '@tmagic/design';
|
||||||
import { FormConfig, MFormBox } from '@tmagic/form';
|
import { type ContainerChangeEventData, type FormConfig, MFormBox } from '@tmagic/form';
|
||||||
|
|
||||||
import FloatingBox from '@editor/components/FloatingBox.vue';
|
import FloatingBox from '@editor/components/FloatingBox.vue';
|
||||||
import { useEditorContentHeight } from '@editor/hooks';
|
import { useEditorContentHeight } from '@editor/hooks';
|
||||||
@ -46,7 +46,9 @@ const props = defineProps<{
|
|||||||
const boxVisible = defineModel<boolean>('visible', { default: false });
|
const boxVisible = defineModel<boolean>('visible', { default: false });
|
||||||
const width = defineModel<number>('width', { default: 670 });
|
const width = defineModel<number>('width', { default: 670 });
|
||||||
|
|
||||||
const emit = defineEmits(['submit']);
|
const emit = defineEmits<{
|
||||||
|
submit: [v: any, eventData: ContainerChangeEventData];
|
||||||
|
}>();
|
||||||
|
|
||||||
const services = inject<Services>('services');
|
const services = inject<Services>('services');
|
||||||
|
|
||||||
@ -63,8 +65,8 @@ watchEffect(() => {
|
|||||||
dataSourceConfig.value = services?.dataSourceService.getFormConfig(initValues.value.type) || [];
|
dataSourceConfig.value = services?.dataSourceService.getFormConfig(initValues.value.type) || [];
|
||||||
});
|
});
|
||||||
|
|
||||||
const submitHandler = (values: any) => {
|
const submitHandler = (values: any, data: ContainerChangeEventData) => {
|
||||||
emit('submit', values);
|
emit('submit', values, data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const errorHandler = (error: any) => {
|
const errorHandler = (error: any) => {
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
class="m-editor-stage"
|
class="m-editor-stage"
|
||||||
ref="stageWrap"
|
ref="stageWrap"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
|
v-loading="stageLoading"
|
||||||
|
element-loading-text="Runtime 加载中..."
|
||||||
:width="stageRect?.width"
|
:width="stageRect?.width"
|
||||||
:height="stageRect?.height"
|
:height="stageRect?.height"
|
||||||
:wrap-width="stageContainerRect?.width"
|
:wrap-width="stageContainerRect?.width"
|
||||||
@ -79,6 +81,8 @@ let runtime: Runtime | null = null;
|
|||||||
const services = inject<Services>('services');
|
const services = inject<Services>('services');
|
||||||
const stageOptions = inject<StageOptions>('stageOptions');
|
const stageOptions = inject<StageOptions>('stageOptions');
|
||||||
|
|
||||||
|
const stageLoading = computed(() => services?.editorService.get('stageLoading') || false);
|
||||||
|
|
||||||
const stageWrap = ref<InstanceType<typeof ScrollViewer>>();
|
const stageWrap = ref<InstanceType<typeof ScrollViewer>>();
|
||||||
const stageContainer = ref<HTMLDivElement>();
|
const stageContainer = ref<HTMLDivElement>();
|
||||||
const menu = ref<InstanceType<typeof ViewerMenu>>();
|
const menu = ref<InstanceType<typeof ViewerMenu>>();
|
||||||
|
@ -4,7 +4,7 @@ import type { Writable } from 'type-fest';
|
|||||||
|
|
||||||
import type { DataSourceSchema, EventOption, Id, MNode, TargetOptions } from '@tmagic/core';
|
import type { DataSourceSchema, EventOption, Id, MNode, TargetOptions } from '@tmagic/core';
|
||||||
import { Target, Watcher } from '@tmagic/core';
|
import { Target, Watcher } from '@tmagic/core';
|
||||||
import type { FormConfig } from '@tmagic/form';
|
import type { ChangeRecord, FormConfig } from '@tmagic/form';
|
||||||
import { guid, toLine } from '@tmagic/utils';
|
import { guid, toLine } from '@tmagic/utils';
|
||||||
|
|
||||||
import editorService from '@editor/services/editor';
|
import editorService from '@editor/services/editor';
|
||||||
@ -115,15 +115,22 @@ class DataSource extends BaseService {
|
|||||||
return newConfig;
|
return newConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
public update(config: DataSourceSchema) {
|
public update(config: DataSourceSchema, { changeRecords = [] }: { changeRecords?: ChangeRecord[] } = {}) {
|
||||||
const dataSources = this.get('dataSources');
|
const dataSources = this.get('dataSources');
|
||||||
|
|
||||||
const index = dataSources.findIndex((ds) => ds.id === config.id);
|
const index = dataSources.findIndex((ds) => ds.id === config.id);
|
||||||
dataSources[index] = cloneDeep(config);
|
|
||||||
|
|
||||||
this.emit('update', config);
|
const oldConfig = dataSources[index];
|
||||||
|
const newConfig = cloneDeep(config);
|
||||||
|
|
||||||
return config;
|
dataSources[index] = newConfig;
|
||||||
|
|
||||||
|
this.emit('update', newConfig, {
|
||||||
|
oldConfig,
|
||||||
|
changeRecords,
|
||||||
|
});
|
||||||
|
|
||||||
|
return newConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
public remove(id: string) {
|
public remove(id: string) {
|
||||||
|
@ -31,11 +31,29 @@ export interface DepEvents {
|
|||||||
collected: [nodes: MNode[], deep: boolean];
|
collected: [nodes: MNode[], deep: boolean];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
collecting: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
type StateKey = keyof State;
|
||||||
|
|
||||||
const idleTask = new IdleTask<{ node: TargetNode; deep: boolean; target: Target }>();
|
const idleTask = new IdleTask<{ node: TargetNode; deep: boolean; target: Target }>();
|
||||||
|
|
||||||
class Dep extends BaseService {
|
class Dep extends BaseService {
|
||||||
|
private state = reactive<State>({
|
||||||
|
collecting: false,
|
||||||
|
});
|
||||||
|
|
||||||
private watcher = new Watcher({ initialTargets: reactive({}) });
|
private watcher = new Watcher({ initialTargets: reactive({}) });
|
||||||
|
|
||||||
|
public set<K extends StateKey, T extends State[K]>(name: K, value: T) {
|
||||||
|
this.state[name] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get<K extends StateKey>(name: K): State[K] {
|
||||||
|
return this.state[name];
|
||||||
|
}
|
||||||
|
|
||||||
public removeTargets(type: string = DepTargetType.DEFAULT) {
|
public removeTargets(type: string = DepTargetType.DEFAULT) {
|
||||||
this.watcher.removeTargets(type);
|
this.watcher.removeTargets(type);
|
||||||
|
|
||||||
@ -71,43 +89,40 @@ class Dep extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public collect(nodes: MNode[], depExtendedData: DepExtendedData = {}, deep = false, type?: DepTargetType) {
|
public collect(nodes: MNode[], depExtendedData: DepExtendedData = {}, deep = false, type?: DepTargetType) {
|
||||||
|
this.set('collecting', true);
|
||||||
this.watcher.collectByCallback(nodes, type, ({ node, target }) => {
|
this.watcher.collectByCallback(nodes, type, ({ node, target }) => {
|
||||||
this.collectNode(node, target, depExtendedData, deep);
|
this.collectNode(node, target, depExtendedData, deep);
|
||||||
});
|
});
|
||||||
|
this.set('collecting', false);
|
||||||
|
|
||||||
this.emit('collected', nodes, deep);
|
this.emit('collected', nodes, deep);
|
||||||
}
|
}
|
||||||
|
|
||||||
public collectIdle(nodes: MNode[], depExtendedData: DepExtendedData = {}, deep = false, type?: DepTargetType) {
|
public collectIdle(nodes: MNode[], depExtendedData: DepExtendedData = {}, deep = false, type?: DepTargetType) {
|
||||||
|
this.set('collecting', true);
|
||||||
let startTask = false;
|
let startTask = false;
|
||||||
this.watcher.collectByCallback(nodes, type, ({ node, target }) => {
|
this.watcher.collectByCallback(nodes, type, ({ node, target }) => {
|
||||||
startTask = true;
|
startTask = true;
|
||||||
idleTask.enqueueTask(
|
|
||||||
({ node, deep, target }) => {
|
this.enqueueTask(node, target, depExtendedData, deep);
|
||||||
this.collectNode(node, target, depExtendedData, deep);
|
|
||||||
},
|
|
||||||
{
|
|
||||||
node,
|
|
||||||
deep,
|
|
||||||
target,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Promise<void>((resolve) => {
|
return new Promise<void>((resolve) => {
|
||||||
if (!startTask) {
|
if (!startTask) {
|
||||||
this.emit('collected', nodes, deep);
|
this.emit('collected', nodes, deep);
|
||||||
|
this.set('collecting', false);
|
||||||
resolve();
|
resolve();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
idleTask.once('finish', () => {
|
idleTask.once('finish', () => {
|
||||||
this.emit('collected', nodes, deep);
|
this.emit('collected', nodes, deep);
|
||||||
|
this.set('collecting', false);
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
collectNode(node: MNode, target: Target, depExtendedData: DepExtendedData = {}, deep = false) {
|
public collectNode(node: MNode, target: Target, depExtendedData: DepExtendedData = {}, deep = false) {
|
||||||
// 先删除原有依赖,重新收集
|
// 先删除原有依赖,重新收集
|
||||||
if (isPage(node)) {
|
if (isPage(node)) {
|
||||||
Object.entries(target.deps).forEach(([depKey, dep]) => {
|
Object.entries(target.deps).forEach(([depKey, dep]) => {
|
||||||
@ -138,6 +153,10 @@ class Dep extends BaseService {
|
|||||||
return this.watcher.hasSpecifiedTypeTarget(type);
|
return this.watcher.hasSpecifiedTypeTarget(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public clearIdleTasks() {
|
||||||
|
idleTask.clearTasks();
|
||||||
|
}
|
||||||
|
|
||||||
public on<Name extends keyof DepEvents, Param extends DepEvents[Name]>(
|
public on<Name extends keyof DepEvents, Param extends DepEvents[Name]>(
|
||||||
eventName: Name,
|
eventName: Name,
|
||||||
listener: (...args: Param) => void | Promise<void>,
|
listener: (...args: Param) => void | Promise<void>,
|
||||||
@ -155,6 +174,25 @@ class Dep extends BaseService {
|
|||||||
public emit<Name extends keyof DepEvents, Param extends DepEvents[Name]>(eventName: Name, ...args: Param) {
|
public emit<Name extends keyof DepEvents, Param extends DepEvents[Name]>(eventName: Name, ...args: Param) {
|
||||||
return super.emit(eventName, ...args);
|
return super.emit(eventName, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enqueueTask(node: MNode, target: Target, depExtendedData: DepExtendedData, deep: boolean) {
|
||||||
|
idleTask.enqueueTask(
|
||||||
|
({ node, deep, target }) => {
|
||||||
|
this.collectNode(node, target, depExtendedData, deep);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node,
|
||||||
|
deep: false,
|
||||||
|
target,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (deep && Array.isArray(node.items) && node.items.length) {
|
||||||
|
node.items.forEach((item) => {
|
||||||
|
this.enqueueTask(item, target, depExtendedData, deep);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DepService = Dep;
|
export type DepService = Dep;
|
||||||
|
@ -22,6 +22,7 @@ import type { Writable } from 'type-fest';
|
|||||||
|
|
||||||
import type { Id, MApp, MContainer, MNode, MPage, MPageFragment, TargetOptions } from '@tmagic/core';
|
import type { Id, MApp, MContainer, MNode, MPage, MPageFragment, TargetOptions } from '@tmagic/core';
|
||||||
import { NodeType, Target, Watcher } from '@tmagic/core';
|
import { NodeType, Target, Watcher } from '@tmagic/core';
|
||||||
|
import type { ChangeRecord } from '@tmagic/form';
|
||||||
import { isFixed } from '@tmagic/stage';
|
import { isFixed } from '@tmagic/stage';
|
||||||
import {
|
import {
|
||||||
calcValueByFontsize,
|
calcValueByFontsize,
|
||||||
@ -68,7 +69,7 @@ export interface EditorEvents {
|
|||||||
select: [node: MNode | null];
|
select: [node: MNode | null];
|
||||||
add: [nodes: MNode[]];
|
add: [nodes: MNode[]];
|
||||||
remove: [nodes: MNode[]];
|
remove: [nodes: MNode[]];
|
||||||
update: [nodes: MNode[]];
|
update: [nodes: { newNode: MNode; oldNode: MNode; changeRecords?: ChangeRecord[] }[]];
|
||||||
'move-layer': [offset: number | LayerOffset];
|
'move-layer': [offset: number | LayerOffset];
|
||||||
'drag-to': [data: { targetIndex: number; configs: MNode | MNode[]; targetParent: MContainer }];
|
'drag-to': [data: { targetIndex: number; configs: MNode | MNode[]; targetParent: MContainer }];
|
||||||
'history-change': [data: MPage | MPageFragment];
|
'history-change': [data: MPage | MPageFragment];
|
||||||
@ -509,7 +510,10 @@ class Editor extends BaseService {
|
|||||||
this.emit('remove', nodes);
|
this.emit('remove', nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async doUpdate(config: MNode) {
|
public async doUpdate(
|
||||||
|
config: MNode,
|
||||||
|
{ changeRecords = [] }: { changeRecords?: ChangeRecord[] } = {},
|
||||||
|
): Promise<{ newNode: MNode; oldNode: MNode; changeRecords?: ChangeRecord[] }> {
|
||||||
const root = this.get('root');
|
const root = this.get('root');
|
||||||
if (!root) throw new Error('root为空');
|
if (!root) throw new Error('root为空');
|
||||||
|
|
||||||
@ -519,7 +523,7 @@ class Editor extends BaseService {
|
|||||||
|
|
||||||
if (!info.node) throw new Error(`获取不到id为${config.id}的节点`);
|
if (!info.node) throw new Error(`获取不到id为${config.id}的节点`);
|
||||||
|
|
||||||
const node = cloneDeep(toRaw(info.node));
|
const node = toRaw(info.node);
|
||||||
|
|
||||||
let newConfig = await this.toggleFixedPosition(toRaw(config), node, root);
|
let newConfig = await this.toggleFixedPosition(toRaw(config), node, root);
|
||||||
|
|
||||||
@ -541,7 +545,11 @@ class Editor extends BaseService {
|
|||||||
|
|
||||||
if (newConfig.type === NodeType.ROOT) {
|
if (newConfig.type === NodeType.ROOT) {
|
||||||
this.set('root', newConfig as MApp);
|
this.set('root', newConfig as MApp);
|
||||||
return newConfig;
|
return {
|
||||||
|
oldNode: node,
|
||||||
|
newNode: newConfig,
|
||||||
|
changeRecords,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const { parent } = info;
|
const { parent } = info;
|
||||||
@ -574,7 +582,11 @@ class Editor extends BaseService {
|
|||||||
|
|
||||||
this.addModifiedNodeId(newConfig.id);
|
this.addModifiedNodeId(newConfig.id);
|
||||||
|
|
||||||
return newConfig;
|
return {
|
||||||
|
oldNode: node,
|
||||||
|
newNode: newConfig,
|
||||||
|
changeRecords,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -582,17 +594,20 @@ class Editor extends BaseService {
|
|||||||
* @param config 新的节点配置,配置中需要有id信息
|
* @param config 新的节点配置,配置中需要有id信息
|
||||||
* @returns 更新后的节点配置
|
* @returns 更新后的节点配置
|
||||||
*/
|
*/
|
||||||
public async update(config: MNode | MNode[]): Promise<MNode | MNode[]> {
|
public async update(
|
||||||
|
config: MNode | MNode[],
|
||||||
|
data: { changeRecords?: ChangeRecord[] } = {},
|
||||||
|
): Promise<MNode | MNode[]> {
|
||||||
const nodes = Array.isArray(config) ? config : [config];
|
const nodes = Array.isArray(config) ? config : [config];
|
||||||
|
|
||||||
const newNodes = await Promise.all(nodes.map((node) => this.doUpdate(node)));
|
const updateData = await Promise.all(nodes.map((node) => this.doUpdate(node, data)));
|
||||||
|
|
||||||
if (newNodes[0]?.type !== NodeType.ROOT) {
|
if (updateData[0].oldNode?.type !== NodeType.ROOT) {
|
||||||
this.pushHistoryState();
|
this.pushHistoryState();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.emit('update', newNodes);
|
this.emit('update', updateData);
|
||||||
return Array.isArray(config) ? newNodes : newNodes[0];
|
return Array.isArray(config) ? updateData.map((item) => item.newNode) : updateData[0].newNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
.el-input__prefix {
|
.el-input__prefix {
|
||||||
padding: 7px;
|
padding: 7px;
|
||||||
|
@ -16,12 +16,23 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { detailedDiff } from 'deep-object-diff';
|
||||||
|
import { isObject } from 'lodash-es';
|
||||||
import serialize from 'serialize-javascript';
|
import serialize from 'serialize-javascript';
|
||||||
|
|
||||||
import type { Id, MApp, MContainer, MNode, MPage, MPageFragment } from '@tmagic/core';
|
import type { Id, MApp, MContainer, MNode, MPage, MPageFragment } from '@tmagic/core';
|
||||||
import { NodeType } from '@tmagic/core';
|
import { NODE_CONDS_KEY, NodeType } from '@tmagic/core';
|
||||||
import type StageCore from '@tmagic/stage';
|
import type StageCore from '@tmagic/stage';
|
||||||
import { calcValueByFontsize, getElById, getNodePath, isNumber, isPage, isPageFragment, isPop } from '@tmagic/utils';
|
import {
|
||||||
|
calcValueByFontsize,
|
||||||
|
DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX,
|
||||||
|
getElById,
|
||||||
|
getNodePath,
|
||||||
|
isNumber,
|
||||||
|
isPage,
|
||||||
|
isPageFragment,
|
||||||
|
isPop,
|
||||||
|
} from '@tmagic/utils';
|
||||||
|
|
||||||
import { Layout } from '@editor/type';
|
import { Layout } from '@editor/type';
|
||||||
|
|
||||||
@ -290,3 +301,84 @@ export const moveItemsInContainer = (sourceIndices: number[], parent: MContainer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isValueIncludeDataSource = (value: any) => {
|
||||||
|
if (typeof value === 'string' && /\$\{([\s\S]+?)\}/.test(value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Array.isArray(value) && `${value[0]}`.startsWith(DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (value?.isBindDataSource && value.dataSourceId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (value?.isBindDataSourceField && value.dataSourceId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isIncludeDataSourceByDiffAddResult = (diffResult: any) => {
|
||||||
|
for (const value of Object.values(diffResult)) {
|
||||||
|
const result = isValueIncludeDataSource(value);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isObject(value)) {
|
||||||
|
return isIncludeDataSourceByDiffAddResult(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isIncludeDataSourceByDiffUpdatedResult = (diffResult: any, oldNode: any) => {
|
||||||
|
for (const [key, value] of Object.entries<any>(diffResult)) {
|
||||||
|
if (isValueIncludeDataSource(value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isValueIncludeDataSource(oldNode[key])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isObject(value)) {
|
||||||
|
return isIncludeDataSourceByDiffUpdatedResult(value, oldNode[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isIncludeDataSource = (node: MNode, oldNode: MNode) => {
|
||||||
|
const diffResult = detailedDiff(oldNode, node);
|
||||||
|
|
||||||
|
let isIncludeDataSource = false;
|
||||||
|
|
||||||
|
if (diffResult.updated) {
|
||||||
|
// 修改了显示条件
|
||||||
|
if ((diffResult.updated as any)[NODE_CONDS_KEY]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
isIncludeDataSource = isIncludeDataSourceByDiffUpdatedResult(diffResult.updated, oldNode);
|
||||||
|
if (isIncludeDataSource) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diffResult.added) {
|
||||||
|
isIncludeDataSource = isIncludeDataSourceByDiffAddResult(diffResult.added);
|
||||||
|
if (isIncludeDataSource) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diffResult.deleted) {
|
||||||
|
// 删除了显示条件
|
||||||
|
if ((diffResult.deleted as any)[NODE_CONDS_KEY]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
isIncludeDataSource = isIncludeDataSourceByDiffAddResult(diffResult.deleted);
|
||||||
|
if (isIncludeDataSource) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isIncludeDataSource;
|
||||||
|
};
|
||||||
|
@ -26,6 +26,11 @@ export class IdleTask<T = any> extends EventEmitter {
|
|||||||
|
|
||||||
private taskHandle: number | null = null;
|
private taskHandle: number | null = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.setMaxListeners(1000);
|
||||||
|
}
|
||||||
|
|
||||||
public enqueueTask(taskHandler: (data: T) => void, taskData: T) {
|
public enqueueTask(taskHandler: (data: T) => void, taskData: T) {
|
||||||
this.taskList.push({
|
this.taskList.push({
|
||||||
handler: taskHandler,
|
handler: taskHandler,
|
||||||
@ -37,6 +42,10 @@ export class IdleTask<T = any> extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public clearTasks() {
|
||||||
|
this.taskList = [];
|
||||||
|
}
|
||||||
|
|
||||||
public on<Name extends keyof IdleTaskEvents, Param extends IdleTaskEvents[Name]>(
|
public on<Name extends keyof IdleTaskEvents, Param extends IdleTaskEvents[Name]>(
|
||||||
eventName: Name,
|
eventName: Name,
|
||||||
listener: (...args: Param) => void | Promise<void>,
|
listener: (...args: Param) => void | Promise<void>,
|
||||||
@ -57,9 +66,31 @@ export class IdleTask<T = any> extends EventEmitter {
|
|||||||
|
|
||||||
private runTaskQueue(deadline: IdleDeadline) {
|
private runTaskQueue(deadline: IdleDeadline) {
|
||||||
// 动画会占用空闲时间,当任务一直无法执行时,看看是否有动画正在播放
|
// 动画会占用空闲时间,当任务一直无法执行时,看看是否有动画正在播放
|
||||||
while ((deadline.timeRemaining() > 10 || deadline.didTimeout) && this.taskList.length) {
|
// 根据空闲时间的多少来决定执行的任务数,保证页面不卡死的情况下尽量多执行任务,不然当任务数巨大时,执行时间会很久
|
||||||
const task = this.taskList.shift();
|
// 执行不完不会影响配置,但是会影响画布渲染
|
||||||
task!.handler(task!.data);
|
while (deadline.timeRemaining() > 0 && this.taskList.length) {
|
||||||
|
const timeRemaining = deadline.timeRemaining();
|
||||||
|
let times = 0;
|
||||||
|
if (timeRemaining <= 5) {
|
||||||
|
times = 10;
|
||||||
|
} else if (timeRemaining <= 10) {
|
||||||
|
times = 100;
|
||||||
|
} else if (timeRemaining <= 15) {
|
||||||
|
times = 300;
|
||||||
|
} else {
|
||||||
|
times = 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < times; i++) {
|
||||||
|
const task = this.taskList.shift();
|
||||||
|
if (task) {
|
||||||
|
task.handler(task.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.taskList.length === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.taskList.length) {
|
if (this.taskList.length) {
|
||||||
|
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@ -322,6 +322,9 @@ importers:
|
|||||||
color:
|
color:
|
||||||
specifier: ^3.1.3
|
specifier: ^3.1.3
|
||||||
version: 3.2.1
|
version: 3.2.1
|
||||||
|
deep-object-diff:
|
||||||
|
specifier: ^1.1.9
|
||||||
|
version: 1.1.9
|
||||||
emmet-monaco-es:
|
emmet-monaco-es:
|
||||||
specifier: ^5.3.0
|
specifier: ^5.3.0
|
||||||
version: 5.5.0(monaco-editor@0.48.0)
|
version: 5.5.0(monaco-editor@0.48.0)
|
||||||
@ -4016,6 +4019,9 @@ packages:
|
|||||||
deep-is@0.1.4:
|
deep-is@0.1.4:
|
||||||
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
||||||
|
|
||||||
|
deep-object-diff@1.1.9:
|
||||||
|
resolution: {integrity: sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA==}
|
||||||
|
|
||||||
deep-state-observer@5.5.13:
|
deep-state-observer@5.5.13:
|
||||||
resolution: {integrity: sha512-Ai55DB6P/k/EBgC4jNlYqIgp8e6Mzl7E/4vzIDMfrJ+TnCFmeA7TySaa3BapioDz4Cr6dYamVI4Mx2FMtpfM4w==}
|
resolution: {integrity: sha512-Ai55DB6P/k/EBgC4jNlYqIgp8e6Mzl7E/4vzIDMfrJ+TnCFmeA7TySaa3BapioDz4Cr6dYamVI4Mx2FMtpfM4w==}
|
||||||
|
|
||||||
@ -9527,6 +9533,8 @@ snapshots:
|
|||||||
|
|
||||||
deep-is@0.1.4: {}
|
deep-is@0.1.4: {}
|
||||||
|
|
||||||
|
deep-object-diff@1.1.9: {}
|
||||||
|
|
||||||
deep-state-observer@5.5.13: {}
|
deep-state-observer@5.5.13: {}
|
||||||
|
|
||||||
defaults@1.0.4:
|
defaults@1.0.4:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user