mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-27 21:03:29 +03:00
Adding CMD/CTRL+S to AdminX (#18466)
refs. https://github.com/TryGhost/Product/issues/3949 - it wasn't possible to save setting values in AdminX with CMD/CTR+S
This commit is contained in:
parent
8bc653802d
commit
223a1d7767
@ -43,6 +43,7 @@ export interface ModalProps {
|
||||
dirty?: boolean;
|
||||
animate?: boolean;
|
||||
formSheet?: boolean;
|
||||
enableCMDS?: boolean;
|
||||
}
|
||||
|
||||
export const topLevelBackdropClasses = 'bg-[rgba(98,109,121,0.2)] backdrop-blur-[3px]';
|
||||
@ -74,7 +75,8 @@ const Modal: React.FC<ModalProps> = ({
|
||||
scrolling = true,
|
||||
dirty = false,
|
||||
animate = true,
|
||||
formSheet = false
|
||||
formSheet = false,
|
||||
enableCMDS = true
|
||||
}) => {
|
||||
const modal = useModal();
|
||||
const {setGlobalDirtyState} = useGlobalDirtyState();
|
||||
@ -116,6 +118,21 @@ const Modal: React.FC<ModalProps> = ({
|
||||
return () => clearTimeout(timeout);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const handleCMDS = (e: KeyboardEvent) => {
|
||||
if ((e.metaKey || e.ctrlKey) && e.key === 's') {
|
||||
e.preventDefault();
|
||||
onOk!();
|
||||
}
|
||||
};
|
||||
if (enableCMDS) {
|
||||
window.addEventListener('keydown', handleCMDS);
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleCMDS);
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
let buttons: ButtonProps[] = [];
|
||||
|
||||
let footerClasses, contentClasses;
|
||||
|
@ -39,6 +39,7 @@ export interface PreviewModalProps {
|
||||
sidebarHeader?: React.ReactNode;
|
||||
sidebarPadding?: boolean;
|
||||
sidebarContentClasses?: string;
|
||||
enableCMDS?: boolean;
|
||||
|
||||
onCancel?: () => void;
|
||||
onOk?: () => void;
|
||||
@ -73,6 +74,7 @@ export const PreviewModalContent: React.FC<PreviewModalProps> = ({
|
||||
sidebarHeader,
|
||||
sidebarPadding = true,
|
||||
sidebarContentClasses,
|
||||
enableCMDS = true,
|
||||
|
||||
onCancel,
|
||||
onOk,
|
||||
@ -88,6 +90,21 @@ export const PreviewModalContent: React.FC<PreviewModalProps> = ({
|
||||
setGlobalDirtyState(dirty);
|
||||
}, [dirty, setGlobalDirtyState]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleCMDS = (e: KeyboardEvent) => {
|
||||
if ((e.metaKey || e.ctrlKey) && e.key === 's') {
|
||||
e.preventDefault();
|
||||
onOk!();
|
||||
}
|
||||
};
|
||||
if (enableCMDS) {
|
||||
window.addEventListener('keydown', handleCMDS);
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleCMDS);
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const [view, setView] = useState('desktop');
|
||||
|
||||
if (view === 'mobile' && deviceSelector) {
|
||||
|
@ -39,6 +39,7 @@ interface SettingGroupProps {
|
||||
onEditingChange?: (isEditing: boolean) => void
|
||||
onSave?: () => void
|
||||
onCancel?: () => void
|
||||
enableCMDS?: boolean
|
||||
}
|
||||
|
||||
const SettingGroup: React.FC<SettingGroupProps> = ({
|
||||
@ -59,7 +60,8 @@ const SettingGroup: React.FC<SettingGroupProps> = ({
|
||||
styles,
|
||||
onEditingChange,
|
||||
onSave,
|
||||
onCancel
|
||||
onCancel,
|
||||
enableCMDS = true
|
||||
}) => {
|
||||
const {checkVisible} = useSearch();
|
||||
const {route} = useRouting();
|
||||
@ -148,6 +150,21 @@ const SettingGroup: React.FC<SettingGroupProps> = ({
|
||||
}
|
||||
}, [highlight]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleCMDS = (e: KeyboardEvent) => {
|
||||
if ((e.metaKey || e.ctrlKey) && e.key === 's') {
|
||||
e.preventDefault();
|
||||
handleSave();
|
||||
}
|
||||
};
|
||||
if (enableCMDS) {
|
||||
window.addEventListener('keydown', handleCMDS);
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleCMDS);
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const containerClasses = clsx(
|
||||
'relative flex-col gap-6 rounded',
|
||||
border && 'border p-5 md:p-7',
|
||||
|
@ -442,7 +442,6 @@ const NewsletterDetailModalContent: React.FC<{newsletter: Newsletter; onlyOne: b
|
||||
initialState: newsletter,
|
||||
onSave: async () => {
|
||||
const {newsletters, meta} = await editNewsletter(formState);
|
||||
|
||||
if (meta?.sent_email_verification) {
|
||||
NiceModal.show(ConfirmationModal, {
|
||||
title: 'Confirm newsletter email address',
|
||||
@ -458,8 +457,6 @@ const NewsletterDetailModalContent: React.FC<{newsletter: Newsletter; onlyOne: b
|
||||
modal.remove();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
modal.remove();
|
||||
}
|
||||
},
|
||||
onSaveError: handleError,
|
||||
@ -491,10 +488,11 @@ const NewsletterDetailModalContent: React.FC<{newsletter: Newsletter; onlyOne: b
|
||||
|
||||
return <PreviewModalContent
|
||||
afterClose={() => updateRoute('newsletters')}
|
||||
buttonsDisabled={saveState === 'saving'}
|
||||
cancelLabel='Close'
|
||||
deviceSelector={false}
|
||||
dirty={saveState === 'unsaved'}
|
||||
okLabel='Save'
|
||||
okLabel={saveState === 'saved' ? 'Saved' : (saveState === 'saving' ? 'Saving...' : 'Save')}
|
||||
preview={preview}
|
||||
previewBgColor={'grey'}
|
||||
previewToolbar={false}
|
||||
@ -503,9 +501,7 @@ const NewsletterDetailModalContent: React.FC<{newsletter: Newsletter; onlyOne: b
|
||||
testId='newsletter-modal'
|
||||
title='Newsletter'
|
||||
onOk={async () => {
|
||||
if (await handleSave()) {
|
||||
updateRoute('newsletters');
|
||||
} else {
|
||||
if (!(await handleSave())) {
|
||||
showToast({
|
||||
type: 'pageError',
|
||||
message: 'Can\'t save newsletter, please double check that you\'ve filled all mandatory fields.'
|
||||
|
@ -115,7 +115,6 @@ const Sidebar: React.FC<SidebarProps> = ({
|
||||
|
||||
const AnnouncementBarModal: React.FC = () => {
|
||||
const {siteData} = useGlobalData();
|
||||
const modal = NiceModal.useModal();
|
||||
const {localSettings, updateSetting, handleSave} = useSettingGroup();
|
||||
const [announcementContent] = getSettingValues<string>(localSettings, ['announcement_content']);
|
||||
const [accentColor] = getSettingValues<string>(localSettings, ['accent_color']);
|
||||
@ -207,11 +206,10 @@ const AnnouncementBarModal: React.FC = () => {
|
||||
|
||||
return <PreviewModalContent
|
||||
afterClose={() => {
|
||||
modal.remove();
|
||||
updateRoute('announcement-bar');
|
||||
}}
|
||||
cancelLabel='Close'
|
||||
deviceSelector={false}
|
||||
deviceSelector={true}
|
||||
dirty={false}
|
||||
okLabel='Save'
|
||||
preview={preview}
|
||||
@ -223,10 +221,8 @@ const AnnouncementBarModal: React.FC = () => {
|
||||
title='Announcement'
|
||||
titleHeadingLevel={5}
|
||||
onOk={async () => {
|
||||
if (await handleSave()) {
|
||||
modal.remove();
|
||||
if (!(await handleSave())) {
|
||||
updateRoute('announcement-bar');
|
||||
} else {
|
||||
showToast({
|
||||
type: 'pageError',
|
||||
message: 'An error occurred while saving your changes. Please try again.'
|
||||
|
@ -66,17 +66,15 @@ const AnnouncementBarPreview: React.FC<AnnouncementBarSettings> = ({announcement
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='h-screen w-screen overflow-hidden'>
|
||||
<IframeBuffering
|
||||
addDelay={true}
|
||||
className="absolute left-0 top-0 h-full w-full"
|
||||
generateContent={injectContentIntoIframe}
|
||||
height='100%'
|
||||
parentClassName="relative h-full w-full"
|
||||
testId='announcement-bar-preview-iframe'
|
||||
width='100%'
|
||||
/>
|
||||
</div>
|
||||
<IframeBuffering
|
||||
addDelay={true}
|
||||
className="absolute h-[110%] w-[110%] origin-top-left scale-[.90909] max-[1600px]:h-[130%] max-[1600px]:w-[130%] max-[1600px]:scale-[.76923]"
|
||||
generateContent={injectContentIntoIframe}
|
||||
height='100%'
|
||||
parentClassName="relative h-full w-full"
|
||||
testId='announcement-bar-preview-iframe'
|
||||
width='100%'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -100,7 +98,7 @@ export default memo(AnnouncementBarPreview, (prevProps, nextProps) => {
|
||||
if (prevProps.announcementBackgroundColor !== nextProps.announcementBackgroundColor) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Check if announcementContent changed
|
||||
if (prevProps.announcementContent !== nextProps.announcementContent) {
|
||||
return false;
|
||||
|
Loading…
Reference in New Issue
Block a user