0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-10 23:36:14 -05:00

Added portal icon picker in AdminX settings (#17216)

refs https://github.com/TryGhost/Product/issues/3545
This commit is contained in:
Jono M 2023-07-06 11:16:52 +12:00 committed by GitHub
parent 3d6848f5fd
commit b9158215ee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 99 additions and 6 deletions

View file

@ -1,6 +1,6 @@
import FileUpload from './FileUpload';
import Icon from '../Icon';
import React from 'react';
import React, {MouseEventHandler} from 'react';
import clsx from 'clsx';
type ImageFit = 'cover' | 'contain' | 'fill' | 'scale-down' | 'none';
@ -25,6 +25,7 @@ interface ImageUploadProps {
unstyled?: boolean;
onUpload: (file: File) => void;
onDelete: () => void;
onImageClick?: MouseEventHandler<HTMLImageElement>;
}
const ImageUpload: React.FC<ImageUploadProps> = ({
@ -42,7 +43,8 @@ const ImageUpload: React.FC<ImageUploadProps> = ({
imageBWCheckedBg = false,
unstyled = false,
onUpload,
onDelete
onDelete,
onImageClick
}) => {
if (!unstyled) {
imageContainerClassName = clsx(
@ -82,7 +84,7 @@ const ImageUpload: React.FC<ImageUploadProps> = ({
<img alt='' className={imageClassName} id={id} src={imageURL} style={{
width: (unstyled ? '' : width || '100%'),
height: (unstyled ? '' : height || 'auto')
}} />
}} onClick={onImageClick} />
<button className={deleteButtonClassName} type='button' onClick={onDelete}>
{deleteButtonContent}
</button>

View file

@ -0,0 +1,4 @@
<svg width="21" height="24" viewBox="0 0 21 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>portal-icon-1</title>
<path d="M10.533 11.267a5.135 5.135 0 1 0-.001-10.27 5.135 5.135 0 0 0 .001 10.27zM1 23a9.531 9.531 0 0 1 16.274-6.741 9.532 9.532 0 0 1 2.793 6.74" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 375 B

View file

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<title>portal-icon-2</title>
<path stroke="currentColor" stroke-width="1.5" stroke-linecap="round" d="M12.5 2v20M2 12.5h20" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 233 B

View file

@ -0,0 +1,5 @@
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>portal-icon-3</title>
<path d="M23.5 6v14.25a2.25 2.25 0 1 1-4.5 0V3c0-.398-.158-.78-.44-1.06a1.494 1.494 0 0 0-1.06-.44h-15c-.398 0-.78.158-1.06.44C1.157 2.22 1 2.601 1 3v17.25a2.25 2.25 0 0 0 2.25 2.25h18M4.75 15h10.5m-10.5 3h6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14.5 5.25h-9a.75.75 0 0 0-.75.75v4.5c0 .414.336.75.75.75h9a.75.75 0 0 0 .75-.75V6a.75.75 0 0 0-.75-.75z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 642 B

View file

@ -0,0 +1,5 @@
<svg width="24" height="18" viewBox="0 0 24 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>portal-icon-4</title>
<path d="M21.75 1.5H2.25A1.5 1.5 0 0 0 .75 3v12a1.5 1.5 0 0 0 1.5 1.5h19.5a1.5 1.5 0 0 0 1.5-1.5V3a1.5 1.5 0 0 0-1.5-1.5zm-6.063 5.475L19.5 10.5M8.313 6.975 4.5 10.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="m22.88 2.014-9.513 6.56a2.41 2.41 0 0 1-2.734 0L1.12 2.014" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 554 B

View file

@ -0,0 +1,5 @@
<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>portal-icon-5</title>
<path d="M17.903 12.016a5.007 5.007 0 0 0-3.031-3.654m-3.835.038a5.002 5.002 0 0 0-2.879 5.85m2.282 3.046A4.975 4.975 0 0 0 13 18a4.99 4.99 0 0 0 4.12-2.167m-1.949 5.387a8.504 8.504 0 0 0 5.756-11.295m-2.316-3.31A8.474 8.474 0 0 0 13 4.5a8.461 8.461 0 0 0-5.608 2.113m-2.28 3.213a8.503 8.503 0 0 0 5.914 11.444" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M8.924 24.29c1.273.46 2.645.71 4.076.71 5.52 0 10.17-3.727 11.57-8.803M6.712 2.777A11.994 11.994 0 0 0 1 13c0 3.545 1.537 6.731 3.982 8.928m19.867-10.839C23.933 5.369 18.977 1 13 1c-.69 0-1.367.058-2.025.17" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 799 B

View file

@ -1,16 +1,66 @@
import Form from '../../../../admin-x-ds/global/form/Form';
import React from 'react';
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 Toggle from '../../../../admin-x-ds/global/form/Toggle';
import {Setting, SettingValue} from '../../../../types/api';
import {getSettingValues} from '../../../../utils/helpers';
import ImageUpload from '../../../../admin-x-ds/global/form/ImageUpload';
import clsx from 'clsx';
import {ReactComponent as PortalIcon1} from '../../../../assets/icons/portal-icon-1.svg';
import {ReactComponent as PortalIcon2} from '../../../../assets/icons/portal-icon-2.svg';
import {ReactComponent as PortalIcon3} from '../../../../assets/icons/portal-icon-3.svg';
import {ReactComponent as PortalIcon4} from '../../../../assets/icons/portal-icon-4.svg';
import {ReactComponent as PortalIcon5} from '../../../../assets/icons/portal-icon-5.svg';
import {ServicesContext} from '../../../providers/ServiceProvider';
const defaultButtonIcons = [
{
Component: PortalIcon1,
value: 'icon-1'
},
{
Component: PortalIcon2,
value: 'icon-2'
},
{
Component: PortalIcon3,
value: 'icon-3'
},
{
Component: PortalIcon4,
value: 'icon-4'
},
{
Component: PortalIcon5,
value: 'icon-5'
}
];
const LookAndFeel: React.FC<{
localSettings: Setting[]
updateSetting: (key: string, setting: SettingValue) => void
}> = ({localSettings, updateSetting}) => {
const [portalButton, portalButtonStyle, portalButtonSignupText] = getSettingValues(localSettings, ['portal_button', 'portal_button_style', 'portal_button_signup_text']);
const {fileService} = useContext(ServicesContext);
const [portalButton, portalButtonStyle, portalButtonIcon, portalButtonSignupText] = getSettingValues(localSettings, ['portal_button', 'portal_button_style', 'portal_button_icon', 'portal_button_signup_text']);
const currentIcon = portalButtonIcon as string || defaultButtonIcons[0].value;
const isDefaultIcon = defaultButtonIcons.map(({value}) => value).includes(currentIcon);
const [uploadedIcon, setUploadedIcon] = useState(isDefaultIcon ? undefined : currentIcon);
const handleImageUpload = async (file: File) => {
const imageUrl = await fileService!.uploadImage(file);
updateSetting('portal_button_icon', imageUrl);
setUploadedIcon(imageUrl);
};
const handleImageDelete = () => {
updateSetting('portal_button_icon', null);
setUploadedIcon(undefined);
};
return <Form marginTop>
<Toggle
@ -29,7 +79,25 @@ const LookAndFeel: React.FC<{
title='Portal button style'
onSelect={option => updateSetting('portal_button_style', option)}
/>
{portalButtonStyle?.toString()?.includes('icon') && <div className='red text-sm'>TODO: icon picker</div>}
{portalButtonStyle?.toString()?.includes('icon') &&
<div className='flex gap-2 border border-green p-4'>
{defaultButtonIcons.map(icon => (
<button className={clsx('border p-4', currentIcon === icon.value ? 'border-green' : 'border-transparent')} type="button" onClick={() => updateSetting('portal_button_icon', icon.value)}>
<icon.Component className="h-6 w-6 text-green" />
</button>
))}
<div className={clsx('w-10 border', currentIcon === uploadedIcon ? 'border-green' : 'border-transparent')}>
<ImageUpload
id='test'
imageClassName='cursor-pointer'
imageURL={uploadedIcon}
onDelete={handleImageDelete}
onImageClick={() => uploadedIcon && updateSetting('portal_button_icon', uploadedIcon)}
onUpload={handleImageUpload}
/>
</div>
</div>
}
{portalButtonStyle?.toString()?.includes('text') &&
<TextField
title='Signup button text'