Jotai 13 (#18178)
## Recoil to Jotai Migration — PR 13: Workflow, Page Layout, AI, Activities, Settings, SSE & Remaining Modules ### Summary This is the 13th PR in the [14-PR migration series](packages/twenty-front/src/modules/ui/utilities/state/jotai/MIGRATION_PLAN.md) to replace Recoil with Jotai as the state management library in `twenty-front`. This PR migrates the remaining consumer code across all modules — updating ~1,076 files with ~10k insertions/deletions. ### What changed **New Jotai infrastructure (13 new files):** - `createComponentSelectorV2` and `createComponentFamilySelectorV2` — instance-scoped derived atoms, replacing Recoil's component selectors - 6 new hooks: `useRecoilComponentSelectorValueV2`, `useRecoilComponentFamilySelectorValueV2`, `useRecoilComponentSelectorCallbackStateV2`, `useRecoilComponentFamilySelectorCallbackStateV2`, `useRecoilComponentStateCallbackStateV2`, `useRecoilComponentFamilyStateCallbackStateV2` - New types: `ComponentSelectorV2`, `ComponentFamilySelectorV2`, `SelectorCallbacksV2` - Extended `buildGetHelper` to support the new selector patterns **State definition migrations:** - `createState()` → `createStateV2()` (Jotai `atom()`) - `createFamilyState()` → `createFamilyStateV2()` (Jotai `atom()` with family cache) - Recoil `selector`/`selectorFamily` → Jotai derived `atom()` via V2 utilities **Hook migrations (~2,400 removed, ~1,545 added V2 equivalents):** - `useRecoilValue` → `useRecoilValueV2` - `useRecoilState` → `useRecoilStateV2` - `useSetRecoilState` → `useSetRecoilStateV2` - `useRecoilCallback` → `useCallback` + `jotaiStore.get/set` - `useRecoilComponentValue` → `useRecoilComponentValueV2` - `useSetRecoilComponentState` → `useSetRecoilComponentStateV2` - `useRecoilComponentFamilyValue` → `useRecoilComponentFamilyValueV2` / `useFamilySelectorValueV2` - ~397 direct `import from 'recoil'` removed **Deleted files (9 files):** - `dateLocaleState.ts` — consolidated - `currentAIChatThreadTitleStateV2.ts` — renamed to replace the original - `isCommandMenuOpenedState.ts` — removed - `filesFieldUploadState.ts` — replaced by V2 - `recordStoreFamilySelector.ts`, `recordStoreFieldValueSelector.ts`, `recordStoreRecordsSelector.ts` — replaced by V2 equivalents - `settingsAllRolesSelector.ts` — replaced by `useSettingsAllRoles` hook - `settingsRoleIdsStateV2.ts` — consolidated **Modules migrated:** - Workflow (diagram, steps, variables, filters) - Page Layout (widgets, fields, graph, tabs) - AI (chat, agents, browsing context) - Activities (rich text editor, targets, timeline) - Action Menu (single/multiple record actions) - Command Menu (navigation, history, pages, workflow) - Context Store - Record Board, Record Table, Record Calendar, Record Index - Record Field, Record Filter, Record Sort, Record Group - Record Drag, Record Picker, Record Store - Views (view bar, view picker, filters, sorts) - Settings (roles, permissions, SSO, security, data model, developers, domains) - SSE (event streams, subscriptions) - Spreadsheet Import - Auth, Favorites, Front Components, Information Banner
This commit is contained in:
+1
-4
@@ -7,19 +7,16 @@ import {
|
||||
Provider as JotaiProvider,
|
||||
} from 'jotai';
|
||||
import { type ReactNode, useState } from 'react';
|
||||
import { RecoilRoot, type MutableSnapshot } from 'recoil';
|
||||
|
||||
export const PAGE_LAYOUT_TEST_INSTANCE_ID =
|
||||
'20202020-f244-4ae0-906b-78958aa07642';
|
||||
|
||||
export const PageLayoutTestWrapper = ({
|
||||
children,
|
||||
initializeState,
|
||||
instanceId: instanceIdFromProps,
|
||||
store: storeFromProps,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
initializeState?: (snapshot: MutableSnapshot) => void;
|
||||
instanceId?: string;
|
||||
store?: ReturnType<typeof getDefaultStore>;
|
||||
}) => {
|
||||
@@ -35,7 +32,7 @@ export const PageLayoutTestWrapper = ({
|
||||
instanceId: getTabListInstanceIdFromPageLayoutId(instanceId),
|
||||
}}
|
||||
>
|
||||
<RecoilRoot initializeState={initializeState}>{children}</RecoilRoot>
|
||||
{children}
|
||||
</TabListComponentInstanceContext.Provider>
|
||||
</PageLayoutComponentInstanceContext.Provider>
|
||||
</JotaiProvider>
|
||||
|
||||
+33
-35
@@ -1,5 +1,6 @@
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { createStore } from 'jotai';
|
||||
import { type ReactNode } from 'react';
|
||||
import { pageLayoutSelectedCellsComponentState } from '@/page-layout/states/pageLayoutSelectedCellsComponentState';
|
||||
import {
|
||||
@@ -10,28 +11,27 @@ import { useChangePageLayoutDragSelection } from '@/page-layout/hooks/useChangeP
|
||||
|
||||
describe('useChangePageLayoutDragSelection', () => {
|
||||
it('should add cell to selection when selected is true', () => {
|
||||
const store = createStore();
|
||||
store.set(
|
||||
pageLayoutSelectedCellsComponentState.atomFamily({
|
||||
instanceId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
}),
|
||||
new Set(['cell-1']),
|
||||
);
|
||||
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
changeDragSelection: useChangePageLayoutDragSelection(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
selectedCells: useRecoilComponentValue(
|
||||
selectedCells: useRecoilComponentValueV2(
|
||||
pageLayoutSelectedCellsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
}),
|
||||
{
|
||||
wrapper: ({ children }: { children: ReactNode }) => (
|
||||
<PageLayoutTestWrapper
|
||||
initializeState={({ set }) => {
|
||||
set(
|
||||
pageLayoutSelectedCellsComponentState.atomFamily({
|
||||
instanceId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
}),
|
||||
new Set(['cell-1']),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<PageLayoutTestWrapper store={store}>
|
||||
{children}
|
||||
</PageLayoutTestWrapper>
|
||||
),
|
||||
@@ -54,28 +54,27 @@ describe('useChangePageLayoutDragSelection', () => {
|
||||
});
|
||||
|
||||
it('should remove cell from selection when selected is false', () => {
|
||||
const store = createStore();
|
||||
store.set(
|
||||
pageLayoutSelectedCellsComponentState.atomFamily({
|
||||
instanceId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
}),
|
||||
new Set(['cell-1', 'cell-2']),
|
||||
);
|
||||
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
changeDragSelection: useChangePageLayoutDragSelection(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
selectedCells: useRecoilComponentValue(
|
||||
selectedCells: useRecoilComponentValueV2(
|
||||
pageLayoutSelectedCellsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
}),
|
||||
{
|
||||
wrapper: ({ children }: { children: ReactNode }) => (
|
||||
<PageLayoutTestWrapper
|
||||
initializeState={({ set }) => {
|
||||
set(
|
||||
pageLayoutSelectedCellsComponentState.atomFamily({
|
||||
instanceId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
}),
|
||||
new Set(['cell-1', 'cell-2']),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<PageLayoutTestWrapper store={store}>
|
||||
{children}
|
||||
</PageLayoutTestWrapper>
|
||||
),
|
||||
@@ -103,7 +102,7 @@ describe('useChangePageLayoutDragSelection', () => {
|
||||
changeDragSelection: useChangePageLayoutDragSelection(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
selectedCells: useRecoilComponentValue(
|
||||
selectedCells: useRecoilComponentValueV2(
|
||||
pageLayoutSelectedCellsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
@@ -133,28 +132,27 @@ describe('useChangePageLayoutDragSelection', () => {
|
||||
});
|
||||
|
||||
it('should handle removing non-existent cell', () => {
|
||||
const store = createStore();
|
||||
store.set(
|
||||
pageLayoutSelectedCellsComponentState.atomFamily({
|
||||
instanceId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
}),
|
||||
new Set(['cell-1']),
|
||||
);
|
||||
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
changeDragSelection: useChangePageLayoutDragSelection(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
selectedCells: useRecoilComponentValue(
|
||||
selectedCells: useRecoilComponentValueV2(
|
||||
pageLayoutSelectedCellsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
}),
|
||||
{
|
||||
wrapper: ({ children }: { children: ReactNode }) => (
|
||||
<PageLayoutTestWrapper
|
||||
initializeState={({ set }) => {
|
||||
set(
|
||||
pageLayoutSelectedCellsComponentState.atomFamily({
|
||||
instanceId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
}),
|
||||
new Set(['cell-1']),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<PageLayoutTestWrapper store={store}>
|
||||
{children}
|
||||
</PageLayoutTestWrapper>
|
||||
),
|
||||
|
||||
+8
-8
@@ -3,8 +3,8 @@ import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pag
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
import { type GraphWidgetFieldSelection } from '@/page-layout/types/GraphWidgetFieldSelection';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@@ -35,16 +35,16 @@ describe('useCreatePageLayoutGraphWidget', () => {
|
||||
instanceId: `${PAGE_LAYOUT_TEST_INSTANCE_ID}-tab-list`,
|
||||
}),
|
||||
);
|
||||
const setPageLayoutDraft = useSetRecoilComponentState(
|
||||
const setPageLayoutDraft = useSetRecoilComponentStateV2(
|
||||
pageLayoutDraftComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
);
|
||||
const pageLayoutDraft = useRecoilComponentValue(
|
||||
const pageLayoutDraft = useRecoilComponentValueV2(
|
||||
pageLayoutDraftComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
);
|
||||
const allWidgets = pageLayoutDraft.tabs.flatMap((tab) => tab.widgets);
|
||||
const pageLayoutCurrentLayouts = useRecoilComponentValue(
|
||||
const pageLayoutCurrentLayouts = useRecoilComponentValueV2(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
);
|
||||
@@ -109,7 +109,7 @@ describe('useCreatePageLayoutGraphWidget', () => {
|
||||
it('should handle different graph types', () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setPageLayoutDraft = useSetRecoilComponentState(
|
||||
const setPageLayoutDraft = useSetRecoilComponentStateV2(
|
||||
pageLayoutDraftComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
);
|
||||
@@ -118,12 +118,12 @@ describe('useCreatePageLayoutGraphWidget', () => {
|
||||
instanceId: `${PAGE_LAYOUT_TEST_INSTANCE_ID}-tab-list`,
|
||||
}),
|
||||
);
|
||||
const pageLayoutDraft = useRecoilComponentValue(
|
||||
const pageLayoutDraft = useRecoilComponentValueV2(
|
||||
pageLayoutDraftComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
);
|
||||
const allWidgets = pageLayoutDraft.tabs.flatMap((tab) => tab.widgets);
|
||||
const pageLayoutCurrentLayouts = useRecoilComponentValue(
|
||||
const pageLayoutCurrentLayouts = useRecoilComponentValueV2(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
);
|
||||
|
||||
+8
-9
@@ -2,9 +2,8 @@ import { useCreatePageLayoutTab } from '@/page-layout/hooks/useCreatePageLayoutT
|
||||
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { PageLayoutType } from '~/generated-metadata/graphql';
|
||||
@@ -29,11 +28,11 @@ describe('useCreatePageLayoutTab', () => {
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
createTab: useCreatePageLayoutTab(PAGE_LAYOUT_TEST_INSTANCE_ID),
|
||||
pageLayoutDraft: useRecoilComponentValue(
|
||||
pageLayoutDraft: useRecoilComponentValueV2(
|
||||
pageLayoutDraftComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
pageLayoutCurrentLayouts: useRecoilComponentValue(
|
||||
pageLayoutCurrentLayouts: useRecoilComponentValueV2(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
@@ -71,7 +70,7 @@ describe('useCreatePageLayoutTab', () => {
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
createTab: useCreatePageLayoutTab(PAGE_LAYOUT_TEST_INSTANCE_ID),
|
||||
pageLayoutDraft: useRecoilComponentValue(
|
||||
pageLayoutDraft: useRecoilComponentValueV2(
|
||||
pageLayoutDraftComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
@@ -99,7 +98,7 @@ describe('useCreatePageLayoutTab', () => {
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
createTab: useCreatePageLayoutTab(PAGE_LAYOUT_TEST_INSTANCE_ID),
|
||||
pageLayoutDraft: useRecoilComponentValue(
|
||||
pageLayoutDraft: useRecoilComponentValueV2(
|
||||
pageLayoutDraftComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
@@ -135,7 +134,7 @@ describe('useCreatePageLayoutTab', () => {
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
createTab: useCreatePageLayoutTab(PAGE_LAYOUT_TEST_INSTANCE_ID),
|
||||
pageLayoutCurrentLayouts: useRecoilComponentValue(
|
||||
pageLayoutCurrentLayouts: useRecoilComponentValueV2(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
@@ -204,11 +203,11 @@ describe('useCreatePageLayoutTab', () => {
|
||||
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const setPageLayoutDraft = useSetRecoilComponentState(
|
||||
const setPageLayoutDraft = useSetRecoilComponentStateV2(
|
||||
pageLayoutDraftComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
);
|
||||
const pageLayoutDraft = useRecoilComponentValue(
|
||||
const pageLayoutDraft = useRecoilComponentValueV2(
|
||||
pageLayoutDraftComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
);
|
||||
|
||||
+3
-3
@@ -2,7 +2,7 @@ import { useNavigatePageLayoutCommandMenu } from '@/command-menu/pages/page-layo
|
||||
import { useCreateWidgetFromClick } from '@/page-layout/hooks/useCreateWidgetFromClick';
|
||||
import { pageLayoutDraggedAreaComponentState } from '@/page-layout/states/pageLayoutDraggedAreaComponentState';
|
||||
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { type ReactNode } from 'react';
|
||||
import { CommandMenuPages } from 'twenty-shared/types';
|
||||
@@ -29,11 +29,11 @@ describe('useCreateWidgetFromClick', () => {
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
createWidget: useCreateWidgetFromClick(),
|
||||
draggedArea: useRecoilComponentValue(
|
||||
draggedArea: useRecoilComponentValueV2(
|
||||
pageLayoutDraggedAreaComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
editingWidgetId: useRecoilComponentValue(
|
||||
editingWidgetId: useRecoilComponentValueV2(
|
||||
pageLayoutEditingWidgetIdComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
|
||||
+48
-50
@@ -3,8 +3,9 @@ import { useEndPageLayoutDragSelection } from '@/page-layout/hooks/useEndPageLay
|
||||
import { pageLayoutDraggedAreaComponentState } from '@/page-layout/states/pageLayoutDraggedAreaComponentState';
|
||||
import { pageLayoutSelectedCellsComponentState } from '@/page-layout/states/pageLayoutSelectedCellsComponentState';
|
||||
import { calculateGridBoundsFromSelectedCells } from '@/page-layout/utils/calculateGridBoundsFromSelectedCells';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { createStore } from 'jotai';
|
||||
import { type ReactNode } from 'react';
|
||||
import { CommandMenuPages } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@@ -18,26 +19,29 @@ jest.mock(
|
||||
);
|
||||
jest.mock('../../utils/calculateGridBoundsFromSelectedCells');
|
||||
|
||||
const createInitializeState =
|
||||
(initialSelectedCells?: Set<string>, initialDraggedArea?: any) =>
|
||||
({ set }: { set: any }) => {
|
||||
if (isDefined(initialSelectedCells)) {
|
||||
set(
|
||||
pageLayoutSelectedCellsComponentState.atomFamily({
|
||||
instanceId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
}),
|
||||
initialSelectedCells,
|
||||
);
|
||||
}
|
||||
if (initialDraggedArea !== undefined) {
|
||||
set(
|
||||
pageLayoutDraggedAreaComponentState.atomFamily({
|
||||
instanceId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
}),
|
||||
initialDraggedArea,
|
||||
);
|
||||
}
|
||||
};
|
||||
const createTestStore = (
|
||||
initialSelectedCells?: Set<string>,
|
||||
initialDraggedArea?: any,
|
||||
) => {
|
||||
const store = createStore();
|
||||
if (isDefined(initialSelectedCells)) {
|
||||
store.set(
|
||||
pageLayoutSelectedCellsComponentState.atomFamily({
|
||||
instanceId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
}),
|
||||
initialSelectedCells,
|
||||
);
|
||||
}
|
||||
if (initialDraggedArea !== undefined) {
|
||||
store.set(
|
||||
pageLayoutDraggedAreaComponentState.atomFamily({
|
||||
instanceId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
}),
|
||||
initialDraggedArea,
|
||||
);
|
||||
}
|
||||
return store;
|
||||
};
|
||||
|
||||
describe('useEndPageLayoutDragSelection', () => {
|
||||
const mockNavigatePageLayoutCommandMenu = jest.fn();
|
||||
@@ -55,28 +59,25 @@ describe('useEndPageLayoutDragSelection', () => {
|
||||
mockBounds,
|
||||
);
|
||||
|
||||
const store = createTestStore(new Set(['0-0', '0-1', '1-0', '1-1']), null);
|
||||
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
endDragSelection: useEndPageLayoutDragSelection(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
selectedCells: useRecoilComponentValue(
|
||||
selectedCells: useRecoilComponentValueV2(
|
||||
pageLayoutSelectedCellsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
draggedArea: useRecoilComponentValue(
|
||||
draggedArea: useRecoilComponentValueV2(
|
||||
pageLayoutDraggedAreaComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
}),
|
||||
{
|
||||
wrapper: ({ children }: { children: ReactNode }) => (
|
||||
<PageLayoutTestWrapper
|
||||
initializeState={createInitializeState(
|
||||
new Set(['0-0', '0-1', '1-0', '1-1']),
|
||||
null,
|
||||
)}
|
||||
>
|
||||
<PageLayoutTestWrapper store={store}>
|
||||
{children}
|
||||
</PageLayoutTestWrapper>
|
||||
),
|
||||
@@ -109,25 +110,25 @@ describe('useEndPageLayoutDragSelection', () => {
|
||||
it('should not navigate when no cells are selected', () => {
|
||||
(calculateGridBoundsFromSelectedCells as jest.Mock).mockReturnValue(null);
|
||||
|
||||
const store = createTestStore(new Set(), null);
|
||||
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
endDragSelection: useEndPageLayoutDragSelection(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
selectedCells: useRecoilComponentValue(
|
||||
selectedCells: useRecoilComponentValueV2(
|
||||
pageLayoutSelectedCellsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
draggedArea: useRecoilComponentValue(
|
||||
draggedArea: useRecoilComponentValueV2(
|
||||
pageLayoutDraggedAreaComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
}),
|
||||
{
|
||||
wrapper: ({ children }: { children: ReactNode }) => (
|
||||
<PageLayoutTestWrapper
|
||||
initializeState={createInitializeState(new Set(), null)}
|
||||
>
|
||||
<PageLayoutTestWrapper store={store}>
|
||||
{children}
|
||||
</PageLayoutTestWrapper>
|
||||
),
|
||||
@@ -147,28 +148,25 @@ describe('useEndPageLayoutDragSelection', () => {
|
||||
it('should not navigate when bounds calculation returns null', () => {
|
||||
(calculateGridBoundsFromSelectedCells as jest.Mock).mockReturnValue(null);
|
||||
|
||||
const store = createTestStore(new Set(['invalid-cell']), null);
|
||||
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
endDragSelection: useEndPageLayoutDragSelection(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
selectedCells: useRecoilComponentValue(
|
||||
selectedCells: useRecoilComponentValueV2(
|
||||
pageLayoutSelectedCellsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
draggedArea: useRecoilComponentValue(
|
||||
draggedArea: useRecoilComponentValueV2(
|
||||
pageLayoutDraggedAreaComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
}),
|
||||
{
|
||||
wrapper: ({ children }: { children: ReactNode }) => (
|
||||
<PageLayoutTestWrapper
|
||||
initializeState={createInitializeState(
|
||||
new Set(['invalid-cell']),
|
||||
null,
|
||||
)}
|
||||
>
|
||||
<PageLayoutTestWrapper store={store}>
|
||||
{children}
|
||||
</PageLayoutTestWrapper>
|
||||
),
|
||||
@@ -206,21 +204,21 @@ describe('useEndPageLayoutDragSelection', () => {
|
||||
mockBounds,
|
||||
);
|
||||
|
||||
const store = createTestStore(new Set(['0-0']));
|
||||
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
endDragSelection: useEndPageLayoutDragSelection(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
selectedCells: useRecoilComponentValue(
|
||||
selectedCells: useRecoilComponentValueV2(
|
||||
pageLayoutSelectedCellsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
}),
|
||||
{
|
||||
wrapper: ({ children }: { children: ReactNode }) => (
|
||||
<PageLayoutTestWrapper
|
||||
initializeState={createInitializeState(new Set(['0-0']))}
|
||||
>
|
||||
<PageLayoutTestWrapper store={store}>
|
||||
{children}
|
||||
</PageLayoutTestWrapper>
|
||||
),
|
||||
@@ -240,21 +238,21 @@ describe('useEndPageLayoutDragSelection', () => {
|
||||
mockBounds,
|
||||
);
|
||||
|
||||
const store = createTestStore(new Set(['0-0']));
|
||||
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
endDragSelection: useEndPageLayoutDragSelection(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
selectedCells: useRecoilComponentValue(
|
||||
selectedCells: useRecoilComponentValueV2(
|
||||
pageLayoutSelectedCellsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
}),
|
||||
{
|
||||
wrapper: ({ children }: { children: ReactNode }) => (
|
||||
<PageLayoutTestWrapper
|
||||
initializeState={createInitializeState(new Set(['0-0']))}
|
||||
>
|
||||
<PageLayoutTestWrapper store={store}>
|
||||
{children}
|
||||
</PageLayoutTestWrapper>
|
||||
),
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
|
||||
@@ -14,7 +14,7 @@ describe('usePageLayoutHandleLayoutChange', () => {
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
handler: usePageLayoutHandleLayoutChange(PAGE_LAYOUT_TEST_INSTANCE_ID),
|
||||
layouts: useRecoilComponentValue(
|
||||
layouts: useRecoilComponentValueV2(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
@@ -57,7 +57,7 @@ describe('usePageLayoutHandleLayoutChange', () => {
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
handler: usePageLayoutHandleLayoutChange(PAGE_LAYOUT_TEST_INSTANCE_ID),
|
||||
layouts: useRecoilComponentValue(
|
||||
layouts: useRecoilComponentValueV2(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
@@ -92,7 +92,7 @@ describe('usePageLayoutHandleLayoutChange', () => {
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
handler: usePageLayoutHandleLayoutChange(PAGE_LAYOUT_TEST_INSTANCE_ID),
|
||||
layouts: useRecoilComponentValue(
|
||||
layouts: useRecoilComponentValueV2(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
|
||||
+22
-23
@@ -1,5 +1,6 @@
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { createStore } from 'jotai';
|
||||
import { type ReactNode } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { pageLayoutSelectedCellsComponentState } from '@/page-layout/states/pageLayoutSelectedCellsComponentState';
|
||||
@@ -9,38 +10,36 @@ import {
|
||||
PageLayoutTestWrapper,
|
||||
} from './PageLayoutTestWrapper';
|
||||
|
||||
const createInitializeState =
|
||||
(initialSelectedCells?: Set<string>) =>
|
||||
({ set }: { set: any }) => {
|
||||
if (isDefined(initialSelectedCells)) {
|
||||
set(
|
||||
pageLayoutSelectedCellsComponentState.atomFamily({
|
||||
instanceId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
}),
|
||||
initialSelectedCells,
|
||||
);
|
||||
}
|
||||
};
|
||||
const createTestStore = (initialSelectedCells?: Set<string>) => {
|
||||
const store = createStore();
|
||||
if (isDefined(initialSelectedCells)) {
|
||||
store.set(
|
||||
pageLayoutSelectedCellsComponentState.atomFamily({
|
||||
instanceId: PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
}),
|
||||
initialSelectedCells,
|
||||
);
|
||||
}
|
||||
return store;
|
||||
};
|
||||
|
||||
describe('useStartPageLayoutDragSelection', () => {
|
||||
it('should clear selected cells when starting drag selection', () => {
|
||||
const store = createTestStore(new Set(['cell-1', 'cell-2']));
|
||||
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
startDragSelection: useStartPageLayoutDragSelection(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
selectedCells: useRecoilComponentValue(
|
||||
selectedCells: useRecoilComponentValueV2(
|
||||
pageLayoutSelectedCellsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
}),
|
||||
{
|
||||
wrapper: ({ children }: { children: ReactNode }) => (
|
||||
<PageLayoutTestWrapper
|
||||
initializeState={createInitializeState(
|
||||
new Set(['cell-1', 'cell-2']),
|
||||
)}
|
||||
>
|
||||
<PageLayoutTestWrapper store={store}>
|
||||
{children}
|
||||
</PageLayoutTestWrapper>
|
||||
),
|
||||
@@ -72,21 +71,21 @@ describe('useStartPageLayoutDragSelection', () => {
|
||||
});
|
||||
|
||||
it('should handle multiple calls correctly', () => {
|
||||
const store = createTestStore(new Set(['cell-1']));
|
||||
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
startDragSelection: useStartPageLayoutDragSelection(
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
selectedCells: useRecoilComponentValue(
|
||||
selectedCells: useRecoilComponentValueV2(
|
||||
pageLayoutSelectedCellsComponentState,
|
||||
PAGE_LAYOUT_TEST_INSTANCE_ID,
|
||||
),
|
||||
}),
|
||||
{
|
||||
wrapper: ({ children }: { children: ReactNode }) => (
|
||||
<PageLayoutTestWrapper
|
||||
initializeState={createInitializeState(new Set(['cell-1']))}
|
||||
>
|
||||
<PageLayoutTestWrapper store={store}>
|
||||
{children}
|
||||
</PageLayoutTestWrapper>
|
||||
),
|
||||
|
||||
@@ -20,8 +20,8 @@ import { DEFAULT_WORKFLOW_VERSION_PAGE_LAYOUT_ID } from '@/page-layout/constants
|
||||
import { recordPageLayoutFromIdFamilySelector } from '@/page-layout/states/selectors/recordPageLayoutFromIdFamilySelector';
|
||||
import { type PageLayout } from '@/page-layout/types/PageLayout';
|
||||
import { transformPageLayout } from '@/page-layout/utils/transformPageLayout';
|
||||
import { useFamilySelectorValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilySelectorValueV2';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const getDefaultLayoutById = (layoutId: string): PageLayout => {
|
||||
@@ -64,8 +64,9 @@ export const useBasePageLayout = (
|
||||
): PageLayout | undefined => {
|
||||
const isDefaultLayout = isDefaultLayoutId(pageLayoutId);
|
||||
|
||||
const cachedRecordPageLayout = useRecoilValue(
|
||||
recordPageLayoutFromIdFamilySelector({ pageLayoutId }),
|
||||
const cachedRecordPageLayout = useFamilySelectorValueV2(
|
||||
recordPageLayoutFromIdFamilySelector,
|
||||
{ pageLayoutId },
|
||||
);
|
||||
|
||||
const shouldSkipQuery = isDefaultLayout || isDefined(cachedRecordPageLayout);
|
||||
|
||||
+19
-17
@@ -1,7 +1,8 @@
|
||||
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { pageLayoutSelectedCellsComponentState } from '@/page-layout/states/pageLayoutSelectedCellsComponentState';
|
||||
|
||||
export const useChangePageLayoutDragSelection = (
|
||||
@@ -12,25 +13,26 @@ export const useChangePageLayoutDragSelection = (
|
||||
pageLayoutIdFromProps,
|
||||
);
|
||||
|
||||
const pageLayoutSelectedCellsState = useRecoilComponentCallbackState(
|
||||
const pageLayoutSelectedCellsState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutSelectedCellsComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const changePageLayoutDragSelection = useRecoilCallback(
|
||||
({ set }) =>
|
||||
(cellId: string, selected: boolean) => {
|
||||
set(pageLayoutSelectedCellsState, (prev) => {
|
||||
const newSet = new Set(prev);
|
||||
if (selected) {
|
||||
newSet.add(cellId);
|
||||
} else {
|
||||
newSet.delete(cellId);
|
||||
}
|
||||
return newSet;
|
||||
});
|
||||
},
|
||||
[pageLayoutSelectedCellsState],
|
||||
const store = useStore();
|
||||
|
||||
const changePageLayoutDragSelection = useCallback(
|
||||
(cellId: string, selected: boolean) => {
|
||||
store.set(pageLayoutSelectedCellsState, (prev) => {
|
||||
const newSet = new Set(prev);
|
||||
if (selected) {
|
||||
newSet.add(cellId);
|
||||
} else {
|
||||
newSet.delete(cellId);
|
||||
}
|
||||
return newSet;
|
||||
});
|
||||
},
|
||||
[pageLayoutSelectedCellsState, store],
|
||||
);
|
||||
|
||||
return { changePageLayoutDragSelection };
|
||||
|
||||
+60
-61
@@ -11,9 +11,10 @@ import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTab
|
||||
import { getUpdatedTabLayouts } from '@/page-layout/utils/getUpdatedTabLayouts';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { WidgetType } from '~/generated-metadata/graphql';
|
||||
@@ -31,93 +32,91 @@ export const useCreatePageLayoutFrontComponentWidget = (
|
||||
getTabListInstanceIdFromPageLayoutId(pageLayoutId),
|
||||
);
|
||||
|
||||
const pageLayoutCurrentLayoutsState = useRecoilComponentCallbackState(
|
||||
const pageLayoutCurrentLayoutsState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const pageLayoutDraggedAreaState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraggedAreaState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraggedAreaComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const pageLayoutDraftState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraftState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const createPageLayoutFrontComponentWidget = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
(title: string, frontComponentId: string): PageLayoutWidget => {
|
||||
const allTabLayouts = snapshot
|
||||
.getLoadable(pageLayoutCurrentLayoutsState)
|
||||
.getValue();
|
||||
const store = useStore();
|
||||
|
||||
const pageLayoutDraggedArea = snapshot
|
||||
.getLoadable(pageLayoutDraggedAreaState)
|
||||
.getValue();
|
||||
const createPageLayoutFrontComponentWidget = useCallback(
|
||||
(title: string, frontComponentId: string): PageLayoutWidget => {
|
||||
const allTabLayouts = store.get(pageLayoutCurrentLayoutsState);
|
||||
|
||||
if (!isDefined(activeTabId)) {
|
||||
throw new Error(
|
||||
'A tab must be selected to create a new front component widget',
|
||||
);
|
||||
}
|
||||
const pageLayoutDraggedArea = store.get(pageLayoutDraggedAreaState);
|
||||
|
||||
const widgetId = uuidv4();
|
||||
const frontComponentSize = WIDGET_SIZES[WidgetType.FRONT_COMPONENT]!;
|
||||
const defaultSize = frontComponentSize.default;
|
||||
const minimumSize = frontComponentSize.minimum;
|
||||
const position = getDefaultWidgetPosition(
|
||||
pageLayoutDraggedArea,
|
||||
defaultSize,
|
||||
minimumSize,
|
||||
if (!isDefined(activeTabId)) {
|
||||
throw new Error(
|
||||
'A tab must be selected to create a new front component widget',
|
||||
);
|
||||
}
|
||||
|
||||
const newWidget = createDefaultFrontComponentWidget(
|
||||
widgetId,
|
||||
activeTabId,
|
||||
title,
|
||||
frontComponentId,
|
||||
{
|
||||
row: position.y,
|
||||
column: position.x,
|
||||
rowSpan: position.h,
|
||||
columnSpan: position.w,
|
||||
},
|
||||
);
|
||||
const widgetId = uuidv4();
|
||||
const frontComponentSize = WIDGET_SIZES[WidgetType.FRONT_COMPONENT]!;
|
||||
const defaultSize = frontComponentSize.default;
|
||||
const minimumSize = frontComponentSize.minimum;
|
||||
const position = getDefaultWidgetPosition(
|
||||
pageLayoutDraggedArea,
|
||||
defaultSize,
|
||||
minimumSize,
|
||||
);
|
||||
|
||||
const newLayout = {
|
||||
i: widgetId,
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
w: position.w,
|
||||
h: position.h,
|
||||
minW: minimumSize.w,
|
||||
minH: minimumSize.h,
|
||||
};
|
||||
const newWidget = createDefaultFrontComponentWidget(
|
||||
widgetId,
|
||||
activeTabId,
|
||||
title,
|
||||
frontComponentId,
|
||||
{
|
||||
row: position.y,
|
||||
column: position.x,
|
||||
rowSpan: position.h,
|
||||
columnSpan: position.w,
|
||||
},
|
||||
);
|
||||
|
||||
const updatedLayouts = getUpdatedTabLayouts(
|
||||
allTabLayouts,
|
||||
activeTabId,
|
||||
newLayout,
|
||||
);
|
||||
const newLayout = {
|
||||
i: widgetId,
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
w: position.w,
|
||||
h: position.h,
|
||||
minW: minimumSize.w,
|
||||
minH: minimumSize.h,
|
||||
};
|
||||
|
||||
set(pageLayoutCurrentLayoutsState, updatedLayouts);
|
||||
const updatedLayouts = getUpdatedTabLayouts(
|
||||
allTabLayouts,
|
||||
activeTabId,
|
||||
newLayout,
|
||||
);
|
||||
|
||||
set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: addWidgetToTab(prev.tabs, activeTabId, newWidget),
|
||||
}));
|
||||
store.set(pageLayoutCurrentLayoutsState, updatedLayouts);
|
||||
|
||||
set(pageLayoutDraggedAreaState, null);
|
||||
store.set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: addWidgetToTab(prev.tabs, activeTabId, newWidget),
|
||||
}));
|
||||
|
||||
return newWidget;
|
||||
},
|
||||
store.set(pageLayoutDraggedAreaState, null);
|
||||
|
||||
return newWidget;
|
||||
},
|
||||
[
|
||||
activeTabId,
|
||||
pageLayoutCurrentLayoutsState,
|
||||
pageLayoutDraftState,
|
||||
pageLayoutDraggedAreaState,
|
||||
store,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
+97
-106
@@ -15,9 +15,9 @@ import { getWidgetSize } from '@/page-layout/utils/getWidgetSize';
|
||||
import { getWidgetTitle } from '@/page-layout/utils/getWidgetTitle';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useStore } from 'jotai';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import {
|
||||
@@ -39,133 +39,124 @@ export const useCreatePageLayoutGraphWidget = (
|
||||
|
||||
const tabListInstanceId = getTabListInstanceIdFromPageLayoutId(pageLayoutId);
|
||||
|
||||
const pageLayoutDraftState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraftState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const pageLayoutCurrentLayoutsState = useRecoilComponentCallbackState(
|
||||
const pageLayoutCurrentLayoutsState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const pageLayoutDraggedAreaState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraggedAreaState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraggedAreaComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const createPageLayoutGraphWidget = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
({
|
||||
fieldSelection,
|
||||
}: {
|
||||
fieldSelection?: GraphWidgetFieldSelection;
|
||||
}): PageLayoutWidget => {
|
||||
const activeTabId = store.get(
|
||||
activeTabIdComponentState.atomFamily({
|
||||
instanceId: tabListInstanceId,
|
||||
}),
|
||||
);
|
||||
const createPageLayoutGraphWidget = useCallback(
|
||||
({
|
||||
fieldSelection,
|
||||
}: {
|
||||
fieldSelection?: GraphWidgetFieldSelection;
|
||||
}): PageLayoutWidget => {
|
||||
const activeTabId = store.get(
|
||||
activeTabIdComponentState.atomFamily({
|
||||
instanceId: tabListInstanceId,
|
||||
}),
|
||||
);
|
||||
|
||||
if (!isDefined(activeTabId)) {
|
||||
throw new Error(
|
||||
'A tab must be selected to create a new graph widget',
|
||||
);
|
||||
if (!isDefined(activeTabId)) {
|
||||
throw new Error('A tab must be selected to create a new graph widget');
|
||||
}
|
||||
|
||||
const pageLayoutDraft = store.get(pageLayoutDraftState);
|
||||
|
||||
const allTabLayouts = store.get(pageLayoutCurrentLayoutsState);
|
||||
|
||||
const pageLayoutDraggedArea = store.get(pageLayoutDraggedAreaState);
|
||||
|
||||
const allWidgets = pageLayoutDraft.tabs.flatMap((tab) => tab.widgets);
|
||||
const existingWidgetCount = allWidgets.filter((widget) => {
|
||||
if (widget.type !== WidgetType.GRAPH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const pageLayoutDraft = snapshot
|
||||
.getLoadable(pageLayoutDraftState)
|
||||
.getValue();
|
||||
if (
|
||||
!isWidgetConfigurationOfType(
|
||||
widget.configuration,
|
||||
'BarChartConfiguration',
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return widget.configuration.layout === BarChartLayout.VERTICAL;
|
||||
}).length;
|
||||
|
||||
const allTabLayouts = snapshot
|
||||
.getLoadable(pageLayoutCurrentLayoutsState)
|
||||
.getValue();
|
||||
const title = getWidgetTitle(
|
||||
{
|
||||
configurationType: WidgetConfigurationType.BAR_CHART,
|
||||
layout: BarChartLayout.VERTICAL,
|
||||
},
|
||||
existingWidgetCount,
|
||||
);
|
||||
const widgetId = uuidv4();
|
||||
|
||||
const pageLayoutDraggedArea = snapshot
|
||||
.getLoadable(pageLayoutDraggedAreaState)
|
||||
.getValue();
|
||||
const defaultSize = getWidgetSize(
|
||||
WidgetConfigurationType.BAR_CHART,
|
||||
'default',
|
||||
);
|
||||
const minimumSize = getWidgetSize(
|
||||
WidgetConfigurationType.BAR_CHART,
|
||||
'minimum',
|
||||
);
|
||||
const position = getDefaultWidgetPosition(
|
||||
pageLayoutDraggedArea,
|
||||
defaultSize,
|
||||
minimumSize,
|
||||
);
|
||||
|
||||
const allWidgets = pageLayoutDraft.tabs.flatMap((tab) => tab.widgets);
|
||||
const existingWidgetCount = allWidgets.filter((widget) => {
|
||||
if (widget.type !== WidgetType.GRAPH) {
|
||||
return false;
|
||||
}
|
||||
const newWidget = createDefaultGraphWidget({
|
||||
id: widgetId,
|
||||
pageLayoutTabId: activeTabId,
|
||||
title,
|
||||
gridPosition: {
|
||||
row: position.y,
|
||||
column: position.x,
|
||||
rowSpan: position.h,
|
||||
columnSpan: position.w,
|
||||
},
|
||||
fieldSelection,
|
||||
timezone: timeZone,
|
||||
firstDayOfTheWeek: calendarStartDay,
|
||||
});
|
||||
|
||||
if (
|
||||
!isWidgetConfigurationOfType(
|
||||
widget.configuration,
|
||||
'BarChartConfiguration',
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return widget.configuration.layout === BarChartLayout.VERTICAL;
|
||||
}).length;
|
||||
const newLayout = {
|
||||
i: widgetId,
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
w: position.w,
|
||||
h: position.h,
|
||||
minW: minimumSize.w,
|
||||
minH: minimumSize.h,
|
||||
};
|
||||
|
||||
const title = getWidgetTitle(
|
||||
{
|
||||
configurationType: WidgetConfigurationType.BAR_CHART,
|
||||
layout: BarChartLayout.VERTICAL,
|
||||
},
|
||||
existingWidgetCount,
|
||||
);
|
||||
const widgetId = uuidv4();
|
||||
const updatedLayouts = getUpdatedTabLayouts(
|
||||
allTabLayouts,
|
||||
activeTabId,
|
||||
newLayout,
|
||||
);
|
||||
store.set(pageLayoutCurrentLayoutsState, updatedLayouts);
|
||||
|
||||
const defaultSize = getWidgetSize(
|
||||
WidgetConfigurationType.BAR_CHART,
|
||||
'default',
|
||||
);
|
||||
const minimumSize = getWidgetSize(
|
||||
WidgetConfigurationType.BAR_CHART,
|
||||
'minimum',
|
||||
);
|
||||
const position = getDefaultWidgetPosition(
|
||||
pageLayoutDraggedArea,
|
||||
defaultSize,
|
||||
minimumSize,
|
||||
);
|
||||
store.set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: addWidgetToTab(prev.tabs, activeTabId, newWidget),
|
||||
}));
|
||||
|
||||
const newWidget = createDefaultGraphWidget({
|
||||
id: widgetId,
|
||||
pageLayoutTabId: activeTabId,
|
||||
title,
|
||||
gridPosition: {
|
||||
row: position.y,
|
||||
column: position.x,
|
||||
rowSpan: position.h,
|
||||
columnSpan: position.w,
|
||||
},
|
||||
fieldSelection,
|
||||
timezone: timeZone,
|
||||
firstDayOfTheWeek: calendarStartDay,
|
||||
});
|
||||
store.set(pageLayoutDraggedAreaState, null);
|
||||
|
||||
const newLayout = {
|
||||
i: widgetId,
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
w: position.w,
|
||||
h: position.h,
|
||||
minW: minimumSize.w,
|
||||
minH: minimumSize.h,
|
||||
};
|
||||
|
||||
const updatedLayouts = getUpdatedTabLayouts(
|
||||
allTabLayouts,
|
||||
activeTabId,
|
||||
newLayout,
|
||||
);
|
||||
set(pageLayoutCurrentLayoutsState, updatedLayouts);
|
||||
|
||||
set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: addWidgetToTab(prev.tabs, activeTabId, newWidget),
|
||||
}));
|
||||
|
||||
set(pageLayoutDraggedAreaState, null);
|
||||
|
||||
return newWidget;
|
||||
},
|
||||
return newWidget;
|
||||
},
|
||||
[
|
||||
tabListInstanceId,
|
||||
pageLayoutCurrentLayoutsState,
|
||||
|
||||
+59
-62
@@ -11,9 +11,10 @@ import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTab
|
||||
import { getUpdatedTabLayouts } from '@/page-layout/utils/getUpdatedTabLayouts';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { WidgetType } from '~/generated-metadata/graphql';
|
||||
@@ -31,93 +32,89 @@ export const useCreatePageLayoutIframeWidget = (
|
||||
getTabListInstanceIdFromPageLayoutId(pageLayoutId),
|
||||
);
|
||||
|
||||
const pageLayoutCurrentLayoutsState = useRecoilComponentCallbackState(
|
||||
const pageLayoutCurrentLayoutsState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const pageLayoutDraggedAreaState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraggedAreaState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraggedAreaComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const pageLayoutDraftState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraftState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const createPageLayoutIframeWidget = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
(title: string, url: string | null): PageLayoutWidget => {
|
||||
const allTabLayouts = snapshot
|
||||
.getLoadable(pageLayoutCurrentLayoutsState)
|
||||
.getValue();
|
||||
const store = useStore();
|
||||
|
||||
const pageLayoutDraggedArea = snapshot
|
||||
.getLoadable(pageLayoutDraggedAreaState)
|
||||
.getValue();
|
||||
const createPageLayoutIframeWidget = useCallback(
|
||||
(title: string, url: string | null): PageLayoutWidget => {
|
||||
const allTabLayouts = store.get(pageLayoutCurrentLayoutsState);
|
||||
|
||||
if (!isDefined(activeTabId)) {
|
||||
throw new Error(
|
||||
'A tab must be selected to create a new iframe widget',
|
||||
);
|
||||
}
|
||||
const pageLayoutDraggedArea = store.get(pageLayoutDraggedAreaState);
|
||||
|
||||
const widgetId = uuidv4();
|
||||
const iframeSize = WIDGET_SIZES[WidgetType.IFRAME]!;
|
||||
const defaultIframeSize = iframeSize.default;
|
||||
const minimumSize = iframeSize.minimum;
|
||||
const position = getDefaultWidgetPosition(
|
||||
pageLayoutDraggedArea,
|
||||
defaultIframeSize,
|
||||
minimumSize,
|
||||
);
|
||||
if (!isDefined(activeTabId)) {
|
||||
throw new Error('A tab must be selected to create a new iframe widget');
|
||||
}
|
||||
|
||||
const newWidget = createDefaultIframeWidget(
|
||||
widgetId,
|
||||
activeTabId,
|
||||
title,
|
||||
url,
|
||||
{
|
||||
row: position.y,
|
||||
column: position.x,
|
||||
rowSpan: position.h,
|
||||
columnSpan: position.w,
|
||||
},
|
||||
);
|
||||
const widgetId = uuidv4();
|
||||
const iframeSize = WIDGET_SIZES[WidgetType.IFRAME]!;
|
||||
const defaultIframeSize = iframeSize.default;
|
||||
const minimumSize = iframeSize.minimum;
|
||||
const position = getDefaultWidgetPosition(
|
||||
pageLayoutDraggedArea,
|
||||
defaultIframeSize,
|
||||
minimumSize,
|
||||
);
|
||||
|
||||
const newLayout = {
|
||||
i: widgetId,
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
w: position.w,
|
||||
h: position.h,
|
||||
minW: minimumSize.w,
|
||||
minH: minimumSize.h,
|
||||
};
|
||||
const newWidget = createDefaultIframeWidget(
|
||||
widgetId,
|
||||
activeTabId,
|
||||
title,
|
||||
url,
|
||||
{
|
||||
row: position.y,
|
||||
column: position.x,
|
||||
rowSpan: position.h,
|
||||
columnSpan: position.w,
|
||||
},
|
||||
);
|
||||
|
||||
const updatedLayouts = getUpdatedTabLayouts(
|
||||
allTabLayouts,
|
||||
activeTabId,
|
||||
newLayout,
|
||||
);
|
||||
const newLayout = {
|
||||
i: widgetId,
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
w: position.w,
|
||||
h: position.h,
|
||||
minW: minimumSize.w,
|
||||
minH: minimumSize.h,
|
||||
};
|
||||
|
||||
set(pageLayoutCurrentLayoutsState, updatedLayouts);
|
||||
const updatedLayouts = getUpdatedTabLayouts(
|
||||
allTabLayouts,
|
||||
activeTabId,
|
||||
newLayout,
|
||||
);
|
||||
|
||||
set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: addWidgetToTab(prev.tabs, activeTabId, newWidget),
|
||||
}));
|
||||
store.set(pageLayoutCurrentLayoutsState, updatedLayouts);
|
||||
|
||||
set(pageLayoutDraggedAreaState, null);
|
||||
store.set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: addWidgetToTab(prev.tabs, activeTabId, newWidget),
|
||||
}));
|
||||
|
||||
return newWidget;
|
||||
},
|
||||
store.set(pageLayoutDraggedAreaState, null);
|
||||
|
||||
return newWidget;
|
||||
},
|
||||
[
|
||||
activeTabId,
|
||||
pageLayoutCurrentLayoutsState,
|
||||
pageLayoutDraftState,
|
||||
pageLayoutDraggedAreaState,
|
||||
store,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
+61
-66
@@ -10,9 +10,9 @@ import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTab
|
||||
import { getUpdatedTabLayouts } from '@/page-layout/utils/getUpdatedTabLayouts';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useStore } from 'jotai';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import {
|
||||
@@ -32,94 +32,89 @@ export const useCreatePageLayoutStandaloneRichTextWidget = (
|
||||
const store = useStore();
|
||||
const tabListInstanceId = getTabListInstanceIdFromPageLayoutId(pageLayoutId);
|
||||
|
||||
const pageLayoutCurrentLayoutsState = useRecoilComponentCallbackState(
|
||||
const pageLayoutCurrentLayoutsState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const pageLayoutDraggedAreaState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraggedAreaState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraggedAreaComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const pageLayoutDraftState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraftState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const createPageLayoutStandaloneRichTextWidget = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
(body: RichTextV2Body): PageLayoutWidget => {
|
||||
const activeTabId = store.get(
|
||||
activeTabIdComponentState.atomFamily({
|
||||
instanceId: tabListInstanceId,
|
||||
}),
|
||||
const createPageLayoutStandaloneRichTextWidget = useCallback(
|
||||
(body: RichTextV2Body): PageLayoutWidget => {
|
||||
const activeTabId = store.get(
|
||||
activeTabIdComponentState.atomFamily({
|
||||
instanceId: tabListInstanceId,
|
||||
}),
|
||||
);
|
||||
|
||||
if (!isDefined(activeTabId)) {
|
||||
throw new Error(
|
||||
'A tab must be selected to create a new rich text widget',
|
||||
);
|
||||
}
|
||||
|
||||
if (!isDefined(activeTabId)) {
|
||||
throw new Error(
|
||||
'A tab must be selected to create a new rich text widget',
|
||||
);
|
||||
}
|
||||
const allTabLayouts = store.get(pageLayoutCurrentLayoutsState);
|
||||
|
||||
const allTabLayouts = snapshot
|
||||
.getLoadable(pageLayoutCurrentLayoutsState)
|
||||
.getValue();
|
||||
const pageLayoutDraggedArea = store.get(pageLayoutDraggedAreaState);
|
||||
|
||||
const pageLayoutDraggedArea = snapshot
|
||||
.getLoadable(pageLayoutDraggedAreaState)
|
||||
.getValue();
|
||||
const widgetId = uuidv4();
|
||||
const richTextSize = WIDGET_SIZES[WidgetType.STANDALONE_RICH_TEXT]!;
|
||||
const defaultRichTextSize = richTextSize.default;
|
||||
const minimumSize = richTextSize.minimum;
|
||||
const position = getDefaultWidgetPosition(
|
||||
pageLayoutDraggedArea,
|
||||
defaultRichTextSize,
|
||||
minimumSize,
|
||||
);
|
||||
|
||||
const widgetId = uuidv4();
|
||||
const richTextSize = WIDGET_SIZES[WidgetType.STANDALONE_RICH_TEXT]!;
|
||||
const defaultRichTextSize = richTextSize.default;
|
||||
const minimumSize = richTextSize.minimum;
|
||||
const position = getDefaultWidgetPosition(
|
||||
pageLayoutDraggedArea,
|
||||
defaultRichTextSize,
|
||||
minimumSize,
|
||||
);
|
||||
const newWidget = createDefaultStandaloneRichTextWidget(
|
||||
widgetId,
|
||||
activeTabId,
|
||||
|
||||
const newWidget = createDefaultStandaloneRichTextWidget(
|
||||
widgetId,
|
||||
activeTabId,
|
||||
body,
|
||||
{
|
||||
row: position.y,
|
||||
column: position.x,
|
||||
rowSpan: position.h,
|
||||
columnSpan: position.w,
|
||||
},
|
||||
);
|
||||
|
||||
body,
|
||||
{
|
||||
row: position.y,
|
||||
column: position.x,
|
||||
rowSpan: position.h,
|
||||
columnSpan: position.w,
|
||||
},
|
||||
);
|
||||
const newLayout = {
|
||||
i: widgetId,
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
w: position.w,
|
||||
h: position.h,
|
||||
minW: minimumSize.w,
|
||||
minH: minimumSize.h,
|
||||
};
|
||||
|
||||
const newLayout = {
|
||||
i: widgetId,
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
w: position.w,
|
||||
h: position.h,
|
||||
minW: minimumSize.w,
|
||||
minH: minimumSize.h,
|
||||
};
|
||||
const updatedLayouts = getUpdatedTabLayouts(
|
||||
allTabLayouts,
|
||||
activeTabId,
|
||||
newLayout,
|
||||
);
|
||||
|
||||
const updatedLayouts = getUpdatedTabLayouts(
|
||||
allTabLayouts,
|
||||
activeTabId,
|
||||
newLayout,
|
||||
);
|
||||
store.set(pageLayoutCurrentLayoutsState, updatedLayouts);
|
||||
|
||||
set(pageLayoutCurrentLayoutsState, updatedLayouts);
|
||||
store.set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: addWidgetToTab(prev.tabs, activeTabId, newWidget),
|
||||
}));
|
||||
|
||||
set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: addWidgetToTab(prev.tabs, activeTabId, newWidget),
|
||||
}));
|
||||
store.set(pageLayoutDraggedAreaState, null);
|
||||
|
||||
set(pageLayoutDraggedAreaState, null);
|
||||
|
||||
return newWidget;
|
||||
},
|
||||
return newWidget;
|
||||
},
|
||||
[
|
||||
tabListInstanceId,
|
||||
pageLayoutCurrentLayoutsState,
|
||||
|
||||
@@ -6,9 +6,10 @@ import { getEmptyTabLayout } from '@/page-layout/utils/getEmptyTabLayout';
|
||||
import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutId';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export const useCreatePageLayoutTab = (pageLayoutIdFromProps?: string) => {
|
||||
@@ -17,12 +18,12 @@ export const useCreatePageLayoutTab = (pageLayoutIdFromProps?: string) => {
|
||||
pageLayoutIdFromProps,
|
||||
);
|
||||
|
||||
const pageLayoutDraftState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraftState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const pageLayoutCurrentLayoutsState = useRecoilComponentCallbackState(
|
||||
const pageLayoutCurrentLayoutsState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
@@ -33,51 +34,51 @@ export const useCreatePageLayoutTab = (pageLayoutIdFromProps?: string) => {
|
||||
tabListInstanceId,
|
||||
);
|
||||
|
||||
const createPageLayoutTab = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
(title?: string): string => {
|
||||
const pageLayoutDraft = snapshot
|
||||
.getLoadable(pageLayoutDraftState)
|
||||
.getValue();
|
||||
const store = useStore();
|
||||
|
||||
const newTabId = uuidv4();
|
||||
const tabsLength = pageLayoutDraft.tabs.length;
|
||||
const maxPosition =
|
||||
tabsLength > 0
|
||||
? Math.max(...pageLayoutDraft.tabs.map((t) => t.position))
|
||||
: -1;
|
||||
const newTab: PageLayoutTab = {
|
||||
id: newTabId,
|
||||
applicationId: '',
|
||||
title: title || `Tab ${tabsLength + 1}`,
|
||||
position: maxPosition + 1,
|
||||
pageLayoutId: pageLayoutId,
|
||||
widgets: [],
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
deletedAt: null,
|
||||
};
|
||||
const createPageLayoutTab = useCallback(
|
||||
(title?: string): string => {
|
||||
const pageLayoutDraft = store.get(pageLayoutDraftState);
|
||||
|
||||
const updatedTabs = [...(pageLayoutDraft.tabs || []), newTab];
|
||||
const newTabId = uuidv4();
|
||||
const tabsLength = pageLayoutDraft.tabs.length;
|
||||
const maxPosition =
|
||||
tabsLength > 0
|
||||
? Math.max(...pageLayoutDraft.tabs.map((t) => t.position))
|
||||
: -1;
|
||||
const newTab: PageLayoutTab = {
|
||||
id: newTabId,
|
||||
applicationId: '',
|
||||
title: title || `Tab ${tabsLength + 1}`,
|
||||
position: maxPosition + 1,
|
||||
pageLayoutId: pageLayoutId,
|
||||
widgets: [],
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
deletedAt: null,
|
||||
};
|
||||
|
||||
set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: updatedTabs,
|
||||
}));
|
||||
const updatedTabs = [...(pageLayoutDraft.tabs || []), newTab];
|
||||
|
||||
set(pageLayoutCurrentLayoutsState, (prev) =>
|
||||
getEmptyTabLayout(prev, newTabId),
|
||||
);
|
||||
store.set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: updatedTabs,
|
||||
}));
|
||||
|
||||
setActiveTabId(newTabId);
|
||||
store.set(pageLayoutCurrentLayoutsState, (prev) =>
|
||||
getEmptyTabLayout(prev, newTabId),
|
||||
);
|
||||
|
||||
return newTabId;
|
||||
},
|
||||
setActiveTabId(newTabId);
|
||||
|
||||
return newTabId;
|
||||
},
|
||||
[
|
||||
pageLayoutCurrentLayoutsState,
|
||||
pageLayoutDraftState,
|
||||
pageLayoutId,
|
||||
setActiveTabId,
|
||||
store,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -2,39 +2,42 @@ import { useNavigatePageLayoutCommandMenu } from '@/command-menu/pages/page-layo
|
||||
import { pageLayoutDraggedAreaComponentState } from '@/page-layout/states/pageLayoutDraggedAreaComponentState';
|
||||
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
|
||||
import { parseCellIdToCoordinates } from '@/page-layout/utils/parseCellIdToCoordinates';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { CommandMenuPages } from 'twenty-shared/types';
|
||||
|
||||
export const useCreateWidgetFromClick = () => {
|
||||
const pageLayoutDraggedAreaState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraggedAreaState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraggedAreaComponentState,
|
||||
);
|
||||
|
||||
const pageLayoutEditingWidgetIdState = useRecoilComponentCallbackState(
|
||||
const pageLayoutEditingWidgetIdState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutEditingWidgetIdComponentState,
|
||||
);
|
||||
|
||||
const { navigatePageLayoutCommandMenu } = useNavigatePageLayoutCommandMenu();
|
||||
|
||||
const createWidgetFromClick = useRecoilCallback(
|
||||
({ set }) =>
|
||||
(cellId: string) => {
|
||||
const { col, row } = parseCellIdToCoordinates(cellId);
|
||||
const bounds = { x: col, y: row, w: 1, h: 1 };
|
||||
const store = useStore();
|
||||
|
||||
set(pageLayoutDraggedAreaState, bounds);
|
||||
set(pageLayoutEditingWidgetIdState, null);
|
||||
const createWidgetFromClick = useCallback(
|
||||
(cellId: string) => {
|
||||
const { col, row } = parseCellIdToCoordinates(cellId);
|
||||
const bounds = { x: col, y: row, w: 1, h: 1 };
|
||||
|
||||
navigatePageLayoutCommandMenu({
|
||||
commandMenuPage: CommandMenuPages.PageLayoutWidgetTypeSelect,
|
||||
resetNavigationStack: true,
|
||||
});
|
||||
},
|
||||
store.set(pageLayoutDraggedAreaState, bounds);
|
||||
store.set(pageLayoutEditingWidgetIdState, null);
|
||||
|
||||
navigatePageLayoutCommandMenu({
|
||||
commandMenuPage: CommandMenuPages.PageLayoutWidgetTypeSelect,
|
||||
resetNavigationStack: true,
|
||||
});
|
||||
},
|
||||
[
|
||||
navigatePageLayoutCommandMenu,
|
||||
pageLayoutDraggedAreaState,
|
||||
pageLayoutEditingWidgetIdState,
|
||||
store,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { isPageLayoutInEditModeComponentState } from '@/page-layout/states/isPageLayoutInEditModeComponentState';
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
import { pageLayoutPersistedComponentState } from '@/page-layout/states/pageLayoutPersistedComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
|
||||
export const useCurrentPageLayout = () => {
|
||||
const pageLayoutPersisted = useRecoilComponentValue(
|
||||
const pageLayoutPersisted = useRecoilComponentValueV2(
|
||||
pageLayoutPersistedComponentState,
|
||||
);
|
||||
|
||||
const pageLayoutDraft = useRecoilComponentValue(
|
||||
const pageLayoutDraft = useRecoilComponentValueV2(
|
||||
pageLayoutDraftComponentState,
|
||||
);
|
||||
|
||||
const isPageLayoutInEditMode = useRecoilComponentValue(
|
||||
const isPageLayoutInEditMode = useRecoilComponentValueV2(
|
||||
isPageLayoutInEditModeComponentState,
|
||||
);
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@ import { removeTabLayouts } from '@/page-layout/utils/removeTabLayouts';
|
||||
import { sortTabsByPosition } from '@/page-layout/utils/sortTabsByPosition';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useStore } from 'jotai';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export const useDeletePageLayoutTab = (pageLayoutIdFromProps?: string) => {
|
||||
const pageLayoutId = useAvailableComponentInstanceIdOrThrow(
|
||||
@@ -16,12 +16,12 @@ export const useDeletePageLayoutTab = (pageLayoutIdFromProps?: string) => {
|
||||
pageLayoutIdFromProps,
|
||||
);
|
||||
|
||||
const pageLayoutDraftState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraftState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const pageLayoutCurrentLayoutsState = useRecoilComponentCallbackState(
|
||||
const pageLayoutCurrentLayoutsState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
@@ -33,36 +33,33 @@ export const useDeletePageLayoutTab = (pageLayoutIdFromProps?: string) => {
|
||||
instanceId: tabListInstanceId,
|
||||
});
|
||||
|
||||
const deleteTab = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
(tabId: string) => {
|
||||
const draft = snapshot.getLoadable(pageLayoutDraftState).getValue();
|
||||
if (draft.tabs.length <= 1) {
|
||||
return;
|
||||
}
|
||||
const deleteTab = useCallback(
|
||||
(tabId: string) => {
|
||||
const draft = store.get(pageLayoutDraftState);
|
||||
if (draft.tabs.length <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sorted = sortTabsByPosition(draft.tabs);
|
||||
const index = sorted.findIndex((t) => t.id === tabId);
|
||||
const sorted = sortTabsByPosition(draft.tabs);
|
||||
const index = sorted.findIndex((t) => t.id === tabId);
|
||||
|
||||
const activeTabId = store.get(activeTabIdAtom);
|
||||
const activeTabId = store.get(activeTabIdAtom);
|
||||
|
||||
const allLayouts = snapshot
|
||||
.getLoadable(pageLayoutCurrentLayoutsState)
|
||||
.getValue();
|
||||
const updatedLayouts = removeTabLayouts(allLayouts, tabId);
|
||||
set(pageLayoutCurrentLayoutsState, updatedLayouts);
|
||||
const allLayouts = store.get(pageLayoutCurrentLayoutsState);
|
||||
const updatedLayouts = removeTabLayouts(allLayouts, tabId);
|
||||
store.set(pageLayoutCurrentLayoutsState, updatedLayouts);
|
||||
|
||||
set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: prev.tabs.filter((t) => t.id !== tabId),
|
||||
}));
|
||||
store.set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: prev.tabs.filter((t) => t.id !== tabId),
|
||||
}));
|
||||
|
||||
if (activeTabId === tabId) {
|
||||
const neighbor = index > 0 ? sorted[index - 1] : sorted[index + 1];
|
||||
const nextActiveId = neighbor?.id ?? null;
|
||||
store.set(activeTabIdAtom, nextActiveId);
|
||||
}
|
||||
},
|
||||
if (activeTabId === tabId) {
|
||||
const neighbor = index > 0 ? sorted[index - 1] : sorted[index + 1];
|
||||
const nextActiveId = neighbor?.id ?? null;
|
||||
store.set(activeTabIdAtom, nextActiveId);
|
||||
}
|
||||
},
|
||||
[
|
||||
pageLayoutCurrentLayoutsState,
|
||||
pageLayoutDraftState,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
@@ -15,50 +16,52 @@ export const useDeletePageLayoutWidget = (pageLayoutIdFromProps?: string) => {
|
||||
pageLayoutIdFromProps,
|
||||
);
|
||||
|
||||
const pageLayoutDraftState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraftState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const pageLayoutCurrentLayoutsState = useRecoilComponentCallbackState(
|
||||
const pageLayoutCurrentLayoutsState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const { closeCommandMenu } = useCommandMenu();
|
||||
|
||||
const deletePageLayoutWidget = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
(widgetId: string) => {
|
||||
closeCommandMenu();
|
||||
const store = useStore();
|
||||
|
||||
const pageLayoutDraft = snapshot
|
||||
.getLoadable(pageLayoutDraftState)
|
||||
.getValue();
|
||||
const allTabLayouts = snapshot
|
||||
.getLoadable(pageLayoutCurrentLayoutsState)
|
||||
.getValue();
|
||||
const deletePageLayoutWidget = useCallback(
|
||||
(widgetId: string) => {
|
||||
closeCommandMenu();
|
||||
|
||||
const tabWithWidget = pageLayoutDraft.tabs.find((tab) =>
|
||||
tab.widgets.some((w) => w.id === widgetId),
|
||||
const pageLayoutDraft = store.get(pageLayoutDraftState);
|
||||
const allTabLayouts = store.get(pageLayoutCurrentLayoutsState);
|
||||
|
||||
const tabWithWidget = pageLayoutDraft.tabs.find((tab) =>
|
||||
tab.widgets.some((w) => w.id === widgetId),
|
||||
);
|
||||
const tabId = tabWithWidget?.id;
|
||||
|
||||
if (isDefined(tabId)) {
|
||||
const updatedLayouts = removeWidgetLayoutFromTab(
|
||||
allTabLayouts,
|
||||
tabId,
|
||||
widgetId,
|
||||
);
|
||||
const tabId = tabWithWidget?.id;
|
||||
store.set(pageLayoutCurrentLayoutsState, updatedLayouts);
|
||||
|
||||
if (isDefined(tabId)) {
|
||||
const updatedLayouts = removeWidgetLayoutFromTab(
|
||||
allTabLayouts,
|
||||
tabId,
|
||||
widgetId,
|
||||
);
|
||||
set(pageLayoutCurrentLayoutsState, updatedLayouts);
|
||||
|
||||
set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: removeWidgetFromTab(prev.tabs, tabId, widgetId),
|
||||
}));
|
||||
}
|
||||
},
|
||||
[closeCommandMenu, pageLayoutCurrentLayoutsState, pageLayoutDraftState],
|
||||
store.set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: removeWidgetFromTab(prev.tabs, tabId, widgetId),
|
||||
}));
|
||||
}
|
||||
},
|
||||
[
|
||||
closeCommandMenu,
|
||||
pageLayoutCurrentLayoutsState,
|
||||
pageLayoutDraftState,
|
||||
store,
|
||||
],
|
||||
);
|
||||
|
||||
return { deletePageLayoutWidget };
|
||||
|
||||
@@ -12,10 +12,10 @@ import { sortTabsByPosition } from '@/page-layout/utils/sortTabsByPosition';
|
||||
import { calculateNewPosition } from '@/ui/layout/draggable-list/utils/calculateNewPosition';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { appendCopySuffix, isDefined } from 'twenty-shared/utils';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
@@ -25,23 +25,25 @@ export const useDuplicatePageLayoutTab = (pageLayoutIdFromProps?: string) => {
|
||||
pageLayoutIdFromProps,
|
||||
);
|
||||
|
||||
const pageLayoutDraftState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraftAtom = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const pageLayoutCurrentLayoutsState = useRecoilComponentCallbackState(
|
||||
const pageLayoutCurrentLayoutsAtom = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const store = useStore();
|
||||
|
||||
const tabListInstanceId = getTabListInstanceIdFromPageLayoutId(pageLayoutId);
|
||||
const setActiveTabId = useSetRecoilComponentStateV2(
|
||||
activeTabIdComponentState,
|
||||
tabListInstanceId,
|
||||
);
|
||||
|
||||
const setTabSettingsOpenTabId = useSetRecoilComponentState(
|
||||
const setTabSettingsOpenTabId = useSetRecoilComponentStateV2(
|
||||
pageLayoutTabSettingsOpenTabIdComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
@@ -50,103 +52,100 @@ export const useDuplicatePageLayoutTab = (pageLayoutIdFromProps?: string) => {
|
||||
|
||||
const { closeCommandMenu } = useCommandMenu();
|
||||
|
||||
const duplicateTab = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
(tabId: string): string => {
|
||||
const pageLayoutDraft = snapshot
|
||||
.getLoadable(pageLayoutDraftState)
|
||||
.getValue();
|
||||
const duplicateTab = useCallback(
|
||||
(tabId: string): string => {
|
||||
const pageLayoutDraft = store.get(pageLayoutDraftAtom);
|
||||
|
||||
const allTabLayouts = snapshot
|
||||
.getLoadable(pageLayoutCurrentLayoutsState)
|
||||
.getValue();
|
||||
const allTabLayouts = store.get(pageLayoutCurrentLayoutsAtom);
|
||||
|
||||
const sourceTab = pageLayoutDraft.tabs.find((t) => t.id === tabId);
|
||||
const sourceTab = pageLayoutDraft.tabs.find((t) => t.id === tabId);
|
||||
|
||||
if (!isDefined(sourceTab)) {
|
||||
throw new Error(`Tab with id ${tabId} not found`);
|
||||
}
|
||||
if (!isDefined(sourceTab)) {
|
||||
throw new Error(`Tab with id ${tabId} not found`);
|
||||
}
|
||||
|
||||
const newTabId = uuidv4();
|
||||
const widgetOldIdNewIdMap = new Map<string, string>();
|
||||
const newTabId = uuidv4();
|
||||
const widgetOldIdNewIdMap = new Map<string, string>();
|
||||
|
||||
const clonedWidgets = sourceTab.widgets.map((widget) => {
|
||||
const newWidgetId = uuidv4();
|
||||
widgetOldIdNewIdMap.set(widget.id, newWidgetId);
|
||||
const clonedWidgets = sourceTab.widgets.map((widget) => {
|
||||
const newWidgetId = uuidv4();
|
||||
widgetOldIdNewIdMap.set(widget.id, newWidgetId);
|
||||
|
||||
return {
|
||||
...widget,
|
||||
id: newWidgetId,
|
||||
pageLayoutTabId: newTabId,
|
||||
...generateDuplicatedTimestamps(),
|
||||
};
|
||||
});
|
||||
|
||||
const sortedTabs = sortTabsByPosition(pageLayoutDraft.tabs);
|
||||
const sourceIndex = sortedTabs.findIndex((t) => t.id === tabId);
|
||||
|
||||
const newTabPosition = calculateNewPosition({
|
||||
items: sortedTabs,
|
||||
destinationIndex: sourceIndex + 1,
|
||||
sourceIndex,
|
||||
});
|
||||
|
||||
const newTab: PageLayoutTab = {
|
||||
...sourceTab,
|
||||
id: newTabId,
|
||||
title: appendCopySuffix(sourceTab.title),
|
||||
position: newTabPosition,
|
||||
widgets: clonedWidgets,
|
||||
return {
|
||||
...widget,
|
||||
id: newWidgetId,
|
||||
pageLayoutTabId: newTabId,
|
||||
...generateDuplicatedTimestamps(),
|
||||
};
|
||||
});
|
||||
|
||||
const sourceLayouts = allTabLayouts[tabId] ?? {
|
||||
desktop: [],
|
||||
mobile: [],
|
||||
};
|
||||
const sortedTabs = sortTabsByPosition(pageLayoutDraft.tabs);
|
||||
const sourceIndex = sortedTabs.findIndex((t) => t.id === tabId);
|
||||
|
||||
const newLayouts = {
|
||||
desktop: sourceLayouts.desktop.map((layout) => ({
|
||||
...layout,
|
||||
i: widgetOldIdNewIdMap.get(layout.i) || layout.i,
|
||||
})),
|
||||
mobile: sourceLayouts.mobile.map((layout) => ({
|
||||
...layout,
|
||||
i: widgetOldIdNewIdMap.get(layout.i) || layout.i,
|
||||
})),
|
||||
};
|
||||
const newTabPosition = calculateNewPosition({
|
||||
items: sortedTabs,
|
||||
destinationIndex: sourceIndex + 1,
|
||||
sourceIndex,
|
||||
});
|
||||
|
||||
set(pageLayoutCurrentLayoutsState, {
|
||||
...allTabLayouts,
|
||||
[newTabId]: newLayouts,
|
||||
});
|
||||
const newTab: PageLayoutTab = {
|
||||
...sourceTab,
|
||||
id: newTabId,
|
||||
title: appendCopySuffix(sourceTab.title),
|
||||
position: newTabPosition,
|
||||
widgets: clonedWidgets,
|
||||
...generateDuplicatedTimestamps(),
|
||||
};
|
||||
|
||||
set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: [...prev.tabs, newTab],
|
||||
}));
|
||||
const sourceLayouts = allTabLayouts[tabId] ?? {
|
||||
desktop: [],
|
||||
mobile: [],
|
||||
};
|
||||
|
||||
closeCommandMenu();
|
||||
const newLayouts = {
|
||||
desktop: sourceLayouts.desktop.map((layout) => ({
|
||||
...layout,
|
||||
i: widgetOldIdNewIdMap.get(layout.i) || layout.i,
|
||||
})),
|
||||
mobile: sourceLayouts.mobile.map((layout) => ({
|
||||
...layout,
|
||||
i: widgetOldIdNewIdMap.get(layout.i) || layout.i,
|
||||
})),
|
||||
};
|
||||
|
||||
setActiveTabId(newTabId);
|
||||
store.set(pageLayoutCurrentLayoutsAtom, {
|
||||
...allTabLayouts,
|
||||
[newTabId]: newLayouts,
|
||||
});
|
||||
|
||||
setTabSettingsOpenTabId(newTabId);
|
||||
const prev = store.get(pageLayoutDraftAtom);
|
||||
store.set(pageLayoutDraftAtom, {
|
||||
...prev,
|
||||
tabs: [...prev.tabs, newTab],
|
||||
});
|
||||
|
||||
navigatePageLayoutCommandMenu({
|
||||
commandMenuPage: CommandMenuPages.PageLayoutTabSettings,
|
||||
pageTitle: newTab.title,
|
||||
focusTitleInput: true,
|
||||
});
|
||||
closeCommandMenu();
|
||||
|
||||
return newTabId;
|
||||
},
|
||||
setActiveTabId(newTabId);
|
||||
|
||||
setTabSettingsOpenTabId(newTabId);
|
||||
|
||||
navigatePageLayoutCommandMenu({
|
||||
commandMenuPage: CommandMenuPages.PageLayoutTabSettings,
|
||||
pageTitle: newTab.title,
|
||||
focusTitleInput: true,
|
||||
});
|
||||
|
||||
return newTabId;
|
||||
},
|
||||
[
|
||||
closeCommandMenu,
|
||||
navigatePageLayoutCommandMenu,
|
||||
pageLayoutCurrentLayoutsState,
|
||||
pageLayoutDraftState,
|
||||
pageLayoutCurrentLayoutsAtom,
|
||||
pageLayoutDraftAtom,
|
||||
setActiveTabId,
|
||||
setTabSettingsOpenTabId,
|
||||
store,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
+92
-93
@@ -9,9 +9,10 @@ import { getScrollWrapperInstanceIdFromPageLayoutId } from '@/page-layout/utils/
|
||||
import { getUpdatedTabLayouts } from '@/page-layout/utils/getUpdatedTabLayouts';
|
||||
import { useScrollWrapperHTMLElement } from '@/ui/utilities/scroll/hooks/useScrollWrapperHTMLElement';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { appendCopySuffix, isDefined } from 'twenty-shared/utils';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
@@ -23,17 +24,17 @@ export const useDuplicatePageLayoutWidget = (
|
||||
pageLayoutIdFromProps,
|
||||
);
|
||||
|
||||
const pageLayoutDraftState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraftState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const pageLayoutCurrentLayoutsState = useRecoilComponentCallbackState(
|
||||
const pageLayoutCurrentLayoutsState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const setEditingWidgetId = useSetRecoilComponentState(
|
||||
const setEditingWidgetId = useSetRecoilComponentStateV2(
|
||||
pageLayoutEditingWidgetIdComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
@@ -42,107 +43,105 @@ export const useDuplicatePageLayoutWidget = (
|
||||
getScrollWrapperInstanceIdFromPageLayoutId(pageLayoutId),
|
||||
);
|
||||
|
||||
const duplicateWidget = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
(widgetId: string): string => {
|
||||
const pageLayoutDraft = snapshot
|
||||
.getLoadable(pageLayoutDraftState)
|
||||
.getValue();
|
||||
const store = useStore();
|
||||
|
||||
const allTabLayouts = snapshot
|
||||
.getLoadable(pageLayoutCurrentLayoutsState)
|
||||
.getValue();
|
||||
const duplicateWidget = useCallback(
|
||||
(widgetId: string): string => {
|
||||
const pageLayoutDraft = store.get(pageLayoutDraftState);
|
||||
|
||||
const sourceWidget = pageLayoutDraft.tabs
|
||||
.flatMap((tab) => tab.widgets)
|
||||
.find((widget) => widget.id === widgetId);
|
||||
const allTabLayouts = store.get(pageLayoutCurrentLayoutsState);
|
||||
|
||||
if (!isDefined(sourceWidget)) {
|
||||
throw new Error(`Widget with id ${widgetId} not found`);
|
||||
}
|
||||
const sourceWidget = pageLayoutDraft.tabs
|
||||
.flatMap((tab) => tab.widgets)
|
||||
.find((widget) => widget.id === widgetId);
|
||||
|
||||
const sourceTab = pageLayoutDraft.tabs.find(
|
||||
(tab) => tab.id === sourceWidget.pageLayoutTabId,
|
||||
if (!isDefined(sourceWidget)) {
|
||||
throw new Error(`Widget with id ${widgetId} not found`);
|
||||
}
|
||||
|
||||
const sourceTab = pageLayoutDraft.tabs.find(
|
||||
(tab) => tab.id === sourceWidget.pageLayoutTabId,
|
||||
);
|
||||
|
||||
if (!isDefined(sourceTab)) {
|
||||
throw new Error(
|
||||
`Tab with id ${sourceWidget.pageLayoutTabId} not found`,
|
||||
);
|
||||
}
|
||||
|
||||
if (!isDefined(sourceTab)) {
|
||||
throw new Error(
|
||||
`Tab with id ${sourceWidget.pageLayoutTabId} not found`,
|
||||
const newWidgetId = uuidv4();
|
||||
|
||||
const clonedWidget: PageLayoutWidget = {
|
||||
...sourceWidget,
|
||||
id: newWidgetId,
|
||||
title: appendCopySuffix(sourceWidget.title),
|
||||
...generateDuplicatedTimestamps(),
|
||||
};
|
||||
|
||||
const currentTabLayouts = allTabLayouts[sourceTab.id] || {
|
||||
desktop: [],
|
||||
mobile: [],
|
||||
};
|
||||
|
||||
const sourceLayout = currentTabLayouts.desktop.find(
|
||||
(layout) => layout.i === widgetId,
|
||||
);
|
||||
|
||||
if (!isDefined(sourceLayout)) {
|
||||
throw new Error(`Layout for widget ${widgetId} not found`);
|
||||
}
|
||||
|
||||
const maxY = currentTabLayouts.desktop.reduce(
|
||||
(max, layout) => Math.max(max, layout.y + layout.h),
|
||||
0,
|
||||
);
|
||||
|
||||
const newLayout = {
|
||||
...sourceLayout,
|
||||
i: newWidgetId,
|
||||
y: maxY,
|
||||
};
|
||||
|
||||
const updatedLayouts = getUpdatedTabLayouts(
|
||||
allTabLayouts,
|
||||
sourceTab.id,
|
||||
newLayout,
|
||||
);
|
||||
|
||||
store.set(pageLayoutCurrentLayoutsState, updatedLayouts);
|
||||
|
||||
store.set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: addWidgetToTab(prev.tabs, sourceTab.id, clonedWidget),
|
||||
}));
|
||||
|
||||
setEditingWidgetId(newWidgetId);
|
||||
|
||||
const { scrollWrapperElement } = getScrollWrapperElement();
|
||||
|
||||
if (isDefined(scrollWrapperElement)) {
|
||||
requestAnimationFrame(() => {
|
||||
const widgetElement = scrollWrapperElement.querySelector(
|
||||
`[data-widget-id="${newWidgetId}"]`,
|
||||
);
|
||||
}
|
||||
|
||||
const newWidgetId = uuidv4();
|
||||
if (isDefined(widgetElement)) {
|
||||
widgetElement.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'nearest',
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const clonedWidget: PageLayoutWidget = {
|
||||
...sourceWidget,
|
||||
id: newWidgetId,
|
||||
title: appendCopySuffix(sourceWidget.title),
|
||||
...generateDuplicatedTimestamps(),
|
||||
};
|
||||
|
||||
const currentTabLayouts = allTabLayouts[sourceTab.id] || {
|
||||
desktop: [],
|
||||
mobile: [],
|
||||
};
|
||||
|
||||
const sourceLayout = currentTabLayouts.desktop.find(
|
||||
(layout) => layout.i === widgetId,
|
||||
);
|
||||
|
||||
if (!isDefined(sourceLayout)) {
|
||||
throw new Error(`Layout for widget ${widgetId} not found`);
|
||||
}
|
||||
|
||||
const maxY = currentTabLayouts.desktop.reduce(
|
||||
(max, layout) => Math.max(max, layout.y + layout.h),
|
||||
0,
|
||||
);
|
||||
|
||||
const newLayout = {
|
||||
...sourceLayout,
|
||||
i: newWidgetId,
|
||||
y: maxY,
|
||||
};
|
||||
|
||||
const updatedLayouts = getUpdatedTabLayouts(
|
||||
allTabLayouts,
|
||||
sourceTab.id,
|
||||
newLayout,
|
||||
);
|
||||
|
||||
set(pageLayoutCurrentLayoutsState, updatedLayouts);
|
||||
|
||||
set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: addWidgetToTab(prev.tabs, sourceTab.id, clonedWidget),
|
||||
}));
|
||||
|
||||
setEditingWidgetId(newWidgetId);
|
||||
|
||||
const { scrollWrapperElement } = getScrollWrapperElement();
|
||||
|
||||
if (isDefined(scrollWrapperElement)) {
|
||||
requestAnimationFrame(() => {
|
||||
const widgetElement = scrollWrapperElement.querySelector(
|
||||
`[data-widget-id="${newWidgetId}"]`,
|
||||
);
|
||||
|
||||
if (isDefined(widgetElement)) {
|
||||
widgetElement.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'nearest',
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return newWidgetId;
|
||||
},
|
||||
return newWidgetId;
|
||||
},
|
||||
[
|
||||
pageLayoutCurrentLayoutsState,
|
||||
pageLayoutDraftState,
|
||||
setEditingWidgetId,
|
||||
getScrollWrapperElement,
|
||||
store,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
|
||||
import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState';
|
||||
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilComponentStateV2';
|
||||
import { CommandMenuPages } from 'twenty-shared/types';
|
||||
|
||||
import { useNavigatePageLayoutCommandMenu } from '@/command-menu/pages/page-layout/hooks/useNavigatePageLayoutCommandMenu';
|
||||
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
|
||||
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useSetRecoilStateV2 } from '@/ui/utilities/state/jotai/hooks/useSetRecoilStateV2';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { WidgetType } from '~/generated-metadata/graphql';
|
||||
|
||||
@@ -19,14 +19,14 @@ export const useEditPageLayoutWidget = (pageLayoutIdFromProps?: string) => {
|
||||
pageLayoutIdFromProps,
|
||||
);
|
||||
|
||||
const setPageLayoutEditingWidgetId = useSetRecoilComponentState(
|
||||
const setPageLayoutEditingWidgetId = useSetRecoilComponentStateV2(
|
||||
pageLayoutEditingWidgetIdComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const { navigatePageLayoutCommandMenu } = useNavigatePageLayoutCommandMenu();
|
||||
const { closeCommandMenu } = useCommandMenu();
|
||||
const setCommandMenuPage = useSetRecoilState(commandMenuPageState);
|
||||
const setCommandMenuPage = useSetRecoilStateV2(commandMenuPageState);
|
||||
|
||||
const handleEditWidget = useCallback(
|
||||
({
|
||||
|
||||
+31
-33
@@ -5,8 +5,9 @@ import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pa
|
||||
import { pageLayoutSelectedCellsComponentState } from '@/page-layout/states/pageLayoutSelectedCellsComponentState';
|
||||
import { calculateGridBoundsFromSelectedCells } from '@/page-layout/utils/calculateGridBoundsFromSelectedCells';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { CommandMenuPages } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@@ -18,54 +19,51 @@ export const useEndPageLayoutDragSelection = (
|
||||
pageLayoutIdFromProps,
|
||||
);
|
||||
|
||||
const pageLayoutSelectedCellsState = useRecoilComponentCallbackState(
|
||||
const pageLayoutSelectedCellsState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutSelectedCellsComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
const pageLayoutDraggedAreaState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraggedAreaState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraggedAreaComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const pageLayoutEditingWidgetIdState = useRecoilComponentCallbackState(
|
||||
const pageLayoutEditingWidgetIdState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutEditingWidgetIdComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const { navigatePageLayoutCommandMenu } = useNavigatePageLayoutCommandMenu();
|
||||
|
||||
const endPageLayoutDragSelection = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
() => {
|
||||
const pageLayoutSelectedCells = snapshot
|
||||
.getLoadable(pageLayoutSelectedCellsState)
|
||||
.getValue();
|
||||
const store = useStore();
|
||||
|
||||
if (pageLayoutSelectedCells.size > 0) {
|
||||
const draggedBounds = calculateGridBoundsFromSelectedCells(
|
||||
Array.from(pageLayoutSelectedCells),
|
||||
);
|
||||
const endPageLayoutDragSelection = useCallback(() => {
|
||||
const pageLayoutSelectedCells = store.get(pageLayoutSelectedCellsState);
|
||||
|
||||
if (isDefined(draggedBounds)) {
|
||||
set(pageLayoutDraggedAreaState, draggedBounds);
|
||||
set(pageLayoutEditingWidgetIdState, null);
|
||||
if (pageLayoutSelectedCells.size > 0) {
|
||||
const draggedBounds = calculateGridBoundsFromSelectedCells(
|
||||
Array.from(pageLayoutSelectedCells),
|
||||
);
|
||||
|
||||
navigatePageLayoutCommandMenu({
|
||||
commandMenuPage: CommandMenuPages.PageLayoutWidgetTypeSelect,
|
||||
resetNavigationStack: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (isDefined(draggedBounds)) {
|
||||
store.set(pageLayoutDraggedAreaState, draggedBounds);
|
||||
store.set(pageLayoutEditingWidgetIdState, null);
|
||||
|
||||
set(pageLayoutSelectedCellsState, new Set());
|
||||
},
|
||||
[
|
||||
navigatePageLayoutCommandMenu,
|
||||
pageLayoutDraggedAreaState,
|
||||
pageLayoutEditingWidgetIdState,
|
||||
pageLayoutSelectedCellsState,
|
||||
],
|
||||
);
|
||||
navigatePageLayoutCommandMenu({
|
||||
commandMenuPage: CommandMenuPages.PageLayoutWidgetTypeSelect,
|
||||
resetNavigationStack: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
store.set(pageLayoutSelectedCellsState, new Set());
|
||||
}, [
|
||||
navigatePageLayoutCommandMenu,
|
||||
pageLayoutDraggedAreaState,
|
||||
pageLayoutEditingWidgetIdState,
|
||||
pageLayoutSelectedCellsState,
|
||||
store,
|
||||
]);
|
||||
|
||||
return { endPageLayoutDragSelection };
|
||||
};
|
||||
|
||||
@@ -3,8 +3,9 @@ import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDr
|
||||
import { sortTabsByPosition } from '@/page-layout/utils/sortTabsByPosition';
|
||||
import { calculateNewPosition } from '@/ui/layout/draggable-list/utils/calculateNewPosition';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export const useMovePageLayoutTab = (pageLayoutIdFromProps?: string) => {
|
||||
const pageLayoutId = useAvailableComponentInstanceIdOrThrow(
|
||||
@@ -12,67 +13,67 @@ export const useMovePageLayoutTab = (pageLayoutIdFromProps?: string) => {
|
||||
pageLayoutIdFromProps,
|
||||
);
|
||||
|
||||
const pageLayoutDraftState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraftState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const moveLeft = useRecoilCallback(
|
||||
({ set }) =>
|
||||
(tabId: string) => {
|
||||
set(pageLayoutDraftState, (prev) => {
|
||||
const sorted = sortTabsByPosition(prev.tabs);
|
||||
const index = sorted.findIndex((t) => t.id === tabId);
|
||||
if (index <= 0) return prev;
|
||||
const store = useStore();
|
||||
|
||||
const items = sorted.filter((t) => t.id !== tabId);
|
||||
const destinationIndex = index - 1;
|
||||
const sourceIndex = index;
|
||||
const moveLeft = useCallback(
|
||||
(tabId: string) => {
|
||||
store.set(pageLayoutDraftState, (prev) => {
|
||||
const sorted = sortTabsByPosition(prev.tabs);
|
||||
const index = sorted.findIndex((t) => t.id === tabId);
|
||||
if (index <= 0) return prev;
|
||||
|
||||
const newPosition = calculateNewPosition({
|
||||
destinationIndex,
|
||||
sourceIndex,
|
||||
items,
|
||||
});
|
||||
const items = sorted.filter((t) => t.id !== tabId);
|
||||
const destinationIndex = index - 1;
|
||||
const sourceIndex = index;
|
||||
|
||||
return {
|
||||
...prev,
|
||||
tabs: prev.tabs.map((t) =>
|
||||
t.id === tabId ? { ...t, position: newPosition } : t,
|
||||
),
|
||||
};
|
||||
const newPosition = calculateNewPosition({
|
||||
destinationIndex,
|
||||
sourceIndex,
|
||||
items,
|
||||
});
|
||||
},
|
||||
[pageLayoutDraftState],
|
||||
|
||||
return {
|
||||
...prev,
|
||||
tabs: prev.tabs.map((t) =>
|
||||
t.id === tabId ? { ...t, position: newPosition } : t,
|
||||
),
|
||||
};
|
||||
});
|
||||
},
|
||||
[pageLayoutDraftState, store],
|
||||
);
|
||||
|
||||
const moveRight = useRecoilCallback(
|
||||
({ set }) =>
|
||||
(tabId: string) => {
|
||||
set(pageLayoutDraftState, (prev) => {
|
||||
const sorted = sortTabsByPosition(prev.tabs);
|
||||
const index = sorted.findIndex((t) => t.id === tabId);
|
||||
if (index < 0 || index >= sorted.length - 1) return prev;
|
||||
const moveRight = useCallback(
|
||||
(tabId: string) => {
|
||||
store.set(pageLayoutDraftState, (prev) => {
|
||||
const sorted = sortTabsByPosition(prev.tabs);
|
||||
const index = sorted.findIndex((t) => t.id === tabId);
|
||||
if (index < 0 || index >= sorted.length - 1) return prev;
|
||||
|
||||
const items = sorted.filter((t) => t.id !== tabId);
|
||||
const destinationIndex = index + 1;
|
||||
const sourceIndex = index;
|
||||
const items = sorted.filter((t) => t.id !== tabId);
|
||||
const destinationIndex = index + 1;
|
||||
const sourceIndex = index;
|
||||
|
||||
const newPosition = calculateNewPosition({
|
||||
destinationIndex,
|
||||
sourceIndex,
|
||||
items,
|
||||
});
|
||||
|
||||
return {
|
||||
...prev,
|
||||
tabs: prev.tabs.map((t) =>
|
||||
t.id === tabId ? { ...t, position: newPosition } : t,
|
||||
),
|
||||
};
|
||||
const newPosition = calculateNewPosition({
|
||||
destinationIndex,
|
||||
sourceIndex,
|
||||
items,
|
||||
});
|
||||
},
|
||||
[pageLayoutDraftState],
|
||||
|
||||
return {
|
||||
...prev,
|
||||
tabs: prev.tabs.map((t) =>
|
||||
t.id === tabId ? { ...t, position: newPosition } : t,
|
||||
),
|
||||
};
|
||||
});
|
||||
},
|
||||
[pageLayoutDraftState, store],
|
||||
);
|
||||
|
||||
return { moveLeft, moveRight };
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateV2';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
import { pageLayoutPersistedComponentState } from '@/page-layout/states/pageLayoutPersistedComponentState';
|
||||
@@ -12,11 +12,11 @@ export const usePageLayoutDraftState = (pageLayoutIdFromProps?: string) => {
|
||||
pageLayoutIdFromProps,
|
||||
);
|
||||
|
||||
const [pageLayoutDraft, setPageLayoutDraft] = useRecoilComponentState(
|
||||
const [pageLayoutDraft, setPageLayoutDraft] = useRecoilComponentStateV2(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
const pageLayoutPersisted = useRecoilComponentValue(
|
||||
const pageLayoutPersisted = useRecoilComponentValueV2(
|
||||
pageLayoutPersistedComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
+51
-56
@@ -2,10 +2,10 @@ import { PageLayoutComponentInstanceContext } from '@/page-layout/states/context
|
||||
import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutId';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useStore } from 'jotai';
|
||||
import { type Layout, type Layouts } from 'react-grid-layout';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
@@ -22,69 +22,64 @@ export const usePageLayoutHandleLayoutChange = (
|
||||
const store = useStore();
|
||||
const tabListInstanceId = getTabListInstanceIdFromPageLayoutId(pageLayoutId);
|
||||
|
||||
const pageLayoutCurrentLayoutsState = useRecoilComponentCallbackState(
|
||||
const pageLayoutCurrentLayoutsState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const pageLayoutDraftState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraftState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const handleLayoutChange = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
(_: Layout[], allLayouts: Layouts) => {
|
||||
const activeTabId = store.get(
|
||||
activeTabIdComponentState.atomFamily({
|
||||
instanceId: tabListInstanceId,
|
||||
const handleLayoutChange = useCallback(
|
||||
(_: Layout[], allLayouts: Layouts) => {
|
||||
const activeTabId = store.get(
|
||||
activeTabIdComponentState.atomFamily({
|
||||
instanceId: tabListInstanceId,
|
||||
}),
|
||||
);
|
||||
|
||||
if (!isDefined(activeTabId)) return;
|
||||
|
||||
const currentTabLayouts = store.get(pageLayoutCurrentLayoutsState);
|
||||
|
||||
store.set(pageLayoutCurrentLayoutsState, {
|
||||
...currentTabLayouts,
|
||||
[activeTabId]: structuredClone(allLayouts),
|
||||
});
|
||||
|
||||
const pageLayoutDraft = store.get(pageLayoutDraftState);
|
||||
|
||||
const currentTab = pageLayoutDraft.tabs.find(
|
||||
(tab) => tab.id === activeTabId,
|
||||
);
|
||||
|
||||
if (!currentTab) return;
|
||||
|
||||
const updatedWidgets = convertLayoutsToWidgets(
|
||||
currentTab.widgets,
|
||||
allLayouts,
|
||||
);
|
||||
|
||||
if (isDefined(activeTabId)) {
|
||||
store.set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: prev.tabs.map((tab) => {
|
||||
if (tab.id === activeTabId) {
|
||||
const tabWidgets = updatedWidgets.filter(
|
||||
(w) => w.pageLayoutTabId === activeTabId,
|
||||
);
|
||||
return {
|
||||
...tab,
|
||||
widgets: tabWidgets,
|
||||
};
|
||||
}
|
||||
return tab;
|
||||
}),
|
||||
);
|
||||
|
||||
if (!isDefined(activeTabId)) return;
|
||||
|
||||
const currentTabLayouts = snapshot
|
||||
.getLoadable(pageLayoutCurrentLayoutsState)
|
||||
.getValue();
|
||||
|
||||
set(pageLayoutCurrentLayoutsState, {
|
||||
...currentTabLayouts,
|
||||
[activeTabId]: structuredClone(allLayouts),
|
||||
});
|
||||
|
||||
const pageLayoutDraft = snapshot
|
||||
.getLoadable(pageLayoutDraftState)
|
||||
.getValue();
|
||||
|
||||
const currentTab = pageLayoutDraft.tabs.find(
|
||||
(tab) => tab.id === activeTabId,
|
||||
);
|
||||
|
||||
if (!currentTab) return;
|
||||
|
||||
const updatedWidgets = convertLayoutsToWidgets(
|
||||
currentTab.widgets,
|
||||
allLayouts,
|
||||
);
|
||||
|
||||
if (isDefined(activeTabId)) {
|
||||
set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: prev.tabs.map((tab) => {
|
||||
if (tab.id === activeTabId) {
|
||||
const tabWidgets = updatedWidgets.filter(
|
||||
(w) => w.pageLayoutTabId === activeTabId,
|
||||
);
|
||||
return {
|
||||
...tab,
|
||||
widgets: tabWidgets,
|
||||
};
|
||||
}
|
||||
return tab;
|
||||
}),
|
||||
}));
|
||||
}
|
||||
},
|
||||
}));
|
||||
}
|
||||
},
|
||||
[
|
||||
tabListInstanceId,
|
||||
pageLayoutCurrentLayoutsState,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
|
||||
@@ -7,6 +5,7 @@ import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { recordPageLayoutByObjectMetadataIdFamilySelector } from '@/page-layout/states/selectors/recordPageLayoutByObjectMetadataIdFamilySelector';
|
||||
import { getDefaultRecordPageLayoutId } from '@/page-layout/utils/getDefaultRecordPageLayoutId';
|
||||
import { type TargetRecordIdentifier } from '@/ui/layout/contexts/TargetRecordIdentifier';
|
||||
import { useFamilySelectorValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilySelectorValueV2';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const usePageLayoutIdForRecord = ({
|
||||
@@ -29,10 +28,9 @@ export const usePageLayoutIdForRecord = ({
|
||||
objectNameSingular: targetObjectNameSingular,
|
||||
});
|
||||
|
||||
const recordPageLayout = useRecoilValue(
|
||||
recordPageLayoutByObjectMetadataIdFamilySelector({
|
||||
objectMetadataId: objectMetadataItem.id,
|
||||
}),
|
||||
const recordPageLayout = useFamilySelectorValueV2(
|
||||
recordPageLayoutByObjectMetadataIdFamilySelector,
|
||||
{ objectMetadataId: objectMetadataItem.id },
|
||||
);
|
||||
|
||||
if (isDashboard) {
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@ import { buildWidgetVisibilityContext } from '@/page-layout/utils/buildWidgetVis
|
||||
import { filterVisibleWidgets } from '@/page-layout/utils/filterVisibleWidgets';
|
||||
import { useLayoutRenderingContext } from '@/ui/layout/contexts/LayoutRenderingContext';
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentValueV2';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const usePageLayoutTabWithVisibleWidgetsOrThrow = (
|
||||
@@ -14,7 +14,7 @@ export const usePageLayoutTabWithVisibleWidgetsOrThrow = (
|
||||
const { currentPageLayout } = useCurrentPageLayout();
|
||||
const isMobile = useIsMobile();
|
||||
const { isInRightDrawer } = useLayoutRenderingContext();
|
||||
const isPageLayoutInEditMode = useRecoilComponentValue(
|
||||
const isPageLayoutInEditMode = useRecoilComponentValueV2(
|
||||
isPageLayoutInEditModeComponentState,
|
||||
);
|
||||
|
||||
|
||||
+4
-5
@@ -2,7 +2,7 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
|
||||
import { recordPageLayoutByObjectMetadataIdFamilySelector } from '@/page-layout/states/selectors/recordPageLayoutByObjectMetadataIdFamilySelector';
|
||||
import { getDefaultRecordPageLayoutId } from '@/page-layout/utils/getDefaultRecordPageLayoutId';
|
||||
import { type TargetRecordIdentifier } from '@/ui/layout/contexts/TargetRecordIdentifier';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useFamilySelectorValueV2 } from '@/ui/utilities/state/jotai/hooks/useFamilySelectorValueV2';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useRecordPageLayoutIdFromRecordStoreOrThrow = ({
|
||||
@@ -12,10 +12,9 @@ export const useRecordPageLayoutIdFromRecordStoreOrThrow = ({
|
||||
objectNameSingular: targetObjectNameSingular,
|
||||
});
|
||||
|
||||
const recordPageLayout = useRecoilValue(
|
||||
recordPageLayoutByObjectMetadataIdFamilySelector({
|
||||
objectMetadataId: objectMetadataItem.id,
|
||||
}),
|
||||
const recordPageLayout = useFamilySelectorValueV2(
|
||||
recordPageLayoutByObjectMetadataIdFamilySelector,
|
||||
{ objectMetadataId: objectMetadataItem.id },
|
||||
);
|
||||
|
||||
const pageLayoutId = isDefined(recordPageLayout)
|
||||
|
||||
+46
-47
@@ -6,8 +6,9 @@ import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pa
|
||||
import { removeWidgetFromTab } from '@/page-layout/utils/removeWidgetFromTab';
|
||||
import { removeWidgetLayoutFromTab } from '@/page-layout/utils/removeWidgetLayoutFromTab';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useRemovePageLayoutWidgetAndPreservePosition = (
|
||||
@@ -18,80 +19,78 @@ export const useRemovePageLayoutWidgetAndPreservePosition = (
|
||||
pageLayoutIdFromProps,
|
||||
);
|
||||
|
||||
const pageLayoutDraftState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraftState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const pageLayoutCurrentLayoutsState = useRecoilComponentCallbackState(
|
||||
const pageLayoutCurrentLayoutsState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const pageLayoutDraggedAreaState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraggedAreaState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraggedAreaComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const pageLayoutEditingWidgetIdState = useRecoilComponentCallbackState(
|
||||
const pageLayoutEditingWidgetIdState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutEditingWidgetIdComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const removePageLayoutWidgetAndPreservePosition = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
(widgetId: string) => {
|
||||
const pageLayoutDraft = snapshot
|
||||
.getLoadable(pageLayoutDraftState)
|
||||
.getValue();
|
||||
const allTabLayouts = snapshot
|
||||
.getLoadable(pageLayoutCurrentLayoutsState)
|
||||
.getValue();
|
||||
const store = useStore();
|
||||
|
||||
const tabWithWidget = pageLayoutDraft.tabs.find((tab) =>
|
||||
tab.widgets.some((w) => w.id === widgetId),
|
||||
);
|
||||
const tabId = tabWithWidget?.id;
|
||||
const removePageLayoutWidgetAndPreservePosition = useCallback(
|
||||
(widgetId: string) => {
|
||||
const pageLayoutDraft = store.get(pageLayoutDraftState);
|
||||
const allTabLayouts = store.get(pageLayoutCurrentLayoutsState);
|
||||
|
||||
if (!isDefined(tabId)) {
|
||||
return;
|
||||
}
|
||||
const tabWithWidget = pageLayoutDraft.tabs.find((tab) =>
|
||||
tab.widgets.some((w) => w.id === widgetId),
|
||||
);
|
||||
const tabId = tabWithWidget?.id;
|
||||
|
||||
const tabLayouts = allTabLayouts[tabId];
|
||||
const widgetLayout = tabLayouts?.desktop?.find(
|
||||
(layout) => layout.i === widgetId,
|
||||
);
|
||||
if (!isDefined(tabId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isDefined(widgetLayout)) {
|
||||
return;
|
||||
}
|
||||
const tabLayouts = allTabLayouts[tabId];
|
||||
const widgetLayout = tabLayouts?.desktop?.find(
|
||||
(layout) => layout.i === widgetId,
|
||||
);
|
||||
|
||||
set(pageLayoutDraggedAreaState, {
|
||||
x: widgetLayout.x,
|
||||
y: widgetLayout.y,
|
||||
w: widgetLayout.w,
|
||||
h: widgetLayout.h,
|
||||
});
|
||||
if (!isDefined(widgetLayout)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedLayouts = removeWidgetLayoutFromTab(
|
||||
allTabLayouts,
|
||||
tabId,
|
||||
widgetId,
|
||||
);
|
||||
set(pageLayoutCurrentLayoutsState, updatedLayouts);
|
||||
store.set(pageLayoutDraggedAreaState, {
|
||||
x: widgetLayout.x,
|
||||
y: widgetLayout.y,
|
||||
w: widgetLayout.w,
|
||||
h: widgetLayout.h,
|
||||
});
|
||||
|
||||
set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: removeWidgetFromTab(prev.tabs, tabId, widgetId),
|
||||
}));
|
||||
const updatedLayouts = removeWidgetLayoutFromTab(
|
||||
allTabLayouts,
|
||||
tabId,
|
||||
widgetId,
|
||||
);
|
||||
store.set(pageLayoutCurrentLayoutsState, updatedLayouts);
|
||||
|
||||
set(pageLayoutEditingWidgetIdState, null);
|
||||
},
|
||||
store.set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: removeWidgetFromTab(prev.tabs, tabId, widgetId),
|
||||
}));
|
||||
|
||||
store.set(pageLayoutEditingWidgetIdState, null);
|
||||
},
|
||||
[
|
||||
pageLayoutCurrentLayoutsState,
|
||||
pageLayoutDraftState,
|
||||
pageLayoutDraggedAreaState,
|
||||
pageLayoutEditingWidgetIdState,
|
||||
store,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
+24
-22
@@ -1,9 +1,10 @@
|
||||
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { type DropResult } from '@hello-pangea/dnd';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useReorderPageLayoutWidgets = (
|
||||
@@ -15,33 +16,34 @@ export const useReorderPageLayoutWidgets = (
|
||||
pageLayoutIdFromProps,
|
||||
);
|
||||
|
||||
const pageLayoutDraftState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraftState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const reorderWidgets = useRecoilCallback(
|
||||
({ set }) =>
|
||||
(result: DropResult) => {
|
||||
if (!result.destination) return;
|
||||
const store = useStore();
|
||||
|
||||
set(pageLayoutDraftState, (prev) => {
|
||||
const tab = prev.tabs.find((t) => t.id === tabId);
|
||||
if (!isDefined(tab)) return prev;
|
||||
const reorderWidgets = useCallback(
|
||||
(result: DropResult) => {
|
||||
if (!result.destination) return;
|
||||
|
||||
const newWidgets = Array.from(tab.widgets ?? []);
|
||||
const [removed] = newWidgets.splice(result.source.index, 1);
|
||||
newWidgets.splice(result.destination!.index, 0, removed);
|
||||
store.set(pageLayoutDraftState, (prev) => {
|
||||
const tab = prev.tabs.find((t) => t.id === tabId);
|
||||
if (!isDefined(tab)) return prev;
|
||||
|
||||
return {
|
||||
...prev,
|
||||
tabs: prev.tabs.map((t) =>
|
||||
t.id === tabId ? { ...t, widgets: newWidgets } : t,
|
||||
),
|
||||
};
|
||||
});
|
||||
},
|
||||
[tabId, pageLayoutDraftState],
|
||||
const newWidgets = Array.from(tab.widgets ?? []);
|
||||
const [removed] = newWidgets.splice(result.source.index, 1);
|
||||
newWidgets.splice(result.destination!.index, 0, removed);
|
||||
|
||||
return {
|
||||
...prev,
|
||||
tabs: prev.tabs.map((t) =>
|
||||
t.id === tabId ? { ...t, widgets: newWidgets } : t,
|
||||
),
|
||||
};
|
||||
});
|
||||
},
|
||||
[tabId, pageLayoutDraftState, store],
|
||||
);
|
||||
|
||||
return { reorderWidgets };
|
||||
|
||||
+44
-52
@@ -8,9 +8,9 @@ import { convertPageLayoutToTabLayouts } from '@/page-layout/utils/convertPageLa
|
||||
import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutId';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useStore } from 'jotai';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const useResetDraftPageLayoutToPersistedPageLayout = (
|
||||
@@ -25,17 +25,17 @@ export const useResetDraftPageLayoutToPersistedPageLayout = (
|
||||
const tabListComponentInstanceId =
|
||||
getTabListInstanceIdFromPageLayoutId(componentInstanceId);
|
||||
|
||||
const pageLayoutDraftState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraftState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraftComponentState,
|
||||
componentInstanceId,
|
||||
);
|
||||
|
||||
const pageLayoutPersistedState = useRecoilComponentCallbackState(
|
||||
const pageLayoutPersistedState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutPersistedComponentState,
|
||||
componentInstanceId,
|
||||
);
|
||||
|
||||
const pageLayoutCurrentLayoutsState = useRecoilComponentCallbackState(
|
||||
const pageLayoutCurrentLayoutsState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
componentInstanceId,
|
||||
);
|
||||
@@ -44,64 +44,56 @@ export const useResetDraftPageLayoutToPersistedPageLayout = (
|
||||
instanceId: tabListComponentInstanceId,
|
||||
});
|
||||
|
||||
const fieldsWidgetGroupsDraftState = useRecoilComponentCallbackState(
|
||||
const fieldsWidgetGroupsDraftState = useRecoilComponentStateCallbackStateV2(
|
||||
fieldsWidgetGroupsDraftComponentState,
|
||||
componentInstanceId,
|
||||
);
|
||||
|
||||
const fieldsWidgetGroupsPersistedState = useRecoilComponentCallbackState(
|
||||
fieldsWidgetGroupsPersistedComponentState,
|
||||
componentInstanceId,
|
||||
);
|
||||
const fieldsWidgetGroupsPersistedState =
|
||||
useRecoilComponentStateCallbackStateV2(
|
||||
fieldsWidgetGroupsPersistedComponentState,
|
||||
componentInstanceId,
|
||||
);
|
||||
|
||||
const resetDraftPageLayoutToPersistedPageLayout = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
() => {
|
||||
const pageLayoutPersisted = snapshot
|
||||
.getLoadable(pageLayoutPersistedState)
|
||||
.getValue();
|
||||
const resetDraftPageLayoutToPersistedPageLayout = useCallback(() => {
|
||||
const pageLayoutPersisted = store.get(pageLayoutPersistedState);
|
||||
|
||||
if (isDefined(pageLayoutPersisted)) {
|
||||
const currentActiveTabId = store.get(activeTabIdAtom);
|
||||
if (isDefined(pageLayoutPersisted)) {
|
||||
const currentActiveTabId = store.get(activeTabIdAtom);
|
||||
|
||||
const persistedTabIds = pageLayoutPersisted.tabs.map((tab) => tab.id);
|
||||
const isActiveTabInPersistedTabs =
|
||||
currentActiveTabId && persistedTabIds.includes(currentActiveTabId);
|
||||
const persistedTabIds = pageLayoutPersisted.tabs.map((tab) => tab.id);
|
||||
const isActiveTabInPersistedTabs =
|
||||
currentActiveTabId && persistedTabIds.includes(currentActiveTabId);
|
||||
|
||||
if (
|
||||
!isActiveTabInPersistedTabs &&
|
||||
pageLayoutPersisted.tabs.length > 0
|
||||
) {
|
||||
store.set(activeTabIdAtom, pageLayoutPersisted.tabs[0].id);
|
||||
}
|
||||
if (!isActiveTabInPersistedTabs && pageLayoutPersisted.tabs.length > 0) {
|
||||
store.set(activeTabIdAtom, pageLayoutPersisted.tabs[0].id);
|
||||
}
|
||||
|
||||
set(pageLayoutDraftState, {
|
||||
id: pageLayoutPersisted.id,
|
||||
name: pageLayoutPersisted.name,
|
||||
type: pageLayoutPersisted.type,
|
||||
objectMetadataId: pageLayoutPersisted.objectMetadataId,
|
||||
tabs: pageLayoutPersisted.tabs,
|
||||
});
|
||||
store.set(pageLayoutDraftState, {
|
||||
id: pageLayoutPersisted.id,
|
||||
name: pageLayoutPersisted.name,
|
||||
type: pageLayoutPersisted.type,
|
||||
objectMetadataId: pageLayoutPersisted.objectMetadataId,
|
||||
tabs: pageLayoutPersisted.tabs,
|
||||
});
|
||||
|
||||
const tabLayouts = convertPageLayoutToTabLayouts(pageLayoutPersisted);
|
||||
set(pageLayoutCurrentLayoutsState, tabLayouts);
|
||||
const tabLayouts = convertPageLayoutToTabLayouts(pageLayoutPersisted);
|
||||
store.set(pageLayoutCurrentLayoutsState, tabLayouts);
|
||||
|
||||
const fieldsWidgetGroupsPersisted = snapshot
|
||||
.getLoadable(fieldsWidgetGroupsPersistedState)
|
||||
.getValue();
|
||||
set(fieldsWidgetGroupsDraftState, fieldsWidgetGroupsPersisted);
|
||||
}
|
||||
},
|
||||
[
|
||||
pageLayoutDraftState,
|
||||
pageLayoutPersistedState,
|
||||
pageLayoutCurrentLayoutsState,
|
||||
fieldsWidgetGroupsDraftState,
|
||||
fieldsWidgetGroupsPersistedState,
|
||||
activeTabIdAtom,
|
||||
store,
|
||||
],
|
||||
);
|
||||
const fieldsWidgetGroupsPersisted = store.get(
|
||||
fieldsWidgetGroupsPersistedState,
|
||||
);
|
||||
store.set(fieldsWidgetGroupsDraftState, fieldsWidgetGroupsPersisted);
|
||||
}
|
||||
}, [
|
||||
pageLayoutDraftState,
|
||||
pageLayoutPersistedState,
|
||||
pageLayoutCurrentLayoutsState,
|
||||
fieldsWidgetGroupsDraftState,
|
||||
fieldsWidgetGroupsPersistedState,
|
||||
activeTabIdAtom,
|
||||
store,
|
||||
]);
|
||||
|
||||
return {
|
||||
resetDraftPageLayoutToPersistedPageLayout,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { UPSERT_FIELDS_WIDGET } from '@/page-layout/graphql/mutations/upsertFieldsWidget';
|
||||
import { fieldsWidgetGroupsDraftComponentState } from '@/page-layout/states/fieldsWidgetGroupsDraftComponentState';
|
||||
import { fieldsWidgetGroupsPersistedComponentState } from '@/page-layout/states/fieldsWidgetGroupsPersistedComponentState';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useRefreshAllCoreViews } from '@/views/hooks/useRefreshAllCoreViews';
|
||||
import { useMutation } from '@apollo/client';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { type ViewFragmentFragment } from '~/generated-metadata/graphql';
|
||||
|
||||
@@ -39,15 +40,16 @@ type UseSaveFieldsWidgetGroupsParams = {
|
||||
export const useSaveFieldsWidgetGroups = ({
|
||||
pageLayoutId,
|
||||
}: UseSaveFieldsWidgetGroupsParams) => {
|
||||
const fieldsWidgetGroupsDraftState = useRecoilComponentCallbackState(
|
||||
const fieldsWidgetGroupsDraftState = useRecoilComponentStateCallbackStateV2(
|
||||
fieldsWidgetGroupsDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const fieldsWidgetGroupsPersistedState = useRecoilComponentCallbackState(
|
||||
fieldsWidgetGroupsPersistedComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
const fieldsWidgetGroupsPersistedState =
|
||||
useRecoilComponentStateCallbackStateV2(
|
||||
fieldsWidgetGroupsPersistedComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const [upsertFieldsWidgetMutation] = useMutation<
|
||||
UpsertFieldsWidgetResult,
|
||||
@@ -56,65 +58,60 @@ export const useSaveFieldsWidgetGroups = ({
|
||||
|
||||
const { refreshAllCoreViews } = useRefreshAllCoreViews();
|
||||
|
||||
const saveFieldsWidgetGroups = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async () => {
|
||||
const allDraftGroups = snapshot
|
||||
.getLoadable(fieldsWidgetGroupsDraftState)
|
||||
.getValue();
|
||||
const allPersistedGroups = snapshot
|
||||
.getLoadable(fieldsWidgetGroupsPersistedState)
|
||||
.getValue();
|
||||
const store = useStore();
|
||||
|
||||
const widgetIds = new Set([
|
||||
...Object.keys(allDraftGroups),
|
||||
...Object.keys(allPersistedGroups),
|
||||
]);
|
||||
const saveFieldsWidgetGroups = useCallback(async () => {
|
||||
const allDraftGroups = store.get(fieldsWidgetGroupsDraftState);
|
||||
const allPersistedGroups = store.get(fieldsWidgetGroupsPersistedState);
|
||||
|
||||
for (const widgetId of widgetIds) {
|
||||
const draftGroups = allDraftGroups[widgetId] ?? [];
|
||||
const widgetIds = new Set([
|
||||
...Object.keys(allDraftGroups),
|
||||
...Object.keys(allPersistedGroups),
|
||||
]);
|
||||
|
||||
await upsertFieldsWidgetMutation({
|
||||
variables: {
|
||||
input: {
|
||||
widgetId,
|
||||
groups: draftGroups.map((group) => ({
|
||||
id: group.id,
|
||||
name: group.name,
|
||||
position: group.position,
|
||||
isVisible: group.isVisible,
|
||||
fields: group.fields.flatMap((field) => {
|
||||
if (!isDefined(field.viewFieldId)) {
|
||||
return [];
|
||||
}
|
||||
for (const widgetId of widgetIds) {
|
||||
const draftGroups = allDraftGroups[widgetId] ?? [];
|
||||
|
||||
return [
|
||||
{
|
||||
viewFieldId: field.viewFieldId,
|
||||
isVisible: field.isVisible,
|
||||
position: field.position,
|
||||
},
|
||||
];
|
||||
}),
|
||||
})),
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
await upsertFieldsWidgetMutation({
|
||||
variables: {
|
||||
input: {
|
||||
widgetId,
|
||||
groups: draftGroups.map((group) => ({
|
||||
id: group.id,
|
||||
name: group.name,
|
||||
position: group.position,
|
||||
isVisible: group.isVisible,
|
||||
fields: group.fields.flatMap((field) => {
|
||||
if (!isDefined(field.viewFieldId)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
set(fieldsWidgetGroupsPersistedState, allDraftGroups);
|
||||
return [
|
||||
{
|
||||
viewFieldId: field.viewFieldId,
|
||||
isVisible: field.isVisible,
|
||||
position: field.position,
|
||||
},
|
||||
];
|
||||
}),
|
||||
})),
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
await refreshAllCoreViews();
|
||||
store.set(fieldsWidgetGroupsPersistedState, allDraftGroups);
|
||||
|
||||
return { status: 'successful' as const };
|
||||
},
|
||||
[
|
||||
fieldsWidgetGroupsDraftState,
|
||||
fieldsWidgetGroupsPersistedState,
|
||||
upsertFieldsWidgetMutation,
|
||||
refreshAllCoreViews,
|
||||
],
|
||||
);
|
||||
await refreshAllCoreViews();
|
||||
|
||||
return { status: 'successful' as const };
|
||||
}, [
|
||||
fieldsWidgetGroupsDraftState,
|
||||
fieldsWidgetGroupsPersistedState,
|
||||
upsertFieldsWidgetMutation,
|
||||
refreshAllCoreViews,
|
||||
store,
|
||||
]);
|
||||
|
||||
return { saveFieldsWidgetGroups };
|
||||
};
|
||||
|
||||
@@ -9,8 +9,9 @@ import { convertPageLayoutToTabLayouts } from '@/page-layout/utils/convertPageLa
|
||||
import { reInjectDynamicRelationWidgetsFromDraft } from '@/page-layout/utils/reInjectDynamicRelationWidgetsFromDraft';
|
||||
import { transformPageLayout } from '@/page-layout/utils/transformPageLayout';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { PageLayoutType } from '~/generated-metadata/graphql';
|
||||
|
||||
@@ -20,17 +21,19 @@ export const useSavePageLayout = (pageLayoutIdFromProps: string) => {
|
||||
pageLayoutIdFromProps,
|
||||
);
|
||||
|
||||
const pageLayoutPersistedCallbackState = useRecoilComponentCallbackState(
|
||||
pageLayoutPersistedComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
const pageLayoutPersistedCallbackState =
|
||||
useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutPersistedComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const pageLayoutCurrentLayoutsCallbackState = useRecoilComponentCallbackState(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
const pageLayoutCurrentLayoutsCallbackState =
|
||||
useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutCurrentLayoutsComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const pageLayoutDraftCallbackState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraftCallbackState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
@@ -38,54 +41,49 @@ export const useSavePageLayout = (pageLayoutIdFromProps: string) => {
|
||||
const { updatePageLayoutWithTabsAndWidgets } =
|
||||
useUpdatePageLayoutWithTabsAndWidgets();
|
||||
|
||||
const savePageLayout = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async () => {
|
||||
const pageLayoutDraft = snapshot
|
||||
.getLoadable(pageLayoutDraftCallbackState)
|
||||
.getValue();
|
||||
const updateInput =
|
||||
convertPageLayoutDraftToUpdateInput(pageLayoutDraft);
|
||||
const store = useStore();
|
||||
|
||||
const result = await updatePageLayoutWithTabsAndWidgets(
|
||||
pageLayoutId,
|
||||
updateInput,
|
||||
);
|
||||
const savePageLayout = useCallback(async () => {
|
||||
const pageLayoutDraft = store.get(pageLayoutDraftCallbackState);
|
||||
const updateInput = convertPageLayoutDraftToUpdateInput(pageLayoutDraft);
|
||||
|
||||
if (result.status === 'successful') {
|
||||
const updatedPageLayout =
|
||||
result.response.data?.updatePageLayoutWithTabsAndWidgets;
|
||||
|
||||
if (isDefined(updatedPageLayout)) {
|
||||
const serverLayout: PageLayout =
|
||||
transformPageLayout(updatedPageLayout);
|
||||
|
||||
const pageLayoutToPersist =
|
||||
serverLayout.type === PageLayoutType.RECORD_PAGE
|
||||
? reInjectDynamicRelationWidgetsFromDraft(
|
||||
serverLayout,
|
||||
pageLayoutDraft,
|
||||
)
|
||||
: serverLayout;
|
||||
|
||||
set(pageLayoutPersistedCallbackState, pageLayoutToPersist);
|
||||
set(
|
||||
pageLayoutCurrentLayoutsCallbackState,
|
||||
convertPageLayoutToTabLayouts(pageLayoutToPersist),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
[
|
||||
pageLayoutCurrentLayoutsCallbackState,
|
||||
pageLayoutDraftCallbackState,
|
||||
const result = await updatePageLayoutWithTabsAndWidgets(
|
||||
pageLayoutId,
|
||||
pageLayoutPersistedCallbackState,
|
||||
updatePageLayoutWithTabsAndWidgets,
|
||||
],
|
||||
);
|
||||
updateInput,
|
||||
);
|
||||
|
||||
if (result.status === 'successful') {
|
||||
const updatedPageLayout =
|
||||
result.response.data?.updatePageLayoutWithTabsAndWidgets;
|
||||
|
||||
if (isDefined(updatedPageLayout)) {
|
||||
const serverLayout: PageLayout = transformPageLayout(updatedPageLayout);
|
||||
|
||||
const pageLayoutToPersist =
|
||||
serverLayout.type === PageLayoutType.RECORD_PAGE
|
||||
? reInjectDynamicRelationWidgetsFromDraft(
|
||||
serverLayout,
|
||||
pageLayoutDraft,
|
||||
)
|
||||
: serverLayout;
|
||||
|
||||
store.set(pageLayoutPersistedCallbackState, pageLayoutToPersist);
|
||||
store.set(
|
||||
pageLayoutCurrentLayoutsCallbackState,
|
||||
convertPageLayoutToTabLayouts(pageLayoutToPersist),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}, [
|
||||
pageLayoutCurrentLayoutsCallbackState,
|
||||
pageLayoutDraftCallbackState,
|
||||
pageLayoutId,
|
||||
pageLayoutPersistedCallbackState,
|
||||
updatePageLayoutWithTabsAndWidgets,
|
||||
store,
|
||||
]);
|
||||
|
||||
return { savePageLayout };
|
||||
};
|
||||
|
||||
+30
-23
@@ -1,13 +1,15 @@
|
||||
import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuComponentInstanceId';
|
||||
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
|
||||
import { isCommandMenuOpenedStateV2 } from '@/command-menu/states/isCommandMenuOpenedStateV2';
|
||||
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
|
||||
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
|
||||
import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
|
||||
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
|
||||
import { currentPageLayoutIdState } from '@/page-layout/states/currentPageLayoutIdState';
|
||||
import { isPageLayoutInEditModeComponentState } from '@/page-layout/states/isPageLayoutInEditModeComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export const useSetIsPageLayoutInEditMode = (pageLayoutIdFromProps: string) => {
|
||||
const pageLayoutId = useAvailableComponentInstanceIdOrThrow(
|
||||
@@ -15,43 +17,48 @@ export const useSetIsPageLayoutInEditMode = (pageLayoutIdFromProps: string) => {
|
||||
pageLayoutIdFromProps,
|
||||
);
|
||||
|
||||
const isPageLayoutInEditModeState = useRecoilComponentCallbackState(
|
||||
const isPageLayoutInEditModeState = useRecoilComponentStateCallbackStateV2(
|
||||
isPageLayoutInEditModeComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const contextStoreIsFullTabWidgetInEditModeState =
|
||||
useRecoilComponentCallbackState(
|
||||
useRecoilComponentStateCallbackStateV2(
|
||||
contextStoreIsPageInEditModeComponentState,
|
||||
MAIN_CONTEXT_STORE_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const setIsPageLayoutInEditMode = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
(value: boolean) => {
|
||||
set(isPageLayoutInEditModeState, value);
|
||||
const store = useStore();
|
||||
|
||||
set(contextStoreIsFullTabWidgetInEditModeState, value);
|
||||
const setIsPageLayoutInEditMode = useCallback(
|
||||
(value: boolean) => {
|
||||
store.set(isPageLayoutInEditModeState, value);
|
||||
|
||||
set(currentPageLayoutIdState, value ? pageLayoutId : null);
|
||||
store.set(contextStoreIsFullTabWidgetInEditModeState, value);
|
||||
|
||||
const isCommandMenuOpened = snapshot
|
||||
.getLoadable(isCommandMenuOpenedState)
|
||||
.getValue();
|
||||
jotaiStore.set(
|
||||
currentPageLayoutIdState.atom,
|
||||
value ? pageLayoutId : null,
|
||||
);
|
||||
|
||||
if (isCommandMenuOpened) {
|
||||
set(
|
||||
contextStoreIsPageInEditModeComponentState.atomFamily({
|
||||
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
|
||||
}),
|
||||
value,
|
||||
);
|
||||
}
|
||||
},
|
||||
const isCommandMenuOpened = jotaiStore.get(
|
||||
isCommandMenuOpenedStateV2.atom,
|
||||
);
|
||||
|
||||
if (isCommandMenuOpened) {
|
||||
store.set(
|
||||
contextStoreIsPageInEditModeComponentState.atomFamily({
|
||||
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
|
||||
}),
|
||||
value,
|
||||
);
|
||||
}
|
||||
},
|
||||
[
|
||||
isPageLayoutInEditModeState,
|
||||
contextStoreIsFullTabWidgetInEditModeState,
|
||||
pageLayoutId,
|
||||
store,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
+9
-10
@@ -1,7 +1,8 @@
|
||||
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { pageLayoutSelectedCellsComponentState } from '@/page-layout/states/pageLayoutSelectedCellsComponentState';
|
||||
|
||||
export const useStartPageLayoutDragSelection = (
|
||||
@@ -12,18 +13,16 @@ export const useStartPageLayoutDragSelection = (
|
||||
pageLayoutIdFromProps,
|
||||
);
|
||||
|
||||
const pageLayoutSelectedCellsState = useRecoilComponentCallbackState(
|
||||
const pageLayoutSelectedCellsState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutSelectedCellsComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const startPageLayoutDragSelection = useRecoilCallback(
|
||||
({ set }) =>
|
||||
() => {
|
||||
set(pageLayoutSelectedCellsState, new Set());
|
||||
},
|
||||
[pageLayoutSelectedCellsState],
|
||||
);
|
||||
const store = useStore();
|
||||
|
||||
const startPageLayoutDragSelection = useCallback(() => {
|
||||
store.set(pageLayoutSelectedCellsState, new Set());
|
||||
}, [pageLayoutSelectedCellsState, store]);
|
||||
|
||||
return { startPageLayoutDragSelection };
|
||||
};
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { type PageLayoutTab } from '@/page-layout/types/PageLayoutTab';
|
||||
|
||||
export const useUpdatePageLayoutTab = (pageLayoutIdFromProps?: string) => {
|
||||
@@ -11,22 +12,23 @@ export const useUpdatePageLayoutTab = (pageLayoutIdFromProps?: string) => {
|
||||
pageLayoutIdFromProps,
|
||||
);
|
||||
|
||||
const pageLayoutDraftState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraftState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const updatePageLayoutTab = useRecoilCallback(
|
||||
({ set }) =>
|
||||
(tabId: string, updates: Partial<PageLayoutTab>) => {
|
||||
set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: prev.tabs.map((tab) =>
|
||||
tab.id === tabId ? { ...tab, ...updates } : tab,
|
||||
),
|
||||
}));
|
||||
},
|
||||
[pageLayoutDraftState],
|
||||
const store = useStore();
|
||||
|
||||
const updatePageLayoutTab = useCallback(
|
||||
(tabId: string, updates: Partial<PageLayoutTab>) => {
|
||||
store.set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: prev.tabs.map((tab) =>
|
||||
tab.id === tabId ? { ...tab, ...updates } : tab,
|
||||
),
|
||||
}));
|
||||
},
|
||||
[pageLayoutDraftState, store],
|
||||
);
|
||||
|
||||
return { updatePageLayoutTab };
|
||||
|
||||
@@ -2,8 +2,9 @@ import { PageLayoutComponentInstanceContext } from '@/page-layout/states/context
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { useRecoilComponentStateCallbackStateV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilComponentStateCallbackStateV2';
|
||||
import { useStore } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export const useUpdatePageLayoutWidget = (pageLayoutIdFromProps?: string) => {
|
||||
const pageLayoutId = useAvailableComponentInstanceIdOrThrow(
|
||||
@@ -11,25 +12,26 @@ export const useUpdatePageLayoutWidget = (pageLayoutIdFromProps?: string) => {
|
||||
pageLayoutIdFromProps,
|
||||
);
|
||||
|
||||
const pageLayoutDraftState = useRecoilComponentCallbackState(
|
||||
const pageLayoutDraftState = useRecoilComponentStateCallbackStateV2(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const updatePageLayoutWidget = useRecoilCallback(
|
||||
({ set }) =>
|
||||
(widgetId: string, updates: Partial<PageLayoutWidget>) => {
|
||||
set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: prev.tabs.map((tab) => ({
|
||||
...tab,
|
||||
widgets: tab.widgets.map((widget) =>
|
||||
widget.id === widgetId ? { ...widget, ...updates } : widget,
|
||||
),
|
||||
})),
|
||||
}));
|
||||
},
|
||||
[pageLayoutDraftState],
|
||||
const store = useStore();
|
||||
|
||||
const updatePageLayoutWidget = useCallback(
|
||||
(widgetId: string, updates: Partial<PageLayoutWidget>) => {
|
||||
store.set(pageLayoutDraftState, (prev) => ({
|
||||
...prev,
|
||||
tabs: prev.tabs.map((tab) => ({
|
||||
...tab,
|
||||
widgets: tab.widgets.map((widget) =>
|
||||
widget.id === widgetId ? { ...widget, ...updates } : widget,
|
||||
),
|
||||
})),
|
||||
}));
|
||||
},
|
||||
[pageLayoutDraftState, store],
|
||||
);
|
||||
|
||||
return { updatePageLayoutWidget };
|
||||
|
||||
Reference in New Issue
Block a user