diff --git a/apps/admin-x-design-system/src/assets/icons/layout-2-col.svg b/apps/admin-x-design-system/src/assets/icons/layout-2-col.svg new file mode 100644 index 0000000000..2f6cd45c2e --- /dev/null +++ b/apps/admin-x-design-system/src/assets/icons/layout-2-col.svg @@ -0,0 +1 @@ +Browser Page Layout Streamline Icon: https://streamlinehq.combrowser-page-layout \ No newline at end of file diff --git a/apps/admin-x-design-system/src/global/Heading.tsx b/apps/admin-x-design-system/src/global/Heading.tsx index 69603e2cd9..500ae79a80 100644 --- a/apps/admin-x-design-system/src/global/Heading.tsx +++ b/apps/admin-x-design-system/src/global/Heading.tsx @@ -38,7 +38,7 @@ type HeadingLabelProps = { level?: never, grey?: boolean } & HeadingBaseProps & React.LabelHTMLAttributes -export const Heading6Styles = clsx('text-xs font-semibold tracking-normal'); +export const Heading6Styles = clsx('text-sm font-medium tracking-normal'); export const Heading6StylesGrey = clsx( Heading6Styles, 'text-grey-900 dark:text-grey-500' diff --git a/apps/admin-x-design-system/src/global/chrome/DesktopChrome.tsx b/apps/admin-x-design-system/src/global/chrome/DesktopChrome.tsx index 223385f905..af408d4aaf 100644 --- a/apps/admin-x-design-system/src/global/chrome/DesktopChrome.tsx +++ b/apps/admin-x-design-system/src/global/chrome/DesktopChrome.tsx @@ -6,7 +6,7 @@ export interface DesktopChromeProps { const DesktopChrome: React.FC> = ({children, ...props}) => { return ( -
+
{children}
diff --git a/apps/admin-x-design-system/src/global/form/ColorIndicator.tsx b/apps/admin-x-design-system/src/global/form/ColorIndicator.tsx index cb8dccce81..0b75393671 100644 --- a/apps/admin-x-design-system/src/global/form/ColorIndicator.tsx +++ b/apps/admin-x-design-system/src/global/form/ColorIndicator.tsx @@ -92,7 +92,7 @@ const ColorIndicator: React.FC = ({title, value, swatches, ))}
{picker && - -
- ); }; @@ -132,7 +114,7 @@ const DesignModal: React.FC = () => { } }, [setFormState, themeSettings]); - const updateBrandSetting = (key: string, value: SettingValue) => { + const updateGlobalSetting = (key: string, value: SettingValue) => { updateForm(state => ({...state, settings: state.settings.map(setting => ( setting.key === key ? {...setting, value, dirty: true} : setting ))})); @@ -144,7 +126,7 @@ const DesignModal: React.FC = () => { ))})); }; - const [description, accentColor, icon, logo, coverImage] = getSettingValues(formState.settings, ['description', 'accent_color', 'icon', 'logo', 'cover_image']) as string[]; + const [description, accentColor, icon, logo, coverImage, headingFont, bodyFont] = getSettingValues(formState.settings, ['description', 'accent_color', 'icon', 'logo', 'cover_image', 'heading_font', 'body_font']) as string[]; const themeSettingGroups = (formState.themeSettings || []).reduce((groups, setting) => { const group = (setting.group === 'homepage' || setting.group === 'post') ? setting.group : 'site-wide'; @@ -201,16 +183,18 @@ const DesignModal: React.FC = () => { icon, logo, coverImage, - themeSettings: formState.themeSettings + themeSettings: formState.themeSettings, + headingFont, + bodyFont }} url={selectedTabURL} />; const sidebarContent = ; diff --git a/apps/admin-x-settings/src/components/settings/site/DesignSetting.tsx b/apps/admin-x-settings/src/components/settings/site/DesignSetting.tsx index 42e1fa0fd0..66f865980d 100644 --- a/apps/admin-x-settings/src/components/settings/site/DesignSetting.tsx +++ b/apps/admin-x-settings/src/components/settings/site/DesignSetting.tsx @@ -1,3 +1,4 @@ +import DesignSettingsImg from '../../../assets/images/design-settings.png'; import React from 'react'; import TopLevelGroup from '../../TopLevelGroup'; import {Button, withErrorBoundary} from '@tryghost/admin-x-design-system'; @@ -12,12 +13,13 @@ const DesignSetting: React.FC<{ keywords: string[] }> = ({keywords}) => { return ( } - description="Customize the theme, colors, and layout of your site" + description="Customize the style and layout of your site" keywords={keywords} navid='design' testId='design' - title="Design & branding" - /> + title="Design & branding"> + + ); }; diff --git a/apps/admin-x-settings/src/components/settings/site/SiteSettings.tsx b/apps/admin-x-settings/src/components/settings/site/SiteSettings.tsx index 0daaa1ae54..bb71dd7493 100644 --- a/apps/admin-x-settings/src/components/settings/site/SiteSettings.tsx +++ b/apps/admin-x-settings/src/components/settings/site/SiteSettings.tsx @@ -1,4 +1,5 @@ import AnnouncementBar from './AnnouncementBar'; +import ChangeTheme from './ChangeTheme'; import DesignSetting from './DesignSetting'; import Navigation from './Navigation'; import React from 'react'; @@ -6,6 +7,7 @@ import SearchableSection from '../../SearchableSection'; export const searchKeywords = { design: ['site', 'logo', 'cover', 'colors', 'fonts', 'background', 'themes', 'appearance', 'style', 'design & branding', 'design and branding'], + theme: ['theme', 'template', 'upload'], navigation: ['site', 'navigation', 'menus', 'primary', 'secondary', 'links'], announcementBar: ['site', 'announcement bar', 'important', 'banner'] }; @@ -15,6 +17,7 @@ const SiteSettings: React.FC = () => { <> + diff --git a/apps/admin-x-settings/src/components/settings/site/ThemeModal.tsx b/apps/admin-x-settings/src/components/settings/site/ThemeModal.tsx index 59bbe85229..c79bb4c655 100644 --- a/apps/admin-x-settings/src/components/settings/site/ThemeModal.tsx +++ b/apps/admin-x-settings/src/components/settings/site/ThemeModal.tsx @@ -6,7 +6,7 @@ import React, {useEffect, useState} from 'react'; import ThemeInstalledModal from './theme/ThemeInstalledModal'; import ThemePreview from './theme/ThemePreview'; import useQueryParams from '../../../hooks/useQueryParams'; -import {Breadcrumbs, Button, ConfirmationModal, FileUpload, LimitModal, Modal, PageHeader, TabView, showToast} from '@tryghost/admin-x-design-system'; +import {Button, ConfirmationModal, FileUpload, LimitModal, Modal, PageHeader, TabView, showToast} from '@tryghost/admin-x-design-system'; import {HostLimitError, useLimiter} from '../../../hooks/useLimiter'; import {InstalledTheme, Theme, ThemesInstallResponseType, isDefaultOrLegacyTheme, useActivateTheme, useBrowseThemes, useInstallTheme, useUploadTheme} from '@tryghost/admin-x-framework/api/themes'; import {JSONError} from '@tryghost/admin-x-framework/errors'; @@ -54,11 +54,11 @@ const ThemeToolbar: React.FC = ({ setCurrentTab, themes }) => { + const modal = useModal(); const {updateRoute} = useRouting(); const {mutateAsync: uploadTheme} = useUploadTheme(); const limiter = useLimiter(); const handleError = useHandleError(); - const refParam = useQueryParams().getParam('ref'); const [uploadConfig, setUploadConfig] = useState<{enabled: boolean; error?: string}>(); @@ -80,11 +80,7 @@ const ThemeToolbar: React.FC = ({ }, [limiter]); const onClose = () => { - if (refParam) { - updateRoute(`design/edit?ref=${refParam}`); - } else { - updateRoute('design/edit'); - } + updateRoute('/'); }; const onThemeUpload = async (file: File) => { @@ -169,7 +165,7 @@ const ThemeToolbar: React.FC = ({ title, prompt, fatalErrors, - onRetry: async (modal) => { + onRetry: async () => { modal?.remove(); handleUpload(); } @@ -219,17 +215,18 @@ const ThemeToolbar: React.FC = ({ }; const left = - + ; + onTabChange={(id: string) => { + setCurrentTab(id); + }} /> + ; const handleUpload = () => { if (uploadConfig?.enabled) { @@ -250,19 +247,11 @@ const ThemeToolbar: React.FC = ({ const right =
-
- { - setCurrentTab(id); - }} /> -
+
; @@ -372,11 +361,7 @@ const ChangeThemeModal: React.FC = ({source, themeRef}) = }); } confirmModal?.remove(); - if (refParam) { - updateRoute(`design/edit?ref=${refParam}`); - } else { - updateRoute('design/edit'); - } + updateRoute(''); } catch (e) { handleError(e); } @@ -457,11 +442,7 @@ const ChangeThemeModal: React.FC = ({source, themeRef}) = prompt, installedTheme: installedTheme!, onActivate: () => { - if (refParam) { - updateRoute(`design/edit?ref=${refParam}`); - } else { - updateRoute('design/edit'); - } + updateRoute(''); } }); }; @@ -470,11 +451,7 @@ const ChangeThemeModal: React.FC = ({source, themeRef}) = return ( { - if (refParam) { - updateRoute(`design/edit?ref=${refParam}`); - } else { - updateRoute('design/edit'); - } + updateRoute(''); }} animate={false} cancelLabel='' @@ -500,7 +477,7 @@ const ChangeThemeModal: React.FC = ({source, themeRef}) = setSelectedTheme(null); }} onClose={() => { - updateRoute('design/edit'); + updateRoute(''); }} onInstall={onInstall} /> } diff --git a/apps/admin-x-settings/src/components/settings/site/designAndBranding/BrandSettings.tsx b/apps/admin-x-settings/src/components/settings/site/designAndBranding/BrandSettings.tsx deleted file mode 100644 index 9b2346b7d6..0000000000 --- a/apps/admin-x-settings/src/components/settings/site/designAndBranding/BrandSettings.tsx +++ /dev/null @@ -1,181 +0,0 @@ -import React, {useRef, useState} from 'react'; -import UnsplashSelector from '../../../selectors/UnsplashSelector'; -import usePinturaEditor from '../../../../hooks/usePinturaEditor'; -import {APIError} from '@tryghost/admin-x-framework/errors'; -import {ColorPickerField, Heading, Hint, ImageUpload, SettingGroupContent, TextField, debounce} from '@tryghost/admin-x-design-system'; -import {SettingValue, getSettingValues} from '@tryghost/admin-x-framework/api/settings'; -import {getImageUrl, useUploadImage} from '@tryghost/admin-x-framework/api/images'; -import {useFramework} from '@tryghost/admin-x-framework'; -import {useGlobalData} from '../../../providers/GlobalDataProvider'; -import {useHandleError} from '@tryghost/admin-x-framework/hooks'; - -export interface BrandSettingValues { - description: string - accentColor: string - icon: string | null - logo: string | null - coverImage: string | null -} - -const BrandSettings: React.FC<{ values: BrandSettingValues, updateSetting: (key: string, value: SettingValue) => void }> = ({values,updateSetting}) => { - const {mutateAsync: uploadImage} = useUploadImage(); - const [siteDescription, setSiteDescription] = useState(values.description); - const {settings} = useGlobalData(); - const [unsplashEnabled] = getSettingValues(settings, ['unsplash']); - const [showUnsplash, setShowUnsplash] = useState(false); - const {unsplashConfig} = useFramework(); - const handleError = useHandleError(); - - const updateDescriptionDebouncedRef = useRef( - debounce((value: string) => { - updateSetting('description', value); - }, 500) - ); - - const editor = usePinturaEditor(); - - return ( -
- - { - // Immediately update the local state - setSiteDescription(event.target.value); - // Debounce the updateSetting call - updateDescriptionDebouncedRef.current(event.target.value); - }} - /> - Accent color} - value={values.accentColor} - alwaysOpen - // we debounce this because the color picker fires a lot of events. - onChange={value => updateSetting('accent_color', value)} - /> -
-
- Publication icon - A square, social icon, at least 60x60px -
-
- -
-
-
- Publication logo - -
-
- Publication cover - setShowUnsplash(true)} - pintura={ - { - isEnabled: editor.isEnabled, - openEditor: async () => editor.openEditor({ - image: values.coverImage || '', - handleSave: async (file:File) => { - try { - updateSetting('cover_image', getImageUrl(await uploadImage({file}))); - } catch (e) { - handleError(e); - } - } - }) - } - } - unsplashButtonClassName='!top-1 !right-1 z-50' - unsplashEnabled={unsplashEnabled} - onDelete={() => updateSetting('cover_image', null)} - onUpload={async (file: any) => { - try { - updateSetting('cover_image', getImageUrl(await uploadImage({file}))); - } catch (e) { - const error = e as APIError; - if (error.response!.status === 415) { - error.message = 'Unsupported file type'; - } - handleError(error); - } - }} - > - Upload cover - - { - showUnsplash && unsplashConfig && unsplashEnabled && ( - { - setShowUnsplash(false); - }} - onImageInsert={(image) => { - if (image.src) { - updateSetting('cover_image', image.src); - } - setShowUnsplash(false); - }} - /> - ) - } -
-
-
- ); -}; - -export default BrandSettings; diff --git a/apps/admin-x-settings/src/components/settings/site/designAndBranding/GlobalSettings.tsx b/apps/admin-x-settings/src/components/settings/site/designAndBranding/GlobalSettings.tsx new file mode 100644 index 0000000000..719d5431c5 --- /dev/null +++ b/apps/admin-x-settings/src/components/settings/site/designAndBranding/GlobalSettings.tsx @@ -0,0 +1,265 @@ +import BehindFeatureFlag from '../../../BehindFeatureFlag'; +import React, {useState} from 'react'; +import UnsplashSelector from '../../../selectors/UnsplashSelector'; +import clsx from 'clsx'; +import usePinturaEditor from '../../../../hooks/usePinturaEditor'; +import {APIError} from '@tryghost/admin-x-framework/errors'; +import {CUSTOM_FONTS, getCSSFriendlyFontClassName} from '@tryghost/custom-fonts'; +import {ColorPickerField, Form, Hint, ImageUpload, Select} from '@tryghost/admin-x-design-system'; +import {SettingValue, getSettingValues} from '@tryghost/admin-x-framework/api/settings'; +import {getImageUrl, useUploadImage} from '@tryghost/admin-x-framework/api/images'; +import {useFramework} from '@tryghost/admin-x-framework'; +import {useGlobalData} from '../../../providers/GlobalDataProvider'; +import {useHandleError} from '@tryghost/admin-x-framework/hooks'; +import type {BodyFont, HeadingFont} from '@tryghost/custom-fonts'; + +type BodyFontOption = { + value: BodyFont | typeof DEFAULT_FONT, + label: BodyFont | typeof DEFAULT_FONT, + className?: string +}; +type HeadingFontOption = { + value: HeadingFont | typeof DEFAULT_FONT, + label: HeadingFont | typeof DEFAULT_FONT, + className?: string +}; + +export interface GlobalSettingValues { + description: string + accentColor: string + icon: string | null + logo: string | null + coverImage: string | null + headingFont: string + bodyFont: string +} +/** + * All custom fonts are maintained in the @tryghost/custom-fonts package. + * If you need to change a font, you'll need to update the @tryghost/custom-fonts package. + */ +const DEFAULT_FONT = 'Theme default'; + +const GlobalSettings: React.FC<{ values: GlobalSettingValues, updateSetting: (key: string, value: SettingValue) => void }> = ({values,updateSetting}) => { + const {mutateAsync: uploadImage} = useUploadImage(); + const {settings} = useGlobalData(); + const [unsplashEnabled] = getSettingValues(settings, ['unsplash']); + const [showUnsplash, setShowUnsplash] = useState(false); + const {unsplashConfig} = useFramework(); + const handleError = useHandleError(); + + const editor = usePinturaEditor(); + + const [headingFont, setHeadingFont] = useState(values.headingFont || DEFAULT_FONT); + const [bodyFont, setBodyFont] = useState(values.bodyFont || DEFAULT_FONT); + + const fontClassName = (fontName: string, heading: boolean = true) => { + return clsx(getCSSFriendlyFontClassName(fontName), heading && 'font-bold'); + }; + + // Populate the heading and body font options + const customHeadingFonts: HeadingFontOption[] = CUSTOM_FONTS.heading.map((x) => { + let className = fontClassName(x, true); + return {label: x, value: x, className}; + }); + customHeadingFonts.unshift({label: DEFAULT_FONT, value: DEFAULT_FONT, className: 'font-sans font-normal'}); + + const customBodyFonts: BodyFontOption[] = CUSTOM_FONTS.body.map((x) => { + let className = fontClassName(x, false); + return {label: x, value: x, className}; + }); + customBodyFonts.unshift({label: DEFAULT_FONT, value: DEFAULT_FONT, className: 'font-sans font-normal'}); + + const selectFont = (fontName: string, heading: boolean) => { + if (fontName === DEFAULT_FONT) { + return ''; + } + return fontClassName(fontName, heading); + }; + + const selectedHeadingFont = {label: headingFont, value: headingFont}; + const selectedBodyFont = {label: bodyFont, value: bodyFont}; + + return ( + <> + + Accent color} + value={values.accentColor} + // we debounce this because the color picker fires a lot of events. + onChange={value => updateSetting('accent_color', value)} + /> +
+
+
Publication icon
+ A square, social icon, at least 60x60px +
+
+ +
+
+
+
+
Publication logo
+ Appears usually in the main header of your theme +
+
+ +
+
+
+
+
Publication cover
+ Usually as a large banner image on your index pages +
+ setShowUnsplash(true)} + pintura={ + { + isEnabled: editor.isEnabled, + openEditor: async () => editor.openEditor({ + image: values.coverImage || '', + handleSave: async (file:File) => { + try { + updateSetting('cover_image', getImageUrl(await uploadImage({file}))); + } catch (e) { + handleError(e); + } + } + }) + } + } + unsplashButtonClassName='!bg-transparent !h-6 !top-1.5 !w-6 !right-1.5 z-50' + unsplashEnabled={unsplashEnabled} + width='160px' + onDelete={() => updateSetting('cover_image', null)} + onUpload={async (file: any) => { + try { + updateSetting('cover_image', getImageUrl(await uploadImage({file}))); + } catch (e) { + const error = e as APIError; + if (error.response!.status === 415) { + error.message = 'Unsupported file type'; + } + handleError(error); + } + }} + > + Upload cover + + { + showUnsplash && unsplashConfig && unsplashEnabled && ( + { + setShowUnsplash(false); + }} + onImageInsert={(image) => { + if (image.src) { + updateSetting('cover_image', image.src); + } + setShowUnsplash(false); + }} + /> + ) + } +
+ + +
+ { + if (option?.value === DEFAULT_FONT) { + setBodyFont(DEFAULT_FONT); + updateSetting('body_font', ''); + } else { + setBodyFont(option?.value || ''); + updateSetting('body_font', option?.value || ''); + } + }} + /> +
+
+ + ); +}; + +export default GlobalSettings; diff --git a/apps/admin-x-settings/src/components/settings/site/designAndBranding/ThemePreview.tsx b/apps/admin-x-settings/src/components/settings/site/designAndBranding/ThemePreview.tsx index aafa37ebdd..e7e2a8e173 100644 --- a/apps/admin-x-settings/src/components/settings/site/designAndBranding/ThemePreview.tsx +++ b/apps/admin-x-settings/src/components/settings/site/designAndBranding/ThemePreview.tsx @@ -3,17 +3,19 @@ import React, {useCallback} from 'react'; import {CustomThemeSetting, hiddenCustomThemeSettingValue} from '@tryghost/admin-x-framework/api/customThemeSettings'; import {isCustomThemeSettingVisible} from '../../../../utils/isCustomThemeSettingsVisible'; -type BrandSettings = { +type GlobalSettings = { description: string; accentColor: string; icon: string; logo: string; coverImage: string; themeSettings?: Array; + bodyFont: string; + headingFont: string; } interface ThemePreviewProps { - settings: BrandSettings + settings: GlobalSettings url: string } @@ -23,14 +25,18 @@ function getPreviewData({ icon, logo, coverImage, - themeSettings + themeSettings, + bodyFont, + headingFont }: { description: string; accentColor: string; icon: string; logo: string; coverImage: string; - themeSettings?: Array, + themeSettings?: Array; + bodyFont: string; + headingFont: string; }) { // Don't render twice while theme settings are loading if (!themeSettings) { @@ -44,6 +50,8 @@ function getPreviewData({ params.append('icon', icon); params.append('logo', logo); params.append('cover', coverImage); + params.append('bf', bodyFont); + params.append('hf', headingFont); const custom: { [key: string]: string | typeof hiddenCustomThemeSettingValue; } = {}; diff --git a/apps/admin-x-settings/src/components/settings/site/designAndBranding/ThemeSetting.tsx b/apps/admin-x-settings/src/components/settings/site/designAndBranding/ThemeSetting.tsx new file mode 100644 index 0000000000..b1f8ca92e0 --- /dev/null +++ b/apps/admin-x-settings/src/components/settings/site/designAndBranding/ThemeSetting.tsx @@ -0,0 +1,99 @@ +import React, {useEffect, useState} from 'react'; +import {ColorPickerField, Heading, Hint, ImageUpload, Select, TextField, Toggle} from '@tryghost/admin-x-design-system'; +import {CustomThemeSetting} from '@tryghost/admin-x-framework/api/customThemeSettings'; +import {getImageUrl, useUploadImage} from '@tryghost/admin-x-framework/api/images'; +import {humanizeSettingKey} from '@tryghost/admin-x-framework/api/settings'; +import {useHandleError} from '@tryghost/admin-x-framework/hooks'; + +interface ThemeSettingProps { + setting: CustomThemeSetting; + setSetting: (value: Setting['value']) => void; +} + +const ThemeSetting: React.FC = ({setting, setSetting}) => { + const [fieldValues, setFieldValues] = useState<{ [key: string]: string | null }>({}); + useEffect(() => { + const valueAsString = setting.value === null ? '' : String(setting.value); + setFieldValues(values => ({...values, [setting.key]: valueAsString})); + }, [setting]); + + const handleBlur = (key: string) => { + if (fieldValues[key] !== undefined) { + setSetting(fieldValues[key]); + } + }; + + const handleChange = (key: string, value: string) => { + setFieldValues(values => ({...values, [key]: value})); + }; + const {mutateAsync: uploadImage} = useUploadImage(); + const handleError = useHandleError(); + + const handleImageUpload = async (file: File) => { + try { + const imageUrl = getImageUrl(await uploadImage({file})); + setSetting(imageUrl); + } catch (e) { + handleError(e); + } + }; + + switch (setting.type) { + case 'text': + return ( + handleBlur(setting.key)} + onChange={event => handleChange(setting.key, event.target.value)} + /> + ); + case 'boolean': + return ( + setSetting(event.target.checked)} + /> + ); + case 'select': + return ( + ({label: option, value: option}))} - selectedOption={{label: setting.value, value: setting.value}} - testId={`setting-select-${setting.key}`} - title={humanizeSettingKey(setting.key)} - onSelect={option => setSetting(option?.value || null)} - /> - ); - case 'color': - return ( - setSetting(value)} - /> - ); - case 'image': - return <> - {humanizeSettingKey(setting.key)} - setSetting(null)} - onUpload={file => handleImageUpload(file)} - >Upload image - {setting.description && {setting.description}} - ; - } -}; - -const ThemeSettings: React.FC<{ settings: CustomThemeSetting[], updateSetting: (setting: CustomThemeSetting) => void }> = ({settings, updateSetting}) => { - // Filter out custom theme settings that should not be visible - const settingsKeyValueObj = settings.reduce((obj, {key, value}) => ({...obj, [key]: value}), {}); - const filteredSettings = settings.filter(setting => isCustomThemeSettingVisible(setting, settingsKeyValueObj)); +interface ThemeSettingsProps { + sections: Array<{ + id: string; + title: string; + settings: CustomThemeSetting[]; + }>; + updateSetting: (setting: CustomThemeSetting) => void; +} +const ThemeSettings: React.FC = ({sections, updateSetting}) => { return ( - - {filteredSettings.map(setting => updateSetting({...setting, value} as CustomThemeSetting)} setting={setting} />)} - + <> + {sections.map((section) => { + const filteredSettings = section.settings.filter(setting => isCustomThemeSettingVisible(setting, section.settings.reduce((obj, {key, value}) => ({...obj, [key]: value}), {})) + ); + + let previousType: string | undefined; + + return ( +
+ {filteredSettings.map((setting) => { + let spaceClass = ''; + if (setting.type === 'boolean' && previousType !== 'boolean' && previousType !== undefined) { + spaceClass = 'mt-3'; + } + if ((setting.type === 'text' || setting.type === 'select') && (previousType === 'text' || previousType === 'select')) { + spaceClass = 'mt-2'; + } + previousType = setting.type; + return
+ updateSetting({...setting, value} as CustomThemeSetting)} + setting={setting} + /> +
; + })} +
+ ); + })} + ); }; diff --git a/apps/admin-x-settings/src/components/settings/site/theme/ThemePreview.tsx b/apps/admin-x-settings/src/components/settings/site/theme/ThemePreview.tsx index e730263a73..72318ff37f 100644 --- a/apps/admin-x-settings/src/components/settings/site/theme/ThemePreview.tsx +++ b/apps/admin-x-settings/src/components/settings/site/theme/ThemePreview.tsx @@ -34,7 +34,6 @@ const ThemePreview: React.FC<{ isInstalling, installedTheme, onBack, - onClose, onInstall }) => { const [previewMode, setPreviewMode] = useState('desktop'); @@ -101,7 +100,6 @@ const ThemePreview: React.FC<{ containerClassName='whitespace-nowrap' itemClassName='hidden md:!block md:!visible' items={[ - {label: 'Design', onClick: onClose}, {label: 'Change theme', onClick: onBack}, {label: selectedTheme.name} ]} @@ -163,7 +161,7 @@ const ThemePreview: React.FC<{ return (
-
+
{previewMode === 'desktop' ?