diff --git a/src/main/frontend/app/components/file-structure/file-tree-dialogs.tsx b/src/main/frontend/app/components/file-structure/file-tree-dialogs.tsx index 3a28b0ed..50adc0d5 100644 --- a/src/main/frontend/app/components/file-structure/file-tree-dialogs.tsx +++ b/src/main/frontend/app/components/file-structure/file-tree-dialogs.tsx @@ -1,5 +1,5 @@ import ContextMenu from './context-menu' -import NameInputDialog from './name-input-dialog' +import NameInputDialog, { FILE_NAME_PATTERNS, FOLDER_OR_ADAPTER_NAME_PATTERNS } from './name-input-dialog' import ConfirmDeleteDialog from './confirm-delete-dialog' import type { ContextMenuState, NameDialogState, DeleteTargetState } from './use-file-tree-context-menu' @@ -51,6 +51,9 @@ export default function FileTreeDialogs({ initialValue={nameDialog.initialValue} onSubmit={nameDialog.onSubmit} onCancel={onCloseNameDialog} + patterns={ + nameDialog.title.toLowerCase().includes('folder') ? FOLDER_OR_ADAPTER_NAME_PATTERNS : FILE_NAME_PATTERNS + } /> )} diff --git a/src/main/frontend/app/components/file-structure/name-input-dialog.tsx b/src/main/frontend/app/components/file-structure/name-input-dialog.tsx index 7c5801ac..3189b9ac 100644 --- a/src/main/frontend/app/components/file-structure/name-input-dialog.tsx +++ b/src/main/frontend/app/components/file-structure/name-input-dialog.tsx @@ -1,24 +1,42 @@ -import { useRef, useState } from 'react' +import React, { useRef, useState } from 'react' import { createPortal } from 'react-dom' import Button from '~/components/inputs/button' import ValidatedInput from '~/components/inputs/validatedInput' -const namePatterns: Record = { +const BASE_NAME_PATTERNS: Record = { 'Cannot be empty': /^(?!\s*$).+/, 'Cannot contain /': /^[^/]*$/, 'Cannot contain \\': /^[^\\]*$/, 'Cannot contain ..': /^(?!.*\.\.).*$/, +} + +export const FILE_NAME_PATTERNS: Record = { + ...BASE_NAME_PATTERNS, 'Must end with:\n.xml, .json, .yaml, .yml, or .properties': /^(.*\.(xml|json|yaml|yml|properties))?$/i, } +export const CONFIGURATION_NAME_PATTERNS: Record = { + ...BASE_NAME_PATTERNS, + 'Must end with:\n.xml': /^(.*\.(xml))?$/i, +} + +export const FOLDER_OR_ADAPTER_NAME_PATTERNS: Record = BASE_NAME_PATTERNS + interface NameInputDialogProps { title: string initialValue?: string onSubmit: (name: string) => void onCancel: () => void + patterns?: Record } -export default function NameInputDialog({ title, initialValue = '', onSubmit, onCancel }: NameInputDialogProps) { +export default function NameInputDialog({ + title, + initialValue = '', + onSubmit, + onCancel, + patterns = FOLDER_OR_ADAPTER_NAME_PATTERNS, +}: NameInputDialogProps) { const [value, setValue] = useState(initialValue) const [isValid, setIsValid] = useState(false) const overlayRef = useRef(null) @@ -54,7 +72,7 @@ export default function NameInputDialog({ title, initialValue = '', onSubmit, on autoFocus onFocus={(e) => e.target.select()} value={value} - patterns={namePatterns} + patterns={patterns} onValidChange={setIsValid} onChange={(e) => setValue(e.target.value)} onKeyDown={handleKeyDown} diff --git a/src/main/frontend/app/components/file-structure/studio-file-tree-dialogs.tsx b/src/main/frontend/app/components/file-structure/studio-file-tree-dialogs.tsx index 1e7c01df..684c0e39 100644 --- a/src/main/frontend/app/components/file-structure/studio-file-tree-dialogs.tsx +++ b/src/main/frontend/app/components/file-structure/studio-file-tree-dialogs.tsx @@ -53,6 +53,7 @@ export default function StudioFileTreeDialogs({ initialValue={nameDialog.initialValue} onSubmit={nameDialog.onSubmit} onCancel={onCloseNameDialog} + patterns={nameDialog.patterns} /> )} diff --git a/src/main/frontend/app/components/file-structure/use-file-tree-context-menu.ts b/src/main/frontend/app/components/file-structure/use-file-tree-context-menu.ts index 20218ebd..cf45d17c 100644 --- a/src/main/frontend/app/components/file-structure/use-file-tree-context-menu.ts +++ b/src/main/frontend/app/components/file-structure/use-file-tree-context-menu.ts @@ -6,6 +6,7 @@ import { clearConfigurationCache } from '~/services/configuration-service' import useTabStore from '~/stores/tab-store' import useEditorTabStore from '~/stores/editor-tab-store' import { showErrorToast, showErrorToastFrom } from '~/components/toast' +import { FILE_NAME_PATTERNS, FOLDER_OR_ADAPTER_NAME_PATTERNS } from '~/components/file-structure/name-input-dialog' export interface ContextMenuState { position: { x: number; y: number } @@ -20,6 +21,7 @@ export interface NameDialogState { title: string initialValue?: string onSubmit: (name: string) => void + patterns?: Record } export interface DeleteTargetState { @@ -130,6 +132,7 @@ export function useFileTreeContextMenu({ } setNameDialog(null) }, + patterns: FILE_NAME_PATTERNS, }) }, [projectName, dataProvider, closeContextMenu], @@ -154,6 +157,7 @@ export function useFileTreeContextMenu({ } setNameDialog(null) }, + patterns: FOLDER_OR_ADAPTER_NAME_PATTERNS, }) }, [projectName, dataProvider, closeContextMenu], @@ -193,6 +197,7 @@ export function useFileTreeContextMenu({ } setNameDialog(null) }, + patterns: menu.isFolder ? FOLDER_OR_ADAPTER_NAME_PATTERNS : FILE_NAME_PATTERNS, }) }, [projectName, dataProvider, onAfterRename, closeContextMenu], diff --git a/src/main/frontend/app/components/file-structure/use-studio-context-menu.ts b/src/main/frontend/app/components/file-structure/use-studio-context-menu.ts index 340bbc99..6da56be7 100644 --- a/src/main/frontend/app/components/file-structure/use-studio-context-menu.ts +++ b/src/main/frontend/app/components/file-structure/use-studio-context-menu.ts @@ -1,4 +1,4 @@ -import { useCallback, useRef, useState } from 'react' +import React, { useCallback, useRef, useState } from 'react' import type { TreeItemIndex } from 'react-complex-tree' import { deleteFile, renameFile } from '~/services/file-service' import { createFolderInProject } from '~/services/file-tree-service' @@ -7,6 +7,10 @@ import { clearConfigurationCache, createConfiguration } from '~/services/configu import useTabStore from '~/stores/tab-store' import { showErrorToastFrom } from '~/components/toast' import type { StudioItemData, StudioFolderData, StudioAdapterData } from './studio-files-data-provider' +import { + CONFIGURATION_NAME_PATTERNS, + FOLDER_OR_ADAPTER_NAME_PATTERNS, +} from '~/components/file-structure/name-input-dialog' export type StudioItemType = 'root' | 'folder' | 'configuration' | 'adapter' @@ -23,6 +27,7 @@ export interface NameDialogState { title: string initialValue?: string onSubmit: (name: string) => void + patterns?: Record } export interface DeleteTargetState { @@ -159,6 +164,7 @@ export function useStudioContextMenu({ projectName, dataProvider }: UseStudioCon } setNameDialog(null) }, + patterns: CONFIGURATION_NAME_PATTERNS, }) }, [projectName, dataProvider, closeContextMenu], @@ -181,6 +187,7 @@ export function useStudioContextMenu({ projectName, dataProvider }: UseStudioCon } setNameDialog(null) }, + patterns: FOLDER_OR_ADAPTER_NAME_PATTERNS, }) }, [projectName, dataProvider, closeContextMenu], @@ -203,6 +210,7 @@ export function useStudioContextMenu({ projectName, dataProvider }: UseStudioCon } setNameDialog(null) }, + patterns: FOLDER_OR_ADAPTER_NAME_PATTERNS, }) }, [projectName, dataProvider, closeContextMenu], @@ -239,6 +247,10 @@ export function useStudioContextMenu({ projectName, dataProvider }: UseStudioCon } setNameDialog(null) }, + patterns: + menu.itemType === 'folder' || menu.itemType === 'adapter' + ? FOLDER_OR_ADAPTER_NAME_PATTERNS + : CONFIGURATION_NAME_PATTERNS, }) }, [projectName, dataProvider, closeContextMenu],