mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
Chore/update sql side panel (#30050)
* init * Update inner-side-menu.mdx * chore: update SQL sidebar to use ui pattern components * mor * Update * Update index.tsx --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
1 parent
aad6c48d03
commit
297fc76bfd
4 files changed
+164
-153
No files matched your search
@@ -36,11 +36,10 @@ export const SQLEditorMenu = ({ onViewOngoingQueries }: SQLEditorMenuProps) => {
|
||||
const router = useRouter()
|
||||
const { profile } = useProfile()
|
||||
const project = useSelectedProject()
|
||||
const { ref, id: activeId } = useParams()
|
||||
const { ref } = useParams()
|
||||
|
||||
const snapV2 = useSqlEditorV2StateSnapshot()
|
||||
const [searchText, setSearchText] = useState('')
|
||||
const [selectedQueries, setSelectedQueries] = useState<string[]>([])
|
||||
|
||||
const canCreateSQLSnippet = useCheckPermissions(PermissionAction.CREATE, 'user_content', {
|
||||
resource: { type: 'sql', owner_id: profile?.id },
|
||||
@@ -80,78 +79,76 @@ export const SQLEditorMenu = ({ onViewOngoingQueries }: SQLEditorMenuProps) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="h-full flex flex-col justify-between">
|
||||
<div className="mt-4 mb-2 flex flex-col gap-y-4">
|
||||
<div className="mx-4 flex items-center justify-between gap-x-2">
|
||||
<InnerSideBarFilters className="w-full p-0 gap-0">
|
||||
<InnerSideBarFilterSearchInput
|
||||
name="search-queries"
|
||||
placeholder="Search queries..."
|
||||
aria-labelledby="Search queries"
|
||||
value={searchText}
|
||||
onChange={(e) => setSearchText(e.target.value)}
|
||||
<div className="h-full flex flex-col justify-between">
|
||||
<div className="mt-4 mb-2 flex flex-col gap-y-4">
|
||||
<div className="mx-4 flex items-center justify-between gap-x-2">
|
||||
<InnerSideBarFilters className="w-full p-0 gap-0">
|
||||
<InnerSideBarFilterSearchInput
|
||||
name="search-queries"
|
||||
placeholder="Search queries..."
|
||||
aria-labelledby="Search queries"
|
||||
value={searchText}
|
||||
onChange={(e) => setSearchText(e.target.value)}
|
||||
>
|
||||
<InnerSideBarFilterSortDropdown
|
||||
value={snapV2.order}
|
||||
onValueChange={(value: any) => snapV2.setOrder(value)}
|
||||
>
|
||||
<InnerSideBarFilterSortDropdown
|
||||
value={snapV2.order}
|
||||
onValueChange={(value: any) => snapV2.setOrder(value)}
|
||||
>
|
||||
<InnerSideBarFilterSortDropdownItem key="name" value="name">
|
||||
Alphabetical
|
||||
</InnerSideBarFilterSortDropdownItem>
|
||||
<InnerSideBarFilterSortDropdownItem key="inserted_at" value="inserted_at">
|
||||
Created At
|
||||
</InnerSideBarFilterSortDropdownItem>
|
||||
</InnerSideBarFilterSortDropdown>
|
||||
</InnerSideBarFilterSearchInput>
|
||||
</InnerSideBarFilters>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
type="default"
|
||||
icon={<Plus className="text-foreground" />}
|
||||
className="w-[26px]"
|
||||
/>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" side="bottom" className="w-48">
|
||||
<DropdownMenuItem className="gap-x-2" onClick={() => handleNewQuery()}>
|
||||
<FilePlus size={14} />
|
||||
Create a new snippet
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="gap-x-2" onClick={() => createNewFolder()}>
|
||||
<FolderPlus size={14} />
|
||||
Create a new folder
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
|
||||
<div className="px-2">
|
||||
<InnerSideMenuItem
|
||||
title="Templates"
|
||||
isActive={router.asPath === `/project/${ref}/sql/templates`}
|
||||
href={`/project/${ref}/sql/templates`}
|
||||
>
|
||||
Templates
|
||||
</InnerSideMenuItem>
|
||||
<InnerSideMenuItem
|
||||
title="Quickstarts"
|
||||
isActive={router.asPath === `/project/${ref}/sql/quickstarts`}
|
||||
href={`/project/${ref}/sql/quickstarts`}
|
||||
>
|
||||
Quickstarts
|
||||
</InnerSideMenuItem>
|
||||
</div>
|
||||
|
||||
<SQLEditorNavV2 searchText={searchText} />
|
||||
<InnerSideBarFilterSortDropdownItem key="name" value="name">
|
||||
Alphabetical
|
||||
</InnerSideBarFilterSortDropdownItem>
|
||||
<InnerSideBarFilterSortDropdownItem key="inserted_at" value="inserted_at">
|
||||
Created At
|
||||
</InnerSideBarFilterSortDropdownItem>
|
||||
</InnerSideBarFilterSortDropdown>
|
||||
</InnerSideBarFilterSearchInput>
|
||||
</InnerSideBarFilters>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
type="default"
|
||||
icon={<Plus className="text-foreground" />}
|
||||
className="w-[26px]"
|
||||
/>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" side="bottom" className="w-48">
|
||||
<DropdownMenuItem className="gap-x-2" onClick={() => handleNewQuery()}>
|
||||
<FilePlus size={14} />
|
||||
Create a new snippet
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="gap-x-2" onClick={() => createNewFolder()}>
|
||||
<FolderPlus size={14} />
|
||||
Create a new folder
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
|
||||
<div className="p-4 border-t sticky bottom-0 bg-studio">
|
||||
<Button block type="default" onClick={onViewOngoingQueries}>
|
||||
View running queries
|
||||
</Button>
|
||||
<div className="px-2">
|
||||
<InnerSideMenuItem
|
||||
title="Templates"
|
||||
isActive={router.asPath === `/project/${ref}/sql/templates`}
|
||||
href={`/project/${ref}/sql/templates`}
|
||||
>
|
||||
Templates
|
||||
</InnerSideMenuItem>
|
||||
<InnerSideMenuItem
|
||||
title="Quickstarts"
|
||||
isActive={router.asPath === `/project/${ref}/sql/quickstarts`}
|
||||
href={`/project/${ref}/sql/quickstarts`}
|
||||
>
|
||||
Quickstarts
|
||||
</InnerSideMenuItem>
|
||||
</div>
|
||||
|
||||
<SQLEditorNavV2 searchText={searchText} />
|
||||
</div>
|
||||
</>
|
||||
|
||||
<div className="p-4 border-t sticky bottom-0 bg-studio">
|
||||
<Button block type="default" onClick={onViewOngoingQueries}>
|
||||
View running queries
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ChevronRight, Eye, EyeOffIcon, Heart, Unlock } from 'lucide-react'
|
||||
import { Eye, EyeOffIcon, Heart, Unlock } from 'lucide-react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
@@ -31,18 +31,15 @@ import {
|
||||
useSnippets,
|
||||
useSqlEditorV2StateSnapshot,
|
||||
} from 'state/sql-editor-v2'
|
||||
import { Separator, Skeleton, TreeView } from 'ui'
|
||||
import {
|
||||
AlertDescription_Shadcn_,
|
||||
AlertTitle_Shadcn_,
|
||||
Alert_Shadcn_,
|
||||
CollapsibleContent_Shadcn_,
|
||||
CollapsibleTrigger_Shadcn_,
|
||||
Collapsible_Shadcn_,
|
||||
Separator,
|
||||
TreeView,
|
||||
} from 'ui'
|
||||
InnerSideBarEmptyPanel,
|
||||
InnerSideMenuCollapsible,
|
||||
InnerSideMenuCollapsibleContent,
|
||||
InnerSideMenuCollapsibleTrigger,
|
||||
InnerSideMenuSeparator,
|
||||
} from 'ui-patterns'
|
||||
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
|
||||
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
|
||||
import { ROOT_NODE, formatFolderResponseForTreeView } from './SQLEditorNav.utils'
|
||||
import { SQLEditorTreeViewItem } from './SQLEditorTreeViewItem'
|
||||
|
||||
@@ -75,11 +72,6 @@ export const SQLEditorNav = ({ searchText: _searchText }: SQLEditorNavProps) =>
|
||||
const [selectedSnippetToDownload, setSelectedSnippetToDownload] = useState<Snippet>()
|
||||
const [selectedFolderToDelete, setSelectedFolderToDelete] = useState<SnippetFolder>()
|
||||
|
||||
const COLLAPSIBLE_TRIGGER_CLASS_NAMES =
|
||||
'flex items-center gap-x-2 px-4 [&[data-state=open]>svg]:!rotate-90'
|
||||
const COLLAPSIBLE_ICON_CLASS_NAMES = 'text-foreground-light transition-transform duration-200'
|
||||
const COLLASIBLE_HEADER_CLASS_NAMES = 'text-foreground-light font-mono text-sm uppercase'
|
||||
|
||||
// =======================================================
|
||||
// [Joshen] Set up favorites, shared, and private snippets
|
||||
// =======================================================
|
||||
@@ -341,27 +333,24 @@ export const SQLEditorNav = ({ searchText: _searchText }: SQLEditorNavProps) =>
|
||||
|
||||
return (
|
||||
<>
|
||||
<Separator />
|
||||
|
||||
<InnerSideMenuSeparator />
|
||||
{((numProjectSnippets === 0 && searchText.length === 0) || numProjectSnippets > 0) && (
|
||||
<>
|
||||
<Collapsible_Shadcn_ open={showSharedSnippets} onOpenChange={setShowSharedSnippets}>
|
||||
<CollapsibleTrigger_Shadcn_ className={COLLAPSIBLE_TRIGGER_CLASS_NAMES}>
|
||||
<ChevronRight size={16} className={COLLAPSIBLE_ICON_CLASS_NAMES} />
|
||||
<span className={COLLASIBLE_HEADER_CLASS_NAMES}>
|
||||
Shared{numProjectSnippets > 0 && ` (${numProjectSnippets})`}
|
||||
</span>
|
||||
</CollapsibleTrigger_Shadcn_>
|
||||
<CollapsibleContent_Shadcn_ className="pt-2">
|
||||
<InnerSideMenuCollapsible
|
||||
open={showSharedSnippets}
|
||||
onOpenChange={setShowSharedSnippets}
|
||||
className="px-0"
|
||||
>
|
||||
<InnerSideMenuCollapsibleTrigger
|
||||
title={`Shared ${numProjectSnippets > 0 ? ` (${numProjectSnippets})` : ''}`}
|
||||
/>
|
||||
<InnerSideMenuCollapsibleContent className="group-data-[state=open]:pt-2">
|
||||
{numProjectSnippets === 0 ? (
|
||||
<div className="mx-4">
|
||||
<Alert_Shadcn_ className="p-3">
|
||||
<AlertTitle_Shadcn_ className="text-xs">No shared queries</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_ className="text-xs ">
|
||||
Share queries with your team by right-clicking on the query.
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
</div>
|
||||
<InnerSideBarEmptyPanel
|
||||
className="mx-2"
|
||||
title="No shared queries"
|
||||
description="Share queries with your team by right-clicking on the query."
|
||||
/>
|
||||
) : (
|
||||
<TreeView
|
||||
data={projectSnippetsTreeState}
|
||||
@@ -391,33 +380,35 @@ export const SQLEditorNav = ({ searchText: _searchText }: SQLEditorNavProps) =>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</CollapsibleContent_Shadcn_>
|
||||
</Collapsible_Shadcn_>
|
||||
<Separator />
|
||||
</InnerSideMenuCollapsibleContent>
|
||||
</InnerSideMenuCollapsible>
|
||||
<InnerSideMenuSeparator />
|
||||
</>
|
||||
)}
|
||||
|
||||
{((numFavoriteSnippets === 0 && searchText.length === 0) || numFavoriteSnippets > 0) && (
|
||||
<>
|
||||
<Collapsible_Shadcn_ open={showFavouriteSnippets} onOpenChange={setShowFavouriteSnippets}>
|
||||
<CollapsibleTrigger_Shadcn_ className={COLLAPSIBLE_TRIGGER_CLASS_NAMES}>
|
||||
<ChevronRight size={16} className={COLLAPSIBLE_ICON_CLASS_NAMES} />
|
||||
<span className={COLLASIBLE_HEADER_CLASS_NAMES}>
|
||||
Favorites{numFavoriteSnippets > 0 && ` (${numFavoriteSnippets})`}
|
||||
</span>
|
||||
</CollapsibleTrigger_Shadcn_>
|
||||
<CollapsibleContent_Shadcn_ className="pt-2">
|
||||
<InnerSideMenuCollapsible
|
||||
className="px-0"
|
||||
open={showFavouriteSnippets}
|
||||
onOpenChange={setShowFavouriteSnippets}
|
||||
>
|
||||
<InnerSideMenuCollapsibleTrigger
|
||||
title={`Favorites ${numFavoriteSnippets > 0 ? ` (${numFavoriteSnippets})` : ''}`}
|
||||
/>
|
||||
<InnerSideMenuCollapsibleContent className="group-data-[state=open]:pt-2">
|
||||
{numFavoriteSnippets === 0 ? (
|
||||
<div className="mx-4">
|
||||
<Alert_Shadcn_ className="p-3">
|
||||
<AlertTitle_Shadcn_ className="text-xs">No favorite queries</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_ className="text-xs ">
|
||||
<InnerSideBarEmptyPanel
|
||||
title="No favorite queries"
|
||||
className="mx-2"
|
||||
description={
|
||||
<>
|
||||
Save a query to favorites for easy accessbility by clicking the{' '}
|
||||
<Heart size={12} className="inline-block relative align-center -top-[1px]" />{' '}
|
||||
icon.
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<TreeView
|
||||
data={favoritesTreeState}
|
||||
@@ -448,34 +439,51 @@ export const SQLEditorNav = ({ searchText: _searchText }: SQLEditorNavProps) =>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</CollapsibleContent_Shadcn_>
|
||||
</Collapsible_Shadcn_>
|
||||
<Separator />
|
||||
</InnerSideMenuCollapsibleContent>
|
||||
</InnerSideMenuCollapsible>
|
||||
<InnerSideMenuSeparator />
|
||||
</>
|
||||
)}
|
||||
|
||||
<Collapsible_Shadcn_ open={showPrivateSnippets} onOpenChange={setShowPrivateSnippets}>
|
||||
<CollapsibleTrigger_Shadcn_ className={COLLAPSIBLE_TRIGGER_CLASS_NAMES}>
|
||||
<ChevronRight size={16} className={COLLAPSIBLE_ICON_CLASS_NAMES} />
|
||||
<span className={COLLASIBLE_HEADER_CLASS_NAMES}>
|
||||
PRIVATE
|
||||
{numPrivateSnippets > 0 && ` (${numPrivateSnippets})`}
|
||||
</span>
|
||||
</CollapsibleTrigger_Shadcn_>
|
||||
<CollapsibleContent_Shadcn_ className="pt-2">
|
||||
<InnerSideMenuCollapsible
|
||||
open={showPrivateSnippets}
|
||||
onOpenChange={setShowPrivateSnippets}
|
||||
className="px-0"
|
||||
>
|
||||
<InnerSideMenuCollapsibleTrigger
|
||||
title={`PRIVATE
|
||||
${numPrivateSnippets > 0 ? ` (${numPrivateSnippets})` : ''}`}
|
||||
/>
|
||||
<InnerSideMenuCollapsibleContent className="group-data-[state=open]:pt-2">
|
||||
{!snapV2.loaded[projectRef as string] ? (
|
||||
<div className="px-4">
|
||||
<GenericSkeletonLoader />
|
||||
</div>
|
||||
<>
|
||||
<div className="flex flex-row h-6 px-3 items-center gap-3">
|
||||
<Skeleton className="h-4 w-5" />
|
||||
<Skeleton className="w-40 h-4" />
|
||||
</div>
|
||||
<div className="flex flex-row h-6 px-3 items-center gap-3">
|
||||
<Skeleton className="h-4 w-5" />
|
||||
<Skeleton className="w-32 h-4" />
|
||||
</div>
|
||||
<div className="flex flex-row h-6 px-3 items-center gap-3 opacity-75">
|
||||
<Skeleton className="h-4 w-5" />
|
||||
<Skeleton className="w-20 h-4" />
|
||||
</div>
|
||||
<div className="flex flex-row h-6 px-3 items-center gap-3 opacity-50">
|
||||
<Skeleton className="h-4 w-5" />
|
||||
<Skeleton className="w-40 h-4" />
|
||||
</div>
|
||||
<div className="flex flex-row h-6 px-3 items-center gap-3 opacity-25">
|
||||
<Skeleton className="h-4 w-5" />
|
||||
<Skeleton className="w-20 h-4" />
|
||||
</div>
|
||||
</>
|
||||
) : folders.length === 0 && numPrivateSnippets === 0 ? (
|
||||
<div className="mx-4">
|
||||
<Alert_Shadcn_ className="p-3">
|
||||
<AlertTitle_Shadcn_ className="text-xs">No queries created yet</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_ className="text-xs">
|
||||
Queries will be automatically saved once you start writing in the editor.
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
</div>
|
||||
<InnerSideBarEmptyPanel
|
||||
className="mx-3 px-4"
|
||||
title="No queries created yet"
|
||||
description="Queries will be automatically saved once you start writing in the editor"
|
||||
/>
|
||||
) : (
|
||||
<TreeView
|
||||
multiSelect
|
||||
@@ -544,8 +552,8 @@ export const SQLEditorNav = ({ searchText: _searchText }: SQLEditorNavProps) =>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</CollapsibleContent_Shadcn_>
|
||||
</Collapsible_Shadcn_>
|
||||
</InnerSideMenuCollapsibleContent>
|
||||
</InnerSideMenuCollapsible>
|
||||
|
||||
<Separator />
|
||||
|
||||
|
||||
@@ -420,6 +420,9 @@ const uiConfig = ui({
|
||||
require('@tailwindcss/typography'),
|
||||
require('tailwindcss-animate'),
|
||||
plugin(motionSafeTransition),
|
||||
function ({ addVariant }) {
|
||||
addVariant('not-disabled', '&:not(:disabled)')
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
|
||||
@@ -39,7 +39,13 @@ const InnerSideMenuCollapsible = forwardRef<
|
||||
ElementRef<typeof Collapsible_Shadcn_>,
|
||||
React.ComponentPropsWithoutRef<typeof Collapsible_Shadcn_>
|
||||
>(({ ...props }, ref) => {
|
||||
return <Collapsible_Shadcn_ ref={ref} {...props} className={cn('w-full px-2', props.className)} />
|
||||
return (
|
||||
<Collapsible_Shadcn_
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={cn('w-full px-2 group', props.className)}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
const InnerSideMenuCollapsibleTrigger = forwardRef<
|
||||
@@ -73,10 +79,7 @@ const InnerSideMenuCollapsibleContent = forwardRef<
|
||||
<CollapsibleContent_Shadcn_
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={cn(
|
||||
'w-full flex flex-col gap-0 transition-all data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down',
|
||||
props.className
|
||||
)}
|
||||
className={cn('w-full flex flex-col gap-0', props.className)}
|
||||
/>
|
||||
)
|
||||
})
|
||||
@@ -220,7 +223,7 @@ const InnerSideBarEmptyPanel = forwardRef<
|
||||
HTMLDivElement,
|
||||
React.ComponentPropsWithoutRef<'div'> & {
|
||||
title: string
|
||||
description?: string
|
||||
description?: string | React.ReactNode
|
||||
illustration?: React.ReactNode
|
||||
actions?: React.ReactNode
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user