mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-20 22:42:53 -05:00
AdminX Portal links (#17227)
refs. https://github.com/TryGhost/Product/issues/3545 Static version of links page in AdminX Portal settings.
This commit is contained in:
parent
31ff544e7a
commit
2642941be6
15 changed files with 256 additions and 58 deletions
|
@ -15,9 +15,10 @@ export interface ButtonProps {
|
|||
fullWidth?: boolean;
|
||||
link?: boolean;
|
||||
disabled?: boolean;
|
||||
unstyled?: boolean;
|
||||
className?: string;
|
||||
tag?: string;
|
||||
onClick?: () => void;
|
||||
onClick?: (e?:React.MouseEvent<HTMLElement>) => void;
|
||||
}
|
||||
|
||||
const Button: React.FC<ButtonProps> = ({
|
||||
|
@ -30,6 +31,7 @@ const Button: React.FC<ButtonProps> = ({
|
|||
fullWidth,
|
||||
link,
|
||||
disabled,
|
||||
unstyled = false,
|
||||
className = '',
|
||||
tag = 'button',
|
||||
onClick,
|
||||
|
@ -41,33 +43,36 @@ const Button: React.FC<ButtonProps> = ({
|
|||
|
||||
let styles = '';
|
||||
|
||||
styles += ' transition whitespace-nowrap flex items-center justify-center rounded-sm text-sm';
|
||||
styles += ((link && color !== 'clear' && color !== 'black') || (!link && color !== 'clear')) ? ' font-bold' : ' font-semibold';
|
||||
styles += !link ? `${size === 'sm' ? ' px-3 h-7 ' : ' px-4 h-[34px] '}` : '';
|
||||
if (!unstyled) {
|
||||
styles += ' transition whitespace-nowrap flex items-center justify-center rounded-sm text-sm';
|
||||
styles += ((link && color !== 'clear' && color !== 'black') || (!link && color !== 'clear')) ? ' font-bold' : ' font-semibold';
|
||||
styles += !link ? `${size === 'sm' ? ' px-3 h-7 ' : ' px-4 h-[34px] '}` : '';
|
||||
|
||||
switch (color) {
|
||||
case 'black':
|
||||
styles += link ? ' text-black hover:text-grey-800' : ' bg-black text-white hover:bg-grey-900';
|
||||
break;
|
||||
case 'grey':
|
||||
styles += link ? ' text-black hover:text-grey-800' : ' bg-grey-100 text-black hover:!bg-grey-300';
|
||||
break;
|
||||
case 'green':
|
||||
styles += link ? ' text-green hover:text-green-400' : ' bg-green text-white hover:bg-green-400';
|
||||
break;
|
||||
case 'red':
|
||||
styles += link ? ' text-red hover:text-red-400' : ' bg-red text-white hover:bg-red-400';
|
||||
break;
|
||||
case 'white':
|
||||
styles += link ? ' text-white hover:text-white' : ' bg-white text-black';
|
||||
break;
|
||||
default:
|
||||
styles += link ? ' text-black hover:text-grey-800' : ' text-black hover:bg-grey-200';
|
||||
break;
|
||||
switch (color) {
|
||||
case 'black':
|
||||
styles += link ? ' text-black hover:text-grey-800' : ' bg-black text-white hover:bg-grey-900';
|
||||
break;
|
||||
case 'grey':
|
||||
styles += link ? ' text-black hover:text-grey-800' : ' bg-grey-100 text-black hover:!bg-grey-300';
|
||||
break;
|
||||
case 'green':
|
||||
styles += link ? ' text-green hover:text-green-400' : ' bg-green text-white hover:bg-green-400';
|
||||
break;
|
||||
case 'red':
|
||||
styles += link ? ' text-red hover:text-red-400' : ' bg-red text-white hover:bg-red-400';
|
||||
break;
|
||||
case 'white':
|
||||
styles += link ? ' text-white hover:text-white' : ' bg-white text-black';
|
||||
break;
|
||||
default:
|
||||
styles += link ? ' text-black hover:text-grey-800' : ' text-black hover:bg-grey-200';
|
||||
break;
|
||||
}
|
||||
|
||||
styles += (fullWidth && !link) ? ' w-full' : '';
|
||||
styles += (disabled) ? ' opacity-40' : ' cursor-pointer';
|
||||
}
|
||||
|
||||
styles += (fullWidth && !link) ? ' w-full' : '';
|
||||
styles += (disabled) ? ' opacity-40' : ' cursor-pointer';
|
||||
styles += ` ${className}`;
|
||||
|
||||
const buttonChildren = <>
|
||||
|
|
|
@ -16,27 +16,42 @@ interface ListProps {
|
|||
title?: React.ReactNode;
|
||||
titleSeparator?: boolean;
|
||||
children?: React.ReactNode;
|
||||
actions?: React.ReactNode;
|
||||
hint?: React.ReactNode;
|
||||
hintSeparator?: boolean;
|
||||
borderTop?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const List: React.FC<ListProps> = ({title, titleSeparator, children, hint, hintSeparator, borderTop, pageTitle}) => {
|
||||
const List: React.FC<ListProps> = ({title, titleSeparator, children, actions, hint, hintSeparator, borderTop, pageTitle, className}) => {
|
||||
titleSeparator = (titleSeparator === undefined) ? true : titleSeparator;
|
||||
hintSeparator = (hintSeparator === undefined) ? true : hintSeparator;
|
||||
|
||||
const listClasses = clsx(
|
||||
(borderTop || pageTitle) && 'border-t border-grey-300',
|
||||
pageTitle && 'mt-14'
|
||||
pageTitle && 'mt-14',
|
||||
className
|
||||
);
|
||||
|
||||
let heading;
|
||||
|
||||
if (title) {
|
||||
const headingTitle = <Heading grey={true} level={6}>{title}</Heading>;
|
||||
heading = actions ? (
|
||||
<div className='flex items-end justify-between gap-2'>
|
||||
{headingTitle}
|
||||
{actions}
|
||||
</div>
|
||||
) : headingTitle;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{pageTitle && <Heading>{pageTitle}</Heading>}
|
||||
<section className={listClasses}>
|
||||
{(!pageTitle && title) &&
|
||||
<div className='flex flex-col gap-1'>
|
||||
<Heading grey={true} level={6}>{title}</Heading>
|
||||
<div className='flex flex-col items-stretch gap-1'>
|
||||
{heading}
|
||||
{titleSeparator && <Separator />}
|
||||
</div>
|
||||
}
|
||||
|
|
|
@ -18,9 +18,10 @@ interface ListItemProps {
|
|||
|
||||
bgOnHover?: boolean;
|
||||
onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const ListItem: React.FC<ListItemProps> = ({id, title, detail, action, hideActions, avatar, className, testId, separator, bgOnHover = true, onClick}) => {
|
||||
const ListItem: React.FC<ListItemProps> = ({id, title, detail, action, hideActions, avatar, className, testId, separator, bgOnHover = true, onClick, children}) => {
|
||||
const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
onClick?.(e);
|
||||
};
|
||||
|
@ -29,19 +30,21 @@ const ListItem: React.FC<ListItemProps> = ({id, title, detail, action, hideActio
|
|||
const listItemClasses = clsx(
|
||||
'group flex items-center justify-between',
|
||||
bgOnHover && 'hover:bg-gradient-to-r hover:from-white hover:to-grey-50',
|
||||
separator ? 'border-y border-grey-100 last-of-type:border-t hover:border-grey-200' : 'border-y border-transparent hover:border-grey-200 first-of-type:hover:border-t-transparent',
|
||||
separator ? 'border-b border-grey-100 last-of-type:border-b-transparent hover:border-grey-200' : 'border-y border-transparent hover:border-grey-200 first-of-type:hover:border-t-transparent',
|
||||
className
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={listItemClasses} data-testid={testId}>
|
||||
<div className={`flex grow items-center gap-3 ${onClick && 'cursor-pointer'}`} onClick={handleClick}>
|
||||
{avatar && avatar}
|
||||
<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>}
|
||||
{children ? children :
|
||||
<div className={`flex grow items-center gap-3 ${onClick && 'cursor-pointer'}`} onClick={handleClick}>
|
||||
{avatar && avatar}
|
||||
<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>
|
||||
</div>
|
||||
}
|
||||
{action &&
|
||||
<div className={`px-6 py-3 ${hideActions ? 'invisible group-hover:visible' : ''}`}>
|
||||
{action}
|
||||
|
|
|
@ -51,7 +51,7 @@ const DesktopChromeHeader: React.FC<DesktopChromeHeaderProps & React.HTMLAttribu
|
|||
);
|
||||
|
||||
return (
|
||||
<header className={`relative flex items-center justify-center bg-grey-50 ${containerSize} ${toolbarClasses}`} {...props}>
|
||||
<header className={`relative flex items-center justify-center ${containerSize} ${toolbarClasses}`} {...props}>
|
||||
{toolbarLeft ?
|
||||
<div className='absolute left-5 flex h-full items-center'>
|
||||
{toolbarLeft}
|
||||
|
|
|
@ -96,10 +96,10 @@ const Select: React.FC<SelectProps> = ({
|
|||
);
|
||||
|
||||
return (
|
||||
unstyled ? select :
|
||||
unstyled ? select : (title || hint ? (
|
||||
<div>
|
||||
{select}
|
||||
</div>
|
||||
</div>) : select)
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ export type TextFieldProps = React.InputHTMLAttributes<HTMLInputElement> & {
|
|||
containerClassName?: string;
|
||||
hintClassName?: string;
|
||||
unstyled?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const TextField: React.FC<TextFieldProps> = ({
|
||||
|
@ -39,6 +40,7 @@ const TextField: React.FC<TextFieldProps> = ({
|
|||
containerClassName = '',
|
||||
hintClassName = '',
|
||||
unstyled = false,
|
||||
disabled,
|
||||
...props
|
||||
}) => {
|
||||
const id = useId();
|
||||
|
@ -46,14 +48,16 @@ const TextField: React.FC<TextFieldProps> = ({
|
|||
const textFieldClasses = !unstyled && clsx(
|
||||
'h-10 border-b py-2',
|
||||
clearBg ? 'bg-transparent' : 'bg-grey-75 px-[10px]',
|
||||
error ? `border-red` : `border-grey-500 hover:border-grey-700 focus:border-black`,
|
||||
error ? `border-red` : `${disabled ? 'border-grey-300' : 'border-grey-500 hover:border-grey-700 focus:border-black'}`,
|
||||
(title && !hideTitle && !clearBg) && `mt-2`,
|
||||
(disabled ? 'text-grey-700' : ''),
|
||||
className
|
||||
);
|
||||
|
||||
const field = <input
|
||||
ref={inputRef}
|
||||
className={textFieldClasses || className}
|
||||
disabled={disabled}
|
||||
id={id}
|
||||
maxLength={maxLength}
|
||||
placeholder={placeholder}
|
||||
|
@ -63,13 +67,17 @@ const TextField: React.FC<TextFieldProps> = ({
|
|||
onChange={onChange}
|
||||
{...props} />;
|
||||
|
||||
return (
|
||||
<div className={`flex flex-col ${containerClassName}`}>
|
||||
{title && <Heading className={hideTitle ? 'sr-only' : ''} grey={value ? true : false} htmlFor={id} useLabelTag={true}>{title}</Heading>}
|
||||
{field}
|
||||
{hint && <Hint className={hintClassName} color={error ? 'red' : ''}>{hint}</Hint>}
|
||||
</div>
|
||||
);
|
||||
if (title || hint) {
|
||||
return (
|
||||
<div className={`flex flex-col ${containerClassName}`}>
|
||||
{title && <Heading className={hideTitle ? 'sr-only' : ''} grey={value ? true : false} htmlFor={id} useLabelTag={true}>{title}</Heading>}
|
||||
{field}
|
||||
{hint && <Hint className={hintClassName} color={error ? 'red' : ''}>{hint}</Hint>}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return field;
|
||||
}
|
||||
};
|
||||
|
||||
export default TextField;
|
||||
|
|
|
@ -2,6 +2,7 @@ import type {Meta, StoryObj} from '@storybook/react';
|
|||
|
||||
import Modal from './Modal';
|
||||
import ModalContainer from './ModalContainer';
|
||||
import ModalPage from './ModalPage';
|
||||
import NiceModal from '@ebay/nice-modal-react';
|
||||
|
||||
const meta = {
|
||||
|
@ -75,7 +76,7 @@ export const ExtraLarge: Story = {
|
|||
}
|
||||
};
|
||||
|
||||
export const full: Story = {
|
||||
export const Full: Story = {
|
||||
args: {
|
||||
size: 'full',
|
||||
onOk: () => {
|
||||
|
@ -97,6 +98,19 @@ export const Bleed: Story = {
|
|||
}
|
||||
};
|
||||
|
||||
export const CompletePage: Story = {
|
||||
args: {
|
||||
size: 'full',
|
||||
footer: <></>,
|
||||
noPadding: true,
|
||||
children: <>
|
||||
<ModalPage heading='Hey there full page'>
|
||||
<p>This is a full page in a modal</p>
|
||||
</ModalPage>
|
||||
</>
|
||||
}
|
||||
};
|
||||
|
||||
export const CustomButtons: Story = {
|
||||
args: {
|
||||
leftButtonLabel: 'Extra action',
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import type {Meta, StoryObj} from '@storybook/react';
|
||||
|
||||
import ModalPage from './ModalPage';
|
||||
|
||||
const meta = {
|
||||
title: 'Global / Modal / Modal page contents',
|
||||
component: ModalPage,
|
||||
tags: ['autodocs']
|
||||
} satisfies Meta<typeof ModalPage>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof ModalPage>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
heading: 'Here\'s a modal page',
|
||||
children: <>
|
||||
<p>Use this component to in full-width or bleed modals in which you build a complete page (e.g. Theme grid)</p>
|
||||
</>
|
||||
}
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
import Heading from '../Heading';
|
||||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
interface ModalPageProps {
|
||||
heading?: string;
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const ModalPage: React.FC<ModalPageProps> = ({heading, children, className}) => {
|
||||
className = clsx(
|
||||
'min-h-full min-w-full p-[8vmin] pt-5',
|
||||
className
|
||||
);
|
||||
return (
|
||||
<div className={className}>
|
||||
{heading && <Heading className='mb-8'>{heading}</Heading>}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModalPage;
|
|
@ -27,6 +27,7 @@ export interface PreviewModalProps {
|
|||
rightToolbar?: boolean;
|
||||
deviceSelector?: boolean;
|
||||
previewToolbarURLs?: SelectOption[];
|
||||
previewBgColor?: 'grey' | 'white';
|
||||
selectedURL?: string;
|
||||
previewToolbarTabs?: Tab[];
|
||||
defaultTab?: string;
|
||||
|
@ -58,6 +59,7 @@ export const PreviewModalContent: React.FC<PreviewModalProps> = ({
|
|||
rightToolbar = true,
|
||||
deviceSelector = true,
|
||||
previewToolbarURLs,
|
||||
previewBgColor = 'grey',
|
||||
selectedURL,
|
||||
previewToolbarTabs,
|
||||
buttonsDisabled,
|
||||
|
@ -137,7 +139,7 @@ export const PreviewModalContent: React.FC<PreviewModalProps> = ({
|
|||
);
|
||||
|
||||
preview = (
|
||||
<>
|
||||
<div className={`min-h-100 min-w-100 flex grow flex-col ${previewBgColor === 'grey' ? 'bg-grey-50' : 'bg-white'}`}>
|
||||
<DesktopChromeHeader
|
||||
data-testid="design-toolbar"
|
||||
size='lg'
|
||||
|
@ -145,10 +147,10 @@ export const PreviewModalContent: React.FC<PreviewModalProps> = ({
|
|||
toolbarLeft={leftToolbar && toolbarLeft}
|
||||
toolbarRight={rightToolbar && toolbarRight}
|
||||
/>
|
||||
<div className='flex h-full grow items-center justify-center bg-grey-50 text-sm text-grey-400'>
|
||||
<div className='flex h-full grow items-center justify-center text-sm text-grey-400'>
|
||||
{preview}
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -149,6 +149,7 @@ const PortalModal: React.FC = () => {
|
|||
dirty={saveState === 'unsaved'}
|
||||
okLabel='Save & close'
|
||||
preview={preview}
|
||||
previewBgColor={selectedPreviewTab === 'links' ? 'white' : 'grey'}
|
||||
previewToolbarTabs={previewTabs}
|
||||
selectedURL={selectedPreviewTab}
|
||||
sidebar={sidebar}
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
import Button from '../../../../admin-x-ds/global/Button';
|
||||
import List from '../../../../admin-x-ds/global/List';
|
||||
import ListItem from '../../../../admin-x-ds/global/ListItem';
|
||||
import ModalPage from '../../../../admin-x-ds/global/modal/ModalPage';
|
||||
import React, {useContext, useState} from 'react';
|
||||
import Select from '../../../../admin-x-ds/global/form/Select';
|
||||
import TextField from '../../../../admin-x-ds/global/form/TextField';
|
||||
import {SettingsContext} from '../../../providers/SettingsProvider';
|
||||
import {getHomepageUrl} from '../../../../utils/helpers';
|
||||
|
||||
interface PortalLinksPrefs {
|
||||
|
||||
}
|
||||
|
||||
interface PortalLinkPrefs {
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
const PortalLink: React.FC<PortalLinkPrefs> = ({name, value}) => {
|
||||
return (
|
||||
<ListItem
|
||||
action={<Button color='black' label='Copy' link onClick={(e) => {
|
||||
navigator.clipboard.writeText(value);
|
||||
const button = e?.target as HTMLButtonElement;
|
||||
button.innerText = 'Copied';
|
||||
setTimeout(() => {
|
||||
button.innerText = 'Copy';
|
||||
}, 1000);
|
||||
}}/>}
|
||||
hideActions
|
||||
separator
|
||||
>
|
||||
<div className='flex w-full grow items-center gap-5 py-3'>
|
||||
<span className='inline-block w-[200px] whitespace-nowrap'>{name}</span>
|
||||
<TextField className='border-b-500 grow bg-transparent p-1 text-grey-700' value={value} disabled unstyled />
|
||||
</div>
|
||||
</ListItem>
|
||||
);
|
||||
};
|
||||
|
||||
const PortalLinks: React.FC<PortalLinksPrefs> = () => {
|
||||
const [isDataAttributes, setIsDataAttributes] = useState(false);
|
||||
const {siteData} = useContext(SettingsContext);
|
||||
|
||||
const toggleIsDataAttributes = () => {
|
||||
setIsDataAttributes(!isDataAttributes);
|
||||
};
|
||||
|
||||
const homePageURL = getHomepageUrl(siteData!);
|
||||
|
||||
return (
|
||||
<ModalPage className='text-base text-black' heading='Links'>
|
||||
<p className='-mt-6 mb-16'>Use these {isDataAttributes ? 'data attributes' : 'links'} in your theme to show pages of Portal.</p>
|
||||
|
||||
<List actions={<Button color='green' label={isDataAttributes ? 'Links' : 'Data attributes'} link onClick={toggleIsDataAttributes}/>} title='Generic'>
|
||||
<PortalLink name='Default' value={isDataAttributes ? 'data-portal' : `${homePageURL}/#/portal`} />
|
||||
<PortalLink name='Sign in' value={isDataAttributes ? 'data-portal="signin"' : `${homePageURL}/#/portal/signin`} />
|
||||
<PortalLink name='Sign up' value={isDataAttributes ? 'data-portal="signup"' : `${homePageURL}/#/portal/signup`} />
|
||||
</List>
|
||||
|
||||
<List className='mt-14' title='Tiers'>
|
||||
<ListItem
|
||||
hideActions
|
||||
separator
|
||||
>
|
||||
<div className='flex w-full items-center gap-5 py-3 pr-6'>
|
||||
<span className='inline-block w-[200px] shrink-0 font-bold'>Tier</span>
|
||||
<Select
|
||||
containerClassName='max-w-[400px]'
|
||||
options={[
|
||||
{
|
||||
label: 'Tier one',
|
||||
value: 'tier-one'
|
||||
},
|
||||
{
|
||||
label: 'Tier two',
|
||||
value: 'tier-two'
|
||||
}
|
||||
]}
|
||||
onSelect={() => {
|
||||
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</ListItem>
|
||||
<PortalLink name='Signup / Monthly' value={isDataAttributes ? 'data-portal="signup/abc123/monthly"' : `${homePageURL}/#/portal/signup/abc123/monthly`} />
|
||||
<PortalLink name='Signup / Yearly' value={isDataAttributes ? 'data-portal="signup/abc123/yearly"' : `${homePageURL}/#/portal/signup/abc123/yearly`} />
|
||||
<PortalLink name='Signup / Free' value={isDataAttributes ? 'data-portal="signup/free"' : `${homePageURL}/#/portal/signup/free`} />
|
||||
</List>
|
||||
|
||||
<List className='mt-14' title='Account'>
|
||||
<PortalLink name='Account' value={isDataAttributes ? 'data-portal="account"' : `${homePageURL}/#/portal/account`} />
|
||||
<PortalLink name='Account / Plans' value={isDataAttributes ? 'data-portal="account/plans"' : `${homePageURL}/#/portal/account/plans`} />
|
||||
<PortalLink name='Account / Profile' value={isDataAttributes ? 'data-portal="account/profile"' : `${homePageURL}/#/portal/account/profile`} />
|
||||
<PortalLink name='Account / Newsletters' value={isDataAttributes ? 'data-portal="account/newsletters"' : `${homePageURL}/#/portal/account/newsletters`} />
|
||||
</List>
|
||||
</ModalPage>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default PortalLinks;
|
|
@ -1,4 +1,5 @@
|
|||
import PortalFrame from './PortalFrame';
|
||||
import PortalLinks from './PortalLinks';
|
||||
import React from 'react';
|
||||
import {Setting, Tier} from '../../../../types/api';
|
||||
|
||||
|
@ -24,7 +25,7 @@ const PortalPreview: React.FC<PortalPreviewProps> = ({
|
|||
);
|
||||
break;
|
||||
case 'links':
|
||||
tabContents = <>Links</>;
|
||||
tabContents = <PortalLinks />;
|
||||
break;
|
||||
default:
|
||||
tabContents = (
|
||||
|
|
|
@ -3,6 +3,7 @@ import ConfirmationModal from '../../../../admin-x-ds/global/modal/ConfirmationM
|
|||
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 ModalPage from '../../../../admin-x-ds/global/modal/ModalPage';
|
||||
import NiceModal from '@ebay/nice-modal-react';
|
||||
import React from 'react';
|
||||
import {Theme} from '../../../../types/api';
|
||||
|
@ -196,12 +197,12 @@ const AdvancedThemeSettings: React.FC<ThemeSettingProps> = ({
|
|||
setThemes
|
||||
}) => {
|
||||
return (
|
||||
<div className='p-[8vmin] pt-5'>
|
||||
<ModalPage>
|
||||
<ThemeList
|
||||
setThemes={setThemes}
|
||||
themes={themes}
|
||||
/>
|
||||
</div>
|
||||
</ModalPage>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import Heading from '../../../../admin-x-ds/global/Heading';
|
||||
import ModalPage from '../../../../admin-x-ds/global/modal/ModalPage';
|
||||
import React from 'react';
|
||||
import {OfficialTheme} from '../../../../models/themes';
|
||||
import {getGhostPaths} from '../../../../utils/helpers';
|
||||
|
@ -13,8 +14,7 @@ const OfficialThemes: React.FC<{
|
|||
const officialThemes = useOfficialThemes();
|
||||
|
||||
return (
|
||||
<div className='h-[calc(100vh-74px-40px)] overflow-y-auto overflow-x-hidden p-[8vmin] pt-5'>
|
||||
<Heading>Themes</Heading>
|
||||
<ModalPage heading='Themes'>
|
||||
<div className='mt-[6vmin] grid grid-cols-1 gap-[6vmin] sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4'>
|
||||
{officialThemes.map((theme) => {
|
||||
return (
|
||||
|
@ -37,7 +37,7 @@ const OfficialThemes: React.FC<{
|
|||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</ModalPage>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue