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 && (
)}
{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, '');
}