feat(toolbar): new Toolbar to enable reactive state synchronization (#3983)
This commit is contained in:
1 parent
79d5c36bda
commit
566b25a544
155 files changed
+6311
-6577
No files matched your search
@@ -0,0 +1,106 @@
|
||||
import React, { createContext, useContext, useReducer } from 'react';
|
||||
|
||||
export const initialState = {};
|
||||
|
||||
export const toolboxReducer = (state, action) => {
|
||||
const { toolbarSectionId } = action.payload;
|
||||
|
||||
if (!state[toolbarSectionId]) {
|
||||
state[toolbarSectionId] = { activeTool: null, toolOptions: {} };
|
||||
}
|
||||
|
||||
switch (action.type) {
|
||||
case 'SET_ACTIVE_TOOL':
|
||||
return {
|
||||
...state,
|
||||
[toolbarSectionId]: {
|
||||
...state[toolbarSectionId],
|
||||
activeTool: action.payload.activeTool,
|
||||
},
|
||||
};
|
||||
case 'UPDATE_TOOL_OPTION':
|
||||
const { toolName, optionName, value } = action.payload;
|
||||
return {
|
||||
...state,
|
||||
[toolbarSectionId]: {
|
||||
...state[toolbarSectionId],
|
||||
toolOptions: {
|
||||
...state[toolbarSectionId].toolOptions,
|
||||
[toolName]: state[toolbarSectionId].toolOptions[toolName].map(option =>
|
||||
option.id === optionName ? { ...option, value } : option
|
||||
),
|
||||
},
|
||||
},
|
||||
};
|
||||
case 'INITIALIZE_TOOL_OPTIONS':
|
||||
// Initialize tool options for each toolbarSectionId
|
||||
return {
|
||||
...state,
|
||||
[action.toolbarSectionId]: {
|
||||
...state[action.toolbarSectionId],
|
||||
toolOptions: action.payload,
|
||||
},
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
const ToolboxContext = createContext();
|
||||
|
||||
export const ToolboxProvider = ({ children }) => {
|
||||
const [state, dispatch] = useReducer(toolboxReducer, initialState);
|
||||
|
||||
const handleToolSelect = (toolbarSectionId, toolName) => {
|
||||
dispatch({
|
||||
type: 'SET_ACTIVE_TOOL',
|
||||
payload: { toolbarSectionId, activeTool: toolName },
|
||||
});
|
||||
};
|
||||
|
||||
const handleToolOptionChange = (toolbarSectionId, toolName, optionName, newValue) => {
|
||||
dispatch({
|
||||
type: 'UPDATE_TOOL_OPTION',
|
||||
payload: { toolbarSectionId, toolName, optionName, value: newValue },
|
||||
});
|
||||
};
|
||||
|
||||
const initializeToolOptions = (toolbarSectionId, toolOptions) => {
|
||||
dispatch({
|
||||
type: 'INITIALIZE_TOOL_OPTIONS',
|
||||
toolbarSectionId,
|
||||
payload: toolOptions,
|
||||
});
|
||||
};
|
||||
|
||||
const api = { handleToolSelect, handleToolOptionChange, initializeToolOptions };
|
||||
|
||||
const value = { state, api };
|
||||
|
||||
return <ToolboxContext.Provider value={value}>{children}</ToolboxContext.Provider>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom hook for accessing toolbox state and actions for a specific toolbar section.
|
||||
* You can use this hook to access the state and actions for a specific toolbar section (
|
||||
* defined by the toolbarSectionId) in your custom toolbar components. This hook
|
||||
* helps to manage the state and actions for the tools and their options in the toolbar.
|
||||
*/
|
||||
export const useToolbox = toolbarSectionId => {
|
||||
const context = useContext(ToolboxContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useToolbox must be used within a ToolboxProvider');
|
||||
}
|
||||
const { state, api } = context;
|
||||
|
||||
return {
|
||||
state: state[toolbarSectionId] || { activeTool: null, toolOptions: {} },
|
||||
api: {
|
||||
handleToolSelect: toolName => api.handleToolSelect(toolbarSectionId, toolName),
|
||||
handleToolOptionChange: (toolName, optionName, value) =>
|
||||
api.handleToolOptionChange(toolbarSectionId, toolName, optionName, value),
|
||||
initializeToolOptions: toolOptions =>
|
||||
api.initializeToolOptions(toolbarSectionId, toolOptions),
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
export function toolboxReducer(state, action) {
|
||||
let newState = { ...state };
|
||||
|
||||
const { payload } = action;
|
||||
const { stateSyncService, buttonSectionId } = payload || {};
|
||||
|
||||
switch (action.type) {
|
||||
case 'SET_ACTIVE_TOOL':
|
||||
newState = {
|
||||
...state,
|
||||
activeTool: payload.activeTool,
|
||||
};
|
||||
break;
|
||||
case 'UPDATE_TOOL_OPTION':
|
||||
newState = {
|
||||
...state,
|
||||
toolOptions: {
|
||||
...state.toolOptions,
|
||||
[payload.toolName]: state.toolOptions[payload.toolName].map(option =>
|
||||
option.id === payload.optionName ? { ...option, value: payload.value } : option
|
||||
),
|
||||
},
|
||||
};
|
||||
break;
|
||||
case 'INITIALIZE_TOOL_OPTIONS':
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const newToolOptions = Object.keys(payload?.toolOptions || {}).reduce((acc, toolId) => {
|
||||
const tool = payload.toolOptions[toolId];
|
||||
if (state.toolOptions[toolId]) {
|
||||
// Preserve existing options, potentially merging with new ones if necessary
|
||||
acc[toolId] = state.toolOptions[toolId].map(existingOption => {
|
||||
const initialOption = tool.find(option => option.id === existingOption.id);
|
||||
return initialOption
|
||||
? { ...initialOption, value: existingOption.value }
|
||||
: existingOption;
|
||||
});
|
||||
} else {
|
||||
acc[toolId] = tool;
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
newState = {
|
||||
...state,
|
||||
toolOptions: newToolOptions,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// store the state in the stateSyncService
|
||||
if (payload?.stateSyncService) {
|
||||
const prevState = stateSyncService.getState()?.['uiStateStore']?.[buttonSectionId];
|
||||
stateSyncService.store({
|
||||
uiStateStore: {
|
||||
...stateSyncService.getState()?.['uiStateStore'],
|
||||
[buttonSectionId]: {
|
||||
...prevState,
|
||||
...newState,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
return newState;
|
||||
}
|
||||
Reference in new issue
Block a user