chore(studio): better empty states for new users (#41044)

* box-plus icon

* design-system updates

* new project empty state

* org empty state

* better /organization empty state

* add missing dependency

* fix admonition

* remove prop

* fix badge

* use org slug

* clarify

* org note found cleanup
This commit is contained in:
Danny White
2025-12-04 20:27:47 +11:00
committed by GitHub
parent 84f9053a3f
commit 6a4814c2a4
10 changed files with 181 additions and 93 deletions
@@ -55,6 +55,7 @@ Follow these steps to add a new custom icon to the Supabase icon library.
- Exported at 24x24px with `viewBox="0 0 24 24"`
- Uses `stroke="currentColor"` for strokes (no hardcoded colors)
- Uses `stroke-width="1.5"` unless there is a good reason to deviate
- Uses `fill="none"` for fills (no hardcoded colors)
- Icon content is optically centered and around 18x18px within the 24x24 frame
- Any unnecessary elements like `<clipPath>`, `<defs>`, and `<g>` wrappers have been removed
@@ -5,36 +5,12 @@ description: Convey the absence of data and provide clear instruction for what t
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.
## 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.
<ComponentPreview name="empty-state-missing-route" peekCode wide />
## No data
There are two ways an empty state may be displayed in cases where there is no data:
- **Zero results**: no data after a search or filter
- **Initial state**: no data to begin with
### 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
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.
<ComponentPreview name="empty-state-zero-items-table" peekCode wide />
#### DataGrid
[DataGrid](../components/data-table) typically spans 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 projects registered users. Any instance with zero results should display a more prominent empty with a clear title, description, and supporting illustration.
<ComponentPreview name="empty-state-zero-items-data-grid" peekCode wide />
Other DataGrid instances include [Cron Jobs](https://supabase.com/dashboard/project/_/integrations/cron/jobs) and [Queues](https://supabase.com/dashboard/project/_/integrations/queues).
- **Zero results**: no data after a search or filter
### Initial state
@@ -56,6 +32,30 @@ 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
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
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.
<ComponentPreview name="empty-state-zero-items-table" peekCode wide />
#### DataGrid
[DataGrid](../components/data-table) typically spans 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 projects registered users. Any instance with zero results should display a more prominent empty with a clear title, description, and supporting illustration.
<ComponentPreview name="empty-state-zero-items-data-grid" peekCode wide />
Other DataGrid instances include [Cron Jobs](https://supabase.com/dashboard/project/_/integrations/cron/jobs) and [Queues](https://supabase.com/dashboard/project/_/integrations/queues).
## 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.
<ComponentPreview name="empty-state-missing-route" peekCode wide />
## 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.
@@ -1,3 +1,4 @@
import { BoxPlus } from 'icons'
import { Plus } from 'lucide-react'
import Link from 'next/link'
@@ -15,6 +16,7 @@ import {
TableHeader,
TableRow,
} from 'ui'
import { EmptyStatePresentational } from 'ui-patterns'
import { ShimmeringCard } from './ShimmeringCard'
export const Header = () => {
@@ -126,35 +128,29 @@ export const NoProjectsState = ({ slug }: { slug: string }) => {
const projectCreationEnabled = useIsFeatureEnabled('projects:create')
return (
<div className="col-span-4 space-y-4 rounded-lg border border-dashed p-6 text-center">
<div className="space-y-1">
<p>No projects</p>
<p className="text-sm text-foreground-light">Get started by creating a new project.</p>
</div>
<EmptyStatePresentational
icon={BoxPlus}
title="Create a project"
description="Launch a complete backend built on Postgres."
>
{projectCreationEnabled && (
<Button asChild icon={<Plus />}>
<Link href={`/new/${slug}`}>New Project</Link>
<Button size="tiny" type="default" asChild icon={<Plus />}>
<Link href={`/new/${slug}`}>New project</Link>
</Button>
)}
</div>
</EmptyStatePresentational>
)
}
export const NoOrganizationsState = () => {
export const NoOrganizationsState = ({}) => {
return (
<div className="col-span-4 space-y-4 rounded-lg border border-dashed border-muted p-6 text-center">
<div className="space-y-1">
<p>You are not part of any organizations yet</p>
<p className="text-sm text-foreground-light">
Create your first organization to get started with Supabase
</p>
</div>
<div>
<Button asChild icon={<Plus />}>
<Link href="/new">New organization</Link>
</Button>
</div>
</div>
<EmptyStatePresentational
title="Create an organization"
description="Manage your team and projects in one place."
>
<Button size="tiny" type="primary" asChild icon={<Plus />}>
<Link href="/new">New organization</Link>
</Button>
</EmptyStatePresentational>
)
}
@@ -1,4 +1,3 @@
import AlertError from 'components/ui/AlertError'
import { useOrganizationsQuery } from 'data/organizations/organizations-query'
import { Skeleton } from 'ui'
import { Admonition } from 'ui-patterns/admonition'
@@ -16,20 +15,26 @@ export const OrgNotFound = ({ slug }: { slug?: string }) => {
return (
<>
{slug !== '_' && (
<Admonition type="danger">
The selected organization does not exist or you don't have permission to access it.{' '}
{slug ? (
<Admonition
type="destructive"
title="Organization not found"
description={
<>
Contact the owner or administrator to create a new project in the <code>{slug}</code>{' '}
organization.
{slug ? (
<>
The organization <code className="text-code-inline">{slug}</code>{' '}
</>
) : (
<>This organization </>
)}
does not exist or you do not have permission to access to it. Contact the the owner if
you believe this is a mistake.
</>
) : (
<>Contact the owner or administrator to create a new project.</>
)}
</Admonition>
}
/>
)}
<h3 className="text-sm">Select an organization to create your new project in</h3>
<h3 className="text-sm">Select a different organization to create your new project in</h3>
<div className="grid gap-2 grid-cols-2">
{isOrganizationsLoading && (
@@ -40,7 +45,11 @@ export const OrgNotFound = ({ slug }: { slug?: string }) => {
</>
)}
{isOrganizationsError && (
<AlertError error={organizationsError} subject="Failed to load organizations" />
<Admonition
type="destructive"
title="Failed to load organizations"
description={organizationsError?.message}
/>
)}
{isOrganizationsSuccess &&
organizations?.map((org) => (
@@ -17,7 +17,7 @@ export function LastSignInWrapper({
{lastSignIn === type && (
<Badge
variant="success"
className="absolute -right-4 -top-3 rounded-full px-2 py-0.5 shadow z-10 bg-brand-400 bg-opacity-100 text-foreground pointer-events-none"
className="absolute -right-4 -top-3 shadow z-10 bg-brand-400 bg-opacity-100 text-foreground pointer-events-none"
>
Last used
</Badge>
+25 -32
View File
@@ -4,6 +4,7 @@ import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
import { useParams } from 'common'
import { NoOrganizationsState } from 'components/interfaces/Home/ProjectList/EmptyStates'
import { OrganizationCard } from 'components/interfaces/Organization/OrganizationCard'
import AppLayout from 'components/layouts/AppLayout/AppLayout'
import DefaultLayout from 'components/layouts/DefaultLayout'
@@ -15,14 +16,8 @@ import { useOrganizationsQuery } from 'data/organizations/organizations-query'
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
import { withAuth } from 'hooks/misc/withAuth'
import type { NextPageWithLayout } from 'types'
import {
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
Alert_Shadcn_,
Button,
CriticalIcon,
Skeleton,
} from 'ui'
import { Button, Skeleton } from 'ui'
import { Admonition } from 'ui-patterns/admonition'
import { Input } from 'ui-patterns/DataInputs/Input'
const OrganizationsPage: NextPageWithLayout = () => {
@@ -53,25 +48,21 @@ const OrganizationsPage: NextPageWithLayout = () => {
<ScaffoldContainer>
<ScaffoldSection isFullWidth className="flex flex-col gap-y-4">
{orgNotFound && (
<Alert_Shadcn_ variant="destructive">
<CriticalIcon />
<AlertTitle_Shadcn_>Organization not found</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
That organization (<code>{orgSlug}</code>) does not exist or you don't have access to
it.
</AlertDescription_Shadcn_>
<AlertDescription_Shadcn_ className="mt-3">
If you think this is an error, please reach out to the org owner to get access.
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
<Admonition
type="destructive"
title="Organization not found"
description={
<>
The organization <code className="text-code-inline">{orgSlug}</code> does not exist
or you do not have permission to access to it. Contact the the owner if you believe
this is a mistake.
</>
}
/>
)}
{organizations.length === 0 && orgNotFound && (
<p className="-mt-4">You don't have any organizations yet. Create one to get started.</p>
)}
<div className="flex items-center justify-between gap-x-2 md:gap-x-3">
{organizations.length > 0 && (
{organizations.length > 0 && (
<div className="flex items-center justify-between gap-x-2 md:gap-x-3">
<Input
size="tiny"
placeholder="Search for an organization"
@@ -80,14 +71,16 @@ const OrganizationsPage: NextPageWithLayout = () => {
value={search}
onChange={(event) => setSearch(event.target.value)}
/>
)}
{organizationCreationEnabled && (
<Button asChild icon={<Plus />} type="primary" className="w-min">
<Link href={`/new`}>New organization</Link>
</Button>
)}
</div>
{organizationCreationEnabled && (
<Button asChild icon={<Plus />} type="primary" className="w-min">
<Link href={`/new`}>New organization</Link>
</Button>
)}
</div>
)}
{isSuccess && organizations.length === 0 && !isError && <NoOrganizationsState />}
{search.length > 0 && filteredOrganizations.length === 0 && (
<NoSearchResults searchString={search} />
+10
View File
@@ -54,6 +54,16 @@ export const Index: Record<string, any> = [
svg: "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"1\">\n <path\n d=\"M5.24121 15.0674H12.7412M5.24121 15.0674V18.0674H12.7412V15.0674M5.24121 15.0674V12.0674H12.7412V15.0674M15 7.60547V4.60547C15 2.94861 13.6569 1.60547 12 1.60547C10.3431 1.60547 9 2.94861 9 4.60547V7.60547M5.20898 9.60547L5.20898 19.1055C5.20898 20.21 6.10441 21.1055 7.20898 21.1055H16.709C17.8136 21.1055 18.709 20.21 18.709 19.1055V9.60547C18.709 8.5009 17.8136 7.60547 16.709 7.60547L7.20899 7.60547C6.10442 7.60547 5.20898 8.5009 5.20898 9.60547Z\" />\n</svg>",
jsx: "import { Auth } from \"icons\"\n <Auth/>\n "
},
{
name: "box-plus",
componentName: "BoxPlus",
deprecated: false,
raw: "import createSupabaseIcon from '../createSupabaseIcon';\n\n/**\n * @component @name BoxPlus\n * @description Supabase SVG icon component, renders SVG Element with children.\n *\n * @preview ![img](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMjAuNzMxNSA3LjAwMTE5QzIwLjU1NiA2LjY5NzU0IDIwLjMwMzcgNi40NDUzOSAyMCA2LjI3MDAyTDEzIDIuMjcwMDJDMTIuNjk2IDIuMDk0NDkgMTIuMzUxMSAyLjAwMjA4IDEyIDIuMDAyMDhDMTEuNjQ4OSAyLjAwMjA4IDExLjMwNCAyLjA5NDQ5IDExIDIuMjcwMDJMNCA2LjI3MDAyQzMuNjk2MjYgNi40NDUzOSAzLjQ0Mzk4IDYuNjk3NTQgMy4yNjg0NiA3LjAwMTE5QzMuMDkyOTQgNy4zMDQ4MyAzLjAwMDM2IDcuNjQ5MyAzIDguMDAwMDJWMTZDMy4wMDAzNiAxNi4zNTA4IDMuMDkyOTQgMTYuNjk1MiAzLjI2ODQ2IDE2Ljk5ODlDMy40NDM5OCAxNy4zMDI1IDMuNjk2MjYgMTcuNTU0NyA0IDE3LjczTDExIDIxLjczQzExLjMwNCAyMS45MDU2IDExLjY0ODkgMjEuOTk4IDEyIDIxLjk5OCIgc3Ryb2tlPSIjMDAwIiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjogI2ZmZjsgYm9yZGVyLXJhZGl1czogMnB4IiBzdHJva2Utd2lkdGg9IjEuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxwYXRoIGQ9Ik0zLjMgN0wxMiAxMkwyMC43IDciIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2Utd2lkdGg9IjEuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxwYXRoIGQ9Ik0xMiAyMlYxMiIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPHBhdGggZD0iTTE5IDE0VjIwIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8cGF0aCBkPSJNMTYgMTdIMjIiIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2Utd2lkdGg9IjEuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cjwvc3ZnPgo=)\n *\n * @param {Object} props - Supabase icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst BoxPlus = createSupabaseIcon('BoxPlus', [\n [\n 'path',\n {\n d: 'M20.7315 7.00119C20.556 6.69754 20.3037 6.44539 20 6.27002L13 2.27002C12.696 2.09449 12.3511 2.00208 12 2.00208C11.6489 2.00208 11.304 2.09449 11 2.27002L4 6.27002C3.69626 6.44539 3.44398 6.69754 3.26846 7.00119C3.09294 7.30483 3.00036 7.6493 3 8.00002V16C3.00036 16.3508 3.09294 16.6952 3.26846 16.9989C3.44398 17.3025 3.69626 17.5547 4 17.73L11 21.73C11.304 21.9056 11.6489 21.998 12 21.998',\n stroke: 'currentColor',\n 'stroke-width': '1.5',\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n key: '1g22fp',\n },\n ],\n [\n 'path',\n {\n d: 'M3.3 7L12 12L20.7 7',\n stroke: 'currentColor',\n 'stroke-width': '1.5',\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n key: '1v4gtz',\n },\n ],\n [\n 'path',\n {\n d: 'M12 22V12',\n stroke: 'currentColor',\n 'stroke-width': '1.5',\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n key: 'hoijqc',\n },\n ],\n [\n 'path',\n {\n d: 'M19 14V20',\n stroke: 'currentColor',\n 'stroke-width': '1.5',\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n key: '7ts4zv',\n },\n ],\n [\n 'path',\n {\n d: 'M16 17H22',\n stroke: 'currentColor',\n 'stroke-width': '1.5',\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n key: 'ir88vv',\n },\n ],\n]);\n\nexport default BoxPlus;\n",
component: React.lazy(() => import('icons/src/icons/box-plus')),
import: "import { BoxPlus } from 'icons'",
svg: "<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<path d=\"M20.7315 7.00119C20.556 6.69754 20.3037 6.44539 20 6.27002L13 2.27002C12.696 2.09449 12.3511 2.00208 12 2.00208C11.6489 2.00208 11.304 2.09449 11 2.27002L4 6.27002C3.69626 6.44539 3.44398 6.69754 3.26846 7.00119C3.09294 7.30483 3.00036 7.6493 3 8.00002V16C3.00036 16.3508 3.09294 16.6952 3.26846 16.9989C3.44398 17.3025 3.69626 17.5547 4 17.73L11 21.73C11.304 21.9056 11.6489 21.998 12 21.998\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n<path d=\"M3.3 7L12 12L20.7 7\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n<path d=\"M12 22V12\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n<path d=\"M19 14V20\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n<path d=\"M16 17H22\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n</svg>\n",
jsx: "import { BoxPlus } from \"icons\"\n <BoxPlus/>\n "
},
{
name: "bucket-plus",
componentName: "BucketPlus",
+71
View File
@@ -0,0 +1,71 @@
import createSupabaseIcon from '../createSupabaseIcon';
/**
* @component @name BoxPlus
* @description Supabase SVG icon component, renders SVG Element with children.
*
* @preview ![img](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMjAuNzMxNSA3LjAwMTE5QzIwLjU1NiA2LjY5NzU0IDIwLjMwMzcgNi40NDUzOSAyMCA2LjI3MDAyTDEzIDIuMjcwMDJDMTIuNjk2IDIuMDk0NDkgMTIuMzUxMSAyLjAwMjA4IDEyIDIuMDAyMDhDMTEuNjQ4OSAyLjAwMjA4IDExLjMwNCAyLjA5NDQ5IDExIDIuMjcwMDJMNCA2LjI3MDAyQzMuNjk2MjYgNi40NDUzOSAzLjQ0Mzk4IDYuNjk3NTQgMy4yNjg0NiA3LjAwMTE5QzMuMDkyOTQgNy4zMDQ4MyAzLjAwMDM2IDcuNjQ5MyAzIDguMDAwMDJWMTZDMy4wMDAzNiAxNi4zNTA4IDMuMDkyOTQgMTYuNjk1MiAzLjI2ODQ2IDE2Ljk5ODlDMy40NDM5OCAxNy4zMDI1IDMuNjk2MjYgMTcuNTU0NyA0IDE3LjczTDExIDIxLjczQzExLjMwNCAyMS45MDU2IDExLjY0ODkgMjEuOTk4IDEyIDIxLjk5OCIgc3Ryb2tlPSIjMDAwIiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjogI2ZmZjsgYm9yZGVyLXJhZGl1czogMnB4IiBzdHJva2Utd2lkdGg9IjEuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxwYXRoIGQ9Ik0zLjMgN0wxMiAxMkwyMC43IDciIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2Utd2lkdGg9IjEuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxwYXRoIGQ9Ik0xMiAyMlYxMiIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPHBhdGggZD0iTTE5IDE0VjIwIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8cGF0aCBkPSJNMTYgMTdIMjIiIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2Utd2lkdGg9IjEuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cjwvc3ZnPgo=)
*
* @param {Object} props - Supabase icons props and any valid SVG attribute
* @returns {JSX.Element} JSX Element
*
*/
const BoxPlus = createSupabaseIcon('BoxPlus', [
[
'path',
{
d: 'M20.7315 7.00119C20.556 6.69754 20.3037 6.44539 20 6.27002L13 2.27002C12.696 2.09449 12.3511 2.00208 12 2.00208C11.6489 2.00208 11.304 2.09449 11 2.27002L4 6.27002C3.69626 6.44539 3.44398 6.69754 3.26846 7.00119C3.09294 7.30483 3.00036 7.6493 3 8.00002V16C3.00036 16.3508 3.09294 16.6952 3.26846 16.9989C3.44398 17.3025 3.69626 17.5547 4 17.73L11 21.73C11.304 21.9056 11.6489 21.998 12 21.998',
stroke: 'currentColor',
'stroke-width': '1.5',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
key: '1g22fp',
},
],
[
'path',
{
d: 'M3.3 7L12 12L20.7 7',
stroke: 'currentColor',
'stroke-width': '1.5',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
key: '1v4gtz',
},
],
[
'path',
{
d: 'M12 22V12',
stroke: 'currentColor',
'stroke-width': '1.5',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
key: 'hoijqc',
},
],
[
'path',
{
d: 'M19 14V20',
stroke: 'currentColor',
'stroke-width': '1.5',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
key: '7ts4zv',
},
],
[
'path',
{
d: 'M16 17H22',
stroke: 'currentColor',
'stroke-width': '1.5',
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
key: 'ir88vv',
},
],
]);
export default BoxPlus;
+1
View File
@@ -3,6 +3,7 @@ export { default as ExampleTemplate } from './_example-template';
export { default as AnalyticsBucket } from './analytics-bucket';
export { default as ApiDocs } from './api-docs';
export { default as Auth } from './auth';
export { default as BoxPlus } from './box-plus';
export { default as BucketPlus } from './bucket-plus';
export { default as Database } from './database';
export { default as Datadog } from './datadog';
@@ -0,0 +1,7 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20.7315 7.00119C20.556 6.69754 20.3037 6.44539 20 6.27002L13 2.27002C12.696 2.09449 12.3511 2.00208 12 2.00208C11.6489 2.00208 11.304 2.09449 11 2.27002L4 6.27002C3.69626 6.44539 3.44398 6.69754 3.26846 7.00119C3.09294 7.30483 3.00036 7.6493 3 8.00002V16C3.00036 16.3508 3.09294 16.6952 3.26846 16.9989C3.44398 17.3025 3.69626 17.5547 4 17.73L11 21.73C11.304 21.9056 11.6489 21.998 12 21.998" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3.3 7L12 12L20.7 7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 22V12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M19 14V20" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M16 17H22" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB