mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
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
This commit is contained in:
1 parent
e11d2d96bc
commit
2662583d7e
5 files changed
+136
-39
No files matched your search
+4
-1
@@ -185,7 +185,10 @@ const RLSCodeEditor = ({
|
||||
{placeholder !== undefined && (
|
||||
<div
|
||||
id={placeholderId}
|
||||
className="monaco-placeholder absolute top-[0px] left-[57px] text-sm pointer-events-none font-mono [&>div>p]:text-foreground-lighter [&>div>p]:!m-0 tracking-tighter"
|
||||
className={cn(
|
||||
'monaco-placeholder absolute top-[0px] left-[57px] text-sm pointer-events-none font-mono tracking-tighter',
|
||||
'[&>div>p]:text-foreground-lighter [&>div>p]:!m-0'
|
||||
)}
|
||||
style={{ display: 'none' }}
|
||||
>
|
||||
<Markdown content={placeholder} />
|
||||
|
||||
+14
-9
@@ -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 (
|
||||
<div className={cn('rounded-md relative group flex-grow')}>
|
||||
<FormControl_Shadcn_>
|
||||
<SqlEditor
|
||||
defaultValue={field.value}
|
||||
onInputChange={(value: string | undefined) => {
|
||||
field.onChange(value)
|
||||
}}
|
||||
contextmenu={false}
|
||||
/>
|
||||
{language !== undefined && (
|
||||
<CodeEditor
|
||||
id="database-functions-editor"
|
||||
language="pgsql"
|
||||
placeholder={language === 'plpgsql' ? `BEGIN\n\nEND;` : undefined}
|
||||
value={field.value}
|
||||
onInputChange={field.onChange}
|
||||
/>
|
||||
)}
|
||||
</FormControl_Shadcn_>
|
||||
<div
|
||||
className={cn(
|
||||
@@ -42,12 +47,12 @@ export const FunctionEditor = ({
|
||||
type="text"
|
||||
size="tiny"
|
||||
className={cn(
|
||||
'px-2 text-foreground-lighter hover:text-foreground',
|
||||
'px-1.5 text-foreground-lighter hover:text-foreground',
|
||||
'transition z-50'
|
||||
)}
|
||||
onClick={() => setFocused(!focused)}
|
||||
icon={focused ? <Minimize2 /> : <Maximize2 />}
|
||||
></Button>
|
||||
/>
|
||||
</TooltipTrigger_Shadcn_>
|
||||
<TooltipContent_Shadcn_ side="bottom">
|
||||
{focused ? 'Minimize editor' : 'Maximize editor'}
|
||||
|
||||
@@ -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<z.infer<typeof FormSchema>>({
|
||||
resolver: zodResolver(FormSchema),
|
||||
})
|
||||
const language = form.watch('language')
|
||||
|
||||
const { mutate: createDatabaseFunction, isLoading: isCreating } =
|
||||
useDatabaseFunctionCreateMutation()
|
||||
@@ -256,11 +256,7 @@ const CreateFunction = ({ func, visible, setVisible }: CreateFunctionProps) => {
|
||||
</FormLabel_Shadcn_>
|
||||
<FormDescription_Shadcn_ className="text-sm text-foreground-light">
|
||||
<p>
|
||||
The language below should be written in{' '}
|
||||
<code>
|
||||
<FormLanguage />
|
||||
</code>
|
||||
.
|
||||
The language below should be written in <code>{language}</code>.
|
||||
</p>
|
||||
{!isEditing && <p>Change the language in the Advanced Settings below.</p>}
|
||||
</FormDescription_Shadcn_>
|
||||
@@ -273,6 +269,7 @@ const CreateFunction = ({ func, visible, setVisible }: CreateFunctionProps) => {
|
||||
>
|
||||
<FunctionEditor
|
||||
field={field}
|
||||
language={language}
|
||||
focused={focusedEditor}
|
||||
setFocused={setFocusedEditor}
|
||||
/>
|
||||
@@ -647,9 +644,3 @@ const FormFieldLanguage = () => {
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const FormLanguage = () => {
|
||||
const language = useWatch_Shadcn_({ name: 'language' })
|
||||
|
||||
return language
|
||||
}
|
||||
@@ -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<any>()
|
||||
|
||||
const editorRef = useRef<editor.IStandaloneCodeEditor>()
|
||||
const monacoRef = useRef<Monaco>()
|
||||
|
||||
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 (
|
||||
<Editor
|
||||
path={id}
|
||||
theme="supabase"
|
||||
className={cn(className, 'monaco-editor')}
|
||||
value={value ?? undefined}
|
||||
language={language}
|
||||
defaultValue={defaultValue ?? undefined}
|
||||
loading={loading || <Loading />}
|
||||
options={optionsMerged}
|
||||
onMount={onMount}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
<>
|
||||
<Editor
|
||||
path={id}
|
||||
theme="supabase"
|
||||
className={cn(className, 'monaco-editor')}
|
||||
value={value ?? undefined}
|
||||
language={language}
|
||||
defaultValue={defaultValue ?? undefined}
|
||||
loading={loading || <Loading />}
|
||||
options={optionsMerged}
|
||||
onMount={onMount}
|
||||
onChange={onChangeContent}
|
||||
/>
|
||||
{placeholder !== undefined && (
|
||||
<div
|
||||
className={cn(
|
||||
'monaco-placeholder absolute top-[3px] left-[57px] text-sm pointer-events-none font-mono',
|
||||
'[&>div>p]:text-foreground-lighter [&>div>p]:!m-0 tracking-tighter',
|
||||
showPlaceholder ? 'block' : 'hidden'
|
||||
)}
|
||||
>
|
||||
<Markdown content={placeholder} />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@ interface SqlEditorProps {
|
||||
readOnly?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use CodeEditor instead
|
||||
*/
|
||||
const SqlEditor = ({
|
||||
queryId,
|
||||
language = 'pgsql',
|
||||
|
||||
Reference in new issue
Block a user