import { PropsWithChildren, useEffect, useMemo } from 'react'; import { useTransitionHook } from '@uirouter/react'; import { BROWSER_OS_PLATFORM } from '@/react/constants'; import { AutomationTestingProps } from '@/types'; import { CodeEditor } from '@@/CodeEditor'; import { Tooltip } from '@@/Tip/Tooltip'; import { FormSectionTitle } from './form-components/FormSectionTitle'; import { FormError } from './form-components/FormError'; import { confirm } from './modals/confirm'; import { ModalType } from './modals'; import { buildConfirmButton } from './modals/utils'; const otherEditorConfig = { tooltip: ( <>
Ctrl+F - Start searching
Ctrl+G - Find next
Ctrl+Shift+G - Find previous
Ctrl+Shift+F - Replace
Ctrl+Shift+R - Replace all
Alt+G - Jump to line
Persistent search:
Enter - Find next
Shift+Enter - Find previous
), searchCmdLabel: 'Ctrl+F for search', } as const; export const editorConfig = { mac: { tooltip: ( <>
Cmd+F - Start searching
Cmd+G - Find next
Cmd+Shift+G - Find previous
Cmd+Option+F - Replace
Cmd+Option+R - Replace all
Option+G - Jump to line
Persistent search:
Enter - Find next
Shift+Enter - Find previous
), searchCmdLabel: 'Cmd+F for search', }, lin: otherEditorConfig, win: otherEditorConfig, } as const; interface Props extends AutomationTestingProps { value: string; onChange: (value: string) => void; id: string; placeholder?: string; yaml?: boolean; shell?: boolean; readonly?: boolean; titleContent?: React.ReactNode; hideTitle?: boolean; error?: string; versions?: number[]; onVersionChange?: (version: number) => void; height?: string; } export function WebEditorForm({ id, onChange, placeholder, value, titleContent = '', hideTitle, readonly, yaml, shell, children, error, versions, onVersionChange, height, 'data-cy': dataCy, }: PropsWithChildren) { return (
{!hideTitle && ( <> {titleContent ?? null} )} {children && (
{children}
)} {error && {error}}
onVersionChange && onVersionChange(v)} height={height} data-cy={dataCy} />
); } function DefaultTitle({ id }: { id: string }) { return ( Web editor
{editorConfig[BROWSER_OS_PLATFORM].searchCmdLabel}
); } export function usePreventExit( initialValue: string, value: string, check: boolean ) { const isChanged = useMemo( () => cleanText(initialValue) !== cleanText(value), [initialValue, value] ); const preventExit = check && isChanged; // when navigating away from the page with unsaved changes, show a portainer prompt to confirm useTransitionHook('onBefore', {}, async () => { if (!preventExit) { return true; } const confirmed = await confirm({ modalType: ModalType.Warn, title: 'Are you sure?', message: 'You currently have unsaved changes in the text editor. Are you sure you want to leave?', confirmButton: buildConfirmButton('Yes', 'danger'), }); return confirmed; }); // when reloading or exiting the page with unsaved changes, show a browser prompt to confirm useEffect(() => { function handler(event: BeforeUnloadEvent) { if (!preventExit) { return undefined; } event.preventDefault(); // eslint-disable-next-line no-param-reassign event.returnValue = ''; return ''; } // if the form is changed, then set the onbeforeunload window.addEventListener('beforeunload', handler); return () => { window.removeEventListener('beforeunload', handler); }; }, [preventExit]); } function cleanText(value: string) { return value.replace(/(\r\n|\n|\r)/gm, ''); }