@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.
- package/content/docs/app/app-shell.mdx +71 -0
- package/content/docs/app/auth-section.mdx +73 -0
- package/content/docs/app/billing-panel.mdx +38 -0
- package/content/docs/app/empty-state.mdx +47 -0
- package/content/docs/app/error-page.mdx +31 -0
- package/content/docs/app/index.mdx +13 -0
- package/content/docs/app/meta.json +4 -0
- package/content/docs/app/onboarding-wizard.mdx +41 -0
- package/content/docs/app/settings-section.mdx +37 -0
- package/content/docs/app/team-members-panel.mdx +35 -0
- package/content/docs/app/update-banner.mdx +43 -0
- package/content/docs/components/activity-feed.mdx +42 -0
- package/content/docs/components/chart.mdx +47 -0
- package/content/docs/components/command-menu.mdx +41 -0
- package/content/docs/components/confirm-dialog.mdx +26 -0
- package/content/docs/components/context-menu.mdx +26 -0
- package/content/docs/components/data-table.mdx +40 -0
- package/content/docs/components/dock.mdx +26 -0
- package/content/docs/components/feature-card.mdx +26 -0
- package/content/docs/components/file-upload.mdx +35 -0
- package/content/docs/components/full-page-spinner.mdx +44 -0
- package/content/docs/components/glass-card.mdx +26 -0
- package/content/docs/components/image-card.mdx +26 -0
- package/content/docs/components/index.mdx +49 -0
- package/content/docs/components/load-more.mdx +34 -0
- package/content/docs/components/meta.json +35 -0
- package/content/docs/components/notifications-popover.mdx +34 -0
- package/content/docs/components/notify.mdx +40 -0
- package/content/docs/components/olwiba-ui-context.mdx +69 -0
- package/content/docs/components/page-header.mdx +42 -0
- package/content/docs/components/pricing-card.mdx +26 -0
- package/content/docs/components/register-hotkeys.mdx +50 -0
- package/content/docs/components/root-error-fallback.mdx +39 -0
- package/content/docs/components/spotlight.mdx +26 -0
- package/content/docs/components/stat-card.mdx +26 -0
- package/content/docs/components/suspensed.mdx +42 -0
- package/content/docs/components/testimonial-card.mdx +26 -0
- package/content/docs/components/theme-color-updater.mdx +40 -0
- package/content/docs/components/theme-switch-minimal.mdx +32 -0
- package/content/docs/components/version-banner.mdx +44 -0
- package/content/docs/email/action-email.mdx +62 -0
- package/content/docs/email/email-layout.mdx +58 -0
- package/content/docs/email/email-link-fallback.mdx +46 -0
- package/content/docs/email/index.mdx +32 -0
- package/content/docs/email/meta.json +10 -0
- package/content/docs/email/notice-email.mdx +51 -0
- package/content/docs/hooks/index.mdx +21 -0
- package/content/docs/hooks/meta.json +16 -0
- package/content/docs/hooks/use-confirm.mdx +81 -0
- package/content/docs/hooks/use-controlled-open.mdx +53 -0
- package/content/docs/hooks/use-copy-to-clipboard.mdx +26 -0
- package/content/docs/hooks/use-debounce.mdx +26 -0
- package/content/docs/hooks/use-intersection-observer.mdx +26 -0
- package/content/docs/hooks/use-local-storage.mdx +26 -0
- package/content/docs/hooks/use-media-query.mdx +26 -0
- package/content/docs/hooks/use-mounted.mdx +39 -0
- package/content/docs/hooks/use-pagination.mdx +26 -0
- package/content/docs/hooks/use-scrolled-past.mdx +44 -0
- package/content/docs/index.mdx +68 -0
- package/content/docs/marketing/contact-section.mdx +16 -0
- package/content/docs/marketing/cta-section.mdx +31 -0
- package/content/docs/marketing/faq-section.mdx +16 -0
- package/content/docs/marketing/features-section.mdx +23 -0
- package/content/docs/marketing/footer.mdx +16 -0
- package/content/docs/marketing/hero-section.mdx +16 -0
- package/content/docs/marketing/index.mdx +22 -0
- package/content/docs/marketing/meta.json +20 -0
- package/content/docs/marketing/navbar.mdx +16 -0
- package/content/docs/marketing/newsletter-section.mdx +16 -0
- package/content/docs/marketing/post-list.mdx +32 -0
- package/content/docs/marketing/pricing-section.mdx +16 -0
- package/content/docs/marketing/stats-section.mdx +16 -0
- package/content/docs/marketing/steps-section.mdx +60 -0
- package/content/docs/marketing/team-section.mdx +16 -0
- package/content/docs/marketing/testimonials-section.mdx +16 -0
- package/content/docs/mechanics/carousel.mdx +42 -0
- package/content/docs/mechanics/index.mdx +11 -0
- package/content/docs/mechanics/meta.json +4 -0
- package/content/docs/mechanics/sortable.mdx +44 -0
- package/content/docs/meta.json +4 -0
- package/content/docs/motion/animated-swap.mdx +75 -0
- package/content/docs/motion/count-up.mdx +31 -0
- package/content/docs/motion/fade-in.mdx +34 -0
- package/content/docs/motion/index.mdx +12 -0
- package/content/docs/motion/meta.json +4 -0
- package/content/docs/motion/page-transition.mdx +29 -0
- package/content/docs/motion/stagger-children.mdx +31 -0
- package/content/docs/overlays/index.mdx +9 -0
- package/content/docs/overlays/meta.json +4 -0
- package/content/docs/overlays/overlay.mdx +60 -0
- package/content/docs/overlays/underlay.mdx +56 -0
- package/dist/email/index.d.ts +8 -1
- package/dist/email/index.js +3 -3
- package/dist/email/index.js.map +1 -1
- package/docs-manifest.json +253 -0
- package/package.json +16 -8
- package/site/demos/action-email.tsx +6 -0
- package/site/demos/activity-feed.tsx +42 -0
- package/site/demos/animated-swap.tsx +42 -0
- package/site/demos/app-shell.tsx +47 -0
- package/site/demos/app-ui-page.tsx +9 -0
- package/site/demos/application-sidebar-block.tsx +9 -0
- package/site/demos/auth-section.tsx +15 -0
- package/site/demos/auth-split-block.tsx +9 -0
- package/site/demos/billing-page.tsx +157 -0
- package/site/demos/billing-panel.tsx +26 -0
- package/site/demos/carousel.tsx +34 -0
- package/site/demos/chart.tsx +55 -0
- package/site/demos/command-menu.tsx +39 -0
- package/site/demos/confirm-dialog.tsx +27 -0
- package/site/demos/contact-form-block.tsx +3 -0
- package/site/demos/contact-section.tsx +2 -0
- package/site/demos/context-menu.tsx +30 -0
- package/site/demos/count-up.tsx +23 -0
- package/site/demos/cta-section.tsx +6 -0
- package/site/demos/dashboard-overview-block.tsx +9 -0
- package/site/demos/dashboard-shell-block.tsx +10 -0
- package/site/demos/data-table.tsx +47 -0
- package/site/demos/demo-brand.tsx +23 -0
- package/site/demos/dock.tsx +20 -0
- package/site/demos/document-sidebar-block.tsx +9 -0
- package/site/demos/email-examples.tsx +58 -0
- package/site/demos/email-frame.tsx +50 -0
- package/site/demos/email-layout.tsx +6 -0
- package/site/demos/email-link-fallback.tsx +6 -0
- package/site/demos/email-previews.generated.ts +11 -0
- package/site/demos/empty-state-block.tsx +3 -0
- package/site/demos/empty-state.tsx +23 -0
- package/site/demos/error-page.tsx +5 -0
- package/site/demos/fade-in.tsx +18 -0
- package/site/demos/faq-section.tsx +6 -0
- package/site/demos/feature-card.tsx +13 -0
- package/site/demos/features-section.tsx +6 -0
- package/site/demos/file-upload.tsx +16 -0
- package/site/demos/footer.tsx +42 -0
- package/site/demos/full-page-spinner.tsx +9 -0
- package/site/demos/glass-card.tsx +14 -0
- package/site/demos/glow-effect.tsx +3 -0
- package/site/demos/gradient-background.tsx +3 -0
- package/site/demos/grid-pattern.tsx +3 -0
- package/site/demos/hero-section.tsx +6 -0
- package/site/demos/image-card.tsx +17 -0
- package/site/demos/login-block.tsx +9 -0
- package/site/demos/marketing-cta-block.tsx +3 -0
- package/site/demos/marketing-demo-data.tsx +114 -0
- package/site/demos/marketing-faq-block.tsx +3 -0
- package/site/demos/marketing-features-block.tsx +3 -0
- package/site/demos/marketing-footer-block.tsx +3 -0
- package/site/demos/marketing-hero-block.tsx +10 -0
- package/site/demos/marketing-nav-block.tsx +3 -0
- package/site/demos/marketing-newsletter-block.tsx +3 -0
- package/site/demos/marketing-page.tsx +52 -0
- package/site/demos/marketing-pricing-block.tsx +3 -0
- package/site/demos/marketing-stats-block.tsx +3 -0
- package/site/demos/marketing-team-block.tsx +3 -0
- package/site/demos/marketing-testimonials-block.tsx +3 -0
- package/site/demos/navbar.tsx +32 -0
- package/site/demos/newsletter-section.tsx +2 -0
- package/site/demos/noise-overlay.tsx +3 -0
- package/site/demos/not-found-block.tsx +3 -0
- package/site/demos/notice-email.tsx +6 -0
- package/site/demos/notifications-popover.tsx +27 -0
- package/site/demos/notify.tsx +44 -0
- package/site/demos/olwiba-ui-context.tsx +51 -0
- package/site/demos/onboarding-block.tsx +3 -0
- package/site/demos/onboarding-wizard.tsx +43 -0
- package/site/demos/overlay.tsx +72 -0
- package/site/demos/page-header.tsx +15 -0
- package/site/demos/page-transition.tsx +34 -0
- package/site/demos/post-list.tsx +24 -0
- package/site/demos/pricing-card.tsx +27 -0
- package/site/demos/pricing-section.tsx +6 -0
- package/site/demos/register-hotkeys.tsx +39 -0
- package/site/demos/root-error-fallback.tsx +28 -0
- package/site/demos/settings-layout-block.tsx +3 -0
- package/site/demos/settings-section.tsx +22 -0
- package/site/demos/sign-in-page.tsx +10 -0
- package/site/demos/sortable.tsx +30 -0
- package/site/demos/spotlight.tsx +29 -0
- package/site/demos/stagger-children.tsx +16 -0
- package/site/demos/stat-card.tsx +12 -0
- package/site/demos/stats-section.tsx +6 -0
- package/site/demos/steps-section.tsx +66 -0
- package/site/demos/suspensed.tsx +31 -0
- package/site/demos/team-members-panel.tsx +26 -0
- package/site/demos/team-section.tsx +2 -0
- package/site/demos/testimonial-card.tsx +17 -0
- package/site/demos/testimonials-section.tsx +6 -0
- package/site/demos/theme-color-updater.tsx +32 -0
- package/site/demos/theme-switch-minimal.tsx +10 -0
- package/site/demos/underlay.tsx +74 -0
- package/site/demos/update-banner.tsx +31 -0
- package/site/demos/use-copy-to-clipboard.tsx +21 -0
- package/site/demos/use-debounce.tsx +25 -0
- package/site/demos/use-intersection-observer.tsx +32 -0
- package/site/demos/use-local-storage.tsx +22 -0
- package/site/demos/use-media-query.tsx +31 -0
- package/site/demos/use-pagination.tsx +31 -0
- package/site/demos/user-profile-block.tsx +3 -0
- package/site/demos/version-banner.tsx +27 -0
- package/src/email/EmailLinkFallback.tsx +7 -1
- 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,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
|
-
|
|
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>
|
package/src/email/index.ts
CHANGED
|
@@ -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';
|