diff --git a/front/src/modules/ui/board/components/BoardColumnMenu.tsx b/front/src/modules/ui/board/components/BoardColumnMenu.tsx index 00fbc6660..9ba79dbca 100644 --- a/front/src/modules/ui/board/components/BoardColumnMenu.tsx +++ b/front/src/modules/ui/board/components/BoardColumnMenu.tsx @@ -5,7 +5,6 @@ import { IconPencil } from '@tabler/icons-react'; import { DropdownMenu } from '@/ui/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuSelectableItem } from '@/ui/dropdown/components/DropdownMenuSelectableItem'; -import DropdownButton from '@/ui/filter-n-sort/components/DropdownButton'; import { icon } from '@/ui/theme/constants/icon'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; @@ -46,9 +45,7 @@ export function BoardColumnMenu({ {openMenu === 'actions' && ( setOpenMenu('title')}> - - - + Rename diff --git a/front/src/modules/ui/button/components/IconButtonGroup.tsx b/front/src/modules/ui/button/components/IconButtonGroup.tsx index 5fe4cbc32..030e2c820 100644 --- a/front/src/modules/ui/button/components/IconButtonGroup.tsx +++ b/front/src/modules/ui/button/components/IconButtonGroup.tsx @@ -12,7 +12,7 @@ const StyledIconButtonGroupContainer = styled.div` padding: ${({ theme }) => theme.spacing(0.5)}; `; -type IconButtonGroupProps = Omit, 'children'> & { +export type IconButtonGroupProps = Omit, 'children'> & { variant: IconButtonVariant; size: IconButtonSize; children: React.ReactElement | React.ReactElement[]; diff --git a/front/src/modules/ui/dropdown/components/DropdownMenuHeader.tsx b/front/src/modules/ui/dropdown/components/DropdownMenuHeader.tsx new file mode 100644 index 000000000..e35b14707 --- /dev/null +++ b/front/src/modules/ui/dropdown/components/DropdownMenuHeader.tsx @@ -0,0 +1,58 @@ +import { ComponentProps, ReactElement } from 'react'; +import styled from '@emotion/styled'; + +const StyledHeader = styled.li` + align-items: center; + color: ${({ theme }) => theme.font.color.primary}; + display: flex; + font-size: ${({ theme }) => theme.font.size.sm}; + font-weight: ${({ theme }) => theme.font.weight.medium}; + + padding: calc(${({ theme }) => theme.spacing(2)}) + calc(${({ theme }) => theme.spacing(2)}); + + user-select: none; + + ${({ onClick, theme }) => { + if (onClick) { + return ` + cursor: pointer; + + &:hover { + background: ${theme.background.transparent.light}; + } + `; + } + }} +`; + +const StyledStartIconWrapper = styled.span` + color: ${({ theme }) => theme.font.color.tertiary}; + display: inline-flex; + margin-right: ${({ theme }) => theme.spacing(2)}; +`; + +const StyledEndIconWrapper = styled(StyledStartIconWrapper)` + color: ${({ theme }) => theme.font.color.tertiary}; + display: inline-flex; + margin-left: auto; + margin-right: 0; +`; + +type DropdownMenuHeaderProps = ComponentProps<'li'> & { + startIcon?: ReactElement; + endIcon?: ReactElement; +}; + +export const DropdownMenuHeader = ({ + children, + startIcon, + endIcon, + ...props +}: DropdownMenuHeaderProps) => ( + + {startIcon && {startIcon}} + {children} + {endIcon && {endIcon}} + +); diff --git a/front/src/modules/ui/dropdown/components/DropdownMenuItem.tsx b/front/src/modules/ui/dropdown/components/DropdownMenuItem.tsx index 0f9ca0807..c060ef44f 100644 --- a/front/src/modules/ui/dropdown/components/DropdownMenuItem.tsx +++ b/front/src/modules/ui/dropdown/components/DropdownMenuItem.tsx @@ -1,8 +1,15 @@ +import { ComponentProps } from 'react'; import styled from '@emotion/styled'; +import { + IconButtonGroup, + type IconButtonGroupProps, +} from '@/ui/button/components/IconButtonGroup'; import { hoverBackground } from '@/ui/theme/constants/effects'; -export const DropdownMenuItem = styled.li` +const styledIconButtonGroupClassName = 'dropdown-menu-item-actions'; + +const StyledItem = styled.li` --horizontal-padding: ${({ theme }) => theme.spacing(1)}; --vertical-padding: ${({ theme }) => theme.spacing(2)}; @@ -25,6 +32,43 @@ export const DropdownMenuItem = styled.li` ${hoverBackground}; + position: relative; user-select: none; + width: calc(100% - 2 * var(--horizontal-padding)); + + &:hover { + .${styledIconButtonGroupClassName} { + display: flex; + } + } `; + +const StyledActions = styled(IconButtonGroup)` + display: none; + position: absolute; + right: ${({ theme }) => theme.spacing(1)}; +`; + +export type DropdownMenuItemProps = ComponentProps<'li'> & { + actions?: IconButtonGroupProps['children']; +}; + +export const DropdownMenuItem = ({ + actions, + children, + ...props +}: DropdownMenuItemProps) => ( + + {children} + {actions && ( + + {actions} + + )} + +); diff --git a/front/src/modules/ui/dropdown/components/DropdownMenuSubheader.tsx b/front/src/modules/ui/dropdown/components/DropdownMenuSubheader.tsx new file mode 100644 index 000000000..8f9d12712 --- /dev/null +++ b/front/src/modules/ui/dropdown/components/DropdownMenuSubheader.tsx @@ -0,0 +1,11 @@ +import styled from '@emotion/styled'; + +export const DropdownMenuSubheader = styled.div` + background-color: ${({ theme }) => theme.background.transparent.lighter}; + color: ${({ theme }) => theme.font.color.light}; + font-size: ${({ theme }) => theme.font.size.xxs}; + font-weight: ${({ theme }) => theme.font.weight.semiBold}; + padding: ${({ theme }) => `${theme.spacing(1)} ${theme.spacing(2)}`}; + text-transform: uppercase; + width: 100%; +`; diff --git a/front/src/modules/ui/dropdown/components/__stories__/DropdownMenu.stories.tsx b/front/src/modules/ui/dropdown/components/__stories__/DropdownMenu.stories.tsx index 9f0b4cca9..6526157d3 100644 --- a/front/src/modules/ui/dropdown/components/__stories__/DropdownMenu.stories.tsx +++ b/front/src/modules/ui/dropdown/components/__stories__/DropdownMenu.stories.tsx @@ -2,18 +2,21 @@ import { useState } from 'react'; import styled from '@emotion/styled'; import type { Meta, StoryObj } from '@storybook/react'; -import { IconPlus } from '@/ui/icon/index'; +import { IconButton } from '@/ui/button/components/IconButton'; +import { IconPlus, IconUser } from '@/ui/icon'; import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem'; import { Avatar } from '@/users/components/Avatar'; import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; import { DropdownMenu } from '../DropdownMenu'; import { DropdownMenuCheckableItem } from '../DropdownMenuCheckableItem'; +import { DropdownMenuHeader } from '../DropdownMenuHeader'; import { DropdownMenuItem } from '../DropdownMenuItem'; import { DropdownMenuItemsContainer } from '../DropdownMenuItemsContainer'; import { DropdownMenuSearch } from '../DropdownMenuSearch'; import { DropdownMenuSelectableItem } from '../DropdownMenuSelectableItem'; import { DropdownMenuSeparator } from '../DropdownMenuSeparator'; +import { DropdownMenuSubheader } from '../DropdownMenuSubheader'; const meta: Meta = { title: 'UI/Dropdown/DropdownMenu', @@ -59,47 +62,36 @@ const MenuAbsolutePositionWrapper = styled.div` width: fit-content; `; -const FakeMenuItemList = () => ( - <> - Company A - Company B - Company C - Person 2 - Company D - Person 1 - -); - const mockSelectArray = [ { id: '1', name: 'Company A', - avatarUrl: avatarUrl, + avatarUrl, }, { id: '2', name: 'Company B', - avatarUrl: avatarUrl, + avatarUrl, }, { id: '3', name: 'Company C', - avatarUrl: avatarUrl, + avatarUrl, }, { id: '4', name: 'Person 2', - avatarUrl: avatarUrl, + avatarUrl, }, { id: '5', name: 'Company D', - avatarUrl: avatarUrl, + avatarUrl, }, { id: '6', name: 'Person 1', - avatarUrl: avatarUrl, + avatarUrl, }, ]; @@ -189,12 +181,77 @@ export const SimpleMenuItem: Story = { render: (args) => ( - + {mockSelectArray.map(({ name }) => ( + {name} + ))} ), }; +export const WithHeaders: Story = { + ...WithContentBelow, + render: (args) => ( + + Header + + Subheader 1 + + {mockSelectArray.slice(0, 3).map(({ name }) => ( + {name} + ))} + + + Subheader 2 + + {mockSelectArray.slice(3).map(({ name }) => ( + {name} + ))} + + + ), +}; + +export const WithIcons: Story = { + ...WithContentBelow, + render: (args) => ( + + + {mockSelectArray.map(({ name }) => ( + + + {name} + + ))} + + + ), +}; + +export const WithActions: Story = { + ...WithContentBelow, + render: (args) => ( + + + {mockSelectArray.map(({ name }, index) => ( + } />, + } />, + ]} + > + {name} + + ))} + + + ), + parameters: { + pseudo: { hover: ['.hover'] }, + }, +}; + export const LoadingMenu: Story = { ...WithContentBelow, render: () => ( @@ -215,25 +272,9 @@ export const Search: Story = { - - - - ), -}; - -export const Button: Story = { - ...WithContentBelow, - render: (args) => ( - - - - -
Create new
-
-
- - - + {mockSelectArray.map(({ name }) => ( + {name} + ))}
), diff --git a/front/src/modules/ui/filter-n-sort/components/DropdownButton.tsx b/front/src/modules/ui/filter-n-sort/components/DropdownButton.tsx index 1d4094d88..c3e3861ed 100644 --- a/front/src/modules/ui/filter-n-sort/components/DropdownButton.tsx +++ b/front/src/modules/ui/filter-n-sort/components/DropdownButton.tsx @@ -2,7 +2,6 @@ import { ReactNode } from 'react'; import styled from '@emotion/styled'; import { Key } from 'ts-key-enum'; -import { IconChevronDown } from '@/ui/icon/index'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { FiltersHotkeyScope } from '../types/FiltersHotkeyScope'; @@ -50,29 +49,6 @@ const StyledDropdownButton = styled.div` } `; -const StyledDropdownTopOption = styled.li` - color: ${({ theme }) => theme.font.color.primary}; - cursor: pointer; - display: flex; - font-size: ${({ theme }) => theme.font.size.sm}; - font-weight: ${({ theme }) => theme.font.weight.medium}; - justify-content: space-between; - - padding: calc(${({ theme }) => theme.spacing(2)}) - calc(${({ theme }) => theme.spacing(2)}); - &:hover { - background: ${({ theme }) => theme.background.transparent.light}; - } - user-select: none; -`; - -const StyledIcon = styled.div` - display: flex; - justify-content: center; - margin-right: ${({ theme }) => theme.spacing(1)}; - min-width: ${({ theme }) => theme.spacing(4)}; -`; - function DropdownButton({ label, isActive, @@ -117,23 +93,4 @@ function DropdownButton({ ); } -const StyleAngleDownContainer = styled.div` - color: ${({ theme }) => theme.font.color.tertiary}; - display: flex; - height: 100%; - justify-content: center; - margin-left: auto; -`; - -function DropdownTopOptionAngleDown() { - return ( - - - - ); -} -DropdownButton.StyledDropdownTopOption = StyledDropdownTopOption; -DropdownButton.StyledDropdownTopOptionAngleDown = DropdownTopOptionAngleDown; -DropdownButton.StyledIcon = StyledIcon; - export default DropdownButton; diff --git a/front/src/modules/ui/filter-n-sort/components/FilterDropdownFilterSelect.tsx b/front/src/modules/ui/filter-n-sort/components/FilterDropdownFilterSelect.tsx index 0083e2528..00b8f6d13 100644 --- a/front/src/modules/ui/filter-n-sort/components/FilterDropdownFilterSelect.tsx +++ b/front/src/modules/ui/filter-n-sort/components/FilterDropdownFilterSelect.tsx @@ -13,8 +13,6 @@ import { filterDropdownSearchInputScopedState } from '../states/filterDropdownSe import { selectedOperandInDropdownScopedState } from '../states/selectedOperandInDropdownScopedState'; import { getOperandsForFilterType } from '../utils/getOperandsForFilterType'; -import DropdownButton from './DropdownButton'; - export function FilterDropdownFilterSelect({ context, }: { @@ -61,9 +59,7 @@ export function FilterDropdownFilterSelect({ setFilterDropdownSearchInput(''); }} > - - {availableFilter.icon} - + {availableFilter.icon} {availableFilter.label} ))} diff --git a/front/src/modules/ui/filter-n-sort/components/FilterDropdownOperandButton.tsx b/front/src/modules/ui/filter-n-sort/components/FilterDropdownOperandButton.tsx index c82cbd78f..d75d151f6 100644 --- a/front/src/modules/ui/filter-n-sort/components/FilterDropdownOperandButton.tsx +++ b/front/src/modules/ui/filter-n-sort/components/FilterDropdownOperandButton.tsx @@ -1,18 +1,21 @@ import { Context } from 'react'; +import { useTheme } from '@emotion/react'; +import { IconChevronDown } from '@tabler/icons-react'; +import { DropdownMenuHeader } from '@/ui/dropdown/components/DropdownMenuHeader'; import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; import { isFilterDropdownOperandSelectUnfoldedScopedState } from '../states/isFilterDropdownOperandSelectUnfoldedScopedState'; import { selectedOperandInDropdownScopedState } from '../states/selectedOperandInDropdownScopedState'; import { getOperandLabel } from '../utils/getOperandLabel'; -import DropdownButton from './DropdownButton'; - export function FilterDropdownOperandButton({ context, }: { context: Context; }) { + const theme = useTheme(); + const [selectedOperandInDropdown] = useRecoilScopedState( selectedOperandInDropdownScopedState, context, @@ -29,12 +32,12 @@ export function FilterDropdownOperandButton({ } return ( - } onClick={() => setIsOperandSelectionUnfolded(true)} > {getOperandLabel(selectedOperandInDropdown)} - - + ); } diff --git a/front/src/modules/ui/filter-n-sort/components/SortDropdownButton.tsx b/front/src/modules/ui/filter-n-sort/components/SortDropdownButton.tsx index ee8654fe4..363f3fd2d 100644 --- a/front/src/modules/ui/filter-n-sort/components/SortDropdownButton.tsx +++ b/front/src/modules/ui/filter-n-sort/components/SortDropdownButton.tsx @@ -1,5 +1,8 @@ import { useCallback, useState } from 'react'; +import { useTheme } from '@emotion/react'; +import { IconChevronDown } from '@tabler/icons-react'; +import { DropdownMenuHeader } from '@/ui/dropdown/components/DropdownMenuHeader'; import { DropdownMenuItemsContainer } from '@/ui/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuSelectableItem } from '@/ui/dropdown/components/DropdownMenuSelectableItem'; import { DropdownMenuSeparator } from '@/ui/dropdown/components/DropdownMenuSeparator'; @@ -24,10 +27,10 @@ export function SortDropdownButton({ onSortSelect, HotkeyScope, }: OwnProps) { + const theme = useTheme(); + const [isUnfolded, setIsUnfolded] = useState(false); - const [isOptionUnfolded, setIsOptionUnfolded] = useState(false); - const [selectedSortDirection, setSelectedSortDirection] = useState['order']>('asc'); @@ -76,13 +79,12 @@ export function SortDropdownButton({ ) : ( <> - } onClick={() => setIsOptionUnfolded(true)} > {selectedSortDirection === 'asc' ? 'Ascending' : 'Descending'} - - - + @@ -94,9 +96,7 @@ export function SortDropdownButton({ onSortItemSelect(sort); }} > - - {sort.icon} - + {sort.icon} {sort.label} ))} diff --git a/front/src/modules/ui/icon/index.ts b/front/src/modules/ui/icon/index.ts index d5e23d09e..a13b69b26 100644 --- a/front/src/modules/ui/icon/index.ts +++ b/front/src/modules/ui/icon/index.ts @@ -51,3 +51,4 @@ export { IconUserCircle } from '@tabler/icons-react'; export { IconCalendar } from '@tabler/icons-react'; export { IconPencil } from '@tabler/icons-react'; export { IconCircleDot } from '@tabler/icons-react'; +export { IconTag } from '@tabler/icons-react'; diff --git a/front/src/modules/ui/table/components/EntityTableColumnMenu.tsx b/front/src/modules/ui/table/components/EntityTableColumnMenu.tsx index 244506674..522b2caf5 100644 --- a/front/src/modules/ui/table/components/EntityTableColumnMenu.tsx +++ b/front/src/modules/ui/table/components/EntityTableColumnMenu.tsx @@ -1,9 +1,8 @@ -import React, { ComponentProps, useRef } from 'react'; +import { cloneElement, ComponentProps, useRef } from 'react'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { IconButton } from '@/ui/button/components/IconButton'; -import { IconButtonGroup } from '@/ui/button/components/IconButtonGroup'; import { DropdownMenu } from '@/ui/dropdown/components/DropdownMenu'; import { DropdownMenuItem } from '@/ui/dropdown/components/DropdownMenuItem'; import { DropdownMenuItemsContainer } from '@/ui/dropdown/components/DropdownMenuItemsContainer'; @@ -19,23 +18,6 @@ const StyledColumnMenu = styled(DropdownMenu)` font-weight: ${({ theme }) => theme.font.weight.regular}; `; -const StyledIconButtonGroup = styled(IconButtonGroup)` - display: none; - position: absolute; - right: ${({ theme }) => theme.spacing(1)}; -`; -const styledIconButtonGroupClassName = 'column-menu-item-icon-button-group'; - -const StyledColumnMenuItem = styled(DropdownMenuItem)` - position: relative; - - &:hover { - .${styledIconButtonGroupClassName} { - display: flex; - } - } -`; - type EntityTableColumnMenuProps = { onAddViewField: ( viewFieldDefinition: ViewFieldDefinition, @@ -62,23 +44,21 @@ export const EntityTableColumnMenu = ({ {viewFieldDefinitions.map((viewFieldDefinition) => ( - - {viewFieldDefinition.columnIcon && - React.cloneElement(viewFieldDefinition.columnIcon, { - size: theme.icon.size.md, - })} - {viewFieldDefinition.columnLabel} - + } onClick={() => onAddViewField(viewFieldDefinition)} /> - - + } + > + {viewFieldDefinition.columnIcon && + cloneElement(viewFieldDefinition.columnIcon, { + size: theme.icon.size.md, + })} + {viewFieldDefinition.columnLabel} + ))} diff --git a/front/src/modules/ui/table/components/EntityTableHeader.tsx b/front/src/modules/ui/table/components/EntityTableHeader.tsx index 02f212d08..6edbccad5 100644 --- a/front/src/modules/ui/table/components/EntityTableHeader.tsx +++ b/front/src/modules/ui/table/components/EntityTableHeader.tsx @@ -19,6 +19,7 @@ import { addableViewFieldDefinitionsState, columnWidthByViewFieldIdState, viewFieldsState, + visibleViewFieldsState, } from '../states/viewFieldsState'; import type { ViewFieldDefinition, @@ -87,8 +88,8 @@ const StyledEntityTableColumnMenu = styled(EntityTableColumnMenu)` export function EntityTableHeader() { const theme = useTheme(); - const [{ objectName, viewFields }, setViewFieldsState] = - useRecoilState(viewFieldsState); + const [{ objectName }, setViewFieldsState] = useRecoilState(viewFieldsState); + const viewFields = useRecoilValue(visibleViewFieldsState); const columnWidths = useRecoilValue(columnWidthByViewFieldIdState); const addableViewFieldDefinitions = useRecoilValue( addableViewFieldDefinitionsState, diff --git a/front/src/modules/ui/table/components/EntityTableRow.tsx b/front/src/modules/ui/table/components/EntityTableRow.tsx index fdccf9379..503900110 100644 --- a/front/src/modules/ui/table/components/EntityTableRow.tsx +++ b/front/src/modules/ui/table/components/EntityTableRow.tsx @@ -2,7 +2,7 @@ import styled from '@emotion/styled'; import { useRecoilValue } from 'recoil'; import { ViewFieldContext } from '../states/ViewFieldContext'; -import { viewFieldsState } from '../states/viewFieldsState'; +import { visibleViewFieldsState } from '../states/viewFieldsState'; import { CheckboxCell } from './CheckboxCell'; import { EntityTableCell } from './EntityTableCell'; @@ -13,7 +13,7 @@ const StyledRow = styled.tr<{ selected: boolean }>` `; export function EntityTableRow({ rowId }: { rowId: string }) { - const { viewFields } = useRecoilValue(viewFieldsState); + const viewFields = useRecoilValue(visibleViewFieldsState); return ( @@ -22,10 +22,7 @@ export function EntityTableRow({ rowId }: { rowId: string }) { {viewFields.map((viewField, columnIndex) => { return ( - + ); diff --git a/front/src/modules/ui/table/hooks/useLoadView.ts b/front/src/modules/ui/table/hooks/useLoadView.ts index 900908c88..2030f9cc9 100644 --- a/front/src/modules/ui/table/hooks/useLoadView.ts +++ b/front/src/modules/ui/table/hooks/useLoadView.ts @@ -28,7 +28,7 @@ export const toViewFieldInput = ( ) => ({ fieldName: viewFieldDefinition.columnLabel, index: viewFieldDefinition.columnOrder, - isVisible: true, + isVisible: viewFieldDefinition.isVisible ?? true, objectName, sizeInPx: viewFieldDefinition.columnSize, }); @@ -64,6 +64,7 @@ export const useLoadView = ({ columnLabel: viewField.fieldName, columnOrder: viewField.index, columnSize: viewField.sizeInPx, + isVisible: viewField.isVisible, })); setViewFieldsState({ objectName, viewFields }); diff --git a/front/src/modules/ui/table/states/viewFieldsState.ts b/front/src/modules/ui/table/states/viewFieldsState.ts index 327857372..47f1a007d 100644 --- a/front/src/modules/ui/table/states/viewFieldsState.ts +++ b/front/src/modules/ui/table/states/viewFieldsState.ts @@ -47,3 +47,15 @@ export const addableViewFieldDefinitionsState = selector({ ); }, }); + +export const visibleViewFieldsState = selector({ + key: 'visibleViewFieldsState', + get: ({ get }) => + get(viewFieldsState).viewFields.filter((viewField) => viewField.isVisible), +}); + +export const hiddenViewFieldsState = selector({ + key: 'hiddenViewFieldsState', + get: ({ get }) => + get(viewFieldsState).viewFields.filter((viewField) => !viewField.isVisible), +}); diff --git a/front/src/modules/ui/table/table-header/components/TableHeader.tsx b/front/src/modules/ui/table/table-header/components/TableHeader.tsx index dc17b0773..193479411 100644 --- a/front/src/modules/ui/table/table-header/components/TableHeader.tsx +++ b/front/src/modules/ui/table/table-header/components/TableHeader.tsx @@ -7,6 +7,7 @@ import { SortDropdownButton } from '@/ui/filter-n-sort/components/SortDropdownBu import { FiltersHotkeyScope } from '@/ui/filter-n-sort/types/FiltersHotkeyScope'; import { SelectedSortType, SortType } from '@/ui/filter-n-sort/types/interface'; import { TopBar } from '@/ui/top-bar/TopBar'; +import { OptionsDropdownButton } from '@/views/components/OptionsDropdownButton'; import { TableContext } from '../../states/TableContext'; @@ -76,6 +77,9 @@ export function TableHeader({ onSortSelect={sortSelect} HotkeyScope={FiltersHotkeyScope.FilterDropdownButton} /> + } bottomComponent={ diff --git a/front/src/modules/ui/table/types/ViewField.ts b/front/src/modules/ui/table/types/ViewField.ts index d5af391dd..eaa6d06fd 100644 --- a/front/src/modules/ui/table/types/ViewField.ts +++ b/front/src/modules/ui/table/types/ViewField.ts @@ -91,6 +91,7 @@ export type ViewFieldDefinition = { columnOrder: number; columnIcon?: JSX.Element; filterIcon?: JSX.Element; + isVisible?: boolean; metadata: T; }; diff --git a/front/src/modules/views/components/OptionsDropdownButton.tsx b/front/src/modules/views/components/OptionsDropdownButton.tsx new file mode 100644 index 000000000..8400b005a --- /dev/null +++ b/front/src/modules/views/components/OptionsDropdownButton.tsx @@ -0,0 +1,137 @@ +import { useCallback, useState } from 'react'; +import { getOperationName } from '@apollo/client/utilities'; +import { useTheme } from '@emotion/react'; +import { useRecoilValue } from 'recoil'; + +import { IconButton } from '@/ui/button/components/IconButton'; +import { DropdownMenuHeader } from '@/ui/dropdown/components/DropdownMenuHeader'; +import { DropdownMenuItem } from '@/ui/dropdown/components/DropdownMenuItem'; +import { DropdownMenuItemsContainer } from '@/ui/dropdown/components/DropdownMenuItemsContainer'; +import { DropdownMenuSeparator } from '@/ui/dropdown/components/DropdownMenuSeparator'; +import DropdownButton from '@/ui/filter-n-sort/components/DropdownButton'; +import { FiltersHotkeyScope } from '@/ui/filter-n-sort/types/FiltersHotkeyScope'; +import { IconChevronLeft, IconMinus, IconPlus, IconTag } from '@/ui/icon'; +import { + hiddenViewFieldsState, + visibleViewFieldsState, +} from '@/ui/table/states/viewFieldsState'; +import { + ViewFieldDefinition, + ViewFieldMetadata, +} from '@/ui/table/types/ViewField'; +import { useUpdateViewFieldMutation } from '~/generated/graphql'; + +import { GET_VIEW_FIELDS } from '../queries/select'; + +import { OptionsDropdownSection } from './OptionsDropdownSection'; + +type OptionsDropdownButtonProps = { + HotkeyScope: FiltersHotkeyScope; +}; + +enum Option { + Properties = 'Properties', +} + +export const OptionsDropdownButton = ({ + HotkeyScope, +}: OptionsDropdownButtonProps) => { + const theme = useTheme(); + const [isUnfolded, setIsUnfolded] = useState(false); + const [selectedOption, setSelectedOption] = useState