7338 refactor actionbar and contextmenu to use the context store (#7462)

Closes #7338
This commit is contained in:
Raphaël Bosi
2024-10-10 13:26:19 +02:00
committed by GitHub
parent 54c328a7e6
commit a7d5aa933d
84 changed files with 1481 additions and 954 deletions

View File

@ -0,0 +1,61 @@
import styled from '@emotion/styled';
import { useBottomBarInternalHotkeyScopeManagement } from '@/ui/layout/bottom-bar/hooks/useBottomBarInternalHotkeyScopeManagement';
import { BottomBarInstanceContext } from '@/ui/layout/bottom-bar/states/contexts/BottomBarInstanceContext';
import { isBottomBarOpenedComponentState } from '@/ui/layout/bottom-bar/states/isBottomBarOpenedComponentState';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
const StyledContainerActionBar = styled.div`
align-items: center;
background: ${({ theme }) => theme.background.secondary};
border: 1px solid ${({ theme }) => theme.border.color.medium};
border-radius: ${({ theme }) => theme.border.radius.md};
bottom: 38px;
box-shadow: ${({ theme }) => theme.boxShadow.strong};
display: flex;
height: 48px;
width: max-content;
left: 50%;
padding-left: ${({ theme }) => theme.spacing(2)};
padding-right: ${({ theme }) => theme.spacing(2)};
position: absolute;
top: auto;
transform: translateX(-50%);
z-index: 1;
`;
type BottomBarProps = {
bottomBarId: string;
bottomBarHotkeyScopeFromParent: HotkeyScope;
children: React.ReactNode;
};
export const BottomBar = ({
bottomBarId,
bottomBarHotkeyScopeFromParent,
children,
}: BottomBarProps) => {
const isBottomBarOpen = useRecoilComponentValueV2(
isBottomBarOpenedComponentState,
bottomBarId,
);
useBottomBarInternalHotkeyScopeManagement({
bottomBarId,
bottomBarHotkeyScopeFromParent,
});
if (!isBottomBarOpen) {
return null;
}
return (
<BottomBarInstanceContext.Provider value={{ instanceId: bottomBarId }}>
<StyledContainerActionBar data-select-disable className="bottom-bar">
{children}
</StyledContainerActionBar>
</BottomBarInstanceContext.Provider>
);
};

View File

@ -0,0 +1,65 @@
import { Meta, StoryObj } from '@storybook/react';
import { IconPlus } from 'twenty-ui';
import { Button } from '@/ui/input/button/components/Button';
import { BottomBar } from '@/ui/layout/bottom-bar/components/BottomBar';
import { isBottomBarOpenedComponentState } from '@/ui/layout/bottom-bar/states/isBottomBarOpenedComponentState';
import styled from '@emotion/styled';
import { RecoilRoot } from 'recoil';
const StyledContainer = styled.div`
display: flex;
gap: 10px;
`;
const meta: Meta<typeof BottomBar> = {
title: 'UI/Layout/BottomBar/BottomBar',
component: BottomBar,
args: {
bottomBarId: 'test',
bottomBarHotkeyScopeFromParent: { scope: 'test' },
children: (
<StyledContainer>
<Button title="Test 1" Icon={IconPlus} />
<Button title="Test 2" Icon={IconPlus} />
<Button title="Test 3" Icon={IconPlus} />
</StyledContainer>
),
},
argTypes: {
bottomBarId: { control: false },
bottomBarHotkeyScopeFromParent: { control: false },
children: { control: false },
},
};
export default meta;
export const Default: StoryObj<typeof BottomBar> = {
decorators: [
(Story) => (
<RecoilRoot
initializeState={({ set }) => {
set(
isBottomBarOpenedComponentState.atomFamily({
instanceId: 'test',
}),
true,
);
}}
>
<Story />
</RecoilRoot>
),
],
};
export const Closed: StoryObj<typeof BottomBar> = {
decorators: [
(Story) => (
<RecoilRoot>
<Story />
</RecoilRoot>
),
],
};

View File

@ -0,0 +1,87 @@
import { useRecoilCallback } from 'recoil';
import { bottomBarHotkeyComponentState } from '@/ui/layout/bottom-bar/states/bottomBarHotkeyComponentState';
import { isBottomBarOpenedComponentState } from '@/ui/layout/bottom-bar/states/isBottomBarOpenedComponentState';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { isDefined } from '~/utils/isDefined';
export const useBottomBar = () => {
const {
setHotkeyScopeAndMemorizePreviousScope,
goBackToPreviousHotkeyScope,
} = usePreviousHotkeyScope();
const closeBottomBar = useRecoilCallback(
({ set }) =>
(specificComponentId: string) => {
goBackToPreviousHotkeyScope();
set(
isBottomBarOpenedComponentState.atomFamily({
instanceId: specificComponentId,
}),
false,
);
},
[goBackToPreviousHotkeyScope],
);
const openBottomBar = useRecoilCallback(
({ set, snapshot }) =>
(specificComponentId: string, customHotkeyScope?: HotkeyScope) => {
const bottomBarHotkeyScope = snapshot
.getLoadable(
bottomBarHotkeyComponentState.atomFamily({
instanceId: specificComponentId,
}),
)
.getValue();
set(
isBottomBarOpenedComponentState.atomFamily({
instanceId: specificComponentId,
}),
true,
);
if (isDefined(customHotkeyScope)) {
setHotkeyScopeAndMemorizePreviousScope(
customHotkeyScope.scope,
customHotkeyScope.customScopes,
);
} else if (isDefined(bottomBarHotkeyScope)) {
setHotkeyScopeAndMemorizePreviousScope(
bottomBarHotkeyScope.scope,
bottomBarHotkeyScope.customScopes,
);
}
},
[setHotkeyScopeAndMemorizePreviousScope],
);
const toggleBottomBar = useRecoilCallback(
({ snapshot }) =>
(specificComponentId: string) => {
const isBottomBarOpen = snapshot
.getLoadable(
isBottomBarOpenedComponentState.atomFamily({
instanceId: specificComponentId,
}),
)
.getValue();
if (isBottomBarOpen) {
closeBottomBar(specificComponentId);
} else {
openBottomBar(specificComponentId);
}
},
[closeBottomBar, openBottomBar],
);
return {
closeBottomBar,
openBottomBar,
toggleBottomBar,
};
};

View File

@ -0,0 +1,27 @@
import { useEffect } from 'react';
import { bottomBarHotkeyComponentState } from '@/ui/layout/bottom-bar/states/bottomBarHotkeyComponentState';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
export const useBottomBarInternalHotkeyScopeManagement = ({
bottomBarId,
bottomBarHotkeyScopeFromParent,
}: {
bottomBarId?: string;
bottomBarHotkeyScopeFromParent?: HotkeyScope;
}) => {
const [bottomBarHotkeyScope, setBottomBarHotkeyScope] =
useRecoilComponentStateV2(bottomBarHotkeyComponentState, bottomBarId);
useEffect(() => {
if (!isDeeplyEqual(bottomBarHotkeyScopeFromParent, bottomBarHotkeyScope)) {
setBottomBarHotkeyScope(bottomBarHotkeyScopeFromParent);
}
}, [
bottomBarHotkeyScope,
bottomBarHotkeyScopeFromParent,
setBottomBarHotkeyScope,
]);
};

View File

@ -0,0 +1,11 @@
import { BottomBarInstanceContext } from '@/ui/layout/bottom-bar/states/contexts/BottomBarInstanceContext';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
export const bottomBarHotkeyComponentState = createComponentStateV2<
HotkeyScope | null | undefined
>({
key: 'bottomBarHotkeyComponentState',
defaultValue: null,
componentInstanceContext: BottomBarInstanceContext,
});

View File

@ -0,0 +1,3 @@
import { createComponentInstanceContext } from '@/ui/utilities/state/component-state/utils/createComponentInstanceContext';
export const BottomBarInstanceContext = createComponentInstanceContext();

View File

@ -0,0 +1,8 @@
import { BottomBarInstanceContext } from '@/ui/layout/bottom-bar/states/contexts/BottomBarInstanceContext';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
export const isBottomBarOpenedComponentState = createComponentStateV2<boolean>({
key: 'isBottomBarOpenedComponentState',
defaultValue: false,
componentInstanceContext: BottomBarInstanceContext,
});