mirror of
https://github.com/supabase/supabase.git
synced 2026-10-09 11:25:06 +03:00
chore(studio): pull unrelated UI nits out of warehouse prototype
Revert assorted fixes that belong on dnywh/prototype/warehouse-unrelated-fixes so this branch stays focused on warehouse storage and table details work. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
0807d74e1a
commit
1369459669
11 files changed
+78
-168
No files matched your search
@@ -88,7 +88,7 @@ test.describe.configure({ mode: 'serial' })
|
||||
3. **`getByText` with exact match** - Good for unique text
|
||||
|
||||
```typescript
|
||||
page.getByText('Data API access', { exact: true })
|
||||
page.getByText('Data API Access', { exact: true })
|
||||
```
|
||||
|
||||
4. **`locator` with CSS** - Use sparingly, more fragile
|
||||
|
||||
@@ -23,7 +23,7 @@ All comments are **advisory**.
|
||||
3. **`getByText` with exact match** — good for unique text
|
||||
|
||||
```typescript
|
||||
page.getByText('Data API access', { exact: true })
|
||||
page.getByText('Data API Access', { exact: true })
|
||||
```
|
||||
|
||||
4. **`locator` with CSS** — use sparingly, more fragile
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ export const TableReplicationRow = ({
|
||||
className="px-1.5"
|
||||
icon={<ExternalLink />}
|
||||
tooltip={{
|
||||
content: { side: 'bottom', text: 'Table Editor' },
|
||||
content: { side: 'bottom', text: 'Open in Table Editor' },
|
||||
}}
|
||||
>
|
||||
<Link
|
||||
|
||||
@@ -162,7 +162,6 @@ export const ColumnList = ({
|
||||
{!isSchemaLocked && isTableEntity && (
|
||||
<ButtonTooltip
|
||||
icon={<Plus />}
|
||||
variant="default"
|
||||
disabled={!canUpdateColumns}
|
||||
onClick={() => onAddColumn()}
|
||||
tooltip={{
|
||||
|
||||
@@ -95,9 +95,7 @@ export const ConnectSection = () => {
|
||||
</span>
|
||||
<div className="flex min-w-0 flex-1 flex-col gap-1 xl:flex-initial">
|
||||
<p className="text-sm">{action.heading}</p>
|
||||
<p className="text-xs text-foreground-lighter leading-none">
|
||||
{action.subheading}!
|
||||
</p>
|
||||
<p className="text-sm text-foreground-lighter">{action.subheading}</p>
|
||||
</div>
|
||||
<ChevronRight
|
||||
size={16}
|
||||
|
||||
-61
@@ -1,61 +0,0 @@
|
||||
import { LOCAL_STORAGE_KEYS } from 'common'
|
||||
import { useParams } from 'common/hooks'
|
||||
import { Button } from 'ui'
|
||||
import { Admonition } from 'ui-patterns'
|
||||
|
||||
import { EnableIndexAdvisorButton } from './EnableIndexAdvisorButton'
|
||||
import { useIndexAdvisorStatus } from '@/components/interfaces/QueryPerformance/hooks/useIsIndexAdvisorStatus'
|
||||
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
|
||||
import { BASE_PATH } from '@/lib/constants'
|
||||
|
||||
export const IndexAdvisorNotice = () => {
|
||||
const { ref } = useParams()
|
||||
const { isIndexAdvisorAvailable, isIndexAdvisorEnabled } = useIndexAdvisorStatus()
|
||||
const [isDismissed, setIsDismissed] = useLocalStorageQuery(
|
||||
LOCAL_STORAGE_KEYS.INDEX_ADVISOR_NOTICE_DISMISSED(ref ?? ''),
|
||||
false
|
||||
)
|
||||
|
||||
if (!isIndexAdvisorAvailable || isIndexAdvisorEnabled || isDismissed) return null
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Admonition showIcon={false} type="tip" className="relative overflow-hidden mb-4">
|
||||
<div className="absolute -inset-16 z-0 opacity-50">
|
||||
<img
|
||||
src={`${BASE_PATH}/img/reports/bg-grafana-dark.svg`}
|
||||
alt="Index Advisor"
|
||||
className="w-full h-full object-cover object-right hidden dark:block"
|
||||
/>
|
||||
<img
|
||||
src={`${BASE_PATH}/img/reports/bg-grafana-light.svg`}
|
||||
alt="Index Advisor"
|
||||
className="w-full h-full object-cover object-right dark:hidden"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-linear-to-r from-background-alternative to-transparent" />
|
||||
</div>
|
||||
<div className="relative z-10 flex flex-col md:flex-row md:items-center gap-y-2 md:gap-x-8 justify-between px-2 py-1">
|
||||
<div className="flex flex-col gap-y-0.5">
|
||||
<div className="flex flex-col gap-y-2 items-start">
|
||||
<p className="text-sm font-medium">Enable Index Advisor</p>
|
||||
</div>
|
||||
<p className="text-sm text-foreground-lighter text-balance">
|
||||
Recommends indexes to improve query performance.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<Button
|
||||
variant="default"
|
||||
size="tiny"
|
||||
onClick={() => setIsDismissed(true)}
|
||||
aria-label="Dismiss notification"
|
||||
>
|
||||
Dismiss
|
||||
</Button>
|
||||
<EnableIndexAdvisorButton />
|
||||
</div>
|
||||
</div>
|
||||
</Admonition>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
import Editor, { Monaco, OnMount } from '@monaco-editor/react'
|
||||
import { Monaco, OnMount } from '@monaco-editor/react'
|
||||
import { useDebounce } from '@uidotdev/usehooks'
|
||||
import { LOCAL_STORAGE_KEYS, useParams } from 'common'
|
||||
import { noop } from 'lodash'
|
||||
import { useRouter } from 'next/router'
|
||||
import { MutableRefObject, useEffect, useRef, useState } from 'react'
|
||||
import { cn } from 'ui'
|
||||
import { RefObject, useEffect, useRef, useState } from 'react'
|
||||
import { Admonition } from 'ui-patterns'
|
||||
import { useSetCommandMenuOpen } from 'ui-patterns/CommandMenu'
|
||||
|
||||
import type { IStandaloneCodeEditor } from './SQLEditor.types'
|
||||
import { createSqlSnippetSkeletonV2 } from './SQLEditor.utils'
|
||||
import { SIDEBAR_KEYS } from '@/components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider'
|
||||
import { getEditorSelectionParts } from '@/components/ui/AIEditor/utils'
|
||||
import { CodeEditor } from '@/components/ui/CodeEditor/CodeEditor'
|
||||
import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
|
||||
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
|
||||
import { useProfile } from '@/lib/profile'
|
||||
@@ -19,14 +19,17 @@ import { SHORTCUT_IDS } from '@/state/shortcuts/registry'
|
||||
import { useIsShortcutEnabled } from '@/state/shortcuts/useIsShortcutEnabled'
|
||||
import { useSidebarManagerSnapshot } from '@/state/sidebar-manager-state'
|
||||
import { useSqlEditorV2StateSnapshot } from '@/state/sql-editor-v2'
|
||||
import { wasNeverPersisted } from '@/state/sql-editor/sql-editor-lifecycle'
|
||||
import { canEditSnippet } from '@/state/sql-editor/sql-editor-rules'
|
||||
import { useSqlEditorSaveCoordinator } from '@/state/sql-editor/sql-editor-save-coordinator'
|
||||
import { useTabsStateSnapshot } from '@/state/tabs'
|
||||
|
||||
export type MonacoEditorProps = {
|
||||
id: string
|
||||
snippetName: string
|
||||
className?: string
|
||||
editorRef: MutableRefObject<IStandaloneCodeEditor | null>
|
||||
monacoRef: MutableRefObject<Monaco | null>
|
||||
editorRef: RefObject<IStandaloneCodeEditor | null>
|
||||
monacoRef: RefObject<Monaco | null>
|
||||
autoFocus?: boolean
|
||||
executeQuery: () => void
|
||||
executeExplainQuery: () => void
|
||||
@@ -44,7 +47,7 @@ export type MonacoEditorProps = {
|
||||
placeholder?: string
|
||||
}
|
||||
|
||||
const MonacoEditor = ({
|
||||
export const MonacoEditor = ({
|
||||
id,
|
||||
snippetName,
|
||||
editorRef,
|
||||
@@ -74,20 +77,12 @@ const MonacoEditor = ({
|
||||
LOCAL_STORAGE_KEYS.SQL_EDITOR_INTELLISENSE,
|
||||
true
|
||||
)
|
||||
const isAIAssistantHotkeyEnabled = useIsShortcutEnabled(SHORTCUT_IDS.AI_ASSISTANT_TOGGLE)
|
||||
const isCommandMenuHotkeyEnabled = useIsShortcutEnabled(SHORTCUT_IDS.COMMAND_MENU_OPEN)
|
||||
const setCommandMenuOpen = useSetCommandMenuOpen()
|
||||
|
||||
// [Joshen] Lodash debounce doesn't seem to be working here, so opting to use useDebounce
|
||||
const [value, setValue] = useState('')
|
||||
const debouncedValue = useDebounce(value, 1000)
|
||||
|
||||
const snippet = snapV2.snippets[id]
|
||||
const disableEdit =
|
||||
snippet?.snippet.visibility === 'project' && snippet?.snippet.owner_id !== profile?.id
|
||||
|
||||
const executeQueryRef = useRef(executeQuery)
|
||||
executeQueryRef.current = executeQuery
|
||||
const disableEdit = !!snippet && !canEditSnippet(snippet.snippet, profile?.id)
|
||||
|
||||
const executeExplainQueryRef = useRef(executeExplainQuery)
|
||||
executeExplainQueryRef.current = executeExplainQuery
|
||||
@@ -95,22 +90,18 @@ const MonacoEditor = ({
|
||||
const prettifyQueryRef = useRef(prettifyQuery)
|
||||
prettifyQueryRef.current = prettifyQuery
|
||||
|
||||
const isAIAssistantHotkeyEnabled = useIsShortcutEnabled(SHORTCUT_IDS.AI_ASSISTANT_TOGGLE)
|
||||
const aiHotkeyEnabledRef = useRef(isAIAssistantHotkeyEnabled)
|
||||
aiHotkeyEnabledRef.current = isAIAssistantHotkeyEnabled
|
||||
|
||||
const commandMenuHotkeyEnabledRef = useRef(isCommandMenuHotkeyEnabled)
|
||||
commandMenuHotkeyEnabledRef.current = isCommandMenuHotkeyEnabled
|
||||
const { requestSave } = useSqlEditorSaveCoordinator()
|
||||
const requestSaveRef = useRef(requestSave)
|
||||
requestSaveRef.current = requestSave
|
||||
|
||||
const setCommandMenuOpenRef = useRef(setCommandMenuOpen)
|
||||
setCommandMenuOpenRef.current = setCommandMenuOpen
|
||||
|
||||
const handleEditorOnMount: OnMount = async (editor, monaco) => {
|
||||
editorRef.current = editor
|
||||
monacoRef.current = monaco
|
||||
|
||||
const model = editorRef.current.getModel()
|
||||
const handleEditorOnMount: OnMount = (editor, monaco) => {
|
||||
const model = editor.getModel()
|
||||
if (model !== null) {
|
||||
monacoRef.current.editor.setModelMarkers(model, 'owner', [])
|
||||
monaco.editor.setModelMarkers(model, 'owner', [])
|
||||
}
|
||||
|
||||
// Blur the editor on Escape so users can hop out to the rest of the UI.
|
||||
@@ -136,17 +127,6 @@ const MonacoEditor = ({
|
||||
].join(' && ')
|
||||
)
|
||||
|
||||
editor.addAction({
|
||||
id: 'run-query',
|
||||
label: 'Run Query',
|
||||
keybindings: [monaco.KeyMod.CtrlCmd + monaco.KeyCode.Enter],
|
||||
contextMenuGroupId: 'operation',
|
||||
contextMenuOrder: 0,
|
||||
run: () => {
|
||||
executeQueryRef.current()
|
||||
},
|
||||
})
|
||||
|
||||
if (showExplainAction) {
|
||||
editor.addAction({
|
||||
id: 'run-explain-query',
|
||||
@@ -167,7 +147,7 @@ const MonacoEditor = ({
|
||||
contextMenuGroupId: 'operation',
|
||||
contextMenuOrder: 0,
|
||||
run: () => {
|
||||
if (snippet) snapV2.addNeedsSaving(snippet.snippet.id)
|
||||
if (snippet) requestSaveRef.current(snippet.snippet.id)
|
||||
},
|
||||
})
|
||||
|
||||
@@ -188,13 +168,15 @@ const MonacoEditor = ({
|
||||
contextMenuGroupId: 'operation',
|
||||
contextMenuOrder: 1,
|
||||
run: () => {
|
||||
const selectedValue = (editorRef?.current as any)
|
||||
.getModel()
|
||||
.getValueInRange((editorRef?.current as any)?.getSelection())
|
||||
const selection = editorRef?.current?.getSelection()
|
||||
if (!selection) return
|
||||
|
||||
const selectedValue = editorRef?.current?.getModel()?.getValueInRange(selection)
|
||||
|
||||
openSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
|
||||
aiSnap.newChat({
|
||||
name: 'Explain code section',
|
||||
sqlSnippets: [selectedValue],
|
||||
sqlSnippets: [selectedValue ?? ''],
|
||||
initialInput: 'Can you explain this section to me in more detail?',
|
||||
})
|
||||
},
|
||||
@@ -223,15 +205,6 @@ const MonacoEditor = ({
|
||||
})
|
||||
}
|
||||
|
||||
// Monaco claims Cmd+K as a chord prefix, which swallows the global command
|
||||
// menu shortcut while the editor is focused. Intercept it here and open the
|
||||
// command menu directly so it works the same inside and outside the editor.
|
||||
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyK, () => {
|
||||
if (commandMenuHotkeyEnabledRef.current) {
|
||||
setCommandMenuOpenRef.current(true)
|
||||
}
|
||||
})
|
||||
|
||||
editor.onDidChangeCursorSelection(({ selection }) => {
|
||||
const noSelection =
|
||||
selection.startLineNumber === selection.endLineNumber &&
|
||||
@@ -239,11 +212,6 @@ const MonacoEditor = ({
|
||||
onHasSelection(!noSelection)
|
||||
})
|
||||
|
||||
if (autoFocus) {
|
||||
if (editor.getValue().length === 1) editor.setPosition({ lineNumber: 1, column: 2 })
|
||||
editor.focus()
|
||||
}
|
||||
|
||||
onMount?.(editor)
|
||||
}
|
||||
|
||||
@@ -263,7 +231,7 @@ const MonacoEditor = ({
|
||||
// than push. The caller navigated to `/sql/new?content=...` (a long,
|
||||
// one-shot URL); replacing collapses it out of history so Back returns to
|
||||
// the originating page instead of a wasted step that re-seeds the snippet.
|
||||
if (router.query.content) {
|
||||
if (router.query.content !== undefined) {
|
||||
router.replace(`/project/${ref}/sql/${snippet.id}`, undefined, { shallow: true })
|
||||
} else {
|
||||
router.push(`/project/${ref}/sql/${snippet.id}`, undefined, { shallow: true })
|
||||
@@ -275,7 +243,7 @@ const MonacoEditor = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (debouncedValue.length > 0 && snippet) {
|
||||
const shouldInvalidate = snippet.snippet.isNotSavedInDatabaseYet
|
||||
const shouldInvalidate = wasNeverPersisted(snippet.snippet.status)
|
||||
snapV2.setSql({ id, sql: value, shouldInvalidate })
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -297,29 +265,26 @@ const MonacoEditor = ({
|
||||
description="This snippet has been shared to the project and is only editable by the owner who created this snippet. You may duplicate this snippet into a personal copy by right clicking on the snippet and selecting “Duplicate query”."
|
||||
/>
|
||||
)}
|
||||
<Editor
|
||||
className={cn(className, 'monaco-editor')}
|
||||
theme={'supabase'}
|
||||
onMount={handleEditorOnMount}
|
||||
onChange={handleEditorChange}
|
||||
defaultLanguage="pgsql"
|
||||
<CodeEditor
|
||||
id={id}
|
||||
language="pgsql"
|
||||
className={className}
|
||||
autofocus={autoFocus}
|
||||
isReadOnly={disableEdit}
|
||||
defaultValue={snippet?.snippet.content?.unchecked_sql}
|
||||
path={id}
|
||||
editorRef={editorRef}
|
||||
monacoRef={monacoRef}
|
||||
actions={{
|
||||
runQuery: { enabled: true, callback: executeQuery },
|
||||
formatDocument: { enabled: false, callback: noop },
|
||||
placeholderFill: { enabled: false },
|
||||
}}
|
||||
options={{
|
||||
tabSize: 2,
|
||||
fontSize: 13,
|
||||
placeholder,
|
||||
lineDecorationsWidth: 0,
|
||||
readOnly: disableEdit,
|
||||
minimap: { enabled: false },
|
||||
wordWrap: 'on',
|
||||
padding: { top: 4 },
|
||||
// [Joshen] Commenting the following out as it causes the autocomplete suggestion popover
|
||||
// to be positioned wrongly somehow. I'm not sure if this affects anything though, but leaving
|
||||
// comment just in case anyone might be wondering. Relevant issues:
|
||||
// - https://github.com/microsoft/monaco-editor/issues/2229
|
||||
// - https://github.com/microsoft/monaco-editor/issues/2503
|
||||
// fixedOverflowWidgets: true,
|
||||
fixedOverflowWidgets: false,
|
||||
lineNumbersMinChars: 5,
|
||||
scrollBeyondLastLine: true,
|
||||
suggest: {
|
||||
showMethods: intellisenseEnabled,
|
||||
showFunctions: intellisenseEnabled,
|
||||
@@ -351,9 +316,9 @@ const MonacoEditor = ({
|
||||
showSnippets: intellisenseEnabled,
|
||||
},
|
||||
}}
|
||||
onInputChange={handleEditorChange}
|
||||
onMount={handleEditorOnMount}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default MonacoEditor
|
||||
+1
-1
@@ -322,7 +322,7 @@ export const ApiAccessToggle = ({
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div>
|
||||
{!hideHeading && <h5>Data API access</h5>}
|
||||
{!hideHeading && <h5>Data API Access</h5>}
|
||||
<p className="text-sm text-foreground-lighter">
|
||||
Allow this table to be queried via Supabase client libraries or the API directly
|
||||
</p>
|
||||
|
||||
@@ -5,6 +5,8 @@ import { useMemo, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
CardContent,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
@@ -15,7 +17,6 @@ import {
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from 'ui'
|
||||
import { Admonition } from 'ui-patterns/admonition'
|
||||
import { CodeBlock } from 'ui-patterns/CodeBlock'
|
||||
|
||||
import { AUTO_ENABLE_RLS_EVENT_TRIGGER_SQL } from '@/components/interfaces/Database/Triggers/EventTriggersList/EventTriggers.constants'
|
||||
@@ -59,13 +60,21 @@ export const AutoEnableRLSNotice = ({ iconOnly }: { iconOnly?: boolean }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Admonition
|
||||
type="note"
|
||||
layout="horizontal"
|
||||
title="Auto-enable RLS for new tables"
|
||||
description="We recommend creating an event trigger that enables Row Level Security on all new tables."
|
||||
actions={
|
||||
<>
|
||||
<Card>
|
||||
<CardContent className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-x-4">
|
||||
<div className="rounded-lg bg-surface-300 text-foreground-light w-10 h-10 flex items-center justify-center">
|
||||
<ShieldCheck size={18} />
|
||||
</div>
|
||||
<div className="text-sm">
|
||||
<p>Auto-enable RLS for new tables</p>
|
||||
<p className="text-foreground-lighter">
|
||||
Create an event trigger that enables Row Level Security on all new tables
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-x-2">
|
||||
<CreateEnsureRLSTriggerDialog />
|
||||
<ButtonTooltip
|
||||
icon={<X />}
|
||||
@@ -74,9 +83,9 @@ export const AutoEnableRLSNotice = ({ iconOnly }: { iconOnly?: boolean }) => {
|
||||
tooltip={{ content: { side: 'bottom', text: 'Minimize' } }}
|
||||
onClick={() => setIsMinimized(true)}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -122,7 +131,7 @@ const CreateEnsureRLSTriggerDialog = ({ iconOnly }: { iconOnly?: boolean }) => {
|
||||
tooltip={{ content: { side: 'bottom', text: 'Auto-enable RLS for new tables' } }}
|
||||
/>
|
||||
) : (
|
||||
<Button variant="default">Learn more</Button>
|
||||
<Button variant="primary">Learn more</Button>
|
||||
)}
|
||||
</DialogTrigger>
|
||||
<DialogContent size="large">
|
||||
|
||||
@@ -57,14 +57,14 @@ async function verifyTablePrivileges(
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the API access toggle switch for Data API access.
|
||||
* Locates the API access toggle switch for Data API Access.
|
||||
* Only present when creating or duplicating a table (not when editing).
|
||||
*/
|
||||
function getApiAccessSwitch(page: Page) {
|
||||
const sidePanel = page.getByTestId('table-editor-side-panel')
|
||||
const dataApiSection = sidePanel
|
||||
.locator('div')
|
||||
.filter({ hasText: 'Data API access' })
|
||||
.filter({ hasText: 'Data API Access' })
|
||||
.filter({ has: page.getByRole('switch') })
|
||||
return dataApiSection.getByRole('switch')
|
||||
}
|
||||
@@ -239,10 +239,10 @@ test.describe('API Access Toggle', () => {
|
||||
await page.getByRole('menuitem', { name: 'Edit table' }).click()
|
||||
await expect(page.getByTestId('table-editor-side-panel')).toBeVisible()
|
||||
|
||||
// Data API access section is visible
|
||||
// Data API Access section is visible
|
||||
await expect(
|
||||
page.getByText('Data API access'),
|
||||
'Data API access label should be visible in edit mode'
|
||||
page.getByText('Data API Access'),
|
||||
'Data API Access label should be visible in edit mode'
|
||||
).toBeVisible()
|
||||
|
||||
// In edit mode the panel shows a "Manage access" link instead of a toggle switch
|
||||
|
||||
@@ -32,14 +32,14 @@ const DropdownMenuSubTrigger = React.forwardRef<
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex cursor-default select-none items-center rounded-xs pl-2 pr-1 py-1.5 text-xs outline-hidden focus:bg-overlay-hover data-[state=open]:bg-overlay-hover',
|
||||
'flex cursor-default select-none items-center rounded-xs px-2 py-1.5 text-xs outline-hidden focus:bg-overlay-hover data-[state=open]:bg-overlay-hover',
|
||||
inset && 'pl-8',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRight className="h-4 w-4 ml-auto!" strokeWidth={1.5} />
|
||||
<ChevronRight className="h-4 w-4 ml-auto!" />
|
||||
</DropdownMenuPrimitive.SubTrigger>
|
||||
))
|
||||
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName
|
||||
|
||||
Reference in new issue
Block a user