0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-17 23:44:39 -05:00

AdminX design fixes (#18029)

refs. https://github.com/TryGhost/Product/issues/3349

- Fixed change theme responsive issues
- Added marketplace link to themes
- Installed theme refinements
- Added current theme indicator
- Improved disabled textfield a bit
This commit is contained in:
Peter Zimon 2023-09-08 14:58:31 +03:00 committed by GitHub
parent 6dc1d08590
commit c1cc0b59f2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 104 additions and 27 deletions

View file

@ -1,5 +1,6 @@
import Button from './Button'; import Button from './Button';
import React from 'react'; import React from 'react';
import clsx from 'clsx';
export type BreadcrumbItem = { export type BreadcrumbItem = {
label: React.ReactNode; label: React.ReactNode;
@ -10,35 +11,58 @@ interface BreadcrumbsProps {
items: BreadcrumbItem[]; items: BreadcrumbItem[];
backIcon?: boolean; backIcon?: boolean;
onBack?: () => void; onBack?: () => void;
containerClassName?: string;
itemClassName?: string;
activeItemClassName?: string;
separatorClassName?: string;
} }
const Breadcrumbs: React.FC<BreadcrumbsProps> = ({ const Breadcrumbs: React.FC<BreadcrumbsProps> = ({
items, items,
backIcon = false, backIcon = false,
onBack onBack,
containerClassName,
itemClassName,
activeItemClassName,
separatorClassName
}) => { }) => {
const allItems = items.length; const allItems = items.length;
let i = 0; let i = 0;
containerClassName = clsx(
'flex items-center gap-2 text-sm',
containerClassName
);
activeItemClassName = clsx(
'font-bold',
activeItemClassName
);
itemClassName = clsx(
'text-sm',
itemClassName
);
return ( return (
<div className='flex items-center gap-2 text-sm'> <div className={containerClassName}>
{backIcon && {backIcon &&
<Button className='mr-6' icon='arrow-left' size='sm' link onClick={onBack} /> <Button className='mr-6' icon='arrow-left' size='sm' link onClick={onBack} />
} }
{items.map((item) => { {items.map((item) => {
const bcItem = (i === allItems - 1 ? const bcItem = (i === allItems - 1 ?
<span className='font-bold'>{item.label}</span> <span className={activeItemClassName}>{item.label}</span>
: :
<> <>
<button <button
key={`bc-${i}`} key={`bc-${i}`}
className={` text-sm ${item.onClick && '-mx-1 cursor-pointer rounded-sm px-1 py-px hover:bg-grey-100'}`} className={`${itemClassName} ${item.onClick && '-mx-1 cursor-pointer rounded-sm px-1 py-px hover:bg-grey-100'}`}
type="button" type="button"
onClick={item.onClick} onClick={item.onClick}
> >
{item.label} {item.label}
</button> </button>
<span>/</span> <span className={separatorClassName}>/</span>
</>); </>);
i = i + 1; i = i + 1;
return bcItem; return bcItem;

View file

@ -30,6 +30,14 @@ export const Default: Story = {
} }
}; };
export const Disabled: Story = {
args: {
placeholder: `Here's a disabled field`,
title: 'Disabled',
disabled: true
}
};
export const ClearBackground: Story = { export const ClearBackground: Story = {
args: { args: {
placeholder: 'Enter something', placeholder: 'Enter something',

View file

@ -61,7 +61,7 @@ const TextField: React.FC<TextFieldProps> = ({
clearBg ? 'bg-transparent' : 'bg-grey-75 px-[10px]', clearBg ? 'bg-transparent' : 'bg-grey-75 px-[10px]',
error && border ? `border-red` : `${disabled ? disabledBorderClasses : enabledBorderClasses}`, error && border ? `border-red` : `${disabled ? disabledBorderClasses : enabledBorderClasses}`,
(title && !hideTitle && !clearBg) && `mt-2`, (title && !hideTitle && !clearBg) && `mt-2`,
(disabled ? 'text-grey-700' : ''), (disabled ? 'cursor-not-allowed text-grey-700' : ''),
rightPlaceholder && 'w-0 grow', rightPlaceholder && 'w-0 grow',
className className
); );
@ -106,9 +106,14 @@ const TextField: React.FC<TextFieldProps> = ({
hintClassName hintClassName
); );
containerClassName = clsx(
'flex flex-col',
containerClassName
);
if (title || hint) { if (title || hint) {
return ( return (
<div className={`flex flex-col ${containerClassName}`}> <div className={containerClassName}>
{field} {field}
{title && <Heading className={hideTitle ? 'sr-only' : 'order-1 !text-grey-700 peer-focus:!text-black'} htmlFor={id} useLabelTag={true}>{title}</Heading>} {title && <Heading className={hideTitle ? 'sr-only' : 'order-1 !text-grey-700 peer-focus:!text-black'} htmlFor={id} useLabelTag={true}>{title}</Heading>}
{hint && <Hint className={hintClassName} color={error ? 'red' : ''}>{hint}</Hint>} {hint && <Hint className={hintClassName} color={error ? 'red' : ''}>{hint}</Hint>}

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 KiB

View file

@ -15,6 +15,7 @@ import {PreviewModalContent} from '../../../admin-x-ds/global/modal/PreviewModal
import {Setting, SettingValue, getSettingValues, useEditSettings} from '../../../api/settings'; import {Setting, SettingValue, getSettingValues, useEditSettings} from '../../../api/settings';
import {getHomepageUrl} from '../../../api/site'; import {getHomepageUrl} from '../../../api/site';
import {useBrowsePosts} from '../../../api/posts'; import {useBrowsePosts} from '../../../api/posts';
import {useBrowseThemes} from '../../../api/themes';
import {useGlobalData} from '../../providers/GlobalDataProvider'; import {useGlobalData} from '../../providers/GlobalDataProvider';
const Sidebar: React.FC<{ const Sidebar: React.FC<{
@ -36,6 +37,9 @@ const Sidebar: React.FC<{
}) => { }) => {
const {updateRoute} = useRouting(); const {updateRoute} = useRouting();
const [selectedTab, setSelectedTab] = useState('brand'); const [selectedTab, setSelectedTab] = useState('brand');
const {data: {themes} = {}} = useBrowseThemes();
const activeTheme = themes?.find(theme => theme.active);
const tabs: Tab[] = [ const tabs: Tab[] = [
{ {
@ -67,7 +71,10 @@ const Sidebar: React.FC<{
modal.remove(); modal.remove();
updateRoute('design/edit/themes'); updateRoute('design/edit/themes');
}}> }}>
Change theme <div className='text-left'>
<div className='font-semibold'>Change theme</div>
<div className='font-sm text-grey-700'>Current theme: {activeTheme?.name}</div>
</div>
<Icon className='mr-2 transition-all group-hover:translate-x-2' name='chevron-right' size='sm' /> <Icon className='mr-2 transition-all group-hover:translate-x-2' name='chevron-right' size='sm' />
</button> </button>
</div> </div>

View file

@ -114,26 +114,31 @@ const ThemeToolbar: React.FC<ThemeToolbarProps> = ({
const left = const left =
<Breadcrumbs <Breadcrumbs
activeItemClassName='hidden md:!block md:!visible'
itemClassName='hidden md:!block md:!visible'
items={[ items={[
{label: 'Design', onClick: onClose}, {label: 'Design', onClick: onClose},
{label: 'Change theme'} {label: 'Change theme'}
]} ]}
separatorClassName='hidden md:!block md:!visible'
backIcon backIcon
onBack={onClose} onBack={onClose}
/>; />;
const right = const right =
<div className='flex items-center gap-14'> <div className='flex items-center gap-14'>
<TabView <div className='hidden md:!visible md:!block'>
border={false} <TabView
selectedTab={currentTab} border={false}
tabs={[ selectedTab={currentTab}
{id: 'official', title: 'Official themes'}, tabs={[
{id: 'installed', title: 'Installed'} {id: 'official', title: 'Official themes'},
]} {id: 'installed', title: 'Installed'}
onTabChange={(id: string) => { ]}
setCurrentTab(id); onTabChange={(id: string) => {
}} /> setCurrentTab(id);
}} />
</div>
<div className='flex items-center gap-3'> <div className='flex items-center gap-3'>
{uploadConfig && ( {uploadConfig && (
uploadConfig.enabled ? uploadConfig.enabled ?
@ -176,7 +181,21 @@ const ThemeToolbar: React.FC<ThemeToolbarProps> = ({
</div> </div>
</div>; </div>;
return <PageHeader containerClassName='bg-white' left={left} right={right} />; return (<>
<PageHeader containerClassName='bg-white' left={left} right={right} />
<div className='px-[8vmin] md:hidden'>
<TabView
border={false}
selectedTab={currentTab}
tabs={[
{id: 'official', title: 'Official themes'},
{id: 'installed', title: 'Installed'}
]}
onTabChange={(id: string) => {
setCurrentTab(id);
}} />
</div>
</>);
}; };
const ThemeModalContent: React.FC<ThemeModalContentProps> = ({ const ThemeModalContent: React.FC<ThemeModalContentProps> = ({
@ -272,6 +291,7 @@ const ChangeThemeModal = NiceModal.create(() => {
afterClose={() => { afterClose={() => {
updateRoute('design/edit'); updateRoute('design/edit');
}} }}
animate={false}
cancelLabel='' cancelLabel=''
footer={false} footer={false}
padding={false} padding={false}
@ -307,11 +327,13 @@ const ChangeThemeModal = NiceModal.create(() => {
setSelectedTheme={setSelectedTheme} setSelectedTheme={setSelectedTheme}
themes={themes} themes={themes}
/> />
<ThemeModalContent {!selectedTheme &&
currentTab={currentTab} <ThemeModalContent
themes={themes} currentTab={currentTab}
onSelectTheme={onSelectTheme} themes={themes}
/> onSelectTheme={onSelectTheme}
/>
}
</div> </div>
</div> </div>
</Modal> </Modal>

View file

@ -24,14 +24,14 @@ function getThemeLabel(theme: Theme): React.ReactNode {
label += ' (default)'; label += ' (default)';
} else if (theme.package?.name !== theme.name) { } else if (theme.package?.name !== theme.name) {
label = label =
<> <span className='text-sm md:text-base'>
{label} <span className='text-grey-600'>({theme.name})</span> {label} <span className='text-grey-600'>({theme.name})</span>
</>; </span>;
} }
if (isActiveTheme(theme)) { if (isActiveTheme(theme)) {
label = label =
<span className="font-bold"> <span className="text-sm font-bold md:text-base">
{label} &mdash; <span className='text-green'> Active</span> {label} &mdash; <span className='text-green'> Active</span>
</span>; </span>;
} }

View file

@ -1,4 +1,5 @@
import Heading from '../../../../admin-x-ds/global/Heading'; import Heading from '../../../../admin-x-ds/global/Heading';
import MarketplaceBgImage from '../../../../assets/images/footer-marketplace-bg.png';
import ModalPage from '../../../../admin-x-ds/global/modal/ModalPage'; import ModalPage from '../../../../admin-x-ds/global/modal/ModalPage';
import React from 'react'; import React from 'react';
import {OfficialTheme, useOfficialThemes} from '../../../providers/ServiceProvider'; import {OfficialTheme, useOfficialThemes} from '../../../providers/ServiceProvider';
@ -36,6 +37,13 @@ const OfficialThemes: React.FC<{
); );
})} })}
</div> </div>
<div className='mx-[-8vmin] mb-[-8vmin] mt-[8vmin] bg-black px-[8vmin] py-16 text-center text-lg text-white' style={
{
background: `#15171a url(${MarketplaceBgImage}) 100% 100% / 35vw no-repeat`
}
}>
Find and buy third-party, premium themes from independent developers in the <a className='inline-block font-semibold text-lime' href="https://ghost.org/themes/" rel="noopener noreferrer" target="_blank">Ghost Marketplace &rarr;</a>
</div>
</ModalPage> </ModalPage>
); );
}; };

View file

@ -59,11 +59,14 @@ const ThemePreview: React.FC<{
const left = const left =
<div className='flex items-center gap-2'> <div className='flex items-center gap-2'>
<Breadcrumbs <Breadcrumbs
activeItemClassName='hidden md:!block md:!visible'
itemClassName='hidden md:!block md:!visible'
items={[ items={[
{label: 'Design', onClick: onClose}, {label: 'Design', onClick: onClose},
{label: 'Change theme', onClick: onBack}, {label: 'Change theme', onClick: onBack},
{label: selectedTheme.name} {label: selectedTheme.name}
]} ]}
separatorClassName='hidden md:!block md:!visible'
backIcon backIcon
onBack={onBack} onBack={onBack}
/> />