@flamingo-stack/openframe-frontend-core 0.0.705 → 0.0.706-snapshot.20261006190707
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/{chunk-4KX6OED2.cjs → chunk-2G7UYK35.cjs} +46 -46
- package/dist/{chunk-KOWVJD4V.cjs → chunk-2KXTOP6G.cjs} +70 -57
- package/dist/{chunk-A75QY6CD.cjs → chunk-2RNB5DTN.cjs} +21 -21
- package/dist/{chunk-PN2RKPDF.js → chunk-36BCTE7D.js} +10 -10
- package/dist/{chunk-ZSAGLQOD.cjs → chunk-3AHTOSXJ.cjs} +17 -17
- package/dist/{chunk-VR2ZLFCP.js → chunk-4IBPRIQH.js} +1 -1
- package/dist/{chunk-FCGC42TS.js → chunk-4L6XCMOU.js} +66 -62
- package/dist/{chunk-X5MQ7JRP.js → chunk-4UM2ETLM.js} +9 -9
- package/dist/{chunk-6NJKKO73.cjs → chunk-6I4ZAFO2.cjs} +15 -15
- package/dist/{chunk-RBDP4WDE.js → chunk-6IW7TWPV.js} +6 -6
- package/dist/{chunk-MMOODW57.cjs → chunk-6NTPY5QA.cjs} +1887 -94
- package/dist/{chunk-QFXRPRFT.js → chunk-6YKAT7NV.js} +3 -3
- package/dist/{chunk-HPWWNJE7.cjs → chunk-AOWDNSTM.cjs} +89 -89
- package/dist/{chunk-IHERL5V4.cjs → chunk-B73373AG.cjs} +34 -34
- package/dist/{chunk-DENB2XJC.js → chunk-CNWOMQQJ.js} +2 -2
- package/dist/{chunk-MP2C3LJ4.js → chunk-CUME3VTQ.js} +1 -1
- package/dist/{chunk-PDWVPI3S.js → chunk-CZ7Y7UG4.js} +4 -72
- package/dist/{chunk-YHM5AWTT.js → chunk-CZSZSDD4.js} +6 -6
- package/dist/{chunk-F6I6XZMN.js → chunk-DA5UDTLG.js} +5 -5
- package/dist/{chunk-MFFCUVWG.js → chunk-F57ECE2I.js} +1 -1
- package/dist/{chunk-CH5232HV.js → chunk-FI6GMDO7.js} +2455 -662
- package/dist/{chunk-NDESEPW5.js → chunk-FXJ3WWUB.js} +68 -0
- package/dist/{chunk-G5A3PSPV.js → chunk-HDOYSEOJ.js} +1080 -691
- package/dist/{chunk-VA5T32XA.js → chunk-I3ST4PLM.js} +2 -2
- package/dist/{chunk-5HBHO5TB.js → chunk-JPG7OHEG.js} +1 -1
- package/dist/{chunk-YFBM2K6X.cjs → chunk-JRU2CXLB.cjs} +4 -4
- package/dist/{chunk-MKXO4Q5P.js → chunk-KUC673HM.js} +383 -142
- package/dist/{chunk-JEZKGCZI.cjs → chunk-L6Y6OOKG.cjs} +26 -26
- package/dist/{chunk-F6AFPNDF.js → chunk-LFFB6EMB.js} +29 -16
- package/dist/{chunk-X72J4ACL.cjs → chunk-LLSUVPQC.cjs} +252 -8
- package/dist/{chunk-RKV5B7EU.cjs → chunk-LM7TXMLU.cjs} +72 -4
- package/dist/{chunk-6SEANMOU.js → chunk-LNW45N2C.js} +11 -11
- package/dist/{chunk-OUY3HHW6.cjs → chunk-LRQTJOF2.cjs} +928 -539
- package/dist/{chunk-DW7G4KG3.js → chunk-MR3A4MZV.js} +3 -3
- package/dist/{chunk-2YXVWZVF.js → chunk-N45D4ZXQ.js} +959 -715
- package/dist/{chunk-Z2VNQQ74.js → chunk-NCPTID44.js} +978 -942
- package/dist/{chunk-WA6GUYQ4.cjs → chunk-NSGXARGJ.cjs} +104 -104
- package/dist/{chunk-ABU3TGXA.cjs → chunk-OF6GEI5A.cjs} +3 -3
- package/dist/{chunk-IKTQ3ZAG.cjs → chunk-OJLR5MEO.cjs} +6 -6
- package/dist/{chunk-VESKITRQ.cjs → chunk-P42V6N34.cjs} +736 -732
- package/dist/{chunk-OHFS7VOU.cjs → chunk-PLYEKV3N.cjs} +2 -2
- package/dist/{chunk-7VVFKJY5.js → chunk-PO33GZVY.js} +1799 -3449
- package/dist/{chunk-7LS22BZU.cjs → chunk-QAOFMK2A.cjs} +14 -14
- package/dist/{chunk-VHCACDKP.cjs → chunk-RQRX4YC3.cjs} +39 -39
- package/dist/{chunk-3MX5S4CF.cjs → chunk-SEXWHIMS.cjs} +899 -2549
- package/dist/{chunk-AQWZNP6A.js → chunk-SS5MBHDP.js} +4 -4
- package/dist/{chunk-ZSAO2HBW.js → chunk-TDYIREDJ.js} +15 -15
- package/dist/{chunk-XOYH2YQE.cjs → chunk-UDFRUKLC.cjs} +2 -2
- package/dist/{chunk-TYWLNCNH.cjs → chunk-UVTD52MD.cjs} +14 -82
- package/dist/{chunk-N3COXDBH.cjs → chunk-VX4E6YSB.cjs} +41 -5
- package/dist/{chunk-2QEUFXNS.cjs → chunk-WCVZCGK5.cjs} +312 -71
- package/dist/{chunk-6XIF2Z7I.js → chunk-WMX26HB5.js} +3 -3
- package/dist/{chunk-D7BTSVMS.cjs → chunk-Y2GSOUCS.cjs} +2 -2
- package/dist/{chunk-4I45Y3DN.cjs → chunk-YBUT4AMG.cjs} +6 -6
- package/dist/components/case-studies/index.cjs +30 -32
- package/dist/components/case-studies/index.js +14 -16
- package/dist/components/chat/entity-cards/case-study-card.d.ts +11 -4
- package/dist/components/chat/entity-cards/case-study-card.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +11 -11
- package/dist/components/chat/index.d.ts +1 -1
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +16 -16
- package/dist/components/chat/nav-link-anchor-via-runtime.d.ts +34 -5
- package/dist/components/chat/nav-link-anchor-via-runtime.d.ts.map +1 -1
- package/dist/components/chat/utils/icon-library.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +12 -14
- package/dist/components/contact/index.js +11 -13
- package/dist/components/docs/index.cjs +15 -17
- package/dist/components/docs/index.js +14 -16
- package/dist/components/embeds/index.cjs +12 -14
- package/dist/components/embeds/index.js +11 -13
- package/dist/components/faq/index.cjs +13 -15
- package/dist/components/faq/index.js +12 -14
- package/dist/components/features/index.cjs +12 -14
- package/dist/components/features/index.js +21 -23
- package/dist/components/help-center-pages/index.cjs +118 -120
- package/dist/components/help-center-pages/index.js +33 -35
- package/dist/components/icons/index.cjs +3 -5
- package/dist/components/icons/index.js +9 -11
- package/dist/components/icons-v2-generated/index.cjs +3 -5
- package/dist/components/icons-v2-generated/index.js +2 -4
- package/dist/components/index.cjs +87 -158
- package/dist/components/index.d.ts +0 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +331 -402
- package/dist/components/loading/page-layout-skeleton.d.ts +1 -1
- package/dist/components/meeting-scheduler/index.cjs +62 -64
- package/dist/components/meeting-scheduler/index.js +12 -14
- package/dist/components/navigation/index.cjs +28 -16
- package/dist/components/navigation/index.d.ts +14 -10
- package/dist/components/navigation/index.d.ts.map +1 -1
- package/dist/components/navigation/index.js +38 -26
- package/dist/components/navigation/mingo-ai-button.d.ts +38 -3
- package/dist/components/navigation/mingo-ai-button.d.ts.map +1 -1
- package/dist/components/navigation/mobile-nav-sheet-id.d.ts +3 -0
- package/dist/components/navigation/mobile-nav-sheet-id.d.ts.map +1 -0
- package/dist/components/navigation/mobile-nav-sheet.d.ts +48 -0
- package/dist/components/navigation/mobile-nav-sheet.d.ts.map +1 -0
- package/dist/components/navigation/nav-item-row.d.ts +27 -0
- package/dist/components/navigation/nav-item-row.d.ts.map +1 -0
- package/dist/components/navigation/nav-panel.d.ts +43 -0
- package/dist/components/navigation/nav-panel.d.ts.map +1 -0
- package/dist/components/navigation/navigation-sidebar-item.d.ts +1 -2
- package/dist/components/navigation/navigation-sidebar-item.d.ts.map +1 -1
- package/dist/components/navigation/site-footer.d.ts +28 -0
- package/dist/components/navigation/site-footer.d.ts.map +1 -0
- package/dist/components/navigation/site-header.d.ts +61 -0
- package/dist/components/navigation/site-header.d.ts.map +1 -0
- package/dist/components/navigation/site-nav-link.d.ts +26 -0
- package/dist/components/navigation/site-nav-link.d.ts.map +1 -0
- package/dist/components/navigation/top-navigation.d.ts +2 -2
- package/dist/components/navigation/top-navigation.d.ts.map +1 -1
- package/dist/components/onboarding-guides/index.cjs +15 -17
- package/dist/components/onboarding-guides/index.js +14 -16
- package/dist/components/platform/ToolBadge.d.ts +6 -0
- package/dist/components/platform/ToolBadge.d.ts.map +1 -1
- package/dist/components/related-content/index.cjs +10 -8
- package/dist/components/related-content/index.js +9 -7
- package/dist/components/tickets/index.cjs +16 -18
- package/dist/components/tickets/index.js +15 -17
- package/dist/components/ui/brand-association-card.d.ts +7 -0
- package/dist/components/ui/brand-association-card.d.ts.map +1 -1
- package/dist/components/ui/index.cjs +14 -14
- package/dist/components/ui/index.js +19 -19
- package/dist/hooks/index.cjs +19 -11
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +19 -11
- package/dist/hooks/ui/index.d.ts +3 -0
- package/dist/hooks/ui/index.d.ts.map +1 -1
- package/dist/hooks/ui/use-active-section.d.ts +10 -0
- package/dist/hooks/ui/use-active-section.d.ts.map +1 -0
- package/dist/hooks/ui/use-nav-menus.d.ts +48 -0
- package/dist/hooks/ui/use-nav-menus.d.ts.map +1 -0
- package/dist/hooks/ui/use-visitor-os.d.ts +15 -0
- package/dist/hooks/ui/use-visitor-os.d.ts.map +1 -0
- package/dist/hooks/use-location-hash.d.ts +9 -0
- package/dist/hooks/use-location-hash.d.ts.map +1 -0
- package/dist/index.cjs +56 -21
- package/dist/index.js +231 -196
- package/dist/types/case-study.d.ts +9 -0
- package/dist/types/case-study.d.ts.map +1 -1
- package/dist/types/navigation.d.ts +95 -65
- package/dist/types/navigation.d.ts.map +1 -1
- package/dist/utils/index.cjs +55 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +50 -1
- package/dist/utils/source-icons.d.ts.map +1 -1
- package/dist/utils/visitor-os.d.ts +45 -0
- package/dist/utils/visitor-os.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/components/chat/entity-cards/case-study-card.tsx +115 -5
- package/src/components/chat/index.ts +7 -1
- package/src/components/chat/nav-link-anchor-via-runtime.tsx +62 -9
- package/src/components/chat/utils/icon-library.tsx +35 -1
- package/src/components/index.ts +0 -2
- package/src/components/loading/page-layout-skeleton.tsx +1 -1
- package/src/components/navigation/index.ts +21 -14
- package/src/components/navigation/mingo-ai-button.tsx +139 -63
- package/src/components/navigation/mobile-nav-sheet-id.ts +2 -0
- package/src/components/navigation/mobile-nav-sheet.tsx +241 -0
- package/src/components/navigation/nav-item-row.tsx +129 -0
- package/src/components/navigation/nav-panel.tsx +196 -0
- package/src/components/navigation/navigation-sidebar-item.tsx +1 -2
- package/src/components/navigation/site-footer.tsx +256 -0
- package/src/components/navigation/site-header.tsx +339 -0
- package/src/components/navigation/site-nav-link.tsx +41 -0
- package/src/components/navigation/ticket-alerts-button.tsx +2 -2
- package/src/components/navigation/top-navigation.tsx +2 -2
- package/src/components/platform/ToolBadge.tsx +18 -1
- package/src/components/ui/brand-association-card.tsx +25 -3
- package/src/hooks/index.ts +1 -0
- package/src/hooks/ui/index.ts +3 -0
- package/src/hooks/ui/use-active-section.ts +44 -0
- package/src/hooks/ui/use-nav-menus.ts +200 -0
- package/src/hooks/ui/use-visitor-os.ts +33 -0
- package/src/hooks/use-location-hash.ts +34 -0
- package/src/styles/chat-animations.css +57 -2
- package/src/types/case-study.ts +16 -0
- package/src/types/navigation.ts +80 -66
- package/src/utils/index.ts +1 -0
- package/src/utils/source-icons.ts +13 -0
- package/src/utils/visitor-os.ts +84 -0
- package/dist/chunk-LMBC5FFP.js +0 -43
- package/dist/chunk-O43V3EOS.js +0 -252
- package/dist/chunk-QZVSJF2K.cjs +0 -252
- package/dist/chunk-RALRPFDW.cjs +0 -43
- package/dist/components/footer-waitlist-button.d.ts +0 -29
- package/dist/components/footer-waitlist-button.d.ts.map +0 -1
- package/dist/components/footer.d.ts +0 -47
- package/dist/components/footer.d.ts.map +0 -1
- package/dist/components/navigation/client-only-header.d.ts +0 -8
- package/dist/components/navigation/client-only-header.d.ts.map +0 -1
- package/dist/components/navigation/header-skeleton.d.ts +0 -12
- package/dist/components/navigation/header-skeleton.d.ts.map +0 -1
- package/dist/components/navigation/header.d.ts +0 -9
- package/dist/components/navigation/header.d.ts.map +0 -1
- package/dist/components/navigation/mobile-nav-panel.d.ts +0 -10
- package/dist/components/navigation/mobile-nav-panel.d.ts.map +0 -1
- package/src/components/footer-waitlist-button.tsx +0 -53
- package/src/components/footer.tsx +0 -195
- package/src/components/navigation/client-only-header.tsx +0 -22
- package/src/components/navigation/header-skeleton.tsx +0 -74
- package/src/components/navigation/header.tsx +0 -429
- package/src/components/navigation/mobile-nav-panel.tsx +0 -155
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* CaseStudyCard (pure presentation).
|
|
5
|
-
* detail)
|
|
4
|
+
* CaseStudyCard (pure presentation). Four densities — `default` (vertical
|
|
5
|
+
* detail), `sm` (compact horizontal for chat-inline), `portrait` (rails and
|
|
6
|
+
* strips) and `menu` (the site menu's card, hover-plays the highlight cut).
|
|
6
7
|
*
|
|
7
8
|
* The card writes NO click logic — callers wrap with their own anchor
|
|
8
9
|
* and pass the resolved detail URL via `href`.
|
|
@@ -12,9 +13,15 @@
|
|
|
12
13
|
* `useOgPlaceholderUrl(...)`) → `bg-ods-bg`.
|
|
13
14
|
*/
|
|
14
15
|
|
|
16
|
+
import { useState } from 'react';
|
|
15
17
|
import Image from '../../../embed-shims/next-image';
|
|
16
|
-
import
|
|
18
|
+
import Link from '../../../embed-shims/next-link';
|
|
19
|
+
import type { CaseStudyCardData } from '../../../types/case-study';
|
|
17
20
|
import { cn } from '../../../utils/cn';
|
|
21
|
+
import { sortBitesByFeaturedAtDesc } from '../../features/video-bites-shared';
|
|
22
|
+
import { CardHoverPlay } from '../../features/video-center-badge';
|
|
23
|
+
import { VideoHoverPreviewSurface } from '../../features/video-hover-preview';
|
|
24
|
+
import { EntityIcon } from '../../icon-display';
|
|
18
25
|
import { Card } from '../../ui/card';
|
|
19
26
|
import {
|
|
20
27
|
COMPACT_CARD_IMAGE_SLOT,
|
|
@@ -36,7 +43,7 @@ import { useEntityCardLink } from './use-entity-card-link';
|
|
|
36
43
|
import { useEntityCardPlaceholder } from './use-entity-card-placeholder';
|
|
37
44
|
|
|
38
45
|
export interface CaseStudyCardProps {
|
|
39
|
-
study:
|
|
46
|
+
study: CaseStudyCardData;
|
|
40
47
|
/** Detail URL resolved by the caller. */
|
|
41
48
|
href: string;
|
|
42
49
|
/** When `_blank`, opens in a new tab. Set by chat dispatch via
|
|
@@ -46,7 +53,14 @@ export interface CaseStudyCardProps {
|
|
|
46
53
|
targetPlatform?: string | null;
|
|
47
54
|
/** OG placeholder URL, used when `study.featured_image` is missing. */
|
|
48
55
|
placeholderUrl?: string | null;
|
|
49
|
-
|
|
56
|
+
/** `menu` is the site menu's card: the story's video plays muted on hover or
|
|
57
|
+
* focus (the video bites' hover grammar), then who it is about and who tells it. */
|
|
58
|
+
size?: 'default' | 'sm' | 'portrait' | 'menu';
|
|
59
|
+
/** `menu`: mount the hover player (the host passes whether its panel is open,
|
|
60
|
+
* so a closed menu holds no player). Omitted: the card's own viewport gate. */
|
|
61
|
+
mediaMounted?: boolean;
|
|
62
|
+
/** `menu`: runs on click, before the navigation (the host closes its panel). */
|
|
63
|
+
onNavigate?: () => void;
|
|
50
64
|
/** Portrait density: render the content-type chip. Mixed rails only; single-type rails pass false. Default true. */
|
|
51
65
|
showTypeBadge?: boolean;
|
|
52
66
|
className?: string;
|
|
@@ -92,6 +106,22 @@ export function CaseStudyCardSkeleton({ size = 'default' }: { size?: 'default' |
|
|
|
92
106
|
);
|
|
93
107
|
}
|
|
94
108
|
|
|
109
|
+
/**
|
|
110
|
+
* A story's HIGHLIGHT cut, the short video a card previews: its highlight
|
|
111
|
+
* video, else the bite an admin featured (the newest when several are; featuring
|
|
112
|
+
* is independent of the bite's own `published` flag, as on every featured surface). Never
|
|
113
|
+
* the full video: that plays on the story's own page. Null when it has neither.
|
|
114
|
+
*/
|
|
115
|
+
function caseStudyHighlight(study: CaseStudyCardData): { url: string; posterUrl: string | null } | null {
|
|
116
|
+
if (study.highlight_video_url) {
|
|
117
|
+
return { url: study.highlight_video_url, posterUrl: study.highlight_video_thumbnail ?? null };
|
|
118
|
+
}
|
|
119
|
+
const bite = (study.video_bites ?? [])
|
|
120
|
+
.filter(candidate => candidate.featured === true && candidate.url)
|
|
121
|
+
.sort(sortBitesByFeaturedAtDesc)[0];
|
|
122
|
+
return bite ? { url: bite.url, posterUrl: bite.thumbnail_url ?? null } : null;
|
|
123
|
+
}
|
|
124
|
+
|
|
95
125
|
export function CaseStudyCard({
|
|
96
126
|
study,
|
|
97
127
|
href,
|
|
@@ -101,8 +131,11 @@ export function CaseStudyCard({
|
|
|
101
131
|
placeholderUrl: placeholderUrlProp,
|
|
102
132
|
size = 'default',
|
|
103
133
|
showTypeBadge = true,
|
|
134
|
+
mediaMounted,
|
|
135
|
+
onNavigate,
|
|
104
136
|
className,
|
|
105
137
|
}: CaseStudyCardProps) {
|
|
138
|
+
const [hovered, setHovered] = useState(false);
|
|
106
139
|
const { target, rel } = useEntityCardLink({
|
|
107
140
|
href,
|
|
108
141
|
targetPlatform,
|
|
@@ -149,6 +182,83 @@ export function CaseStudyCard({
|
|
|
149
182
|
);
|
|
150
183
|
}
|
|
151
184
|
|
|
185
|
+
if (size === 'menu') {
|
|
186
|
+
const preview = caseStudyHighlight(study);
|
|
187
|
+
const videoUrl = preview?.url ?? null;
|
|
188
|
+
const hasAnyVideo = !!(videoUrl || study.main_video_url);
|
|
189
|
+
// The cover is the FULL video's poster (wide, like the card); the highlight
|
|
190
|
+
// cut's own frame is usually upright and would not fill a 16:9 slot.
|
|
191
|
+
const poster = study.main_video_thumbnail || study.featured_image || preview?.posterUrl || null;
|
|
192
|
+
return (
|
|
193
|
+
<Link
|
|
194
|
+
href={href}
|
|
195
|
+
target={target}
|
|
196
|
+
rel={rel}
|
|
197
|
+
onClick={onNavigate}
|
|
198
|
+
onPointerEnter={() => setHovered(true)}
|
|
199
|
+
onPointerLeave={() => setHovered(false)}
|
|
200
|
+
onFocus={() => setHovered(true)}
|
|
201
|
+
onBlur={() => setHovered(false)}
|
|
202
|
+
className={cn(
|
|
203
|
+
// `group/card` is what the shared play glyph accents on.
|
|
204
|
+
'group/card group flex min-w-0 flex-col gap-[var(--spacing-system-sf)] rounded-xl border border-ods-border bg-ods-bg p-[var(--spacing-system-mf)] text-ods-text-primary transition-colors hover:bg-ods-bg-hover focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ods-accent',
|
|
205
|
+
className,
|
|
206
|
+
)}
|
|
207
|
+
>
|
|
208
|
+
{/* The media never takes the click: the whole card is the link. */}
|
|
209
|
+
<span className="pointer-events-none relative block aspect-video w-full shrink-0 overflow-hidden rounded-md bg-ods-bg">
|
|
210
|
+
{videoUrl ? (
|
|
211
|
+
// The shared hover surface, filled the way the walkthrough card fills
|
|
212
|
+
// its own 16:9 slot (`cover`): an upright cut is cropped to the slot,
|
|
213
|
+
// never letterboxed. Sound on hover, muted when the browser blocks it.
|
|
214
|
+
<VideoHoverPreviewSurface
|
|
215
|
+
url={videoUrl}
|
|
216
|
+
posterUrl={poster}
|
|
217
|
+
active={hovered}
|
|
218
|
+
playerMounted={mediaMounted}
|
|
219
|
+
fit="cover"
|
|
220
|
+
posterSizes="360px"
|
|
221
|
+
/>
|
|
222
|
+
) : (
|
|
223
|
+
<>
|
|
224
|
+
{(poster ?? placeholderUrl) && (
|
|
225
|
+
<Image
|
|
226
|
+
src={poster ?? placeholderUrl ?? ''}
|
|
227
|
+
alt=""
|
|
228
|
+
className="object-cover"
|
|
229
|
+
fill
|
|
230
|
+
sizes="360px"
|
|
231
|
+
unoptimized
|
|
232
|
+
onError={hideOnError}
|
|
233
|
+
/>
|
|
234
|
+
)}
|
|
235
|
+
{hasAnyVideo && <CardHoverPlay size="md" />}
|
|
236
|
+
</>
|
|
237
|
+
)}
|
|
238
|
+
</span>
|
|
239
|
+
{study.msp?.name && (
|
|
240
|
+
<span className="flex items-center gap-[var(--spacing-system-xsf)] text-ods-text-secondary text-h6">
|
|
241
|
+
{study.msp.icon_url && (
|
|
242
|
+
<EntityIcon
|
|
243
|
+
icon={{ url: study.msp.icon_url }}
|
|
244
|
+
size={20}
|
|
245
|
+
className="size-5 shrink-0 rounded object-contain"
|
|
246
|
+
/>
|
|
247
|
+
)}
|
|
248
|
+
<span className="min-w-0 truncate">{study.msp.name}</span>
|
|
249
|
+
</span>
|
|
250
|
+
)}
|
|
251
|
+
<span className="line-clamp-2 text-h6">{study.title}</span>
|
|
252
|
+
{study.user?.full_name && (
|
|
253
|
+
<span className="truncate text-ods-text-muted text-h6">
|
|
254
|
+
{study.user.job_title ? `${study.user.full_name}, ${study.user.job_title}` : study.user.full_name}
|
|
255
|
+
</span>
|
|
256
|
+
)}
|
|
257
|
+
<span className="mt-auto text-ods-accent text-h6">{hasAnyVideo ? 'Watch the story' : 'Read the story'}</span>
|
|
258
|
+
</Link>
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
152
262
|
if (size === 'portrait') {
|
|
153
263
|
// Rail/strip density — shared <EntityPortraitCard> shell.
|
|
154
264
|
return (
|
|
@@ -61,7 +61,13 @@ export { remarkCardLinks } from './remark-card-links';
|
|
|
61
61
|
export { remarkMentionChips } from './remark-mention-chips';
|
|
62
62
|
|
|
63
63
|
// Card-supporting UI migrated from hub `components/shared/*` + `components/blog/*`
|
|
64
|
-
export {
|
|
64
|
+
export {
|
|
65
|
+
NavLinkAnchorViaRuntime,
|
|
66
|
+
type NavLinkAnchorViaRuntimeProps,
|
|
67
|
+
type NavLinkViaRuntimeInput,
|
|
68
|
+
type NavLinkViaRuntimeProps,
|
|
69
|
+
useNavLinkViaRuntime,
|
|
70
|
+
} from './nav-link-anchor-via-runtime';
|
|
65
71
|
export { SourceActionButton, type SourceActionButtonProps, type SourceActionDensity } from './source-action-button';
|
|
66
72
|
export {
|
|
67
73
|
ChatAttachmentAddButton,
|
|
@@ -18,11 +18,14 @@
|
|
|
18
18
|
* the lib should never silently fall back when the runtime is missing
|
|
19
19
|
* (the chat tree always provides one in both host + embed modes).
|
|
20
20
|
*
|
|
21
|
-
*
|
|
22
|
-
* the
|
|
21
|
+
* The same component serves a host's own chrome and pages (site header,
|
|
22
|
+
* footer, call-to-action links): the host mounts the runtime app-wide, names
|
|
23
|
+
* the destination's platform in `targetPlatform`, and may pass its own
|
|
24
|
+
* `onClick` (it runs first; `preventDefault()` cancels the navigation) and
|
|
25
|
+
* plain anchor attributes (`aria-*`, `title`, `data-*`, `style`, `rel`).
|
|
23
26
|
*/
|
|
24
27
|
|
|
25
|
-
import type { ReactNode, MouseEvent } from 'react';
|
|
28
|
+
import type { AnchorHTMLAttributes, ReactNode, MouseEvent } from 'react';
|
|
26
29
|
import { useRequiredChatRuntime } from '../../contexts/chat-runtime-context';
|
|
27
30
|
import { useRouter } from '../../embed-shims/next-navigation';
|
|
28
31
|
import { useChatPanel } from './chat-panel-context';
|
|
@@ -30,7 +33,10 @@ import { resolveHrefForRuntime } from './utils/chat-nav-resolution';
|
|
|
30
33
|
import { executeNavigation } from './utils/execute-navigation';
|
|
31
34
|
import { computeIsNewTab, newTabAnchorAttrs } from './utils/nav-anchor-props';
|
|
32
35
|
|
|
33
|
-
export interface NavLinkAnchorViaRuntimeProps
|
|
36
|
+
export interface NavLinkAnchorViaRuntimeProps extends Omit<
|
|
37
|
+
AnchorHTMLAttributes<HTMLAnchorElement>,
|
|
38
|
+
'href' | 'target' | 'onClick' | 'className' | 'children'
|
|
39
|
+
> {
|
|
34
40
|
href: string;
|
|
35
41
|
path?: string | null;
|
|
36
42
|
targetPlatform?: string | null;
|
|
@@ -38,15 +44,41 @@ export interface NavLinkAnchorViaRuntimeProps {
|
|
|
38
44
|
/** Optional — matches `NavLinkAnchorComponent`'s contract so the
|
|
39
45
|
* markdown-anchor slot can render an empty anchor (rare but legal). */
|
|
40
46
|
children?: ReactNode;
|
|
47
|
+
/** Runs before the navigation; `preventDefault()` cancels it. */
|
|
48
|
+
onClick?: (event: MouseEvent<HTMLAnchorElement>) => void;
|
|
41
49
|
}
|
|
42
50
|
|
|
43
|
-
export
|
|
51
|
+
export interface NavLinkViaRuntimeInput {
|
|
52
|
+
href: string;
|
|
53
|
+
path?: string | null;
|
|
54
|
+
/** The platform that owns `href`, by name. */
|
|
55
|
+
targetPlatform?: string | null;
|
|
56
|
+
/** Runs before the navigation; `preventDefault()` cancels it. */
|
|
57
|
+
onClick?: (event: MouseEvent<HTMLAnchorElement>) => void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface NavLinkViaRuntimeProps {
|
|
61
|
+
href: string;
|
|
62
|
+
target?: '_blank';
|
|
63
|
+
rel?: 'noopener' | 'noopener noreferrer';
|
|
64
|
+
onClick: (event: MouseEvent<HTMLAnchorElement>) => void;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The anchor props of {@link NavLinkAnchorViaRuntime}, for an element that is
|
|
69
|
+
* not a bare `<a>` (a `Button`'s `linkProps`). ONE decision and ONE click
|
|
70
|
+
* handler for both forms.
|
|
71
|
+
*
|
|
72
|
+
* A new-tab link to a NAMED platform is one of our own sites and keeps the
|
|
73
|
+
* Referer (`noopener` alone: the cross-domain analytics attribute the visit by
|
|
74
|
+
* it); any other new-tab link also gets `noreferrer`.
|
|
75
|
+
*/
|
|
76
|
+
export function useNavLinkViaRuntime({
|
|
44
77
|
href,
|
|
45
78
|
path,
|
|
46
79
|
targetPlatform,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}: NavLinkAnchorViaRuntimeProps) {
|
|
80
|
+
onClick: onClickProp,
|
|
81
|
+
}: NavLinkViaRuntimeInput): NavLinkViaRuntimeProps {
|
|
50
82
|
const runtime = useRequiredChatRuntime();
|
|
51
83
|
const router = useRouter();
|
|
52
84
|
const panel = useChatPanel();
|
|
@@ -54,6 +86,8 @@ export function NavLinkAnchorViaRuntime({
|
|
|
54
86
|
const isNewTab = computeIsNewTab(runtime, resolvedHref, targetPlatform ?? null);
|
|
55
87
|
|
|
56
88
|
const onClick = (e: MouseEvent<HTMLAnchorElement>) => {
|
|
89
|
+
onClickProp?.(e);
|
|
90
|
+
if (e.defaultPrevented) return;
|
|
57
91
|
const handled = executeNavigation({
|
|
58
92
|
event: e,
|
|
59
93
|
runtime,
|
|
@@ -64,8 +98,27 @@ export function NavLinkAnchorViaRuntime({
|
|
|
64
98
|
});
|
|
65
99
|
if (handled && !isNewTab && panel?.closeChat) panel.closeChat();
|
|
66
100
|
};
|
|
101
|
+
const attrs = newTabAnchorAttrs(isNewTab);
|
|
102
|
+
return {
|
|
103
|
+
href: resolvedHref,
|
|
104
|
+
...attrs,
|
|
105
|
+
...(isNewTab && targetPlatform && { rel: 'noopener' as const }),
|
|
106
|
+
onClick,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function NavLinkAnchorViaRuntime({
|
|
111
|
+
href,
|
|
112
|
+
path,
|
|
113
|
+
targetPlatform,
|
|
114
|
+
className,
|
|
115
|
+
children,
|
|
116
|
+
onClick,
|
|
117
|
+
...anchorAttrs
|
|
118
|
+
}: NavLinkAnchorViaRuntimeProps) {
|
|
119
|
+
const linkProps = useNavLinkViaRuntime({ href, path, targetPlatform, onClick });
|
|
67
120
|
return (
|
|
68
|
-
<a
|
|
121
|
+
<a {...linkProps} {...anchorAttrs} className={className}>
|
|
69
122
|
{children}
|
|
70
123
|
</a>
|
|
71
124
|
);
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
import { Megaphone, Bell, Info, Star, Package as PackageGlyph } from 'lucide-react';
|
|
32
32
|
import type { ComponentType, CSSProperties } from 'react';
|
|
33
|
+
import { platformAccentVarName } from '../../../utils/platform-identity';
|
|
33
34
|
import * as IconsV2 from '../../icons-v2-generated';
|
|
34
35
|
import { HeadphoneIcon } from '../../icons-v2-generated/audio-and-visual/headphone-icon';
|
|
35
36
|
import { VideoRecorderIcon } from '../../icons-v2-generated/audio-and-visual/video-recorder-icon';
|
|
@@ -52,6 +53,8 @@ import { GraduationCapIcon } from '../../icons-v2-generated/school/graduation-ca
|
|
|
52
53
|
import { TicketIcon } from '../../icons-v2-generated/shopping/ticket-icon';
|
|
53
54
|
import { Rocket02Icon } from '../../icons-v2-generated/vehicles-and-delivery/rocket-02-icon';
|
|
54
55
|
import { FlamingoLogo } from '../../icons/flamingo-logo';
|
|
56
|
+
import { MiamiCyberGangLogoFaceOnly } from '../../icons/miami-cyber-gang-logo-face-only';
|
|
57
|
+
import { MlgLogo } from '../../icons/mlg-logo';
|
|
55
58
|
import { OpenFrameLogo } from '../../icons/openframe-logo';
|
|
56
59
|
import { OpenmspLogo } from '../../icons/openmsp-logo';
|
|
57
60
|
import { getIconComponent } from './icon-registry';
|
|
@@ -71,8 +74,37 @@ function sizedLogo(Logo: ComponentType<{ className?: string; style?: CSSProperti
|
|
|
71
74
|
return <Logo className={className} style={{ width: size, height: size }} />;
|
|
72
75
|
};
|
|
73
76
|
}
|
|
74
|
-
|
|
77
|
+
// A sister platform's mark is drawn in THAT platform's brand colour (the
|
|
78
|
+
// platform brand registry), never in the accent of the page showing it: on
|
|
79
|
+
// flamingo.run the OpenMSP mark stays OpenMSP yellow, not flamingo pink.
|
|
80
|
+
const brandColor = (platform: string) => `var(${platformAccentVarName(platform)})`;
|
|
81
|
+
// The OpenMSP mark's own defaults are black and white (made for a light page):
|
|
82
|
+
// on the dark surfaces every glyph sits on, the bubbles take the theme's text
|
|
83
|
+
// and background tokens and the back bubble OpenMSP's brand colour.
|
|
84
|
+
const OpenmspLogoIcon: IconComponent = ({ size = 16, className }) => (
|
|
85
|
+
<OpenmspLogo
|
|
86
|
+
className={className}
|
|
87
|
+
style={{ width: size, height: size }}
|
|
88
|
+
frontBubbleColor="var(--color-text-primary)"
|
|
89
|
+
innerFrontBubbleColor="var(--color-bg)"
|
|
90
|
+
backBubbleColor={brandColor('openmsp')}
|
|
91
|
+
/>
|
|
92
|
+
);
|
|
75
93
|
const FlamingoLogoIcon = sizedLogo(FlamingoLogo);
|
|
94
|
+
// The two community marks size themselves through a `size` prop, the resolver's own contract.
|
|
95
|
+
// Major League GitHub draws its mark in the theme's text colour on its own site
|
|
96
|
+
// (its header, hero and footer), never in an accent: so does every glyph of it.
|
|
97
|
+
const MlgLogoIcon: IconComponent = ({ size = 16, className, color }) => (
|
|
98
|
+
<MlgLogo size={size} className={className} color={color ?? 'var(--color-text-primary)'} />
|
|
99
|
+
);
|
|
100
|
+
const TmcgLogoIcon: IconComponent = ({ size = 16, className }) => (
|
|
101
|
+
<MiamiCyberGangLogoFaceOnly
|
|
102
|
+
size={size}
|
|
103
|
+
className={className}
|
|
104
|
+
originalPink={brandColor('tmcg')}
|
|
105
|
+
originalWhite="var(--color-text-primary)"
|
|
106
|
+
/>
|
|
107
|
+
);
|
|
76
108
|
|
|
77
109
|
/**
|
|
78
110
|
* `OpenFrameLogo` from `components/icons/` predates the v2 set and:
|
|
@@ -138,6 +170,8 @@ export const ICON_ALIASES: Record<string, IconComponent> = {
|
|
|
138
170
|
// (was the separate `renderSvgIcon` map; now ONE resolver everywhere).
|
|
139
171
|
'openframe-logo': LogoOpenframeIcon,
|
|
140
172
|
'openmsp-logo': OpenmspLogoIcon,
|
|
173
|
+
'mlg-logo': MlgLogoIcon,
|
|
174
|
+
'tmcg-logo': TmcgLogoIcon,
|
|
141
175
|
flamingo: FlamingoLogoIcon,
|
|
142
176
|
'flamingo-logo': FlamingoLogoIcon,
|
|
143
177
|
megaphone: Megaphone,
|
package/src/components/index.ts
CHANGED
|
@@ -33,10 +33,8 @@ export * from './faq-accordion';
|
|
|
33
33
|
export * from './faq';
|
|
34
34
|
export * from './related-content';
|
|
35
35
|
export * from './filter-chip';
|
|
36
|
-
export * from './footer';
|
|
37
36
|
export * from './unified-filter-logic';
|
|
38
37
|
export * from './unified-pagination';
|
|
39
|
-
export * from './footer-waitlist-button';
|
|
40
38
|
export * from './hero-image-uploader';
|
|
41
39
|
export * from './icons-block';
|
|
42
40
|
export * from './image-cropper';
|
|
@@ -33,7 +33,7 @@ export function AnnouncementBarSkeleton() {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
* Header skeleton that
|
|
36
|
+
* Header skeleton for a page that is still loading: logo, navigation and action placeholders
|
|
37
37
|
*/
|
|
38
38
|
export function HeaderSkeleton() {
|
|
39
39
|
return (
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
// Navigation component exports
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export {
|
|
4
|
+
// Site navigation: one data model (`SiteNav`), rendered by the header, its
|
|
5
|
+
// menu panels, the mobile sheet and the footer.
|
|
6
|
+
export { SiteHeader } from './site-header';
|
|
7
|
+
export type { SiteHeaderProps } from './site-header';
|
|
8
|
+
export { NavPanel, isMegaMenu } from './nav-panel';
|
|
9
|
+
export type { NavPanelProps } from './nav-panel';
|
|
10
|
+
export { NavItemRow } from './nav-item-row';
|
|
11
|
+
export type { NavItemRowProps, NavItemRowVariant } from './nav-item-row';
|
|
12
|
+
export { ASK_AI_OPEN_EVENT, MingoAiButton, openAskAi } from './mingo-ai-button';
|
|
8
13
|
export type { MingoAiButtonProps } from './mingo-ai-button';
|
|
9
|
-
|
|
10
|
-
export {
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
-
export {
|
|
14
|
-
export type {
|
|
15
|
-
|
|
16
|
-
export { MobileNavPanel, MOBILE_NAV_PANEL_ID } from './mobile-nav-panel';
|
|
17
|
-
export type { MobileNavPanelProps } from './mobile-nav-panel';
|
|
14
|
+
export { MobileNavSheet, MOBILE_NAV_SHEET_ID, siteNavHasMobileMenus } from './mobile-nav-sheet';
|
|
15
|
+
export type { MobileNavSheetProps } from './mobile-nav-sheet';
|
|
16
|
+
export { SiteFooter } from './site-footer';
|
|
17
|
+
export type { SiteFooterProps } from './site-footer';
|
|
18
|
+
export { defaultRenderSiteNavLink, navLinkLabel } from './site-nav-link';
|
|
19
|
+
export type { SiteNavLinkRenderer } from './site-nav-link';
|
|
18
20
|
|
|
19
21
|
export { SlidingSidebar } from './sliding-sidebar';
|
|
20
22
|
export type { SlidingSidebarProps } from './sliding-sidebar';
|
|
@@ -81,10 +83,15 @@ export type { NavigationNode } from './multi-level-navigation';
|
|
|
81
83
|
|
|
82
84
|
// Re-export types from navigation types
|
|
83
85
|
export type {
|
|
84
|
-
|
|
86
|
+
NavFeature,
|
|
87
|
+
NavGroup,
|
|
88
|
+
NavLink,
|
|
89
|
+
NavMenu,
|
|
85
90
|
NavigationItem,
|
|
86
91
|
NavigationSidebarConfig,
|
|
87
92
|
NavigationSidebarItem,
|
|
93
|
+
SiteNav,
|
|
94
|
+
SiteNavPrimaryCta,
|
|
88
95
|
SlidingSidebarConfig,
|
|
89
96
|
UnifiedSidebarUser,
|
|
90
97
|
} from '../../types/navigation';
|