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 React from 'react';
import clsx from 'clsx';
export type BreadcrumbItem = {
label: React.ReactNode;
@ -10,35 +11,58 @@ interface BreadcrumbsProps {
items: BreadcrumbItem[];
backIcon?: boolean;
onBack?: () => void;
containerClassName?: string;
itemClassName?: string;
activeItemClassName?: string;
separatorClassName?: string;
}
const Breadcrumbs: React.FC<BreadcrumbsProps> = ({
items,
backIcon = false,
onBack
onBack,
containerClassName,
itemClassName,
activeItemClassName,
separatorClassName
}) => {
const allItems = items.length;
let i = 0;
containerClassName = clsx(
'flex items-center gap-2 text-sm',
containerClassName
);
activeItemClassName = clsx(
'font-bold',
activeItemClassName
);
itemClassName = clsx(
'text-sm',
itemClassName
);
return (
<div className='flex items-center gap-2 text-sm'>
<div className={containerClassName}>
{backIcon &&
<Button className='mr-6' icon='arrow-left' size='sm' link onClick={onBack} />
}
{items.map((item) => {
const bcItem = (i === allItems - 1 ?
<span className='font-bold'>{item.label}</span>
<span className={activeItemClassName}>{item.label}</span>
:
<>
<button
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"
onClick={item.onClick}
>
{item.label}
</button>
<span>/</span>
<span className={separatorClassName}>/</span>
</>);
i = i + 1;
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 = {
args: {
placeholder: 'Enter something',

View File

@ -61,7 +61,7 @@ const TextField: React.FC<TextFieldProps> = ({
clearBg ? 'bg-transparent' : 'bg-grey-75 px-[10px]',
error && border ? `border-red` : `${disabled ? disabledBorderClasses : enabledBorderClasses}`,
(title && !hideTitle && !clearBg) && `mt-2`,
(disabled ? 'text-grey-700' : ''),
(disabled ? 'cursor-not-allowed text-grey-700' : ''),
rightPlaceholder && 'w-0 grow',
className
);
@ -106,9 +106,14 @@ const TextField: React.FC<TextFieldProps> = ({
hintClassName
);
containerClassName = clsx(
'flex flex-col',
containerClassName
);
if (title || hint) {
return (
<div className={`flex flex-col ${containerClassName}`}>
<div className={containerClassName}>
{field}
{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>}

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 {getHomepageUrl} from '../../../api/site';
import {useBrowsePosts} from '../../../api/posts';
import {useBrowseThemes} from '../../../api/themes';
import {useGlobalData} from '../../providers/GlobalDataProvider';
const Sidebar: React.FC<{
@ -36,6 +37,9 @@ const Sidebar: React.FC<{
}) => {
const {updateRoute} = useRouting();
const [selectedTab, setSelectedTab] = useState('brand');
const {data: {themes} = {}} = useBrowseThemes();
const activeTheme = themes?.find(theme => theme.active);
const tabs: Tab[] = [
{
@ -67,7 +71,10 @@ const Sidebar: React.FC<{
modal.remove();
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' />
</button>
</div>

View File

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

View File

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

View File

@ -1,4 +1,5 @@
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 React from 'react';
import {OfficialTheme, useOfficialThemes} from '../../../providers/ServiceProvider';
@ -36,6 +37,13 @@ const OfficialThemes: React.FC<{
);
})}
</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>
);
};

View File

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