mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-06 22:40:14 -05:00
Added SortMenu component to the design system (#19045)
refs https://github.com/TryGhost/Product/issues/4162 - this component is based on the existing PopOver component, and customized for sorting function
This commit is contained in:
parent
a398067159
commit
63f0265e28
3 changed files with 110 additions and 0 deletions
32
apps/admin-x-design-system/src/global/SortMenu.stories.tsx
Normal file
32
apps/admin-x-design-system/src/global/SortMenu.stories.tsx
Normal file
|
@ -0,0 +1,32 @@
|
|||
import type {Meta, StoryObj} from '@storybook/react';
|
||||
|
||||
import SortMenu from './SortMenu';
|
||||
|
||||
const meta = {
|
||||
title: 'Global / SortMenu',
|
||||
component: SortMenu,
|
||||
tags: ['autodocs']
|
||||
} satisfies Meta<typeof SortMenu>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof SortMenu>;
|
||||
|
||||
const items = [
|
||||
{id: 'date-added', label: 'Date added', selected: true},
|
||||
{id: 'name', label: 'Name'},
|
||||
{id: 'redemptions', label: 'Redemptions'}
|
||||
];
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
items: items,
|
||||
onSortChange: () => {},
|
||||
onDirectionChange: () => {},
|
||||
position: 'left'
|
||||
},
|
||||
decorators: [
|
||||
ThisStory => (
|
||||
<div style={{maxWidth: '100px', margin: '0 auto'}}><ThisStory /></div>
|
||||
)
|
||||
]
|
||||
};
|
76
apps/admin-x-design-system/src/global/SortMenu.tsx
Normal file
76
apps/admin-x-design-system/src/global/SortMenu.tsx
Normal file
|
@ -0,0 +1,76 @@
|
|||
import React, {useState, useEffect} from 'react';
|
||||
import Button, {ButtonProps} from './Button';
|
||||
import Popover, {PopoverPosition} from './Popover';
|
||||
import {Icon} from '..';
|
||||
|
||||
export type SortItem = {
|
||||
id: string,
|
||||
label: string;
|
||||
selected?: boolean;
|
||||
}
|
||||
|
||||
export interface SortMenuProps {
|
||||
items: SortItem[];
|
||||
direction: string;
|
||||
onSortChange: (selectedOption: string) => void;
|
||||
onDirectionChange: (selectedDirection: string) => void;
|
||||
trigger?: React.ReactNode;
|
||||
triggerButtonProps?: ButtonProps;
|
||||
position?: PopoverPosition;
|
||||
}
|
||||
|
||||
const SortMenu: React.FC<SortMenuProps> = ({
|
||||
items,
|
||||
direction,
|
||||
onSortChange,
|
||||
onDirectionChange,
|
||||
trigger,
|
||||
triggerButtonProps,
|
||||
position = 'left'
|
||||
}) => {
|
||||
const [localItems, setLocalItems] = useState<SortItem[]>(items);
|
||||
const [localDirection, setLocalDirection] = useState<string>(direction || 'desc');
|
||||
|
||||
useEffect(() => {
|
||||
setLocalItems(items);
|
||||
}, [items]);
|
||||
|
||||
const handleSortChange = (selectedValue: string) => {
|
||||
const updatedItems = localItems.map(item => ({
|
||||
...item,
|
||||
selected: item.id === selectedValue ? true : false
|
||||
}));
|
||||
setLocalItems(updatedItems);
|
||||
|
||||
onSortChange(selectedValue);
|
||||
};
|
||||
|
||||
const handleSortDirection = () => {
|
||||
setLocalDirection(currentDirection => (currentDirection === 'desc' ? 'asc' : 'desc'));
|
||||
onDirectionChange(localDirection);
|
||||
};
|
||||
|
||||
if (!trigger) {
|
||||
trigger = <Button className='flex-row-reverse' icon={`${localDirection === 'asc' ? 'arrow-up' : 'arrow-down'}`} iconColorClass='!w-3 !h-3 !mr-0 ml-1.5' label={`${localItems.find(item => item.selected)?.label}`} {...triggerButtonProps} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Popover position={position} trigger={trigger}>
|
||||
<div className="flex min-w-[160px] flex-col justify-stretch py-1" role="none">
|
||||
{localItems.map(item => (
|
||||
<button key={item.id} className="group relative mx-1 flex grow cursor-pointer items-center rounded-[2.5px] px-8 py-1.5 pr-12 text-left text-sm hover:bg-grey-100 dark:hover:bg-grey-800" type="button" onClick={() => {
|
||||
handleSortChange(item.id);
|
||||
}}>
|
||||
{item.selected ? <Icon className='absolute left-2' name='check' size='sm' /> : null}
|
||||
{item.label}
|
||||
{item.selected ? <button className='absolute right-2 flex h-6 w-6 cursor-pointer items-center justify-center rounded-full bg-grey-200 opacity-0 group-hover:bg-grey-300 group-hover:opacity-100' title={`${localDirection === 'asc' ? 'Ascending' : 'Descending'}`} type='button' onClick={handleSortDirection}>
|
||||
{localDirection === 'asc' ? <Icon name='arrow-up' size='xs' /> : <Icon name='arrow-down' size='xs' />}
|
||||
</button> : null}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
export default SortMenu;
|
|
@ -108,6 +108,8 @@ export {default as Separator} from './global/Separator';
|
|||
export type {SeparatorProps} from './global/Separator';
|
||||
export {DragIndicator, default as SortableList} from './global/SortableList';
|
||||
export type {DragIndicatorProps, SortableItemContainerProps, SortableListProps} from './global/SortableList';
|
||||
export {default as SortMenu} from './global/SortMenu';
|
||||
export type {SortMenuProps} from './global/SortMenu';
|
||||
export {default as StickyFooter} from './global/StickyFooter';
|
||||
export type {StickyFooterProps} from './global/StickyFooter';
|
||||
export {default as TabView} from './global/TabView';
|
||||
|
|
Loading…
Reference in a new issue