From 2662583d7e4eceff31e2d68f1e540aaf7893a0c0 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 15 Jul 2024 14:23:59 +0800 Subject: [PATCH] Add placeholder in database functions editor (#27922) * Add placeholder in database functions editor * Address feedback * Don't show placeholder if language is sql * Fixes * Fix --- .../AIPolicyEditorPanel/RLSCodeEditor.tsx | 5 +- .../CreateFunction/FunctionEditor.tsx | 23 ++-- .../Functions/CreateFunction/index.tsx | 15 +- .../components/ui/CodeEditor/CodeEditor.tsx | 129 +++++++++++++++--- apps/studio/components/ui/SqlEditor.tsx | 3 + 5 files changed, 136 insertions(+), 39 deletions(-) diff --git a/apps/studio/components/interfaces/Auth/Policies/AIPolicyEditorPanel/RLSCodeEditor.tsx b/apps/studio/components/interfaces/Auth/Policies/AIPolicyEditorPanel/RLSCodeEditor.tsx index 6b33be84e2c..babff3766a2 100644 --- a/apps/studio/components/interfaces/Auth/Policies/AIPolicyEditorPanel/RLSCodeEditor.tsx +++ b/apps/studio/components/interfaces/Auth/Policies/AIPolicyEditorPanel/RLSCodeEditor.tsx @@ -185,7 +185,10 @@ const RLSCodeEditor = ({ {placeholder !== undefined && (
div>p]:text-foreground-lighter [&>div>p]:!m-0' + )} style={{ display: 'none' }} > diff --git a/apps/studio/components/interfaces/Database/Functions/CreateFunction/FunctionEditor.tsx b/apps/studio/components/interfaces/Database/Functions/CreateFunction/FunctionEditor.tsx index 8742807280e..37db03c783e 100644 --- a/apps/studio/components/interfaces/Database/Functions/CreateFunction/FunctionEditor.tsx +++ b/apps/studio/components/interfaces/Database/Functions/CreateFunction/FunctionEditor.tsx @@ -9,26 +9,31 @@ import { Tooltip_Shadcn_, cn, } from 'ui' +import CodeEditor from 'components/ui/CodeEditor/CodeEditor' export const FunctionEditor = ({ field, + language, focused, setFocused, }: { field: any + language: string focused: boolean setFocused: (b: boolean) => void }) => { return (
- { - field.onChange(value) - }} - contextmenu={false} - /> + {language !== undefined && ( + + )}
setFocused(!focused)} icon={focused ? : } - > + /> {focused ? 'Minimize editor' : 'Maximize editor'} diff --git a/apps/studio/components/interfaces/Database/Functions/CreateFunction/index.tsx b/apps/studio/components/interfaces/Database/Functions/CreateFunction/index.tsx index c8db848536d..e0950b10feb 100644 --- a/apps/studio/components/interfaces/Database/Functions/CreateFunction/index.tsx +++ b/apps/studio/components/interfaces/Database/Functions/CreateFunction/index.tsx @@ -39,7 +39,6 @@ import { SheetSection, Toggle, cn, - useWatch_Shadcn_, } from 'ui' import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' @@ -82,6 +81,7 @@ const CreateFunction = ({ func, visible, setVisible }: CreateFunctionProps) => { const form = useForm>({ resolver: zodResolver(FormSchema), }) + const language = form.watch('language') const { mutate: createDatabaseFunction, isLoading: isCreating } = useDatabaseFunctionCreateMutation() @@ -256,11 +256,7 @@ const CreateFunction = ({ func, visible, setVisible }: CreateFunctionProps) => {

- The language below should be written in{' '} - - - - . + The language below should be written in {language}.

{!isEditing &&

Change the language in the Advanced Settings below.

}
@@ -273,6 +269,7 @@ const CreateFunction = ({ func, visible, setVisible }: CreateFunctionProps) => { > @@ -647,9 +644,3 @@ const FormFieldLanguage = () => { /> ) } - -const FormLanguage = () => { - const language = useWatch_Shadcn_({ name: 'language' }) - - return language -} diff --git a/apps/studio/components/ui/CodeEditor/CodeEditor.tsx b/apps/studio/components/ui/CodeEditor/CodeEditor.tsx index f955a339e97..24eeb0d677d 100644 --- a/apps/studio/components/ui/CodeEditor/CodeEditor.tsx +++ b/apps/studio/components/ui/CodeEditor/CodeEditor.tsx @@ -1,10 +1,11 @@ -import Editor, { EditorProps, OnMount } from '@monaco-editor/react' +import Editor, { EditorProps, Monaco, OnChange, OnMount } from '@monaco-editor/react' import { merge, noop } from 'lodash' import { editor } from 'monaco-editor' -import { useRef } from 'react' -import { cn } from 'ui' +import { useEffect, useRef, useState } from 'react' +import { Markdown } from 'components/interfaces/Markdown' import { timeout } from 'lib/helpers' +import { cn } from 'ui' import { Loading } from '../Loading' import { alignEditor } from './CodeEditor.utils' @@ -21,6 +22,8 @@ interface CodeEditorProps { loading?: boolean options?: EditorProps['options'] value?: string + placeholder?: string + disableTabToUsePlaceholder?: boolean } const CodeEditor = ({ @@ -36,13 +39,47 @@ const CodeEditor = ({ loading, options, value, + placeholder, + disableTabToUsePlaceholder = false, }: CodeEditorProps) => { + const hasValue = useRef() + const editorRef = useRef() + const monacoRef = useRef() + + const showPlaceholderDefault = placeholder !== undefined && (value ?? '').trim().length === 0 + const [showPlaceholder, setShowPlaceholder] = useState(showPlaceholderDefault) const onMount: OnMount = async (editor, monaco) => { editorRef.current = editor + monacoRef.current = monaco alignEditor(editor) + hasValue.current = editor.createContextKey('hasValue', false) + hasValue.current.set(value !== undefined && value.trim().length > 0) + setShowPlaceholder(showPlaceholderDefault) + + if (!disableTabToUsePlaceholder) { + editor.addCommand( + monaco.KeyCode.Tab, + () => { + editor.executeEdits('source', [ + { + // @ts-ignore + identifier: 'add-placeholder', + range: new monaco.Range(1, 1, 1, 1), + text: (placeholder ?? '') + .split('\n\n') + .join('\n') + .replaceAll('*', '') + .replaceAll(' ', ''), + }, + ]) + }, + '!hasValue' + ) + } + editor.addAction({ id: 'supabase', label: 'Run Query', @@ -57,10 +94,22 @@ const CodeEditor = ({ }, }) + const model = editor.getModel() + if (model) { + const position = model.getPositionAt((value ?? '').length) + editor.setPosition(position) + } + await timeout(500) if (autofocus) editor?.focus() } + const onChangeContent: OnChange = (value) => { + hasValue.current.set((value ?? '').length > 0) + setShowPlaceholder(!value) + onInputChange(value) + } + const optionsMerged = merge( { tabSize: 2, @@ -72,27 +121,73 @@ const CodeEditor = ({ contextmenu: true, lineNumbers: hideLineNumbers ? 'off' : undefined, glyphMargin: hideLineNumbers ? false : undefined, - lineNumbersMinChars: hideLineNumbers ? 0 : undefined, + lineNumbersMinChars: hideLineNumbers ? 0 : 4, folding: hideLineNumbers ? false : undefined, + scrollBeyondLastLine: false, }, options ) - merge({ cpp: '12' }, { java: '23' }, { python: '35' }) + useEffect(() => { + setShowPlaceholder(showPlaceholderDefault) + }, [showPlaceholderDefault]) + + useEffect(() => { + if ( + !disableTabToUsePlaceholder && + editorRef.current !== undefined && + monacoRef.current !== undefined + ) { + const editor = editorRef.current + const monaco = monacoRef.current + + editor.addCommand( + monaco.KeyCode.Tab, + () => { + editor.executeEdits('source', [ + { + // @ts-ignore + identifier: 'add-placeholder', + range: new monaco.Range(1, 1, 1, 1), + text: (placeholder ?? ' ') + .split('\n\n') + .join('\n') + .replaceAll('*', '') + .replaceAll(' ', ''), + }, + ]) + }, + '!hasValue' + ) + } + }, [placeholder, disableTabToUsePlaceholder]) return ( - } - options={optionsMerged} - onMount={onMount} - onChange={onInputChange} - /> + <> + } + options={optionsMerged} + onMount={onMount} + onChange={onChangeContent} + /> + {placeholder !== undefined && ( +
div>p]:text-foreground-lighter [&>div>p]:!m-0 tracking-tighter', + showPlaceholder ? 'block' : 'hidden' + )} + > + +
+ )} + ) } diff --git a/apps/studio/components/ui/SqlEditor.tsx b/apps/studio/components/ui/SqlEditor.tsx index 15ab2f73845..ad982e4ebbc 100644 --- a/apps/studio/components/ui/SqlEditor.tsx +++ b/apps/studio/components/ui/SqlEditor.tsx @@ -16,6 +16,9 @@ interface SqlEditorProps { readOnly?: boolean } +/** + * @deprecated Use CodeEditor instead + */ const SqlEditor = ({ queryId, language = 'pgsql',