@spunto/design-system 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -3
- package/package.json +1 -1
- package/src/components/auth/auth-brand-panel.tsx +163 -0
- package/src/components/auth/auth-form.tsx +263 -0
- package/src/components/auth/auth-layout.tsx +79 -0
- package/src/components/auth/auth-separator.tsx +24 -0
- package/src/components/auth/index.ts +38 -0
- package/src/components/auth/oauth-button.tsx +81 -0
- package/src/components/auth/oauth-icons.tsx +53 -0
- package/src/components/auth/types.ts +29 -0
- package/src/components/command-menu.tsx +630 -0
- package/src/components/command-palette.tsx +7 -50
- package/src/components/command-shared.tsx +59 -0
- package/src/components/sheet.tsx +173 -0
- package/src/components/table.tsx +98 -0
- package/src/index.ts +87 -0
|
@@ -15,6 +15,7 @@ import { Dialog } from "@base-ui/react/dialog"
|
|
|
15
15
|
import { SearchIcon } from "lucide-react"
|
|
16
16
|
|
|
17
17
|
import { cn } from "../utils"
|
|
18
|
+
import { highlightMatch, type CommandFilter, type CommandFilterItem } from "./command-shared"
|
|
18
19
|
import { Kbd } from "./kbd"
|
|
19
20
|
import { Skeleton } from "./skeleton"
|
|
20
21
|
import { Spinner } from "./spinner"
|
|
@@ -41,17 +42,13 @@ import { useOverlayContainer } from "./spunto-provider"
|
|
|
41
42
|
// already-filtered items in.
|
|
42
43
|
// ---------------------------------------------------------------------------
|
|
43
44
|
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
keywords: string[]
|
|
50
|
-
/** The item's `value`, when it has one. */
|
|
51
|
-
value?: string
|
|
52
|
-
}
|
|
45
|
+
/**
|
|
46
|
+
* What the filter gets to look at for one item. Shared with `CommandMenu` — the
|
|
47
|
+
* two components filter identically, only their shell differs.
|
|
48
|
+
*/
|
|
49
|
+
export type CommandPaletteFilterItem = CommandFilterItem
|
|
53
50
|
|
|
54
|
-
export type CommandPaletteFilter =
|
|
51
|
+
export type CommandPaletteFilter = CommandFilter
|
|
55
52
|
|
|
56
53
|
interface PaletteContextValue {
|
|
57
54
|
query: string
|
|
@@ -107,46 +104,6 @@ function comboMatches(event: KeyboardEvent, combo: Combo): boolean {
|
|
|
107
104
|
return true
|
|
108
105
|
}
|
|
109
106
|
|
|
110
|
-
// --- match highlighting -----------------------------------------------------
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Folds a string to a comparable form (lowercase, no diacritics) while keeping,
|
|
114
|
-
* for every folded character, the index it came from — so a match found on the
|
|
115
|
-
* folded text can be highlighted on the original one ("déploiement" matched by
|
|
116
|
-
* "deploi" highlights the right six characters, accent included).
|
|
117
|
-
*/
|
|
118
|
-
function fold(text: string): { folded: string; map: number[] } {
|
|
119
|
-
let folded = ""
|
|
120
|
-
const map: number[] = []
|
|
121
|
-
for (let i = 0; i < text.length; i++) {
|
|
122
|
-
const chunk = text[i].normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase()
|
|
123
|
-
for (const char of chunk) {
|
|
124
|
-
folded += char
|
|
125
|
-
map.push(i)
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return { folded, map }
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/** Wraps the matched slice of `text` in a `<mark>`. Returns the plain text when there's no match. */
|
|
132
|
-
function highlightMatch(text: string, query: string): ReactNode {
|
|
133
|
-
if (!query.trim()) return text
|
|
134
|
-
const haystack = fold(text)
|
|
135
|
-
const needle = fold(query.trim()).folded
|
|
136
|
-
if (!needle) return text
|
|
137
|
-
const at = haystack.folded.indexOf(needle)
|
|
138
|
-
if (at === -1) return text
|
|
139
|
-
const start = haystack.map[at]
|
|
140
|
-
const end = haystack.map[at + needle.length - 1] + 1
|
|
141
|
-
return (
|
|
142
|
-
<>
|
|
143
|
-
{text.slice(0, start)}
|
|
144
|
-
<mark className="rounded-xs bg-primary/15 text-inherit">{text.slice(start, end)}</mark>
|
|
145
|
-
{text.slice(end)}
|
|
146
|
-
</>
|
|
147
|
-
)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
107
|
// --- root -------------------------------------------------------------------
|
|
151
108
|
|
|
152
109
|
export interface CommandPaletteProps {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { ReactNode } from "react"
|
|
2
|
+
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// What `CommandPalette` (modal, ⌘K) and `CommandMenu` (inline, anchored) share:
|
|
5
|
+
// how an item describes itself to the filter, and how a match is highlighted.
|
|
6
|
+
// The two components differ in their shell — a focus-trapping Dialog vs. a
|
|
7
|
+
// Popover that never takes focus — not in what "matching" means, so a filter
|
|
8
|
+
// written for one works verbatim on the other.
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
/** What the filter gets to look at for one item. */
|
|
12
|
+
export interface CommandFilterItem {
|
|
13
|
+
/** `label` prop, or the item's children when they're a plain string. */
|
|
14
|
+
label: string
|
|
15
|
+
/** Extra terms that match but are never displayed. */
|
|
16
|
+
keywords: string[]
|
|
17
|
+
/** The item's `value`, when it has one. */
|
|
18
|
+
value?: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type CommandFilter = (item: CommandFilterItem, query: string) => boolean
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Folds a string to a comparable form (lowercase, no diacritics) while keeping,
|
|
25
|
+
* for every folded character, the index it came from — so a match found on the
|
|
26
|
+
* folded text can be highlighted on the original one ("déploiement" matched by
|
|
27
|
+
* "deploi" highlights the right six characters, accent included).
|
|
28
|
+
*/
|
|
29
|
+
export function fold(text: string): { folded: string; map: number[] } {
|
|
30
|
+
let folded = ""
|
|
31
|
+
const map: number[] = []
|
|
32
|
+
for (let i = 0; i < text.length; i++) {
|
|
33
|
+
const chunk = text[i].normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase()
|
|
34
|
+
for (const char of chunk) {
|
|
35
|
+
folded += char
|
|
36
|
+
map.push(i)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return { folded, map }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Wraps the matched slice of `text` in a `<mark>`. Returns the plain text when there's no match. */
|
|
43
|
+
export function highlightMatch(text: string, query: string): ReactNode {
|
|
44
|
+
if (!query.trim()) return text
|
|
45
|
+
const haystack = fold(text)
|
|
46
|
+
const needle = fold(query.trim()).folded
|
|
47
|
+
if (!needle) return text
|
|
48
|
+
const at = haystack.folded.indexOf(needle)
|
|
49
|
+
if (at === -1) return text
|
|
50
|
+
const start = haystack.map[at]
|
|
51
|
+
const end = haystack.map[at + needle.length - 1] + 1
|
|
52
|
+
return (
|
|
53
|
+
<>
|
|
54
|
+
{text.slice(0, start)}
|
|
55
|
+
<mark className="rounded-xs bg-primary/15 text-inherit">{text.slice(start, end)}</mark>
|
|
56
|
+
{text.slice(end)}
|
|
57
|
+
</>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import type { ComponentProps } from "react"
|
|
4
|
+
import { Dialog as SheetPrimitive } from "@base-ui/react/dialog"
|
|
5
|
+
|
|
6
|
+
import { cn } from "../utils"
|
|
7
|
+
import { useOverlayContainer } from "./spunto-provider"
|
|
8
|
+
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// A sheet is a Dialog that enters from an edge. Same primitive (`@base-ui/react/
|
|
11
|
+
// dialog` — focus trap, Escape, backdrop, focus restore), same portal target as
|
|
12
|
+
// every other overlay here (the `SpuntoProvider` container), only the geometry
|
|
13
|
+
// changes.
|
|
14
|
+
//
|
|
15
|
+
// Position and size are two separate props on purpose. `side` decides which edge
|
|
16
|
+
// the panel is glued to; `size` decides how far it comes in. Both resolve to
|
|
17
|
+
// plain, unprefixed utilities computed in JS — *not* `data-[side=right]:`-style
|
|
18
|
+
// selectors — so a `className` on `SheetContent` still wins on specificity, and
|
|
19
|
+
// an app can go from a 32rem panel to full-bleed with `size="full"` or with its
|
|
20
|
+
// own `sm:max-w-*` without reaching for `!important`.
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
export type SheetSide = "right" | "left" | "top" | "bottom"
|
|
24
|
+
|
|
25
|
+
/** How far the panel comes in. Widths for left/right, heights for top/bottom. */
|
|
26
|
+
export type SheetSize = "sm" | "md" | "lg" | "xl" | "full"
|
|
27
|
+
|
|
28
|
+
const SIDE_CLASSES: Record<SheetSide, string> = {
|
|
29
|
+
right:
|
|
30
|
+
"inset-y-0 right-0 h-full w-full border-l data-ending-style:translate-x-full data-starting-style:translate-x-full",
|
|
31
|
+
left:
|
|
32
|
+
"inset-y-0 left-0 h-full w-full border-r data-ending-style:-translate-x-full data-starting-style:-translate-x-full",
|
|
33
|
+
top:
|
|
34
|
+
"inset-x-0 top-0 w-full border-b data-ending-style:-translate-y-full data-starting-style:-translate-y-full",
|
|
35
|
+
bottom:
|
|
36
|
+
"inset-x-0 bottom-0 w-full border-t data-ending-style:translate-y-full data-starting-style:translate-y-full",
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Below `sm` a side panel always takes the full width — a 24rem panel on a phone
|
|
40
|
+
// is a modal with a useless 3rem gutter.
|
|
41
|
+
const HORIZONTAL_SIZE_CLASSES: Record<SheetSize, string> = {
|
|
42
|
+
sm: "sm:max-w-sm",
|
|
43
|
+
md: "sm:max-w-lg",
|
|
44
|
+
lg: "sm:max-w-3xl",
|
|
45
|
+
xl: "sm:max-w-5xl",
|
|
46
|
+
full: "sm:max-w-none",
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const VERTICAL_SIZE_CLASSES: Record<SheetSize, string> = {
|
|
50
|
+
sm: "h-auto max-h-[16rem]",
|
|
51
|
+
md: "h-auto max-h-[24rem]",
|
|
52
|
+
lg: "h-auto max-h-[32rem]",
|
|
53
|
+
xl: "h-auto max-h-[80dvh]",
|
|
54
|
+
full: "h-dvh",
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Groups the sheet parts. Controlled via `open`/`onOpenChange`, or uncontrolled. */
|
|
58
|
+
function Sheet(props: SheetPrimitive.Root.Props) {
|
|
59
|
+
return <SheetPrimitive.Root data-slot="sheet" {...props} />
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Element that opens the sheet. Pass `render` to reuse an existing button. */
|
|
63
|
+
function SheetTrigger(props: SheetPrimitive.Trigger.Props) {
|
|
64
|
+
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Imperatively closes the sheet. Wrap a `Button` via `render` for a footer action. */
|
|
68
|
+
function SheetClose(props: SheetPrimitive.Close.Props) {
|
|
69
|
+
return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface SheetContentProps extends SheetPrimitive.Popup.Props {
|
|
73
|
+
/** Which edge the panel slides in from. @default "right" */
|
|
74
|
+
side?: SheetSide
|
|
75
|
+
/** Width (left/right) or height (top/bottom) of the panel. @default "md" */
|
|
76
|
+
size?: SheetSize
|
|
77
|
+
/** Draws the ✕ in the top corner. @default true */
|
|
78
|
+
showClose?: boolean
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Portal + backdrop + the sliding panel, in one styled wrapper. Renders into the
|
|
83
|
+
* `SpuntoProvider` overlay container (falls back to `document.body`).
|
|
84
|
+
*
|
|
85
|
+
* The panel is a `flex flex-col` by default; `className` is the layout escape
|
|
86
|
+
* hatch and is merged last — pass `p-0` to lay out your own padding, `grid
|
|
87
|
+
* grid-cols-2` to split it in two columns, `overflow-y-auto` to scroll the whole
|
|
88
|
+
* panel rather than an inner region.
|
|
89
|
+
*/
|
|
90
|
+
function SheetContent({
|
|
91
|
+
className,
|
|
92
|
+
children,
|
|
93
|
+
side = "right",
|
|
94
|
+
size = "md",
|
|
95
|
+
showClose = true,
|
|
96
|
+
...props
|
|
97
|
+
}: SheetContentProps) {
|
|
98
|
+
const container = useOverlayContainer()
|
|
99
|
+
const horizontal = side === "left" || side === "right"
|
|
100
|
+
return (
|
|
101
|
+
<SheetPrimitive.Portal container={container ?? undefined}>
|
|
102
|
+
<SheetPrimitive.Backdrop
|
|
103
|
+
data-slot="sheet-backdrop"
|
|
104
|
+
className="fixed inset-0 z-50 bg-black/40 backdrop-blur-[2px] transition-opacity duration-200 data-ending-style:opacity-0 data-starting-style:opacity-0 dark:bg-black/60"
|
|
105
|
+
/>
|
|
106
|
+
<SheetPrimitive.Popup
|
|
107
|
+
data-slot="sheet-content"
|
|
108
|
+
data-side={side}
|
|
109
|
+
className={cn(
|
|
110
|
+
"fixed z-50 flex flex-col gap-4 border-border bg-background bg-clip-padding text-sm text-foreground shadow-xl shadow-black/[0.08] outline-none",
|
|
111
|
+
"transition-transform duration-200 ease-out",
|
|
112
|
+
SIDE_CLASSES[side],
|
|
113
|
+
horizontal ? HORIZONTAL_SIZE_CLASSES[size] : VERTICAL_SIZE_CLASSES[size],
|
|
114
|
+
className
|
|
115
|
+
)}
|
|
116
|
+
{...props}
|
|
117
|
+
>
|
|
118
|
+
{children}
|
|
119
|
+
{showClose && (
|
|
120
|
+
<SheetPrimitive.Close
|
|
121
|
+
aria-label="Fermer"
|
|
122
|
+
className="absolute top-3.5 right-3.5 flex size-7 items-center justify-center rounded-md text-muted-foreground/70 transition-colors outline-none hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/50"
|
|
123
|
+
>
|
|
124
|
+
<svg width={16} height={16} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" aria-hidden>
|
|
125
|
+
<path d="M18 6 6 18M6 6l12 12" />
|
|
126
|
+
</svg>
|
|
127
|
+
</SheetPrimitive.Close>
|
|
128
|
+
)}
|
|
129
|
+
</SheetPrimitive.Popup>
|
|
130
|
+
</SheetPrimitive.Portal>
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Title + description stack at the top of the panel. */
|
|
135
|
+
function SheetHeader({ className, ...props }: ComponentProps<"div">) {
|
|
136
|
+
return <div data-slot="sheet-header" className={cn("flex flex-col gap-0.5 p-4", className)} {...props} />
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Action row pinned to the bottom of the panel. */
|
|
140
|
+
function SheetFooter({ className, ...props }: ComponentProps<"div">) {
|
|
141
|
+
return <div data-slot="sheet-footer" className={cn("mt-auto flex flex-col gap-2 p-4", className)} {...props} />
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function SheetTitle({ className, ...props }: SheetPrimitive.Title.Props) {
|
|
145
|
+
return (
|
|
146
|
+
<SheetPrimitive.Title
|
|
147
|
+
data-slot="sheet-title"
|
|
148
|
+
className={cn("text-base leading-none font-semibold text-foreground", className)}
|
|
149
|
+
{...props}
|
|
150
|
+
/>
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function SheetDescription({ className, ...props }: SheetPrimitive.Description.Props) {
|
|
155
|
+
return (
|
|
156
|
+
<SheetPrimitive.Description
|
|
157
|
+
data-slot="sheet-description"
|
|
158
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
159
|
+
{...props}
|
|
160
|
+
/>
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export {
|
|
165
|
+
Sheet,
|
|
166
|
+
SheetTrigger,
|
|
167
|
+
SheetClose,
|
|
168
|
+
SheetContent,
|
|
169
|
+
SheetHeader,
|
|
170
|
+
SheetFooter,
|
|
171
|
+
SheetTitle,
|
|
172
|
+
SheetDescription,
|
|
173
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import type { ComponentProps } from "react"
|
|
4
|
+
|
|
5
|
+
import { cn } from "../utils"
|
|
6
|
+
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Presentational only, on purpose: no sorting, no pagination, no column model.
|
|
9
|
+
// Apps that show tables already own that logic (and disagree on it) — what they
|
|
10
|
+
// were re-writing every time is the markup and the token choices, so that's all
|
|
11
|
+
// this ships. Every part is the plain HTML element with a `data-slot` and a
|
|
12
|
+
// class string, `className` merged last.
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
export interface TableProps extends ComponentProps<"table"> {
|
|
16
|
+
/**
|
|
17
|
+
* Class for the scroll container wrapping the `<table>` — the only place a
|
|
18
|
+
* `max-height`, a sticky-header context or a border/rounding can live, since
|
|
19
|
+
* it's the element that scrolls.
|
|
20
|
+
*/
|
|
21
|
+
containerClassName?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** The `<table>` plus its horizontal scroll container. */
|
|
25
|
+
function Table({ className, containerClassName, ...props }: TableProps) {
|
|
26
|
+
return (
|
|
27
|
+
<div data-slot="table-container" className={cn("relative w-full overflow-x-auto", containerClassName)}>
|
|
28
|
+
<table data-slot="table" className={cn("w-full caption-bottom text-sm", className)} {...props} />
|
|
29
|
+
</div>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function TableHeader({ className, ...props }: ComponentProps<"thead">) {
|
|
34
|
+
return <thead data-slot="table-header" className={cn("[&_tr]:border-b [&_tr]:border-border", className)} {...props} />
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function TableBody({ className, ...props }: ComponentProps<"tbody">) {
|
|
38
|
+
return <tbody data-slot="table-body" className={cn("[&_tr:last-child]:border-0", className)} {...props} />
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Totals row. Sits under the body with its own tinted background. */
|
|
42
|
+
function TableFooter({ className, ...props }: ComponentProps<"tfoot">) {
|
|
43
|
+
return (
|
|
44
|
+
<tfoot
|
|
45
|
+
data-slot="table-footer"
|
|
46
|
+
className={cn("border-t border-border bg-muted/50 font-medium [&>tr]:last:border-b-0", className)}
|
|
47
|
+
{...props}
|
|
48
|
+
/>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** A row. Hover tint by default; `data-[state=selected]` keeps it tinted. */
|
|
53
|
+
function TableRow({ className, ...props }: ComponentProps<"tr">) {
|
|
54
|
+
return (
|
|
55
|
+
<tr
|
|
56
|
+
data-slot="table-row"
|
|
57
|
+
className={cn(
|
|
58
|
+
"border-b border-border transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
|
|
59
|
+
className
|
|
60
|
+
)}
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** A header cell (`<th>`). */
|
|
67
|
+
function TableHead({ className, ...props }: ComponentProps<"th">) {
|
|
68
|
+
return (
|
|
69
|
+
<th
|
|
70
|
+
data-slot="table-head"
|
|
71
|
+
className={cn(
|
|
72
|
+
"h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0",
|
|
73
|
+
className
|
|
74
|
+
)}
|
|
75
|
+
{...props}
|
|
76
|
+
/>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** A body cell (`<td>`). */
|
|
81
|
+
function TableCell({ className, ...props }: ComponentProps<"td">) {
|
|
82
|
+
return (
|
|
83
|
+
<td
|
|
84
|
+
data-slot="table-cell"
|
|
85
|
+
className={cn("p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0", className)}
|
|
86
|
+
{...props}
|
|
87
|
+
/>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Caption under the table — its accessible description. */
|
|
92
|
+
function TableCaption({ className, ...props }: ComponentProps<"caption">) {
|
|
93
|
+
return (
|
|
94
|
+
<caption data-slot="table-caption" className={cn("mt-4 text-sm text-muted-foreground", className)} {...props} />
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export { Table, TableHeader, TableBody, TableFooter, TableRow, TableHead, TableCell, TableCaption }
|
package/src/index.ts
CHANGED
|
@@ -91,6 +91,35 @@ export {
|
|
|
91
91
|
AlertDialogDescription,
|
|
92
92
|
} from "./components/alert-dialog"
|
|
93
93
|
|
|
94
|
+
// Sheet — the same Base UI dialog, entering from an edge. `side` picks the edge,
|
|
95
|
+
// `size` how far it comes in; both resolve to plain utilities so `className`
|
|
96
|
+
// stays the layout escape hatch (no `!important` needed to widen a panel).
|
|
97
|
+
export {
|
|
98
|
+
Sheet,
|
|
99
|
+
SheetTrigger,
|
|
100
|
+
SheetClose,
|
|
101
|
+
SheetContent,
|
|
102
|
+
SheetHeader,
|
|
103
|
+
SheetFooter,
|
|
104
|
+
SheetTitle,
|
|
105
|
+
SheetDescription,
|
|
106
|
+
} from "./components/sheet"
|
|
107
|
+
export type { SheetContentProps, SheetSide, SheetSize } from "./components/sheet"
|
|
108
|
+
|
|
109
|
+
// Table — presentational only (no sorting, no pagination): apps own that logic,
|
|
110
|
+
// what they were duplicating is the markup and the tokens.
|
|
111
|
+
export {
|
|
112
|
+
Table,
|
|
113
|
+
TableHeader,
|
|
114
|
+
TableBody,
|
|
115
|
+
TableFooter,
|
|
116
|
+
TableRow,
|
|
117
|
+
TableHead,
|
|
118
|
+
TableCell,
|
|
119
|
+
TableCaption,
|
|
120
|
+
} from "./components/table"
|
|
121
|
+
export type { TableProps } from "./components/table"
|
|
122
|
+
|
|
94
123
|
// CommandPalette — the ⌘K palette, domain-free: an input, groups, items, and a
|
|
95
124
|
// jump. Base UI `Autocomplete` (roles + ↑↓ navigation) rendered inline inside a
|
|
96
125
|
// Base UI `Dialog` (focus trap + Escape), portalled into the provider's overlay
|
|
@@ -121,6 +150,64 @@ export type {
|
|
|
121
150
|
CommandPaletteLinkRender,
|
|
122
151
|
} from "./components/command-palette"
|
|
123
152
|
|
|
153
|
+
// CommandMenu — the same parts, inline: a Base UI `Popover` anchored to whatever
|
|
154
|
+
// the caller points at (an element or a caret rect), which **never moves focus**
|
|
155
|
+
// (`initialFocus`/`finalFocus` off, no backdrop, non-modal) and takes ↑ ↓ ↵ esc on
|
|
156
|
+
// `document` in the capture phase. That's what a "/" menu inside an editor needs
|
|
157
|
+
// and what the dialog-based palette can't do without losing the caret.
|
|
158
|
+
export {
|
|
159
|
+
CommandMenu,
|
|
160
|
+
CommandMenuInput,
|
|
161
|
+
CommandMenuList,
|
|
162
|
+
CommandMenuGroup,
|
|
163
|
+
CommandMenuSeparator,
|
|
164
|
+
CommandMenuItem,
|
|
165
|
+
CommandMenuEmpty,
|
|
166
|
+
CommandMenuLoading,
|
|
167
|
+
} from "./components/command-menu"
|
|
168
|
+
export type {
|
|
169
|
+
CommandMenuProps,
|
|
170
|
+
CommandMenuInputProps,
|
|
171
|
+
CommandMenuGroupProps,
|
|
172
|
+
CommandMenuItemProps,
|
|
173
|
+
CommandMenuLoadingProps,
|
|
174
|
+
CommandMenuFilter,
|
|
175
|
+
CommandMenuFilterItem,
|
|
176
|
+
CommandMenuLinkProps,
|
|
177
|
+
CommandMenuLinkRender,
|
|
178
|
+
} from "./components/command-menu"
|
|
179
|
+
|
|
180
|
+
// Auth — the login/signup screen in bricks (layout, brand panel, form, OAuth
|
|
181
|
+
// button, separator). Domain-free like the palette, hence its place in the root:
|
|
182
|
+
// every word of it is a prop, and it neither fetches nor redirects. Assembling
|
|
183
|
+
// them: the showcase's "Créer sa page de login" guide.
|
|
184
|
+
export {
|
|
185
|
+
AuthLayout,
|
|
186
|
+
AuthBrandPanel,
|
|
187
|
+
AuthBrandMark,
|
|
188
|
+
AuthForm,
|
|
189
|
+
authFormCopy,
|
|
190
|
+
OAuthButton,
|
|
191
|
+
AuthSeparator,
|
|
192
|
+
GoogleIcon,
|
|
193
|
+
GitHubIcon,
|
|
194
|
+
} from "./components/auth"
|
|
195
|
+
export type {
|
|
196
|
+
AuthLayoutProps,
|
|
197
|
+
AuthBrandPanelProps,
|
|
198
|
+
AuthBrandMarkProps,
|
|
199
|
+
AuthFormProps,
|
|
200
|
+
AuthFormCopy,
|
|
201
|
+
AuthModeCopy,
|
|
202
|
+
AuthFieldCopy,
|
|
203
|
+
AuthMode,
|
|
204
|
+
AuthSubmitValues,
|
|
205
|
+
AuthLinkProps,
|
|
206
|
+
AuthLinkRender,
|
|
207
|
+
OAuthButtonProps,
|
|
208
|
+
OAuthProvider,
|
|
209
|
+
} from "./components/auth"
|
|
210
|
+
|
|
124
211
|
// Kbd — one keyboard cap, or a whole shortcut (`keys="mod+k"` → ⌘ K). Used by the
|
|
125
212
|
// palette, exported on its own for anywhere else a shortcut needs drawing.
|
|
126
213
|
export { Kbd } from "./components/kbd"
|