@olwiba/ui 0.2.25 → 0.2.26

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 (202) hide show
  1. package/content/docs/app/app-shell.mdx +71 -0
  2. package/content/docs/app/auth-section.mdx +73 -0
  3. package/content/docs/app/billing-panel.mdx +38 -0
  4. package/content/docs/app/empty-state.mdx +47 -0
  5. package/content/docs/app/error-page.mdx +31 -0
  6. package/content/docs/app/index.mdx +13 -0
  7. package/content/docs/app/meta.json +4 -0
  8. package/content/docs/app/onboarding-wizard.mdx +41 -0
  9. package/content/docs/app/settings-section.mdx +37 -0
  10. package/content/docs/app/team-members-panel.mdx +35 -0
  11. package/content/docs/app/update-banner.mdx +43 -0
  12. package/content/docs/components/activity-feed.mdx +42 -0
  13. package/content/docs/components/chart.mdx +47 -0
  14. package/content/docs/components/command-menu.mdx +41 -0
  15. package/content/docs/components/confirm-dialog.mdx +26 -0
  16. package/content/docs/components/context-menu.mdx +26 -0
  17. package/content/docs/components/data-table.mdx +40 -0
  18. package/content/docs/components/dock.mdx +26 -0
  19. package/content/docs/components/feature-card.mdx +26 -0
  20. package/content/docs/components/file-upload.mdx +35 -0
  21. package/content/docs/components/full-page-spinner.mdx +44 -0
  22. package/content/docs/components/glass-card.mdx +26 -0
  23. package/content/docs/components/image-card.mdx +26 -0
  24. package/content/docs/components/index.mdx +49 -0
  25. package/content/docs/components/load-more.mdx +34 -0
  26. package/content/docs/components/meta.json +35 -0
  27. package/content/docs/components/notifications-popover.mdx +34 -0
  28. package/content/docs/components/notify.mdx +40 -0
  29. package/content/docs/components/olwiba-ui-context.mdx +69 -0
  30. package/content/docs/components/page-header.mdx +42 -0
  31. package/content/docs/components/pricing-card.mdx +26 -0
  32. package/content/docs/components/register-hotkeys.mdx +50 -0
  33. package/content/docs/components/root-error-fallback.mdx +39 -0
  34. package/content/docs/components/spotlight.mdx +26 -0
  35. package/content/docs/components/stat-card.mdx +26 -0
  36. package/content/docs/components/suspensed.mdx +42 -0
  37. package/content/docs/components/testimonial-card.mdx +26 -0
  38. package/content/docs/components/theme-color-updater.mdx +40 -0
  39. package/content/docs/components/theme-switch-minimal.mdx +32 -0
  40. package/content/docs/components/version-banner.mdx +44 -0
  41. package/content/docs/email/action-email.mdx +62 -0
  42. package/content/docs/email/email-layout.mdx +58 -0
  43. package/content/docs/email/email-link-fallback.mdx +46 -0
  44. package/content/docs/email/index.mdx +32 -0
  45. package/content/docs/email/meta.json +10 -0
  46. package/content/docs/email/notice-email.mdx +51 -0
  47. package/content/docs/hooks/index.mdx +21 -0
  48. package/content/docs/hooks/meta.json +16 -0
  49. package/content/docs/hooks/use-confirm.mdx +81 -0
  50. package/content/docs/hooks/use-controlled-open.mdx +53 -0
  51. package/content/docs/hooks/use-copy-to-clipboard.mdx +26 -0
  52. package/content/docs/hooks/use-debounce.mdx +26 -0
  53. package/content/docs/hooks/use-intersection-observer.mdx +26 -0
  54. package/content/docs/hooks/use-local-storage.mdx +26 -0
  55. package/content/docs/hooks/use-media-query.mdx +26 -0
  56. package/content/docs/hooks/use-mounted.mdx +39 -0
  57. package/content/docs/hooks/use-pagination.mdx +26 -0
  58. package/content/docs/hooks/use-scrolled-past.mdx +44 -0
  59. package/content/docs/index.mdx +68 -0
  60. package/content/docs/marketing/contact-section.mdx +16 -0
  61. package/content/docs/marketing/cta-section.mdx +31 -0
  62. package/content/docs/marketing/faq-section.mdx +16 -0
  63. package/content/docs/marketing/features-section.mdx +23 -0
  64. package/content/docs/marketing/footer.mdx +16 -0
  65. package/content/docs/marketing/hero-section.mdx +16 -0
  66. package/content/docs/marketing/index.mdx +22 -0
  67. package/content/docs/marketing/meta.json +20 -0
  68. package/content/docs/marketing/navbar.mdx +16 -0
  69. package/content/docs/marketing/newsletter-section.mdx +16 -0
  70. package/content/docs/marketing/post-list.mdx +32 -0
  71. package/content/docs/marketing/pricing-section.mdx +16 -0
  72. package/content/docs/marketing/stats-section.mdx +16 -0
  73. package/content/docs/marketing/steps-section.mdx +60 -0
  74. package/content/docs/marketing/team-section.mdx +16 -0
  75. package/content/docs/marketing/testimonials-section.mdx +16 -0
  76. package/content/docs/mechanics/carousel.mdx +42 -0
  77. package/content/docs/mechanics/index.mdx +11 -0
  78. package/content/docs/mechanics/meta.json +4 -0
  79. package/content/docs/mechanics/sortable.mdx +44 -0
  80. package/content/docs/meta.json +4 -0
  81. package/content/docs/motion/animated-swap.mdx +75 -0
  82. package/content/docs/motion/count-up.mdx +31 -0
  83. package/content/docs/motion/fade-in.mdx +34 -0
  84. package/content/docs/motion/index.mdx +12 -0
  85. package/content/docs/motion/meta.json +4 -0
  86. package/content/docs/motion/page-transition.mdx +29 -0
  87. package/content/docs/motion/stagger-children.mdx +31 -0
  88. package/content/docs/overlays/index.mdx +9 -0
  89. package/content/docs/overlays/meta.json +4 -0
  90. package/content/docs/overlays/overlay.mdx +60 -0
  91. package/content/docs/overlays/underlay.mdx +56 -0
  92. package/dist/email/index.d.ts +8 -1
  93. package/dist/email/index.js +3 -3
  94. package/dist/email/index.js.map +1 -1
  95. package/docs-manifest.json +253 -0
  96. package/package.json +16 -8
  97. package/site/demos/action-email.tsx +6 -0
  98. package/site/demos/activity-feed.tsx +42 -0
  99. package/site/demos/animated-swap.tsx +42 -0
  100. package/site/demos/app-shell.tsx +47 -0
  101. package/site/demos/app-ui-page.tsx +9 -0
  102. package/site/demos/application-sidebar-block.tsx +9 -0
  103. package/site/demos/auth-section.tsx +15 -0
  104. package/site/demos/auth-split-block.tsx +9 -0
  105. package/site/demos/billing-page.tsx +157 -0
  106. package/site/demos/billing-panel.tsx +26 -0
  107. package/site/demos/carousel.tsx +34 -0
  108. package/site/demos/chart.tsx +55 -0
  109. package/site/demos/command-menu.tsx +39 -0
  110. package/site/demos/confirm-dialog.tsx +27 -0
  111. package/site/demos/contact-form-block.tsx +3 -0
  112. package/site/demos/contact-section.tsx +2 -0
  113. package/site/demos/context-menu.tsx +30 -0
  114. package/site/demos/count-up.tsx +23 -0
  115. package/site/demos/cta-section.tsx +6 -0
  116. package/site/demos/dashboard-overview-block.tsx +9 -0
  117. package/site/demos/dashboard-shell-block.tsx +10 -0
  118. package/site/demos/data-table.tsx +47 -0
  119. package/site/demos/demo-brand.tsx +23 -0
  120. package/site/demos/dock.tsx +20 -0
  121. package/site/demos/document-sidebar-block.tsx +9 -0
  122. package/site/demos/email-examples.tsx +58 -0
  123. package/site/demos/email-frame.tsx +50 -0
  124. package/site/demos/email-layout.tsx +6 -0
  125. package/site/demos/email-link-fallback.tsx +6 -0
  126. package/site/demos/email-previews.generated.ts +11 -0
  127. package/site/demos/empty-state-block.tsx +3 -0
  128. package/site/demos/empty-state.tsx +23 -0
  129. package/site/demos/error-page.tsx +5 -0
  130. package/site/demos/fade-in.tsx +18 -0
  131. package/site/demos/faq-section.tsx +6 -0
  132. package/site/demos/feature-card.tsx +13 -0
  133. package/site/demos/features-section.tsx +6 -0
  134. package/site/demos/file-upload.tsx +16 -0
  135. package/site/demos/footer.tsx +42 -0
  136. package/site/demos/full-page-spinner.tsx +9 -0
  137. package/site/demos/glass-card.tsx +14 -0
  138. package/site/demos/glow-effect.tsx +3 -0
  139. package/site/demos/gradient-background.tsx +3 -0
  140. package/site/demos/grid-pattern.tsx +3 -0
  141. package/site/demos/hero-section.tsx +6 -0
  142. package/site/demos/image-card.tsx +17 -0
  143. package/site/demos/login-block.tsx +9 -0
  144. package/site/demos/marketing-cta-block.tsx +3 -0
  145. package/site/demos/marketing-demo-data.tsx +114 -0
  146. package/site/demos/marketing-faq-block.tsx +3 -0
  147. package/site/demos/marketing-features-block.tsx +3 -0
  148. package/site/demos/marketing-footer-block.tsx +3 -0
  149. package/site/demos/marketing-hero-block.tsx +10 -0
  150. package/site/demos/marketing-nav-block.tsx +3 -0
  151. package/site/demos/marketing-newsletter-block.tsx +3 -0
  152. package/site/demos/marketing-page.tsx +52 -0
  153. package/site/demos/marketing-pricing-block.tsx +3 -0
  154. package/site/demos/marketing-stats-block.tsx +3 -0
  155. package/site/demos/marketing-team-block.tsx +3 -0
  156. package/site/demos/marketing-testimonials-block.tsx +3 -0
  157. package/site/demos/navbar.tsx +32 -0
  158. package/site/demos/newsletter-section.tsx +2 -0
  159. package/site/demos/noise-overlay.tsx +3 -0
  160. package/site/demos/not-found-block.tsx +3 -0
  161. package/site/demos/notice-email.tsx +6 -0
  162. package/site/demos/notifications-popover.tsx +27 -0
  163. package/site/demos/notify.tsx +44 -0
  164. package/site/demos/olwiba-ui-context.tsx +51 -0
  165. package/site/demos/onboarding-block.tsx +3 -0
  166. package/site/demos/onboarding-wizard.tsx +43 -0
  167. package/site/demos/overlay.tsx +72 -0
  168. package/site/demos/page-header.tsx +15 -0
  169. package/site/demos/page-transition.tsx +34 -0
  170. package/site/demos/post-list.tsx +24 -0
  171. package/site/demos/pricing-card.tsx +27 -0
  172. package/site/demos/pricing-section.tsx +6 -0
  173. package/site/demos/register-hotkeys.tsx +39 -0
  174. package/site/demos/root-error-fallback.tsx +28 -0
  175. package/site/demos/settings-layout-block.tsx +3 -0
  176. package/site/demos/settings-section.tsx +22 -0
  177. package/site/demos/sign-in-page.tsx +10 -0
  178. package/site/demos/sortable.tsx +30 -0
  179. package/site/demos/spotlight.tsx +29 -0
  180. package/site/demos/stagger-children.tsx +16 -0
  181. package/site/demos/stat-card.tsx +12 -0
  182. package/site/demos/stats-section.tsx +6 -0
  183. package/site/demos/steps-section.tsx +66 -0
  184. package/site/demos/suspensed.tsx +31 -0
  185. package/site/demos/team-members-panel.tsx +26 -0
  186. package/site/demos/team-section.tsx +2 -0
  187. package/site/demos/testimonial-card.tsx +17 -0
  188. package/site/demos/testimonials-section.tsx +6 -0
  189. package/site/demos/theme-color-updater.tsx +32 -0
  190. package/site/demos/theme-switch-minimal.tsx +10 -0
  191. package/site/demos/underlay.tsx +74 -0
  192. package/site/demos/update-banner.tsx +31 -0
  193. package/site/demos/use-copy-to-clipboard.tsx +21 -0
  194. package/site/demos/use-debounce.tsx +25 -0
  195. package/site/demos/use-intersection-observer.tsx +32 -0
  196. package/site/demos/use-local-storage.tsx +22 -0
  197. package/site/demos/use-media-query.tsx +31 -0
  198. package/site/demos/use-pagination.tsx +31 -0
  199. package/site/demos/user-profile-block.tsx +3 -0
  200. package/site/demos/version-banner.tsx +27 -0
  201. package/src/email/EmailLinkFallback.tsx +7 -1
  202. package/src/email/index.ts +3 -0
@@ -0,0 +1,74 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import { Underlay, type UnderlayVariant } from '@olwiba/ui';
5
+ import { DemoControls, useUsageCode } from '~/components/ComponentPreview';
6
+ import { Button, Badge } from '@olwiba/cn';
7
+
8
+ const variants: UnderlayVariant[] = ['dots', 'lines', 'cross', 'glow'];
9
+
10
+ function MockContent() {
11
+ return (
12
+ <div className="relative z-10 flex w-full flex-col gap-4 p-8">
13
+ <div className="flex items-center justify-between">
14
+ <div>
15
+ <p className="text-xs text-muted-foreground">Total revenue</p>
16
+ <p className="text-2xl font-semibold">$48,295</p>
17
+ </div>
18
+ <Badge variant="secondary">+12.4%</Badge>
19
+ </div>
20
+ <div className="flex gap-3">
21
+ {['Mon', 'Tue', 'Wed', 'Thu', 'Fri'].map((d, i) => (
22
+ <div key={d} className="flex flex-1 flex-col items-center gap-1">
23
+ <div
24
+ className="w-full rounded bg-primary/20"
25
+ style={{ height: `${[40, 65, 50, 80, 55][i]}px` }}
26
+ />
27
+ <span className="text-[10px] text-muted-foreground">{d}</span>
28
+ </div>
29
+ ))}
30
+ </div>
31
+ </div>
32
+ );
33
+ }
34
+
35
+ export default function UnderlayDemo() {
36
+ const [variant, setVariant] = React.useState<UnderlayVariant>('dots');
37
+
38
+ const extraProps = variant === 'glow' ? ` size={320} followCursor blur="xl"` : '';
39
+
40
+ useUsageCode(
41
+ `<div className="relative overflow-hidden rounded-xl">\n <Underlay variant="${variant}"${extraProps} />\n <div className="relative z-10 p-8">Content</div>\n</div>`,
42
+ );
43
+
44
+ return (
45
+ <>
46
+ <div className="relative w-full overflow-hidden bg-card">
47
+ <Underlay
48
+ key={variant}
49
+ variant={variant}
50
+ {...(variant === 'glow' ? { size: 360, followCursor: true, blur: 'xl' as const } : {})}
51
+ />
52
+ <MockContent />
53
+ </div>
54
+
55
+ <DemoControls>
56
+ <div className="space-y-1.5">
57
+ <span className="text-xs font-medium text-muted-foreground">Variant</span>
58
+ <div className="flex flex-wrap gap-1.5">
59
+ {variants.map((v) => (
60
+ <Button
61
+ key={v}
62
+ variant={variant === v ? 'default' : 'secondary'}
63
+ size="sm"
64
+ onClick={() => setVariant(v)}
65
+ >
66
+ {v}
67
+ </Button>
68
+ ))}
69
+ </div>
70
+ </div>
71
+ </DemoControls>
72
+ </>
73
+ );
74
+ }
@@ -0,0 +1,31 @@
1
+ import { UpdateBanner } from '@olwiba/ui';
2
+ import * as React from 'react';
3
+
4
+ export default function UpdateBannerDemo() {
5
+ const [show, setShow] = React.useState(true);
6
+
7
+ return (
8
+ <div className="relative min-h-[260px] overflow-hidden rounded-2xl border bg-background p-6">
9
+ <p className="text-sm text-muted-foreground">
10
+ The banner polls a version endpoint and slides in when the deployed version no longer matches
11
+ the running bundle. This demo forces it visible.
12
+ </p>
13
+ <button
14
+ className="mt-4 rounded-md border px-3 py-1.5 text-sm"
15
+ onClick={() => setShow((value) => !value)}
16
+ >
17
+ Toggle banner
18
+ </button>
19
+
20
+ {/* Demo container overrides the fixed positioning context */}
21
+ <div className="absolute inset-0 [&>div]:absolute [&>div]:top-4">
22
+ <UpdateBanner
23
+ forceShow={show}
24
+ currentVersion="dev-local"
25
+ fetchVersion={async () => ({ version: 'dev-local' })}
26
+ onRefresh={() => setShow(false)}
27
+ />
28
+ </div>
29
+ </div>
30
+ );
31
+ }
@@ -0,0 +1,21 @@
1
+ 'use client';
2
+ import { useCopyToClipboard } from '@olwiba/ui';
3
+ import { Button } from '@olwiba/cn';
4
+ import { Check, Copy } from 'lucide-react';
5
+
6
+ const snippet = 'bun add @olwiba/ui @olwiba/cn';
7
+
8
+ export default function Demo() {
9
+ const [copied, copy] = useCopyToClipboard();
10
+
11
+ return (
12
+ <div className="flex items-center justify-center p-8">
13
+ <div className="flex items-center gap-2 rounded-xl border bg-muted px-4 py-2">
14
+ <code className="font-mono text-sm">{snippet}</code>
15
+ <Button size="icon" variant="ghost" className="size-7 shrink-0" onClick={() => copy(snippet)}>
16
+ {copied ? <Check className="size-3.5 text-emerald-500" /> : <Copy className="size-3.5" />}
17
+ </Button>
18
+ </div>
19
+ </div>
20
+ );
21
+ }
@@ -0,0 +1,25 @@
1
+ 'use client';
2
+ import * as React from 'react';
3
+ import { useDebounce } from '@olwiba/ui';
4
+ import { Input } from '@olwiba/cn';
5
+
6
+ export default function Demo() {
7
+ const [value, setValue] = React.useState('');
8
+ const debounced = useDebounce(value, 400);
9
+
10
+ return (
11
+ <div className="mx-auto flex max-w-sm flex-col gap-4 p-8">
12
+ <Input placeholder="Type something..." value={value} onChange={(e) => setValue(e.target.value)} />
13
+ <div className="space-y-1 rounded-xl border bg-card p-4 text-sm">
14
+ <div className="flex justify-between gap-4">
15
+ <span className="text-muted-foreground">Live</span>
16
+ <span className="font-mono">{value || '—'}</span>
17
+ </div>
18
+ <div className="flex justify-between gap-4">
19
+ <span className="text-muted-foreground">Debounced (400ms)</span>
20
+ <span className="font-mono">{debounced || '—'}</span>
21
+ </div>
22
+ </div>
23
+ </div>
24
+ );
25
+ }
@@ -0,0 +1,32 @@
1
+ 'use client';
2
+ import * as React from 'react';
3
+ import { useIntersectionObserver } from '@olwiba/ui';
4
+ import { cn } from '@olwiba/cn';
5
+
6
+ export default function Demo() {
7
+ const [ref, isVisible] = useIntersectionObserver({ threshold: 0.5 });
8
+
9
+ return (
10
+ <div className="space-y-3 p-6">
11
+ <div className="text-sm text-center">
12
+ Status:{' '}
13
+ <strong className={isVisible ? 'text-emerald-500' : 'text-muted-foreground'}>
14
+ {isVisible ? 'intersecting' : 'not intersecting'}
15
+ </strong>
16
+ </div>
17
+ <div className="h-44 overflow-y-auto rounded-xl border">
18
+ <div className="flex h-16 items-center justify-center text-xs text-muted-foreground">↓ scroll</div>
19
+ <div
20
+ ref={ref as unknown as React.RefObject<HTMLDivElement>}
21
+ className={cn(
22
+ 'mx-4 rounded-xl border p-4 text-sm text-center transition-all duration-500',
23
+ isVisible ? 'border-primary bg-primary/5 text-primary' : 'bg-muted text-muted-foreground',
24
+ )}
25
+ >
26
+ Observed element
27
+ </div>
28
+ <div className="flex h-16 items-center justify-center text-xs text-muted-foreground">↑ scroll</div>
29
+ </div>
30
+ </div>
31
+ );
32
+ }
@@ -0,0 +1,22 @@
1
+ 'use client';
2
+ import { useLocalStorage } from '@olwiba/ui';
3
+ import { Button } from '@olwiba/cn';
4
+
5
+ export default function Demo() {
6
+ const [count, setCount] = useLocalStorage('demo-count', 0);
7
+
8
+ return (
9
+ <div className="flex flex-col items-center gap-4 p-8">
10
+ <div className="w-40 rounded-2xl border bg-card p-6 text-center">
11
+ <div className="text-4xl font-bold tabular-nums">{count}</div>
12
+ <div className="mt-1 text-xs text-muted-foreground">persisted in localStorage</div>
13
+ </div>
14
+ <div className="flex gap-2">
15
+ <Button variant="outline" size="sm" onClick={() => setCount(count - 1)}>−</Button>
16
+ <Button variant="outline" size="sm" onClick={() => setCount(0)}>Reset</Button>
17
+ <Button size="sm" onClick={() => setCount(count + 1)}>+</Button>
18
+ </div>
19
+ <p className="text-xs text-muted-foreground">Value persists across page reloads.</p>
20
+ </div>
21
+ );
22
+ }
@@ -0,0 +1,31 @@
1
+ 'use client';
2
+ import { useMediaQuery } from '@olwiba/ui';
3
+ import { cn } from '@olwiba/cn';
4
+
5
+ const breakpoints = [
6
+ { label: 'sm', query: '(min-width: 640px)' },
7
+ { label: 'md', query: '(min-width: 768px)' },
8
+ { label: 'lg', query: '(min-width: 1024px)' },
9
+ { label: 'xl', query: '(min-width: 1280px)' },
10
+ { label: 'dark', query: '(prefers-color-scheme: dark)' },
11
+ ];
12
+
13
+ function Row({ label, query }: { label: string; query: string }) {
14
+ const matches = useMediaQuery(query);
15
+ return (
16
+ <div className={cn('flex items-center justify-between rounded-xl border px-4 py-2.5 text-sm transition-colors', matches ? 'border-primary bg-primary/5' : 'bg-card')}>
17
+ <code className="font-mono text-xs">{query}</code>
18
+ <span className={cn('text-xs font-medium', matches ? 'text-primary' : 'text-muted-foreground')}>
19
+ {matches ? 'matches' : 'no match'}
20
+ </span>
21
+ </div>
22
+ );
23
+ }
24
+
25
+ export default function Demo() {
26
+ return (
27
+ <div className="flex flex-col gap-2 p-6">
28
+ {breakpoints.map((bp) => <Row key={bp.label} {...bp} />)}
29
+ </div>
30
+ );
31
+ }
@@ -0,0 +1,31 @@
1
+ 'use client';
2
+ import { usePagination } from '@olwiba/ui';
3
+ import { Button } from '@olwiba/cn';
4
+
5
+ const ALL_ITEMS = Array.from({ length: 23 }, (_, i) => `Item ${i + 1}`);
6
+
7
+ export default function Demo() {
8
+ const { page, totalPages, offset, pageSize, hasPrev, hasNext, prev, next, goTo } = usePagination(ALL_ITEMS.length, 5);
9
+ const pageItems = ALL_ITEMS.slice(offset, offset + pageSize);
10
+
11
+ return (
12
+ <div className="flex flex-col gap-4 p-6">
13
+ <div className="space-y-1.5">
14
+ {pageItems.map((item) => (
15
+ <div key={item} className="rounded-xl border bg-card px-4 py-2.5 text-sm">{item}</div>
16
+ ))}
17
+ </div>
18
+ <div className="flex items-center justify-between gap-2">
19
+ <Button variant="outline" size="sm" disabled={!hasPrev} onClick={prev}>Previous</Button>
20
+ <div className="flex gap-1">
21
+ {Array.from({ length: totalPages }, (_, i) => (
22
+ <Button key={i} size="sm" variant={page === i + 1 ? 'default' : 'outline'} className="size-8 p-0" onClick={() => goTo(i + 1)}>
23
+ {i + 1}
24
+ </Button>
25
+ ))}
26
+ </div>
27
+ <Button variant="outline" size="sm" disabled={!hasNext} onClick={next}>Next</Button>
28
+ </div>
29
+ </div>
30
+ );
31
+ }
@@ -0,0 +1,3 @@
1
+ export default function Demo() {
2
+ return <div className="p-8 text-muted-foreground text-sm">UserProfileBlock — coming soon</div>;
3
+ }
@@ -0,0 +1,27 @@
1
+ import { VersionBanner } from '@olwiba/ui';
2
+ import * as React from 'react';
3
+
4
+ export default function VersionBannerDemo() {
5
+ const [show, setShow] = React.useState(true);
6
+
7
+ return (
8
+ <div className="w-full max-w-lg rounded-lg border overflow-hidden">
9
+ {show ? (
10
+ <VersionBanner
11
+ version="2.1.0"
12
+ message="New components available — see what's changed."
13
+ onDismiss={() => setShow(false)}
14
+ />
15
+ ) : (
16
+ <div className="flex items-center justify-center py-4">
17
+ <button
18
+ onClick={() => setShow(true)}
19
+ className="text-sm text-muted-foreground underline"
20
+ >
21
+ Reset demo
22
+ </button>
23
+ </div>
24
+ )}
25
+ </div>
26
+ );
27
+ }
@@ -17,7 +17,13 @@ export function EmailLinkFallback({
17
17
  {label}
18
18
  </EmailText>
19
19
  <EmailText style={{ margin: 0 }}>
20
- <EmailLink href={actionUrl} brandColor={brandColor}>
20
+ {/*
21
+ Set through `style` rather than CN's `brandColor` prop, which this
22
+ package's pinned @olwiba/cn does not have yet. CN applies that prop as
23
+ `color` anyway, and `style` wins over its default, so the result is
24
+ identical without coupling this file to a release.
25
+ */}
26
+ <EmailLink href={actionUrl} style={brandColor ? { color: brandColor } : undefined}>
21
27
  {actionUrl}
22
28
  </EmailLink>
23
29
  </EmailText>
@@ -1,3 +1,6 @@
1
1
  export { ActionEmail, type ActionEmailProps } from './ActionEmail';
2
2
  export { NoticeEmail, type NoticeEmailProps } from './NoticeEmail';
3
3
  export { EmailLayout, type EmailLayoutProps } from './EmailLayout';
4
+ // Used internally by ActionEmail and useful on its own: any email with a button
5
+ // wants a copyable link for the clients that strip them.
6
+ export { EmailLinkFallback, type EmailLinkFallbackProps } from './EmailLinkFallback';