mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
docs(design-system) add dialog pattern documentation (#41296)
* docs * naming * docs * mini fix * update policies component * use destructured prop * rls policies dialogs * custom domain dialogs * restart server dialog * remove ConfirmDialog (aka ConfirmModal) * docs updates * remove unrelated work * remove unrelated work * remove unrelated work * confirmation-modal demo * use indicative components * links * tiny docs updates * examples * sheets and alert dialog examples * docs and examples * docs * docs * text confirm modal * docs * docs * revert cursor rules * fixes * fix links * Update apps/design-system/registry/examples.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * rabbit * rabbit * use variants --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
c6351514b4
commit
f6e8207e6d
37 files changed
+929
-704
No files matched your search
@@ -148,6 +148,17 @@ export const Index: Record<string, any> = {
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"alert-dialog-close-only": {
|
||||
name: "alert-dialog-close-only",
|
||||
type: "components:example",
|
||||
registryDependencies: ["alert-dialog","button"],
|
||||
component: React.lazy(() => import("@/registry/default/example/alert-dialog-close-only")),
|
||||
source: "",
|
||||
files: ["registry/default/example/alert-dialog-close-only.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"aspect-ratio-demo": {
|
||||
name: "aspect-ratio-demo",
|
||||
type: "components:example",
|
||||
@@ -159,6 +170,28 @@ export const Index: Record<string, any> = {
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"alert-dialog-destructive": {
|
||||
name: "alert-dialog-destructive",
|
||||
type: "components:example",
|
||||
registryDependencies: ["alert-dialog","button"],
|
||||
component: React.lazy(() => import("@/registry/default/example/alert-dialog-destructive")),
|
||||
source: "",
|
||||
files: ["registry/default/example/alert-dialog-destructive.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"alert-dialog-warning": {
|
||||
name: "alert-dialog-warning",
|
||||
type: "components:example",
|
||||
registryDependencies: ["alert-dialog","button"],
|
||||
component: React.lazy(() => import("@/registry/default/example/alert-dialog-warning")),
|
||||
source: "",
|
||||
files: ["registry/default/example/alert-dialog-warning.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"avatar-demo": {
|
||||
name: "avatar-demo",
|
||||
type: "components:example",
|
||||
@@ -1237,6 +1270,17 @@ export const Index: Record<string, any> = {
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"sheet-nonmodal": {
|
||||
name: "sheet-nonmodal",
|
||||
type: "components:example",
|
||||
registryDependencies: ["sheet"],
|
||||
component: React.lazy(() => import("@/registry/default/example/sheet-nonmodal")),
|
||||
source: "",
|
||||
files: ["registry/default/example/sheet-nonmodal.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"sheet-side": {
|
||||
name: "sheet-side",
|
||||
type: "components:example",
|
||||
@@ -1820,39 +1864,6 @@ export const Index: Record<string, any> = {
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"text-confirm-dialog-with-info-alert": {
|
||||
name: "text-confirm-dialog-with-info-alert",
|
||||
type: "components:example",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/example/text-confirm-dialog-with-info-alert")),
|
||||
source: "",
|
||||
files: ["registry/default/example/text-confirm-dialog-with-info-alert.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"text-confirm-dialog-with-warning-alert": {
|
||||
name: "text-confirm-dialog-with-warning-alert",
|
||||
type: "components:example",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/example/text-confirm-dialog-with-warning-alert")),
|
||||
source: "",
|
||||
files: ["registry/default/example/text-confirm-dialog-with-warning-alert.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"text-confirm-dialog-with-destructive-alert": {
|
||||
name: "text-confirm-dialog-with-destructive-alert",
|
||||
type: "components:example",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/example/text-confirm-dialog-with-destructive-alert")),
|
||||
source: "",
|
||||
files: ["registry/default/example/text-confirm-dialog-with-destructive-alert.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"text-confirm-dialog-with-size": {
|
||||
name: "text-confirm-dialog-with-size",
|
||||
type: "components:example",
|
||||
@@ -2458,46 +2469,13 @@ export const Index: Record<string, any> = {
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"modal-demo": {
|
||||
name: "modal-demo",
|
||||
"confirmation-modal-demo": {
|
||||
name: "confirmation-modal-demo",
|
||||
type: "components:example",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/example/modal-demo")),
|
||||
component: React.lazy(() => import("@/registry/default/example/confirmation-modal-demo")),
|
||||
source: "",
|
||||
files: ["registry/default/example/modal-demo.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"modal-aligned-footer": {
|
||||
name: "modal-aligned-footer",
|
||||
type: "components:example",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/example/modal-aligned-footer")),
|
||||
source: "",
|
||||
files: ["registry/default/example/modal-aligned-footer.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"modal-custom-footer": {
|
||||
name: "modal-custom-footer",
|
||||
type: "components:example",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/example/modal-custom-footer")),
|
||||
source: "",
|
||||
files: ["registry/default/example/modal-custom-footer.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"modal-hide-footer": {
|
||||
name: "modal-hide-footer",
|
||||
type: "components:example",
|
||||
registryDependencies: undefined,
|
||||
component: React.lazy(() => import("@/registry/default/example/modal-hide-footer")),
|
||||
source: "",
|
||||
files: ["registry/default/example/modal-hide-footer.tsx"],
|
||||
files: ["registry/default/example/confirmation-modal-demo.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
@@ -3005,7 +2983,7 @@ export const Index: Record<string, any> = {
|
||||
source: "",
|
||||
files: ["registry/default/example/copy-error-messages.tsx"],
|
||||
category: "Getting Started",
|
||||
subcategory: "Copywriting",
|
||||
subcategory: "Copywriting",
|
||||
chunks: []
|
||||
},
|
||||
"copy-success-messages": {
|
||||
@@ -3060,7 +3038,7 @@ export const Index: Record<string, any> = {
|
||||
source: "",
|
||||
files: ["registry/default/example/copy-confirmations.tsx"],
|
||||
category: "Getting Started",
|
||||
subcategory: "Copywriting",
|
||||
subcategory: "Copywriting",
|
||||
chunks: []
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { HomepageSvgHandler } from '@/components/homepage-svg-handler'
|
||||
import Link from 'next/link'
|
||||
import { Auth, Database, Realtime } from 'icons/src/icons'
|
||||
import { Paintbrush } from 'lucide-react'
|
||||
import { Realtime, Database, Auth } from 'icons/src/icons'
|
||||
import Link from 'next/link'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
@@ -15,41 +15,6 @@ export default function Home() {
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 grid-cols-1 gap-4 w-full">
|
||||
<Link href="/docs/components/atom-components" className="h-full flex">
|
||||
<div className="p-6 gap-4 flex flex-col justify-between h-full w-full bg-surface-75 hover:bg-overlay/50 hover:border-foreground-muted cursor-pointer transition-all border rounded-md">
|
||||
<div className="flex items-center justify-start min-h-[24px]">
|
||||
<HomepageSvgHandler name="atoms" className="w-6 h-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-medium text-foreground">Atom components</h3>
|
||||
<p className="text-sm text-foreground-light">Building blocks of User interfaces</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<Link href="/docs/fragments/fragment-components" className="h-full flex">
|
||||
<div className="p-6 gap-4 flex flex-col justify-between h-full w-full bg-surface-75 hover:bg-overlay/50 hover:border-foreground-muted cursor-pointer transition-all border rounded-md">
|
||||
<div className="flex items-center justify-start min-h-[24px]">
|
||||
<HomepageSvgHandler name="fragments" className="w-6 h-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-medium text-foreground">Fragment components</h3>
|
||||
<p className="text-sm text-foreground-light">Components assembled from Atoms</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<Link href="/docs/components/ui-patterns" className="h-full flex">
|
||||
<div className="p-6 gap-4 flex flex-col justify-between h-full w-full bg-surface-75 hover:bg-overlay/50 hover:border-foreground-muted cursor-pointer transition-all border rounded-md">
|
||||
<div className="flex items-center justify-start min-h-[24px]">
|
||||
<HomepageSvgHandler name="ui-patterns" className="w-6 h-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-medium text-foreground">UI Patterns components</h3>
|
||||
<p className="text-sm text-foreground-light">
|
||||
Components assembled from Atoms & Fragments
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<Link href="/docs/color-usage" className="h-full flex">
|
||||
<div className="p-6 gap-4 flex flex-col justify-between h-full w-full bg-surface-75 hover:bg-overlay/50 hover:border-foreground-muted cursor-pointer transition-all border rounded-md">
|
||||
<div className="flex flex-col gap-2 min-h-[24px]">
|
||||
@@ -57,19 +22,7 @@ export default function Home() {
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-medium text-foreground">Colors</h3>
|
||||
<p className="text-sm text-foreground-light">Building blocks of User interfaces</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<Link href="/docs/theming" className="h-full flex">
|
||||
<div className="p-6 gap-4 flex flex-col justify-between h-full w-full bg-surface-75 hover:bg-overlay/50 hover:border-foreground-muted cursor-pointer transition-all border rounded-md">
|
||||
<div className="flex items-center justify-start min-h-[24px] text-brand">
|
||||
<Paintbrush className="w-6 h-6" strokeWidth={1.5} stroke="currentColor" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-medium text-foreground">Theming</h3>
|
||||
<p className="text-sm text-foreground-light">Simple extensible theming system</p>
|
||||
<p className="text-sm text-foreground-light">Custom color palette for Supabase</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
@@ -87,6 +40,55 @@ export default function Home() {
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<Link href="/docs/theming" className="h-full flex">
|
||||
<div className="p-6 gap-4 flex flex-col justify-between h-full w-full bg-surface-75 hover:bg-overlay/50 hover:border-foreground-muted cursor-pointer transition-all border rounded-md">
|
||||
<div className="flex items-center justify-start min-h-[24px] text-brand">
|
||||
<Paintbrush className="w-6 h-6" strokeWidth={1.5} stroke="currentColor" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-medium text-foreground">Theming</h3>
|
||||
<p className="text-sm text-foreground-light">Simple extensible theming system</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<Link href="/docs/ui-patterns/introduction" className="h-full flex">
|
||||
<div className="p-6 gap-4 flex flex-col justify-between h-full w-full bg-surface-75 hover:bg-overlay/50 hover:border-foreground-muted cursor-pointer transition-all border rounded-md">
|
||||
<div className="flex items-center justify-start min-h-[24px]">
|
||||
<HomepageSvgHandler name="ui-patterns" className="w-6 h-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-medium text-foreground">UI patterns</h3>
|
||||
<p className="text-sm text-foreground-light">
|
||||
Design guidelines for common interface patterns
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<Link href="/docs/fragments/introduction" className="h-full flex">
|
||||
<div className="p-6 gap-4 flex flex-col justify-between h-full w-full bg-surface-75 hover:bg-overlay/50 hover:border-foreground-muted cursor-pointer transition-all border rounded-md">
|
||||
<div className="flex items-center justify-start min-h-[24px]">
|
||||
<HomepageSvgHandler name="fragments" className="w-6 h-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-medium text-foreground">Fragment components</h3>
|
||||
<p className="text-sm text-foreground-light">Components assembled from atoms</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<Link href="/docs/components/introduction" className="h-full flex">
|
||||
<div className="p-6 gap-4 flex flex-col justify-between h-full w-full bg-surface-75 hover:bg-overlay/50 hover:border-foreground-muted cursor-pointer transition-all border rounded-md">
|
||||
<div className="flex items-center justify-start min-h-[24px]">
|
||||
<HomepageSvgHandler name="atoms" className="w-6 h-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-medium text-foreground">Atom components</h3>
|
||||
<p className="text-sm text-foreground-light">Building blocks of user interfaces</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
'use client'
|
||||
|
||||
import * as React from 'react'
|
||||
import { Index } from '@/__registry__'
|
||||
import * as React from 'react'
|
||||
|
||||
import { useConfig } from '@/hooks/use-config'
|
||||
import { styles } from '@/registry/styles'
|
||||
import { ChevronRight, Expand } from 'lucide-react'
|
||||
import {
|
||||
Button,
|
||||
CollapsibleContent_Shadcn_,
|
||||
@@ -10,9 +13,6 @@ import {
|
||||
Collapsible_Shadcn_,
|
||||
cn,
|
||||
} from 'ui'
|
||||
import { useConfig } from '@/hooks/use-config'
|
||||
import { styles } from '@/registry/styles'
|
||||
import { ChevronRight, Expand } from 'lucide-react'
|
||||
|
||||
interface ComponentPreviewProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
name: string
|
||||
@@ -70,7 +70,7 @@ export function ComponentPreview({
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={cn('preview flex min-h-[350px] w-full justify-center p-10', {
|
||||
className={cn('preview flex min-h-[256px] w-full justify-center p-10', {
|
||||
'items-center': align === 'center',
|
||||
'items-start': align === 'start',
|
||||
'items-end': align === 'end',
|
||||
|
||||
+150
-140
@@ -55,6 +55,155 @@ export const docsConfig: DocsConfig = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'UI Patterns',
|
||||
sortOrder: 'alphabetical',
|
||||
items: [
|
||||
{
|
||||
title: 'Introduction',
|
||||
href: '/docs/ui-patterns/ui-patterns',
|
||||
items: [],
|
||||
priority: true,
|
||||
},
|
||||
{
|
||||
title: 'Charts',
|
||||
href: '/docs/ui-patterns/charts',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Empty States',
|
||||
href: '/docs/ui-patterns/empty-states',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Forms',
|
||||
href: '/docs/ui-patterns/forms',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Layout',
|
||||
href: '/docs/ui-patterns/layout',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
href: '/docs/ui-patterns/modality',
|
||||
title: 'Modality',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
href: '/docs/ui-patterns/navigation',
|
||||
title: 'Navigation',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Tables',
|
||||
href: '/docs/ui-patterns/tables',
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Fragment Components',
|
||||
sortOrder: 'alphabetical',
|
||||
items: [
|
||||
{
|
||||
title: 'Introduction',
|
||||
href: '/docs/fragments/fragment-components',
|
||||
items: [],
|
||||
priority: true,
|
||||
},
|
||||
{
|
||||
title: 'Admonition',
|
||||
href: '/docs/fragments/admonition',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Assistant Chat',
|
||||
href: '/docs/fragments/assistant-chat',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Empty State Presentational',
|
||||
href: '/docs/fragments/empty-state-presentational',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Modal',
|
||||
href: '/docs/fragments/modal',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Page Container',
|
||||
href: '/docs/fragments/page-container',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Page Header',
|
||||
href: '/docs/fragments/page-header',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Page Section',
|
||||
href: '/docs/fragments/page-section',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Text Confirm Dialog',
|
||||
href: '/docs/fragments/text-confirm-dialog',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Info Tooltip',
|
||||
href: '/docs/fragments/info-tooltip',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Inner Side Menu',
|
||||
href: '/docs/fragments/inner-side-menu',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Data Input',
|
||||
href: '/docs/fragments/data-input',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Form Item Layout',
|
||||
href: '/docs/fragments/form-item-layout',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Multi Select',
|
||||
href: '/docs/fragments/multi-select',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Filter Bar',
|
||||
href: '/docs/fragments/filter-bar',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Logs Bar Chart',
|
||||
href: '/docs/fragments/logs-bar-chart',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Metric Card',
|
||||
href: '/docs/fragments/metric-card',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Table of Contents (TOC)',
|
||||
href: '/docs/fragments/toc',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Confirmation Modal',
|
||||
href: '/docs/fragments/confirmation-modal',
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Atom Components',
|
||||
sortOrder: 'alphabetical',
|
||||
@@ -214,7 +363,7 @@ export const docsConfig: DocsConfig = {
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'NavMenu',
|
||||
title: 'Nav Menu',
|
||||
href: '/docs/components/nav-menu',
|
||||
items: [],
|
||||
},
|
||||
@@ -345,144 +494,5 @@ export const docsConfig: DocsConfig = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Fragment Components',
|
||||
sortOrder: 'alphabetical',
|
||||
items: [
|
||||
{
|
||||
title: 'Introduction',
|
||||
href: '/docs/fragments/fragment-components',
|
||||
items: [],
|
||||
priority: true,
|
||||
},
|
||||
{
|
||||
title: 'Admonition',
|
||||
href: '/docs/fragments/admonition',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Assistant Chat',
|
||||
href: '/docs/fragments/assistant-chat',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'EmptyStatePresentational',
|
||||
href: '/docs/fragments/empty-state-presentational',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Modal',
|
||||
href: '/docs/fragments/modal',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Page Container',
|
||||
href: '/docs/fragments/page-container',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Page Header',
|
||||
href: '/docs/fragments/page-header',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Page Section',
|
||||
href: '/docs/fragments/page-section',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Text Confirm Dialog',
|
||||
href: '/docs/fragments/text-confirm-dialog',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Info Tooltip',
|
||||
href: '/docs/fragments/info-tooltip',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Inner Side Menu',
|
||||
href: '/docs/fragments/inner-side-menu',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Data Input',
|
||||
href: '/docs/fragments/data-input',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Form Item Layout',
|
||||
href: '/docs/fragments/form-item-layout',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Multi Select',
|
||||
href: '/docs/fragments/multi-select',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Filter Bar',
|
||||
href: '/docs/fragments/filter-bar',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Logs Bar Chart',
|
||||
href: '/docs/fragments/logs-bar-chart',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Metric Card',
|
||||
href: '/docs/fragments/metric-card',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Table of Contents (TOC)',
|
||||
href: '/docs/fragments/toc',
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'UI Patterns',
|
||||
sortOrder: 'alphabetical',
|
||||
items: [
|
||||
{
|
||||
title: 'Introduction',
|
||||
href: '/docs/ui-patterns/ui-patterns',
|
||||
items: [],
|
||||
priority: true,
|
||||
},
|
||||
{
|
||||
title: 'Charts',
|
||||
href: '/docs/ui-patterns/charts',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Empty States',
|
||||
href: '/docs/ui-patterns/empty-states',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Forms',
|
||||
href: '/docs/ui-patterns/forms',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Layout',
|
||||
href: '/docs/ui-patterns/layout',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Navigation',
|
||||
href: '/docs/ui-patterns/navigation',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Tables',
|
||||
href: '/docs/ui-patterns/tables',
|
||||
items: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -1,16 +1,19 @@
|
||||
---
|
||||
title: Alert Dialog
|
||||
description: A modal dialog that interrupts the user with important content and expects a response.
|
||||
featured: true
|
||||
description: A modal dialog for critical confirmations and acknowledgements that require an explicit user decision.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/alert-dialog
|
||||
api: https://www.radix-ui.com/docs/primitives/components/alert-dialog#api-reference
|
||||
source:
|
||||
radix: true
|
||||
shadcn: true
|
||||
radix: true
|
||||
---
|
||||
|
||||
Alert Dialog interrupts the user’s workflow to communicate critical information or confirm an action that cannot be taken lightly. It presents a short, focused message and requires the user to explicitly confirm or cancel before proceeding.
|
||||
|
||||
Use Alert Dialog for actions such as deleting data, performing irreversible changes, or acknowledging important warnings where dismissal without a decision would be unsafe.
|
||||
|
||||
<ComponentPreview name="alert-dialog-demo" peekCode wide />
|
||||
|
||||
## Installation
|
||||
@@ -85,3 +88,32 @@ import {
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
```
|
||||
|
||||
## Behavior
|
||||
|
||||
Unlike a generic [Dialog](../components/dialog), an Alert Dialog cannot be dismissed by clicking outside the modal. The user must take an explicit action by confirming, cancelling, or pressing Escape.
|
||||
|
||||
This enforced decision helps prevent accidental dismissal of critical warnings or destructive actions.
|
||||
|
||||
## Guidelines
|
||||
|
||||
- **Keep content concise:** AlertDialogDescription renders as a single paragraph and must not contain block-level elements such as lists, multiple paragraphs, or complex layouts.
|
||||
- **Use for critical decisions only:** Reserve Alert Dialog for destructive or irreversible actions, or for warnings that require explicit acknowledgement.
|
||||
- **Always provide a cancel action:** Include AlertDialogCancel so users can safely back out, in addition to supporting the Escape key.
|
||||
- **Avoid rich content:** If the dialog requires detailed explanations, callouts, or form inputs, use [Confirmation Modal](../fragments/confirmation-modal) or [Dialog](../components/dialog) instead.
|
||||
|
||||
See [Modality](../ui-patterns/modality) for guidance on choosing the appropriate dialog pattern.
|
||||
|
||||
## Examples
|
||||
|
||||
### Close only
|
||||
|
||||
<ComponentPreview name="alert-dialog-close-only" />
|
||||
|
||||
### Warning
|
||||
|
||||
<ComponentPreview name="alert-dialog-warning" />
|
||||
|
||||
### Destructive
|
||||
|
||||
<ComponentPreview name="alert-dialog-destructive" />
|
||||
@@ -1,7 +1,6 @@
|
||||
---
|
||||
title: Dialog
|
||||
description: A window overlaid on either the primary window or another dialog window, rendering the content underneath inert.
|
||||
featured: true
|
||||
description: A general-purpose modal for non-critical flows, forms, and custom interactions.
|
||||
component: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/dialog
|
||||
@@ -11,6 +10,20 @@ source:
|
||||
shadcn: true
|
||||
---
|
||||
|
||||
Dialog is a flexible, general-purpose modal used for bespoke interactions such as forms, pickers, multi-step flows, or displaying non-urgent information. Unlike confirmation-focused dialogs, it is designed to be safely dismissible and does not force an explicit decision.
|
||||
|
||||
Dialog can be closed by clicking outside the modal or pressing the Escape key, making it suitable for workflows where cancellation is expected and low-risk.
|
||||
|
||||
Use Dialog when you need full control over layout, content, and behavior, and the interaction does not involve a critical or destructive action.
|
||||
|
||||
For confirmations or warnings, try to use an existing component:
|
||||
|
||||
- Use [Alert Dialog](../components/alert-dialog) for critical confirmations that require an explicit decision
|
||||
- Use [Confirmation Modal](../fragments/confirmation-modal) when additional context is needed for a confirmation
|
||||
- Use [Text Confirm Dialog](../fragments/text-confirm-dialog) for highly destructive actions that require typed intent
|
||||
|
||||
See [Modality](../ui-patterns/modality) for guidance on choosing the appropriate dialog pattern.
|
||||
|
||||
<ComponentPreview name="dialog-demo" peekCode wide />
|
||||
|
||||
## Usage
|
||||
@@ -31,23 +44,31 @@ import {
|
||||
<DialogTrigger>Open</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Are you absolutely sure?</DialogTitle>
|
||||
<DialogTitle>Project settings</DialogTitle>
|
||||
<DialogDescription>
|
||||
This action cannot be undone. This will permanently delete your account and remove your data
|
||||
from our servers.
|
||||
Update configuration options for this project. Changes can be discarded at any time.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
{/* Custom content goes here */}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
```
|
||||
|
||||
## Guidelines
|
||||
|
||||
- **Use for non-critical interactions:** Dialog is appropriate when dismissal has no serious consequences.
|
||||
- **Design for cancellation**: Assume users may close the dialog without completing the action.
|
||||
- **Keep focus contained**: Dialog content should remain scoped to a single task or flow.
|
||||
- **Avoid destructive confirmations**: If the dialog’s primary purpose is to confirm a risky action, use a confirmation-focused pattern instead.
|
||||
- **Compose freely**: Dialog is intentionally unopinionated. Build custom layouts, forms, or step-based flows as needed.
|
||||
|
||||
## Examples
|
||||
|
||||
### Custom close button
|
||||
|
||||
<ComponentPreview name="dialog-close-button" />
|
||||
|
||||
### Centered behaviour
|
||||
### Centered behavior
|
||||
|
||||
You can control whether the dialog is centered by passing `centered={false}` to the `DialogContent` component.
|
||||
|
||||
@@ -69,30 +90,27 @@ You can control whether the dialog is centered by passing `centered={false}` to
|
||||
To activate the `Dialog` component from within a `Context Menu` or `Dropdown Menu`, you must encase the `Context Menu` or
|
||||
`Dropdown Menu` component in the `Dialog` component. For more information, refer to the linked issue [here](https://github.com/radix-ui/primitives/issues/1836).
|
||||
|
||||
```tsx {14-25}
|
||||
```tsx {7-11, 14-23}
|
||||
<Dialog>
|
||||
<ContextMenu>
|
||||
<ContextMenuTrigger>Dialog not centered</ContextMenuTrigger>
|
||||
<ContextMenuTrigger>Show Menu</ContextMenuTrigger>
|
||||
<ContextMenuContent>
|
||||
<ContextMenuItem>Open</ContextMenuItem>
|
||||
<ContextMenuItem>Download</ContextMenuItem>
|
||||
<DialogTrigger asChild>
|
||||
<ContextMenuItem>
|
||||
<span>Delete</span>
|
||||
<span>Show Dialog</span>
|
||||
</ContextMenuItem>
|
||||
</DialogTrigger>
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Are you absolutely sure?</DialogTitle>
|
||||
<DialogDescription>
|
||||
This action cannot be undone. Are you sure you want to permanently delete this file from our
|
||||
servers?
|
||||
</DialogDescription>
|
||||
<DialogTitle>Edit profile</DialogTitle>
|
||||
<DialogDescription>Make changes to your profile here.</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
<Button type="submit">Confirm</Button>
|
||||
<Button>Save changes</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
File renamed without changes.
@@ -12,6 +12,22 @@ source:
|
||||
|
||||
<ComponentPreview name="sheet-demo" peekCode wide />
|
||||
|
||||
1. **Use for side panels**
|
||||
|
||||
- Forms with multiple fields
|
||||
- Settings panels
|
||||
- Detailed editors
|
||||
|
||||
2. **Consider screen size**
|
||||
|
||||
- Sheets work well on desktop
|
||||
- On mobile, consider full-screen or bottom sheet variants
|
||||
|
||||
3. **Structure content clearly**
|
||||
- Use `SheetHeader` and `SheetTitle` for context
|
||||
- Use `SheetSection` to group related fields
|
||||
- Use `SheetFooter` for actions
|
||||
|
||||
## Installation
|
||||
|
||||
<Tabs defaultValue="cli">
|
||||
@@ -80,11 +96,21 @@ import {
|
||||
|
||||
## Examples
|
||||
|
||||
### Side
|
||||
### Nonmodal
|
||||
|
||||
Use the `side` property to `<SheetContent />` to indicate the edge of the screen where the component will appear. The values can be `top`, `right`, `bottom` or `left`.
|
||||
This sheet is nonmodal, meaning it does not block the underlying content. It’s useful when
|
||||
you want to display content that complements the main content of the screen.
|
||||
|
||||
<ComponentPreview name="sheet-side" />
|
||||
<ComponentPreview name="sheet-nonmodal" />
|
||||
|
||||
To have the underlying content resize to fit the sheet (so nothing is overlapping) use the Sidebar component
|
||||
or build a custom panel. You can refer to the following Studio components for guidance:
|
||||
|
||||
- `AIAssistant`
|
||||
- `EditorPanel`
|
||||
- `AdvisorPanel`
|
||||
|
||||
See [`LayoutSidebarProvider`](https://github.com/supabase/supabase/blob/master/apps/studio/components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider.tsx) for more.
|
||||
|
||||
### Size
|
||||
|
||||
@@ -104,3 +130,11 @@ You can adjust the size of the sheet using CSS classes:
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
```
|
||||
|
||||
### Side
|
||||
|
||||
Use the `side` property to `<SheetContent />` to indicate the edge of the screen where the component will appear. The values can be `top`, `right`, `bottom` or `left`.
|
||||
|
||||
<ComponentPreview name="sheet-side" />
|
||||
|
||||
That said, stick to the default `right` unless you have a strong reason not to.
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
title: Confirmation Modal
|
||||
description: A modal dialog for confirmations that require additional context or simple interaction.
|
||||
component: true
|
||||
---
|
||||
|
||||
Confirmation Modal is a convenience wrapper for confirmation flows that are more complex than a single paragraph but do not warrant a full custom dialog. It is built on top of [Dialog](../components/dialog) and provides a prop-based API for consistent confirmation patterns.
|
||||
|
||||
Use Confirmation Modal when the user needs extra context to make a decision, such as explanatory copy, callouts, or small form elements, and the action is not so destructive that it requires typed confirmation.
|
||||
|
||||
If the confirmation can be expressed as a single short paragraph, use [Alert Dialog](../components/alert-dialog). If the action is highly destructive and requires explicit typed intent, use [Text Confirm Dialog](../fragments/text-confirm-dialog). See [Modality](../ui-patterns/modality) for broader guidance on choosing the appropriate pattern.
|
||||
|
||||
<ComponentPreview name="confirmation-modal-demo" peekCode wide />
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { Button } from 'ui'
|
||||
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
|
||||
```
|
||||
|
||||
```tsx
|
||||
export default function ConfirmationModalDemo() {
|
||||
const [visible, setVisible] = useState(false)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={() => setVisible(true)}>Show Confirmation Modal</Button>
|
||||
|
||||
<ConfirmationModal
|
||||
visible={visible}
|
||||
size="small"
|
||||
title="Resume this project"
|
||||
confirmLabel="Resume"
|
||||
confirmLabelLoading="Resuming"
|
||||
cancelLabel="Cancel"
|
||||
onConfirm={() => {
|
||||
setVisible(false)
|
||||
}}
|
||||
onCancel={() => {
|
||||
setVisible(false)
|
||||
}}
|
||||
>
|
||||
This will resume the project and restart any paused processes.
|
||||
</ConfirmationModal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Guidelines
|
||||
|
||||
- **Use for moderate complexity:** Suitable when the confirmation requires more than a single sentence but does not need typed intent.
|
||||
- **Avoid critical destruction:** Do not use for irreversible or high-risk actions that could benefit from stronger safeguards.
|
||||
- **Keep content focused:** Include only the context needed to make the decision. If the dialog becomes a full flow, use a custom [Dialog](../components/dialog) instead.
|
||||
- **Provide clear actions:** Ensure confirm and cancel labels clearly describe the outcome of each choice.
|
||||
|
||||
## Props
|
||||
|
||||
- `visible`: Controls open state
|
||||
- `title`: Dialog title
|
||||
- `description`: Optional description
|
||||
- `variant`: 'default' | 'destructive' | 'warning'
|
||||
- `loading`: Loading state
|
||||
- `onConfirm`: Confirm handler
|
||||
- `onCancel`: Cancel handler
|
||||
- `alert`: Optional callout (see [Admonition](../fragments/admonition))
|
||||
- `children`: Additional content
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: EmptyStatePresentational
|
||||
title: Empty State Presentational
|
||||
description: An empty state for encouraging action.
|
||||
component: true
|
||||
fragment: true
|
||||
@@ -17,7 +17,7 @@ All text should be written using active language. The title should prompt the us
|
||||
|
||||
### Icon
|
||||
|
||||
Supports both Lucide icons and [custom icons](../icons) via the `icons` package. If neither are passed, EmptyStatePresentational falls back to Lucide’s `SquarePlus`.
|
||||
Supports both Lucide icons and [custom icons](../icons) via the `icons` package. If neither are passed, Empty State Presentational falls back to Lucide’s `SquarePlus`.
|
||||
|
||||
<ComponentPreview name="empty-state-presentational-demo" peekCode />
|
||||
|
||||
@@ -25,10 +25,10 @@ See also [Empty States](../ui-patterns/empty-states).
|
||||
|
||||
## Examples
|
||||
|
||||
It’s okay to repeat buttons inside of EmptyStatePresentational that are also available outside of it. The alternative is to conditionally determine button placement whilst polling for list length (to determine whether to show an empty state or not). This is problematic for two reasons:
|
||||
It’s okay to repeat buttons inside of Empty State Presentational that are also available outside of it. The alternative is to conditionally determine button placement whilst polling for list length (to determine whether to show an empty state or not). This is problematic for two reasons:
|
||||
|
||||
1. Rendering after client-side polling often leads to confusing layout shift. This layout shift becomes exacerbated when buttons are stacked against other objects.
|
||||
2. Consistent entry points outside of EmptyStatePresentational also teach a pattern that will continue to exist post initial object creation.
|
||||
2. Consistent entry points outside of Empty State Presentational also teach a pattern that will continue to exist post initial object creation.
|
||||
|
||||
When repeating buttons, set the `type` to `default` so the original `primary`, button remains the only `primary` action on display.
|
||||
|
||||
|
||||
File renamed without changes.
@@ -1,24 +0,0 @@
|
||||
---
|
||||
title: Modal
|
||||
description: A window overlaid on either the primary window or another dialog window, rendering the content underneath inert.
|
||||
fragment: true
|
||||
links:
|
||||
doc: https://www.radix-ui.com/docs/primitives/components/dialog
|
||||
api: https://www.radix-ui.com/docs/primitives/components/dialog#api-reference
|
||||
---
|
||||
|
||||
<ComponentPreview name="modal-demo" peekCode wide />
|
||||
|
||||
## Examples
|
||||
|
||||
### Aligned footer
|
||||
|
||||
<ComponentPreview name="modal-aligned-footer" />
|
||||
|
||||
### Hide footer
|
||||
|
||||
<ComponentPreview name="modal-hide-footer" />
|
||||
|
||||
### Custom footer
|
||||
|
||||
<ComponentPreview name="modal-custom-footer" />
|
||||
@@ -1,57 +1,73 @@
|
||||
---
|
||||
title: Text Confirm Dialog
|
||||
description: A modal dialog that interrupts the user with important content and expects a response.
|
||||
description: A modal dialog that adds a deliberate confirmation step for highly destructive actions.
|
||||
component: true
|
||||
---
|
||||
|
||||
<ComponentPreview
|
||||
name="text-confirm-dialog-demo"
|
||||
description="An alert with an icon, title and description. The title says 'Heads up!' and the description is 'You can add components to your app using the cli.'."
|
||||
peekCode
|
||||
showDottedGrid
|
||||
wide
|
||||
/>
|
||||
Text Confirm Dialog adds a deliberate “speed bump” before a highly destructive action by requiring the user to type an exact confirmation string before the confirm action is enabled. It wraps the Shadcn [Dialog](../components/dialog) component and is intended for actions that must not be triggered accidentally.
|
||||
|
||||
Use Text Confirm Dialog for irreversible operations such as deleting buckets, projects, or other critical resources where an explicit signal of user intent is required beyond a button click.
|
||||
|
||||
For non-destructive or less critical confirmations, use [Alert Dialog](../components/alert-dialog) or [Confirmation Modal](../fragments/confirmation-modal) instead. See [Modality](../ui-patterns/modality) for guidance on choosing the appropriate dialog pattern.
|
||||
|
||||
<ComponentPreview name="text-confirm-dialog-demo" peekCode showDottedGrid wide />
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { Button } from 'ui'
|
||||
import TextConfirmModal from 'ui-patterns/Dialogs/TextConfirmModal'
|
||||
```
|
||||
|
||||
```tsx
|
||||
export default function TextConfirmDialogDemo() {
|
||||
const [visible, setVisible] = useState(false)
|
||||
const bucketName = 'profile-pictures'
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button type="danger" onClick={() => setVisible(true)}>
|
||||
Show Text Confirm Dialog
|
||||
</Button>
|
||||
|
||||
<TextConfirmModal
|
||||
visible={visible}
|
||||
size="small"
|
||||
variant="destructive"
|
||||
title="Delete bucket"
|
||||
confirmPlaceholder={bucketName}
|
||||
confirmString={bucketName}
|
||||
confirmLabel="Delete bucket"
|
||||
onConfirm={() => setVisible(false)}
|
||||
onCancel={() => setVisible(false)}
|
||||
>
|
||||
{/* Optional body content */}
|
||||
</TextConfirmModal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
- `confirmString`: The exact string the user must type to enable the confirm action
|
||||
- `confirmPlaceholder`: Placeholder text shown in the confirmation input
|
||||
- `variant`: Visual intent of the dialog (`default`, `destructive`, or `warning`)
|
||||
- Other standard modal props inherited from the underlying [Dialog](../components/dialog) component
|
||||
|
||||
## Examples
|
||||
|
||||
### With Info Alert
|
||||
### Cancel button
|
||||
|
||||
<ComponentPreview
|
||||
name="text-confirm-dialog-with-info-alert"
|
||||
description="An alert with an icon, title and description. The title says 'Heads up!' and the description is 'You can add components to your app using the cli.'."
|
||||
/>
|
||||
<ComponentPreview name="text-confirm-dialog-with-cancel-button" />
|
||||
|
||||
### With warning Alert
|
||||
### Children
|
||||
|
||||
<ComponentPreview
|
||||
name="text-confirm-dialog-with-warning-alert"
|
||||
description="An alert with a destructive variant. The title says 'Delete this item?' and the description is 'This action cannot be undone.'."
|
||||
/>
|
||||
<ComponentPreview name="text-confirm-dialog-with-children" />
|
||||
|
||||
### With destructive Alert
|
||||
### Size
|
||||
|
||||
<ComponentPreview
|
||||
name="text-confirm-dialog-with-destructive-alert"
|
||||
description="An alert with a destructive variant. The title says 'Delete this item?' and the description is 'This action cannot be undone.'."
|
||||
/>
|
||||
|
||||
### With cancel button
|
||||
|
||||
<ComponentPreview
|
||||
name="text-confirm-dialog-with-cancel-button"
|
||||
description="An alert with a destructive variant. The title says 'Delete this item?' and the description is 'This action cannot be undone.'."
|
||||
/>
|
||||
|
||||
### With children
|
||||
|
||||
<ComponentPreview
|
||||
name="text-confirm-dialog-with-children"
|
||||
description="An alert with a destructive variant. The title says 'Delete this item?' and the description is 'This action cannot be undone.'."
|
||||
/>
|
||||
|
||||
### With size
|
||||
|
||||
<ComponentPreview
|
||||
name="text-confirm-dialog-with-size"
|
||||
description="An alert with a destructive variant. The title says 'Delete this item?' and the description is 'This action cannot be undone.'."
|
||||
/>
|
||||
<ComponentPreview name="text-confirm-dialog-with-size" />
|
||||
@@ -3,28 +3,30 @@ title: Empty states
|
||||
description: Convey the absence of data and provide clear instruction for what to do about it.
|
||||
---
|
||||
|
||||
Empty states convey the fact that there is nothing to list, perform, or display on the current page. **Ideally**, they also provide a clear action for the user to take.
|
||||
Empty states convey the fact that there is nothing to list, perform, or display on the current page. Ideally, they also provide a clear action for the user to take.
|
||||
|
||||
## No data
|
||||
## Best practices
|
||||
|
||||
### No data
|
||||
|
||||
There are two ways an empty state may be displayed in cases where there is no data:
|
||||
|
||||
- **Initial state**: no data to begin with
|
||||
- **Zero results**: no data after a search or filter
|
||||
|
||||
### Initial state
|
||||
#### Initial state
|
||||
|
||||
Perhaps the user has not yet created any data. The presentation of this empty state depends on the context of the list and the type of data it contains. Be mindful of the journey to rendering an empty state and any possible layout shift along the way.
|
||||
|
||||
#### Presentational
|
||||
##### Presentational
|
||||
|
||||
The user may be learning about a feature for the first time, and could benefit from lightweight feature education or onboarding. Use the dedicated [EmptyStatePresentational](../fragments/empty-state-presentational) component in this case, putting emphasis on an action the user can take.
|
||||
The user may be learning about a feature for the first time, and could benefit from lightweight feature education or onboarding. Use the dedicated [Empty State Presentational](../fragments/empty-state-presentational) component in this case, putting emphasis on an action the user can take.
|
||||
|
||||
<ComponentPreview name="empty-state-presentational-icon" peekCode wide />
|
||||
|
||||
Remember to use active language in presentational empty states. For example: “Create a vector bucket” instead of “No vector buckets found”. The latter is more appropriate in table-based presentations, as described below.
|
||||
|
||||
#### Informational
|
||||
##### Informational
|
||||
|
||||
Or perhaps the list type is data-heavy or does not benefit from additional information. In these cases, the empty state should provide show the initial state in the same presentation as the list when there is data, much like the [zero results](#zero-results) scenario.
|
||||
|
||||
@@ -32,11 +34,11 @@ Or perhaps the list type is data-heavy or does not benefit from additional infor
|
||||
|
||||
Keep in mind that empty states will likely appear after a visual loading state. Consider layout shift and button placement during and after the transition.
|
||||
|
||||
### Zero results
|
||||
#### Zero results
|
||||
|
||||
Data-heavy presentations without results should have an empty state that broadly matches the state when there is data. This makes the transition between the two states more seamless.
|
||||
|
||||
#### Table
|
||||
##### Table
|
||||
|
||||
A [Table](../components/table) instance with zero results should display a single row. Dulling the TableHead text color and removing the TableCell hover state can further reinforce the lack of usable data.
|
||||
|
||||
@@ -47,7 +49,7 @@ Studio contains two pre-built components to handle these cases consistently:
|
||||
- No Filter Results
|
||||
- No Search Results
|
||||
|
||||
#### Data Grid
|
||||
##### Data Grid
|
||||
|
||||
[Data Grid](../ui-patterns/tables#data-grid) and [Data Table](../ui-patterns/tables#data-table) component patterns typically span the full height and width of a container. A classic example is [Users](https://supabase.com/dashboard/project/_/auth/users), which (as it sounds) displays a list of the project’s registered users. Any instance with zero results should display a more prominent empty with a clear title, description, and supporting illustration.
|
||||
|
||||
@@ -55,7 +57,7 @@ Studio contains two pre-built components to handle these cases consistently:
|
||||
|
||||
Other Data Grid instances include [Cron Jobs](https://supabase.com/dashboard/project/_/integrations/cron/jobs) and [Queues](https://supabase.com/dashboard/project/_/integrations/queues).
|
||||
|
||||
## Missing route
|
||||
### Missing route
|
||||
|
||||
Users may accidentally navigate to a non-existent dynamic route, such as a non-existent bucket in [Storage](https://supabase.com/dashboard/project/_/storage) or a non-existent table in the [Table Editor](https://supabase.com/dashboard/project/_/editor). In these cases, follow the pattern of a centered [Admonition](../fragments/admonition) as shown below.
|
||||
|
||||
@@ -63,6 +65,6 @@ Users may accidentally navigate to a non-existent dynamic route, such as a non-e
|
||||
|
||||
## Components
|
||||
|
||||
For presentational empty states (initial states with value propositions and actions), use the [EmptyStatePresentational](../fragments/empty-state-presentational) component from `ui-patterns`. This component provides a consistent structure with support for icons, titles, descriptions, and action buttons.
|
||||
For presentational empty states (initial states with value propositions and actions), use the [Empty State Presentational](../fragments/empty-state-presentational) component from `ui-patterns`. This component provides a consistent structure with support for icons, titles, descriptions, and action buttons.
|
||||
|
||||
For other empty state scenarios (zero results, missing routes, etc), custom components may still be appropriate as the context and needs for each placement can differ significantly.
|
||||
File renamed without changes.
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: Modality
|
||||
description: Present ephemeral information and demand action.
|
||||
---
|
||||
|
||||
Modal elements interrupt the user’s current task to ask for input, a decision, or focused attention. They appear at the top of the visual stack and (by default) render everything beneath them inactive.
|
||||
|
||||
Given their highly interruptive nature, modal elements should be used sparingly. Common use cases include:
|
||||
|
||||
- Requiring confirmation from the user
|
||||
- Requiring an ephemeral form submission from the user before an action can be completed
|
||||
- Alerting or slowing the user down before a destructive action
|
||||
|
||||
We have two main ways of handling modality:
|
||||
|
||||
- [Dialogs](#dialogs)
|
||||
- [Sheets](#sheets)
|
||||
|
||||
As a general rule: use dialogs for short, focused tasks and use sheets for longer forms or more detailed views.
|
||||
|
||||
## Dialogs
|
||||
|
||||
Dialogs are centered overlays used for short, focused tasks. All dialogs should follow these best practices:
|
||||
|
||||
- **Reiterative:** Dialog header and confirmation button text and should match the action and flow on from the entry point.
|
||||
- **Simple:** No layered elements like subtitles or admonitions unless necessary. Put all the focus on the actions to get out of the dialog.
|
||||
- **Accessible:** Always provide clear labels and descriptions via semantic HTML and the correct ARIA attributes. Ensure keyboard navigation works correctly.
|
||||
|
||||
### Components
|
||||
|
||||
There are quite a few dialog components, each suited to a different task or context:
|
||||
|
||||
- [Alert Dialog](../components/alert-dialog) contains a single, short paragraph and an explicit action.
|
||||
- [Text Confirm Dialog](../fragments/text-confirm-dialog) requires a textual response before the action is enabled.
|
||||
- [Confirmation Modal](../fragments/confirmation-modal) provides more flexible dialog body contents.
|
||||
- [Dialog](../components/dialog) is a generalized component for bespoke purposes.
|
||||
|
||||
#### Alert Dialog
|
||||
|
||||
[Alert Dialog](../components/alert-dialog) is used to confirm or acknowledge a critical action with a single, short paragraph and a clear decision.
|
||||
|
||||
<ComponentPreview name="alert-dialog-demo" />
|
||||
|
||||
#### Text Confirm Dialog
|
||||
|
||||
[Text Confirm Dialog](../fragments/text-confirm-dialog) adds a deliberate speed bump for highly destructive actions by requiring the user to type an exact confirmation string before proceeding.
|
||||
|
||||
<ComponentPreview name="text-confirm-dialog-demo" />
|
||||
|
||||
#### Confirmation Modal
|
||||
|
||||
[Confirmation Modal](../fragments/confirmation-modal) is a convenience wrapper for less-critical confirmations that require more than a single paragraph, such as additional context, callouts, or simple form elements.
|
||||
|
||||
<ComponentPreview name="confirmation-modal-demo" />
|
||||
|
||||
#### Dialog
|
||||
|
||||
[Dialog](../components/dialog) is a general-purpose modal for bespoke flows such as forms, pickers, or non-critical interactions where dismissal is acceptable.
|
||||
|
||||
<ComponentPreview name="dialog-demo" />
|
||||
|
||||
## Sheets
|
||||
|
||||
Sheets are dialogs presented as side panels. Use them for content that is larger than a few fields, or when a centered dialog would feel cramped.
|
||||
|
||||
- **Use for**: multi-field forms, editors, settings panels, and detailed views.
|
||||
- **Prefer the default**: sheets slide in from the right unless you have a strong reason to use another side.
|
||||
- **Group content**: use header/sections/footer so the user can scan and act quickly.
|
||||
|
||||
### Components
|
||||
|
||||
#### Sheet
|
||||
|
||||
[Sheet](../components/sheet) is modal by default, blocking interaction with the underlying page.
|
||||
|
||||
<ComponentPreview name="sheet-demo" />
|
||||
@@ -0,0 +1,33 @@
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
Button,
|
||||
} from 'ui'
|
||||
|
||||
export default function AlertDialogCloseOnly() {
|
||||
return (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button type="outline">Show Alert Dialog</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Application submitted</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
Thank you for your submission! Please check your email for a confirmation link to
|
||||
complete your application.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Close</AlertDialogCancel>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
)
|
||||
}
|
||||
@@ -8,26 +8,27 @@ import {
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
Button,
|
||||
} from 'ui'
|
||||
import { Button } from 'ui'
|
||||
|
||||
export default function AlertDialogDemo() {
|
||||
return (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button type="outline">Show Dialog</Button>
|
||||
<Button type="outline">Show Alert Dialog</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
||||
<AlertDialogTitle>Create new API keys</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
This action cannot be undone. This will permanently delete your account and remove your
|
||||
data from our servers.
|
||||
This will create a default publishable key and a default secret key both named{' '}
|
||||
<code className="text-code-inline">default</code>. These keys are required to connect
|
||||
your application to your Supabase project.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction>Continue</AlertDialogAction>
|
||||
<AlertDialogAction>Create keys</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
Button,
|
||||
} from 'ui'
|
||||
|
||||
export default function AlertDialogDestructive() {
|
||||
return (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button type="danger">Show Alert Dialog</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>
|
||||
Delete <code className="text-code-inline">hello-world</code>
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
This action cannot be undone. Ensure that you have a backup in case you want to restore
|
||||
this edge function.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction variant="danger">Delete</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
Button,
|
||||
} from 'ui'
|
||||
|
||||
export default function AlertDialogWarning() {
|
||||
return (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button type="warning">Show Alert Dialog</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Update branch</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
This branch has 3 modified edge functions that will be overwritten when updating with
|
||||
the latest functions from the production branch. This action cannot be undone.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction variant="warning">Update</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import { useState } from 'react'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import {
|
||||
Button,
|
||||
Form_Shadcn_,
|
||||
FormControl_Shadcn_,
|
||||
FormField_Shadcn_,
|
||||
Select_Shadcn_,
|
||||
SelectContent_Shadcn_,
|
||||
SelectItem_Shadcn_,
|
||||
SelectTrigger_Shadcn_,
|
||||
SelectValue_Shadcn_,
|
||||
} from 'ui'
|
||||
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
|
||||
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
||||
|
||||
export default function ConfirmationModalDemo() {
|
||||
const [visible, setVisible] = useState(false)
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
postgresVersion: '17.6.1.054',
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button type="default" onClick={() => setVisible(!visible)}>
|
||||
Show Confirmation Modal
|
||||
</Button>
|
||||
<ConfirmationModal
|
||||
visible={visible}
|
||||
size="small"
|
||||
title="Resume this project"
|
||||
onCancel={() => setVisible(false)}
|
||||
onConfirm={() => {}}
|
||||
loading={false}
|
||||
confirmLabel="Resume"
|
||||
confirmLabelLoading="Resuming"
|
||||
cancelLabel="Cancel"
|
||||
>
|
||||
{/* Dialog contents */}
|
||||
<div>
|
||||
{/* Text content */}
|
||||
<p className="block text-sm pb-4 border-b border-border-muted">
|
||||
Your project’s data will be restored to when it was initially paused.
|
||||
</p>
|
||||
{/* Dropdown for Postgres version */}
|
||||
<div className="pt-4">
|
||||
<Form_Shadcn_ {...form}>
|
||||
<FormField_Shadcn_
|
||||
control={form.control}
|
||||
name="postgresVersion"
|
||||
render={({ field }) => (
|
||||
<FormItemLayout layout="vertical" label="Postgres version">
|
||||
<FormControl_Shadcn_>
|
||||
<Select_Shadcn_ value={field.value} onValueChange={field.onChange}>
|
||||
<SelectTrigger_Shadcn_>
|
||||
<SelectValue_Shadcn_ />
|
||||
</SelectTrigger_Shadcn_>
|
||||
<SelectContent_Shadcn_>
|
||||
<SelectItem_Shadcn_ value="17.6.1.054">17.6.1.054</SelectItem_Shadcn_>
|
||||
<SelectItem_Shadcn_ value="17.6.1.055">17.6.1.055</SelectItem_Shadcn_>
|
||||
</SelectContent_Shadcn_>
|
||||
</Select_Shadcn_>
|
||||
</FormControl_Shadcn_>
|
||||
</FormItemLayout>
|
||||
)}
|
||||
/>
|
||||
</Form_Shadcn_>
|
||||
</div>
|
||||
</div>
|
||||
</ConfirmationModal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,29 +1,31 @@
|
||||
import { Button, DialogSection, DialogSectionSeparator } from 'ui'
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogSection,
|
||||
DialogSectionSeparator,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
Input_Shadcn_,
|
||||
Label_Shadcn_,
|
||||
} from 'ui'
|
||||
import { Input_Shadcn_ } from 'ui'
|
||||
import { Label_Shadcn_ } from 'ui'
|
||||
|
||||
export default function DialogDemo() {
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button type="default">Edit Profile</Button>
|
||||
<Button type="default">Edit profile</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[425px]" centered={false}>
|
||||
<DialogHeader padding={'small'}>
|
||||
<DialogHeader>
|
||||
<DialogTitle>This dialog is not centered.</DialogTitle>
|
||||
<DialogDescription>This dialog is not centered.</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogSectionSeparator />
|
||||
<DialogSection className="space-y-4" padding={'small'}>
|
||||
<DialogSection className="space-y-4">
|
||||
<div>
|
||||
<Label_Shadcn_ htmlFor="name" className="text-right">
|
||||
Name
|
||||
@@ -37,7 +39,7 @@ export default function DialogDemo() {
|
||||
<Input_Shadcn_ id="username" defaultValue="@peduarte" className="col-span-3" />
|
||||
</div>
|
||||
</DialogSection>
|
||||
<DialogFooter padding={'small'}>
|
||||
<DialogFooter>
|
||||
<Button htmlType="submit">Save changes</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
import { Copy } from 'lucide-react'
|
||||
|
||||
import { Button, DialogSection, DialogSectionSeparator } from 'ui'
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogSection,
|
||||
DialogSectionSeparator,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
Input_Shadcn_,
|
||||
Label_Shadcn_,
|
||||
} from 'ui'
|
||||
import { Input_Shadcn_ } from 'ui'
|
||||
import { Label_Shadcn_ } from 'ui'
|
||||
|
||||
export default function DialogCloseButton() {
|
||||
return (
|
||||
@@ -21,12 +23,12 @@ export default function DialogCloseButton() {
|
||||
<Button type="default">Share</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader padding={'small'}>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Share link</DialogTitle>
|
||||
<DialogDescription>Anyone who has this link will be able to view this.</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogSectionSeparator />
|
||||
<DialogSection padding={'small'}>
|
||||
<DialogSection>
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="grid flex-1 gap-2">
|
||||
<Label_Shadcn_ htmlFor="link" className="sr-only">
|
||||
@@ -44,7 +46,7 @@ export default function DialogCloseButton() {
|
||||
</Button>
|
||||
</div>
|
||||
</DialogSection>
|
||||
<DialogFooter className="sm:justify-start" padding={'small'}>
|
||||
<DialogFooter className="sm:justify-start">
|
||||
<DialogClose asChild>
|
||||
<Button type="default" htmlType="button">
|
||||
Custom Close Button
|
||||
|
||||
@@ -1,46 +1,42 @@
|
||||
import { Button, DialogSection, DialogSectionSeparator } from 'ui'
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogSection,
|
||||
DialogSectionSeparator,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
Input_Shadcn_,
|
||||
Label_Shadcn_,
|
||||
} from 'ui'
|
||||
import { Input_Shadcn_ } from 'ui'
|
||||
import { Label_Shadcn_ } from 'ui'
|
||||
|
||||
export default function DialogDemo() {
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button type="default">Edit Profile</Button>
|
||||
<Button type="default">Show Dialog</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader padding={'small'}>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Edit profile</DialogTitle>
|
||||
<DialogDescription>
|
||||
Make changes to your profile here. Click save when you're done.
|
||||
</DialogDescription>
|
||||
<DialogDescription>Make changes to your profile here.</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogSectionSeparator />
|
||||
<DialogSection className="space-y-4" padding={'small'}>
|
||||
<DialogSection className="space-y-4">
|
||||
<div>
|
||||
<Label_Shadcn_ htmlFor="name" className="text-right">
|
||||
Name
|
||||
</Label_Shadcn_>
|
||||
<Input_Shadcn_ id="name" defaultValue="Pedro Duarte" className="col-span-3" />
|
||||
<Label_Shadcn_ htmlFor="name">Name</Label_Shadcn_>
|
||||
<Input_Shadcn_ id="name" defaultValue="Pedro Duarte" />
|
||||
</div>
|
||||
<div>
|
||||
<Label_Shadcn_ htmlFor="username" className="text-right">
|
||||
Username
|
||||
</Label_Shadcn_>
|
||||
<Input_Shadcn_ id="username" defaultValue="@peduarte" className="col-span-3" />
|
||||
<Label_Shadcn_ htmlFor="username">Username</Label_Shadcn_>
|
||||
<Input_Shadcn_ id="username" defaultValue="@peduarte" />
|
||||
</div>
|
||||
</DialogSection>
|
||||
<DialogFooter padding={'small'}>
|
||||
<Button htmlType="submit">Save changes</Button>
|
||||
<DialogFooter>
|
||||
<Button>Save changes</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@@ -2,18 +2,16 @@
|
||||
|
||||
import * as React from 'react'
|
||||
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useMediaQuery } from '@/hooks/use-media-query'
|
||||
import { Button } from 'ui'
|
||||
import { cn } from '@/lib/utils'
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from 'ui'
|
||||
import {
|
||||
Drawer,
|
||||
DrawerClose,
|
||||
DrawerContent,
|
||||
@@ -22,9 +20,9 @@ import {
|
||||
DrawerHeader,
|
||||
DrawerTitle,
|
||||
DrawerTrigger,
|
||||
Input_Shadcn_,
|
||||
Label_Shadcn_,
|
||||
} from 'ui'
|
||||
import { Input_Shadcn_ } from 'ui'
|
||||
import { Label_Shadcn_ } from 'ui'
|
||||
|
||||
export default function DrawerDialogDemo() {
|
||||
const [open, setOpen] = React.useState(false)
|
||||
@@ -34,7 +32,7 @@ export default function DrawerDialogDemo() {
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button type="outline">Edit Profile</Button>
|
||||
<Button type="outline">Edit profile</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
@@ -52,7 +50,7 @@ export default function DrawerDialogDemo() {
|
||||
return (
|
||||
<Drawer open={open} onOpenChange={setOpen}>
|
||||
<DrawerTrigger asChild>
|
||||
<Button type="outline">Edit Profile</Button>
|
||||
<Button type="outline">Edit profile</Button>
|
||||
</DrawerTrigger>
|
||||
<DrawerContent>
|
||||
<DrawerHeader className="text-left">
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import { Link2 } from 'lucide-react'
|
||||
import { useState } from 'react'
|
||||
import { Button, Modal } from 'ui'
|
||||
|
||||
export default function ModalDemo() {
|
||||
const [visible, setVisible] = useState(false)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button type="default" onClick={() => setVisible(!visible)}>
|
||||
Open Modal
|
||||
</Button>
|
||||
<Modal
|
||||
visible={visible}
|
||||
onCancel={() => setVisible(!visible)}
|
||||
onConfirm={() => setVisible(!visible)}
|
||||
title="This is the title of the modal"
|
||||
description="And i am the description"
|
||||
size="medium"
|
||||
hideClose={false}
|
||||
header={
|
||||
<div className="flex items-center gap-2 text-foreground">
|
||||
<div className="text-brand">
|
||||
<Link2 />
|
||||
</div>
|
||||
<div className="flex items-baseline gap-2">
|
||||
<h3>This is the title</h3>
|
||||
<span className="text-xs text-foreground-muted">This is the title</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Modal.Content>
|
||||
<p>
|
||||
Modal content is inserted here, if you need to insert anything into the Modal you can do
|
||||
so via `children`.
|
||||
</p>
|
||||
</Modal.Content>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
import { Button, Input_Shadcn_, Label_Shadcn_ } from 'ui'
|
||||
import {
|
||||
Button,
|
||||
Input_Shadcn_,
|
||||
Label_Shadcn_,
|
||||
Sheet,
|
||||
SheetClose,
|
||||
SheetContent,
|
||||
SheetDescription,
|
||||
SheetFooter,
|
||||
SheetHeader,
|
||||
SheetSection,
|
||||
SheetTitle,
|
||||
SheetTrigger,
|
||||
} from 'ui'
|
||||
@@ -14,34 +17,34 @@ export default function SheetDemo() {
|
||||
return (
|
||||
<Sheet>
|
||||
<SheetTrigger asChild>
|
||||
<Button type="outline">Open</Button>
|
||||
<Button type="outline">Show Sheet</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent>
|
||||
<SheetContent className="flex flex-col gap-0">
|
||||
<SheetHeader>
|
||||
<SheetTitle>Edit profile</SheetTitle>
|
||||
<SheetDescription>
|
||||
Make changes to your profile here. Click save when you re done.
|
||||
</SheetDescription>
|
||||
<SheetDescription>Make changes to your profile here.</SheetDescription>
|
||||
</SheetHeader>
|
||||
<div className="grid gap-4 py-4">
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label_Shadcn_ htmlFor="name" className="text-right">
|
||||
Name
|
||||
</Label_Shadcn_>
|
||||
<Input_Shadcn_ id="name" value="Pedro Duarte" className="col-span-3" readOnly />
|
||||
</div>
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label_Shadcn_ htmlFor="username" className="text-right">
|
||||
Username
|
||||
</Label_Shadcn_>
|
||||
<Input_Shadcn_ id="username" value="@peduarte" className="col-span-3" readOnly />
|
||||
</div>
|
||||
<div className="overflow-auto flex-grow px-0">
|
||||
<SheetSection>
|
||||
<div className="grid gap-4 py-4">
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label_Shadcn_ htmlFor="name" className="text-right">
|
||||
Name
|
||||
</Label_Shadcn_>
|
||||
<Input_Shadcn_ id="name" value="Pedro Duarte" className="col-span-3" readOnly />
|
||||
</div>
|
||||
<div className="grid grid-cols-4 items-center gap-4">
|
||||
<Label_Shadcn_ htmlFor="username" className="text-right">
|
||||
Username
|
||||
</Label_Shadcn_>
|
||||
<Input_Shadcn_ id="username" value="@peduarte" className="col-span-3" readOnly />
|
||||
</div>
|
||||
</div>
|
||||
</SheetSection>
|
||||
</div>
|
||||
<SheetFooter>
|
||||
<SheetClose asChild>
|
||||
<Button htmlType="submit" type="secondary">
|
||||
Save changes
|
||||
</Button>
|
||||
<Button type="primary">Save changes</Button>
|
||||
</SheetClose>
|
||||
</SheetFooter>
|
||||
</SheetContent>
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import {
|
||||
Button,
|
||||
Sheet,
|
||||
SheetClose,
|
||||
SheetContent,
|
||||
SheetFooter,
|
||||
SheetHeader,
|
||||
SheetSection,
|
||||
SheetTitle,
|
||||
SheetTrigger,
|
||||
} from 'ui'
|
||||
|
||||
export default function SheetNonmodal() {
|
||||
return (
|
||||
<Sheet modal={false}>
|
||||
<SheetTrigger asChild>
|
||||
<Button type="outline">Show Sheet</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent className="flex flex-col gap-0">
|
||||
<SheetHeader>
|
||||
<SheetTitle>Log details</SheetTitle>
|
||||
</SheetHeader>
|
||||
<div className="overflow-auto flex-grow px-0">
|
||||
<SheetSection>
|
||||
<p className="text-sm text-foreground-lighter">
|
||||
This sheet does not block the underlying content, but it does overlap it.
|
||||
</p>
|
||||
</SheetSection>
|
||||
</div>
|
||||
<SheetFooter>
|
||||
<SheetClose asChild>
|
||||
<Button type="outline">Close</Button>
|
||||
</SheetClose>
|
||||
</SheetFooter>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
)
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
'use client'
|
||||
|
||||
import { Button } from 'ui'
|
||||
import { Input_Shadcn_ } from 'ui'
|
||||
import { Label_Shadcn_ } from 'ui'
|
||||
import {
|
||||
Button,
|
||||
Input_Shadcn_,
|
||||
Label_Shadcn_,
|
||||
Sheet,
|
||||
SheetClose,
|
||||
SheetContent,
|
||||
@@ -24,7 +24,7 @@ export default function SheetSide() {
|
||||
{SHEET_SIDES.map((side) => (
|
||||
<Sheet key={side}>
|
||||
<SheetTrigger asChild>
|
||||
<Button type="outline">{side}</Button>
|
||||
<Button type="outline">From {side}</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent side={side}>
|
||||
<SheetHeader>
|
||||
|
||||
@@ -1,46 +1,36 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { Button } from 'ui'
|
||||
import TextConfirmModal from 'ui-patterns/Dialogs/TextConfirmModal'
|
||||
|
||||
const TextConfirmModalPrimary = () => {
|
||||
export default function TextConfirmDialogDemo() {
|
||||
const [visible, setVisible] = useState(false)
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
function onVisibleChange() {
|
||||
setVisible(!visible)
|
||||
}
|
||||
|
||||
function onSubmit() {
|
||||
setLoading(true)
|
||||
setTimeout(() => {
|
||||
setLoading(false)
|
||||
setVisible(false)
|
||||
toast('Updated project', { description: 'Friday, February 10, 2023 at 5:57 PM' })
|
||||
}, 3000)
|
||||
}
|
||||
const bucketName = 'profile-pictures'
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button type="default" onClick={onVisibleChange}>
|
||||
Open text confirm dialog
|
||||
<Button type="danger" onClick={() => setVisible(true)}>
|
||||
Show Text Confirm Dialog
|
||||
</Button>
|
||||
|
||||
<TextConfirmModal
|
||||
key="primary"
|
||||
confirmString="project name"
|
||||
visible={visible}
|
||||
title="Are you sure you want to delete?"
|
||||
size="small"
|
||||
onCancel={onVisibleChange}
|
||||
loading={loading}
|
||||
confirmPlaceholder='Type "project name" to confirm'
|
||||
onConfirm={onSubmit}
|
||||
/>
|
||||
variant="destructive"
|
||||
title="Delete bucket"
|
||||
confirmPlaceholder={bucketName}
|
||||
confirmString={bucketName}
|
||||
confirmLabel="Delete bucket"
|
||||
loading={false}
|
||||
onConfirm={() => setVisible(false)}
|
||||
onCancel={() => setVisible(false)}
|
||||
>
|
||||
<p className="text-sm">
|
||||
Your bucket <span className="font-medium text-foreground">{bucketName}</span> and all of
|
||||
its contents will be permanently deleted. This action cannot be undone.
|
||||
</p>
|
||||
</TextConfirmModal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default TextConfirmModalPrimary
|
||||
+2
-2
@@ -25,8 +25,8 @@ const TextConfirmModalWithCancelButton = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button type="default" onClick={onVisibleChange}>
|
||||
Open warning text confirm dialog
|
||||
<Button type="danger" onClick={onVisibleChange}>
|
||||
Show Text Confirm Dialog
|
||||
</Button>
|
||||
<TextConfirmModal
|
||||
key="withCancelButton"
|
||||
|
||||
@@ -26,8 +26,8 @@ const TextConfirmModalWithChildren = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button type="default" onClick={onVisibleChange}>
|
||||
Open warning text confirm dialog
|
||||
<Button type="danger" onClick={onVisibleChange}>
|
||||
Show Text Confirm Dialog
|
||||
</Button>
|
||||
<TextConfirmModal
|
||||
key="withChildren"
|
||||
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { Button } from 'ui'
|
||||
import TextConfirmModal from 'ui-patterns/Dialogs/TextConfirmModal'
|
||||
|
||||
const TextConfirmModalWithDestructiveAlert = () => {
|
||||
const [visible, setVisible] = useState(false)
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
function onVisibleChange() {
|
||||
setVisible(!visible)
|
||||
}
|
||||
|
||||
function onSubmit() {
|
||||
setLoading(true)
|
||||
setTimeout(() => {
|
||||
setLoading(false)
|
||||
setVisible(false)
|
||||
toast('Updated project', { description: 'Friday, February 10, 2023 at 5:57 PM' })
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button type="default" onClick={onVisibleChange}>
|
||||
Open warning text confirm dialog
|
||||
</Button>
|
||||
<TextConfirmModal
|
||||
key="withDestructiveAlert"
|
||||
confirmString="project name"
|
||||
visible={visible}
|
||||
variant="destructive"
|
||||
title="Are you sure you want to delete?"
|
||||
alert={{
|
||||
title: 'This is a warning',
|
||||
description: 'You are about to delete this item. This action cannot be undone.',
|
||||
}}
|
||||
onCancel={onVisibleChange}
|
||||
loading={loading}
|
||||
confirmPlaceholder='Type "project name" to confirm'
|
||||
onConfirm={onSubmit}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default TextConfirmModalWithDestructiveAlert
|
||||
@@ -1,50 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { Button } from 'ui'
|
||||
import TextConfirmModal from 'ui-patterns/Dialogs/TextConfirmModal'
|
||||
|
||||
const TextConfirmModalWithInfoAlert = () => {
|
||||
const [visible, setVisible] = useState(false)
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
function onVisibleChange() {
|
||||
setVisible(!visible)
|
||||
}
|
||||
|
||||
function onSubmit() {
|
||||
setLoading(true)
|
||||
setTimeout(() => {
|
||||
setLoading(false)
|
||||
setVisible(false)
|
||||
toast('Updated project', { description: 'Friday, February 10, 2023 at 5:57 PM' })
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button type="default" onClick={onVisibleChange}>
|
||||
Open warning text confirm dialog
|
||||
</Button>
|
||||
<TextConfirmModal
|
||||
key="withInfoAlert"
|
||||
confirmString="project name"
|
||||
visible={visible}
|
||||
variant="default"
|
||||
title="Are you sure you want to move?"
|
||||
alert={{
|
||||
title: 'This is a reminder banner',
|
||||
description: 'This should not be used for important CRUD events.',
|
||||
}}
|
||||
onCancel={onVisibleChange}
|
||||
loading={loading}
|
||||
confirmPlaceholder='Type "project name" to confirm'
|
||||
onConfirm={onSubmit}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default TextConfirmModalWithInfoAlert
|
||||
@@ -25,8 +25,8 @@ const TextConfirmModalWithSize = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button type="default" onClick={onVisibleChange}>
|
||||
Open warning text confirm dialog
|
||||
<Button type="danger" onClick={onVisibleChange}>
|
||||
Show Text Confirm Dialog
|
||||
</Button>
|
||||
<TextConfirmModal
|
||||
key="withSize"
|
||||
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { Button } from 'ui'
|
||||
import TextConfirmModal from 'ui-patterns/Dialogs/TextConfirmModal'
|
||||
|
||||
const TextConfirmModalWithWarningAlert = () => {
|
||||
const [visible, setVisible] = useState(false)
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
function onVisibleChange() {
|
||||
setVisible(!visible)
|
||||
}
|
||||
|
||||
function onSubmit() {
|
||||
setLoading(true)
|
||||
setTimeout(() => {
|
||||
setLoading(false)
|
||||
setVisible(false)
|
||||
toast('Updated project', {
|
||||
description: 'Friday, February 10, 2023 at 5:57 PM',
|
||||
})
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button type="default" onClick={onVisibleChange}>
|
||||
Open warning text confirm dialog
|
||||
</Button>
|
||||
<TextConfirmModal
|
||||
key="withWarningAlert"
|
||||
confirmString="project name"
|
||||
visible={visible}
|
||||
variant="warning"
|
||||
title="Are you sure you want to delete?"
|
||||
alert={{
|
||||
title: 'This is a warning',
|
||||
description: 'You are about to delete this item. This action cannot be undone.',
|
||||
}}
|
||||
onCancel={onVisibleChange}
|
||||
loading={loading}
|
||||
confirmPlaceholder='Type "project name" to confirm'
|
||||
onConfirm={onSubmit}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default TextConfirmModalWithWarningAlert
|
||||
@@ -43,6 +43,24 @@ export const examples: Registry = [
|
||||
registryDependencies: ['alert-dialog', 'button'],
|
||||
files: ['example/alert-dialog-demo.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'alert-dialog-close-only',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['alert-dialog', 'button'],
|
||||
files: ['example/alert-dialog-close-only.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'alert-dialog-destructive',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['alert-dialog', 'button'],
|
||||
files: ['example/alert-dialog-destructive.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'alert-dialog-warning',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['alert-dialog', 'button'],
|
||||
files: ['example/alert-dialog-warning.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'aspect-ratio-demo',
|
||||
type: 'components:example',
|
||||
@@ -731,6 +749,12 @@ export const examples: Registry = [
|
||||
registryDependencies: ['sheet'],
|
||||
files: ['example/sheet-demo.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'sheet-nonmodal',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['sheet'],
|
||||
files: ['example/sheet-nonmodal.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'sheet-side',
|
||||
type: 'components:example',
|
||||
@@ -1039,21 +1063,6 @@ export const examples: Registry = [
|
||||
type: 'components:example',
|
||||
files: ['example/text-confirm-dialog-demo.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'text-confirm-dialog-with-info-alert',
|
||||
type: 'components:example',
|
||||
files: ['example/text-confirm-dialog-with-info-alert.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'text-confirm-dialog-with-warning-alert',
|
||||
type: 'components:example',
|
||||
files: ['example/text-confirm-dialog-with-warning-alert.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'text-confirm-dialog-with-destructive-alert',
|
||||
type: 'components:example',
|
||||
files: ['example/text-confirm-dialog-with-destructive-alert.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'text-confirm-dialog-with-size',
|
||||
type: 'components:example',
|
||||
@@ -1360,24 +1369,9 @@ export const examples: Registry = [
|
||||
files: ['example/tree-view-multi-select.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'modal-demo',
|
||||
name: 'confirmation-modal-demo',
|
||||
type: 'components:example',
|
||||
files: ['example/modal-demo.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'modal-aligned-footer',
|
||||
type: 'components:example',
|
||||
files: ['example/modal-aligned-footer.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'modal-custom-footer',
|
||||
type: 'components:example',
|
||||
files: ['example/modal-custom-footer.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'modal-hide-footer',
|
||||
type: 'components:example',
|
||||
files: ['example/modal-hide-footer.tsx'],
|
||||
files: ['example/confirmation-modal-demo.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'assistant-chat-demo',
|
||||
|
||||
Reference in new issue
Block a user