tembro 6.0.3 → 6.1.1
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/CHANGELOG.md +13 -0
- package/dist/components/actions/action-menu.d.ts +11 -1
- package/dist/components/calendar/calendar-range-scheduler.cjs +1 -0
- package/dist/components/calendar/calendar-range-scheduler.d.ts +20 -0
- package/dist/components/calendar/calendar-range-scheduler.js +1 -0
- package/dist/components/calendar/date-time-picker.cjs +1 -0
- package/dist/components/calendar/date-time-picker.d.ts +15 -0
- package/dist/components/calendar/date-time-picker.js +1 -0
- package/dist/components/calendar/index.d.ts +2 -0
- package/dist/components/charts/chart-card.cjs +1 -0
- package/dist/components/charts/chart-card.d.ts +24 -0
- package/dist/components/charts/chart-card.js +1 -0
- package/dist/components/charts/index.d.ts +1 -0
- package/dist/components/command/advanced-command-menu.cjs +1 -0
- package/dist/components/command/advanced-command-menu.d.ts +25 -0
- package/dist/components/command/advanced-command-menu.js +1 -0
- package/dist/components/command/index.d.ts +1 -0
- package/dist/components/data-table/data-grid.cjs +1 -0
- package/dist/components/data-table/data-grid.d.ts +15 -0
- package/dist/components/data-table/data-grid.js +1 -0
- package/dist/components/data-table/index.d.ts +1 -0
- package/dist/components/data-table/public.d.ts +1 -0
- package/dist/components/display/index.d.ts +0 -2
- package/dist/components/feedback/async-boundary.cjs +1 -0
- package/dist/components/feedback/async-boundary.d.ts +12 -0
- package/dist/components/feedback/async-boundary.js +1 -0
- package/dist/components/feedback/index.d.ts +1 -0
- package/dist/components/filters/index.d.ts +0 -1
- package/dist/components/inputs/index.d.ts +1 -0
- package/dist/components/inputs/multi-select.cjs +1 -0
- package/dist/components/inputs/multi-select.d.ts +16 -0
- package/dist/components/inputs/multi-select.js +1 -0
- package/dist/components/inputs/public.d.ts +1 -0
- package/dist/components/inputs/time-picker.d.ts +11 -3
- package/dist/components/modern/file-manager.cjs +1 -0
- package/dist/components/modern/file-manager.d.ts +20 -0
- package/dist/components/modern/file-manager.js +1 -0
- package/dist/components/modern/index.d.ts +4 -1
- package/dist/components/modern/map.cjs +1 -0
- package/dist/components/modern/map.d.ts +29 -0
- package/dist/components/modern/map.js +1 -0
- package/dist/components/modern/media-player.cjs +1 -0
- package/dist/components/modern/media-player.d.ts +21 -0
- package/dist/components/modern/media-player.js +1 -0
- package/dist/components/modern/spreadsheet.cjs +1 -0
- package/dist/components/modern/spreadsheet.d.ts +35 -0
- package/dist/components/modern/spreadsheet.js +1 -0
- package/dist/components/notifications/action-toast.cjs +1 -0
- package/dist/components/notifications/action-toast.d.ts +16 -0
- package/dist/components/notifications/action-toast.js +1 -0
- package/dist/components/notifications/index.d.ts +1 -0
- package/dist/components/overlay/drawer.d.ts +3 -1
- package/dist/components/patterns/index.d.ts +1 -0
- package/dist/components/patterns/permissions-matrix.cjs +1 -0
- package/dist/components/patterns/permissions-matrix.d.ts +19 -0
- package/dist/components/patterns/permissions-matrix.js +1 -0
- package/dist/components/patterns/public.d.ts +1 -0
- package/dist/index.d.ts +0 -2
- package/dist/src/components/actions/action-menu.cjs +1 -1
- package/dist/src/components/actions/action-menu.js +47 -39
- package/dist/src/components/actions/copy-button.cjs +1 -1
- package/dist/src/components/actions/copy-button.js +4 -1
- package/dist/src/components/calendar/calendar-range-scheduler.cjs +1 -0
- package/dist/src/components/calendar/calendar-range-scheduler.js +75 -0
- package/dist/src/components/calendar/date-picker.cjs +1 -1
- package/dist/src/components/calendar/date-picker.js +5 -8
- package/dist/src/components/calendar/date-range-picker.cjs +1 -1
- package/dist/src/components/calendar/date-range-picker.js +12 -27
- package/dist/src/components/calendar/date-time-picker.cjs +1 -0
- package/dist/src/components/calendar/date-time-picker.js +36 -0
- package/dist/src/components/calendar/index.cjs +1 -1
- package/dist/src/components/calendar/index.js +5 -3
- package/dist/src/components/charts/chart-card.cjs +1 -0
- package/dist/src/components/charts/chart-card.js +55 -0
- package/dist/src/components/charts/index.cjs +1 -1
- package/dist/src/components/charts/index.js +3 -2
- package/dist/src/components/command/advanced-command-menu.cjs +1 -0
- package/dist/src/components/command/advanced-command-menu.js +57 -0
- package/dist/src/components/command/index.cjs +1 -1
- package/dist/src/components/command/index.js +3 -2
- package/dist/src/components/data-table/data-grid.cjs +1 -0
- package/dist/src/components/data-table/data-grid.js +32 -0
- package/dist/src/components/data-table/data-table-saved-filters.cjs +1 -1
- package/dist/src/components/data-table/data-table-saved-filters.js +11 -11
- package/dist/src/components/data-table/data-table.cjs +1 -1
- package/dist/src/components/data-table/data-table.js +175 -174
- package/dist/src/components/data-table/index.cjs +1 -1
- package/dist/src/components/data-table/index.js +15 -14
- package/dist/src/components/data-table/public.cjs +1 -1
- package/dist/src/components/data-table/public.js +3 -2
- package/dist/src/components/display/index.cjs +1 -1
- package/dist/src/components/display/index.js +9 -11
- package/dist/src/components/feedback/async-boundary.cjs +1 -0
- package/dist/src/components/feedback/async-boundary.js +22 -0
- package/dist/src/components/feedback/index.cjs +1 -1
- package/dist/src/components/feedback/index.js +2 -1
- package/dist/src/components/filters/index.cjs +1 -1
- package/dist/src/components/filters/index.js +2 -3
- package/dist/src/components/inputs/color-picker.cjs +1 -1
- package/dist/src/components/inputs/color-picker.js +63 -46
- package/dist/src/components/inputs/index.cjs +1 -1
- package/dist/src/components/inputs/index.js +13 -12
- package/dist/src/components/inputs/multi-select.cjs +1 -0
- package/dist/src/components/inputs/multi-select.js +80 -0
- package/dist/src/components/inputs/public.cjs +1 -1
- package/dist/src/components/inputs/public.js +13 -12
- package/dist/src/components/inputs/slider.cjs +1 -1
- package/dist/src/components/inputs/slider.js +3 -3
- package/dist/src/components/inputs/tag-input.cjs +1 -1
- package/dist/src/components/inputs/tag-input.js +7 -4
- package/dist/src/components/inputs/time-picker.cjs +1 -1
- package/dist/src/components/inputs/time-picker.js +90 -34
- package/dist/src/components/modern/file-manager.cjs +1 -0
- package/dist/src/components/modern/file-manager.js +75 -0
- package/dist/src/components/modern/index.cjs +1 -1
- package/dist/src/components/modern/index.js +7 -4
- package/dist/src/components/modern/map.cjs +1 -0
- package/dist/src/components/modern/map.js +158 -0
- package/dist/src/components/modern/media-player.cjs +1 -0
- package/dist/src/components/modern/media-player.js +176 -0
- package/dist/src/components/modern/resizable-panel.cjs +1 -1
- package/dist/src/components/modern/resizable-panel.js +39 -32
- package/dist/src/components/modern/spreadsheet.cjs +1 -0
- package/dist/src/components/modern/spreadsheet.js +97 -0
- package/dist/src/components/navigation/pagination.cjs +1 -1
- package/dist/src/components/navigation/pagination.js +5 -5
- package/dist/src/components/notifications/action-toast.cjs +1 -0
- package/dist/src/components/notifications/action-toast.js +49 -0
- package/dist/src/components/notifications/index.cjs +1 -1
- package/dist/src/components/notifications/index.js +3 -2
- package/dist/src/components/notifications/toast.cjs +1 -1
- package/dist/src/components/notifications/toast.js +68 -63
- package/dist/src/components/overlay/alert-dialog.cjs +1 -1
- package/dist/src/components/overlay/alert-dialog.js +8 -6
- package/dist/src/components/overlay/drawer.cjs +1 -1
- package/dist/src/components/overlay/drawer.js +38 -20
- package/dist/src/components/patterns/index.cjs +1 -1
- package/dist/src/components/patterns/index.js +2 -1
- package/dist/src/components/patterns/permissions-matrix.cjs +1 -0
- package/dist/src/components/patterns/permissions-matrix.js +43 -0
- package/dist/src/components/patterns/public.cjs +1 -1
- package/dist/src/components/patterns/public.js +2 -1
- package/dist/src/components/ui/checkbox/index.cjs +1 -1
- package/dist/src/components/ui/checkbox/index.js +2 -1
- package/dist/src/components/ui/dialog/index.cjs +1 -1
- package/dist/src/components/ui/dialog/index.js +8 -8
- package/dist/src/components/ui/dropdown-menu/index.cjs +1 -1
- package/dist/src/components/ui/dropdown-menu/index.js +21 -19
- package/dist/src/components/ui/input/group.cjs +1 -1
- package/dist/src/components/ui/input/group.js +8 -8
- package/dist/src/components/ui/popover/index.cjs +1 -1
- package/dist/src/components/ui/popover/index.js +10 -10
- package/dist/src/components/ui/tabs/index.cjs +1 -1
- package/dist/src/components/ui/tabs/index.js +6 -6
- package/dist/src/components/ui/toggle-group/index.cjs +1 -1
- package/dist/src/components/ui/toggle-group/index.js +2 -2
- package/dist/src/index.cjs +1 -1
- package/dist/src/index.js +111 -104
- package/dist/src/public-component-surface.cjs +1 -1
- package/dist/src/public-component-surface.js +59 -14
- package/package.json +1 -2
- package/packages/cli/dist/index.cjs +45 -165
- package/packages/cli/vendor/src/components/actions/action-menu.tsx +47 -19
- package/packages/cli/vendor/src/components/actions/copy-button.tsx +2 -2
- package/packages/cli/vendor/src/components/calendar/calendar-range-scheduler.tsx +72 -0
- package/packages/cli/vendor/src/components/calendar/date-picker.tsx +2 -5
- package/packages/cli/vendor/src/components/calendar/date-range-picker.tsx +5 -12
- package/packages/cli/vendor/src/components/calendar/date-time-picker.tsx +53 -0
- package/packages/cli/vendor/src/components/calendar/index.ts +5 -3
- package/packages/cli/vendor/src/components/charts/chart-card.tsx +81 -0
- package/packages/cli/vendor/src/components/charts/index.ts +1 -0
- package/packages/cli/vendor/src/components/command/advanced-command-menu.tsx +88 -0
- package/packages/cli/vendor/src/components/command/index.ts +2 -1
- package/packages/cli/vendor/src/components/data-table/data-grid.tsx +59 -0
- package/packages/cli/vendor/src/components/data-table/data-table.tsx +45 -34
- package/packages/cli/vendor/src/components/data-table/index.ts +3 -2
- package/packages/cli/vendor/src/components/data-table/public.ts +1 -0
- package/packages/cli/vendor/src/components/display/index.ts +3 -5
- package/packages/cli/vendor/src/components/feedback/async-boundary.tsx +36 -0
- package/packages/cli/vendor/src/components/feedback/index.ts +1 -0
- package/packages/cli/vendor/src/components/filters/index.ts +0 -1
- package/packages/cli/vendor/src/components/inputs/color-picker.tsx +15 -1
- package/packages/cli/vendor/src/components/inputs/index.ts +1 -0
- package/packages/cli/vendor/src/components/inputs/multi-select.tsx +75 -0
- package/packages/cli/vendor/src/components/inputs/public.ts +1 -0
- package/packages/cli/vendor/src/components/inputs/slider.tsx +3 -3
- package/packages/cli/vendor/src/components/inputs/tag-input.tsx +4 -4
- package/packages/cli/vendor/src/components/inputs/time-picker.tsx +91 -38
- package/packages/cli/vendor/src/components/modern/file-manager.tsx +79 -0
- package/packages/cli/vendor/src/components/modern/index.ts +4 -1
- package/packages/cli/vendor/src/components/modern/map.tsx +146 -0
- package/packages/cli/vendor/src/components/modern/media-player.tsx +182 -0
- package/packages/cli/vendor/src/components/modern/resizable-panel.tsx +15 -5
- package/packages/cli/vendor/src/components/modern/spreadsheet.tsx +155 -0
- package/packages/cli/vendor/src/components/navigation/pagination.tsx +9 -5
- package/packages/cli/vendor/src/components/notifications/action-toast.tsx +78 -0
- package/packages/cli/vendor/src/components/notifications/index.ts +2 -1
- package/packages/cli/vendor/src/components/notifications/toast.tsx +20 -10
- package/packages/cli/vendor/src/components/overlay/alert-dialog.tsx +3 -5
- package/packages/cli/vendor/src/components/overlay/drawer.tsx +22 -11
- package/packages/cli/vendor/src/components/patterns/index.ts +1 -0
- package/packages/cli/vendor/src/components/patterns/permissions-matrix.tsx +71 -0
- package/packages/cli/vendor/src/components/patterns/public.ts +1 -0
- package/packages/cli/vendor/src/components/ui/checkbox/index.tsx +3 -1
- package/packages/cli/vendor/src/components/ui/dialog/index.tsx +4 -4
- package/packages/cli/vendor/src/components/ui/dropdown-menu/index.tsx +18 -22
- package/packages/cli/vendor/src/components/ui/popover/index.tsx +4 -4
- package/packages/cli/vendor/src/components/ui/tabs/index.tsx +8 -3
- package/packages/cli/vendor/src/components/ui/toggle-group/index.tsx +6 -6
- package/packages/cli/vendor/src/index.ts +0 -2
- package/packages/cli/vendor/src/public-component-surface.ts +13 -4
- package/packages/cli/vendor/templates/showcase/src/showcase/data/registry.ts +6 -6
- package/packages/cli/vendor/templates/showcase/src/showcase/layout/HeroSection.tsx +23 -8
- package/packages/cli/vendor/templates/showcase/src/showcase/layout/WorkbenchSidebar.tsx +1 -1
- package/packages/cli/vendor/templates/showcase/src/showcase/sections/PatternsSection.tsx +1 -1
- package/packages/cli/vendor/templates/styles/globals.css +0 -120
- package/registry.json +3197 -2794
- package/dist/components/display/info-card.cjs +0 -1
- package/dist/components/display/info-card.d.ts +0 -51
- package/dist/components/display/info-card.js +0 -1
- package/dist/components/display/statistic.cjs +0 -1
- package/dist/components/display/statistic.d.ts +0 -25
- package/dist/components/display/statistic.js +0 -1
- package/dist/components/filters/filter-bar.cjs +0 -1
- package/dist/components/filters/filter-bar.d.ts +0 -28
- package/dist/components/filters/filter-bar.js +0 -1
- package/dist/components/modern/image-cropper.cjs +0 -1
- package/dist/components/modern/image-cropper.d.ts +0 -39
- package/dist/components/modern/image-cropper.js +0 -1
- package/dist/components/ui/number-field/index.cjs +0 -1
- package/dist/components/ui/number-field/index.d.ts +0 -18
- package/dist/components/ui/number-field/index.js +0 -1
- package/dist/components/ui/toolbar/index.cjs +0 -1
- package/dist/components/ui/toolbar/index.d.ts +0 -14
- package/dist/components/ui/toolbar/index.js +0 -1
- package/dist/src/components/display/info-card.cjs +0 -1
- package/dist/src/components/display/info-card.js +0 -124
- package/dist/src/components/display/statistic.cjs +0 -1
- package/dist/src/components/display/statistic.js +0 -104
- package/dist/src/components/filters/filter-bar.cjs +0 -1
- package/dist/src/components/filters/filter-bar.js +0 -103
- package/dist/src/components/modern/image-cropper.cjs +0 -1
- package/dist/src/components/modern/image-cropper.js +0 -143
- package/dist/src/components/ui/number-field/index.cjs +0 -1
- package/dist/src/components/ui/number-field/index.js +0 -73
- package/dist/src/components/ui/toolbar/index.cjs +0 -1
- package/dist/src/components/ui/toolbar/index.js +0 -50
- package/packages/cli/vendor/src/components/display/info-card.tsx +0 -195
- package/packages/cli/vendor/src/components/display/statistic.tsx +0 -114
- package/packages/cli/vendor/src/components/filters/filter-bar.tsx +0 -163
- package/packages/cli/vendor/src/components/modern/image-cropper.tsx +0 -226
- package/packages/cli/vendor/src/components/ui/number-field/index.tsx +0 -89
- package/packages/cli/vendor/src/components/ui/toolbar/index.tsx +0 -67
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import * as React from "react"
|
|
4
|
-
import { SlidersHorizontalIcon, XIcon } from "lucide-react"
|
|
5
|
-
|
|
6
|
-
import { Badge } from "@/components/ui/badge"
|
|
7
|
-
import { Button } from "@/components/ui/button"
|
|
8
|
-
import { cn, stopInteractivePropagation } from "@/lib/utils"
|
|
9
|
-
|
|
10
|
-
export type FilterBarChip = {
|
|
11
|
-
key: string
|
|
12
|
-
label: React.ReactNode
|
|
13
|
-
value?: React.ReactNode
|
|
14
|
-
tone?: "default" | "success" | "warning" | "danger" | "info" | "muted"
|
|
15
|
-
disabled?: boolean
|
|
16
|
-
hidden?: boolean
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export type FilterBarProps = React.ComponentProps<"div"> & {
|
|
20
|
-
search?: React.ReactNode
|
|
21
|
-
filters?: React.ReactNode
|
|
22
|
-
actions?: React.ReactNode
|
|
23
|
-
chips?: FilterBarChip[]
|
|
24
|
-
activeCount?: number
|
|
25
|
-
activeLabel?: (count: number) => React.ReactNode
|
|
26
|
-
resetLabel?: React.ReactNode
|
|
27
|
-
clearChipLabel?: React.ReactNode
|
|
28
|
-
onReset?: () => void
|
|
29
|
-
onRemoveChip?: (key: string) => void
|
|
30
|
-
collapsible?: boolean
|
|
31
|
-
defaultExpanded?: boolean
|
|
32
|
-
emptyChips?: React.ReactNode
|
|
33
|
-
chipLimit?: number
|
|
34
|
-
chipOverflowLabel?: (hiddenCount: number) => React.ReactNode
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const chipVariant: Record<NonNullable<FilterBarChip["tone"]>, React.ComponentProps<typeof Badge>["variant"]> = {
|
|
38
|
-
default: "secondary",
|
|
39
|
-
success: "secondary",
|
|
40
|
-
warning: "outline",
|
|
41
|
-
danger: "destructive",
|
|
42
|
-
info: "outline",
|
|
43
|
-
muted: "outline",
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function FilterBar({
|
|
47
|
-
className,
|
|
48
|
-
search,
|
|
49
|
-
filters,
|
|
50
|
-
actions,
|
|
51
|
-
chips,
|
|
52
|
-
activeCount,
|
|
53
|
-
activeLabel = (count) => `${count} active`,
|
|
54
|
-
resetLabel = "Reset",
|
|
55
|
-
clearChipLabel,
|
|
56
|
-
onReset,
|
|
57
|
-
onRemoveChip,
|
|
58
|
-
collapsible = false,
|
|
59
|
-
defaultExpanded = false,
|
|
60
|
-
emptyChips,
|
|
61
|
-
chipLimit,
|
|
62
|
-
chipOverflowLabel = (hiddenCount) => `+${hiddenCount} more`,
|
|
63
|
-
children,
|
|
64
|
-
...props
|
|
65
|
-
}: FilterBarProps) {
|
|
66
|
-
const [expanded, setExpanded] = React.useState(defaultExpanded)
|
|
67
|
-
const visibleChips = chips?.filter((chip) => !chip.hidden) ?? []
|
|
68
|
-
const renderedChips = chipLimit && chipLimit > 0 ? visibleChips.slice(0, chipLimit) : visibleChips
|
|
69
|
-
const hiddenChipCount = Math.max(visibleChips.length - renderedChips.length, 0)
|
|
70
|
-
const resolvedActiveCount = activeCount ?? visibleChips.length
|
|
71
|
-
const hasFilters = Boolean(filters || children)
|
|
72
|
-
const hasChips = Boolean(chips)
|
|
73
|
-
const shouldShowFilters = !collapsible || expanded
|
|
74
|
-
const shouldShowChipRow = visibleChips.length > 0 || (hasChips && emptyChips)
|
|
75
|
-
|
|
76
|
-
return (
|
|
77
|
-
<div
|
|
78
|
-
data-slot="filter-bar"
|
|
79
|
-
className={cn("flex flex-col gap-3 rounded-lg border bg-card p-3", className)}
|
|
80
|
-
{...props}
|
|
81
|
-
>
|
|
82
|
-
<div className="flex flex-col gap-2 lg:flex-row lg:items-center lg:justify-between">
|
|
83
|
-
<div className="flex min-w-0 flex-1 flex-col gap-2 sm:flex-row sm:items-center">
|
|
84
|
-
{search && <div className="min-w-0 flex-1">{search}</div>}
|
|
85
|
-
|
|
86
|
-
{collapsible && hasFilters && (
|
|
87
|
-
<Button
|
|
88
|
-
type="button"
|
|
89
|
-
variant={expanded ? "secondary" : "outline"}
|
|
90
|
-
size="sm"
|
|
91
|
-
onClick={() => setExpanded((value) => !value)}
|
|
92
|
-
>
|
|
93
|
-
<SlidersHorizontalIcon data-icon="inline-start" />
|
|
94
|
-
Filters
|
|
95
|
-
{resolvedActiveCount > 0 && (
|
|
96
|
-
<span className="ml-1 rounded-full bg-background px-1.5 text-xs">
|
|
97
|
-
{resolvedActiveCount}
|
|
98
|
-
</span>
|
|
99
|
-
)}
|
|
100
|
-
</Button>
|
|
101
|
-
)}
|
|
102
|
-
</div>
|
|
103
|
-
|
|
104
|
-
<div className="flex shrink-0 flex-wrap items-center gap-2">
|
|
105
|
-
{resolvedActiveCount > 0 && (
|
|
106
|
-
<span className="text-sm text-muted-foreground">{activeLabel(resolvedActiveCount)}</span>
|
|
107
|
-
)}
|
|
108
|
-
{resolvedActiveCount > 0 && onReset && (
|
|
109
|
-
<Button type="button" variant="ghost" size="sm" onClick={onReset}>
|
|
110
|
-
<XIcon data-icon="inline-start" />
|
|
111
|
-
{resetLabel}
|
|
112
|
-
</Button>
|
|
113
|
-
)}
|
|
114
|
-
{actions}
|
|
115
|
-
</div>
|
|
116
|
-
</div>
|
|
117
|
-
|
|
118
|
-
{hasFilters && shouldShowFilters && (
|
|
119
|
-
<div className="flex flex-col gap-2 sm:flex-row sm:flex-wrap sm:items-center">
|
|
120
|
-
{filters}
|
|
121
|
-
{children}
|
|
122
|
-
</div>
|
|
123
|
-
)}
|
|
124
|
-
|
|
125
|
-
{shouldShowChipRow && (
|
|
126
|
-
<div data-slot="filter-bar-chips" className="flex flex-wrap items-center gap-2">
|
|
127
|
-
{visibleChips.length > 0 ? (
|
|
128
|
-
<>
|
|
129
|
-
{renderedChips.map((chip) => (
|
|
130
|
-
<Badge key={chip.key} variant={chipVariant[chip.tone ?? "default"]} className={cn("gap-1.5", chip.disabled && "opacity-60")}>
|
|
131
|
-
<span>{chip.label}</span>
|
|
132
|
-
{chip.value !== undefined && <span className="text-muted-foreground">{chip.value}</span>}
|
|
133
|
-
{onRemoveChip && !chip.disabled && (
|
|
134
|
-
<button
|
|
135
|
-
type="button"
|
|
136
|
-
aria-label={typeof clearChipLabel === "string" ? clearChipLabel : "Remove filter"}
|
|
137
|
-
className="rounded-full p-0.5 hover:bg-muted"
|
|
138
|
-
onClick={(event) => {
|
|
139
|
-
stopInteractivePropagation(event)
|
|
140
|
-
onRemoveChip(chip.key)
|
|
141
|
-
}}
|
|
142
|
-
onMouseDown={stopInteractivePropagation}
|
|
143
|
-
onDoubleClick={stopInteractivePropagation}
|
|
144
|
-
>
|
|
145
|
-
<XIcon className="size-3" />
|
|
146
|
-
</button>
|
|
147
|
-
)}
|
|
148
|
-
</Badge>
|
|
149
|
-
))}
|
|
150
|
-
{hiddenChipCount > 0 ? (
|
|
151
|
-
<Badge variant="outline">{chipOverflowLabel(hiddenChipCount)}</Badge>
|
|
152
|
-
) : null}
|
|
153
|
-
</>
|
|
154
|
-
) : (
|
|
155
|
-
<span className="text-sm text-muted-foreground">{emptyChips}</span>
|
|
156
|
-
)}
|
|
157
|
-
</div>
|
|
158
|
-
)}
|
|
159
|
-
</div>
|
|
160
|
-
)
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export { FilterBar }
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import * as React from "react"
|
|
4
|
-
import Cropper, { type Area, type Point } from "react-easy-crop"
|
|
5
|
-
import { RotateCwIcon, ZoomInIcon } from "lucide-react"
|
|
6
|
-
|
|
7
|
-
import { cn } from "@/lib/utils"
|
|
8
|
-
|
|
9
|
-
function degreesToRadians(degrees: number) {
|
|
10
|
-
return (degrees * Math.PI) / 180
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function getRotatedSize(width: number, height: number, rotation: number) {
|
|
14
|
-
const radians = degreesToRadians(rotation)
|
|
15
|
-
return {
|
|
16
|
-
width: Math.abs(Math.cos(radians) * width) + Math.abs(Math.sin(radians) * height),
|
|
17
|
-
height: Math.abs(Math.sin(radians) * width) + Math.abs(Math.cos(radians) * height),
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function loadImage(src: string, crossOrigin?: string) {
|
|
22
|
-
return new Promise<HTMLImageElement>((resolve, reject) => {
|
|
23
|
-
const image = new Image()
|
|
24
|
-
if (crossOrigin) image.crossOrigin = crossOrigin
|
|
25
|
-
image.onload = () => resolve(image)
|
|
26
|
-
image.onerror = () => reject(new Error("Crop source image could not be loaded"))
|
|
27
|
-
image.src = src
|
|
28
|
-
})
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export async function createCroppedImageBlob({
|
|
32
|
-
src,
|
|
33
|
-
crop,
|
|
34
|
-
rotation = 0,
|
|
35
|
-
type = "image/png",
|
|
36
|
-
quality = 0.92,
|
|
37
|
-
crossOrigin = "anonymous",
|
|
38
|
-
}: {
|
|
39
|
-
src: string
|
|
40
|
-
crop: Area
|
|
41
|
-
rotation?: number
|
|
42
|
-
type?: string
|
|
43
|
-
quality?: number
|
|
44
|
-
crossOrigin?: string
|
|
45
|
-
}) {
|
|
46
|
-
const image = await loadImage(src, crossOrigin)
|
|
47
|
-
const rotatedSize = getRotatedSize(image.naturalWidth, image.naturalHeight, rotation)
|
|
48
|
-
const sourceCanvas = document.createElement("canvas")
|
|
49
|
-
sourceCanvas.width = Math.ceil(rotatedSize.width)
|
|
50
|
-
sourceCanvas.height = Math.ceil(rotatedSize.height)
|
|
51
|
-
const sourceContext = sourceCanvas.getContext("2d")
|
|
52
|
-
if (!sourceContext) throw new Error("Canvas 2D context is unavailable")
|
|
53
|
-
|
|
54
|
-
sourceContext.translate(sourceCanvas.width / 2, sourceCanvas.height / 2)
|
|
55
|
-
sourceContext.rotate(degreesToRadians(rotation))
|
|
56
|
-
sourceContext.translate(-image.naturalWidth / 2, -image.naturalHeight / 2)
|
|
57
|
-
sourceContext.drawImage(image, 0, 0)
|
|
58
|
-
|
|
59
|
-
const outputCanvas = document.createElement("canvas")
|
|
60
|
-
outputCanvas.width = Math.max(1, Math.round(crop.width))
|
|
61
|
-
outputCanvas.height = Math.max(1, Math.round(crop.height))
|
|
62
|
-
const outputContext = outputCanvas.getContext("2d")
|
|
63
|
-
if (!outputContext) throw new Error("Canvas 2D context is unavailable")
|
|
64
|
-
outputContext.drawImage(
|
|
65
|
-
sourceCanvas,
|
|
66
|
-
Math.round(crop.x),
|
|
67
|
-
Math.round(crop.y),
|
|
68
|
-
Math.round(crop.width),
|
|
69
|
-
Math.round(crop.height),
|
|
70
|
-
0,
|
|
71
|
-
0,
|
|
72
|
-
outputCanvas.width,
|
|
73
|
-
outputCanvas.height
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
return new Promise<Blob>((resolve, reject) => {
|
|
77
|
-
outputCanvas.toBlob(
|
|
78
|
-
(blob) => (blob ? resolve(blob) : reject(new Error("Cropped image could not be encoded"))),
|
|
79
|
-
type,
|
|
80
|
-
quality
|
|
81
|
-
)
|
|
82
|
-
})
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export type ImageCropperProps = Omit<React.ComponentProps<"div">, "onChange"> & {
|
|
86
|
-
src: string
|
|
87
|
-
alt?: string
|
|
88
|
-
crop?: Point
|
|
89
|
-
defaultCrop?: Point
|
|
90
|
-
onCropChange?: (crop: Point) => void
|
|
91
|
-
zoom?: number
|
|
92
|
-
defaultZoom?: number
|
|
93
|
-
onZoomChange?: (zoom: number) => void
|
|
94
|
-
rotation?: number
|
|
95
|
-
defaultRotation?: number
|
|
96
|
-
onRotationChange?: (rotation: number) => void
|
|
97
|
-
onCropComplete?: (area: Area, areaPixels: Area) => void
|
|
98
|
-
aspect?: number
|
|
99
|
-
cropShape?: "rect" | "round"
|
|
100
|
-
objectFit?: "contain" | "cover" | "horizontal-cover" | "vertical-cover"
|
|
101
|
-
minZoom?: number
|
|
102
|
-
maxZoom?: number
|
|
103
|
-
zoomStep?: number
|
|
104
|
-
showGrid?: boolean
|
|
105
|
-
disabled?: boolean
|
|
106
|
-
labels?: {
|
|
107
|
-
cropper?: string
|
|
108
|
-
zoom?: string
|
|
109
|
-
rotation?: string
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function ImageCropper({
|
|
114
|
-
src,
|
|
115
|
-
alt = "Image crop preview",
|
|
116
|
-
crop,
|
|
117
|
-
defaultCrop = { x: 0, y: 0 },
|
|
118
|
-
onCropChange,
|
|
119
|
-
zoom,
|
|
120
|
-
defaultZoom = 1,
|
|
121
|
-
onZoomChange,
|
|
122
|
-
rotation,
|
|
123
|
-
defaultRotation = 0,
|
|
124
|
-
onRotationChange,
|
|
125
|
-
onCropComplete,
|
|
126
|
-
aspect = 1,
|
|
127
|
-
cropShape = "rect",
|
|
128
|
-
objectFit = "contain",
|
|
129
|
-
minZoom = 1,
|
|
130
|
-
maxZoom = 3,
|
|
131
|
-
zoomStep = 0.1,
|
|
132
|
-
showGrid = true,
|
|
133
|
-
disabled = false,
|
|
134
|
-
labels,
|
|
135
|
-
className,
|
|
136
|
-
...props
|
|
137
|
-
}: ImageCropperProps) {
|
|
138
|
-
const [internalCrop, setInternalCrop] = React.useState(defaultCrop)
|
|
139
|
-
const [internalZoom, setInternalZoom] = React.useState(defaultZoom)
|
|
140
|
-
const [internalRotation, setInternalRotation] = React.useState(defaultRotation)
|
|
141
|
-
const currentCrop = crop ?? internalCrop
|
|
142
|
-
const currentZoom = zoom ?? internalZoom
|
|
143
|
-
const currentRotation = rotation ?? internalRotation
|
|
144
|
-
|
|
145
|
-
return (
|
|
146
|
-
<div data-slot="image-cropper" data-disabled={disabled || undefined} className={cn("grid gap-3 data-[disabled]:opacity-60", className)} {...props}>
|
|
147
|
-
<div
|
|
148
|
-
role="img"
|
|
149
|
-
aria-label={labels?.cropper ?? alt}
|
|
150
|
-
className={cn("relative min-h-64 overflow-hidden rounded-md bg-black", disabled && "pointer-events-none")}
|
|
151
|
-
style={{ aspectRatio: aspect }}
|
|
152
|
-
>
|
|
153
|
-
<Cropper
|
|
154
|
-
image={src}
|
|
155
|
-
crop={currentCrop}
|
|
156
|
-
zoom={currentZoom}
|
|
157
|
-
rotation={currentRotation}
|
|
158
|
-
aspect={aspect}
|
|
159
|
-
cropShape={cropShape}
|
|
160
|
-
objectFit={objectFit}
|
|
161
|
-
minZoom={minZoom}
|
|
162
|
-
maxZoom={maxZoom}
|
|
163
|
-
zoomSpeed={zoomStep}
|
|
164
|
-
showGrid={showGrid}
|
|
165
|
-
onCropChange={(nextCrop) => {
|
|
166
|
-
if (crop === undefined) setInternalCrop(nextCrop)
|
|
167
|
-
onCropChange?.(nextCrop)
|
|
168
|
-
}}
|
|
169
|
-
onZoomChange={(nextZoom) => {
|
|
170
|
-
if (zoom === undefined) setInternalZoom(nextZoom)
|
|
171
|
-
onZoomChange?.(nextZoom)
|
|
172
|
-
}}
|
|
173
|
-
onRotationChange={(nextRotation) => {
|
|
174
|
-
if (rotation === undefined) setInternalRotation(nextRotation)
|
|
175
|
-
onRotationChange?.(nextRotation)
|
|
176
|
-
}}
|
|
177
|
-
onCropComplete={onCropComplete}
|
|
178
|
-
/>
|
|
179
|
-
</div>
|
|
180
|
-
<div className="grid gap-2 sm:grid-cols-2">
|
|
181
|
-
<label className="grid grid-cols-[auto_1fr_auto] items-center gap-2 text-xs text-muted-foreground">
|
|
182
|
-
<ZoomInIcon className="size-4" aria-hidden="true" />
|
|
183
|
-
<span className="sr-only">{labels?.zoom ?? "Zoom"}</span>
|
|
184
|
-
<input
|
|
185
|
-
type="range"
|
|
186
|
-
min={minZoom}
|
|
187
|
-
max={maxZoom}
|
|
188
|
-
step={zoomStep}
|
|
189
|
-
value={currentZoom}
|
|
190
|
-
disabled={disabled}
|
|
191
|
-
aria-label={labels?.zoom ?? "Zoom"}
|
|
192
|
-
className="w-full accent-foreground"
|
|
193
|
-
onChange={(event) => {
|
|
194
|
-
const nextZoom = Number(event.target.value)
|
|
195
|
-
if (zoom === undefined) setInternalZoom(nextZoom)
|
|
196
|
-
onZoomChange?.(nextZoom)
|
|
197
|
-
}}
|
|
198
|
-
/>
|
|
199
|
-
<span className="w-9 text-right tabular-nums">{currentZoom.toFixed(1)}x</span>
|
|
200
|
-
</label>
|
|
201
|
-
<label className="grid grid-cols-[auto_1fr_auto] items-center gap-2 text-xs text-muted-foreground">
|
|
202
|
-
<RotateCwIcon className="size-4" aria-hidden="true" />
|
|
203
|
-
<span className="sr-only">{labels?.rotation ?? "Rotation"}</span>
|
|
204
|
-
<input
|
|
205
|
-
type="range"
|
|
206
|
-
min="-180"
|
|
207
|
-
max="180"
|
|
208
|
-
step="1"
|
|
209
|
-
value={currentRotation}
|
|
210
|
-
disabled={disabled}
|
|
211
|
-
aria-label={labels?.rotation ?? "Rotation"}
|
|
212
|
-
className="w-full accent-foreground"
|
|
213
|
-
onChange={(event) => {
|
|
214
|
-
const nextRotation = Number(event.target.value)
|
|
215
|
-
if (rotation === undefined) setInternalRotation(nextRotation)
|
|
216
|
-
onRotationChange?.(nextRotation)
|
|
217
|
-
}}
|
|
218
|
-
/>
|
|
219
|
-
<span className="w-10 text-right tabular-nums">{Math.round(currentRotation)}°</span>
|
|
220
|
-
</label>
|
|
221
|
-
</div>
|
|
222
|
-
</div>
|
|
223
|
-
)
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
export { ImageCropper }
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import * as React from "react"
|
|
4
|
-
import { NumberField as NumberFieldPrimitive } from "@base-ui/react/number-field"
|
|
5
|
-
import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"
|
|
6
|
-
|
|
7
|
-
import { cn } from "@/lib/utils"
|
|
8
|
-
|
|
9
|
-
export type NumberFieldProps = NumberFieldPrimitive.Root.Props
|
|
10
|
-
export type NumberFieldGroupProps = NumberFieldPrimitive.Group.Props
|
|
11
|
-
export type NumberFieldInputProps = NumberFieldPrimitive.Input.Props
|
|
12
|
-
export type NumberFieldIncrementProps = NumberFieldPrimitive.Increment.Props
|
|
13
|
-
export type NumberFieldDecrementProps = NumberFieldPrimitive.Decrement.Props
|
|
14
|
-
export type NumberFieldScrubAreaProps = NumberFieldPrimitive.ScrubArea.Props
|
|
15
|
-
export type NumberFieldScrubAreaCursorProps = NumberFieldPrimitive.ScrubAreaCursor.Props
|
|
16
|
-
|
|
17
|
-
function NumberField({ className, ...props }: NumberFieldProps) {
|
|
18
|
-
return <NumberFieldPrimitive.Root data-slot="number-field" className={cn("grid gap-2", className)} {...props} />
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function NumberFieldGroup({ className, ...props }: NumberFieldGroupProps) {
|
|
22
|
-
return (
|
|
23
|
-
<NumberFieldPrimitive.Group
|
|
24
|
-
data-slot="number-field-group"
|
|
25
|
-
className={cn(
|
|
26
|
-
"flex min-h-10 w-full overflow-hidden rounded-[var(--radius-md)] border border-input bg-background text-foreground shadow-xs transition-[border-color,box-shadow] focus-within:border-ring focus-within:ring-2 focus-within:ring-ring/35 has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50",
|
|
27
|
-
className
|
|
28
|
-
)}
|
|
29
|
-
{...props}
|
|
30
|
-
/>
|
|
31
|
-
)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function NumberFieldInput({ className, ...props }: NumberFieldInputProps) {
|
|
35
|
-
return (
|
|
36
|
-
<NumberFieldPrimitive.Input
|
|
37
|
-
data-slot="number-field-input"
|
|
38
|
-
className={cn("min-w-0 flex-1 bg-transparent px-3 py-2 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed", className)}
|
|
39
|
-
{...props}
|
|
40
|
-
/>
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function NumberFieldIncrement({ className, children, ...props }: NumberFieldIncrementProps) {
|
|
45
|
-
return (
|
|
46
|
-
<NumberFieldPrimitive.Increment
|
|
47
|
-
data-slot="number-field-increment"
|
|
48
|
-
className={cn("flex h-5 w-8 items-center justify-center border-l border-border text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:pointer-events-none disabled:opacity-40", className)}
|
|
49
|
-
{...props}
|
|
50
|
-
>
|
|
51
|
-
{children ?? <ChevronUpIcon aria-hidden="true" className="size-3.5" />}
|
|
52
|
-
</NumberFieldPrimitive.Increment>
|
|
53
|
-
)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function NumberFieldDecrement({ className, children, ...props }: NumberFieldDecrementProps) {
|
|
57
|
-
return (
|
|
58
|
-
<NumberFieldPrimitive.Decrement
|
|
59
|
-
data-slot="number-field-decrement"
|
|
60
|
-
className={cn("flex h-5 w-8 items-center justify-center border-l border-t border-border text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:pointer-events-none disabled:opacity-40", className)}
|
|
61
|
-
{...props}
|
|
62
|
-
>
|
|
63
|
-
{children ?? <ChevronDownIcon aria-hidden="true" className="size-3.5" />}
|
|
64
|
-
</NumberFieldPrimitive.Decrement>
|
|
65
|
-
)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function NumberFieldStepper({ className, ...props }: React.ComponentProps<"div">) {
|
|
69
|
-
return <div data-slot="number-field-stepper" className={cn("grid shrink-0 grid-rows-2", className)} {...props} />
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function NumberFieldScrubArea({ className, ...props }: NumberFieldScrubAreaProps) {
|
|
73
|
-
return <NumberFieldPrimitive.ScrubArea data-slot="number-field-scrub-area" className={cn("cursor-ew-resize select-none", className)} {...props} />
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function NumberFieldScrubAreaCursor({ className, ...props }: NumberFieldScrubAreaCursorProps) {
|
|
77
|
-
return <NumberFieldPrimitive.ScrubAreaCursor data-slot="number-field-scrub-area-cursor" className={cn("fixed z-50 rounded bg-foreground px-2 py-1 text-xs text-background shadow-lg", className)} {...props} />
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export {
|
|
81
|
-
NumberField,
|
|
82
|
-
NumberFieldDecrement,
|
|
83
|
-
NumberFieldGroup,
|
|
84
|
-
NumberFieldIncrement,
|
|
85
|
-
NumberFieldInput,
|
|
86
|
-
NumberFieldScrubArea,
|
|
87
|
-
NumberFieldScrubAreaCursor,
|
|
88
|
-
NumberFieldStepper,
|
|
89
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import { Toolbar as ToolbarPrimitive } from "@base-ui/react/toolbar"
|
|
4
|
-
|
|
5
|
-
import { cn } from "@/lib/utils"
|
|
6
|
-
|
|
7
|
-
export type ToolbarProps = ToolbarPrimitive.Root.Props
|
|
8
|
-
export type ToolbarGroupProps = ToolbarPrimitive.Group.Props
|
|
9
|
-
export type ToolbarButtonProps = ToolbarPrimitive.Button.Props
|
|
10
|
-
export type ToolbarLinkProps = ToolbarPrimitive.Link.Props
|
|
11
|
-
export type ToolbarInputProps = ToolbarPrimitive.Input.Props
|
|
12
|
-
export type ToolbarSeparatorProps = ToolbarPrimitive.Separator.Props
|
|
13
|
-
|
|
14
|
-
function Toolbar({ className, orientation = "horizontal", ...props }: ToolbarProps) {
|
|
15
|
-
return (
|
|
16
|
-
<ToolbarPrimitive.Root
|
|
17
|
-
data-slot="toolbar"
|
|
18
|
-
orientation={orientation}
|
|
19
|
-
className={cn(
|
|
20
|
-
"flex min-w-0 items-center gap-1 rounded-[var(--radius-lg)] border border-border bg-card p-1 text-card-foreground shadow-xs",
|
|
21
|
-
orientation === "vertical" && "w-max flex-col items-stretch",
|
|
22
|
-
className
|
|
23
|
-
)}
|
|
24
|
-
{...props}
|
|
25
|
-
/>
|
|
26
|
-
)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function ToolbarGroup({ className, ...props }: ToolbarGroupProps) {
|
|
30
|
-
return <ToolbarPrimitive.Group data-slot="toolbar-group" className={cn("flex min-w-0 items-center gap-1", className)} {...props} />
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function ToolbarButton({ className, ...props }: ToolbarButtonProps) {
|
|
34
|
-
return (
|
|
35
|
-
<ToolbarPrimitive.Button
|
|
36
|
-
data-slot="toolbar-button"
|
|
37
|
-
className={cn("inline-flex h-8 min-w-8 items-center justify-center gap-1.5 rounded-[var(--radius-md)] px-2.5 text-sm font-medium text-muted-foreground outline-none transition-colors hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[pressed]:bg-muted data-[pressed]:text-foreground [&_svg]:size-4 [&_svg]:shrink-0", className)}
|
|
38
|
-
{...props}
|
|
39
|
-
/>
|
|
40
|
-
)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function ToolbarLink({ className, ...props }: ToolbarLinkProps) {
|
|
44
|
-
return (
|
|
45
|
-
<ToolbarPrimitive.Link
|
|
46
|
-
data-slot="toolbar-link"
|
|
47
|
-
className={cn("inline-flex h-8 items-center justify-center gap-1.5 rounded-[var(--radius-md)] px-2.5 text-sm font-medium text-muted-foreground outline-none transition-colors hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring", className)}
|
|
48
|
-
{...props}
|
|
49
|
-
/>
|
|
50
|
-
)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function ToolbarInput({ className, ...props }: ToolbarInputProps) {
|
|
54
|
-
return (
|
|
55
|
-
<ToolbarPrimitive.Input
|
|
56
|
-
data-slot="toolbar-input"
|
|
57
|
-
className={cn("h-8 min-w-32 rounded-[var(--radius-md)] border border-input bg-background px-2.5 text-sm outline-none placeholder:text-muted-foreground focus-visible:ring-2 focus-visible:ring-ring", className)}
|
|
58
|
-
{...props}
|
|
59
|
-
/>
|
|
60
|
-
)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function ToolbarSeparator({ className, ...props }: ToolbarSeparatorProps) {
|
|
64
|
-
return <ToolbarPrimitive.Separator data-slot="toolbar-separator" className={cn("mx-1 h-5 w-px bg-border data-[orientation=vertical]:h-px data-[orientation=vertical]:w-full", className)} {...props} />
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export { Toolbar, ToolbarButton, ToolbarGroup, ToolbarInput, ToolbarLink, ToolbarSeparator }
|