mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-10 23:36:14 -05:00
Admin X design system updates (#19102)
refs. https://github.com/TryGhost/Product/issues/4169 - some of the new components were not prepared for mobile sizes and dark mode - Storybook settings had to be updated to include mobile sizes that reflect the actual system
This commit is contained in:
parent
9848469568
commit
82a775086c
11 changed files with 172 additions and 53 deletions
|
@ -7,6 +7,46 @@ import type { Preview } from "@storybook/react";
|
||||||
import DesignSystemProvider from '../src/providers/DesignSystemProvider';
|
import DesignSystemProvider from '../src/providers/DesignSystemProvider';
|
||||||
import adminxTheme from './adminx-theme';
|
import adminxTheme from './adminx-theme';
|
||||||
|
|
||||||
|
// import { MINIMAL_VIEWPORTS } from '@storybook/addon-viewport';
|
||||||
|
|
||||||
|
const customViewports = {
|
||||||
|
sm: {
|
||||||
|
name: 'sm',
|
||||||
|
styles: {
|
||||||
|
width: '480px',
|
||||||
|
height: '801px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
md: {
|
||||||
|
name: 'md',
|
||||||
|
styles: {
|
||||||
|
width: '640px',
|
||||||
|
height: '801px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lg: {
|
||||||
|
name: 'lg',
|
||||||
|
styles: {
|
||||||
|
width: '1024px',
|
||||||
|
height: '801px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
xl: {
|
||||||
|
name: 'xl',
|
||||||
|
styles: {
|
||||||
|
width: '1320px',
|
||||||
|
height: '801px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tablet: {
|
||||||
|
name: 'tablet',
|
||||||
|
styles: {
|
||||||
|
width: '860px',
|
||||||
|
height: '801px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const preview: Preview = {
|
const preview: Preview = {
|
||||||
parameters: {
|
parameters: {
|
||||||
actions: { argTypesRegex: "^on[A-Z].*" },
|
actions: { argTypesRegex: "^on[A-Z].*" },
|
||||||
|
@ -25,6 +65,11 @@ const preview: Preview = {
|
||||||
docs: {
|
docs: {
|
||||||
theme: adminxTheme,
|
theme: adminxTheme,
|
||||||
},
|
},
|
||||||
|
viewport: {
|
||||||
|
viewports: {
|
||||||
|
...customViewports,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
decorators: [
|
decorators: [
|
||||||
(Story, context) => {
|
(Story, context) => {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import Icon from './Icon';
|
import Icon, {IconSize} from './Icon';
|
||||||
import React, {HTMLProps} from 'react';
|
import React, {HTMLProps} from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import {LoadingIndicator, LoadingIndicatorColor, LoadingIndicatorSize} from './LoadingIndicator';
|
import {LoadingIndicator, LoadingIndicatorColor, LoadingIndicatorSize} from './LoadingIndicator';
|
||||||
|
@ -11,6 +11,7 @@ export interface ButtonProps extends Omit<HTMLProps<HTMLButtonElement>, 'label'
|
||||||
label?: React.ReactNode;
|
label?: React.ReactNode;
|
||||||
hideLabel?: boolean;
|
hideLabel?: boolean;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
iconSize?: IconSize;
|
||||||
iconColorClass?: string;
|
iconColorClass?: string;
|
||||||
key?: string;
|
key?: string;
|
||||||
color?: ButtonColor;
|
color?: ButtonColor;
|
||||||
|
@ -24,6 +25,7 @@ export interface ButtonProps extends Omit<HTMLProps<HTMLButtonElement>, 'label'
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
loadingIndicatorSize?: LoadingIndicatorSize;
|
loadingIndicatorSize?: LoadingIndicatorSize;
|
||||||
loadingIndicatorColor?: LoadingIndicatorColor;
|
loadingIndicatorColor?: LoadingIndicatorColor;
|
||||||
|
outlineOnMobile?: boolean;
|
||||||
onClick?: (e?:React.MouseEvent<HTMLElement>) => void;
|
onClick?: (e?:React.MouseEvent<HTMLElement>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +34,7 @@ const Button: React.FC<ButtonProps> = ({
|
||||||
label = '',
|
label = '',
|
||||||
hideLabel = false,
|
hideLabel = false,
|
||||||
icon = '',
|
icon = '',
|
||||||
|
iconSize,
|
||||||
iconColorClass,
|
iconColorClass,
|
||||||
color = 'clear',
|
color = 'clear',
|
||||||
fullWidth,
|
fullWidth,
|
||||||
|
@ -43,6 +46,7 @@ const Button: React.FC<ButtonProps> = ({
|
||||||
tag = 'button',
|
tag = 'button',
|
||||||
loading = false,
|
loading = false,
|
||||||
loadingIndicatorColor,
|
loadingIndicatorColor,
|
||||||
|
outlineOnMobile = false,
|
||||||
onClick,
|
onClick,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
|
@ -108,7 +112,8 @@ const Button: React.FC<ButtonProps> = ({
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
className = clsx(
|
className = clsx(
|
||||||
link ? ' text-black hover:text-grey-800 dark:text-white' : ` text-black dark:text-white dark:hover:bg-grey-900 ${!disabled && 'hover:bg-grey-200'}`,
|
link ? ' text-black hover:text-grey-800 dark:text-white' : `text-black dark:text-white dark:hover:bg-grey-900 ${!disabled && 'hover:bg-grey-200'}`,
|
||||||
|
(outlineOnMobile && !link) && 'border border-grey-300 hover:border-transparent md:border-transparent',
|
||||||
className
|
className
|
||||||
);
|
);
|
||||||
loadingIndicatorColor = 'dark';
|
loadingIndicatorColor = 'dark';
|
||||||
|
@ -128,8 +133,10 @@ const Button: React.FC<ButtonProps> = ({
|
||||||
labelClasses += (label && hideLabel) ? 'sr-only' : '';
|
labelClasses += (label && hideLabel) ? 'sr-only' : '';
|
||||||
labelClasses += loading ? 'invisible' : '';
|
labelClasses += loading ? 'invisible' : '';
|
||||||
|
|
||||||
|
iconSize = iconSize || ((size === 'sm') || (label && icon) ? 'sm' : 'md');
|
||||||
|
|
||||||
const buttonChildren = <>
|
const buttonChildren = <>
|
||||||
{icon && <Icon className={iconClasses} colorClass={iconColorClass} name={icon} size={size === 'sm' || (label && icon) ? 'sm' : 'md'} />}
|
{icon && <Icon className={iconClasses} colorClass={iconColorClass} name={icon} size={iconSize} />}
|
||||||
<span className={labelClasses}>{label}</span>
|
<span className={labelClasses}>{label}</span>
|
||||||
{loading && <div className='absolute flex'><LoadingIndicator color={loadingIndicatorColor} size={size}/><span className='sr-only'>Loading...</span></div>}
|
{loading && <div className='absolute flex'><LoadingIndicator color={loadingIndicatorColor} size={size}/><span className='sr-only'>Loading...</span></div>}
|
||||||
</>;
|
</>;
|
||||||
|
|
|
@ -10,10 +10,11 @@ export interface ButtonGroupProps {
|
||||||
link?: boolean;
|
link?: boolean;
|
||||||
linkWithPadding?: boolean;
|
linkWithPadding?: boolean;
|
||||||
clearBg?: boolean;
|
clearBg?: boolean;
|
||||||
|
outlineOnMobile?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ButtonGroup: React.FC<ButtonGroupProps> = ({size = 'md', buttons, link, linkWithPadding, clearBg = true, className}) => {
|
const ButtonGroup: React.FC<ButtonGroupProps> = ({size = 'md', buttons, link, linkWithPadding, clearBg = true, outlineOnMobile, className}) => {
|
||||||
let groupColorClasses = clsx(
|
let groupColorClasses = clsx(
|
||||||
'flex items-center justify-start rounded',
|
'flex items-center justify-start rounded',
|
||||||
link ? 'gap-4' : 'gap-5',
|
link ? 'gap-4' : 'gap-5',
|
||||||
|
@ -24,6 +25,7 @@ const ButtonGroup: React.FC<ButtonGroupProps> = ({size = 'md', buttons, link, li
|
||||||
groupColorClasses = clsx(
|
groupColorClasses = clsx(
|
||||||
'transition-all hover:bg-grey-200 dark:hover:bg-grey-900',
|
'transition-all hover:bg-grey-200 dark:hover:bg-grey-900',
|
||||||
size === 'sm' ? 'h-7 px-3' : 'h-[34px] px-4',
|
size === 'sm' ? 'h-7 px-3' : 'h-[34px] px-4',
|
||||||
|
outlineOnMobile && 'border border-grey-300 hover:border-transparent md:border-transparent',
|
||||||
groupColorClasses
|
groupColorClasses
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import Button from '../Button';
|
||||||
|
|
||||||
const PageMenu: React.FC = () => {
|
const PageMenu: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<Button icon='hamburger' iconColorClass='text-black' size='sm' link onClick={() => {
|
<Button icon='hamburger' iconColorClass='text-black dark:text-white' size='sm' link onClick={() => {
|
||||||
alert('Clicked on hamburger');
|
alert('Clicked on hamburger');
|
||||||
}} />
|
}} />
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,7 +3,7 @@ import Button from '../Button';
|
||||||
|
|
||||||
const GlobalActions: React.FC = () => {
|
const GlobalActions: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<Button icon='magnifying-glass' iconColorClass='text-black' size='sm' link onClick={() => {}} />
|
<Button icon='magnifying-glass' iconColorClass='dark:text-white text-black' size='sm' link onClick={() => {}} />
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -108,6 +108,7 @@ const currentAdminExample = <ViewContainer
|
||||||
>
|
>
|
||||||
<DynamicTable
|
<DynamicTable
|
||||||
columns={testColumns}
|
columns={testColumns}
|
||||||
|
pageHasSidebar={false}
|
||||||
rows={testRows(100)}
|
rows={testRows(100)}
|
||||||
/>
|
/>
|
||||||
</ViewContainer>;
|
</ViewContainer>;
|
||||||
|
@ -127,6 +128,7 @@ const simpleList = <ViewContainer
|
||||||
<DynamicTable
|
<DynamicTable
|
||||||
columns={testColumns}
|
columns={testColumns}
|
||||||
footer={<Hint>Just a regular table footer</Hint>}
|
footer={<Hint>Just a regular table footer</Hint>}
|
||||||
|
pageHasSidebar={false}
|
||||||
rows={testRows(100)}
|
rows={testRows(100)}
|
||||||
/>
|
/>
|
||||||
</ViewContainer>;
|
</ViewContainer>;
|
||||||
|
@ -149,6 +151,7 @@ const stickyList = <ViewContainer
|
||||||
<DynamicTable
|
<DynamicTable
|
||||||
columns={testColumns}
|
columns={testColumns}
|
||||||
footer={<Hint>Sticky footer</Hint>}
|
footer={<Hint>Sticky footer</Hint>}
|
||||||
|
pageHasSidebar={false}
|
||||||
rows={testRows(40)}
|
rows={testRows(40)}
|
||||||
stickyFooter
|
stickyFooter
|
||||||
stickyHeader
|
stickyHeader
|
||||||
|
@ -180,6 +183,7 @@ const examplePrimaryAction = <ViewContainer
|
||||||
<DynamicTable
|
<DynamicTable
|
||||||
columns={testColumns}
|
columns={testColumns}
|
||||||
footer={<Hint>Sticky footer</Hint>}
|
footer={<Hint>Sticky footer</Hint>}
|
||||||
|
pageHasSidebar={false}
|
||||||
rows={testRows(40)}
|
rows={testRows(40)}
|
||||||
stickyFooter
|
stickyFooter
|
||||||
stickyHeader
|
stickyHeader
|
||||||
|
@ -213,6 +217,7 @@ const exampleActionsContent = <ViewContainer
|
||||||
<DynamicTable
|
<DynamicTable
|
||||||
columns={testColumns}
|
columns={testColumns}
|
||||||
footer={<Hint>Sticky footer</Hint>}
|
footer={<Hint>Sticky footer</Hint>}
|
||||||
|
pageHasSidebar={false}
|
||||||
rows={testRows(40)}
|
rows={testRows(40)}
|
||||||
stickyFooter
|
stickyFooter
|
||||||
stickyHeader
|
stickyHeader
|
||||||
|
@ -262,7 +267,7 @@ const exampleCardViewContent = (
|
||||||
title='Ideas'
|
title='Ideas'
|
||||||
type='page'
|
type='page'
|
||||||
>
|
>
|
||||||
<div className='grid grid-cols-4 gap-7 py-7'>
|
<div className='grid grid-cols-2 gap-7 py-7 tablet:grid-cols-4'>
|
||||||
{mockIdeaCards()}
|
{mockIdeaCards()}
|
||||||
</div>
|
</div>
|
||||||
</ViewContainer>
|
</ViewContainer>
|
||||||
|
@ -352,7 +357,10 @@ export const ExampleDetailScreen: Story = {
|
||||||
breadCrumbs: <Breadcrumbs
|
breadCrumbs: <Breadcrumbs
|
||||||
items={[
|
items={[
|
||||||
{
|
{
|
||||||
label: 'Members'
|
label: 'Members',
|
||||||
|
onClick: () => {
|
||||||
|
alert('Clicked back');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Emerson Vaccaro'
|
label: 'Emerson Vaccaro'
|
||||||
|
@ -379,25 +387,25 @@ export const ExampleDetailScreen: Story = {
|
||||||
}
|
}
|
||||||
type='page'
|
type='page'
|
||||||
>
|
>
|
||||||
<div className='grid grid-cols-4 border-b border-grey-200 pb-5'>
|
<div className='grid grid-cols-3 border-b border-grey-200 pb-5 tablet:grid-cols-4'>
|
||||||
<div className='-ml-5 flex h-full flex-col px-5'>
|
<div className='col-span-3 -ml-5 mb-5 hidden h-full gap-4 px-5 tablet:col-span-1 tablet:mb-0 tablet:!flex tablet:flex-col tablet:gap-0'>
|
||||||
<span>Last seen on <strong>22 June 2023</strong></span>
|
<span>Last seen on <strong>22 June 2023</strong></span>
|
||||||
<span className='mt-2'>Created on <strong>27 Jan 2021</strong></span>
|
<span className='tablet:mt-2'>Created on <strong>27 Jan 2021</strong></span>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex h-full flex-col px-5'>
|
<div className='flex h-full flex-col tablet:px-5'>
|
||||||
<Heading level={6}>Emails received</Heading>
|
<Heading level={6}>Emails received</Heading>
|
||||||
<span className='mt-1 text-4xl font-bold leading-none'>181</span>
|
<span className='mt-1 text-4xl font-bold leading-none'>181</span>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex h-full flex-col px-5'>
|
<div className='flex h-full flex-col tablet:px-5'>
|
||||||
<Heading level={6}>Emails opened</Heading>
|
<Heading level={6}>Emails opened</Heading>
|
||||||
<span className='mt-1 text-4xl font-bold leading-none'>104</span>
|
<span className='mt-1 text-4xl font-bold leading-none'>104</span>
|
||||||
</div>
|
</div>
|
||||||
<div className='-mr-5 flex h-full flex-col px-5'>
|
<div className='-mr-5 flex h-full flex-col tablet:px-5'>
|
||||||
<Heading level={6}>Average open rate</Heading>
|
<Heading level={6}>Average open rate</Heading>
|
||||||
<span className='mt-1 text-4xl font-bold leading-none'>57%</span>
|
<span className='mt-1 text-4xl font-bold leading-none'>57%</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='grid grid-cols-4 items-baseline py-5'>
|
<div className='grid grid-cols-2 items-baseline border-b border-grey-200 py-5 tablet:grid-cols-4'>
|
||||||
<div className='-ml-5 flex h-full flex-col gap-6 border-r border-grey-200 px-5'>
|
<div className='-ml-5 flex h-full flex-col gap-6 border-r border-grey-200 px-5'>
|
||||||
<div className='flex justify-between'>
|
<div className='flex justify-between'>
|
||||||
<Heading level={5}>Member data</Heading>
|
<Heading level={5}>Member data</Heading>
|
||||||
|
@ -413,10 +421,17 @@ export const ExampleDetailScreen: Story = {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Heading level={6}>Labels</Heading>
|
<Heading level={6}>Labels</Heading>
|
||||||
<div className='inline-block rounded-sm bg-grey-300 px-1 text-xs font-medium'>VIP</div>
|
<div className='mt-2 flex gap-1'>
|
||||||
|
<div className='inline-block rounded-sm bg-grey-200 px-1.5 text-xs font-medium'>VIP</div>
|
||||||
|
<div className='inline-block rounded-sm bg-grey-200 px-1.5 text-xs font-medium'>Inner Circle</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Heading level={6}>Notes</Heading>
|
||||||
|
<div className='text-grey-500'>No notes.</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex h-full flex-col gap-6 border-r border-grey-200 px-5'>
|
<div className='flex h-full flex-col gap-6 border-grey-200 px-5 tablet:border-r'>
|
||||||
<Heading level={5}>Newsletters</Heading>
|
<Heading level={5}>Newsletters</Heading>
|
||||||
<div className='flex flex-col gap-3'>
|
<div className='flex flex-col gap-3'>
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
|
@ -427,24 +442,44 @@ export const ExampleDetailScreen: Story = {
|
||||||
<Toggle />
|
<Toggle />
|
||||||
<span>Weekly roundup</span>
|
<span>Weekly roundup</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div className='flex items-center gap-2'>
|
||||||
|
<Toggle checked />
|
||||||
|
<span>The Inner Circle</span>
|
||||||
|
</div>
|
||||||
|
<div className='mt-5 rounded border border-red p-4 text-sm text-red'>
|
||||||
|
This member cannot receive emails due to permanent failure (bounce).
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex h-full flex-col gap-6 border-r border-grey-200 px-5'>
|
<div className='-ml-5 flex h-full flex-col gap-6 border-r border-grey-200 px-5 pt-10 tablet:ml-0 tablet:pt-0'>
|
||||||
<Heading level={5}>Subscriptions</Heading>
|
<Heading level={5}>Subscriptions</Heading>
|
||||||
<div className='flex flex-col'>
|
<div className='flex items-center gap-3'>
|
||||||
<span className='font-semibold'>Gold — $12/month</span>
|
<div className='flex h-16 w-16 flex-col items-center justify-center rounded-md bg-grey-200'>
|
||||||
<span className='text-sm text-grey-500'>Renews 21 Jan 2024</span>
|
<Heading level={5}>$5</Heading>
|
||||||
|
<span className='text-xs text-grey-700'>Yearly</span>
|
||||||
|
</div>
|
||||||
|
<div className='flex flex-col'>
|
||||||
|
<span className='font-semibold'>Gold</span>
|
||||||
|
<span className='text-sm text-grey-500'>Renews 21 Jan 2024</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='-mr-5 flex h-full flex-col gap-6 px-5'>
|
<div className='-mr-5 flex h-full flex-col gap-6 px-5 pt-10 tablet:pt-0'>
|
||||||
<Heading level={5}>Activity</Heading>
|
<div className='flex justify-between'>
|
||||||
<div className='flex flex-col'>
|
<Heading level={5}>Activity</Heading>
|
||||||
<span className='font-semibold'>Logged in</span>
|
<Button color='green' label='View all' link />
|
||||||
<span className='text-sm text-grey-500'>Renews 21 Jan 2024</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className='flex flex-col'>
|
<div className='flex flex-col text-sm'>
|
||||||
|
<span className='font-semibold'>Logged in</span>
|
||||||
|
<span className='text-sm text-grey-500'>13 days ago</span>
|
||||||
|
</div>
|
||||||
|
<div className='flex flex-col text-sm'>
|
||||||
<span className='font-semibold'>Subscribed to Daily News</span>
|
<span className='font-semibold'>Subscribed to Daily News</span>
|
||||||
<span className='text-sm text-grey-500'>Renews 21 Jan 2024</span>
|
<span className='text-sm text-grey-500'>17 days ago</span>
|
||||||
|
</div>
|
||||||
|
<div className='flex flex-col text-sm'>
|
||||||
|
<span className='font-semibold'>Logged in</span>
|
||||||
|
<span className='text-sm text-grey-500'>21 days ago</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -125,7 +125,7 @@ const Page: React.FC<PageProps> = ({
|
||||||
<div className='sticky flex items-center gap-7'>
|
<div className='sticky flex items-center gap-7'>
|
||||||
{(customGlobalActions?.map((action) => {
|
{(customGlobalActions?.map((action) => {
|
||||||
return (
|
return (
|
||||||
<Button icon={action.iconName} iconColorClass='text-black' size='sm' link onClick={action.onClick} />
|
<Button icon={action.iconName} iconColorClass='text-black dark:text-white' size='sm' link onClick={action.onClick} />
|
||||||
);
|
);
|
||||||
}))}
|
}))}
|
||||||
{showGlobalActions && <GlobalActions />}
|
{showGlobalActions && <GlobalActions />}
|
||||||
|
@ -138,7 +138,7 @@ const Page: React.FC<PageProps> = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
pageToolbarClassName = clsx(
|
pageToolbarClassName = clsx(
|
||||||
'sticky top-0 z-50 flex h-18 w-full items-center justify-between gap-5 bg-white p-6',
|
'sticky top-0 z-50 flex h-18 w-full items-center justify-between gap-5 bg-white p-6 dark:bg-black',
|
||||||
!fullBleedToolbar && 'mx-auto max-w-7xl',
|
!fullBleedToolbar && 'mx-auto max-w-7xl',
|
||||||
pageToolbarClassName
|
pageToolbarClassName
|
||||||
);
|
);
|
||||||
|
|
|
@ -39,7 +39,7 @@ const PageHeader: React.FC<PageHeaderProps> = ({
|
||||||
const leftClasses = clsx(
|
const leftClasses = clsx(
|
||||||
'flex flex-auto items-center',
|
'flex flex-auto items-center',
|
||||||
(right && center) && 'basis-1/3',
|
(right && center) && 'basis-1/3',
|
||||||
((right && !center) || (!right && center)) && 'basis-1/2'
|
((!right && center)) && 'basis-1/2'
|
||||||
);
|
);
|
||||||
left = <div className={leftClasses}>{left}</div>;
|
left = <div className={leftClasses}>{left}</div>;
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ const PageHeader: React.FC<PageHeaderProps> = ({
|
||||||
const rightClasses = clsx(
|
const rightClasses = clsx(
|
||||||
'flex flex-auto items-center justify-end',
|
'flex flex-auto items-center justify-end',
|
||||||
(left && center) && 'basis-1/3',
|
(left && center) && 'basis-1/3',
|
||||||
((left && !center) || (!left && center)) && 'basis-1/2'
|
((!left && center)) && 'basis-1/2'
|
||||||
);
|
);
|
||||||
right = <div className={rightClasses}>{right}</div>;
|
right = <div className={rightClasses}>{right}</div>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,9 @@ import ButtonGroup from '../ButtonGroup';
|
||||||
const meta = {
|
const meta = {
|
||||||
title: 'Global / Layout / View Container',
|
title: 'Global / Layout / View Container',
|
||||||
component: ViewContainer,
|
component: ViewContainer,
|
||||||
|
parameters: {
|
||||||
|
layout: 'fullscreen'
|
||||||
|
},
|
||||||
render: function Component(args) {
|
render: function Component(args) {
|
||||||
const [, updateArgs] = useArgs();
|
const [, updateArgs] = useArgs();
|
||||||
|
|
||||||
|
@ -33,13 +36,13 @@ export default meta;
|
||||||
type Story = StoryObj<typeof ViewContainer>;
|
type Story = StoryObj<typeof ViewContainer>;
|
||||||
|
|
||||||
export const exampleActions = [
|
export const exampleActions = [
|
||||||
<Button label='Filter' onClick={() => {
|
<Button label='Filter' outlineOnMobile onClick={() => {
|
||||||
alert('Clicked filter');
|
alert('Clicked filter');
|
||||||
}} />,
|
}} />,
|
||||||
<Button label='Sort' onClick={() => {
|
<Button label='Sort' outlineOnMobile onClick={() => {
|
||||||
alert('Clicked sort');
|
alert('Clicked sort');
|
||||||
}} />,
|
}} />,
|
||||||
<Button icon='magnifying-glass' size='sm' onClick={() => {
|
<Button icon='magnifying-glass' iconSize='sm' outlineOnMobile onClick={() => {
|
||||||
alert('Clicked search');
|
alert('Clicked search');
|
||||||
}} />,
|
}} />,
|
||||||
<ButtonGroup buttons={[
|
<ButtonGroup buttons={[
|
||||||
|
@ -59,7 +62,7 @@ export const exampleActions = [
|
||||||
alert('Clicked card view');
|
alert('Clicked card view');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]} clearBg={false} link />
|
]} clearBg={false} link outlineOnMobile />
|
||||||
];
|
];
|
||||||
|
|
||||||
const primaryAction: PrimaryActionProps = {
|
const primaryAction: PrimaryActionProps = {
|
||||||
|
@ -152,13 +155,37 @@ export const TabsWithPrimaryAction: Story = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const sectionActions = [
|
||||||
|
<Button label='Filter' size='sm' onClick={() => {
|
||||||
|
alert('Clicked filter');
|
||||||
|
}} />,
|
||||||
|
<ButtonGroup buttons={[
|
||||||
|
{
|
||||||
|
icon: 'listview',
|
||||||
|
size: 'sm',
|
||||||
|
iconColorClass: 'text-black',
|
||||||
|
onClick: () => {
|
||||||
|
alert('Clicked list view');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'cardview',
|
||||||
|
size: 'sm',
|
||||||
|
iconColorClass: 'text-grey-500',
|
||||||
|
onClick: () => {
|
||||||
|
alert('Clicked card view');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]} clearBg={false} size='sm' link />
|
||||||
|
];
|
||||||
|
|
||||||
export const TabsWithActions: Story = {
|
export const TabsWithActions: Story = {
|
||||||
args: {
|
args: {
|
||||||
type: 'section',
|
type: 'section',
|
||||||
title: 'Section title',
|
title: 'Section title',
|
||||||
tabs: tabs,
|
tabs: tabs,
|
||||||
primaryAction: primaryAction,
|
primaryAction: primaryAction,
|
||||||
actions: exampleActions
|
actions: sectionActions
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -167,7 +194,7 @@ export const HiddenActions: Story = {
|
||||||
type: 'section',
|
type: 'section',
|
||||||
title: 'Hover to show actions',
|
title: 'Hover to show actions',
|
||||||
tabs: tabs,
|
tabs: tabs,
|
||||||
actions: exampleActions,
|
actions: sectionActions,
|
||||||
actionsHidden: true
|
actionsHidden: true
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -172,15 +172,16 @@ const ViewContainer: React.FC<ViewContainerProps> = ({
|
||||||
|
|
||||||
toolbarWrapperClassName = clsx(
|
toolbarWrapperClassName = clsx(
|
||||||
'z-50',
|
'z-50',
|
||||||
type === 'page' && 'mx-auto w-full max-w-7xl bg-white px-12',
|
type === 'page' && 'mx-auto w-full max-w-7xl bg-white px-[4vw] dark:bg-black tablet:px-12',
|
||||||
(type === 'page' && stickyHeader) && (firstOnPage ? 'sticky top-0 pt-8' : 'sticky top-18 pt-[3vmin]'),
|
(type === 'page' && stickyHeader) && (firstOnPage ? 'sticky top-0 pt-8' : 'sticky top-18 pt-[3vmin]'),
|
||||||
toolbarContainerClassName
|
toolbarContainerClassName
|
||||||
);
|
);
|
||||||
|
|
||||||
toolbarContainerClassName = clsx(
|
toolbarContainerClassName = clsx(
|
||||||
'flex items-end justify-between',
|
'flex justify-between gap-5',
|
||||||
(firstOnPage && type === 'page') ? 'pb-8' : (tabs?.length ? '' : 'pb-2'),
|
(type === 'page' && actions?.length) ? 'flex-col md:flex-row md:items-end' : 'items-end',
|
||||||
toolbarBorder && 'border-b border-grey-200',
|
(firstOnPage && type === 'page') ? 'pb-3 tablet:pb-8' : (tabs?.length ? '' : 'pb-2'),
|
||||||
|
toolbarBorder && 'border-b border-grey-200 dark:border-grey-900',
|
||||||
toolbarContainerClassName
|
toolbarContainerClassName
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -190,9 +191,9 @@ const ViewContainer: React.FC<ViewContainerProps> = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
actionsClassName = clsx(
|
actionsClassName = clsx(
|
||||||
'flex items-center gap-5 transition-all',
|
'flex items-center justify-between gap-3 transition-all tablet:justify-start tablet:gap-5',
|
||||||
actionsHidden && 'opacity-0 group-hover/view-container:opacity-100',
|
actionsHidden && 'opacity-0 group-hover/view-container:opacity-100',
|
||||||
tabs?.length ? 'pb-2' : (type === 'page' ? 'pb-1' : ''),
|
tabs?.length ? 'pb-1' : (type === 'page' ? 'pb-1' : ''),
|
||||||
actionsClassName
|
actionsClassName
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -243,7 +244,7 @@ const ViewContainer: React.FC<ViewContainerProps> = ({
|
||||||
|
|
||||||
contentWrapperClassName = clsx(
|
contentWrapperClassName = clsx(
|
||||||
'relative mx-auto w-full flex-auto',
|
'relative mx-auto w-full flex-auto',
|
||||||
(!contentFullBleed && type === 'page') && 'max-w-7xl px-12',
|
(!contentFullBleed && type === 'page') && 'max-w-7xl px-[4vw] tablet:px-12',
|
||||||
contentWrapperClassName,
|
contentWrapperClassName,
|
||||||
(!title && !actions) && 'pt-[3vmin]'
|
(!title && !actions) && 'pt-[3vmin]'
|
||||||
);
|
);
|
||||||
|
|
|
@ -32,6 +32,7 @@ export interface DynamicTableProps {
|
||||||
* Set this parameter if the table is the main content in a viewcontainer or on a page
|
* Set this parameter if the table is the main content in a viewcontainer or on a page
|
||||||
*/
|
*/
|
||||||
singlePageTable?: boolean;
|
singlePageTable?: boolean;
|
||||||
|
pageHasSidebar?: boolean;
|
||||||
|
|
||||||
border?: boolean;
|
border?: boolean;
|
||||||
footerBorder?:boolean;
|
footerBorder?:boolean;
|
||||||
|
@ -60,6 +61,7 @@ const DynamicTable: React.FC<DynamicTableProps> = ({
|
||||||
footerBorder = true,
|
footerBorder = true,
|
||||||
stickyFooter = false,
|
stickyFooter = false,
|
||||||
singlePageTable = false,
|
singlePageTable = false,
|
||||||
|
pageHasSidebar = true,
|
||||||
containerClassName,
|
containerClassName,
|
||||||
tableContainerClassName,
|
tableContainerClassName,
|
||||||
tableClassName,
|
tableClassName,
|
||||||
|
@ -81,7 +83,7 @@ const DynamicTable: React.FC<DynamicTableProps> = ({
|
||||||
tableContainerClassName = clsx(
|
tableContainerClassName = clsx(
|
||||||
'flex-auto overflow-x-auto',
|
'flex-auto overflow-x-auto',
|
||||||
!horizontalScrolling && 'w-full max-w-full',
|
!horizontalScrolling && 'w-full max-w-full',
|
||||||
(singlePageTable && (stickyHeader || stickyFooter || absolute)) && 'px-12 xl:px-[calc((100%-1320px)/2+48px)]',
|
(singlePageTable && (stickyHeader || stickyFooter || absolute)) && `px-[4vw] tablet:px-12 ${pageHasSidebar ? 'min-[1640px]:px-[calc((100%-1320px)/2+48px)]' : 'xl:px-[calc((100%-1320px)/2+48px)]'}`,
|
||||||
tableContainerClassName
|
tableContainerClassName
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -91,13 +93,13 @@ const DynamicTable: React.FC<DynamicTableProps> = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
thClassName = clsx(
|
thClassName = clsx(
|
||||||
'last-child:pr-5 bg-white py-3 text-left [&:not(:first-child)]:pl-5',
|
'last-child:pr-5 bg-white py-3 text-left dark:bg-black [&:not(:first-child)]:pl-5',
|
||||||
thClassName
|
thClassName
|
||||||
);
|
);
|
||||||
|
|
||||||
tdClassName = clsx(
|
tdClassName = clsx(
|
||||||
'w-full border-b group-hover:border-grey-200',
|
'w-full border-b group-hover:border-grey-200 dark:group-hover:border-grey-900',
|
||||||
border ? 'border-grey-200' : 'border-transparent',
|
border ? 'border-grey-200 dark:border-grey-900' : 'border-transparent',
|
||||||
tdClassName
|
tdClassName
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -113,11 +115,11 @@ const DynamicTable: React.FC<DynamicTableProps> = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
footerClassName = clsx(
|
footerClassName = clsx(
|
||||||
'bg-white',
|
'bg-white dark:bg-black',
|
||||||
(singlePageTable && stickyFooter) && 'mx-12 xl:mx-[calc((100%-1320px)/2+48px)]',
|
(singlePageTable && stickyFooter) && `mx-[4vw] tablet:mx-12 ${pageHasSidebar ? 'min-[1640px]:mx-[calc((100%-1320px)/2+48px)]' : 'xl:mx-[calc((100%-1320px)/2+48px)]'}`,
|
||||||
footer && 'py-4',
|
footer && 'py-4',
|
||||||
stickyFooter && 'sticky inset-x-0 bottom-0',
|
stickyFooter && 'sticky inset-x-0 bottom-0',
|
||||||
footerBorder && 'border-t border-grey-200',
|
footerBorder && 'border-t border-grey-200 dark:border-grey-900',
|
||||||
footerClassName
|
footerClassName
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -150,7 +152,7 @@ const DynamicTable: React.FC<DynamicTableProps> = ({
|
||||||
</tr>
|
</tr>
|
||||||
{headerBorder && (
|
{headerBorder && (
|
||||||
<tr>
|
<tr>
|
||||||
<th className='h-px bg-grey-200 p-0' colSpan={columns.length}></th>
|
<th className='h-px bg-grey-200 p-0 dark:bg-grey-900' colSpan={columns.length}></th>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
</thead>
|
</thead>
|
||||||
|
|
Loading…
Add table
Reference in a new issue