@open-mercato/ui 0.6.4-develop.4178.1.aad9ddaa95 → 0.6.4-develop.4210.1.d412061cfe
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/.turbo/turbo-build.log +1 -1
- package/AGENTS.md +14 -0
- package/dist/backend/DataTable.js +28 -62
- package/dist/backend/DataTable.js.map +2 -2
- package/dist/backend/detail/AttachmentMetadataDialog.js +1 -1
- package/dist/backend/detail/AttachmentMetadataDialog.js.map +2 -2
- package/dist/primitives/activity-feed.js +196 -0
- package/dist/primitives/activity-feed.js.map +7 -0
- package/dist/primitives/avatar.js +140 -14
- package/dist/primitives/avatar.js.map +3 -3
- package/dist/primitives/badge.js +94 -5
- package/dist/primitives/badge.js.map +2 -2
- package/dist/primitives/button-group.js +52 -0
- package/dist/primitives/button-group.js.map +7 -0
- package/dist/primitives/color-picker.js +520 -0
- package/dist/primitives/color-picker.js.map +7 -0
- package/dist/primitives/command-menu.js +355 -0
- package/dist/primitives/command-menu.js.map +7 -0
- package/dist/primitives/dialog.js +210 -35
- package/dist/primitives/dialog.js.map +2 -2
- package/dist/primitives/drawer.js +219 -0
- package/dist/primitives/drawer.js.map +7 -0
- package/dist/primitives/notification-feed.js +213 -0
- package/dist/primitives/notification-feed.js.map +7 -0
- package/dist/primitives/notification.js +23 -2
- package/dist/primitives/notification.js.map +2 -2
- package/dist/primitives/pagination.js +245 -0
- package/dist/primitives/pagination.js.map +7 -0
- package/dist/primitives/progress.js +178 -5
- package/dist/primitives/progress.js.map +2 -2
- package/dist/primitives/rating.js +174 -0
- package/dist/primitives/rating.js.map +7 -0
- package/dist/primitives/scroll-area.js +82 -0
- package/dist/primitives/scroll-area.js.map +7 -0
- package/dist/primitives/segmented-control.js +101 -0
- package/dist/primitives/segmented-control.js.map +7 -0
- package/dist/primitives/separator.js +103 -4
- package/dist/primitives/separator.js.map +2 -2
- package/dist/primitives/slider.js +115 -0
- package/dist/primitives/slider.js.map +7 -0
- package/dist/primitives/step-indicator.js +187 -0
- package/dist/primitives/step-indicator.js.map +7 -0
- package/dist/primitives/table.js +102 -10
- package/dist/primitives/table.js.map +2 -2
- package/dist/primitives/tabs.js +122 -14
- package/dist/primitives/tabs.js.map +2 -2
- package/dist/primitives/time-picker.js +9 -11
- package/dist/primitives/time-picker.js.map +2 -2
- package/package.json +8 -4
- package/src/backend/DataTable.tsx +43 -60
- package/src/backend/detail/AttachmentMetadataDialog.tsx +1 -1
- package/src/primitives/__tests__/activity-feed.test.tsx +216 -0
- package/src/primitives/__tests__/avatar.test.tsx +131 -0
- package/src/primitives/__tests__/badge.test.tsx +189 -0
- package/src/primitives/__tests__/button-group.test.tsx +117 -0
- package/src/primitives/__tests__/color-picker.test.tsx +589 -0
- package/src/primitives/__tests__/command-menu.test.tsx +231 -0
- package/src/primitives/__tests__/dialog.test.tsx +290 -0
- package/src/primitives/__tests__/drawer.test.tsx +264 -0
- package/src/primitives/__tests__/notification-feed.test.tsx +303 -0
- package/src/primitives/__tests__/notification.test.tsx +102 -2
- package/src/primitives/__tests__/pagination.test.tsx +268 -0
- package/src/primitives/__tests__/progress.test.tsx +222 -0
- package/src/primitives/__tests__/rating.test.tsx +164 -0
- package/src/primitives/__tests__/scroll-area.test.tsx +109 -0
- package/src/primitives/__tests__/segmented-control.test.tsx +129 -0
- package/src/primitives/__tests__/separator.test.tsx +143 -0
- package/src/primitives/__tests__/slider.test.tsx +86 -0
- package/src/primitives/__tests__/step-indicator.test.tsx +205 -0
- package/src/primitives/__tests__/table.test.tsx +199 -0
- package/src/primitives/__tests__/tabs.test.tsx +139 -0
- package/src/primitives/activity-feed.tsx +326 -0
- package/src/primitives/avatar.tsx +225 -22
- package/src/primitives/badge.tsx +141 -6
- package/src/primitives/button-group.tsx +100 -0
- package/src/primitives/color-picker.tsx +693 -0
- package/src/primitives/command-menu.tsx +503 -0
- package/src/primitives/dialog.tsx +315 -51
- package/src/primitives/drawer.tsx +338 -0
- package/src/primitives/notification-feed.tsx +358 -0
- package/src/primitives/notification.tsx +42 -2
- package/src/primitives/pagination.tsx +376 -0
- package/src/primitives/progress.tsx +267 -6
- package/src/primitives/rating.tsx +245 -0
- package/src/primitives/scroll-area.tsx +169 -0
- package/src/primitives/segmented-control.tsx +162 -0
- package/src/primitives/separator.tsx +163 -3
- package/src/primitives/slider.tsx +160 -0
- package/src/primitives/step-indicator.tsx +287 -0
- package/src/primitives/table.tsx +142 -11
- package/src/primitives/tabs.tsx +174 -12
- package/src/primitives/time-picker.tsx +9 -11
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { Check, ChevronRight, X } from 'lucide-react'
|
|
5
|
+
import { cva, type VariantProps } from 'class-variance-authority'
|
|
6
|
+
|
|
7
|
+
import { cn } from '@open-mercato/shared/lib/utils'
|
|
8
|
+
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Multi-step progress indicator for wizards, onboarding flows, checkout
|
|
12
|
+
* funnels.
|
|
13
|
+
*
|
|
14
|
+
* Anchored on Figma:
|
|
15
|
+
* - `Step Indicator Horizontal [1.1]` (DS OM componentSet `3507:28`) —
|
|
16
|
+
* horizontal flow: each item followed by a chevron-right icon
|
|
17
|
+
* connector except the last item, which has no trailing connector
|
|
18
|
+
* - `Step Indicator Horizontal Items [1.1]` (componentSet `3505:3498`) —
|
|
19
|
+
* horizontal item with 3 states (Default / Active / Completed)
|
|
20
|
+
* - `Step Indicator Vertical [1.1]` (componentSet `3507:227`) — vertical
|
|
21
|
+
* pill list (each item is its own bg+rounded container)
|
|
22
|
+
* - `Step Indicator Vertical Items [1.1]` (componentSet `3507:190`) —
|
|
23
|
+
* vertical item with 3 states
|
|
24
|
+
*
|
|
25
|
+
* Distinct from `Progress` (continuous %) — StepIndicator is discrete
|
|
26
|
+
* (Step 2 of 5) with named labels per step.
|
|
27
|
+
*
|
|
28
|
+
* The four `status` values supported by this primitive are:
|
|
29
|
+
*
|
|
30
|
+
* - `'pending'` — Figma "Default" (white dot, muted label)
|
|
31
|
+
* - `'current'` — Figma "Active" (accent-indigo dot, foreground label)
|
|
32
|
+
* - `'complete'` — Figma "Completed" (status-success dot with check)
|
|
33
|
+
* - `'error'` — extension beyond the Figma source. Figma does not
|
|
34
|
+
* model an error state, but real product surfaces (failed checkout
|
|
35
|
+
* step, rejected workflow step) need one. Rendered with
|
|
36
|
+
* `status-error-icon` + an X glyph, paralleling the success branch.
|
|
37
|
+
*
|
|
38
|
+
* ```tsx
|
|
39
|
+
* const steps: StepIndicatorStep[] = [
|
|
40
|
+
* { id: 'account', label: 'Account', status: 'complete' },
|
|
41
|
+
* { id: 'profile', label: 'Profile', status: 'current' },
|
|
42
|
+
* { id: 'review', label: 'Review', status: 'pending' },
|
|
43
|
+
* ]
|
|
44
|
+
* <StepIndicator steps={steps} />
|
|
45
|
+
* <StepIndicator steps={steps} orientation="vertical" />
|
|
46
|
+
*
|
|
47
|
+
* // Interactive — only completed + current become clickable by default
|
|
48
|
+
* <StepIndicator steps={steps} onStepClick={(id) => goToStep(id)} />
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
export type StepIndicatorStatus = 'pending' | 'current' | 'complete' | 'error'
|
|
53
|
+
|
|
54
|
+
export type StepIndicatorStep = {
|
|
55
|
+
/** Stable id, returned by onStepClick. */
|
|
56
|
+
id: string
|
|
57
|
+
/** Label rendered beside the dot. */
|
|
58
|
+
label: string
|
|
59
|
+
/** Optional sub-text rendered below the label (vertical only). */
|
|
60
|
+
description?: string
|
|
61
|
+
/** Current visual state. */
|
|
62
|
+
status: StepIndicatorStatus
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const rootVariants = cva('flex', {
|
|
66
|
+
variants: {
|
|
67
|
+
orientation: {
|
|
68
|
+
horizontal: 'flex-row items-center gap-4',
|
|
69
|
+
vertical: 'flex-col items-stretch gap-2',
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
defaultVariants: { orientation: 'horizontal' },
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
// Horizontal item: dot + gap + label, no own bg/border (lives inline).
|
|
76
|
+
const horizontalItemVariants = cva('inline-flex items-center gap-2', {
|
|
77
|
+
variants: {
|
|
78
|
+
status: {
|
|
79
|
+
pending: '',
|
|
80
|
+
current: '',
|
|
81
|
+
complete: '',
|
|
82
|
+
error: '',
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
defaultVariants: { status: 'pending' },
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
// Vertical item: pill container per Figma `Step Indicator Vertical Items
|
|
89
|
+
// [1.1]` (`cornerRadius: 10`). Active item raises with bg-background;
|
|
90
|
+
// pending / complete items sit on bg-muted/40 (Figma `#F7F7F7`).
|
|
91
|
+
const verticalItemVariants = cva(
|
|
92
|
+
'inline-flex items-center gap-2.5 rounded-lg px-3 py-2 transition-colors w-full',
|
|
93
|
+
{
|
|
94
|
+
variants: {
|
|
95
|
+
status: {
|
|
96
|
+
pending: 'bg-muted/40',
|
|
97
|
+
current: 'bg-background ring-1 ring-border',
|
|
98
|
+
complete: 'bg-muted/40',
|
|
99
|
+
error: 'bg-status-error-bg ring-1 ring-status-error-border',
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
defaultVariants: { status: 'pending' },
|
|
103
|
+
},
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
// Dot — 20×20 rounded-full per Figma. Solid bg per state, no internal
|
|
107
|
+
// number (Figma sample uses the Default state as an empty white circle).
|
|
108
|
+
const dotVariants = cva(
|
|
109
|
+
'relative z-10 inline-flex shrink-0 items-center justify-center rounded-full transition-colors',
|
|
110
|
+
{
|
|
111
|
+
variants: {
|
|
112
|
+
size: {
|
|
113
|
+
sm: 'size-4',
|
|
114
|
+
default: 'size-5',
|
|
115
|
+
},
|
|
116
|
+
status: {
|
|
117
|
+
pending: 'border border-muted-foreground/30 bg-background',
|
|
118
|
+
current: 'bg-accent-indigo text-accent-indigo-foreground',
|
|
119
|
+
complete:
|
|
120
|
+
'bg-status-success-icon text-status-success-bg',
|
|
121
|
+
error: 'bg-status-error-icon text-status-error-bg',
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
defaultVariants: { size: 'default', status: 'pending' },
|
|
125
|
+
},
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
const labelVariants = cva('text-sm leading-tight', {
|
|
129
|
+
variants: {
|
|
130
|
+
status: {
|
|
131
|
+
pending: 'text-muted-foreground',
|
|
132
|
+
current: 'text-foreground font-medium',
|
|
133
|
+
complete: 'text-muted-foreground',
|
|
134
|
+
error: 'text-status-error-text font-medium',
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
defaultVariants: { status: 'pending' },
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
export type StepIndicatorProps = React.HTMLAttributes<HTMLOListElement> &
|
|
141
|
+
VariantProps<typeof rootVariants> & {
|
|
142
|
+
steps: StepIndicatorStep[]
|
|
143
|
+
size?: 'sm' | 'default'
|
|
144
|
+
/** Optional callback. When provided, every step becomes a button. */
|
|
145
|
+
onStepClick?: (stepId: string) => void
|
|
146
|
+
/** Optional restriction on which statuses are clickable. Defaults to
|
|
147
|
+
* `['complete', 'current']` — past + present only. */
|
|
148
|
+
clickableStatuses?: StepIndicatorStatus[]
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const DEFAULT_CLICKABLE: StepIndicatorStatus[] = ['complete', 'current']
|
|
152
|
+
|
|
153
|
+
function renderDotContent(status: StepIndicatorStatus): React.ReactNode {
|
|
154
|
+
if (status === 'complete') return <Check aria-hidden="true" className="size-3" />
|
|
155
|
+
if (status === 'error') return <X aria-hidden="true" className="size-3" />
|
|
156
|
+
return null
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export const StepIndicator = React.forwardRef<HTMLOListElement, StepIndicatorProps>(
|
|
160
|
+
(
|
|
161
|
+
{
|
|
162
|
+
className,
|
|
163
|
+
steps,
|
|
164
|
+
orientation,
|
|
165
|
+
size,
|
|
166
|
+
onStepClick,
|
|
167
|
+
clickableStatuses = DEFAULT_CLICKABLE,
|
|
168
|
+
...props
|
|
169
|
+
},
|
|
170
|
+
ref,
|
|
171
|
+
) => {
|
|
172
|
+
const t = useT()
|
|
173
|
+
const isVertical = orientation === 'vertical'
|
|
174
|
+
const interactive = typeof onStepClick === 'function'
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<ol
|
|
178
|
+
ref={ref}
|
|
179
|
+
data-slot="step-indicator"
|
|
180
|
+
aria-orientation={isVertical ? 'vertical' : 'horizontal'}
|
|
181
|
+
className={cn(rootVariants({ orientation }), className)}
|
|
182
|
+
{...props}
|
|
183
|
+
>
|
|
184
|
+
{steps.map((step, index) => {
|
|
185
|
+
const isLast = index === steps.length - 1
|
|
186
|
+
const canClick = interactive && clickableStatuses.includes(step.status)
|
|
187
|
+
|
|
188
|
+
const dotNode = (
|
|
189
|
+
<span
|
|
190
|
+
data-slot="step-indicator-dot"
|
|
191
|
+
data-status={step.status}
|
|
192
|
+
className={cn(dotVariants({ size, status: step.status }))}
|
|
193
|
+
aria-current={step.status === 'current' ? 'step' : undefined}
|
|
194
|
+
>
|
|
195
|
+
{renderDotContent(step.status)}
|
|
196
|
+
</span>
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
const labelNode = (
|
|
200
|
+
<div className="min-w-0">
|
|
201
|
+
<span className={cn(labelVariants({ status: step.status }))}>{step.label}</span>
|
|
202
|
+
{step.description && isVertical ? (
|
|
203
|
+
<span className="mt-0.5 block text-xs text-muted-foreground leading-tight">
|
|
204
|
+
{step.description}
|
|
205
|
+
</span>
|
|
206
|
+
) : null}
|
|
207
|
+
</div>
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
// Vertical trailing chevron — Figma shows it only on the
|
|
211
|
+
// Active item to mark "you are here" (the rest of the rail
|
|
212
|
+
// is bg + rounded-lg + dot).
|
|
213
|
+
const verticalTrailingChevron =
|
|
214
|
+
isVertical && step.status === 'current' ? (
|
|
215
|
+
<ChevronRight
|
|
216
|
+
aria-hidden="true"
|
|
217
|
+
className="ml-auto size-4 text-muted-foreground"
|
|
218
|
+
/>
|
|
219
|
+
) : null
|
|
220
|
+
|
|
221
|
+
const itemBody = isVertical ? (
|
|
222
|
+
<span
|
|
223
|
+
className={cn(
|
|
224
|
+
verticalItemVariants({ status: step.status }),
|
|
225
|
+
canClick && 'cursor-pointer',
|
|
226
|
+
)}
|
|
227
|
+
>
|
|
228
|
+
{dotNode}
|
|
229
|
+
{labelNode}
|
|
230
|
+
{verticalTrailingChevron}
|
|
231
|
+
</span>
|
|
232
|
+
) : (
|
|
233
|
+
<span className={cn(horizontalItemVariants({ status: step.status }))}>
|
|
234
|
+
{dotNode}
|
|
235
|
+
{labelNode}
|
|
236
|
+
</span>
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
return (
|
|
240
|
+
<li
|
|
241
|
+
key={step.id}
|
|
242
|
+
data-slot="step-indicator-item"
|
|
243
|
+
data-status={step.status}
|
|
244
|
+
className={cn(
|
|
245
|
+
isVertical ? 'list-none' : 'flex items-center gap-4 list-none',
|
|
246
|
+
)}
|
|
247
|
+
>
|
|
248
|
+
{canClick ? (
|
|
249
|
+
<button
|
|
250
|
+
type="button"
|
|
251
|
+
onClick={() => onStepClick?.(step.id)}
|
|
252
|
+
aria-label={t('ui.stepIndicator.goToStep.ariaLabel', 'Go to step: {label}', { label: step.label })}
|
|
253
|
+
className={cn(
|
|
254
|
+
'inline-flex items-center gap-2',
|
|
255
|
+
isVertical && 'w-full',
|
|
256
|
+
'rounded-md outline-none focus-visible:shadow-focus text-left',
|
|
257
|
+
)}
|
|
258
|
+
>
|
|
259
|
+
{itemBody}
|
|
260
|
+
</button>
|
|
261
|
+
) : (
|
|
262
|
+
itemBody
|
|
263
|
+
)}
|
|
264
|
+
{/* Horizontal connector — Figma uses a chevron-right
|
|
265
|
+
icon between items, NOT a line. */}
|
|
266
|
+
{!isVertical && !isLast ? (
|
|
267
|
+
<ChevronRight
|
|
268
|
+
aria-hidden="true"
|
|
269
|
+
data-slot="step-indicator-connector"
|
|
270
|
+
className="size-4 shrink-0 text-muted-foreground/50"
|
|
271
|
+
/>
|
|
272
|
+
) : null}
|
|
273
|
+
</li>
|
|
274
|
+
)
|
|
275
|
+
})}
|
|
276
|
+
</ol>
|
|
277
|
+
)
|
|
278
|
+
},
|
|
279
|
+
)
|
|
280
|
+
StepIndicator.displayName = 'StepIndicator'
|
|
281
|
+
|
|
282
|
+
export {
|
|
283
|
+
rootVariants as stepIndicatorRootVariants,
|
|
284
|
+
dotVariants as stepIndicatorDotVariants,
|
|
285
|
+
horizontalItemVariants as stepIndicatorHorizontalItemVariants,
|
|
286
|
+
verticalItemVariants as stepIndicatorVerticalItemVariants,
|
|
287
|
+
}
|
package/src/primitives/table.tsx
CHANGED
|
@@ -1,27 +1,158 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
1
3
|
import * as React from 'react'
|
|
4
|
+
|
|
2
5
|
import { cn } from '@open-mercato/shared/lib/utils'
|
|
3
6
|
|
|
4
|
-
|
|
5
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Low-level table primitive used by `DataTable` and a handful of
|
|
9
|
+
* direct consumers (`DictionaryEntriesEditor.tsx`). Phase B.6 polish
|
|
10
|
+
* aligns the chrome with Figma `Table` page (`553:14955`):
|
|
11
|
+
*
|
|
12
|
+
* - `Table Header Cell [1.1]` (`587:5793`) — subtle `bg-muted/40`
|
|
13
|
+
* header strip + sortable chevron slot
|
|
14
|
+
* - `Table Row Cell [1.1]` (`553:22175`) — comfortable padding,
|
|
15
|
+
* hover affordance
|
|
16
|
+
* - `Sorting Icons [1.1]` (`581:2327`) — handled by DataTable
|
|
17
|
+
* - Assembled examples in `Blocks` (167144:147461 et al.) confirm
|
|
18
|
+
* the chrome.
|
|
19
|
+
*
|
|
20
|
+
* Backward compatibility: every existing export (`Table`,
|
|
21
|
+
* `TableHeader`, `TableBody`, `TableRow`, `TableHead`, `TableCell`)
|
|
22
|
+
* stays callable verbatim. Header now ships a subtle `bg-muted/40`
|
|
23
|
+
* strip — visible-but-soft polish per Figma. Row hover ships
|
|
24
|
+
* `hover:bg-muted/30`. Both default to the new look since the spec
|
|
25
|
+
* scope calls for "row-hover state token" + "header cell padding
|
|
26
|
+
* alignment with Figma".
|
|
27
|
+
*
|
|
28
|
+
* New (additive):
|
|
29
|
+
* - `<Table variant="striped">` — even-row bg-muted/20 tint per
|
|
30
|
+
* Figma striped block.
|
|
31
|
+
* - `<TableFooter>` — bordered top footer row group.
|
|
32
|
+
* - `<TableCaption>` — accessible caption above the table.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
type TableContextValue = {
|
|
36
|
+
variant: 'default' | 'striped'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const TableContext = React.createContext<TableContextValue>({ variant: 'default' })
|
|
40
|
+
|
|
41
|
+
export type TableVariant = 'default' | 'striped'
|
|
42
|
+
|
|
43
|
+
export type TableProps = React.HTMLAttributes<HTMLTableElement> & {
|
|
44
|
+
/** `striped` adds even-row `bg-muted/20` tint per Figma. */
|
|
45
|
+
variant?: TableVariant
|
|
6
46
|
}
|
|
7
47
|
|
|
8
|
-
export function
|
|
9
|
-
|
|
48
|
+
export function Table({ className, variant = 'default', ...props }: TableProps) {
|
|
49
|
+
const contextValue = React.useMemo(() => ({ variant }), [variant])
|
|
50
|
+
return (
|
|
51
|
+
<TableContext.Provider value={contextValue}>
|
|
52
|
+
<table
|
|
53
|
+
data-slot="table"
|
|
54
|
+
data-variant={variant}
|
|
55
|
+
className={cn('w-full text-sm', className)}
|
|
56
|
+
{...props}
|
|
57
|
+
/>
|
|
58
|
+
</TableContext.Provider>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function TableHeader({
|
|
63
|
+
className,
|
|
64
|
+
...props
|
|
65
|
+
}: React.HTMLAttributes<HTMLTableSectionElement>) {
|
|
66
|
+
return (
|
|
67
|
+
<thead
|
|
68
|
+
data-slot="table-header"
|
|
69
|
+
className={cn('bg-muted/40 [&_tr]:border-b', className)}
|
|
70
|
+
{...props}
|
|
71
|
+
/>
|
|
72
|
+
)
|
|
10
73
|
}
|
|
11
74
|
|
|
12
75
|
export function TableBody(props: React.HTMLAttributes<HTMLTableSectionElement>) {
|
|
13
|
-
return <tbody {...props} />
|
|
76
|
+
return <tbody data-slot="table-body" {...props} />
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function TableFooter({
|
|
80
|
+
className,
|
|
81
|
+
...props
|
|
82
|
+
}: React.HTMLAttributes<HTMLTableSectionElement>) {
|
|
83
|
+
return (
|
|
84
|
+
<tfoot
|
|
85
|
+
data-slot="table-footer"
|
|
86
|
+
className={cn('border-t bg-muted/40 font-medium', className)}
|
|
87
|
+
{...props}
|
|
88
|
+
/>
|
|
89
|
+
)
|
|
14
90
|
}
|
|
15
91
|
|
|
16
|
-
export
|
|
17
|
-
|
|
92
|
+
export type TableRowProps = React.HTMLAttributes<HTMLTableRowElement>
|
|
93
|
+
|
|
94
|
+
export function TableRow({ className, ...props }: TableRowProps) {
|
|
95
|
+
const { variant } = React.useContext(TableContext)
|
|
96
|
+
return (
|
|
97
|
+
<tr
|
|
98
|
+
data-slot="table-row"
|
|
99
|
+
className={cn(
|
|
100
|
+
'border-b last:border-b-0 transition-colors',
|
|
101
|
+
// Row hover affordance — only applies to body rows (header
|
|
102
|
+
// rows have their own bg-muted/40 strip and shouldn't bounce
|
|
103
|
+
// on hover). Children that are inside <thead> bypass via
|
|
104
|
+
// CSS-attribute selectors at the consumer level if needed.
|
|
105
|
+
'[&:not(thead_*)]:hover:bg-muted/30',
|
|
106
|
+
variant === 'striped' ? '[&:not(thead_*)]:even:bg-muted/20' : '',
|
|
107
|
+
className,
|
|
108
|
+
)}
|
|
109
|
+
{...props}
|
|
110
|
+
/>
|
|
111
|
+
)
|
|
18
112
|
}
|
|
19
113
|
|
|
20
|
-
export function TableHead({
|
|
21
|
-
|
|
114
|
+
export function TableHead({
|
|
115
|
+
className,
|
|
116
|
+
ref,
|
|
117
|
+
...props
|
|
118
|
+
}: React.ThHTMLAttributes<HTMLTableCellElement> & {
|
|
119
|
+
ref?: React.Ref<HTMLTableCellElement>
|
|
120
|
+
}) {
|
|
121
|
+
return (
|
|
122
|
+
<th
|
|
123
|
+
ref={ref}
|
|
124
|
+
data-slot="table-head"
|
|
125
|
+
className={cn(
|
|
126
|
+
'text-left font-medium px-4 py-2 whitespace-nowrap text-muted-foreground',
|
|
127
|
+
className,
|
|
128
|
+
)}
|
|
129
|
+
{...props}
|
|
130
|
+
/>
|
|
131
|
+
)
|
|
22
132
|
}
|
|
23
133
|
|
|
24
|
-
export function TableCell({
|
|
25
|
-
|
|
134
|
+
export function TableCell({
|
|
135
|
+
className,
|
|
136
|
+
...props
|
|
137
|
+
}: React.TdHTMLAttributes<HTMLTableCellElement>) {
|
|
138
|
+
return (
|
|
139
|
+
<td
|
|
140
|
+
data-slot="table-cell"
|
|
141
|
+
className={cn('px-4 py-2', className)}
|
|
142
|
+
{...props}
|
|
143
|
+
/>
|
|
144
|
+
)
|
|
26
145
|
}
|
|
27
146
|
|
|
147
|
+
export function TableCaption({
|
|
148
|
+
className,
|
|
149
|
+
...props
|
|
150
|
+
}: React.HTMLAttributes<HTMLTableCaptionElement>) {
|
|
151
|
+
return (
|
|
152
|
+
<caption
|
|
153
|
+
data-slot="table-caption"
|
|
154
|
+
className={cn('mt-2 text-sm text-muted-foreground', className)}
|
|
155
|
+
{...props}
|
|
156
|
+
/>
|
|
157
|
+
)
|
|
158
|
+
}
|