mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-11 02:12:21 -05:00
Fixed minor AdminX logic issues (#18209)
refs https://github.com/TryGhost/Product/issues/3832 --- ### <samp>🤖 Generated by Copilot at fd2a023</samp> This pull request improves the UI and UX of the admin settings app by fixing some bugs, enhancing some components, and adding some logic to handle different feature states. It affects the `ColorPickerField`, `Toggle`, `EnableNewsletters`, `TierDetailModal`, `Modal`, and `DefaultRecipients` components, as well as the files `ColorPickerField.tsx`, `Toggle.tsx`, `EnableNewsletters.tsx`, `TierDetailModal.tsx`, `Modal.tsx`, and `DefaultRecipients.tsx`.
This commit is contained in:
parent
530492635f
commit
bd013ed18c
6 changed files with 43 additions and 9 deletions
|
@ -9,6 +9,14 @@ const ColorPickerContext = createContext<{colorPickers: Array<{ id: string; setE
|
|||
colorPickers: []
|
||||
});
|
||||
|
||||
const to6DigitHex = (hex: string) => {
|
||||
if (hex.length === 4) {
|
||||
return hex.replace(/#(.)(.)(.)/, '#$1$1$2$2$3$3');
|
||||
} else {
|
||||
return hex;
|
||||
}
|
||||
};
|
||||
|
||||
const ColorPickerField = ({testId, title, direction, value, hint, error, eyedropper, clearButtonValue, onChange, swatches = [], alwaysOpen = false, debounceMs}: {
|
||||
testId?: string;
|
||||
title?: ReactNode;
|
||||
|
@ -29,7 +37,15 @@ const ColorPickerField = ({testId, title, direction, value, hint, error, eyedrop
|
|||
const id = useId();
|
||||
|
||||
useEffect(() => {
|
||||
setLocalValue(value);
|
||||
setLocalValue((currentValue) => {
|
||||
// If the current value is the 3-digit equivalent of the new value,
|
||||
// the user probably typed it as 3 digits so keep showing it that way in the UI
|
||||
if (to6DigitHex(currentValue || '') === value) {
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
}, [value]);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -66,7 +82,7 @@ const ColorPickerField = ({testId, title, direction, value, hint, error, eyedrop
|
|||
|
||||
const handleChange = (newValue: string | null) => {
|
||||
setLocalValue(newValue);
|
||||
debouncedOnChange?.(newValue);
|
||||
debouncedOnChange?.(newValue ? to6DigitHex(newValue) : null);
|
||||
};
|
||||
|
||||
let content = (
|
||||
|
|
|
@ -32,6 +32,7 @@ const Toggle: React.FC<ToggleProps> = ({
|
|||
separator,
|
||||
error,
|
||||
checked,
|
||||
disabled,
|
||||
onChange
|
||||
}) => {
|
||||
const id = useId();
|
||||
|
@ -83,7 +84,16 @@ const Toggle: React.FC<ToggleProps> = ({
|
|||
<div>
|
||||
<div className={`group flex items-start gap-2 dark:text-white ${direction === 'rtl' && 'justify-between'} ${separator && 'pb-2'}`}>
|
||||
<input checked={checked}
|
||||
className={`${toggleBgClass} appearance-none rounded-full bg-grey-300 transition after:absolute after:ml-0.5 after:mt-0.5 after:rounded-full after:border-none after:bg-white after:transition-[background-color_0.2s,transform_0.2s] after:content-[''] checked:after:absolute checked:after:rounded-full checked:after:border-none checked:after:bg-white checked:after:transition-[background-color_0.2s,transform_0.2s] checked:after:content-[''] hover:cursor-pointer group-hover:opacity-80 dark:bg-grey-800 ${sizeStyles} ${direction === 'rtl' && ' order-2'}`}
|
||||
className={clsx(
|
||||
toggleBgClass,
|
||||
'appearance-none rounded-full bg-grey-300 transition dark:bg-grey-800',
|
||||
`after:absolute after:ml-0.5 after:mt-0.5 after:rounded-full after:border-none after:bg-white after:transition-[background-color_0.2s,transform_0.2s] after:content-['']`,
|
||||
`checked:after:absolute checked:after:rounded-full checked:after:border-none checked:after:bg-white checked:after:transition-[background-color_0.2s,transform_0.2s] checked:after:content-['']`,
|
||||
'enabled:hover:cursor-pointer disabled:opacity-40 enabled:group-hover:opacity-80',
|
||||
sizeStyles,
|
||||
direction === 'rtl' && ' order-2'
|
||||
)}
|
||||
disabled={disabled}
|
||||
id={id}
|
||||
role="switch"
|
||||
type="checkbox"
|
||||
|
|
|
@ -260,7 +260,7 @@ const Modal: React.FC<ModalProps> = ({
|
|||
);
|
||||
|
||||
return (
|
||||
<div className={backdropClasses} id='modal-backdrop' onClick={handleBackdropClick}>
|
||||
<div className={backdropClasses} id='modal-backdrop' onMouseDown={handleBackdropClick}>
|
||||
<div className={clsx(
|
||||
'pointer-events-none fixed inset-0 z-0',
|
||||
(backDrop && !formSheet) && topLevelBackdropClasses,
|
||||
|
|
|
@ -120,7 +120,7 @@ const DefaultRecipients: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
|||
},
|
||||
{
|
||||
label: 'Active Tiers',
|
||||
options: tiers?.filter(({active}) => active).map(tier => ({value: tier.id, label: tier.name, color: 'black'})) || []
|
||||
options: tiers?.filter(({active, type}) => active && type !== 'free').map(tier => ({value: tier.id, label: tier.name, color: 'black'})) || []
|
||||
},
|
||||
{
|
||||
label: 'Archived Tiers',
|
||||
|
|
|
@ -10,7 +10,9 @@ const EnableNewsletters: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
|||
const {settings} = useGlobalData();
|
||||
const {mutateAsync: editSettings} = useEditSettings();
|
||||
|
||||
const [newslettersEnabled] = getSettingValues(settings, ['editor_default_email_recipients']) as [string];
|
||||
const [newslettersEnabled, membersSignupAccess] = getSettingValues<string>(settings, ['editor_default_email_recipients', 'members_signup_access']);
|
||||
|
||||
const isDisabled = membersSignupAccess === 'none';
|
||||
|
||||
const handleToggleChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const updates: Setting[] = [
|
||||
|
@ -27,8 +29,9 @@ const EnableNewsletters: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
|||
const enableToggle = (
|
||||
<>
|
||||
<Toggle
|
||||
checked={newslettersEnabled !== 'disabled'}
|
||||
checked={isDisabled ? false : newslettersEnabled !== 'disabled'}
|
||||
direction='rtl'
|
||||
disabled={isDisabled}
|
||||
onChange={handleToggleChange}
|
||||
/>
|
||||
</>
|
||||
|
@ -46,7 +49,7 @@ const EnableNewsletters: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
|||
values={[
|
||||
{
|
||||
key: 'private',
|
||||
value: newslettersEnabled !== 'disabled' ? (
|
||||
value: (!isDisabled && newslettersEnabled !== 'disabled') ? (
|
||||
<div className='flex items-center gap-2'>
|
||||
<Icon colorClass='text-green' name='check' size='sm' />
|
||||
<span>Enabled</span>
|
||||
|
@ -54,7 +57,10 @@ const EnableNewsletters: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
|||
) : (
|
||||
<div className='flex items-center gap-2 text-grey-900'>
|
||||
<Icon colorClass='text-grey-600' name='mail-block' size='sm' />
|
||||
<span>Disabled</span>
|
||||
<span>
|
||||
Disabled
|
||||
{isDisabled && ' by Access settings'}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -146,11 +146,13 @@ const TierDetailModalContent: React.FC<{tier?: Tier}> = ({tier}) => {
|
|||
placeholder='Bronze'
|
||||
title='Name'
|
||||
value={formState.name || ''}
|
||||
autoFocus
|
||||
onBlur={() => validators.name()}
|
||||
onChange={e => updateForm(state => ({...state, name: e.target.value}))}
|
||||
/>}
|
||||
<TextField
|
||||
autoComplete='off'
|
||||
autoFocus={isFreeTier}
|
||||
placeholder={isFreeTier ? `Free preview of ${siteTitle}` : 'Full access to premium content'}
|
||||
title='Description'
|
||||
value={formState.description || ''}
|
||||
|
|
Loading…
Add table
Reference in a new issue