mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-27 10:42:45 +03:00
Added basic list to Admin Design System
refs. https://github.com/TryGhost/Team/issues/3150
This commit is contained in:
parent
2e0073f9ec
commit
950fc832e4
@ -0,0 +1,40 @@
|
||||
import type {Meta, StoryObj} from '@storybook/react';
|
||||
|
||||
import * as ListItemStories from './ListItem.stories';
|
||||
import List from './List';
|
||||
import ListItem from './ListItem';
|
||||
|
||||
const meta = {
|
||||
title: 'Global / List',
|
||||
component: List,
|
||||
tags: ['autodocs'],
|
||||
decorators: [(_story: any) => (<div style={{maxWidth: '600px'}}>{_story()}</div>)]
|
||||
} satisfies Meta<typeof List>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof List>;
|
||||
|
||||
const listItemProps = {
|
||||
title: ListItemStories.HiddenActions.args?.title,
|
||||
detail: ListItemStories.HiddenActions.args?.detail,
|
||||
action: ListItemStories.HiddenActions.args?.action,
|
||||
hideActions: ListItemStories.HiddenActions.args?.hideActions,
|
||||
separator: true,
|
||||
onClick: ListItemStories.HiddenActions.args?.onClick
|
||||
};
|
||||
|
||||
const listItems = (
|
||||
<>
|
||||
<ListItem id='list-item-1' {...listItemProps}/>
|
||||
<ListItem id='list-item-2' {...listItemProps}/>
|
||||
<ListItem id='list-item-3' {...listItemProps}/>
|
||||
</>
|
||||
);
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
title: 'This is a list',
|
||||
children: listItems,
|
||||
hint: 'And here is a hint for the whole list'
|
||||
}
|
||||
};
|
39
ghost/admin-x-settings/src/admin-x-ds/global/List.tsx
Normal file
39
ghost/admin-x-settings/src/admin-x-ds/global/List.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
import Heading from './Heading';
|
||||
import Hint from './Hint';
|
||||
import React from 'react';
|
||||
import Separator from './Separator';
|
||||
|
||||
interface ListProps {
|
||||
title?: React.ReactNode;
|
||||
titleSeparator?: boolean;
|
||||
children?: React.ReactNode;
|
||||
hint?: React.ReactNode;
|
||||
hintSeparator?: boolean;
|
||||
}
|
||||
|
||||
const List: React.FC<ListProps> = ({title, titleSeparator, children, hint, hintSeparator}) => {
|
||||
titleSeparator = (titleSeparator === undefined) ? true : titleSeparator;
|
||||
hintSeparator = (hintSeparator === undefined) ? true : hintSeparator;
|
||||
|
||||
return (
|
||||
<section>
|
||||
{title &&
|
||||
<div className='flex flex-col gap-1'>
|
||||
<Heading grey={true} level={6}>{title}</Heading>
|
||||
{titleSeparator && <Separator />}
|
||||
</div>
|
||||
}
|
||||
<div className='flex flex-col'>
|
||||
{children}
|
||||
</div>
|
||||
{hint &&
|
||||
<>
|
||||
{hintSeparator && <Separator />}
|
||||
<Hint>{hint}</Hint>
|
||||
</>
|
||||
}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default List;
|
@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
import type {Meta, StoryObj} from '@storybook/react';
|
||||
|
||||
import Button from './Button';
|
||||
import ListItem from './ListItem';
|
||||
|
||||
const meta = {
|
||||
title: 'Global / List / List Item',
|
||||
component: ListItem,
|
||||
tags: ['autodocs'],
|
||||
decorators: [(_story: any) => (<div style={{maxWidth: '600px'}}>{_story()}</div>)],
|
||||
argTypes: {
|
||||
title: {control: 'text'},
|
||||
detail: {control: 'text'}
|
||||
}
|
||||
} satisfies Meta<typeof ListItem>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof ListItem>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
id: 'list-item',
|
||||
title: 'A list item',
|
||||
detail: 'Some details',
|
||||
action: <Button color='green' label='Edit' link={true} />,
|
||||
separator: true,
|
||||
onClick: (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
const clickedDiv = e.currentTarget;
|
||||
alert(`Clicked on "${clickedDiv.id}"`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const HiddenActions: Story = {
|
||||
args: {
|
||||
id: 'list-item',
|
||||
title: 'A list item',
|
||||
detail: 'Some details',
|
||||
action: <Button color='green' label='Edit' link={true} />,
|
||||
separator: true,
|
||||
hideActions: true,
|
||||
onClick: (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
const clickedDiv = e.currentTarget;
|
||||
alert(`Clicked on "${clickedDiv.id}"`);
|
||||
}
|
||||
}
|
||||
};
|
37
ghost/admin-x-settings/src/admin-x-ds/global/ListItem.tsx
Normal file
37
ghost/admin-x-settings/src/admin-x-ds/global/ListItem.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
|
||||
interface ListItemProps {
|
||||
id: string;
|
||||
title?: React.ReactNode;
|
||||
detail?: React.ReactNode;
|
||||
action?: React.ReactNode;
|
||||
hideActions?: boolean;
|
||||
|
||||
/**
|
||||
* Hidden for the last item in the list
|
||||
*/
|
||||
separator?: boolean;
|
||||
onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
||||
}
|
||||
|
||||
const ListItem: React.FC<ListItemProps> = ({id, title, detail, action, hideActions, separator, onClick}) => {
|
||||
const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
onClick?.(e);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`group flex items-center justify-between hover:bg-gradient-to-r hover:from-white hover:to-grey-50 ${separator ? 'border-b border-grey-100 last-of-type:border-none' : ''}`}>
|
||||
<div className={`flex grow flex-col pr-6 ${separator ? 'py-3' : 'py-2'} ${onClick && 'cursor-pointer'}`} id={id} onClick={handleClick}>
|
||||
<span>{title}</span>
|
||||
{detail && <span className='text-xs text-grey-700'>{detail}</span>}
|
||||
</div>
|
||||
{action &&
|
||||
<div className={`px-3 ${separator ? 'py-3' : 'py-2'} ${hideActions ? 'invisible group-hover:visible' : ''}`}>
|
||||
{action}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListItem;
|
@ -186,7 +186,7 @@ module.exports = {
|
||||
supertight: '1.1em'
|
||||
},
|
||||
transition: {
|
||||
basic: 'all 0.4 ease'
|
||||
basic: 'all 0.4s ease'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user