Refactor UI folder (#2016)
* Added Overview page * Revised Getting Started page * Minor revision * Edited readme, minor modifications to docs * Removed sweep.yaml, .devcontainer, .ergomake * Moved security.md to .github, added contributing.md * changes as per code review * updated contributing.md * fixed broken links & added missing links in doc, improved structure * fixed link in wsl setup * fixed server link, added https cloning in yarn-setup * removed package-lock.json * added doc card, admonitions * removed underline from nav buttons * refactoring modules/ui * refactoring modules/ui * Change folder case * Fix theme location * Fix case 2 * Fix storybook --------- Co-authored-by: Nimra Ahmed <nimra1408@gmail.com> Co-authored-by: Nimra Ahmed <50912134+nimraahmed@users.noreply.github.com>
This commit is contained in:
@ -0,0 +1,256 @@
|
||||
import { useContext, useRef, useState } from 'react';
|
||||
import {
|
||||
useRecoilCallback,
|
||||
useRecoilState,
|
||||
useRecoilValue,
|
||||
useResetRecoilState,
|
||||
} from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { BoardContext } from '@/companies/states/contexts/BoardContext';
|
||||
import { ViewFieldsVisibilityDropdownSection } from '@/ui/data/view-bar/components/ViewFieldsVisibilityDropdownSection';
|
||||
import { useUpsertView } from '@/ui/data/view-bar/hooks/useUpsertView';
|
||||
import { currentViewScopedSelector } from '@/ui/data/view-bar/states/selectors/currentViewScopedSelector';
|
||||
import { viewsByIdScopedSelector } from '@/ui/data/view-bar/states/selectors/viewsByIdScopedSelector';
|
||||
import { viewEditModeState } from '@/ui/data/view-bar/states/viewEditModeState';
|
||||
import {
|
||||
IconChevronLeft,
|
||||
IconLayoutKanban,
|
||||
IconPlus,
|
||||
IconTag,
|
||||
} from '@/ui/display/icon';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
|
||||
import { DropdownMenuInput } from '@/ui/layout/dropdown/components/DropdownMenuInput';
|
||||
import { DropdownMenuInputContainer } from '@/ui/layout/dropdown/components/DropdownMenuInputContainer';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||
import { StyledDropdownMenu } from '@/ui/layout/dropdown/components/StyledDropdownMenu';
|
||||
import { StyledDropdownMenuSeparator } from '@/ui/layout/dropdown/components/StyledDropdownMenuSeparator';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { MenuItemNavigate } from '@/ui/navigation/menu-item/components/MenuItemNavigate';
|
||||
import { ThemeColor } from '@/ui/theme/constants/colors';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
|
||||
|
||||
import { useBoardCardFields } from '../hooks/useBoardCardFields';
|
||||
import { boardCardFieldsScopedState } from '../states/boardCardFieldsScopedState';
|
||||
import { boardColumnsState } from '../states/boardColumnsState';
|
||||
import { savedBoardCardFieldsFamilyState } from '../states/savedBoardCardFieldsFamilyState';
|
||||
import { hiddenBoardCardFieldsScopedSelector } from '../states/selectors/hiddenBoardCardFieldsScopedSelector';
|
||||
import { visibleBoardCardFieldsScopedSelector } from '../states/selectors/visibleBoardCardFieldsScopedSelector';
|
||||
import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
|
||||
|
||||
export type BoardOptionsDropdownContentProps = {
|
||||
customHotkeyScope: HotkeyScope;
|
||||
onStageAdd?: (boardColumn: BoardColumnDefinition) => void;
|
||||
};
|
||||
|
||||
type BoardOptionsMenu = 'fields' | 'stage-creation' | 'stages';
|
||||
|
||||
type ColumnForCreate = {
|
||||
id: string;
|
||||
colorCode: ThemeColor;
|
||||
index: number;
|
||||
title: string;
|
||||
};
|
||||
|
||||
export const BoardOptionsDropdownContent = ({
|
||||
customHotkeyScope,
|
||||
onStageAdd,
|
||||
}: BoardOptionsDropdownContentProps) => {
|
||||
const { BoardRecoilScopeContext } = useContext(BoardContext);
|
||||
|
||||
const boardRecoilScopeId = useRecoilScopeId(BoardRecoilScopeContext);
|
||||
|
||||
const stageInputRef = useRef<HTMLInputElement>(null);
|
||||
const viewEditInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const [currentMenu, setCurrentMenu] = useState<
|
||||
BoardOptionsMenu | undefined
|
||||
>();
|
||||
|
||||
const [boardColumns, setBoardColumns] = useRecoilState(boardColumnsState);
|
||||
|
||||
const hiddenBoardCardFields = useRecoilScopedValue(
|
||||
hiddenBoardCardFieldsScopedSelector,
|
||||
BoardRecoilScopeContext,
|
||||
);
|
||||
const hasHiddenFields = hiddenBoardCardFields.length > 0;
|
||||
const visibleBoardCardFields = useRecoilScopedValue(
|
||||
visibleBoardCardFieldsScopedSelector,
|
||||
BoardRecoilScopeContext,
|
||||
);
|
||||
const hasVisibleFields = visibleBoardCardFields.length > 0;
|
||||
|
||||
const viewsById = useRecoilScopedValue(
|
||||
viewsByIdScopedSelector,
|
||||
BoardRecoilScopeContext, // TODO: replace with ViewBarRecoilScopeContext
|
||||
);
|
||||
const currentView = useRecoilScopedValue(
|
||||
currentViewScopedSelector,
|
||||
BoardRecoilScopeContext,
|
||||
);
|
||||
const viewEditMode = useRecoilValue(viewEditModeState);
|
||||
const resetViewEditMode = useResetRecoilState(viewEditModeState);
|
||||
|
||||
const handleStageSubmit = () => {
|
||||
if (currentMenu !== 'stage-creation' || !stageInputRef?.current?.value)
|
||||
return;
|
||||
|
||||
const columnToCreate: ColumnForCreate = {
|
||||
id: v4(),
|
||||
colorCode: 'gray',
|
||||
index: boardColumns.length,
|
||||
title: stageInputRef.current.value,
|
||||
};
|
||||
|
||||
setBoardColumns((previousBoardColumns) => [
|
||||
...previousBoardColumns,
|
||||
columnToCreate,
|
||||
]);
|
||||
onStageAdd?.(columnToCreate);
|
||||
};
|
||||
|
||||
const { upsertView } = useUpsertView();
|
||||
|
||||
const handleViewNameSubmit = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async () => {
|
||||
const boardCardFields = await snapshot.getPromise(
|
||||
boardCardFieldsScopedState(boardRecoilScopeId),
|
||||
);
|
||||
const isCreateMode = viewEditMode.mode === 'create';
|
||||
const name = viewEditInputRef.current?.value;
|
||||
const view = await upsertView(name);
|
||||
|
||||
if (view && isCreateMode) {
|
||||
set(savedBoardCardFieldsFamilyState(view.id), boardCardFields);
|
||||
}
|
||||
},
|
||||
[boardRecoilScopeId, upsertView, viewEditMode.mode],
|
||||
);
|
||||
|
||||
const resetMenu = () => setCurrentMenu(undefined);
|
||||
|
||||
const handleMenuNavigate = (menu: BoardOptionsMenu) => {
|
||||
handleViewNameSubmit();
|
||||
setCurrentMenu(menu);
|
||||
};
|
||||
|
||||
const { handleFieldVisibilityChange } = useBoardCardFields();
|
||||
|
||||
const { closeDropdown } = useDropdown();
|
||||
|
||||
useScopedHotkeys(
|
||||
Key.Escape,
|
||||
() => {
|
||||
resetViewEditMode();
|
||||
closeDropdown();
|
||||
},
|
||||
customHotkeyScope.scope,
|
||||
);
|
||||
|
||||
useScopedHotkeys(
|
||||
Key.Enter,
|
||||
() => {
|
||||
handleStageSubmit();
|
||||
handleViewNameSubmit();
|
||||
resetViewEditMode();
|
||||
closeDropdown();
|
||||
},
|
||||
customHotkeyScope.scope,
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledDropdownMenu>
|
||||
{!currentMenu && (
|
||||
<>
|
||||
<DropdownMenuInputContainer>
|
||||
<DropdownMenuInput
|
||||
ref={viewEditInputRef}
|
||||
autoFocus={
|
||||
viewEditMode.mode === 'create' || !!viewEditMode.viewId
|
||||
}
|
||||
placeholder={
|
||||
viewEditMode.mode === 'create' ? 'New view' : 'View name'
|
||||
}
|
||||
defaultValue={
|
||||
viewEditMode.mode === 'create'
|
||||
? ''
|
||||
: viewEditMode.viewId
|
||||
? viewsById[viewEditMode.viewId]?.name
|
||||
: currentView?.name
|
||||
}
|
||||
/>
|
||||
</DropdownMenuInputContainer>
|
||||
<StyledDropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItemNavigate
|
||||
onClick={() => handleMenuNavigate('fields')}
|
||||
LeftIcon={IconTag}
|
||||
text="Fields"
|
||||
/>
|
||||
<MenuItemNavigate
|
||||
onClick={() => handleMenuNavigate('stages')}
|
||||
LeftIcon={IconLayoutKanban}
|
||||
text="Stages"
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
)}
|
||||
{currentMenu === 'stages' && (
|
||||
<>
|
||||
<DropdownMenuHeader StartIcon={IconChevronLeft} onClick={resetMenu}>
|
||||
Stages
|
||||
</DropdownMenuHeader>
|
||||
<StyledDropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
onClick={() => setCurrentMenu('stage-creation')}
|
||||
LeftIcon={IconPlus}
|
||||
text="Add stage"
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
)}
|
||||
{currentMenu === 'stage-creation' && (
|
||||
<DropdownMenuSearchInput
|
||||
autoFocus
|
||||
placeholder="New stage"
|
||||
ref={stageInputRef}
|
||||
/>
|
||||
)}
|
||||
{currentMenu === 'fields' && (
|
||||
<>
|
||||
<DropdownMenuHeader StartIcon={IconChevronLeft} onClick={resetMenu}>
|
||||
Fields
|
||||
</DropdownMenuHeader>
|
||||
<StyledDropdownMenuSeparator />
|
||||
{hasVisibleFields && (
|
||||
<ViewFieldsVisibilityDropdownSection
|
||||
title="Visible"
|
||||
fields={visibleBoardCardFields}
|
||||
onVisibilityChange={handleFieldVisibilityChange}
|
||||
isDraggable={true}
|
||||
/>
|
||||
)}
|
||||
{hasVisibleFields && hasHiddenFields && (
|
||||
<StyledDropdownMenuSeparator />
|
||||
)}
|
||||
{hasHiddenFields && (
|
||||
<ViewFieldsVisibilityDropdownSection
|
||||
title="Hidden"
|
||||
fields={hiddenBoardCardFields}
|
||||
onVisibilityChange={handleFieldVisibilityChange}
|
||||
isDraggable={false}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</StyledDropdownMenu>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user