@medalsocial/meda 2.0.0 → 2.2.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/calendar/calendar-toolbar.d.ts +2 -0
- package/dist/calendar/calendar-toolbar.js +22 -0
- package/dist/calendar/calendar.d.ts +2 -0
- package/dist/calendar/calendar.js +55 -0
- package/dist/calendar/day-view.d.ts +2 -0
- package/dist/calendar/day-view.js +40 -0
- package/dist/calendar/index.d.ts +6 -0
- package/dist/calendar/index.js +7 -0
- package/dist/calendar/internal/date.d.ts +26 -0
- package/dist/calendar/internal/date.js +107 -0
- package/dist/calendar/internal/day-cell.d.ts +15 -0
- package/dist/calendar/internal/day-cell.js +38 -0
- package/dist/calendar/internal/event-pill.d.ts +13 -0
- package/dist/calendar/internal/event-pill.js +30 -0
- package/dist/calendar/internal/time-slot.d.ts +17 -0
- package/dist/calendar/internal/time-slot.js +23 -0
- package/dist/calendar/month-view.d.ts +2 -0
- package/dist/calendar/month-view.js +46 -0
- package/dist/calendar/public.d.ts +6 -0
- package/dist/calendar/public.js +8 -0
- package/dist/calendar/types.d.ts +113 -0
- package/dist/calendar/types.js +15 -0
- package/dist/calendar/week-view.d.ts +2 -0
- package/dist/calendar/week-view.js +39 -0
- package/dist/chat/turn-card.js +10 -5
- package/dist/email-builder/block-error-boundary.d.ts +16 -0
- package/dist/email-builder/block-error-boundary.js +21 -0
- package/dist/email-builder/block-palette.d.ts +7 -0
- package/dist/email-builder/block-palette.js +22 -0
- package/dist/email-builder/block-registry.d.ts +15 -0
- package/dist/email-builder/block-registry.js +157 -0
- package/dist/email-builder/block-renderer.d.ts +5 -0
- package/dist/email-builder/block-renderer.js +40 -0
- package/dist/email-builder/block-renderers/button.d.ts +4 -0
- package/dist/email-builder/block-renderers/button.js +26 -0
- package/dist/email-builder/block-renderers/columns.d.ts +5 -0
- package/dist/email-builder/block-renderers/columns.js +27 -0
- package/dist/email-builder/block-renderers/divider.d.ts +4 -0
- package/dist/email-builder/block-renderers/divider.js +14 -0
- package/dist/email-builder/block-renderers/footer.d.ts +4 -0
- package/dist/email-builder/block-renderers/footer.js +12 -0
- package/dist/email-builder/block-renderers/heading.d.ts +4 -0
- package/dist/email-builder/block-renderers/heading.js +19 -0
- package/dist/email-builder/block-renderers/image.d.ts +4 -0
- package/dist/email-builder/block-renderers/image.js +18 -0
- package/dist/email-builder/block-renderers/social.d.ts +4 -0
- package/dist/email-builder/block-renderers/social.js +19 -0
- package/dist/email-builder/block-renderers/spacer.d.ts +4 -0
- package/dist/email-builder/block-renderers/spacer.js +5 -0
- package/dist/email-builder/block-renderers/text.d.ts +4 -0
- package/dist/email-builder/block-renderers/text.js +17 -0
- package/dist/email-builder/builder-canvas.d.ts +15 -0
- package/dist/email-builder/builder-canvas.js +28 -0
- package/dist/email-builder/builder-header.d.ts +9 -0
- package/dist/email-builder/builder-header.js +8 -0
- package/dist/email-builder/builder-left-tabs.d.ts +11 -0
- package/dist/email-builder/builder-left-tabs.js +20 -0
- package/dist/email-builder/email-builder.d.ts +3 -0
- package/dist/email-builder/email-builder.js +94 -0
- package/dist/email-builder/envelope-card.d.ts +7 -0
- package/dist/email-builder/envelope-card.js +16 -0
- package/dist/email-builder/floating-bar.d.ts +13 -0
- package/dist/email-builder/floating-bar.js +9 -0
- package/dist/email-builder/index.d.ts +4 -0
- package/dist/email-builder/index.js +12 -0
- package/dist/email-builder/internal/builder-state-utils.d.ts +7 -0
- package/dist/email-builder/internal/builder-state-utils.js +53 -0
- package/dist/email-builder/internal/dnd-wrapper.d.ts +10 -0
- package/dist/email-builder/internal/dnd-wrapper.js +9 -0
- package/dist/email-builder/internal/email-font-options.d.ts +5 -0
- package/dist/email-builder/internal/email-font-options.js +12 -0
- package/dist/email-builder/mobile-drawers.d.ts +10 -0
- package/dist/email-builder/mobile-drawers.js +7 -0
- package/dist/email-builder/mobile-tab-bar.d.ts +10 -0
- package/dist/email-builder/mobile-tab-bar.js +9 -0
- package/dist/email-builder/property-editors/_shared.d.ts +58 -0
- package/dist/email-builder/property-editors/_shared.js +54 -0
- package/dist/email-builder/property-editors/button-editor.d.ts +7 -0
- package/dist/email-builder/property-editors/button-editor.js +10 -0
- package/dist/email-builder/property-editors/columns-editor.d.ts +7 -0
- package/dist/email-builder/property-editors/columns-editor.js +15 -0
- package/dist/email-builder/property-editors/divider-editor.d.ts +7 -0
- package/dist/email-builder/property-editors/divider-editor.js +10 -0
- package/dist/email-builder/property-editors/footer-editor.d.ts +7 -0
- package/dist/email-builder/property-editors/footer-editor.js +6 -0
- package/dist/email-builder/property-editors/heading-editor.d.ts +7 -0
- package/dist/email-builder/property-editors/heading-editor.js +16 -0
- package/dist/email-builder/property-editors/image-editor.d.ts +8 -0
- package/dist/email-builder/property-editors/image-editor.js +17 -0
- package/dist/email-builder/property-editors/social-editor.d.ts +7 -0
- package/dist/email-builder/property-editors/social-editor.js +18 -0
- package/dist/email-builder/property-editors/spacer-editor.d.ts +7 -0
- package/dist/email-builder/property-editors/spacer-editor.js +6 -0
- package/dist/email-builder/property-editors/text-editor.d.ts +13 -0
- package/dist/email-builder/property-editors/text-editor.js +8 -0
- package/dist/email-builder/property-inspector.d.ts +11 -0
- package/dist/email-builder/property-inspector.js +42 -0
- package/dist/email-builder/public.d.ts +1 -0
- package/dist/email-builder/public.js +3 -0
- package/dist/email-builder/render-to-email-html.d.ts +11 -0
- package/dist/email-builder/render-to-email-html.js +220 -0
- package/dist/email-builder/starter-shell.d.ts +3 -0
- package/dist/email-builder/starter-shell.js +21 -0
- package/dist/email-builder/types.d.ts +182 -0
- package/dist/email-builder/types.js +41 -0
- package/dist/email-builder/view-controls.d.ts +10 -0
- package/dist/email-builder/view-controls.js +12 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/lib/format-time.js +2 -3
- package/dist/marketing/index.d.ts +10 -1
- package/dist/marketing/index.js +9 -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-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 +26 -0
- package/dist/marketing/marketing-nav-item.d.ts +4 -0
- package/dist/marketing/marketing-nav-item.js +10 -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 +99 -0
- package/dist/post-preview/index.d.ts +4 -0
- package/dist/post-preview/index.js +7 -0
- package/dist/post-preview/internal/avatar.d.ts +12 -0
- package/dist/post-preview/internal/avatar.js +21 -0
- package/dist/post-preview/internal/format-content.d.ts +12 -0
- package/dist/post-preview/internal/format-content.js +32 -0
- package/dist/post-preview/internal/platform-meta.d.ts +10 -0
- package/dist/post-preview/internal/platform-meta.js +18 -0
- package/dist/post-preview/internal/textarea.d.ts +8 -0
- package/dist/post-preview/internal/textarea.js +12 -0
- package/dist/post-preview/internal/use-swipe.d.ts +17 -0
- package/dist/post-preview/internal/use-swipe.js +29 -0
- package/dist/post-preview/platforms/bluesky.d.ts +15 -0
- package/dist/post-preview/platforms/bluesky.js +21 -0
- package/dist/post-preview/platforms/discord.d.ts +44 -0
- package/dist/post-preview/platforms/discord.js +34 -0
- package/dist/post-preview/platforms/facebook.d.ts +23 -0
- package/dist/post-preview/platforms/facebook.js +26 -0
- package/dist/post-preview/platforms/generic.d.ts +22 -0
- package/dist/post-preview/platforms/generic.js +30 -0
- package/dist/post-preview/platforms/google-business.d.ts +18 -0
- package/dist/post-preview/platforms/google-business.js +24 -0
- package/dist/post-preview/platforms/index.d.ts +24 -0
- package/dist/post-preview/platforms/index.js +13 -0
- package/dist/post-preview/platforms/instagram.d.ts +31 -0
- package/dist/post-preview/platforms/instagram.js +104 -0
- package/dist/post-preview/platforms/linkedin.d.ts +52 -0
- package/dist/post-preview/platforms/linkedin.js +140 -0
- package/dist/post-preview/platforms/telegram.d.ts +46 -0
- package/dist/post-preview/platforms/telegram.js +44 -0
- package/dist/post-preview/platforms/threads.d.ts +19 -0
- package/dist/post-preview/platforms/threads.js +26 -0
- package/dist/post-preview/platforms/tiktok.d.ts +18 -0
- package/dist/post-preview/platforms/tiktok.js +25 -0
- package/dist/post-preview/platforms/twitter.d.ts +40 -0
- package/dist/post-preview/platforms/twitter.js +137 -0
- package/dist/post-preview/platforms/youtube.d.ts +17 -0
- package/dist/post-preview/platforms/youtube.js +23 -0
- package/dist/post-preview/post-preview.d.ts +29 -0
- package/dist/post-preview/post-preview.js +70 -0
- package/dist/post-preview/public.d.ts +1 -0
- package/dist/post-preview/public.js +6 -0
- package/dist/post-preview/types.d.ts +82 -0
- package/dist/post-preview/types.js +1 -0
- package/dist/primitives/filter-rail.js +17 -2
- package/dist/shell/command-palette.js +2 -0
- package/dist/shell/drag-mode-banner.js +4 -0
- package/dist/shell/extras/shell-scrollable-content.js +2 -2
- package/dist/shell/extras/workbench-layout.js +24 -13
- 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/right-panel.js +10 -1
- package/dist/shell/shell-header.js +7 -1
- package/dist/shell/theme-next-themes.js +7 -2
- package/dist/shell/theme.js +7 -1
- package/dist/shell/use-shell-viewport.js +2 -0
- package/dist/timeline/lane-timeline.js +1 -1
- package/dist/timeline/scrub-bar.js +1 -1
- package/dist/timeline/timeline-tape.js +8 -5
- package/dist/voice/voice-orb-scene.js +4 -4
- package/dist/voice/voice-status-pill.js +8 -7
- package/dist/workflow-builder/edges/default-edge.d.ts +10 -0
- package/dist/workflow-builder/edges/default-edge.js +21 -0
- package/dist/workflow-builder/edges/index.d.ts +3 -0
- package/dist/workflow-builder/edges/index.js +5 -0
- package/dist/workflow-builder/index.d.ts +5 -0
- package/dist/workflow-builder/index.js +12 -0
- package/dist/workflow-builder/internal/trigger-icons.d.ts +4 -0
- package/dist/workflow-builder/internal/trigger-icons.js +43 -0
- package/dist/workflow-builder/internal/trigger-sentence.d.ts +14 -0
- package/dist/workflow-builder/internal/trigger-sentence.js +50 -0
- package/dist/workflow-builder/nodes/action-node.d.ts +3 -0
- package/dist/workflow-builder/nodes/action-node.js +7 -0
- package/dist/workflow-builder/nodes/base-node.d.ts +20 -0
- package/dist/workflow-builder/nodes/base-node.js +18 -0
- package/dist/workflow-builder/nodes/condition-node.d.ts +3 -0
- package/dist/workflow-builder/nodes/condition-node.js +11 -0
- package/dist/workflow-builder/nodes/delay-node.d.ts +3 -0
- package/dist/workflow-builder/nodes/delay-node.js +7 -0
- package/dist/workflow-builder/nodes/end-node.d.ts +3 -0
- package/dist/workflow-builder/nodes/end-node.js +7 -0
- package/dist/workflow-builder/nodes/index.d.ts +13 -0
- package/dist/workflow-builder/nodes/index.js +22 -0
- package/dist/workflow-builder/nodes/trigger-node.d.ts +3 -0
- package/dist/workflow-builder/nodes/trigger-node.js +8 -0
- package/dist/workflow-builder/public.d.ts +1 -0
- package/dist/workflow-builder/public.js +4 -0
- package/dist/workflow-builder/types.d.ts +140 -0
- package/dist/workflow-builder/types.js +18 -0
- package/dist/workflow-builder/workflow-builder.d.ts +10 -0
- package/dist/workflow-builder/workflow-builder.js +22 -0
- package/dist/workflow-builder/workflow-canvas.d.ts +26 -0
- package/dist/workflow-builder/workflow-canvas.js +29 -0
- package/dist/workflow-builder/workflow-card-compact.d.ts +2 -0
- package/dist/workflow-builder/workflow-card-compact.js +22 -0
- package/dist/workflow-builder/workflow-card.d.ts +2 -0
- package/dist/workflow-builder/workflow-card.js +44 -0
- package/dist/workflow-builder/workflow-header.d.ts +2 -0
- package/dist/workflow-builder/workflow-header.js +32 -0
- package/dist/workflow-builder/workflow-toolbox.d.ts +2 -0
- package/dist/workflow-builder/workflow-toolbox.js +19 -0
- package/package.json +72 -50
- package/dist/styles/theme.test.ts +0 -41
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useCallback, useRef } from 'react';
|
|
2
|
+
const SWIPE_THRESHOLD = 50;
|
|
3
|
+
/**
|
|
4
|
+
* Touch swipe detection for carousel navigation. Tracks touchstart/touchend
|
|
5
|
+
* and fires onSwipeLeft / onSwipeRight when the horizontal delta exceeds
|
|
6
|
+
* SWIPE_THRESHOLD (50px). Returns event handlers to spread onto the
|
|
7
|
+
* swipeable container.
|
|
8
|
+
*
|
|
9
|
+
* Ported from `apps/web/src/components/composer/editors/use-swipe.ts`.
|
|
10
|
+
*/
|
|
11
|
+
export function useSwipe({ onSwipeLeft, onSwipeRight }) {
|
|
12
|
+
const touchStartX = useRef(null);
|
|
13
|
+
const onTouchStart = useCallback((e) => {
|
|
14
|
+
touchStartX.current = e.touches[0].clientX;
|
|
15
|
+
}, []);
|
|
16
|
+
const onTouchEnd = useCallback((e) => {
|
|
17
|
+
if (touchStartX.current === null)
|
|
18
|
+
return;
|
|
19
|
+
const deltaX = e.changedTouches[0].clientX - touchStartX.current;
|
|
20
|
+
touchStartX.current = null;
|
|
21
|
+
if (Math.abs(deltaX) < SWIPE_THRESHOLD)
|
|
22
|
+
return;
|
|
23
|
+
if (deltaX < 0)
|
|
24
|
+
onSwipeLeft();
|
|
25
|
+
else
|
|
26
|
+
onSwipeRight();
|
|
27
|
+
}, [onSwipeLeft, onSwipeRight]);
|
|
28
|
+
return { onTouchStart, onTouchEnd };
|
|
29
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PostPreviewBaseProps } from '../types.js';
|
|
2
|
+
export interface BlueSkyLabels {
|
|
3
|
+
placeholder: string;
|
|
4
|
+
share: string;
|
|
5
|
+
moreOptions: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const DEFAULT_BLUESKY_LABELS: BlueSkyLabels;
|
|
8
|
+
export interface BlueSkyPreviewProps extends PostPreviewBaseProps {
|
|
9
|
+
labels?: Partial<BlueSkyLabels>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* BlueSky feed-card preview. Auto-formats the username with `.bsky.social`
|
|
13
|
+
* if no domain is present.
|
|
14
|
+
*/
|
|
15
|
+
export declare function BlueSkyPreview({ displayName, username, avatarUrl, content, mediaUrls, editable, onContentChange, className, labels, }: BlueSkyPreviewProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Heart, MessageCircle, MoreHorizontal, Repeat2, Share } from 'lucide-react';
|
|
4
|
+
import { cn } from '../../lib/utils.js';
|
|
5
|
+
import { Avatar } from '../internal/avatar.js';
|
|
6
|
+
import { Textarea } from '../internal/textarea.js';
|
|
7
|
+
export const DEFAULT_BLUESKY_LABELS = {
|
|
8
|
+
placeholder: "What's up?",
|
|
9
|
+
share: 'Share',
|
|
10
|
+
moreOptions: 'More options',
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* BlueSky feed-card preview. Auto-formats the username with `.bsky.social`
|
|
14
|
+
* if no domain is present.
|
|
15
|
+
*/
|
|
16
|
+
export function BlueSkyPreview({ displayName, username, avatarUrl, content, mediaUrls, editable = false, onContentChange, className, labels, }) {
|
|
17
|
+
const l = { ...DEFAULT_BLUESKY_LABELS, ...labels };
|
|
18
|
+
const handle = username.startsWith('@') ? username.slice(1) : username;
|
|
19
|
+
const formattedHandle = handle.includes('.') ? handle : `${handle}.bsky.social`;
|
|
20
|
+
return (_jsx("div", { "data-slot": "post-preview", "data-platform": "bluesky", className: cn('@container bg-white p-4 text-gray-900 @[420px]:text-[15px]', className), children: _jsxs("div", { className: "flex gap-3", children: [_jsx(Avatar, { src: avatarUrl, displayName: displayName, className: "h-11 w-11 flex-shrink-0" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-baseline gap-1", children: [_jsx("span", { className: "cursor-pointer font-bold text-[15px] text-gray-900 hover:underline", children: displayName }), _jsxs("span", { className: "text-[14px] text-gray-500", children: ["@", formattedHandle] }), _jsx("span", { className: "text-[14px] text-gray-400", children: "\u00B7 now" })] }), _jsx("button", { type: "button", "aria-label": l.moreOptions, className: "-m-1.5 inline-flex min-h-11 min-w-11 items-center justify-center rounded-full p-1.5 text-gray-400 hover:bg-gray-100", children: _jsx(MoreHorizontal, { className: "h-4 w-4" }) })] }), editable ? (_jsx(Textarea, { value: content, onChange: (e) => onContentChange?.(e.target.value), placeholder: l.placeholder, className: "mt-1 min-h-[60px] text-[15px] text-gray-900 placeholder:text-gray-500" })) : (_jsx("p", { className: "mt-1 whitespace-pre-wrap break-words text-[15px]", children: content })), mediaUrls && mediaUrls.length > 0 && (_jsx("div", { className: cn('mt-3 overflow-hidden rounded-xl border border-gray-200', mediaUrls.length === 1 && 'aspect-video', mediaUrls.length === 2 && 'grid grid-cols-2 gap-0.5', mediaUrls.length >= 3 && 'grid grid-cols-2 gap-0.5'), children: mediaUrls.slice(0, 4).map((url, i) => (_jsx("div", { className: cn('bg-gray-100', mediaUrls.length === 1 && 'aspect-video', mediaUrls.length === 2 && 'aspect-square', mediaUrls.length >= 3 && 'aspect-square'), children: _jsx("img", { src: url, alt: `Media ${i + 1}`, loading: "lazy", decoding: "async", className: "h-full w-full object-cover" }) }, url))) })), _jsxs("div", { className: "mt-3 flex items-center gap-1 text-gray-500", children: [_jsxs("button", { type: "button", "aria-label": "Comments", className: "-ml-2 inline-flex min-h-11 min-w-11 items-center gap-1.5 rounded-full p-2 hover:bg-blue-50 hover:text-[#1185FE]", children: [_jsx(MessageCircle, { className: "h-[18px] w-[18px]" }), _jsx("span", { className: "text-[13px]", children: "0" })] }), _jsxs("button", { type: "button", "aria-label": "Repost", className: "inline-flex min-h-11 min-w-11 items-center gap-1.5 rounded-full p-2 hover:bg-green-50 hover:text-green-600", children: [_jsx(Repeat2, { className: "h-[18px] w-[18px]" }), _jsx("span", { className: "text-[13px]", children: "0" })] }), _jsxs("button", { type: "button", "aria-label": "Like", className: "inline-flex min-h-11 min-w-11 items-center gap-1.5 rounded-full p-2 hover:bg-red-50 hover:text-red-500", children: [_jsx(Heart, { className: "h-[18px] w-[18px]" }), _jsx("span", { className: "text-[13px]", children: "0" })] }), _jsx("button", { type: "button", "aria-label": l.share, className: "inline-flex min-h-11 min-w-11 items-center justify-center rounded-full p-2 hover:bg-blue-50 hover:text-[#1185FE]", children: _jsx(Share, { className: "h-[18px] w-[18px]" }) })] })] })] }) }));
|
|
21
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { PostPreviewBaseProps } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Discord embed draft. Mirrors the Discord webhook embed shape.
|
|
4
|
+
*/
|
|
5
|
+
export interface DiscordEmbedDraft {
|
|
6
|
+
title?: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
/** 0xRRGGBB integer (Discord wire format). */
|
|
9
|
+
color?: number;
|
|
10
|
+
url?: string;
|
|
11
|
+
thumbnailUrl?: string;
|
|
12
|
+
imageUrl?: string;
|
|
13
|
+
authorName?: string;
|
|
14
|
+
authorIconUrl?: string;
|
|
15
|
+
fields?: Array<{
|
|
16
|
+
name: string;
|
|
17
|
+
value: string;
|
|
18
|
+
inline?: boolean;
|
|
19
|
+
}>;
|
|
20
|
+
footerText?: string;
|
|
21
|
+
/** ISO 8601 timestamp. */
|
|
22
|
+
timestamp?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface DiscordLabels {
|
|
25
|
+
placeholder: string;
|
|
26
|
+
timestamp: string;
|
|
27
|
+
embedsSuppressed: string;
|
|
28
|
+
reply: string;
|
|
29
|
+
close: string;
|
|
30
|
+
moreOptions: string;
|
|
31
|
+
addReaction: string;
|
|
32
|
+
}
|
|
33
|
+
export declare const DEFAULT_DISCORD_LABELS: DiscordLabels;
|
|
34
|
+
export interface DiscordPreviewProps extends PostPreviewBaseProps {
|
|
35
|
+
labels?: Partial<DiscordLabels>;
|
|
36
|
+
embeds?: DiscordEmbedDraft[];
|
|
37
|
+
/** Hide rendered embeds (matches Discord's "Suppress Embeds" message option). */
|
|
38
|
+
suppressEmbeds?: boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Discord channel message preview. Renders the user message followed by
|
|
42
|
+
* any embeds (unless `suppressEmbeds` is true).
|
|
43
|
+
*/
|
|
44
|
+
export declare function DiscordPreview({ displayName, username: _username, avatarUrl, content, mediaUrls, editable, onContentChange, className, labels, embeds, suppressEmbeds, }: DiscordPreviewProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { MoreHorizontal, Plus, Smile } from 'lucide-react';
|
|
4
|
+
import { cn } from '../../lib/utils.js';
|
|
5
|
+
import { Avatar } from '../internal/avatar.js';
|
|
6
|
+
import { Textarea } from '../internal/textarea.js';
|
|
7
|
+
export const DEFAULT_DISCORD_LABELS = {
|
|
8
|
+
placeholder: 'Message #channel',
|
|
9
|
+
timestamp: 'Today at 12:00 PM',
|
|
10
|
+
embedsSuppressed: 'Embeds are hidden in this preview.',
|
|
11
|
+
reply: 'React',
|
|
12
|
+
close: 'Close',
|
|
13
|
+
moreOptions: 'More options',
|
|
14
|
+
addReaction: 'Add reaction',
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Discord channel message preview. Renders the user message followed by
|
|
18
|
+
* any embeds (unless `suppressEmbeds` is true).
|
|
19
|
+
*/
|
|
20
|
+
export function DiscordPreview({ displayName, username: _username, avatarUrl, content, mediaUrls, editable = false, onContentChange, className, labels, embeds, suppressEmbeds, }) {
|
|
21
|
+
const l = { ...DEFAULT_DISCORD_LABELS, ...labels };
|
|
22
|
+
const visibleEmbeds = (embeds ?? []).filter((embed) => [embed.title, embed.description, embed.url, embed.imageUrl].some((field) => typeof field === 'string' && field.trim().length > 0));
|
|
23
|
+
return (_jsx("div", { "data-slot": "post-preview", "data-platform": "discord", className: cn('@container min-h-[150px] bg-white text-[#313338] @[420px]:text-[15px]', className), children: _jsxs("div", { className: "group relative px-4 py-1 hover:bg-[#F2F3F5]", children: [_jsxs("div", { className: "-top-4 absolute right-4 hidden items-center rounded border border-[#DDDEE1] bg-white shadow-lg group-hover:flex", children: [_jsx("button", { type: "button", "aria-label": l.reply, className: "inline-flex min-h-11 min-w-11 items-center justify-center p-1.5 text-[#747681] hover:bg-[#F2F3F5] hover:text-[#313338]", children: _jsx(Smile, { className: "h-5 w-5" }) }), _jsx("button", { type: "button", "aria-label": l.moreOptions, className: "inline-flex min-h-11 min-w-11 items-center justify-center p-1.5 text-[#747681] hover:bg-[#F2F3F5] hover:text-[#313338]", children: _jsx(MoreHorizontal, { className: "h-5 w-5" }) })] }), _jsxs("div", { className: "flex gap-4 py-0.5", children: [_jsx(Avatar, { src: avatarUrl, displayName: displayName, className: "mt-0.5 h-10 w-10 flex-shrink-0" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-baseline gap-2", children: [_jsx("span", { className: "cursor-pointer font-medium text-[#060607] text-[15px] hover:underline", children: displayName }), _jsx("span", { className: "text-[#5C5E66] text-[11px]", children: l.timestamp })] }), editable ? (_jsx(Textarea, { value: content, onChange: (e) => onContentChange?.(e.target.value), placeholder: l.placeholder, className: "mt-0.5 min-h-[40px] text-[#313338] text-[15px] placeholder:text-[#747681]" })) : (_jsx("p", { className: "mt-0.5 whitespace-pre-wrap break-words text-[#313338] text-[15px]", children: content })), mediaUrls && mediaUrls.length > 0 && (_jsx("div", { className: "mt-2 max-w-md", children: _jsx("div", { className: cn('overflow-hidden rounded', mediaUrls.length === 1 && 'max-w-[400px]', mediaUrls.length >= 2 && 'grid max-w-[400px] grid-cols-2 gap-1'), children: mediaUrls.slice(0, 4).map((url, i) => (_jsx("div", { className: cn('overflow-hidden rounded bg-[#F2F3F5]', mediaUrls.length === 1 && 'aspect-video', mediaUrls.length >= 2 && 'aspect-square'), children: _jsx("img", { src: url, alt: `Media ${i + 1}`, loading: "lazy", decoding: "async", className: "h-full w-full cursor-pointer object-cover hover:opacity-90" }) }, url))) }) })), !suppressEmbeds && visibleEmbeds.length > 0 && (_jsx("div", { "data-slot": "discord-embeds", className: "mt-3 flex max-w-[520px] flex-col gap-2", children: visibleEmbeds.map((embed, index) => (_jsx(DiscordEmbedCard, { embed: embed }, embedKey(embed, index)))) })), suppressEmbeds && (_jsx("p", { "data-slot": "discord-embeds-suppressed", className: "mt-2 text-[#5C5E66] text-xs", children: l.embedsSuppressed })), _jsxs("div", { className: "mt-2 flex items-center gap-1", children: [_jsxs("button", { type: "button", className: "flex min-h-11 items-center gap-1 rounded border border-transparent bg-[#F2F3F5] px-3 py-0.5 text-[14px] hover:border-[#5865F2] hover:bg-[#E8E9EB]", children: [_jsx("span", { "aria-hidden": "true", children: "\uD83D\uDC4D" }), _jsx("span", { className: "text-[#5C5E66] text-[12px]", children: "1" })] }), _jsx("button", { type: "button", "aria-label": l.addReaction, className: "inline-flex min-h-11 min-w-11 items-center justify-center rounded border border-transparent bg-[#F2F3F5] hover:border-[#5865F2] hover:bg-[#E8E9EB]", children: _jsx(Plus, { className: "h-4 w-4 text-[#747681]" }) })] })] })] })] }) }));
|
|
24
|
+
}
|
|
25
|
+
function embedKey(embed, index) {
|
|
26
|
+
return `${embed.title ?? ''}::${embed.url ?? ''}::${embed.imageUrl ?? ''}::${index}`;
|
|
27
|
+
}
|
|
28
|
+
/* v8 ignore next — v8 phantom duplicate function record */
|
|
29
|
+
function DiscordEmbedCard({ embed }) {
|
|
30
|
+
const accent = typeof embed.color === 'number' ? `#${embed.color.toString(16).padStart(6, '0')}` : '#5865f2';
|
|
31
|
+
const href = embed.url?.trim();
|
|
32
|
+
/* v8 ignore next — v8 phantom duplicate function body record */
|
|
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] }));
|
|
34
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { PostPreviewBaseProps } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Facebook preview labels. Override individual entries via the `labels` prop.
|
|
4
|
+
*/
|
|
5
|
+
export interface FacebookLabels {
|
|
6
|
+
justNow: string;
|
|
7
|
+
placeholder: string;
|
|
8
|
+
comments: (count: number) => string;
|
|
9
|
+
shares: (count: number) => string;
|
|
10
|
+
like: string;
|
|
11
|
+
comment: string;
|
|
12
|
+
share: string;
|
|
13
|
+
moreOptions: string;
|
|
14
|
+
}
|
|
15
|
+
export declare const DEFAULT_FACEBOOK_LABELS: FacebookLabels;
|
|
16
|
+
export interface FacebookPreviewProps extends PostPreviewBaseProps {
|
|
17
|
+
labels?: Partial<FacebookLabels>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Facebook feed-card preview. Renders displayName, avatar, content, optional
|
|
21
|
+
* media grid, and the standard Like/Comment/Share footer.
|
|
22
|
+
*/
|
|
23
|
+
export declare function FacebookPreview({ displayName, username: _username, avatarUrl, content, mediaUrls, editable, onContentChange, className, labels, }: FacebookPreviewProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Globe, MessageCircle, MoreHorizontal, Share2, ThumbsUp } from 'lucide-react';
|
|
4
|
+
import { cn } from '../../lib/utils.js';
|
|
5
|
+
import { Avatar } from '../internal/avatar.js';
|
|
6
|
+
import { Textarea } from '../internal/textarea.js';
|
|
7
|
+
export const DEFAULT_FACEBOOK_LABELS = {
|
|
8
|
+
justNow: 'Just now',
|
|
9
|
+
placeholder: "What's on your mind?",
|
|
10
|
+
comments: (count) => `${count} comments`,
|
|
11
|
+
shares: (count) => `${count} shares`,
|
|
12
|
+
like: 'Like',
|
|
13
|
+
comment: 'Comment',
|
|
14
|
+
share: 'Share',
|
|
15
|
+
moreOptions: 'More options',
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Facebook feed-card preview. Renders displayName, avatar, content, optional
|
|
19
|
+
* media grid, and the standard Like/Comment/Share footer.
|
|
20
|
+
*/
|
|
21
|
+
/* v8 ignore next 11 — v8 phantom duplicate function record */
|
|
22
|
+
export function FacebookPreview({ displayName, username: _username, avatarUrl, content, mediaUrls, editable = false, onContentChange, className, labels, }) {
|
|
23
|
+
const l = { ...DEFAULT_FACEBOOK_LABELS, ...labels };
|
|
24
|
+
/* v8 ignore next — v8 phantom duplicate function body record at this position */
|
|
25
|
+
return (_jsxs("div", { "data-slot": "post-preview", "data-platform": "facebook", className: cn('@container bg-white text-gray-900 @[420px]:text-[15px] dark:bg-[#242526] dark:text-[#E4E6EB]', className), children: [_jsxs("div", { className: "p-4", children: [_jsxs("div", { className: "flex items-start justify-between", children: [_jsxs("div", { className: "flex gap-3", children: [_jsx(Avatar, { src: avatarUrl, displayName: displayName, className: "h-10 w-10" }), _jsxs("div", { children: [_jsx("p", { className: "cursor-pointer font-semibold text-[15px] text-gray-900 hover:underline", children: displayName }), _jsxs("div", { className: "flex items-center gap-1 text-[13px] text-gray-500", children: [_jsx("span", { children: l.justNow }), _jsx("span", { children: "\u00B7" }), _jsx(Globe, { className: "h-3 w-3", "aria-hidden": "true" })] })] })] }), _jsx("button", { type: "button", "aria-label": l.moreOptions, className: "-m-2 inline-flex min-h-11 min-w-11 items-center justify-center rounded-full p-2 text-gray-500 hover:bg-gray-100", children: _jsx(MoreHorizontal, { className: "h-5 w-5" }) })] }), editable ? (_jsx(Textarea, { value: content, onChange: (e) => onContentChange?.(e.target.value), placeholder: l.placeholder, className: "mt-3 min-h-[60px] text-[15px] text-gray-900 placeholder:text-gray-500" })) : (_jsx("p", { className: "mt-3 whitespace-pre-wrap break-words text-[15px]", children: content }))] }), mediaUrls && mediaUrls.length > 0 && (_jsx("div", { className: cn('bg-gray-100', mediaUrls.length === 1 && 'aspect-video', mediaUrls.length === 2 && 'grid grid-cols-2 gap-0.5', mediaUrls.length === 3 && 'grid grid-cols-2 gap-0.5', mediaUrls.length >= 4 && 'grid grid-cols-2 gap-0.5'), children: mediaUrls.slice(0, 4).map((url, i) => (_jsxs("div", { className: cn('relative bg-gray-200', mediaUrls.length === 1 && 'aspect-video', mediaUrls.length === 2 && 'aspect-square', mediaUrls.length === 3 && i === 0 && 'row-span-2', mediaUrls.length >= 4 && 'aspect-square'), children: [_jsx("img", { src: url, alt: `Media ${i + 1}`, loading: "lazy", decoding: "async", className: "h-full w-full object-cover" }), mediaUrls.length > 4 && i === 3 && (_jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black/50", children: _jsxs("span", { className: "font-semibold text-2xl text-white", children: ["+", mediaUrls.length - 4] }) }))] }, url))) })), _jsxs("div", { className: "flex items-center justify-between px-4 py-2 text-[13px] text-gray-500", children: [_jsxs("div", { className: "flex items-center gap-1", children: [_jsxs("div", { className: "flex -space-x-1", children: [_jsx("div", { className: "flex h-[18px] w-[18px] items-center justify-center rounded-full bg-[#1877F2]", children: _jsx(ThumbsUp, { className: "h-2.5 w-2.5 text-white", fill: "currentColor" }) }), _jsx("div", { className: "flex h-[18px] w-[18px] items-center justify-center rounded-full bg-red-500", children: _jsx("span", { className: "text-[10px]", "aria-hidden": "true", children: "\u2665" }) })] }), _jsx("span", { children: "0" })] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("span", { children: l.comments(0) }), _jsx("span", { children: l.shares(0) })] })] }), _jsx("div", { className: "mx-4 border-gray-200 border-t" }), _jsxs("div", { className: "flex items-center justify-around px-2 py-1", children: [_jsxs("button", { type: "button", className: "flex min-h-11 flex-1 items-center justify-center gap-2 rounded-lg py-2 text-gray-600 hover:bg-gray-100", children: [_jsx(ThumbsUp, { className: "h-5 w-5" }), _jsx("span", { className: "font-semibold text-[15px]", children: l.like })] }), _jsxs("button", { type: "button", className: "flex min-h-11 flex-1 items-center justify-center gap-2 rounded-lg py-2 text-gray-600 hover:bg-gray-100", children: [_jsx(MessageCircle, { className: "h-5 w-5" }), _jsx("span", { className: "font-semibold text-[15px]", children: l.comment })] }), _jsxs("button", { type: "button", className: "flex min-h-11 flex-1 items-center justify-center gap-2 rounded-lg py-2 text-gray-600 hover:bg-gray-100", children: [_jsx(Share2, { className: "h-5 w-5" }), _jsx("span", { className: "font-semibold text-[15px]", children: l.share })] })] })] }));
|
|
26
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { PlatformId, PostPreviewBaseProps } from '../types.js';
|
|
2
|
+
export interface GenericLabels {
|
|
3
|
+
placeholderTemplate: (platform: string) => string;
|
|
4
|
+
badgeTemplate: (platform: string) => string;
|
|
5
|
+
moreOptions: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const DEFAULT_GENERIC_LABELS: GenericLabels;
|
|
8
|
+
export interface GenericPreviewProps extends PostPreviewBaseProps {
|
|
9
|
+
/**
|
|
10
|
+
* The platform this preview represents. Accepts a known `PlatformId` (in
|
|
11
|
+
* which case the per-platform display name is used) or any free-form
|
|
12
|
+
* string for unknown platforms.
|
|
13
|
+
*/
|
|
14
|
+
platform: PlatformId | (string & {});
|
|
15
|
+
labels?: Partial<GenericLabels>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Generic preview. Used as a fallback for any platform without a dedicated
|
|
19
|
+
* component. Layout is intentionally neutral so it composes well inside
|
|
20
|
+
* `PlatformChrome` if a consumer wants to add device framing.
|
|
21
|
+
*/
|
|
22
|
+
export declare function GenericPreview({ platform, displayName, username, avatarUrl, content, mediaUrls, editable, onContentChange, className, labels, }: GenericPreviewProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { MoreHorizontal } from 'lucide-react';
|
|
4
|
+
import { cn } from '../../lib/utils.js';
|
|
5
|
+
import { Avatar } from '../internal/avatar.js';
|
|
6
|
+
import { PLATFORM_META } from '../internal/platform-meta.js';
|
|
7
|
+
import { Textarea } from '../internal/textarea.js';
|
|
8
|
+
export const DEFAULT_GENERIC_LABELS = {
|
|
9
|
+
placeholderTemplate: (platform) => `Write your ${platform} post...`,
|
|
10
|
+
badgeTemplate: (platform) => `${platform} preview`,
|
|
11
|
+
moreOptions: 'More options',
|
|
12
|
+
};
|
|
13
|
+
function resolvePlatformName(platform) {
|
|
14
|
+
if (platform in PLATFORM_META) {
|
|
15
|
+
return PLATFORM_META[platform].displayName;
|
|
16
|
+
}
|
|
17
|
+
return platform;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Generic preview. Used as a fallback for any platform without a dedicated
|
|
21
|
+
* component. Layout is intentionally neutral so it composes well inside
|
|
22
|
+
* `PlatformChrome` if a consumer wants to add device framing.
|
|
23
|
+
*/
|
|
24
|
+
/* v8 ignore next 12 — v8 phantom duplicate function record */
|
|
25
|
+
export function GenericPreview({ platform, displayName, username, avatarUrl, content, mediaUrls, editable = false, onContentChange, className, labels, }) {
|
|
26
|
+
const l = { ...DEFAULT_GENERIC_LABELS, ...labels };
|
|
27
|
+
const platformName = resolvePlatformName(platform);
|
|
28
|
+
/* v8 ignore next — v8 phantom duplicate function body record */
|
|
29
|
+
return (_jsxs("div", { "data-slot": "post-preview", "data-platform": "generic", "data-generic-platform": platform, className: cn('@container bg-white p-4 text-gray-900 @[420px]:text-[14px] dark:bg-neutral-950 dark:text-neutral-100', className), children: [_jsxs("div", { className: "flex gap-3", children: [_jsx(Avatar, { src: avatarUrl, displayName: displayName, className: "h-10 w-10 flex-shrink-0" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx("span", { className: "truncate font-semibold text-[14px]", children: displayName }), _jsxs("span", { className: "truncate text-[13px] text-gray-500", children: ["@", username] })] }), _jsx("button", { type: "button", "aria-label": l.moreOptions, className: "-m-1.5 inline-flex min-h-11 min-w-11 items-center justify-center rounded-full p-1.5 text-gray-500 hover:bg-gray-100 dark:hover:bg-neutral-800", children: _jsx(MoreHorizontal, { className: "h-4 w-4" }) })] }), editable ? (_jsx(Textarea, { value: content, onChange: (e) => onContentChange?.(e.target.value), placeholder: l.placeholderTemplate(platformName), className: "mt-2 min-h-[60px] text-[14px] placeholder:text-gray-500" })) : (_jsx("p", { className: "mt-2 whitespace-pre-wrap break-words text-[14px]", children: content })), mediaUrls && mediaUrls.length > 0 && (_jsx("div", { className: "mt-3 overflow-hidden rounded-lg", children: mediaUrls.length === 1 ? (_jsx("div", { className: "aspect-video bg-gray-200", children: _jsx("img", { src: mediaUrls[0], alt: "Post media", loading: "lazy", decoding: "async", className: "h-full w-full object-cover" }) })) : (_jsx("div", { className: "grid grid-cols-2 gap-1", children: mediaUrls.slice(0, 4).map((url, i) => (_jsx("div", { className: "aspect-square bg-gray-200", children: _jsx("img", { src: url, alt: `Media ${i + 1}`, loading: "lazy", decoding: "async", className: "h-full w-full object-cover" }) }, url))) })) }))] })] }), _jsx("div", { className: "mt-3 border-gray-200 border-t pt-3 dark:border-neutral-800", children: _jsx("p", { className: "text-gray-500 text-xs", children: l.badgeTemplate(platformName) }) })] }));
|
|
30
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { PostPreviewBaseProps } from '../types.js';
|
|
2
|
+
export interface GoogleBusinessLabels {
|
|
3
|
+
placeholder: string;
|
|
4
|
+
postedJustNow: string;
|
|
5
|
+
learnMore: string;
|
|
6
|
+
like: string;
|
|
7
|
+
share: string;
|
|
8
|
+
moreOptions: string;
|
|
9
|
+
}
|
|
10
|
+
export declare const DEFAULT_GOOGLE_BUSINESS_LABELS: GoogleBusinessLabels;
|
|
11
|
+
export interface GoogleBusinessPreviewProps extends PostPreviewBaseProps {
|
|
12
|
+
labels?: Partial<GoogleBusinessLabels>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Google Business Profile update-post preview. Renders the business name,
|
|
16
|
+
* timestamp, content, optional media, and a Learn-more CTA button.
|
|
17
|
+
*/
|
|
18
|
+
export declare function GoogleBusinessPreview({ displayName, username: _username, avatarUrl, content, mediaUrls, editable, onContentChange, className, labels, }: GoogleBusinessPreviewProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Building2, ExternalLink, MoreVertical, ThumbsUp } from 'lucide-react';
|
|
4
|
+
import { cn } from '../../lib/utils.js';
|
|
5
|
+
import { Avatar } from '../internal/avatar.js';
|
|
6
|
+
import { Textarea } from '../internal/textarea.js';
|
|
7
|
+
export const DEFAULT_GOOGLE_BUSINESS_LABELS = {
|
|
8
|
+
placeholder: 'Share an update about your business...',
|
|
9
|
+
postedJustNow: 'Posted just now',
|
|
10
|
+
learnMore: 'Learn more',
|
|
11
|
+
like: 'Like',
|
|
12
|
+
share: 'Share',
|
|
13
|
+
moreOptions: 'More options',
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Google Business Profile update-post preview. Renders the business name,
|
|
17
|
+
* timestamp, content, optional media, and a Learn-more CTA button.
|
|
18
|
+
*/
|
|
19
|
+
/* v8 ignore next 11 — v8 phantom duplicate function record */
|
|
20
|
+
export function GoogleBusinessPreview({ displayName, username: _username, avatarUrl, content, mediaUrls, editable = false, onContentChange, className, labels, }) {
|
|
21
|
+
const l = { ...DEFAULT_GOOGLE_BUSINESS_LABELS, ...labels };
|
|
22
|
+
/* v8 ignore next — v8 phantom duplicate function body record */
|
|
23
|
+
return (_jsxs("div", { "data-slot": "post-preview", "data-platform": "google_business", className: cn('@container overflow-hidden rounded-lg border border-gray-200 bg-white text-gray-900 @[420px]:text-[14px] dark:border-neutral-800 dark:bg-neutral-900 dark:text-neutral-100', className), children: [_jsxs("div", { className: "flex items-start justify-between p-4", children: [_jsxs("div", { className: "flex gap-3", children: [avatarUrl ? (_jsx(Avatar, { src: avatarUrl, displayName: displayName, className: "h-10 w-10 rounded" })) : (_jsx("span", { role: "img", "aria-label": displayName, className: "inline-flex h-10 w-10 shrink-0 items-center justify-center overflow-hidden rounded bg-[#4285F4] text-white", children: _jsx(Building2, { className: "h-5 w-5" }) })), _jsxs("div", { children: [_jsx("p", { className: "font-medium text-[14px] text-gray-900", children: displayName }), _jsx("p", { className: "text-[12px] text-gray-500", children: l.postedJustNow })] })] }), _jsx("button", { type: "button", "aria-label": l.moreOptions, className: "inline-flex min-h-11 min-w-11 items-center justify-center rounded-full p-1 text-gray-500 hover:bg-gray-100", children: _jsx(MoreVertical, { className: "h-5 w-5" }) })] }), _jsx("div", { className: "px-4 pb-3", children: editable ? (_jsx(Textarea, { value: content, onChange: (e) => onContentChange?.(e.target.value), placeholder: l.placeholder, className: "min-h-[80px] text-[14px] text-gray-900 placeholder:text-gray-500" })) : (_jsx("p", { className: "whitespace-pre-wrap break-words text-[14px]", children: content })) }), mediaUrls && mediaUrls.length > 0 && (_jsx("div", { className: "aspect-video bg-gray-100", children: _jsx("img", { src: mediaUrls[0], alt: "Post media", loading: "lazy", decoding: "async", className: "h-full w-full object-cover" }) })), _jsx("div", { className: "border-gray-100 border-t p-4", children: _jsxs("button", { type: "button", className: "flex min-h-11 w-full items-center justify-center gap-2 rounded-md bg-[#1A73E8] px-4 py-2 font-medium text-[14px] text-white hover:bg-[#1557B0]", children: [_jsx("span", { children: l.learnMore }), _jsx(ExternalLink, { className: "h-4 w-4" })] }) }), _jsxs("div", { className: "flex items-center gap-4 px-4 pb-4", children: [_jsxs("button", { type: "button", className: "inline-flex min-h-11 items-center gap-1.5 text-gray-600 hover:text-[#1A73E8]", "aria-label": l.like, children: [_jsx(ThumbsUp, { className: "h-4 w-4" }), _jsx("span", { className: "text-[13px]", children: l.like })] }), _jsxs("button", { type: "button", "aria-label": l.share, className: "inline-flex min-h-11 items-center gap-1.5 text-gray-600 hover:text-[#1A73E8]", children: [_jsx("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: _jsx("path", { d: "M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92s2.92-1.31 2.92-2.92-1.31-2.92-2.92-2.92z" }) }), _jsx("span", { className: "text-[13px]", children: l.share })] })] })] }));
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type { BlueSkyLabels, BlueSkyPreviewProps } from './bluesky.js';
|
|
2
|
+
export { BlueSkyPreview, DEFAULT_BLUESKY_LABELS } from './bluesky.js';
|
|
3
|
+
export type { DiscordEmbedDraft, DiscordLabels, DiscordPreviewProps, } from './discord.js';
|
|
4
|
+
export { DEFAULT_DISCORD_LABELS, DiscordPreview } from './discord.js';
|
|
5
|
+
export type { FacebookLabels, FacebookPreviewProps } from './facebook.js';
|
|
6
|
+
export { DEFAULT_FACEBOOK_LABELS, FacebookPreview } from './facebook.js';
|
|
7
|
+
export type { GenericLabels, GenericPreviewProps } from './generic.js';
|
|
8
|
+
export { DEFAULT_GENERIC_LABELS, GenericPreview } from './generic.js';
|
|
9
|
+
export type { GoogleBusinessLabels, GoogleBusinessPreviewProps, } from './google-business.js';
|
|
10
|
+
export { DEFAULT_GOOGLE_BUSINESS_LABELS, GoogleBusinessPreview, } from './google-business.js';
|
|
11
|
+
export type { InstagramLabels, InstagramPostType, InstagramPreviewProps, } from './instagram.js';
|
|
12
|
+
export { DEFAULT_INSTAGRAM_LABELS, InstagramPreview } from './instagram.js';
|
|
13
|
+
export type { LinkedInLabels, LinkedInMentionData, LinkedInMentionPickerContext, LinkedInPreviewProps, } from './linkedin.js';
|
|
14
|
+
export { DEFAULT_LINKEDIN_LABELS, LinkedInPreview } from './linkedin.js';
|
|
15
|
+
export type { TelegramInlineKeyboardButton, TelegramInlineKeyboardMarkup, TelegramLabels, TelegramPollState, TelegramPreviewProps, } from './telegram.js';
|
|
16
|
+
export { DEFAULT_TELEGRAM_LABELS, TelegramPreview } from './telegram.js';
|
|
17
|
+
export type { ThreadsLabels, ThreadsPreviewProps } from './threads.js';
|
|
18
|
+
export { DEFAULT_THREADS_LABELS, ThreadsPreview } from './threads.js';
|
|
19
|
+
export type { TikTokLabels, TikTokPreviewProps } from './tiktok.js';
|
|
20
|
+
export { DEFAULT_TIKTOK_LABELS, TikTokPreview } from './tiktok.js';
|
|
21
|
+
export type { TwitterLabels, TwitterMentionPickerContext, TwitterPreviewProps, TwitterTheme, } from './twitter.js';
|
|
22
|
+
export { DEFAULT_TWITTER_LABELS, TwitterPreview } from './twitter.js';
|
|
23
|
+
export type { YouTubeLabels, YouTubePreviewProps } from './youtube.js';
|
|
24
|
+
export { DEFAULT_YOUTUBE_LABELS, YouTubePreview } from './youtube.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Re-exports for the post-preview platforms barrel.
|
|
2
|
+
export { BlueSkyPreview, DEFAULT_BLUESKY_LABELS } from './bluesky.js';
|
|
3
|
+
export { DEFAULT_DISCORD_LABELS, DiscordPreview } from './discord.js';
|
|
4
|
+
export { DEFAULT_FACEBOOK_LABELS, FacebookPreview } from './facebook.js';
|
|
5
|
+
export { DEFAULT_GENERIC_LABELS, GenericPreview } from './generic.js';
|
|
6
|
+
export { DEFAULT_GOOGLE_BUSINESS_LABELS, GoogleBusinessPreview, } from './google-business.js';
|
|
7
|
+
export { DEFAULT_INSTAGRAM_LABELS, InstagramPreview } from './instagram.js';
|
|
8
|
+
export { DEFAULT_LINKEDIN_LABELS, LinkedInPreview } from './linkedin.js';
|
|
9
|
+
export { DEFAULT_TELEGRAM_LABELS, TelegramPreview } from './telegram.js';
|
|
10
|
+
export { DEFAULT_THREADS_LABELS, ThreadsPreview } from './threads.js';
|
|
11
|
+
export { DEFAULT_TIKTOK_LABELS, TikTokPreview } from './tiktok.js';
|
|
12
|
+
export { DEFAULT_TWITTER_LABELS, TwitterPreview } from './twitter.js';
|
|
13
|
+
export { DEFAULT_YOUTUBE_LABELS, YouTubePreview } from './youtube.js';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { PostPreviewBaseProps } from '../types.js';
|
|
2
|
+
export type InstagramPostType = 'feed' | 'carousel' | 'reel' | 'story';
|
|
3
|
+
export interface InstagramLabels {
|
|
4
|
+
placeholder: string;
|
|
5
|
+
justNow: string;
|
|
6
|
+
likes: (count: number) => string;
|
|
7
|
+
addImage: string;
|
|
8
|
+
moreOptions: string;
|
|
9
|
+
like: string;
|
|
10
|
+
comment: string;
|
|
11
|
+
share: string;
|
|
12
|
+
save: string;
|
|
13
|
+
carouselPrev: string;
|
|
14
|
+
carouselNext: string;
|
|
15
|
+
carouselCounter: (current: number, total: number) => string;
|
|
16
|
+
reelLabel: string;
|
|
17
|
+
reelAudio: string;
|
|
18
|
+
storyLabel: string;
|
|
19
|
+
storySendMessage: string;
|
|
20
|
+
}
|
|
21
|
+
export declare const DEFAULT_INSTAGRAM_LABELS: InstagramLabels;
|
|
22
|
+
export interface InstagramPreviewProps extends PostPreviewBaseProps {
|
|
23
|
+
labels?: Partial<InstagramLabels>;
|
|
24
|
+
/** Override the layout. Auto-detects to `carousel` for >1 media. */
|
|
25
|
+
instagramPostType?: InstagramPostType;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Instagram preview supporting Feed / Carousel / Reel / Story layouts.
|
|
29
|
+
* Auto-detects `carousel` when more than one media URL is present.
|
|
30
|
+
*/
|
|
31
|
+
export declare function InstagramPreview({ displayName, username, avatarUrl, content, mediaUrls, editable, onContentChange, className, labels, instagramPostType, }: InstagramPreviewProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { Bookmark, ChevronLeft, ChevronRight, Heart, MessageCircle, MoreHorizontal, Music, Send, } from 'lucide-react';
|
|
4
|
+
import { useCallback, useState } from 'react';
|
|
5
|
+
import { cn } from '../../lib/utils.js';
|
|
6
|
+
import { Avatar } from '../internal/avatar.js';
|
|
7
|
+
import { Textarea } from '../internal/textarea.js';
|
|
8
|
+
import { useSwipe } from '../internal/use-swipe.js';
|
|
9
|
+
export const DEFAULT_INSTAGRAM_LABELS = {
|
|
10
|
+
placeholder: 'Write a caption...',
|
|
11
|
+
justNow: 'Just now',
|
|
12
|
+
likes: (count) => `${count} likes`,
|
|
13
|
+
addImage: 'Add an image to preview',
|
|
14
|
+
moreOptions: 'More options',
|
|
15
|
+
like: 'Like',
|
|
16
|
+
comment: 'Comment',
|
|
17
|
+
share: 'Share',
|
|
18
|
+
save: 'Save',
|
|
19
|
+
carouselPrev: 'Previous slide',
|
|
20
|
+
carouselNext: 'Next slide',
|
|
21
|
+
carouselCounter: (current, total) => `${current} / ${total}`,
|
|
22
|
+
reelLabel: 'Reels',
|
|
23
|
+
reelAudio: 'Original audio',
|
|
24
|
+
storyLabel: 'Story',
|
|
25
|
+
storySendMessage: 'Send message',
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Instagram preview supporting Feed / Carousel / Reel / Story layouts.
|
|
29
|
+
* Auto-detects `carousel` when more than one media URL is present.
|
|
30
|
+
*/
|
|
31
|
+
export function InstagramPreview({ displayName, username, avatarUrl, content, mediaUrls, editable = false, onContentChange, className, labels, instagramPostType, }) {
|
|
32
|
+
const l = { ...DEFAULT_INSTAGRAM_LABELS, ...labels };
|
|
33
|
+
const formattedUsername = username.startsWith('@') ? username.slice(1) : username;
|
|
34
|
+
const hasMedia = mediaUrls !== undefined && mediaUrls.length > 0;
|
|
35
|
+
const postType = instagramPostType ?? (mediaUrls && mediaUrls.length > 1 ? 'carousel' : 'feed');
|
|
36
|
+
if (postType === 'reel') {
|
|
37
|
+
return (_jsx(ReelPreview, { displayName: displayName, username: formattedUsername, avatarUrl: avatarUrl, content: content, mediaUrl: hasMedia ? mediaUrls[0] : undefined, labels: l, className: className }));
|
|
38
|
+
}
|
|
39
|
+
if (postType === 'story') {
|
|
40
|
+
return (_jsx(StoryPreview, { displayName: displayName, username: formattedUsername, avatarUrl: avatarUrl, mediaUrl: hasMedia ? mediaUrls[0] : undefined, labels: l, className: className }));
|
|
41
|
+
}
|
|
42
|
+
return (_jsx(FeedPreview, { displayName: displayName, username: formattedUsername, avatarUrl: avatarUrl, content: content, mediaUrls: mediaUrls, editable: editable, onContentChange: onContentChange, isCarousel: postType === 'carousel', labels: l, className: className }));
|
|
43
|
+
}
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Feed / Carousel layout
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
function FeedPreview({ displayName, username, avatarUrl, content, mediaUrls, editable, onContentChange, isCarousel, labels, className, }) {
|
|
48
|
+
const hasMedia = mediaUrls !== undefined && mediaUrls.length > 0;
|
|
49
|
+
const [carouselIndex, setCarouselIndex] = useState(0);
|
|
50
|
+
const goToPrev = useCallback(() => {
|
|
51
|
+
setCarouselIndex((prev) => Math.max(0, prev - 1));
|
|
52
|
+
}, []);
|
|
53
|
+
const goToNext = useCallback(() => {
|
|
54
|
+
/* v8 ignore next — mediaUrls is always defined when goToNext is reachable (carousel requires >1 url) */
|
|
55
|
+
if (!mediaUrls)
|
|
56
|
+
return;
|
|
57
|
+
setCarouselIndex((prev) => Math.min(mediaUrls.length - 1, prev + 1));
|
|
58
|
+
}, [mediaUrls]);
|
|
59
|
+
const swipe = useSwipe({ onSwipeLeft: goToNext, onSwipeRight: goToPrev });
|
|
60
|
+
return (_jsxs("div", { "data-slot": "post-preview", "data-platform": "instagram", "data-instagram-post-type": isCarousel ? 'carousel' : 'feed', className: cn('@container bg-white text-gray-900 @[420px]:text-[15px] dark:bg-black dark:text-white', className), children: [_jsxs("div", { className: "flex items-center justify-between p-3", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Avatar, { src: avatarUrl, displayName: displayName, className: "h-8 w-8 ring-2 ring-pink-500 ring-offset-2" }), _jsx("p", { className: "font-semibold text-[14px]", children: username })] }), _jsx("button", { type: "button", "aria-label": labels.moreOptions, className: "-m-2 inline-flex min-h-11 min-w-11 items-center justify-center p-2", children: _jsx(MoreHorizontal, { className: "h-5 w-5" }) })] }), hasMedia ? (_jsxs("div", { "data-slot": "instagram-carousel-track", className: "relative aspect-square bg-gray-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500", style: { touchAction: 'pan-y' }, ...(isCarousel && mediaUrls.length > 1
|
|
61
|
+
? {
|
|
62
|
+
role: 'region',
|
|
63
|
+
'aria-label': labels.carouselCounter(carouselIndex + 1, mediaUrls.length),
|
|
64
|
+
tabIndex: 0,
|
|
65
|
+
onKeyDown: (event) => {
|
|
66
|
+
if (event.key === 'ArrowLeft') {
|
|
67
|
+
event.preventDefault();
|
|
68
|
+
event.stopPropagation();
|
|
69
|
+
goToPrev();
|
|
70
|
+
}
|
|
71
|
+
else if (event.key === 'ArrowRight') {
|
|
72
|
+
event.preventDefault();
|
|
73
|
+
event.stopPropagation();
|
|
74
|
+
goToNext();
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
: {}), ...swipe, children: [_jsx("img", { src: mediaUrls[isCarousel ? carouselIndex : 0], alt: "Post", loading: "lazy", decoding: "async", className: "h-full w-full object-cover" }), isCarousel && mediaUrls.length > 1 && (_jsxs(_Fragment, { children: [carouselIndex > 0 && (_jsx("button", { type: "button", onClick: goToPrev, "aria-label": labels.carouselPrev, className: "-translate-y-1/2 absolute top-1/2 left-2 inline-flex h-7 w-7 items-center justify-center rounded-full bg-black/50 text-white hover:bg-black/70", children: _jsx(ChevronLeft, { className: "h-4 w-4" }) })), carouselIndex < mediaUrls.length - 1 && (_jsx("button", { type: "button", onClick: goToNext, "aria-label": labels.carouselNext, className: "-translate-y-1/2 absolute top-1/2 right-2 inline-flex h-7 w-7 items-center justify-center rounded-full bg-black/50 text-white hover:bg-black/70", children: _jsx(ChevronRight, { className: "h-4 w-4" }) })), _jsx("div", { className: "absolute top-3 right-3 rounded-full bg-black/60 px-2.5 py-0.5 font-medium text-white text-xs", children: labels.carouselCounter(carouselIndex + 1, mediaUrls.length) }), _jsxs("div", { className: "-translate-x-1/2 absolute bottom-4 left-1/2 flex gap-1", children: [mediaUrls.slice(0, 5).map((url, i) => (_jsx("button", { type: "button", "aria-label": `Go to slide ${i + 1}`, ...(i === carouselIndex ? { 'aria-current': 'true' } : {}), onClick: () => setCarouselIndex(i), className: cn(
|
|
79
|
+
// Visible dot stays small; expand the touch target via padding so the
|
|
80
|
+
// hit area is at least 44x44px without breaking the IG visual.
|
|
81
|
+
'inline-flex min-h-11 min-w-11 items-center justify-center p-2'), children: _jsx("span", { "aria-hidden": "true", className: cn('h-1.5 w-1.5 rounded-full transition-colors', i === carouselIndex ? 'bg-blue-500' : 'bg-white/60') }) }, url))), mediaUrls.length > 5 && (_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-white/40", "aria-hidden": "true" }))] })] }))] })) : (_jsx(MediaPlaceholder, { label: labels.addImage })), _jsxs("div", { className: "p-3", children: [_jsxs("div", { className: "mb-3 flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-4", children: [_jsx("button", { type: "button", "aria-label": labels.like, className: "-m-1 inline-flex min-h-11 min-w-11 items-center justify-center p-1 hover:opacity-60", children: _jsx(Heart, { className: "h-6 w-6" }) }), _jsx("button", { type: "button", "aria-label": labels.comment, className: "-m-1 inline-flex min-h-11 min-w-11 items-center justify-center p-1 hover:opacity-60", children: _jsx(MessageCircle, { className: "-scale-x-100 h-6 w-6" }) }), _jsx("button", { type: "button", "aria-label": labels.share, className: "-m-1 inline-flex min-h-11 min-w-11 items-center justify-center p-1 hover:opacity-60", children: _jsx(Send, { className: "-rotate-12 h-6 w-6" }) })] }), _jsx("button", { type: "button", "aria-label": labels.save, className: "-m-1 inline-flex min-h-11 min-w-11 items-center justify-center p-1 hover:opacity-60", children: _jsx(Bookmark, { className: "h-6 w-6" }) })] }), _jsx("p", { className: "mb-1 font-semibold text-[14px]", children: labels.likes(0) }), _jsxs("div", { className: "text-[14px]", children: [_jsx("span", { className: "mr-1 font-semibold", children: username }), editable ? (_jsx(Textarea, { value: content, onChange: (e) => onContentChange?.(e.target.value), placeholder: labels.placeholder, className: "mt-1 inline min-h-[40px] w-full text-[14px] text-gray-900 placeholder:text-gray-500" })) : (_jsx("span", { className: "whitespace-pre-wrap break-words", children: content }))] }), _jsx("p", { className: "mt-2 text-[10px] text-gray-400 uppercase tracking-wide", children: labels.justNow })] })] }));
|
|
82
|
+
}
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// Reel layout
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
/* v8 ignore next 17 — v8 phantom duplicate function record */
|
|
87
|
+
function ReelPreview({ displayName, username, avatarUrl, content, mediaUrl, labels, className, }) {
|
|
88
|
+
/* v8 ignore next — v8 phantom duplicate function body record */
|
|
89
|
+
return (_jsxs("div", { "data-slot": "post-preview", "data-platform": "instagram", "data-instagram-post-type": "reel", className: cn('@container relative aspect-[9/16] overflow-hidden rounded-lg bg-black @[420px]:rounded-xl', className), children: [mediaUrl ? (_jsx("img", { src: mediaUrl, alt: "Reel", loading: "lazy", decoding: "async", className: "h-full w-full object-cover" })) : (_jsx("div", { className: "flex h-full w-full items-center justify-center bg-gradient-to-b from-gray-800 to-gray-900", children: _jsxs("div", { className: "text-center text-gray-500", children: [_jsx("div", { className: "mx-auto mb-2 h-16 w-16 rounded-full border-2 border-gray-600 border-dashed" }), _jsx("p", { className: "text-sm", children: labels.reelLabel })] }) })), _jsx("div", { className: "absolute top-4 right-3 font-semibold text-sm text-white drop-shadow-lg", children: labels.reelLabel }), _jsxs("div", { className: "absolute right-3 bottom-20 flex flex-col items-center gap-5", children: [_jsxs("button", { type: "button", "aria-label": labels.like, className: "inline-flex min-h-11 min-w-11 flex-col items-center gap-0.5", children: [_jsx(Heart, { className: "h-7 w-7 text-white drop-shadow-lg" }), _jsx("span", { className: "text-[11px] text-white drop-shadow-lg", children: "0" })] }), _jsxs("button", { type: "button", "aria-label": labels.comment, className: "inline-flex min-h-11 min-w-11 flex-col items-center gap-0.5", children: [_jsx(MessageCircle, { className: "-scale-x-100 h-7 w-7 text-white drop-shadow-lg" }), _jsx("span", { className: "text-[11px] text-white drop-shadow-lg", children: "0" })] }), _jsx("button", { type: "button", "aria-label": labels.share, className: "inline-flex min-h-11 min-w-11 flex-col items-center gap-0.5", children: _jsx(Send, { className: "-rotate-12 h-7 w-7 text-white drop-shadow-lg" }) }), _jsx("button", { type: "button", "aria-label": labels.moreOptions, className: "inline-flex min-h-11 min-w-11 flex-col items-center gap-0.5", children: _jsx(MoreHorizontal, { className: "h-7 w-7 text-white drop-shadow-lg" }) }), _jsx(Avatar, { src: avatarUrl, displayName: displayName, className: "h-8 w-8 rounded-md border-2 border-white/40" })] }), _jsxs("div", { className: "absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/70 via-black/30 to-transparent p-4 pb-5", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [_jsx(Avatar, { src: avatarUrl, displayName: displayName, className: "h-8 w-8" }), _jsx("span", { className: "font-semibold text-sm text-white", children: username })] }), content ? _jsx("p", { className: "line-clamp-2 text-sm text-white/90", children: content }) : null, _jsxs("div", { className: "mt-3 flex items-center gap-2", children: [_jsx(Music, { className: "h-3 w-3 text-white/80", "aria-hidden": "true" }), _jsxs("p", { className: "flex-1 truncate text-white/80 text-xs", children: [labels.reelAudio, " \u00B7 ", username] })] })] })] }));
|
|
90
|
+
}
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// Story layout
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
/* v8 ignore next 15 — v8 phantom duplicate function record */
|
|
95
|
+
function StoryPreview({ displayName, username, avatarUrl, mediaUrl, labels, className, }) {
|
|
96
|
+
/* v8 ignore next — v8 phantom duplicate function body record */
|
|
97
|
+
return (_jsxs("div", { "data-slot": "post-preview", "data-platform": "instagram", "data-instagram-post-type": "story", className: cn('@container relative aspect-[9/16] overflow-hidden rounded-lg bg-black @[420px]:rounded-xl', className), children: [mediaUrl ? (_jsx("img", { src: mediaUrl, alt: "Story", loading: "lazy", decoding: "async", className: "h-full w-full object-cover" })) : (_jsx("div", { className: "flex h-full w-full items-center justify-center bg-gradient-to-b from-gray-800 to-gray-900", children: _jsxs("div", { className: "text-center text-gray-500", children: [_jsx("div", { className: "mx-auto mb-2 h-16 w-16 rounded-full border-2 border-gray-600 border-dashed" }), _jsx("p", { className: "text-sm", children: labels.storyLabel })] }) })), _jsx("div", { className: "absolute inset-x-0 top-0 px-2 pt-2", children: _jsx("div", { className: "h-0.5 w-full overflow-hidden rounded-full bg-white/30", children: _jsx("div", { className: "h-full w-1/3 rounded-full bg-white" }) }) }), _jsxs("div", { className: "absolute inset-x-0 top-3 flex items-center gap-2 px-4 pt-1", children: [_jsx(Avatar, { src: avatarUrl, displayName: displayName, className: "h-8 w-8 ring-2 ring-white/60" }), _jsx("span", { className: "font-semibold text-sm text-white drop-shadow-lg", children: username }), _jsx("span", { className: "text-white/70 text-xs drop-shadow-lg", children: labels.justNow }), _jsx("div", { className: "ml-auto", children: _jsx(MoreHorizontal, { className: "h-5 w-5 text-white drop-shadow-lg", "aria-hidden": "true" }) })] }), _jsx("div", { className: "absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/50 to-transparent p-4 pt-8", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex-1 rounded-full border border-white/40 px-4 py-2", children: _jsx("span", { className: "text-sm text-white/60", children: labels.storySendMessage }) }), _jsx(Heart, { className: "h-6 w-6 text-white", "aria-hidden": "true" }), _jsx(Send, { className: "-rotate-12 h-6 w-6 text-white", "aria-hidden": "true" })] }) })] }));
|
|
98
|
+
}
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
// Shared
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
function MediaPlaceholder({ label }) {
|
|
103
|
+
return (_jsx("div", { className: "flex aspect-square items-center justify-center bg-gradient-to-br from-gray-100 to-gray-200", children: _jsxs("div", { className: "text-center text-gray-400", children: [_jsx("svg", { className: "mx-auto mb-2 h-16 w-16", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1, d: "M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" }) }), _jsx("p", { className: "text-sm", children: label })] }) }));
|
|
104
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { PostPreviewBaseProps } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* LinkedIn mention metadata. Wire format used by the LinkedIn share API:
|
|
5
|
+
* `@[Name](urn:li:person:abc)` is rendered as a plain `Name` token in the
|
|
6
|
+
* UI but the spans here let consumers persist the mention range +
|
|
7
|
+
* URN reference.
|
|
8
|
+
*/
|
|
9
|
+
export interface LinkedInMentionData {
|
|
10
|
+
/** Zero-based start offset within the post content. */
|
|
11
|
+
offset: number;
|
|
12
|
+
/** Length of the rendered name in the post content. */
|
|
13
|
+
length: number;
|
|
14
|
+
/** LinkedIn URN, e.g. `urn:li:person:ABC123`. */
|
|
15
|
+
urn: string;
|
|
16
|
+
/** Display name to highlight. */
|
|
17
|
+
name: string;
|
|
18
|
+
}
|
|
19
|
+
export interface LinkedInMentionPickerContext {
|
|
20
|
+
query: string;
|
|
21
|
+
onPick: (mention: LinkedInMentionData) => void;
|
|
22
|
+
onCancel: () => void;
|
|
23
|
+
}
|
|
24
|
+
export interface LinkedInLabels {
|
|
25
|
+
placeholder: string;
|
|
26
|
+
now: string;
|
|
27
|
+
moreOptions: string;
|
|
28
|
+
comments: (count: number) => string;
|
|
29
|
+
reposts: (count: number) => string;
|
|
30
|
+
like: string;
|
|
31
|
+
comment: string;
|
|
32
|
+
repost: string;
|
|
33
|
+
send: string;
|
|
34
|
+
}
|
|
35
|
+
export declare const DEFAULT_LINKEDIN_LABELS: LinkedInLabels;
|
|
36
|
+
export interface LinkedInPreviewProps extends PostPreviewBaseProps {
|
|
37
|
+
labels?: Partial<LinkedInLabels>;
|
|
38
|
+
/** Mention spans to highlight in rendered content. */
|
|
39
|
+
mentions?: LinkedInMentionData[];
|
|
40
|
+
/** Called when the user picks a new mention via `renderMentionPicker`. */
|
|
41
|
+
onMentionsChange?: (mentions: LinkedInMentionData[]) => void;
|
|
42
|
+
/** Optional render slot for an `@mention` picker. Called with the
|
|
43
|
+
* current query (text after `@`) when the user types `@xxx` in the
|
|
44
|
+
* editable textarea. */
|
|
45
|
+
renderMentionPicker?: (ctx: LinkedInMentionPickerContext) => ReactNode;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* LinkedIn feed-card preview. Renders avatar, name, headline (`username`),
|
|
49
|
+
* content with optional `@mention` highlight spans, media grid, and the
|
|
50
|
+
* standard Like/Comment/Repost/Send footer.
|
|
51
|
+
*/
|
|
52
|
+
export declare function LinkedInPreview({ displayName, username, avatarUrl, content, mediaUrls, editable, onContentChange, className, labels, mentions, onMentionsChange, renderMentionPicker, }: LinkedInPreviewProps): import("react/jsx-runtime").JSX.Element;
|