Skip to content

Commit

Permalink
✨ feat: 完善 AssetStore 数据链路
Browse files Browse the repository at this point in the history
  • Loading branch information
arvinxx committed Jun 14, 2023
1 parent 7a55431 commit eefef20
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 46 deletions.
13 changes: 8 additions & 5 deletions src/ComponentAsset/ComponentAsset.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export class ComponentAsset<Config = any, Props = any> {
componentStore: UseBoundStore<any>;
componentStoreApi: () => WithoutCallSignature<StoreApi<any>>;
configSelector: (s: Config) => Partial<Config>;
setConfig: (config: Config, set) => void;
codeEmitter: CodeEmitter<Config, Props>;

isStarterMode: (store: any) => boolean = () => false;
Expand All @@ -67,18 +68,20 @@ export class ComponentAsset<Config = any, Props = any> {
this.defaultConfig = params.defaultConfig;
}

const { createStore, Provider, useStoreApi } = createAssetStore(params.createStore, {
initialState: params.defaultConfig,
...params.storeOptions,
});
const { createStore, Provider, useStoreApi } = createAssetStore(
params.createStore,
params.storeOptions,
);

// 初始化 store
this.componentStore = createStore();
this.componentStoreApi = useStoreApi;
this.AssetProvider = Provider;

this.configSelector =
params.storeOptions?.partial || ((s: Config) => JSON.parse(JSON.stringify(s)));
params.storeOptions?.getConfig || ((s: Config) => JSON.parse(JSON.stringify(s)));
this.setConfig = params.storeOptions?.setConfig || ((config: Config, set) => set(config));

// 交互规则
this.rules = params.ui.rules;

Expand Down
14 changes: 5 additions & 9 deletions src/ComponentAsset/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { DevtoolsOptions } from 'zustand/middleware';
import { PublicProEditorStore } from '@/ProEditor/store';

export interface AssetStoreOptions<T = any> {
initialState?: T;
devtools?: boolean | DevtoolsOptions;
partial?: (state: T) => any;
getConfig?: (state: any) => T;
setConfig?: (config: T, set) => void;
}

export type CreateAssetStore<T> = StateCreator<
Expand All @@ -33,11 +33,7 @@ export const createAssetStore = <T>(

const devtools = optionalDevtools(!(options?.devtools === false));

const initialState = options?.initialState || {};

return create<T>()(
devtools((...params) => ({ ...createStore(...params), ...initialState }), devtoolsOptions),
);
return create<T>()(devtools(createStore, devtoolsOptions));
};

return { Provider, createStore: store, useStoreApi };
Expand All @@ -48,6 +44,6 @@ export type WithoutCallSignature<T> = {
};

export const createUseAssetStore = <T>(): {
useStore: UseContextStore<StoreApi<T>>;
useStoreApi: () => WithoutCallSignature<StoreApi<T>>;
useStore: UseContextStore<StoreApi<T & PublicProEditorStore>>;
useStoreApi: () => WithoutCallSignature<StoreApi<T & PublicProEditorStore>>;
} => ({ useStore, useStoreApi });
2 changes: 1 addition & 1 deletion src/LevaPanel/Schema.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const Schema: <T>(props: SchemaProps<T>) => ReactNode = memo(
onChange?.({ [context.key]: value } as any, full as any);
},
} as SchemaItem;
if (outConfig[key]) {
if (outConfig && outConfig[key]) {
config['value'] = outConfig[key];
}
return config;
Expand Down
40 changes: 26 additions & 14 deletions src/ProEditor/components/AssetStoreUpdater/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import isEqual from 'fast-deep-equal';
import { memo, useEffect } from 'react';
import { createStoreUpdater } from 'zustand-utils';
import { shallow } from 'zustand/shallow';

import { useProEditor } from '../../hooks/useProEditor';
Expand All @@ -9,38 +8,51 @@ import { useStore, useStoreApi } from '../../store';
const AssetStoreUpdater = memo(() => {
const instance = useProEditor();

const [useAssetStoreApi, useAssetStore, configSelector] = useStore(
const [useAssetStoreApi, configSelector, setConfig, config] = useStore(
(s) => [
s.componentAsset.componentStoreApi,
s.componentAsset.componentStore,
s.componentAsset.configSelector,
s.componentAsset.setConfig,
s.config,
],
shallow,
);
const assetStoreApi = useAssetStoreApi();

// 将 instance 的方法全部同步到 assetStore
const setState = (state, action: any) => (assetStoreApi.setState as any)(state, false, action);

useEffect(() => {
assetStoreApi.setState({ ...instance });
setState(instance, { type: '⏬ 注入 editor 方法', payload: Object.keys(instance) });
}, []);

// 将计算后的默认值传给面板
// 用等式做一次优化,不然每次都会重新计算
const defaultConfig = useStore((s) => s.componentAsset.getDefaultConfig(s.mode), isEqual);
const defaultConfig = useStore(
(s) =>
s.componentAsset.defaultConfig || configSelector(s.componentAsset.getDefaultConfig(s.mode)),
isEqual,
);

const proEditorStoreApi = useStoreApi();
const useStoreUpdater = createStoreUpdater(proEditorStoreApi);

const syncState = (state) => setState(state, { type: '🔄 从 Editor 同步状态', payload: state });

// 用 defaultConfig 更新一次config
useStoreUpdater('config', defaultConfig, []);
useEffect(() => {
const state = { config: defaultConfig };
proEditorStoreApi.setState(state);
proEditorStoreApi.getState().yjsDoc.updateHistoryData(state);

setConfig(config, syncState);
}, []);

// 将 assetStore 的 config 自动同步到 proEditorStore
const assetConfig = useAssetStore(configSelector, isEqual);
// 将 proEditorStore 的 config 自动同步到 assetStore
useEffect(() => {
if (typeof assetConfig === 'undefined') return;
if (isEqual(assetConfig, proEditorStoreApi.getState().config)) return;
const assetConfig = configSelector(assetStoreApi.getState());
if (isEqual(assetConfig, config)) return;

proEditorStoreApi.setState({ config: assetConfig });
}, [assetConfig]);
setConfig(config, syncState);
}, [config]);

return null;
});
Expand Down
23 changes: 14 additions & 9 deletions src/ProEditor/components/Canvas/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,29 @@ import { memo } from 'react';
import { shallow } from 'zustand/shallow';

import FreeCanvas from '../../../FreeCanvas';
import { useUpdateEditorAwareness } from '../../hooks/useEditorAwareness';
import { useStore } from '../../store';
import Component from './Component';

const Canvas: FC = memo(() => {
const [viewport, componentAsset] = useStore(
(s) => [s.editorAwareness.viewport, s.componentAsset],
shallow,
);
const [enableCanvasInteraction, toggleCanvasInteraction] = useStore(
(s) => [s.enableCanvasInteraction, s.toggleCanvasInteraction],
const [
viewport,
componentAsset,
enableCanvasInteraction,
toggleCanvasInteraction,
updateEditorAwareness,
] = useStore(
(s) => [
s.editorAwareness.viewport,
s.componentAsset,
s.enableCanvasInteraction,
s.toggleCanvasInteraction,
s.internalUpdateEditorAwareness,
],
shallow,
);

const ErrorBoundary = componentAsset.ErrorBoundary;

const { updateEditorAwareness } = useUpdateEditorAwareness();

return (
<ErrorBoundary>
<FreeCanvas
Expand Down
22 changes: 16 additions & 6 deletions src/ProEditor/store/slices/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@ export interface ConfigPublicAction {
*/
exportConfig: () => void;
resetConfig: () => void;
updateConfig: <T>(config: Partial<T>, options?: ActionOptions) => void;
updateConfig: <T>(config: Partial<T>, replace?: boolean, options?: ActionOptions) => void;
}

export interface ConfigSlice extends ConfigPublicAction, ConfigSliceState {
/**
* 内部更新配置
**/
internalUpdateConfig: <T>(config: Partial<T>, payload?: ActionPayload) => void;
internalUpdateConfig: <T>(config: Partial<T>, payload?: ActionPayload, replace?: boolean) => void;
}

export const configSlice: StateCreator<
Expand All @@ -80,19 +80,25 @@ export const configSlice: StateCreator<
yjsDoc: new DocWithHistoryManager<{ config: any }>(),
};

const undoLength = initialConfigState.yjsDoc.undoManager.undoStack.length;

const redoLength = initialConfigState.yjsDoc.undoManager.redoStack.length;

return {
...initialConfigState,
undoLength,
redoLength,
resetConfig: () => {
set({ config: initialConfigState.config, props: initialConfigState.props });
},
/**
* 内部修改 config 方法
* 传给 ProTableStore 进行 config 同步
*/
internalUpdateConfig: (config, payload) => {
internalUpdateConfig: (config, payload, replace) => {
const { onConfigChange, componentAsset } = get();

const nextConfig = { ...get().config, ...config };
const nextConfig = replace ? config : { ...get().config, ...config };

set({ config: nextConfig }, false, payload);

Expand All @@ -114,8 +120,12 @@ export const configSlice: StateCreator<
document.body.removeChild(eleLink);
},

updateConfig: (config, action) => {
get().internalUpdateConfig(config, { type: '外部 updateConfig 更新', payload: config });
updateConfig: (config, replace, action) => {
get().internalUpdateConfig(
config,
{ type: '外部 updateConfig 更新', payload: config },
replace,
);

const useAction = merge({}, { recordHistory: true }, action);

Expand Down
13 changes: 11 additions & 2 deletions src/ProEditor/store/slices/general.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { StackItem } from 'yjs/dist/src/utils/UndoManager';
import { StateCreator } from 'zustand';

import getPrefixCls from '@/_util/getPrefixCls';
Expand Down Expand Up @@ -33,6 +34,8 @@ const initialGeneralState: GeneralSliceState = {
export interface GeneralPublicAction {
undo: () => void;
redo: () => void;
undoStack: () => StackItem[];
redoStack: () => StackItem[];
}

export interface GeneralSlice extends GeneralPublicAction, GeneralSliceState {}
Expand All @@ -45,13 +48,19 @@ export const generalSlice: StateCreator<
> = (set, get) => ({
...initialGeneralState,

undoStack: () => {
return get().yjsDoc.undoManager.undoStack;
},
redoStack: () => {
return get().yjsDoc.undoManager.redoStack;
},
undo: () => {
const { yjsDoc, internalUpdateConfig } = get();
const stack = yjsDoc.undo();

const { config } = yjsDoc.getHistoryJSON();

internalUpdateConfig(config, { type: 'history/undo', payload: stack });
internalUpdateConfig(config, { type: 'history/undo', payload: stack }, true);
},
redo: () => {
const { yjsDoc, internalUpdateConfig } = get();
Expand All @@ -60,6 +69,6 @@ export const generalSlice: StateCreator<

const { config } = yjsDoc.getHistoryJSON();

internalUpdateConfig(config, { type: 'history/redo', payload: stack });
internalUpdateConfig(config, { type: 'history/redo', payload: stack }, true);
},
});

0 comments on commit eefef20

Please sign in to comment.