mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-06 22:40:14 -05:00
Updated installed theme list
refs. https://github.com/TryGhost/Team/issues/3432
This commit is contained in:
parent
dfd69f9cf7
commit
78aa31973c
21 changed files with 151 additions and 106 deletions
|
@ -0,0 +1,5 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="6" cy="12" r="1.5" fill="currentColor"/>
|
||||
<circle cx="12" cy="12" r="1.5" fill="currentColor"/>
|
||||
<path d="M19.5 12C19.5 12.8284 18.8284 13.5 18 13.5C17.1716 13.5 16.5 12.8284 16.5 12C16.5 11.1716 17.1716 10.5 18 10.5C18.8284 10.5 19.5 11.1716 19.5 12Z" fill="currentColor"/>
|
||||
</svg>
|
After Width: | Height: | Size: 388 B |
|
@ -1 +0,0 @@
|
|||
<svg viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg" height="16" width="16"><path d="M7.500 5.000 A1.250 1.250 0 1 0 10.000 5.000 A1.250 1.250 0 1 0 7.500 5.000 Z" fill="currentColor" stroke="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="0"></path><path d="M3.750 5.000 A1.250 1.250 0 1 0 6.250 5.000 A1.250 1.250 0 1 0 3.750 5.000 Z" fill="currentColor" stroke="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="0"></path><path d="M0.000 5.000 A1.250 1.250 0 1 0 2.500 5.000 A1.250 1.250 0 1 0 0.000 5.000 Z" fill="currentColor" stroke="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="0"></path></svg>
|
Before Width: | Height: | Size: 666 B |
|
@ -61,7 +61,7 @@ export const LinkButton: Story = {
|
|||
|
||||
export const Icon: Story = {
|
||||
args: {
|
||||
icon: 'menu-horizontal',
|
||||
icon: 'ellipsis',
|
||||
color: 'green',
|
||||
iconColorClass: 'text-white'
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ export const Icon: Story = {
|
|||
export const IconSmall: Story = {
|
||||
args: {
|
||||
size: 'sm',
|
||||
icon: 'menu-horizontal',
|
||||
icon: 'ellipsis',
|
||||
color: 'green',
|
||||
iconColorClass: 'text-white'
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import React from 'react';
|
|||
export type ButtonColor = 'clear' | 'grey' | 'black' | 'green' | 'red' | 'white';
|
||||
export type ButtonSize = 'sm' | 'md';
|
||||
|
||||
export interface IButton {
|
||||
export interface ButtonProps {
|
||||
size?: ButtonSize;
|
||||
label?: React.ReactNode;
|
||||
icon?: string;
|
||||
|
@ -18,7 +18,7 @@ export interface IButton {
|
|||
onClick?: () => void;
|
||||
}
|
||||
|
||||
const Button: React.FC<IButton> = ({
|
||||
const Button: React.FC<ButtonProps> = ({
|
||||
size = 'md',
|
||||
label = '',
|
||||
icon = '',
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import Button from './Button';
|
||||
import React from 'react';
|
||||
|
||||
import {IButton} from './Button';
|
||||
import {ButtonProps} from './Button';
|
||||
|
||||
interface ButtonGroupProps {
|
||||
buttons: Array<IButton>;
|
||||
buttons: Array<ButtonProps>;
|
||||
link?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
|
|
@ -7,8 +7,7 @@ import ListItem from './ListItem';
|
|||
const meta = {
|
||||
title: 'Global / List',
|
||||
component: List,
|
||||
tags: ['autodocs'],
|
||||
decorators: [(_story: any) => (<div style={{maxWidth: '600px'}}>{_story()}</div>)]
|
||||
tags: ['autodocs']
|
||||
} satisfies Meta<typeof List>;
|
||||
|
||||
export default meta;
|
||||
|
@ -29,5 +28,14 @@ export const Default: Story = {
|
|||
title: 'This is a list',
|
||||
children: listItems,
|
||||
hint: 'And here is a hint for the whole list'
|
||||
},
|
||||
decorators: [(_story: any) => (<div style={{maxWidth: '600px'}}>{_story()}</div>)]
|
||||
};
|
||||
|
||||
export const PageLevel: Story = {
|
||||
args: {
|
||||
pageTitle: 'A page with a list',
|
||||
children: listItems,
|
||||
hint: 'And here is a hint for the whole list'
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2,37 +2,55 @@ import Heading from './Heading';
|
|||
import Hint from './Hint';
|
||||
import React from 'react';
|
||||
import Separator from './Separator';
|
||||
import clsx from 'clsx';
|
||||
|
||||
interface ListProps {
|
||||
/**
|
||||
* If the list is the primary content on a page (e.g. Members list) then you can set a pagetitle to be consistent
|
||||
*/
|
||||
pageTitle?: string;
|
||||
|
||||
/**
|
||||
* When you use the list in a block and it's not the primary content of the page then you can set a title to the list
|
||||
*/
|
||||
title?: React.ReactNode;
|
||||
titleSeparator?: boolean;
|
||||
children?: React.ReactNode;
|
||||
hint?: React.ReactNode;
|
||||
hintSeparator?: boolean;
|
||||
borderTop?: boolean;
|
||||
}
|
||||
|
||||
const List: React.FC<ListProps> = ({title, titleSeparator, children, hint, hintSeparator}) => {
|
||||
const List: React.FC<ListProps> = ({title, titleSeparator, children, hint, hintSeparator, borderTop, pageTitle}) => {
|
||||
titleSeparator = (titleSeparator === undefined) ? true : titleSeparator;
|
||||
hintSeparator = (hintSeparator === undefined) ? true : hintSeparator;
|
||||
|
||||
const listClasses = clsx(
|
||||
(borderTop || pageTitle) && 'border-t border-grey-300',
|
||||
pageTitle && 'mt-14'
|
||||
);
|
||||
|
||||
return (
|
||||
<section>
|
||||
{title &&
|
||||
<div className='flex flex-col gap-1'>
|
||||
<Heading grey={true} level={6}>{title}</Heading>
|
||||
{titleSeparator && <Separator />}
|
||||
<>
|
||||
{pageTitle && <Heading>{pageTitle}</Heading>}
|
||||
<section className={listClasses}>
|
||||
{(!pageTitle && 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>
|
||||
}
|
||||
<div className='flex flex-col'>
|
||||
{children}
|
||||
</div>
|
||||
{hint &&
|
||||
<>
|
||||
{hintSeparator && <Separator />}
|
||||
<Hint>{hint}</Hint>
|
||||
</>
|
||||
}
|
||||
</section>
|
||||
{hint &&
|
||||
<>
|
||||
{hintSeparator && <Separator />}
|
||||
<Hint>{hint}</Hint>
|
||||
</>
|
||||
}
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
interface ListItemProps {
|
||||
id: string;
|
||||
|
@ -7,32 +8,41 @@ interface ListItemProps {
|
|||
action?: React.ReactNode;
|
||||
hideActions?: boolean;
|
||||
avatar?: React.ReactNode;
|
||||
className?: string;
|
||||
|
||||
/**
|
||||
* Hidden for the last item in the list
|
||||
*/
|
||||
separator?: boolean;
|
||||
|
||||
bgOnHover?: boolean;
|
||||
onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
||||
}
|
||||
|
||||
const ListItem: React.FC<ListItemProps> = ({id, title, detail, action, hideActions, avatar, separator, onClick}) => {
|
||||
const ListItem: React.FC<ListItemProps> = ({id, title, detail, action, hideActions, avatar, className, separator, bgOnHover = true, onClick}) => {
|
||||
const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
onClick?.(e);
|
||||
};
|
||||
|
||||
separator = (separator === undefined) ? true : separator;
|
||||
const listItemClasses = clsx(
|
||||
'group flex items-center justify-between',
|
||||
bgOnHover && 'hover:bg-gradient-to-r hover:from-white hover:to-grey-50',
|
||||
separator ? 'border-b border-grey-100 last-of-type:border-none hover:border-grey-200' : 'border-b border-transparent hover:border-grey-200',
|
||||
className
|
||||
);
|
||||
|
||||
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={listItemClasses}>
|
||||
<div className={`flex grow items-center gap-3 ${onClick && 'cursor-pointer'}`} onClick={handleClick}>
|
||||
{avatar && avatar}
|
||||
<div className={`flex grow flex-col pr-6 ${separator ? 'py-3' : 'py-2'}`} id={id}>
|
||||
<div className={`flex grow flex-col py-3 pr-6`} id={id}>
|
||||
<span>{title}</span>
|
||||
{detail && <span className='text-xs text-grey-700'>{detail}</span>}
|
||||
</div>
|
||||
</div>
|
||||
{action &&
|
||||
<div className={`px-6 ${separator ? 'py-3' : 'py-2'} ${hideActions ? 'invisible group-hover:visible' : ''}`}>
|
||||
{action &&
|
||||
<div className={`px-6 py-3 ${hideActions ? 'invisible group-hover:visible' : ''}`}>
|
||||
{action}
|
||||
</div>
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import Button, {ButtonProps, ButtonSize} from './Button';
|
||||
import React, {useState} from 'react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
export type MenuItem = {
|
||||
id: string,
|
||||
|
@ -10,53 +12,49 @@ type MenuPosition = 'left' | 'right';
|
|||
|
||||
interface MenuProps {
|
||||
trigger?: React.ReactNode;
|
||||
triggerButtonProps?: ButtonProps;
|
||||
triggerSize?: ButtonSize;
|
||||
items: MenuItem[];
|
||||
position?: MenuPosition;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Menu: React.FC<MenuProps> = ({trigger, items, position, className}) => {
|
||||
const Menu: React.FC<MenuProps> = ({trigger, triggerButtonProps, items, position, className}) => {
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
|
||||
let menuListStyles = 'absolute z-40 mt-2 min-w-[160px] w-max origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black/5 focus:outline-none';
|
||||
|
||||
const toggleMenu = () => {
|
||||
setMenuOpen(!menuOpen);
|
||||
};
|
||||
|
||||
const handleBackdropClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
const handleBackdropClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (e.target === e.currentTarget) {
|
||||
setMenuOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
switch (position) {
|
||||
case 'left':
|
||||
menuListStyles += ' right-0 ';
|
||||
break;
|
||||
case 'right':
|
||||
menuListStyles += ' left-0 ';
|
||||
break;
|
||||
|
||||
default:
|
||||
menuListStyles += ' left-0 ';
|
||||
break;
|
||||
if (!trigger) {
|
||||
trigger = <Button icon='ellipsis' {...triggerButtonProps} />;
|
||||
}
|
||||
|
||||
menuListStyles += menuOpen ? 'block' : 'hidden';
|
||||
const menuClasses = clsx(
|
||||
'absolute z-40 mt-2 w-max min-w-[160px] origin-top-right rounded bg-white shadow-md ring-1 ring-[rgba(0,0,0,0.01)] focus:outline-none',
|
||||
position === 'left' && 'right-0',
|
||||
(position === 'right' || !position) && 'left-0',
|
||||
menuOpen ? 'block' : 'hidden'
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={`relative inline-block ${className}`}>
|
||||
<div className={`fixed inset-0 z-40 ${menuOpen ? 'block' : 'hidden'}`} onClick={handleBackdropClick}></div>
|
||||
{/* Menu Trigger */}
|
||||
<div className='relative z-50' onClick={toggleMenu}>
|
||||
<div className='relative z-30' onClick={toggleMenu}>
|
||||
{trigger}
|
||||
</div>
|
||||
{/* Menu List */}
|
||||
<div aria-labelledby="menu-button" aria-orientation="vertical" className={menuListStyles} role="menu">
|
||||
<div className="py-1" role="none">
|
||||
<div aria-labelledby="menu-button" aria-orientation="vertical" className={menuClasses} role="menu">
|
||||
<div className="flex flex-col justify-stretch py-1" role="none">
|
||||
{items.map(item => (
|
||||
<button key={item.id} className="block w-full cursor-pointer px-4 py-2 text-left text-sm text-grey-900 hover:bg-grey-100" type="button" onClick={item.onClick}>{item.label}</button>
|
||||
<button key={item.id} className="mx-1 block cursor-pointer rounded-[2.5px] px-4 py-1.5 text-left text-sm hover:bg-grey-100" type="button" onClick={item.onClick}>{item.label}</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import React from 'react';
|
||||
|
||||
interface SeparatorProps {
|
||||
color?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Separator: React.FC<SeparatorProps> = ({color}) => {
|
||||
return <hr className={`border-${color ? color : 'grey-300'}`} />;
|
||||
const Separator: React.FC<SeparatorProps> = ({className = 'border-grey-300'}) => {
|
||||
return <hr className={className} />;
|
||||
};
|
||||
|
||||
export default Separator;
|
|
@ -47,7 +47,7 @@ const Checkbox: React.FC<CheckboxProps> = ({id, title, label, value, onChange, e
|
|||
</div>
|
||||
</label>
|
||||
</div>
|
||||
{(separator || error) && <Separator color={error ? 'red' : ''} />}
|
||||
{(separator || error) && <Separator className={error ? 'border-red' : ''} />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -12,9 +12,10 @@ export interface FileUploadProps {
|
|||
className?: string;
|
||||
onUpload: (file: File) => void;
|
||||
style?: {}
|
||||
unstyled?: boolean;
|
||||
}
|
||||
|
||||
const FileUpload: React.FC<FileUploadProps> = ({id, onUpload, children, style, ...props}) => {
|
||||
const FileUpload: React.FC<FileUploadProps> = ({id, onUpload, children, style, unstyled = false, ...props}) => {
|
||||
const [fileKey, setFileKey] = useState<number>(Date.now());
|
||||
|
||||
const handleFileChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
|
@ -29,7 +30,7 @@ const FileUpload: React.FC<FileUploadProps> = ({id, onUpload, children, style, .
|
|||
<label htmlFor={id} style={style} {...props}>
|
||||
<input key={fileKey} id={id} type="file" hidden onChange={handleFileChange} />
|
||||
{(typeof children === 'string') ?
|
||||
<div className='inline-flex h-[34px] cursor-pointer items-center justify-center rounded px-4 text-sm font-semibold hover:bg-grey-100'>
|
||||
<div className={!unstyled ? `inline-flex h-[34px] cursor-pointer items-center justify-center rounded px-4 text-sm font-semibold hover:bg-grey-100` : ''}>
|
||||
{children}
|
||||
</div>
|
||||
:
|
||||
|
|
|
@ -116,7 +116,7 @@ const ImageUpload: React.FC<ImageUploadProps> = ({
|
|||
width: (unstyled ? '' : width),
|
||||
height: (unstyled ? '' : height)
|
||||
}
|
||||
} onUpload={onUpload}>
|
||||
} unstyled={unstyled} onUpload={onUpload}>
|
||||
{children}
|
||||
</FileUpload>
|
||||
);
|
||||
|
|
|
@ -58,7 +58,7 @@ const Radio: React.FC<RadioProps> = ({id, title, options, onSelect, error, hint,
|
|||
))}
|
||||
{hint && <Hint color={error ? 'red' : ''}>{hint}</Hint>}
|
||||
</div>
|
||||
{(separator || error) && <Separator color={error ? 'red' : ''} />}
|
||||
{(separator || error) && <Separator className={error ? 'border-red' : ''} />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -54,7 +54,7 @@ const Toggle: React.FC<ToggleProps> = ({id, size, direction, label, hint, separa
|
|||
</label>
|
||||
}
|
||||
</div>
|
||||
{(separator || error) && <Separator color={error ? 'red' : ''} />}
|
||||
{(separator || error) && <Separator className={error ? 'border-red' : ''} />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import Button, {IButton} from '../Button';
|
||||
import Button, {ButtonProps} from '../Button';
|
||||
import ButtonGroup from '../ButtonGroup';
|
||||
import ConfirmationModal from './ConfirmationModal';
|
||||
import Heading from '../Heading';
|
||||
|
@ -66,7 +66,7 @@ const Modal: React.FC<ModalProps> = ({
|
|||
}) => {
|
||||
const modal = useModal();
|
||||
|
||||
let buttons: IButton[] = [];
|
||||
let buttons: ButtonProps[] = [];
|
||||
|
||||
const removeModal = () => {
|
||||
if (!dirty) {
|
||||
|
|
|
@ -7,7 +7,7 @@ import NiceModal, {useModal} from '@ebay/nice-modal-react';
|
|||
import React, {useState} from 'react';
|
||||
import Select, {SelectOption} from '../form/Select';
|
||||
import TabView, {Tab} from '../TabView';
|
||||
import {IButton} from '../Button';
|
||||
import {ButtonProps} from '../Button';
|
||||
|
||||
export interface PreviewModalProps {
|
||||
testId?: string;
|
||||
|
@ -61,7 +61,7 @@ export const PreviewModalContent: React.FC<PreviewModalProps> = ({
|
|||
onSelectMobileView
|
||||
}) => {
|
||||
const modal = useModal();
|
||||
let buttons: IButton[] = [];
|
||||
let buttons: ButtonProps[] = [];
|
||||
|
||||
const [view, setView] = useState('desktop');
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import ButtonGroup from '../global/ButtonGroup';
|
||||
import React from 'react';
|
||||
import SettingGroupHeader from './SettingGroupHeader';
|
||||
import {IButton} from '../global/Button';
|
||||
import {ButtonProps} from '../global/Button';
|
||||
import {SaveState} from '../../hooks/useForm';
|
||||
|
||||
interface SettingGroupProps {
|
||||
|
@ -96,7 +96,7 @@ const SettingGroup: React.FC<SettingGroupProps> = ({
|
|||
);
|
||||
}
|
||||
|
||||
let editButtons: IButton[] = [
|
||||
let editButtons: ButtonProps[] = [
|
||||
{
|
||||
label: 'Cancel',
|
||||
key: 'cancel',
|
||||
|
|
|
@ -401,7 +401,7 @@ interface UserDetailModalProps {
|
|||
|
||||
const UserMenuTrigger = () => (
|
||||
<div className='flex h-8 cursor-pointer items-center justify-center rounded bg-[rgba(0,0,0,0.75)] px-3 opacity-80 hover:opacity-100'>
|
||||
<Icon colorClass='text-white' name='menu-horizontal' size='sm' />
|
||||
<Icon colorClass='text-white' name='ellipsis' size='md' />
|
||||
</div>
|
||||
);
|
||||
|
||||
|
|
|
@ -76,9 +76,11 @@ const UsersList: React.FC<UsersListProps> = ({users, updateUser}) => {
|
|||
key={user.id}
|
||||
action={<Button color='green' label='Edit' link={true} onClick={() => showDetailModal(user)}/>}
|
||||
avatar={(<Avatar bgColor={generateAvatarColor((user.name ? user.name : user.email))} image={user.profile_image} label={getInitials(user.name)} labelColor='white' />)}
|
||||
className='min-h-[64px]'
|
||||
detail={user.email}
|
||||
hideActions={true}
|
||||
id={`list-item-${user.id}`}
|
||||
separator={false}
|
||||
title={title}
|
||||
onClick={() => showDetailModal(user)} />
|
||||
);
|
||||
|
@ -160,9 +162,11 @@ const InvitesUserList: React.FC<InviteListProps> = ({users}) => {
|
|||
key={user.id}
|
||||
action={<UserInviteActions invite={user} />}
|
||||
avatar={(<Avatar bgColor={generateAvatarColor((user.email))} image={''} label={''} labelColor='white' />)}
|
||||
className='min-h-[64px]'
|
||||
detail={user.role}
|
||||
hideActions={true}
|
||||
id={`list-item-${user.id}`}
|
||||
separator={false}
|
||||
title={user.email}
|
||||
onClick={() => {
|
||||
// do nothing
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import Button from '../../../../admin-x-ds/global/Button';
|
||||
import Button, {ButtonProps} from '../../../../admin-x-ds/global/Button';
|
||||
import ConfirmationModal from '../../../../admin-x-ds/global/modal/ConfirmationModal';
|
||||
import Heading from '../../../../admin-x-ds/global/Heading';
|
||||
import List from '../../../../admin-x-ds/global/List';
|
||||
import ListItem from '../../../../admin-x-ds/global/ListItem';
|
||||
import Menu from '../../../../admin-x-ds/global/Menu';
|
||||
import NiceModal from '@ebay/nice-modal-react';
|
||||
import React from 'react';
|
||||
import {Theme} from '../../../../types/api';
|
||||
|
@ -21,17 +21,23 @@ interface ThemeSettingProps {
|
|||
setThemes: (themes: Theme[]) => void;
|
||||
}
|
||||
|
||||
function getThemeLabel(theme: Theme): string {
|
||||
let label = theme.package?.name || theme.name;
|
||||
function getThemeLabel(theme: Theme): React.ReactNode {
|
||||
let label: React.ReactNode = theme.package?.name || theme.name;
|
||||
|
||||
if (isDefaultTheme(theme)) {
|
||||
label += ' (default)';
|
||||
} else {
|
||||
label += ` (${theme.name})`;
|
||||
} else if (theme.package?.name !== theme.name) {
|
||||
label =
|
||||
<>
|
||||
{label} <span className='text-grey-600'>({theme.name})</span>
|
||||
</>;
|
||||
}
|
||||
|
||||
if (isActiveTheme(theme)) {
|
||||
label += ' (active)';
|
||||
label =
|
||||
<span className='font-bold'>
|
||||
{label} — <span className='text-green'> Active</span>
|
||||
</span>;
|
||||
}
|
||||
|
||||
return label;
|
||||
|
@ -100,17 +106,7 @@ const ThemeActions: React.FC<ThemeActionProps> = ({
|
|||
};
|
||||
|
||||
let actions = [];
|
||||
if (isDeletableTheme(theme)) {
|
||||
actions.push(
|
||||
<Button
|
||||
key='delete'
|
||||
color='red'
|
||||
label={'Delete'}
|
||||
link={true}
|
||||
onClick={handleDelete}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isActiveTheme(theme)) {
|
||||
actions.push(
|
||||
<Button
|
||||
|
@ -124,20 +120,30 @@ const ThemeActions: React.FC<ThemeActionProps> = ({
|
|||
);
|
||||
}
|
||||
|
||||
actions.push(
|
||||
<Button
|
||||
key='download'
|
||||
className='ml-2'
|
||||
color='green'
|
||||
label={'Download'}
|
||||
link={true}
|
||||
onClick={handleDownload}
|
||||
/>
|
||||
);
|
||||
let menuItems = [
|
||||
{
|
||||
id: 'download',
|
||||
label: 'Download',
|
||||
onClick: handleDownload
|
||||
}
|
||||
];
|
||||
|
||||
if (isDeletableTheme(theme)) {
|
||||
menuItems.push({
|
||||
id: 'delete',
|
||||
label: 'Delete',
|
||||
onClick: handleDelete
|
||||
});
|
||||
}
|
||||
|
||||
const buttonProps: ButtonProps = {
|
||||
size: 'sm'
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='flex gap-2'>
|
||||
<div className='-mr-3 flex items-center gap-4'>
|
||||
{actions}
|
||||
<Menu items={menuItems} position='left' triggerButtonProps={buttonProps} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -147,9 +153,7 @@ const ThemeList:React.FC<ThemeSettingProps> = ({
|
|||
setThemes
|
||||
}) => {
|
||||
return (
|
||||
<List
|
||||
title='Installed themes'
|
||||
>
|
||||
<List pageTitle='Installed themes'>
|
||||
{themes.map((theme) => {
|
||||
const label = getThemeLabel(theme);
|
||||
const detail = getThemeVersion(theme);
|
||||
|
@ -166,6 +170,7 @@ const ThemeList:React.FC<ThemeSettingProps> = ({
|
|||
}
|
||||
detail={detail}
|
||||
id={`theme-${theme.name}`}
|
||||
separator={false}
|
||||
title={label}
|
||||
/>
|
||||
);
|
||||
|
@ -180,13 +185,10 @@ const AdvancedThemeSettings: React.FC<ThemeSettingProps> = ({
|
|||
}) => {
|
||||
return (
|
||||
<div className='p-[8vmin] pt-5'>
|
||||
<Heading>Installed themes</Heading>
|
||||
<div className='mt-5'>
|
||||
<ThemeList
|
||||
setThemes={setThemes}
|
||||
themes={themes}
|
||||
/>
|
||||
</div>
|
||||
<ThemeList
|
||||
setThemes={setThemes}
|
||||
themes={themes}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue