@vendure-io/ui 1.3.0 → 2.0.0-beta.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.
Files changed (112) hide show
  1. package/README.md +5 -5
  2. package/package.json +10 -5
  3. package/src/components/{ui → atoms}/alert-dialog.tsx +4 -4
  4. package/src/components/{ui → atoms}/badge.tsx +6 -3
  5. package/src/components/{ui → atoms}/button-group.tsx +1 -2
  6. package/src/components/{ui → atoms}/button.tsx +8 -2
  7. package/src/components/{ui → atoms}/calendar.tsx +1 -1
  8. package/src/components/{ui → atoms}/card.tsx +2 -3
  9. package/src/components/{ui → atoms}/carousel.tsx +1 -1
  10. package/src/components/atoms/checkbox.tsx +29 -0
  11. package/src/components/{ui → atoms}/combobox.tsx +3 -4
  12. package/src/components/{ui → atoms}/command.tsx +2 -2
  13. package/src/components/{ui → atoms}/context-menu.tsx +1 -1
  14. package/src/components/{ui → atoms}/dialog.tsx +4 -4
  15. package/src/components/{ui → atoms}/drawer.tsx +3 -3
  16. package/src/components/{ui → atoms}/dropdown-menu.tsx +3 -2
  17. package/src/components/{ui → atoms}/empty.tsx +1 -1
  18. package/src/components/{ui → atoms}/field.tsx +2 -2
  19. package/src/components/{ui → atoms}/hover-card.tsx +1 -1
  20. package/src/components/{ui → atoms}/input-group.tsx +3 -4
  21. package/src/components/{ui → atoms}/input-otp.tsx +0 -1
  22. package/src/components/{ui → atoms}/input.tsx +0 -1
  23. package/src/components/{ui → atoms}/item.tsx +2 -2
  24. package/src/components/{ui → atoms}/menubar.tsx +3 -4
  25. package/src/components/{ui → atoms}/native-select.tsx +0 -1
  26. package/src/components/{ui → atoms}/navigation-menu.tsx +7 -4
  27. package/src/components/{ui → atoms}/pagination.tsx +1 -1
  28. package/src/components/{ui → atoms}/popover.tsx +2 -2
  29. package/src/components/{ui → atoms}/select.tsx +22 -4
  30. package/src/components/{ui → atoms}/sheet.tsx +4 -4
  31. package/src/components/{ui → atoms}/sidebar.tsx +6 -6
  32. package/src/components/atoms/skeleton.tsx +16 -0
  33. package/src/components/{ui → atoms}/slider.tsx +0 -1
  34. package/src/components/{ui → atoms}/sonner.tsx +1 -1
  35. package/src/components/{ui → atoms}/switch.tsx +0 -1
  36. package/src/components/{ui → atoms}/tabs.tsx +6 -4
  37. package/src/components/{ui → atoms}/textarea.tsx +0 -1
  38. package/src/components/{ui → atoms}/toggle-group.tsx +3 -3
  39. package/src/components/{ui → atoms}/toggle.tsx +0 -1
  40. package/src/components/{ui → atoms}/tooltip.tsx +2 -1
  41. package/src/components/molecules/anonymized-token.tsx +170 -0
  42. package/src/components/molecules/chip.tsx +85 -0
  43. package/src/components/{custom → molecules}/combobox-free-text.tsx +7 -3
  44. package/src/components/molecules/confirm-dialog.tsx +108 -0
  45. package/src/components/molecules/copyable-text.tsx +102 -0
  46. package/src/components/molecules/data-table/data-table-bulk-actions.tsx +69 -0
  47. package/src/components/molecules/data-table/data-table-column-header.tsx +61 -0
  48. package/src/components/molecules/data-table/data-table-filters.tsx +198 -0
  49. package/src/components/molecules/data-table/data-table-helpers.tsx +73 -0
  50. package/src/components/molecules/data-table/data-table-types.tsx +241 -0
  51. package/src/components/molecules/data-table/data-table-view-options.tsx +73 -0
  52. package/src/components/molecules/data-table/data-table.tsx +500 -0
  53. package/src/components/molecules/data-table/list-header.tsx +53 -0
  54. package/src/components/molecules/data-table/table-pagination.tsx +263 -0
  55. package/src/components/molecules/date-time.tsx +73 -0
  56. package/src/components/molecules/description-list.tsx +120 -0
  57. package/src/components/molecules/format-provider.tsx +46 -0
  58. package/src/components/molecules/id-chip.tsx +79 -0
  59. package/src/components/molecules/illustrations/access-denied.tsx +55 -0
  60. package/src/components/molecules/illustrations/empty-collection.tsx +66 -0
  61. package/src/components/molecules/illustrations/empty-database.tsx +44 -0
  62. package/src/components/molecules/illustrations/empty-media.tsx +68 -0
  63. package/src/components/molecules/illustrations/error.tsx +64 -0
  64. package/src/components/molecules/illustrations/first-run.tsx +55 -0
  65. package/src/components/molecules/illustrations/illustration-types.tsx +9 -0
  66. package/src/components/molecules/illustrations/no-activity.tsx +49 -0
  67. package/src/components/molecules/illustrations/no-deployments.tsx +49 -0
  68. package/src/components/molecules/illustrations/no-documents.tsx +67 -0
  69. package/src/components/molecules/illustrations/no-keys.tsx +50 -0
  70. package/src/components/molecules/illustrations/no-logs.tsx +53 -0
  71. package/src/components/molecules/illustrations/no-members.tsx +56 -0
  72. package/src/components/molecules/illustrations/no-notifications.tsx +52 -0
  73. package/src/components/molecules/illustrations/no-orders.tsx +48 -0
  74. package/src/components/molecules/illustrations/no-plugins.tsx +72 -0
  75. package/src/components/molecules/illustrations/no-results.tsx +69 -0
  76. package/src/components/molecules/illustrations/not-found.tsx +48 -0
  77. package/src/components/molecules/illustrations/offline.tsx +43 -0
  78. package/src/components/molecules/illustrations/pending-approval.tsx +56 -0
  79. package/src/components/molecules/illustrations/upload-dropzone.tsx +59 -0
  80. package/src/components/molecules/money.tsx +91 -0
  81. package/src/components/{custom → molecules}/multi-select.tsx +2 -2
  82. package/src/components/molecules/page-header.tsx +80 -0
  83. package/src/components/molecules/password-input.tsx +47 -0
  84. package/src/components/molecules/relative-time.tsx +81 -0
  85. package/src/components/molecules/stat-card.tsx +134 -0
  86. package/src/components/molecules/state-views/empty-state.tsx +75 -0
  87. package/src/components/molecules/state-views/error-state.tsx +91 -0
  88. package/src/components/molecules/state-views/loading-state.tsx +80 -0
  89. package/src/components/molecules/status-badge.tsx +100 -0
  90. package/src/hooks/use-copy.ts +51 -0
  91. package/src/lib/base-ui.ts +1 -1
  92. package/src/lib/format-relative.ts +41 -0
  93. package/src/lib/state-dictionary.ts +137 -0
  94. package/src/components/ui/checkbox.tsx +0 -30
  95. package/src/components/ui/skeleton.tsx +0 -13
  96. /package/src/components/{ui → atoms}/accordion.tsx +0 -0
  97. /package/src/components/{ui → atoms}/alert.tsx +0 -0
  98. /package/src/components/{ui → atoms}/aspect-ratio.tsx +0 -0
  99. /package/src/components/{ui → atoms}/avatar.tsx +0 -0
  100. /package/src/components/{ui → atoms}/breadcrumb.tsx +0 -0
  101. /package/src/components/{ui → atoms}/chart.tsx +0 -0
  102. /package/src/components/{ui → atoms}/collapsible.tsx +0 -0
  103. /package/src/components/{ui → atoms}/direction.tsx +0 -0
  104. /package/src/components/{ui → atoms}/kbd.tsx +0 -0
  105. /package/src/components/{ui → atoms}/label.tsx +0 -0
  106. /package/src/components/{ui → atoms}/progress.tsx +0 -0
  107. /package/src/components/{ui → atoms}/radio-group.tsx +0 -0
  108. /package/src/components/{ui → atoms}/resizable.tsx +0 -0
  109. /package/src/components/{ui → atoms}/scroll-area.tsx +0 -0
  110. /package/src/components/{ui → atoms}/separator.tsx +0 -0
  111. /package/src/components/{ui → atoms}/spinner.tsx +0 -0
  112. /package/src/components/{ui → atoms}/table.tsx +0 -0
@@ -0,0 +1,41 @@
1
+ const UNITS: Array<{ unit: Intl.RelativeTimeFormatUnit; ms: number }> = [
2
+ { unit: 'year', ms: 365 * 24 * 60 * 60 * 1000 },
3
+ { unit: 'month', ms: 30 * 24 * 60 * 60 * 1000 },
4
+ { unit: 'day', ms: 24 * 60 * 60 * 1000 },
5
+ { unit: 'hour', ms: 60 * 60 * 1000 },
6
+ { unit: 'minute', ms: 60 * 1000 },
7
+ { unit: 'second', ms: 1000 },
8
+ ];
9
+
10
+ interface FormatRelativeOptions {
11
+ locale?: string;
12
+ numeric?: 'auto' | 'always';
13
+ unitStyle?: Intl.RelativeTimeFormatOptions['style'];
14
+ }
15
+
16
+ /**
17
+ * Formats an instant relative to now, for non-React sites (table titles, CSV
18
+ * export, `<time title>`). Picks the largest unit the delta clears, then defers
19
+ * wording to Intl — signed input yields "in 3 days" / "2 hours ago" without a
20
+ * manual past/future branch.
21
+ *
22
+ * Unparseable input (an invalid date string) yields the zero-delta phrase —
23
+ * "now" under `numeric: 'auto'` — rather than throwing; validate upstream if
24
+ * you need to distinguish it (the `<RelativeTime>` molecule renders its
25
+ * `fallback` instead).
26
+ */
27
+ function formatRelative(
28
+ value: string | number | Date,
29
+ { locale, numeric = 'auto', unitStyle = 'long' }: FormatRelativeOptions = {},
30
+ ): string {
31
+ const date = value instanceof Date ? value : new Date(value);
32
+ const diff = date.getTime() - Date.now();
33
+ const abs = Math.abs(diff);
34
+ const rtf = new Intl.RelativeTimeFormat(locale, { numeric, style: unitStyle });
35
+ for (const { unit, ms } of UNITS) {
36
+ if (abs >= ms) return rtf.format(Math.round(diff / ms), unit);
37
+ }
38
+ return rtf.format(0, 'second');
39
+ }
40
+
41
+ export { formatRelative, type FormatRelativeOptions };
@@ -0,0 +1,137 @@
1
+ /**
2
+ * The state dictionary — the mechanism that makes the same entity state look
3
+ * the same in every Vendure surface (see CONTEXT.md → "State dictionary").
4
+ *
5
+ * This module ships the *contract*, not the app's states: the tone vocabulary,
6
+ * the typed declaration helper, the severity rollup, and one universal map for
7
+ * states every app shares (`commonStates`). Domain maps — deployment states,
8
+ * quote states, order states — live in their owning repo, declared with
9
+ * `defineStateEntries` and reviewed against the Storybook reference table.
10
+ * The ui package never learns app enums: the app owns what `DEPLOYING` means;
11
+ * the design system owns what `progress` looks like.
12
+ */
13
+
14
+ /**
15
+ * The semantic meaning a color expresses about a state. Tone ≠ token name:
16
+ * `critical` renders with the `destructive` slots and `progress` renders with
17
+ * the `neutral` slots plus an info-colored pulsing dot — see StatusBadge.
18
+ */
19
+ export type Tone = 'neutral' | 'info' | 'success' | 'warning' | 'critical' | 'progress';
20
+
21
+ export interface StateEntry {
22
+ tone: Tone;
23
+ defaultLabel: string;
24
+ }
25
+
26
+ /**
27
+ * A declared, exhaustiveness-checked state map plus lookups that tolerate open
28
+ * unions. `toneFor` / `labelFor` accept any string (states arrive from the wire
29
+ * as bare strings) and fall back to `neutral` / the raw state, warning once per
30
+ * unknown state in development so a typo surfaces without spamming a 200-row
31
+ * table.
32
+ */
33
+ export interface StateMap<S extends string> {
34
+ entries: Record<S, StateEntry>;
35
+ toneFor(state: string): Tone;
36
+ labelFor(state: string): string;
37
+ }
38
+
39
+ const isDev = process.env.NODE_ENV !== 'production';
40
+
41
+ /**
42
+ * Declare a domain's state→tone map. The `Record<S, StateEntry>` argument is
43
+ * exhaustive over `S`, so typing the call as `defineStateEntries<DeploymentStatus>`
44
+ * makes the compiler flag any enum member the map forgets. Lookups are
45
+ * case-insensitive because the same state ships as `RUNNING`, `running`, and
46
+ * `Running` across the estate.
47
+ */
48
+ export function defineStateEntries<S extends string>(entries: Record<S, StateEntry>): StateMap<S> {
49
+ // Case-insensitive index built once at declaration, not per lookup.
50
+ const index = new Map<string, StateEntry>();
51
+ for (const [state, entry] of Object.entries(entries) as [S, StateEntry][]) {
52
+ index.set(state.toLowerCase(), entry);
53
+ }
54
+
55
+ const warned = new Set<string>();
56
+ function resolve(state: string): StateEntry | undefined {
57
+ const key = state.toLowerCase();
58
+ const entry = index.get(key);
59
+ // Dedup on the lowercased key so casing variants of the same unknown state
60
+ // ("Foo" / "foo") warn once — matching the lookup semantics.
61
+ if (!entry && isDev && !warned.has(key)) {
62
+ warned.add(key);
63
+ console.warn(
64
+ `[state-dictionary] Unmapped state "${state}" — rendering as neutral. ` +
65
+ 'Add it to the domain map or check for a typo.',
66
+ );
67
+ }
68
+ return entry;
69
+ }
70
+
71
+ return {
72
+ entries,
73
+ toneFor: (state) => resolve(state)?.tone ?? 'neutral',
74
+ labelFor: (state) => resolve(state)?.defaultLabel ?? state,
75
+ };
76
+ }
77
+
78
+ /**
79
+ * The universal map — the only state strings this package ships. Every entry
80
+ * has one unambiguous tone regardless of domain.
81
+ *
82
+ * `pending` and `running` are deliberately absent: they have no single tone
83
+ * (principles 1 & 3 — a pending approval is `warning`, a queued job is
84
+ * `neutral`; a running job is `progress`, a running service is `success`), so
85
+ * every domain must decide them explicitly in its own map.
86
+ */
87
+ export const commonStates = defineStateEntries({
88
+ enabled: { tone: 'success', defaultLabel: 'Enabled' },
89
+ disabled: { tone: 'neutral', defaultLabel: 'Disabled' },
90
+ completed: { tone: 'success', defaultLabel: 'Completed' },
91
+ failed: { tone: 'critical', defaultLabel: 'Failed' },
92
+ cancelled: { tone: 'neutral', defaultLabel: 'Cancelled' },
93
+ expired: { tone: 'neutral', defaultLabel: 'Expired' },
94
+ draft: { tone: 'neutral', defaultLabel: 'Draft' },
95
+ suspended: { tone: 'warning', defaultLabel: 'Suspended' },
96
+ approved: { tone: 'success', defaultLabel: 'Approved' },
97
+ rejected: { tone: 'critical', defaultLabel: 'Rejected' },
98
+ error: { tone: 'critical', defaultLabel: 'Error' },
99
+ degraded: { tone: 'warning', defaultLabel: 'Degraded' },
100
+ unknown: { tone: 'neutral', defaultLabel: 'Unknown' },
101
+ });
102
+
103
+ /**
104
+ * Severity ordering, `neutral` (0) → `critical` (5). Used by `maxTone` to roll
105
+ * a set of child states up to a single parent tone (a "worst-of" rollup for
106
+ * parent status rows, e.g. an environment summarizing its deployments).
107
+ */
108
+ export const TONE_SEVERITY = {
109
+ neutral: 0,
110
+ success: 1,
111
+ info: 2,
112
+ progress: 3,
113
+ warning: 4,
114
+ critical: 5,
115
+ } satisfies Record<Tone, number>;
116
+
117
+ /**
118
+ * The worst-of rollup: given child tones, return the most severe. An empty call
119
+ * rolls up to `neutral` (nothing to report).
120
+ */
121
+ export function maxTone(...tones: Tone[]): Tone {
122
+ let worst: Tone = 'neutral';
123
+ for (const tone of tones) {
124
+ if (TONE_SEVERITY[tone] > TONE_SEVERITY[worst]) {
125
+ worst = tone;
126
+ }
127
+ }
128
+ return worst;
129
+ }
130
+
131
+ /**
132
+ * `progress` states are exactly the "poll me" states — the system is working
133
+ * right now and the value will change on its own. A polling loop keys on this.
134
+ */
135
+ export function isProgressTone(tone: Tone): boolean {
136
+ return tone === 'progress';
137
+ }
@@ -1,30 +0,0 @@
1
- "use client"
2
-
3
- import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox"
4
-
5
- import { cn } from "@vendure-io/ui/lib/utils"
6
- import { CheckIcon } from "lucide-react"
7
-
8
- // [vendure] shadow removed for flat look — restore if overwritten by shadcn upgrade
9
- function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) {
10
- return (
11
- <CheckboxPrimitive.Root
12
- data-slot="checkbox"
13
- className={cn(
14
- "border-input dark:bg-input/30 data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-checked:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 peer relative flex size-4 shrink-0 items-center justify-center rounded-[4px] border transition-shadow outline-none group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:ring-3 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3",
15
- className
16
- )}
17
- {...props}
18
- >
19
- <CheckboxPrimitive.Indicator
20
- data-slot="checkbox-indicator"
21
- className="grid place-content-center text-current transition-none [&>svg]:size-3.5"
22
- >
23
- <CheckIcon
24
- />
25
- </CheckboxPrimitive.Indicator>
26
- </CheckboxPrimitive.Root>
27
- )
28
- }
29
-
30
- export { Checkbox }
@@ -1,13 +0,0 @@
1
- import { cn } from "@vendure-io/ui/lib/utils"
2
-
3
- function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
4
- return (
5
- <div
6
- data-slot="skeleton"
7
- className={cn("bg-muted animate-pulse rounded-md", className)}
8
- {...props}
9
- />
10
- )
11
- }
12
-
13
- export { Skeleton }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes