mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
This PR removes all `paths` in `tsconfig.json` for all apps and packages. They were added previosly because some of the components had a `_Shadcn` suffix because of an ongoing migration. How that the migration is done, the paths can be removed. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Standardized shared UI component, utility, and icon imports across design-system examples and application screens. * Simplified shared component access and project configuration. * Added shared access to anchor-link helpers and animation styles. * **Compatibility** * Updated component exports and imports without changing existing behavior. * No changes to user-facing workflows, screens, or functionality. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
460 lines
11 KiB
TypeScript
460 lines
11 KiB
TypeScript
import { cva } from 'class-variance-authority'
|
|
import React from 'react'
|
|
import { cn, FormDescription, FormLabel, FormMessage, Label, SIZE } from 'ui'
|
|
|
|
type Props = {
|
|
align?: 'left' | 'right'
|
|
description?: string | React.ReactNode
|
|
error?: string | React.ReactNode
|
|
label?: string | React.ReactNode
|
|
labelOptional?: string | React.ReactNode
|
|
layout?: 'horizontal' | 'vertical' | 'flex' | 'flex-row-reverse'
|
|
size?: 'tiny' | 'small' | 'medium' | 'large' | 'xlarge'
|
|
beforeLabel?: string | React.ReactNode
|
|
afterLabel?: string | React.ReactNode
|
|
nonBoxInput?: boolean
|
|
labelLayout?: 'horizontal' | 'vertical'
|
|
isReactForm?: boolean
|
|
hideMessage?: boolean
|
|
name?: string
|
|
/**
|
|
* For layout="horizontal", switches the label/content split to react to the nearest
|
|
* `@container` ancestor's width instead of the viewport width. Use when the layout sits
|
|
* inside a narrower column (e.g. next to a sidebar) so it stacks to a single column as
|
|
* soon as that column gets tight, rather than waiting for a viewport breakpoint that may
|
|
* never reflect the column's actual available space.
|
|
*/
|
|
isContainerResponsive?: boolean
|
|
}
|
|
|
|
const ContainerVariants = cva('relative grid gap-10', {
|
|
variants: {
|
|
size: {
|
|
tiny: 'text-xs',
|
|
small: 'text-base md:text-sm leading-4',
|
|
medium: 'text-base md:text-sm',
|
|
large: 'text-base',
|
|
xlarge: 'text-base',
|
|
},
|
|
align: { left: '', right: '' },
|
|
responsive: {
|
|
true: '',
|
|
false: '',
|
|
},
|
|
layout: {
|
|
horizontal: 'flex flex-col gap-2',
|
|
vertical: 'flex flex-col gap-2',
|
|
flex: 'flex flex-row gap-3',
|
|
'flex-row-reverse':
|
|
'flex flex-col-reverse gap-2 md:gap-6 md:flex-row-reverse md:justify-between',
|
|
},
|
|
flex: {
|
|
true: '',
|
|
false: '',
|
|
},
|
|
},
|
|
compoundVariants: [
|
|
{
|
|
layout: 'flex',
|
|
align: 'right',
|
|
className: 'justify-between',
|
|
},
|
|
{
|
|
layout: 'flex-row-reverse',
|
|
align: 'right',
|
|
className: 'justify-between',
|
|
},
|
|
{
|
|
layout: 'horizontal',
|
|
responsive: false,
|
|
className: 'md:grid md:grid-cols-12',
|
|
},
|
|
{
|
|
layout: 'horizontal',
|
|
responsive: true,
|
|
className: '@xl:grid @xl:grid-cols-12',
|
|
},
|
|
],
|
|
defaultVariants: {
|
|
responsive: false,
|
|
},
|
|
})
|
|
|
|
const LabelContainerVariants = cva('transition-all duration-500 ease-in-out', {
|
|
variants: {
|
|
flex: {
|
|
true: '',
|
|
false: '',
|
|
},
|
|
align: {
|
|
left: '',
|
|
right: '',
|
|
},
|
|
layout: {
|
|
horizontal: 'flex flex-col gap-2 col-span-4',
|
|
vertical: 'flex flex-row gap-2 justify-between',
|
|
flex: 'flex flex-col gap-0 min-w-0',
|
|
'flex-row-reverse': 'flex flex-col min-w-0 grow',
|
|
},
|
|
labelLayout: {
|
|
horizontal: '',
|
|
vertical: '',
|
|
'': '',
|
|
},
|
|
},
|
|
compoundVariants: [
|
|
{
|
|
flex: true,
|
|
align: 'left',
|
|
className: 'order-2',
|
|
},
|
|
{
|
|
flex: true,
|
|
align: 'right',
|
|
className: 'order-1',
|
|
},
|
|
{
|
|
layout: ['vertical', 'flex'],
|
|
labelLayout: undefined,
|
|
flex: false,
|
|
className: 'flex flex-row gap-2 justify-between',
|
|
},
|
|
{
|
|
layout: 'horizontal',
|
|
className: 'flex flex-col gap-2',
|
|
},
|
|
],
|
|
defaultVariants: {},
|
|
})
|
|
|
|
const DataContainerVariants = cva('transition-all duration-500 ease-in-out', {
|
|
variants: {
|
|
flex: {
|
|
true: '',
|
|
false: '',
|
|
},
|
|
align: {
|
|
left: 'order-1',
|
|
right: 'order-2',
|
|
},
|
|
layout: {
|
|
horizontal: '',
|
|
vertical: '',
|
|
flex: '',
|
|
'flex-row-reverse': '',
|
|
},
|
|
},
|
|
compoundVariants: [
|
|
{
|
|
flex: true,
|
|
align: 'left',
|
|
className: 'order-1',
|
|
},
|
|
{
|
|
flex: true,
|
|
align: 'right',
|
|
className: 'order-2',
|
|
},
|
|
{
|
|
layout: ['vertical', 'flex'],
|
|
className: 'col-span-12',
|
|
},
|
|
{
|
|
layout: 'horizontal',
|
|
align: 'left',
|
|
className: 'col-span-8',
|
|
},
|
|
{
|
|
layout: 'horizontal',
|
|
align: 'right',
|
|
className: 'text-right',
|
|
},
|
|
],
|
|
defaultVariants: {},
|
|
})
|
|
|
|
const DescriptionVariants = cva('text-foreground-lighter leading-normal', {
|
|
variants: {
|
|
size: {
|
|
...SIZE.text,
|
|
},
|
|
layout: {
|
|
vertical: 'mt-2',
|
|
horizontal: 'mt-2',
|
|
flex: '',
|
|
'flex-row-reverse': '',
|
|
},
|
|
},
|
|
defaultVariants: {},
|
|
})
|
|
|
|
const LabelBeforeVariants = cva('text-foreground-muted', {
|
|
variants: {
|
|
size: {
|
|
...SIZE.text,
|
|
},
|
|
},
|
|
defaultVariants: {},
|
|
})
|
|
|
|
const LabelAfterVariants = cva('text-foreground-muted', {
|
|
variants: {
|
|
size: {
|
|
...SIZE.text,
|
|
},
|
|
},
|
|
defaultVariants: {},
|
|
})
|
|
|
|
const LabelOptionalVariants = cva('text-foreground-muted', {
|
|
variants: {
|
|
size: {
|
|
...SIZE.text,
|
|
},
|
|
},
|
|
defaultVariants: {},
|
|
})
|
|
|
|
const FlexContainer = cva('', {
|
|
variants: {
|
|
flex: {
|
|
true: '',
|
|
false: '',
|
|
},
|
|
align: {
|
|
left: '',
|
|
right: '',
|
|
},
|
|
layout: {
|
|
horizontal: '',
|
|
vertical: '',
|
|
flex: '',
|
|
'flex-row-reverse': '',
|
|
},
|
|
},
|
|
compoundVariants: [
|
|
{
|
|
flex: true,
|
|
align: 'left',
|
|
className: '',
|
|
},
|
|
{
|
|
flex: true,
|
|
align: 'right',
|
|
className: 'order-last',
|
|
},
|
|
{
|
|
layout: 'flex-row-reverse',
|
|
className:
|
|
'flex flex-col justify-center items-start md:items-end shrink-0 md:w-1/2 xl:w-2/5 [&>div]:md:w-full',
|
|
},
|
|
],
|
|
})
|
|
|
|
const NonBoxInputContainer = cva('', {
|
|
variants: {
|
|
nonBoxInput: {
|
|
true: '',
|
|
false: '',
|
|
},
|
|
label: {
|
|
true: '',
|
|
false: '',
|
|
},
|
|
layout: {
|
|
vertical: '',
|
|
horizontal: '',
|
|
'flex-row-reverse': '',
|
|
},
|
|
},
|
|
compoundVariants: [
|
|
{
|
|
nonBoxInput: true,
|
|
label: true,
|
|
layout: 'vertical',
|
|
className: 'my-3',
|
|
},
|
|
{
|
|
nonBoxInput: true,
|
|
label: true,
|
|
layout: 'horizontal',
|
|
className: 'my-3 md:mt-0 mb-3',
|
|
},
|
|
],
|
|
defaultVariants: {},
|
|
})
|
|
|
|
export const FormLayout = React.forwardRef<
|
|
HTMLDivElement,
|
|
React.ComponentPropsWithoutRef<'div'> & Props
|
|
>(
|
|
(
|
|
{
|
|
align = 'left',
|
|
className,
|
|
description,
|
|
id,
|
|
label,
|
|
labelOptional,
|
|
layout = 'vertical',
|
|
style,
|
|
labelLayout,
|
|
size = 'medium',
|
|
beforeLabel,
|
|
afterLabel,
|
|
nonBoxInput = !label,
|
|
hideMessage = false,
|
|
isReactForm,
|
|
error,
|
|
isContainerResponsive = false,
|
|
...props
|
|
},
|
|
ref
|
|
) => {
|
|
const flex = layout === 'flex' || layout === 'flex-row-reverse'
|
|
const hasLabel = Boolean(label || beforeLabel || afterLabel)
|
|
const renderError =
|
|
isReactForm && !hideMessage ? (
|
|
<FormMessage
|
|
className={cn(
|
|
'mt-2 transition-all duration-300 ease-in-out',
|
|
layout === 'flex-row-reverse' && 'mt-0'
|
|
)}
|
|
data-formlayout-id="message"
|
|
/>
|
|
) : error && !hideMessage ? (
|
|
<p className={cn('mt-2 text-sm text-destructive', layout === 'flex-row-reverse' && 'mt-0')}>
|
|
{error}
|
|
</p>
|
|
) : null
|
|
|
|
const renderDescription =
|
|
description && isReactForm ? (
|
|
<FormDescription
|
|
className={cn(DescriptionVariants({ size, layout }))}
|
|
data-formlayout-id={'description'}
|
|
id={`${id}-description`}
|
|
>
|
|
{description}
|
|
</FormDescription>
|
|
) : description ? (
|
|
// Rendered as a div rather than a p as descriptions can be arbitrary JSX
|
|
// which may contain block-level elements (invalid HTML inside a p)
|
|
<div
|
|
className={cn(DescriptionVariants({ size, layout }))}
|
|
data-formlayout-id={'description'}
|
|
>
|
|
{description}
|
|
</div>
|
|
) : null
|
|
|
|
const LabelContents = () => (
|
|
<>
|
|
{beforeLabel && (
|
|
<span
|
|
className={cn(LabelBeforeVariants({ size }))}
|
|
id={id + '-before'}
|
|
data-formlayout-id={'beforeLabel'}
|
|
>
|
|
<span>{beforeLabel}</span>
|
|
</span>
|
|
)}
|
|
<span>{label}</span>
|
|
{afterLabel && (
|
|
<span
|
|
className={cn(LabelAfterVariants({ size }))}
|
|
id={id + '-after'}
|
|
data-formlayout-id={'afterLabel'}
|
|
>
|
|
{afterLabel}
|
|
</span>
|
|
)}
|
|
</>
|
|
)
|
|
|
|
return (
|
|
<div
|
|
ref={ref}
|
|
{...props}
|
|
className={cn(
|
|
ContainerVariants({ size, flex, align, layout, responsive: isContainerResponsive }),
|
|
className
|
|
)}
|
|
>
|
|
{flex && (
|
|
<div className={cn(FlexContainer({ flex, align, layout }))}>
|
|
{props.children}
|
|
{layout === 'flex-row-reverse' && renderError}
|
|
</div>
|
|
)}
|
|
{hasLabel || labelOptional || layout === 'horizontal' ? (
|
|
<>
|
|
<div
|
|
className={cn(LabelContainerVariants({ align, labelLayout, flex, layout }))}
|
|
data-formlayout-id={'labelContainer'}
|
|
>
|
|
{hasLabel && isReactForm ? (
|
|
<FormLabel
|
|
className="text-foreground flex gap-2 items-center wrap-break-word"
|
|
data-formlayout-id="formLabel"
|
|
htmlFor={id}
|
|
>
|
|
<LabelContents />
|
|
</FormLabel>
|
|
) : (
|
|
<Label
|
|
className="text-foreground flex gap-2 items-center wrap-break-word leading-normal"
|
|
data-formlayout-id="label"
|
|
htmlFor={props.name || id}
|
|
>
|
|
<LabelContents />
|
|
</Label>
|
|
)}
|
|
{labelOptional && (
|
|
<span
|
|
className={cn(LabelOptionalVariants({ size }))}
|
|
id={id + '-optional'}
|
|
data-formlayout-id={'labelOptional'}
|
|
>
|
|
{labelOptional}
|
|
</span>
|
|
)}
|
|
{flex && (
|
|
<>
|
|
{renderDescription}
|
|
{layout !== 'flex-row-reverse' && renderError}
|
|
</>
|
|
)}
|
|
</div>
|
|
</>
|
|
) : null}
|
|
{!flex && (
|
|
<div
|
|
className={cn(DataContainerVariants({ align, layout }))}
|
|
style={style}
|
|
data-formlayout-id={'dataContainer'}
|
|
>
|
|
<>
|
|
<div
|
|
className={cn(
|
|
NonBoxInputContainer({
|
|
nonBoxInput,
|
|
// @ts-expect-error
|
|
label,
|
|
layout,
|
|
})
|
|
)}
|
|
data-formlayout-id={'nonBoxInputContainer'}
|
|
>
|
|
{props.children}
|
|
</div>
|
|
{renderError}
|
|
{renderDescription}
|
|
</>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
)
|