@medalsocial/meda 2.1.0 → 2.3.0
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/dist/email-builder/block-error-boundary.js +1 -0
- package/dist/email-builder/block-registry.js +1 -0
- package/dist/email-builder/builder-canvas.js +3 -1
- package/dist/email-builder/builder-left-tabs.js +1 -0
- package/dist/email-builder/email-builder.js +11 -1
- package/dist/email-builder/internal/builder-state-utils.js +1 -0
- package/dist/email-builder/property-editors/_shared.js +2 -0
- package/dist/email-builder/render-to-email-html.js +1 -0
- package/dist/marketing/index.d.ts +15 -1
- package/dist/marketing/index.js +14 -0
- package/dist/marketing/marketing-announcement-bar.d.ts +2 -0
- package/dist/marketing/marketing-announcement-bar.js +6 -0
- package/dist/marketing/marketing-bento-card.d.ts +2 -0
- package/dist/marketing/marketing-bento-card.js +24 -0
- package/dist/marketing/marketing-bento-grid.d.ts +2 -0
- package/dist/marketing/marketing-bento-grid.js +12 -0
- package/dist/marketing/marketing-cta.d.ts +2 -0
- package/dist/marketing/marketing-cta.js +5 -0
- package/dist/marketing/marketing-faq.d.ts +2 -0
- package/dist/marketing/marketing-faq.js +12 -0
- package/dist/marketing/marketing-footer.d.ts +2 -0
- package/dist/marketing/marketing-footer.js +5 -0
- package/dist/marketing/marketing-header-logo.d.ts +10 -0
- package/dist/marketing/marketing-header-logo.js +6 -0
- package/dist/marketing/marketing-header.d.ts +2 -0
- package/dist/marketing/marketing-header.js +30 -0
- package/dist/marketing/marketing-hero.d.ts +2 -0
- package/dist/marketing/marketing-hero.js +5 -0
- package/dist/marketing/marketing-mega-menu.d.ts +2 -0
- package/dist/marketing/marketing-mega-menu.js +8 -0
- package/dist/marketing/marketing-nav-item.d.ts +6 -0
- package/dist/marketing/marketing-nav-item.js +10 -0
- package/dist/marketing/marketing-numbered-features.d.ts +2 -0
- package/dist/marketing/marketing-numbered-features.js +5 -0
- package/dist/marketing/marketing-shell.d.ts +2 -0
- package/dist/marketing/marketing-shell.js +5 -0
- package/dist/marketing/marketing-trust-bar.d.ts +2 -0
- package/dist/marketing/marketing-trust-bar.js +5 -0
- package/dist/marketing/public.d.ts +1 -1
- package/dist/marketing/types.d.ts +148 -0
- package/dist/post-preview/internal/format-content.js +1 -0
- package/dist/post-preview/platforms/discord.js +2 -0
- package/dist/post-preview/platforms/facebook.js +2 -0
- package/dist/post-preview/platforms/generic.js +2 -0
- package/dist/post-preview/platforms/google-business.js +2 -0
- package/dist/post-preview/platforms/instagram.js +5 -0
- package/dist/post-preview/platforms/linkedin.js +9 -0
- package/dist/post-preview/platforms/telegram.js +2 -0
- package/dist/post-preview/platforms/threads.js +2 -0
- package/dist/post-preview/platforms/tiktok.js +2 -0
- package/dist/post-preview/platforms/twitter.js +5 -0
- package/dist/post-preview/platforms/youtube.js +2 -0
- package/dist/shell/command-palette.js +2 -0
- package/dist/shell/drag-mode-banner.js +4 -0
- package/dist/shell/internal/mobile-bottom-nav.js +1 -0
- package/dist/shell/internal/mobile-drawers.js +5 -0
- package/dist/shell/rail-drop-slot.js +20 -3
- package/dist/shell/rail-drop-zones.js +2 -0
- package/dist/shell/shell-header.js +7 -1
- package/dist/shell/theme-next-themes.js +5 -0
- package/dist/shell/use-shell-viewport.js +2 -0
- package/dist/workflow-builder/workflow-canvas.js +6 -2
- package/dist/workflow-builder/workflow-header.js +1 -0
- package/package.json +1 -1
- package/dist/styles/theme.test.ts +0 -41
|
@@ -12,6 +12,7 @@ export class BlockErrorBoundary extends Component {
|
|
|
12
12
|
// want to call console here (and trip the lint rule) in a public package.
|
|
13
13
|
}
|
|
14
14
|
render() {
|
|
15
|
+
/* v8 ignore next -- v8 counts an implicit undefined branch on null-initialised class field */
|
|
15
16
|
if (this.state.error) {
|
|
16
17
|
return (_jsxs("div", { "data-slot": "email-builder-block-error", className: "rounded-md border border-destructive/40 bg-destructive/10 p-3 text-destructive text-sm", role: "alert", children: ["Block failed to render: ", this.state.error.message] }));
|
|
17
18
|
}
|
|
@@ -90,6 +90,7 @@ export const COLUMN_WIDTHS = {
|
|
|
90
90
|
'33-33-33': [33, 33, 34],
|
|
91
91
|
};
|
|
92
92
|
export function getColumnWidths(layout) {
|
|
93
|
+
/* v8 ignore next -- fallback is unreachable; ColumnLayout union exhausts COLUMN_WIDTHS keys */
|
|
93
94
|
return COLUMN_WIDTHS[layout] ?? [100];
|
|
94
95
|
}
|
|
95
96
|
export function createBlock(kind, overrides) {
|
|
@@ -16,7 +16,9 @@ function SortableBlock({ block, selected, onSelect, renderFloatingBar }) {
|
|
|
16
16
|
// dnd-kit injects role="button" + tabIndex by default; strip those so we don't
|
|
17
17
|
// create nested interactive controls (the inner select <button> handles a11y).
|
|
18
18
|
const { role: _role, tabIndex: _tabIndex, ...sortableAttrs } = attributes;
|
|
19
|
-
return (_jsxs("div", { ref: setNodeRef, "data-slot": "email-builder-canvas-block", "data-block-id": block.id, "data-selected": selected ? 'true' : undefined, style: { transform: CSS.Transform.toString(transform), transition }, className: cn('group relative cursor-grab transition-shadow', selected && 'ring-2 ring-primary ring-inset',
|
|
19
|
+
return (_jsxs("div", { ref: setNodeRef, "data-slot": "email-builder-canvas-block", "data-block-id": block.id, "data-selected": selected ? 'true' : undefined, style: { transform: CSS.Transform.toString(transform), transition }, className: cn('group relative cursor-grab transition-shadow', selected && 'ring-2 ring-primary ring-inset',
|
|
20
|
+
/* v8 ignore next -- isDragging=true requires live pointer drag; not reachable in jsdom */
|
|
21
|
+
isDragging && 'z-10 opacity-60'), ...sortableAttrs, ...listeners, children: [_jsx(BlockRenderer, { block: block }), _jsx("button", { type: "button", "data-slot": "email-builder-canvas-block-select", "aria-label": `Select ${block.kind} block`, "aria-pressed": selected, className: cn('absolute inset-0 z-0 cursor-pointer bg-transparent outline-none', selected ? 'pointer-events-none' : 'pointer-events-auto'), onClick: (e) => {
|
|
20
22
|
e.stopPropagation();
|
|
21
23
|
onSelect();
|
|
22
24
|
} }), selected && renderFloatingBar ? (_jsx(_Fragment, { children: _jsx("div", { "data-slot": "email-builder-canvas-block-actions", className: "-top-3 -translate-x-1/2 absolute left-1/2 z-10", onClick: (e) => e.stopPropagation(), onPointerDown: (e) => e.stopPropagation(), children: renderFloatingBar(block) }) })) : null] }));
|
|
@@ -12,6 +12,7 @@ export function BuilderLeftTabs({ labels, blocksContent, designContent, envelope
|
|
|
12
12
|
if (designContent) {
|
|
13
13
|
tabs.splice(1, 0, { id: 'design', label: labels.designTab, content: designContent });
|
|
14
14
|
}
|
|
15
|
+
/* v8 ignore next -- fallback to tabs[0] is unreachable; tab state is always set to a valid TabId */
|
|
15
16
|
const active = tabs.find((t) => t.id === tab) ?? tabs[0];
|
|
16
17
|
return (_jsxs("div", { "data-slot": "email-builder-left-tabs", className: "flex h-full flex-col", children: [_jsx("div", { role: "tablist", className: "flex border-border border-b", children: tabs.map((t) => (_jsx("button", { type: "button", role: "tab", "aria-selected": t.id === tab, onClick: () => setTab(t.id), className: cn('flex h-11 flex-1 items-center justify-center text-sm transition-colors', t.id === tab
|
|
17
18
|
? 'border-primary border-b-2 font-medium text-foreground'
|
|
@@ -45,6 +45,7 @@ export function EmailBuilder({ document, onDocumentChange, brand, renderMediaPic
|
|
|
45
45
|
}, [document, onDocumentChange]);
|
|
46
46
|
const handleMove = useCallback((blockId, delta) => {
|
|
47
47
|
const idx = document.blocks.findIndex((b) => b.id === blockId);
|
|
48
|
+
/* v8 ignore next -- defensive guard; floating bar only renders for blocks that exist in document */
|
|
48
49
|
if (idx === -1)
|
|
49
50
|
return;
|
|
50
51
|
onDocumentChange(moveBlock(document, idx, idx + delta));
|
|
@@ -65,6 +66,7 @@ export function EmailBuilder({ document, onDocumentChange, brand, renderMediaPic
|
|
|
65
66
|
onExportHtml(html);
|
|
66
67
|
return;
|
|
67
68
|
}
|
|
69
|
+
/* v8 ignore next -- typeof window guard is always true in jsdom; SSR-only branch */
|
|
68
70
|
if (typeof window !== 'undefined') {
|
|
69
71
|
const blob = new Blob([html], { type: 'text/html' });
|
|
70
72
|
const url = URL.createObjectURL(blob);
|
|
@@ -78,7 +80,15 @@ export function EmailBuilder({ document, onDocumentChange, brand, renderMediaPic
|
|
|
78
80
|
const blockIds = useMemo(() => document.blocks.map((b) => b.id), [document.blocks]);
|
|
79
81
|
const blocksTabContent = (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(BlockPalette, { onPick: handleAddBlock }), renderSavedBlocks ? (_jsx("div", { "data-slot": "email-builder-saved-blocks-slot", className: "border-border border-t p-2", children: renderSavedBlocks({ onPick: handleSavedBlockPick }) })) : null] }));
|
|
80
82
|
const envelopeTabContent = (_jsx(EnvelopeCard, { envelope: document.envelope, onChange: (envelope) => onDocumentChange({ ...document, envelope }) }));
|
|
81
|
-
return (_jsxs("div", { "data-slot": "email-builder", className: cn('relative flex h-full min-h-[600px] w-full flex-col bg-background', className), children: [_jsx(BuilderHeader, { device: device, onDeviceChange: setDevice, onExport: handleExport, labels: labels }), _jsx(BuilderDndWrapper, { blockIds: blockIds, onDragEnd: handleDragEnd, children: _jsxs("div", { className: "flex min-h-0 flex-1", children: [_jsx("aside", { "data-slot": "email-builder-left", "aria-label": labels.blocksTab, className: "hidden w-72 shrink-0 border-border border-r md:block", children: _jsx(BuilderLeftTabs, { labels: labels, blocksContent: blocksTabContent, envelopeContent: envelopeTabContent }) }), _jsx("main", { className: "min-w-0 flex-1", children: _jsx(BuilderCanvas, { blocks: document.blocks, selectedBlockId: selectedBlockId, onSelectBlock: setSelectedBlockId, device: device, labels: labels, renderFloatingBar: (block) => (_jsx(FloatingBar, { labels: labels, canMoveUp: document.blocks[0]?.id !== block.id, canMoveDown: document.blocks[document.blocks.length - 1]?.id !== block.id, onMoveUp: () => handleMove(block.id, -1), onMoveDown: () => handleMove(block.id, 1), onDuplicate: () => handleDuplicate(block.id), onDelete: () => handleDelete(block.id) })) }) }), _jsx("aside", { "data-slot": "email-builder-right", "aria-label": labels.openInspector, className: "hidden w-80 shrink-0 overflow-y-auto border-border border-l md:block", children: _jsx(PropertyInspector, { block: selectedBlock, onChange: (id, patch) => handlePropChange(id, patch), emptyContent: labels.inspectorEmpty, renderMediaPicker: renderMediaPicker, renderTextEditor: renderTextEditor }) })] }) }), _jsx(MobileTabBar, { labels: labels, onOpenBlocks: () => setMobilePane('blocks'), onOpenInspector: () => setMobilePane('inspector'), onOpenSettings: () => setMobilePane('settings') }), _jsx(MobileDrawer, { open: mobilePane === 'blocks',
|
|
83
|
+
return (_jsxs("div", { "data-slot": "email-builder", className: cn('relative flex h-full min-h-[600px] w-full flex-col bg-background', className), children: [_jsx(BuilderHeader, { device: device, onDeviceChange: setDevice, onExport: handleExport, labels: labels }), _jsx(BuilderDndWrapper, { blockIds: blockIds, onDragEnd: handleDragEnd, children: _jsxs("div", { className: "flex min-h-0 flex-1", children: [_jsx("aside", { "data-slot": "email-builder-left", "aria-label": labels.blocksTab, className: "hidden w-72 shrink-0 border-border border-r md:block", children: _jsx(BuilderLeftTabs, { labels: labels, blocksContent: blocksTabContent, envelopeContent: envelopeTabContent }) }), _jsx("main", { className: "min-w-0 flex-1", children: _jsx(BuilderCanvas, { blocks: document.blocks, selectedBlockId: selectedBlockId, onSelectBlock: setSelectedBlockId, device: device, labels: labels, renderFloatingBar: (block) => (_jsx(FloatingBar, { labels: labels, canMoveUp: document.blocks[0]?.id !== block.id, canMoveDown: document.blocks[document.blocks.length - 1]?.id !== block.id, onMoveUp: () => handleMove(block.id, -1), onMoveDown: () => handleMove(block.id, 1), onDuplicate: () => handleDuplicate(block.id), onDelete: () => handleDelete(block.id) })) }) }), _jsx("aside", { "data-slot": "email-builder-right", "aria-label": labels.openInspector, className: "hidden w-80 shrink-0 overflow-y-auto border-border border-l md:block", children: _jsx(PropertyInspector, { block: selectedBlock, onChange: (id, patch) => handlePropChange(id, patch), emptyContent: labels.inspectorEmpty, renderMediaPicker: renderMediaPicker, renderTextEditor: renderTextEditor }) })] }) }), _jsx(MobileTabBar, { labels: labels, onOpenBlocks: () => setMobilePane('blocks'), onOpenInspector: () => setMobilePane('inspector'), onOpenSettings: () => setMobilePane('settings') }), _jsx(MobileDrawer, { open: mobilePane === 'blocks',
|
|
84
|
+
/* v8 ignore next -- vaul close events (swipe/overlay/escape) don't fire in jsdom */
|
|
85
|
+
onOpenChange: (o) => !o && setMobilePane(null), title: labels.blocksTab, children: blocksTabContent }), _jsx(MobileDrawer, { open: mobilePane === 'inspector',
|
|
86
|
+
/* v8 ignore next -- vaul close events (swipe/overlay/escape) don't fire in jsdom */
|
|
87
|
+
onOpenChange: (o) => !o && setMobilePane(null), title: labels.openInspector, children: _jsx(PropertyInspector, { block: selectedBlock,
|
|
88
|
+
/* v8 ignore next -- onChange is identical to the right-rail's; mobile drawer portal content is not exercised in jsdom */
|
|
89
|
+
onChange: (id, patch) => handlePropChange(id, patch), emptyContent: labels.inspectorEmpty, renderMediaPicker: renderMediaPicker, renderTextEditor: renderTextEditor }) }), _jsx(MobileDrawer, { open: mobilePane === 'settings',
|
|
90
|
+
/* v8 ignore next -- vaul close events (swipe/overlay/escape) don't fire in jsdom */
|
|
91
|
+
onOpenChange: (o) => !o && setMobilePane(null), title: labels.envelopeTab, children: envelopeTabContent })] }));
|
|
82
92
|
}
|
|
83
93
|
// Re-exported for convenience.
|
|
84
94
|
export { renderToEmailHtml } from './render-to-email-html.js';
|
|
@@ -24,6 +24,7 @@ export function TextAreaInput({ id, value, onChange, rows = 4, 'aria-label': ari
|
|
|
24
24
|
export function NumberInput({ id, value, onChange, min, max, step = 1, 'aria-label': ariaLabel, }) {
|
|
25
25
|
return (_jsx("input", { id: id, type: "number", value: value, min: min, max: max, step: step, "aria-label": ariaLabel, onChange: (e) => {
|
|
26
26
|
const n = Number(e.target.value);
|
|
27
|
+
/* v8 ignore next -- number inputs sanitize non-numeric input in real browsers; jsdom always coerces to 0 */
|
|
27
28
|
if (!Number.isNaN(n))
|
|
28
29
|
onChange(n);
|
|
29
30
|
}, className: "w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-ring" }));
|
|
@@ -45,6 +46,7 @@ export function SpacingEditor({ value, onChange, }) {
|
|
|
45
46
|
const label = (side[0]?.toUpperCase() + side.slice(1));
|
|
46
47
|
return (_jsxs("div", { "data-slot": "email-builder-field", className: "flex flex-col gap-1.5", children: [_jsx("span", { className: "font-medium text-foreground text-xs", children: label }), _jsx("input", { type: "number", value: value[side], min: 0, "aria-label": `${label} padding`, onChange: (e) => {
|
|
47
48
|
const n = Number(e.target.value);
|
|
49
|
+
/* v8 ignore next -- number inputs sanitize non-numeric input in real browsers; jsdom always coerces to 0 */
|
|
48
50
|
if (!Number.isNaN(n))
|
|
49
51
|
onChange({ ...value, [side]: n });
|
|
50
52
|
}, className: "w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-ring" })] }, side));
|
|
@@ -1,5 +1,19 @@
|
|
|
1
|
+
export { MarketingAnnouncementBar } from './marketing-announcement-bar.js';
|
|
2
|
+
export { MarketingBentoCard } from './marketing-bento-card.js';
|
|
3
|
+
export { MarketingBentoGrid } from './marketing-bento-grid.js';
|
|
1
4
|
export { MarketingCallout } from './marketing-callout.js';
|
|
2
5
|
export { MarketingContact } from './marketing-contact.js';
|
|
6
|
+
export { MarketingCTA } from './marketing-cta.js';
|
|
3
7
|
export { MarketingCtaList } from './marketing-cta-list.js';
|
|
8
|
+
export { MarketingFAQ } from './marketing-faq.js';
|
|
9
|
+
export { MarketingFooter } from './marketing-footer.js';
|
|
10
|
+
export { MarketingHeader } from './marketing-header.js';
|
|
11
|
+
export { MarketingHeaderLogo } from './marketing-header-logo.js';
|
|
12
|
+
export { MarketingHero } from './marketing-hero.js';
|
|
4
13
|
export { MarketingLeadMagnet } from './marketing-lead-magnet.js';
|
|
5
|
-
export
|
|
14
|
+
export { MarketingMegaMenu } from './marketing-mega-menu.js';
|
|
15
|
+
export { MarketingNavItem } from './marketing-nav-item.js';
|
|
16
|
+
export { MarketingNumberedFeatures } from './marketing-numbered-features.js';
|
|
17
|
+
export { MarketingShell } from './marketing-shell.js';
|
|
18
|
+
export { MarketingTrustBar } from './marketing-trust-bar.js';
|
|
19
|
+
export type { MarketingAlign, MarketingAnnouncementBarProps, MarketingBentoCardProps, MarketingBentoGridProps, MarketingCalloutProps, MarketingCalloutVariant, MarketingContactPerson, MarketingContactProps, MarketingCTAProps, MarketingCta, MarketingCtaListProps, MarketingCtaVariant, MarketingFAQItem, MarketingFAQProps, MarketingFooterColumn, MarketingFooterProps, MarketingHeaderProps, MarketingHeaderUser, MarketingHeroProps, MarketingLeadMagnetProps, MarketingLeadMagnetVariant, MarketingMegaMenuFeature, MarketingMegaMenuProps, MarketingNavItemDescriptor, MarketingNumberedFeature, MarketingNumberedFeaturesProps, MarketingOffice, MarketingShellProps, MarketingTrustBarProps, MarketingTrustStat, } from './types.js';
|
package/dist/marketing/index.js
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
|
+
export { MarketingAnnouncementBar } from './marketing-announcement-bar.js';
|
|
2
|
+
export { MarketingBentoCard } from './marketing-bento-card.js';
|
|
3
|
+
export { MarketingBentoGrid } from './marketing-bento-grid.js';
|
|
1
4
|
export { MarketingCallout } from './marketing-callout.js';
|
|
2
5
|
export { MarketingContact } from './marketing-contact.js';
|
|
6
|
+
export { MarketingCTA } from './marketing-cta.js';
|
|
3
7
|
export { MarketingCtaList } from './marketing-cta-list.js';
|
|
8
|
+
export { MarketingFAQ } from './marketing-faq.js';
|
|
9
|
+
export { MarketingFooter } from './marketing-footer.js';
|
|
10
|
+
export { MarketingHeader } from './marketing-header.js';
|
|
11
|
+
export { MarketingHeaderLogo } from './marketing-header-logo.js';
|
|
12
|
+
export { MarketingHero } from './marketing-hero.js';
|
|
4
13
|
export { MarketingLeadMagnet } from './marketing-lead-magnet.js';
|
|
14
|
+
export { MarketingMegaMenu } from './marketing-mega-menu.js';
|
|
15
|
+
export { MarketingNavItem } from './marketing-nav-item.js';
|
|
16
|
+
export { MarketingNumberedFeatures } from './marketing-numbered-features.js';
|
|
17
|
+
export { MarketingShell } from './marketing-shell.js';
|
|
18
|
+
export { MarketingTrustBar } from './marketing-trust-bar.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from './utils.js';
|
|
3
|
+
export function MarketingAnnouncementBar({ href, icon, children, className, }) {
|
|
4
|
+
const Tag = href ? 'a' : 'span';
|
|
5
|
+
return (_jsxs(Tag, { ...(href ? { href } : {}), className: cx('inline-flex items-center gap-2 rounded-full border border-border/60 bg-card/50 px-4 py-1.5 text-xs font-medium text-foreground/80 backdrop-blur', className), children: [icon, children] }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from './utils.js';
|
|
3
|
+
const COL_SPAN = {
|
|
4
|
+
1: 'col-span-1',
|
|
5
|
+
2: 'col-span-2 sm:col-span-2',
|
|
6
|
+
3: 'col-span-3 sm:col-span-3',
|
|
7
|
+
4: 'col-span-4 sm:col-span-4',
|
|
8
|
+
6: 'col-span-6 sm:col-span-6',
|
|
9
|
+
12: 'col-span-12',
|
|
10
|
+
};
|
|
11
|
+
const ROW_SPAN = {
|
|
12
|
+
1: 'row-span-1',
|
|
13
|
+
2: 'row-span-2',
|
|
14
|
+
3: 'row-span-3',
|
|
15
|
+
};
|
|
16
|
+
const PADDING = {
|
|
17
|
+
default: 'p-6',
|
|
18
|
+
feature: 'p-8',
|
|
19
|
+
compact: 'p-4',
|
|
20
|
+
};
|
|
21
|
+
export function MarketingBentoCard({ colSpan = 4, rowSpan = 1, variant = 'default', icon, title, description, children, href, className, }) {
|
|
22
|
+
const Tag = href ? 'a' : 'div';
|
|
23
|
+
return (_jsxs(Tag, { "data-bento-card": true, ...(href ? { href } : {}), className: cx('rounded-2xl border border-border bg-card text-card-foreground transition', PADDING[variant], COL_SPAN[colSpan], ROW_SPAN[rowSpan], href && 'hover:border-primary/40 hover:bg-card/80', className), children: [icon && (_jsx("div", { className: "mb-4 inline-flex h-9 w-9 items-center justify-center rounded-xl bg-primary/10 text-primary", children: icon })), title && _jsx("h3", { className: "text-lg font-semibold", children: title }), description && _jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: description }), children] }));
|
|
24
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from './utils.js';
|
|
3
|
+
const COLS = {
|
|
4
|
+
2: 'grid-cols-2',
|
|
5
|
+
3: 'grid-cols-3',
|
|
6
|
+
4: 'grid-cols-4',
|
|
7
|
+
6: 'grid-cols-6',
|
|
8
|
+
12: 'grid-cols-12',
|
|
9
|
+
};
|
|
10
|
+
export function MarketingBentoGrid({ cols = 12, children, className }) {
|
|
11
|
+
return (_jsx("section", { className: cx('mx-auto w-full max-w-6xl px-6 py-16', className), children: _jsx("div", { className: cx('grid gap-4', COLS[cols]), children: children }) }));
|
|
12
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from './utils.js';
|
|
3
|
+
export function MarketingCTA({ eyebrow, title, subtitle, ctas, className }) {
|
|
4
|
+
return (_jsxs("section", { className: cx('mx-auto w-full max-w-4xl px-6 py-24 text-center', className), children: [eyebrow && _jsx("p", { className: "mb-3 text-sm font-semibold text-primary", children: eyebrow }), _jsx("h2", { className: "mx-auto max-w-3xl text-4xl font-semibold sm:text-5xl", children: title }), subtitle && (_jsx("p", { className: "mx-auto mt-4 max-w-xl text-base text-muted-foreground sm:text-lg", children: subtitle })), ctas && _jsx("div", { className: "mt-8 flex flex-wrap items-center justify-center gap-3", children: ctas })] }));
|
|
5
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { ChevronDown } from 'lucide-react';
|
|
4
|
+
import { useState } from 'react';
|
|
5
|
+
import { cx } from './utils.js';
|
|
6
|
+
export function MarketingFAQ({ eyebrow, title, items, defaultOpenId, className, }) {
|
|
7
|
+
const [openId, setOpenId] = useState(defaultOpenId ?? null);
|
|
8
|
+
return (_jsxs("section", { className: cx('mx-auto w-full max-w-3xl px-6 py-16', className), children: [(eyebrow || title) && (_jsxs("div", { className: "mb-10 text-center", children: [eyebrow && _jsx("p", { className: "text-sm font-semibold text-primary", children: eyebrow }), title && _jsx("h2", { className: "mt-2 text-3xl font-semibold sm:text-4xl", children: title })] })), _jsx("ul", { className: "space-y-3", children: items.map((item) => {
|
|
9
|
+
const open = openId === item.id;
|
|
10
|
+
return (_jsxs("li", { className: "rounded-2xl border border-border bg-card", children: [_jsxs("button", { type: "button", "aria-expanded": open, onClick: () => setOpenId(open ? null : item.id), className: "flex w-full items-center justify-between px-5 py-4 text-left text-base font-medium", children: [item.question, _jsx(ChevronDown, { className: cx('h-4 w-4 transition', open && 'rotate-180'), "aria-hidden": true })] }), open && (_jsx("div", { className: "border-t border-border px-5 py-4 text-sm text-muted-foreground", children: item.answer }))] }, item.id));
|
|
11
|
+
}) })] }));
|
|
12
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from './utils.js';
|
|
3
|
+
export function MarketingFooter({ brand, tagline, columns = [], bottomSlot, className, }) {
|
|
4
|
+
return (_jsxs("div", { className: cx('border-t border-border bg-background', className), children: [_jsxs("div", { className: "mx-auto grid w-full max-w-6xl gap-12 px-6 py-16 md:grid-cols-12", children: [_jsxs("div", { className: "md:col-span-4 space-y-3", children: [_jsx("div", { className: "text-lg font-semibold", children: brand }), tagline && _jsx("p", { className: "max-w-sm text-sm text-muted-foreground", children: tagline })] }), columns.map((col, i) => (_jsxs("div", { className: "md:col-span-2 space-y-3", children: [_jsx("h3", { className: "text-sm font-semibold text-foreground", children: col.title }), _jsx("ul", { className: "space-y-2", children: col.links.map((link, j) => (_jsx("li", { children: _jsx("a", { href: link.href, className: "text-sm text-muted-foreground hover:text-foreground", children: link.label }) }, `${j}-${link.href}`))) })] }, i)))] }), bottomSlot && (_jsx("div", { className: "border-t border-border", children: _jsx("div", { className: "mx-auto flex w-full max-w-6xl items-center justify-between px-6 py-4 text-xs text-muted-foreground", children: bottomSlot }) }))] }));
|
|
5
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export interface MarketingHeaderLogoProps {
|
|
3
|
+
mark?: ReactNode;
|
|
4
|
+
word?: ReactNode;
|
|
5
|
+
href?: string;
|
|
6
|
+
/** Accessible name for the logo link. Falls back to `word` if it's a string, then to "Home". */
|
|
7
|
+
label?: string;
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function MarketingHeaderLogo({ mark, word, href, label, className, }: MarketingHeaderLogoProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from './utils.js';
|
|
3
|
+
export function MarketingHeaderLogo({ mark, word, href = '/', label, className, }) {
|
|
4
|
+
const accessibleName = label ?? (typeof word === 'string' ? word : undefined) ?? 'Home';
|
|
5
|
+
return (_jsxs("a", { href: href, "aria-label": accessibleName, className: cx('flex items-center gap-2 font-semibold tracking-tight', className), children: [mark, word && _jsx("span", { children: word })] }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import type { MarketingHeaderProps } from './types.js';
|
|
2
|
+
export declare function MarketingHeader({ logo, navItems, user, signInHref, signUpHref, appHref, rightSlot, toggles, renderLoggedOut, renderLoggedIn, className, }: MarketingHeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { MarketingNavItem } from './marketing-nav-item.js';
|
|
5
|
+
import { cx } from './utils.js';
|
|
6
|
+
const DEFAULT_SIGN_IN = '/sign-in';
|
|
7
|
+
const DEFAULT_SIGN_UP = '/sign-up';
|
|
8
|
+
const DEFAULT_APP = '/app';
|
|
9
|
+
function DefaultLoggedOut({ signInHref, signUpHref }) {
|
|
10
|
+
return (_jsxs(_Fragment, { children: [_jsx("a", { href: signInHref, className: "text-sm text-foreground/80 hover:text-foreground", children: "Sign in" }), _jsx("a", { href: signUpHref, className: "inline-flex items-center rounded-full bg-primary px-4 py-1.5 text-sm font-medium text-primary-foreground hover:bg-primary/90", children: "Start free" })] }));
|
|
11
|
+
}
|
|
12
|
+
function DefaultLoggedIn({ appHref }) {
|
|
13
|
+
return (_jsx("a", { href: appHref, className: "inline-flex items-center rounded-full bg-primary px-4 py-1.5 text-sm font-medium text-primary-foreground hover:bg-primary/90", children: "Open dashboard" }));
|
|
14
|
+
}
|
|
15
|
+
export function MarketingHeader({ logo, navItems = [], user, signInHref = DEFAULT_SIGN_IN, signUpHref = DEFAULT_SIGN_UP, appHref = DEFAULT_APP, rightSlot, toggles, renderLoggedOut, renderLoggedIn, className, }) {
|
|
16
|
+
const [openId, setOpenId] = useState(null);
|
|
17
|
+
let right;
|
|
18
|
+
if (rightSlot !== undefined) {
|
|
19
|
+
right = rightSlot;
|
|
20
|
+
}
|
|
21
|
+
else if (user) {
|
|
22
|
+
right = renderLoggedIn ? (renderLoggedIn(user, { appHref })) : (_jsx(DefaultLoggedIn, { appHref: appHref }));
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
right = renderLoggedOut ? (renderLoggedOut({ signInHref, signUpHref })) : (_jsx(DefaultLoggedOut, { signInHref: signInHref, signUpHref: signUpHref }));
|
|
26
|
+
}
|
|
27
|
+
const activeItem = navItems.find((i) => i.id === openId);
|
|
28
|
+
const activePanel = activeItem && 'panel' in activeItem ? activeItem.panel : undefined;
|
|
29
|
+
return (_jsxs("nav", { "aria-label": "Primary", className: "relative", onMouseLeave: () => setOpenId(null), children: [_jsxs("div", { className: cx('mx-auto flex w-full max-w-6xl items-center justify-between rounded-full border border-border/60 bg-background/70 px-4 py-2 backdrop-blur supports-[backdrop-filter]:bg-background/50', className), children: [_jsxs("div", { className: "flex items-center gap-6", children: [logo, _jsx("ul", { className: "hidden items-center gap-1 md:flex", children: navItems.map((item) => (_jsx("li", { children: _jsx(MarketingNavItem, { ...item, open: openId === item.id, onToggle: item.hasMenu ? () => setOpenId(openId === item.id ? null : item.id) : undefined }) }, item.id))) })] }), _jsxs("div", { className: "flex items-center gap-3", children: [toggles, right] })] }), activePanel] }));
|
|
30
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from './utils.js';
|
|
3
|
+
export function MarketingHero({ eyebrow, headline, subtitle, ctas, meta, productMockup, className, }) {
|
|
4
|
+
return (_jsxs("section", { className: cx('mx-auto w-full max-w-6xl px-6 pt-16 pb-24', className), children: [_jsxs("div", { className: "flex flex-col items-center gap-6 text-center", children: [eyebrow, _jsx("h1", { className: "max-w-4xl text-5xl font-semibold leading-[1.05] tracking-tight sm:text-6xl md:text-7xl lg:text-[80px]", children: headline }), subtitle && (_jsx("p", { className: "max-w-2xl text-base text-muted-foreground sm:text-lg", children: subtitle })), ctas && (_jsx("div", { className: "mt-2 flex flex-wrap items-center justify-center gap-3", children: ctas })), meta && _jsx("p", { className: "text-xs text-muted-foreground", children: meta })] }), productMockup && (_jsx("div", { className: "mt-16 overflow-hidden rounded-3xl border border-border bg-card shadow-2xl", children: productMockup }))] }));
|
|
5
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { cx } from './utils.js';
|
|
4
|
+
export function MarketingMegaMenu({ triggerId, open, onOpenChange, features, children, className, }) {
|
|
5
|
+
if (!open)
|
|
6
|
+
return null;
|
|
7
|
+
return (_jsx("div", { role: "dialog", "aria-labelledby": triggerId, onMouseLeave: () => onOpenChange(false), className: cx('absolute left-1/2 top-full mt-3 w-[min(960px,calc(100vw-32px))] -translate-x-1/2 rounded-3xl border border-border bg-card p-6 shadow-2xl backdrop-blur', className), children: children ?? (_jsx("ul", { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3", children: features?.map((f) => (_jsx("li", { children: _jsxs("a", { href: f.href, className: "flex items-start gap-3 rounded-xl p-3 hover:bg-muted/40", children: [f.icon && (_jsx("span", { className: "mt-0.5 inline-flex h-8 w-8 items-center justify-center rounded-lg bg-primary/10 text-primary", children: f.icon })), _jsxs("span", { children: [_jsx("span", { className: "block text-sm font-semibold", children: f.title }), f.description && (_jsx("span", { className: "mt-1 block text-xs text-muted-foreground", children: f.description }))] })] }) }, f.id))) })) }));
|
|
8
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { MarketingNavItemDescriptor } from './types.js';
|
|
2
|
+
export declare function MarketingNavItem({ id, label, className, open, onToggle, ...rest }: MarketingNavItemDescriptor & {
|
|
3
|
+
className?: string;
|
|
4
|
+
open?: boolean;
|
|
5
|
+
onToggle?: () => void;
|
|
6
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { ChevronDown } from 'lucide-react';
|
|
4
|
+
import { cx } from './utils.js';
|
|
5
|
+
export function MarketingNavItem({ id, label, className, open = false, onToggle, ...rest }) {
|
|
6
|
+
if (rest.hasMenu) {
|
|
7
|
+
return (_jsxs("button", { type: "button", "data-nav-id": id, id: id, "aria-expanded": open, "aria-haspopup": "menu", onClick: onToggle, className: cx('inline-flex items-center gap-1 rounded-full px-3 py-1.5 text-sm text-foreground/80 hover:text-foreground', className), children: [label, _jsx(ChevronDown, { "data-chevron": true, className: cx('h-3.5 w-3.5 transition', open && 'rotate-180'), "aria-hidden": true })] }));
|
|
8
|
+
}
|
|
9
|
+
return (_jsx("a", { href: rest.href, "data-nav-id": id, className: cx('inline-flex items-center rounded-full px-3 py-1.5 text-sm text-foreground/80 hover:text-foreground', className), children: label }));
|
|
10
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from './utils.js';
|
|
3
|
+
export function MarketingNumberedFeatures({ eyebrow, title, features, className, }) {
|
|
4
|
+
return (_jsxs("section", { className: cx('mx-auto w-full max-w-6xl px-6 py-16', className), children: [(eyebrow || title) && (_jsxs("div", { className: "mb-12 flex flex-col items-center gap-2 text-center", children: [eyebrow && _jsx("p", { className: "text-sm font-semibold text-primary", children: eyebrow }), title && _jsx("h2", { className: "max-w-3xl text-3xl font-semibold sm:text-4xl", children: title })] })), _jsx("ol", { className: "grid gap-6 md:grid-cols-3", children: features.map((f) => (_jsxs("li", { className: "rounded-2xl border border-border bg-card p-6", children: [_jsx("div", { className: "text-sm font-semibold text-primary", children: f.index }), _jsx("h3", { className: "mt-2 text-xl font-semibold", children: f.title }), _jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: f.description })] }, f.id))) })] }));
|
|
5
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from './utils.js';
|
|
3
|
+
export function MarketingShell({ header, footer, children, className }) {
|
|
4
|
+
return (_jsxs("div", { className: cx('flex min-h-screen flex-col bg-background text-foreground', className), children: [header && _jsx("header", { className: "sticky top-4 z-40 px-4 sm:px-6", children: header }), _jsx("main", { className: "flex-1", children: children }), footer && _jsx("footer", { className: "mt-auto", children: footer })] }));
|
|
5
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from './utils.js';
|
|
3
|
+
export function MarketingTrustBar({ eyebrow, title, stats, className }) {
|
|
4
|
+
return (_jsxs("section", { className: cx('mx-auto w-full max-w-6xl px-6 py-16', className), children: [(eyebrow || title) && (_jsxs("div", { className: "mb-10 flex flex-col items-center gap-2 text-center", children: [eyebrow && _jsx("p", { className: "text-sm font-semibold text-primary", children: eyebrow }), title && _jsx("h2", { className: "text-2xl font-semibold sm:text-3xl", children: title })] })), _jsx("ul", { className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-4", children: stats.map((s, i) => (_jsxs("li", { className: "rounded-2xl border border-border bg-card p-6 text-card-foreground", children: [_jsx("div", { className: "text-3xl font-semibold tracking-tight", children: s.value }), _jsx("div", { className: "mt-1 text-sm text-muted-foreground", children: s.label }), s.caption && _jsx("div", { className: "mt-2 text-xs text-muted-foreground", children: s.caption })] }, i))) })] }));
|
|
5
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export type { MarketingCalloutProps, MarketingContactPerson, MarketingContactProps, MarketingCta, MarketingLeadMagnetProps, MarketingOffice, } from './types.js';
|
|
1
|
+
export type { MarketingAnnouncementBarProps, MarketingBentoCardProps, MarketingBentoGridProps, MarketingCalloutProps, MarketingContactPerson, MarketingContactProps, MarketingCTAProps, MarketingCta, MarketingFAQItem, MarketingFAQProps, MarketingFooterColumn, MarketingFooterProps, MarketingHeaderProps, MarketingHeaderUser, MarketingHeroProps, MarketingLeadMagnetProps, MarketingMegaMenuFeature, MarketingMegaMenuProps, MarketingNavItemDescriptor, MarketingNumberedFeature, MarketingNumberedFeaturesProps, MarketingOffice, MarketingShellProps, MarketingTrustBarProps, MarketingTrustStat, } from './types.js';
|
|
@@ -65,3 +65,151 @@ export interface MarketingLeadMagnetProps {
|
|
|
65
65
|
onOpenChange?: (open: boolean) => void;
|
|
66
66
|
className?: string;
|
|
67
67
|
}
|
|
68
|
+
export interface MarketingHeaderUser {
|
|
69
|
+
name: string;
|
|
70
|
+
email?: string;
|
|
71
|
+
image?: string;
|
|
72
|
+
workspaceSlug?: string;
|
|
73
|
+
}
|
|
74
|
+
interface MarketingNavItemBase {
|
|
75
|
+
id: string;
|
|
76
|
+
label: ReactNode;
|
|
77
|
+
}
|
|
78
|
+
export interface MarketingNavLinkItem extends MarketingNavItemBase {
|
|
79
|
+
href: string;
|
|
80
|
+
hasMenu?: false;
|
|
81
|
+
panel?: never;
|
|
82
|
+
}
|
|
83
|
+
export interface MarketingNavMenuItem extends MarketingNavItemBase {
|
|
84
|
+
hasMenu: true;
|
|
85
|
+
href?: string;
|
|
86
|
+
panel?: ReactNode;
|
|
87
|
+
}
|
|
88
|
+
export type MarketingNavItemDescriptor = MarketingNavLinkItem | MarketingNavMenuItem;
|
|
89
|
+
export interface MarketingHeaderProps {
|
|
90
|
+
logo?: ReactNode;
|
|
91
|
+
navItems?: MarketingNavItemDescriptor[];
|
|
92
|
+
user?: MarketingHeaderUser | null;
|
|
93
|
+
signInHref?: string;
|
|
94
|
+
signUpHref?: string;
|
|
95
|
+
appHref?: string;
|
|
96
|
+
rightSlot?: ReactNode;
|
|
97
|
+
toggles?: ReactNode;
|
|
98
|
+
renderLoggedOut?: (defaults: {
|
|
99
|
+
signInHref: string;
|
|
100
|
+
signUpHref: string;
|
|
101
|
+
}) => ReactNode;
|
|
102
|
+
renderLoggedIn?: (user: MarketingHeaderUser, defaults: {
|
|
103
|
+
appHref: string;
|
|
104
|
+
}) => ReactNode;
|
|
105
|
+
className?: string;
|
|
106
|
+
}
|
|
107
|
+
export interface MarketingShellProps {
|
|
108
|
+
header?: ReactNode;
|
|
109
|
+
footer?: ReactNode;
|
|
110
|
+
children?: ReactNode;
|
|
111
|
+
className?: string;
|
|
112
|
+
}
|
|
113
|
+
export interface MarketingFooterColumn {
|
|
114
|
+
title: ReactNode;
|
|
115
|
+
links: Array<{
|
|
116
|
+
label: ReactNode;
|
|
117
|
+
href: string;
|
|
118
|
+
}>;
|
|
119
|
+
}
|
|
120
|
+
export interface MarketingFooterProps {
|
|
121
|
+
brand?: ReactNode;
|
|
122
|
+
tagline?: ReactNode;
|
|
123
|
+
columns?: MarketingFooterColumn[];
|
|
124
|
+
bottomSlot?: ReactNode;
|
|
125
|
+
className?: string;
|
|
126
|
+
}
|
|
127
|
+
export interface MarketingTrustStat {
|
|
128
|
+
value: ReactNode;
|
|
129
|
+
label: ReactNode;
|
|
130
|
+
caption?: ReactNode;
|
|
131
|
+
}
|
|
132
|
+
export interface MarketingTrustBarProps {
|
|
133
|
+
eyebrow?: ReactNode;
|
|
134
|
+
title?: ReactNode;
|
|
135
|
+
stats: MarketingTrustStat[];
|
|
136
|
+
className?: string;
|
|
137
|
+
}
|
|
138
|
+
export interface MarketingBentoGridProps {
|
|
139
|
+
cols?: 2 | 3 | 4 | 6 | 12;
|
|
140
|
+
children?: ReactNode;
|
|
141
|
+
className?: string;
|
|
142
|
+
}
|
|
143
|
+
export interface MarketingBentoCardProps {
|
|
144
|
+
colSpan?: 1 | 2 | 3 | 4 | 6 | 12;
|
|
145
|
+
rowSpan?: 1 | 2 | 3;
|
|
146
|
+
variant?: 'default' | 'feature' | 'compact';
|
|
147
|
+
icon?: ReactNode;
|
|
148
|
+
title?: ReactNode;
|
|
149
|
+
description?: ReactNode;
|
|
150
|
+
children?: ReactNode;
|
|
151
|
+
href?: string;
|
|
152
|
+
className?: string;
|
|
153
|
+
}
|
|
154
|
+
export interface MarketingFAQItem {
|
|
155
|
+
id: string;
|
|
156
|
+
question: ReactNode;
|
|
157
|
+
answer: ReactNode;
|
|
158
|
+
}
|
|
159
|
+
export interface MarketingFAQProps {
|
|
160
|
+
eyebrow?: ReactNode;
|
|
161
|
+
title?: ReactNode;
|
|
162
|
+
items: MarketingFAQItem[];
|
|
163
|
+
defaultOpenId?: string;
|
|
164
|
+
className?: string;
|
|
165
|
+
}
|
|
166
|
+
export interface MarketingMegaMenuFeature {
|
|
167
|
+
id: string;
|
|
168
|
+
icon?: ReactNode;
|
|
169
|
+
title: ReactNode;
|
|
170
|
+
description?: ReactNode;
|
|
171
|
+
href: string;
|
|
172
|
+
}
|
|
173
|
+
export interface MarketingMegaMenuProps {
|
|
174
|
+
triggerId: string;
|
|
175
|
+
open: boolean;
|
|
176
|
+
onOpenChange: (open: boolean) => void;
|
|
177
|
+
features?: MarketingMegaMenuFeature[];
|
|
178
|
+
children?: ReactNode;
|
|
179
|
+
className?: string;
|
|
180
|
+
}
|
|
181
|
+
export interface MarketingAnnouncementBarProps {
|
|
182
|
+
href?: string;
|
|
183
|
+
icon?: ReactNode;
|
|
184
|
+
children: ReactNode;
|
|
185
|
+
className?: string;
|
|
186
|
+
}
|
|
187
|
+
export interface MarketingHeroProps {
|
|
188
|
+
eyebrow?: ReactNode;
|
|
189
|
+
headline: ReactNode;
|
|
190
|
+
subtitle?: ReactNode;
|
|
191
|
+
ctas?: ReactNode;
|
|
192
|
+
meta?: ReactNode;
|
|
193
|
+
productMockup?: ReactNode;
|
|
194
|
+
className?: string;
|
|
195
|
+
}
|
|
196
|
+
export interface MarketingNumberedFeature {
|
|
197
|
+
id: string;
|
|
198
|
+
index: ReactNode;
|
|
199
|
+
title: ReactNode;
|
|
200
|
+
description: ReactNode;
|
|
201
|
+
}
|
|
202
|
+
export interface MarketingNumberedFeaturesProps {
|
|
203
|
+
eyebrow?: ReactNode;
|
|
204
|
+
title?: ReactNode;
|
|
205
|
+
features: MarketingNumberedFeature[];
|
|
206
|
+
className?: string;
|
|
207
|
+
}
|
|
208
|
+
export interface MarketingCTAProps {
|
|
209
|
+
eyebrow?: ReactNode;
|
|
210
|
+
title: ReactNode;
|
|
211
|
+
subtitle?: ReactNode;
|
|
212
|
+
ctas?: ReactNode;
|
|
213
|
+
className?: string;
|
|
214
|
+
}
|
|
215
|
+
export {};
|
|
@@ -11,6 +11,7 @@ export function splitContent(content) {
|
|
|
11
11
|
const parts = [];
|
|
12
12
|
let lastIndex = 0;
|
|
13
13
|
for (const match of content.matchAll(PATTERN)) {
|
|
14
|
+
/* v8 ignore next — match.index is always defined for matchAll results */
|
|
14
15
|
const matchIndex = match.index ?? 0;
|
|
15
16
|
if (matchIndex > lastIndex) {
|
|
16
17
|
parts.push({ type: 'text', value: content.slice(lastIndex, matchIndex) });
|
|
@@ -25,8 +25,10 @@ export function DiscordPreview({ displayName, username: _username, avatarUrl, co
|
|
|
25
25
|
function embedKey(embed, index) {
|
|
26
26
|
return `${embed.title ?? ''}::${embed.url ?? ''}::${embed.imageUrl ?? ''}::${index}`;
|
|
27
27
|
}
|
|
28
|
+
/* v8 ignore next — v8 phantom duplicate function record */
|
|
28
29
|
function DiscordEmbedCard({ embed }) {
|
|
29
30
|
const accent = typeof embed.color === 'number' ? `#${embed.color.toString(16).padStart(6, '0')}` : '#5865f2';
|
|
30
31
|
const href = embed.url?.trim();
|
|
32
|
+
/* v8 ignore next — v8 phantom duplicate function body record */
|
|
31
33
|
return (_jsxs("div", { "data-slot": "discord-embed", className: "overflow-hidden rounded-md border border-[#DDDEE1] border-l-4 bg-[#F2F3F5]", style: { borderLeftColor: accent }, children: [_jsxs("div", { className: "p-3", children: [embed.title ? (_jsx("p", { className: "font-medium text-[#060607] text-sm", children: href ? (_jsx("a", { href: href, target: "_blank", rel: "noreferrer", className: "hover:underline", children: embed.title })) : (embed.title) })) : null, embed.description ? (_jsx("p", { className: "mt-1 whitespace-pre-wrap text-[#313338] text-sm", children: embed.description })) : null, href ? _jsx("p", { className: "mt-2 truncate text-[#0068e0] text-xs", children: href }) : null] }), embed.imageUrl ? (_jsx("img", { src: embed.imageUrl, alt: embed.title ?? 'Discord embed', loading: "lazy", decoding: "async", className: "w-full object-cover" })) : null] }));
|
|
32
34
|
}
|