@flamingo-stack/openframe-frontend-core 0.0.428 → 0.0.429
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-N2CTZ3Q2.cjs → chunk-2LJA3ABH.cjs} +37 -37
- package/dist/{chunk-N2CTZ3Q2.cjs.map → chunk-2LJA3ABH.cjs.map} +1 -1
- package/dist/{chunk-UXXCNZXC.js → chunk-2WJ25MTA.js} +4 -4
- package/dist/{chunk-55EIHSOQ.js → chunk-4VODJPTB.js} +2 -2
- package/dist/{chunk-OBDDE4WV.js → chunk-62L7S7HQ.js} +2769 -2697
- package/dist/chunk-62L7S7HQ.js.map +1 -0
- package/dist/{chunk-7ELFQWYF.cjs → chunk-7MJTM6QT.cjs} +7 -7
- package/dist/{chunk-7ELFQWYF.cjs.map → chunk-7MJTM6QT.cjs.map} +1 -1
- package/dist/{chunk-YP3DO4BM.js → chunk-7PA4B6SD.js} +4 -4
- package/dist/{chunk-KMXHSL6M.js → chunk-B3FBMRHQ.js} +2 -2
- package/dist/{chunk-I7YFFSDS.cjs → chunk-B3Y47C2U.cjs} +59 -59
- package/dist/{chunk-I7YFFSDS.cjs.map → chunk-B3Y47C2U.cjs.map} +1 -1
- package/dist/{chunk-F4WTCWOY.cjs → chunk-BN3V465H.cjs} +15 -15
- package/dist/{chunk-F4WTCWOY.cjs.map → chunk-BN3V465H.cjs.map} +1 -1
- package/dist/{chunk-533XR4JK.cjs → chunk-CCL6JEMS.cjs} +62 -62
- package/dist/{chunk-533XR4JK.cjs.map → chunk-CCL6JEMS.cjs.map} +1 -1
- package/dist/{chunk-LZYKGEN3.cjs → chunk-CRGH6TV6.cjs} +26 -26
- package/dist/{chunk-LZYKGEN3.cjs.map → chunk-CRGH6TV6.cjs.map} +1 -1
- package/dist/{chunk-MJH4VS4L.cjs → chunk-E4PKZNR7.cjs} +11 -11
- package/dist/{chunk-MJH4VS4L.cjs.map → chunk-E4PKZNR7.cjs.map} +1 -1
- package/dist/{chunk-MO4QIPT7.cjs → chunk-GGJUXN6Z.cjs} +9 -9
- package/dist/{chunk-MO4QIPT7.cjs.map → chunk-GGJUXN6Z.cjs.map} +1 -1
- package/dist/{chunk-43KGFHI3.js → chunk-GMR5AA3C.js} +2 -2
- package/dist/{chunk-RR77Z27A.js → chunk-KHKB5RY2.js} +2 -2
- package/dist/{chunk-DQ5NRLCG.js → chunk-MAYEJ3NY.js} +2 -2
- package/dist/{chunk-DA2VSDAX.js → chunk-NYISC4Z3.js} +2 -2
- package/dist/{chunk-NXCFIPEY.cjs → chunk-OLGFIDZY.cjs} +26 -26
- package/dist/{chunk-NXCFIPEY.cjs.map → chunk-OLGFIDZY.cjs.map} +1 -1
- package/dist/{chunk-SLCANQN3.js → chunk-RTOVYU4D.js} +4 -4
- package/dist/{chunk-7JUUQAAH.js → chunk-TJ4HJ3KX.js} +2 -2
- package/dist/{chunk-FFDGTMZX.cjs → chunk-WXKFFVGC.cjs} +5 -5
- package/dist/{chunk-FFDGTMZX.cjs.map → chunk-WXKFFVGC.cjs.map} +1 -1
- package/dist/{chunk-SJTNVETH.cjs → chunk-XDBD6BJN.cjs} +92 -20
- package/dist/chunk-XDBD6BJN.cjs.map +1 -0
- package/dist/{chunk-CVYVCBMM.js → chunk-Y2TW664W.js} +4 -4
- package/dist/{chunk-OZTPC6VW.cjs → chunk-YGITDP6O.cjs} +31 -31
- package/dist/{chunk-OZTPC6VW.cjs.map → chunk-YGITDP6O.cjs.map} +1 -1
- package/dist/components/case-studies/index.cjs +8 -8
- package/dist/components/case-studies/index.js +2 -2
- package/dist/components/chat/chat-message-skeleton.d.ts +17 -2
- package/dist/components/chat/chat-message-skeleton.d.ts.map +1 -1
- package/dist/components/chat/chat-panel-header.d.ts +6 -1
- package/dist/components/chat/chat-panel-header.d.ts.map +1 -1
- package/dist/components/chat/chat-quick-action-row.d.ts +12 -2
- package/dist/components/chat/chat-quick-action-row.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +22 -2
- package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +4 -2
- package/dist/components/chat/index.cjs.map +1 -1
- package/dist/components/chat/index.js +3 -1
- package/dist/components/chat/quick-action-chip.d.ts +5 -1
- package/dist/components/chat/quick-action-chip.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +3 -3
- package/dist/components/contact/index.js +2 -2
- package/dist/components/docs/index.cjs +5 -5
- package/dist/components/docs/index.js +4 -4
- package/dist/components/embeds/index.cjs +3 -3
- package/dist/components/embeds/index.js +2 -2
- package/dist/components/faq/index.cjs +3 -3
- package/dist/components/faq/index.js +2 -2
- package/dist/components/features/index.cjs +2 -2
- package/dist/components/features/index.js +1 -1
- package/dist/components/help-center-pages/index.cjs +20 -20
- package/dist/components/help-center-pages/index.js +11 -11
- package/dist/components/index.cjs +132 -128
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +13 -9
- package/dist/components/index.js.map +1 -1
- package/dist/components/navigation/index.cjs +2 -2
- package/dist/components/navigation/index.js +1 -1
- package/dist/components/onboarding-guides/index.cjs +5 -5
- package/dist/components/onboarding-guides/index.js +4 -4
- package/dist/components/related-content/index.cjs +3 -3
- package/dist/components/related-content/index.js +2 -2
- package/dist/components/tickets/index.cjs +5 -5
- package/dist/components/tickets/index.js +4 -4
- package/dist/components/ui/index.cjs +6 -2
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.d.ts +1 -0
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +5 -1
- package/dist/components/ui/info-card-row.d.ts +13 -0
- package/dist/components/ui/info-card-row.d.ts.map +1 -0
- package/dist/components/ui/tag.d.ts +1 -1
- package/dist/components/ui/tag.d.ts.map +1 -1
- package/dist/index.cjs +6 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5 -1
- package/package.json +1 -1
- package/src/components/chat/chat-message-skeleton.tsx +40 -9
- package/src/components/chat/chat-panel-header.tsx +28 -16
- package/src/components/chat/chat-quick-action-row.tsx +33 -0
- package/src/components/chat/embeddable-chat.tsx +11 -1
- package/src/components/chat/hooks/use-nats-chat-adapter.ts +7 -1
- package/src/components/chat/quick-action-chip.tsx +6 -1
- package/src/components/ui/index.ts +1 -0
- package/src/components/ui/info-card-row.tsx +70 -0
- package/src/components/ui/tag.tsx +7 -0
- package/src/stories/InfoCardRow.stories.tsx +68 -0
- package/dist/chunk-OBDDE4WV.js.map +0 -1
- package/dist/chunk-SJTNVETH.cjs.map +0 -1
- /package/dist/{chunk-UXXCNZXC.js.map → chunk-2WJ25MTA.js.map} +0 -0
- /package/dist/{chunk-55EIHSOQ.js.map → chunk-4VODJPTB.js.map} +0 -0
- /package/dist/{chunk-YP3DO4BM.js.map → chunk-7PA4B6SD.js.map} +0 -0
- /package/dist/{chunk-KMXHSL6M.js.map → chunk-B3FBMRHQ.js.map} +0 -0
- /package/dist/{chunk-43KGFHI3.js.map → chunk-GMR5AA3C.js.map} +0 -0
- /package/dist/{chunk-RR77Z27A.js.map → chunk-KHKB5RY2.js.map} +0 -0
- /package/dist/{chunk-DQ5NRLCG.js.map → chunk-MAYEJ3NY.js.map} +0 -0
- /package/dist/{chunk-DA2VSDAX.js.map → chunk-NYISC4Z3.js.map} +0 -0
- /package/dist/{chunk-SLCANQN3.js.map → chunk-RTOVYU4D.js.map} +0 -0
- /package/dist/{chunk-7JUUQAAH.js.map → chunk-TJ4HJ3KX.js.map} +0 -0
- /package/dist/{chunk-CVYVCBMM.js.map → chunk-Y2TW664W.js.map} +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import * as React from 'react'
|
|
4
|
+
import { cn } from '../../utils/cn'
|
|
4
5
|
import { Chevron01LeftIcon } from '../icons-v2-generated/arrows/chevron-01-left-icon'
|
|
5
6
|
import {
|
|
6
7
|
Ellipsis01Icon,
|
|
@@ -42,6 +43,11 @@ export interface ChatPanelHeaderProps {
|
|
|
42
43
|
onArchive?: () => void
|
|
43
44
|
/** Open the Chat Archive page — renders the clock button (list view only). */
|
|
44
45
|
onOpenArchive?: () => void
|
|
46
|
+
/** Force the COMPACT (`h-14` desktop-style) bar at every width, skipping the
|
|
47
|
+
* full-screen mobile header. For EMBEDDED surfaces (e.g. a small preview
|
|
48
|
+
* panel) where the phone-sized full-bleed header (large `text-h2` title) is
|
|
49
|
+
* wrong regardless of viewport. Default false = viewport-responsive. */
|
|
50
|
+
compact?: boolean
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
/**
|
|
@@ -61,6 +67,7 @@ export function ChatPanelHeader({
|
|
|
61
67
|
onRename,
|
|
62
68
|
onArchive,
|
|
63
69
|
onOpenArchive,
|
|
70
|
+
compact = false,
|
|
64
71
|
}: ChatPanelHeaderProps) {
|
|
65
72
|
// Desktop ⋯ menu (active, non-archived conversation only) — rename / archive.
|
|
66
73
|
const menuItems = [
|
|
@@ -70,23 +77,28 @@ export function ChatPanelHeader({
|
|
|
70
77
|
|
|
71
78
|
return (
|
|
72
79
|
<>
|
|
73
|
-
{/* Mobile (<md): a distinct layout (Figma node 7363:85532).
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
{/* Mobile (<md): a distinct full-screen layout (Figma node 7363:85532).
|
|
81
|
+
Skipped in `compact` mode — an embedded preview keeps the small bar
|
|
82
|
+
at every width. */}
|
|
83
|
+
{!compact && (
|
|
84
|
+
<ChatPanelHeaderMobile
|
|
85
|
+
className="flex md:hidden"
|
|
86
|
+
showBack={showBack}
|
|
87
|
+
title={title}
|
|
88
|
+
backAriaLabel={backAriaLabel}
|
|
89
|
+
isArchivedView={isArchivedView}
|
|
90
|
+
onBack={onBack}
|
|
91
|
+
onClose={onClose}
|
|
92
|
+
onRestore={onRestore}
|
|
93
|
+
onRename={onRename}
|
|
94
|
+
onArchive={onArchive}
|
|
95
|
+
onOpenArchive={onOpenArchive}
|
|
96
|
+
/>
|
|
97
|
+
)}
|
|
87
98
|
|
|
88
|
-
{/*
|
|
89
|
-
|
|
99
|
+
{/* Compact bar: `md+` normally, or ALL widths when `compact`. Fixed-height
|
|
100
|
+
bar with full-height divider action cells. */}
|
|
101
|
+
<div className={cn('flex-shrink-0 h-14 w-full overflow-hidden border-b border-ods-border bg-ods-card', compact ? 'flex' : 'hidden md:flex')}>
|
|
90
102
|
<div className="flex flex-1 min-w-0 items-center gap-2 px-4 py-3">
|
|
91
103
|
{showBack ? (
|
|
92
104
|
<>
|
|
@@ -9,8 +9,10 @@ import { Ellipsis01Icon } from '../icons-v2-generated'
|
|
|
9
9
|
import { useAutoLimitTags } from '../../hooks/ui/use-auto-limit-tags'
|
|
10
10
|
import {
|
|
11
11
|
QuickActionChipButton,
|
|
12
|
+
QuickActionChipSkeleton,
|
|
12
13
|
renderQuickActionIcon,
|
|
13
14
|
type QuickActionIconSpec,
|
|
15
|
+
type QuickActionAccent,
|
|
14
16
|
} from './quick-action-chip'
|
|
15
17
|
|
|
16
18
|
// =============================================================================
|
|
@@ -27,6 +29,11 @@ export interface QuickActionChip {
|
|
|
27
29
|
icon?: React.ReactNode | QuickActionIconSpec
|
|
28
30
|
/** `'primary'` = accent (yellow) chip, `'outline'` = bordered chip (default). */
|
|
29
31
|
variant?: 'primary' | 'outline'
|
|
32
|
+
/** Active single-select state — renders the accented `selected` skin
|
|
33
|
+
* (overrides `variant`). */
|
|
34
|
+
selected?: boolean
|
|
35
|
+
/** Accent for the `selected` skin (`'cyan'` = cyan twin, else pink). */
|
|
36
|
+
selectedAccent?: QuickActionAccent
|
|
30
37
|
onSelect?: () => void
|
|
31
38
|
/** Pointer/keyboard focus enters the chip — e.g. preview the full prompt in
|
|
32
39
|
* the composer. */
|
|
@@ -47,9 +54,18 @@ export interface ChatQuickActionRowProps {
|
|
|
47
54
|
* for roomy contexts (e.g. the Guide empty state) where overflow-hiding chips
|
|
48
55
|
* would defeat per-chip hover/preview. */
|
|
49
56
|
wrap?: boolean
|
|
57
|
+
/** Render `skeletonCount` loading chips (the real {@link QuickActionChipSkeleton},
|
|
58
|
+
* so geometry is 1:1 with the loaded chips) instead of `chips`. Wrap-aware. */
|
|
59
|
+
loading?: boolean
|
|
60
|
+
/** How many skeleton chips to draw when `loading`. Default 8. */
|
|
61
|
+
skeletonCount?: number
|
|
50
62
|
className?: string
|
|
51
63
|
}
|
|
52
64
|
|
|
65
|
+
// Deterministic label widths (in `ch`) so a loading wall reads like a populated
|
|
66
|
+
// one — a varied spread, not a uniform block.
|
|
67
|
+
const SKELETON_LABEL_CHS = [16, 9, 13, 7, 18, 8, 20, 11, 15, 10, 19, 12, 17, 8, 21, 14]
|
|
68
|
+
|
|
53
69
|
// =============================================================================
|
|
54
70
|
// Chip button
|
|
55
71
|
// =============================================================================
|
|
@@ -62,6 +78,8 @@ function ChipButton({ chip }: { chip: QuickActionChip }) {
|
|
|
62
78
|
label={chip.label}
|
|
63
79
|
icon={chip.icon}
|
|
64
80
|
variant={chip.variant ?? 'outline'}
|
|
81
|
+
selected={chip.selected}
|
|
82
|
+
selectedAccent={chip.selectedAccent}
|
|
65
83
|
onSelect={chip.onSelect}
|
|
66
84
|
onHoverStart={chip.onHoverStart}
|
|
67
85
|
onHoverEnd={chip.onHoverEnd}
|
|
@@ -86,6 +104,8 @@ export function ChatQuickActionRow({
|
|
|
86
104
|
leading,
|
|
87
105
|
chips,
|
|
88
106
|
wrap = false,
|
|
107
|
+
loading = false,
|
|
108
|
+
skeletonCount = 8,
|
|
89
109
|
className,
|
|
90
110
|
}: ChatQuickActionRowProps) {
|
|
91
111
|
const auto = useAutoLimitTags({
|
|
@@ -96,6 +116,19 @@ export function ChatQuickActionRow({
|
|
|
96
116
|
const visibleChips = chips.slice(0, auto.visibleCount)
|
|
97
117
|
const hiddenChips = chips.slice(auto.visibleCount)
|
|
98
118
|
|
|
119
|
+
// Loading: draw skeleton chips (real `QuickActionChipSkeleton`, 1:1 geometry)
|
|
120
|
+
// in the same wrap/inline layout — one shared skeleton for every consumer, no
|
|
121
|
+
// hand-rolled bars.
|
|
122
|
+
if (loading) {
|
|
123
|
+
return (
|
|
124
|
+
<div className={cn('flex shrink-0 items-center gap-1', wrap && 'flex-wrap', className)}>
|
|
125
|
+
{Array.from({ length: skeletonCount }, (_, i) => (
|
|
126
|
+
<QuickActionChipSkeleton key={i} labelCh={SKELETON_LABEL_CHS[i % SKELETON_LABEL_CHS.length]} />
|
|
127
|
+
))}
|
|
128
|
+
</div>
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
99
132
|
if (!leading && chips.length === 0) return null
|
|
100
133
|
|
|
101
134
|
// Wrap mode: no width-measurement / "⋯" overflow — every chip renders inline
|
|
@@ -1557,7 +1557,13 @@ function EmbeddableChatInner({
|
|
|
1557
1557
|
// history has loaded (`hasMessages`). Driving the surface + content branch
|
|
1558
1558
|
// off this makes the normal-chat open animate identically to the archived
|
|
1559
1559
|
// one instead of lagging behind the message fetch.
|
|
1560
|
-
|
|
1560
|
+
// In `previewMode`, the host drives the whole state (`mingoStateOverride`) and
|
|
1561
|
+
// there is no dialog manager to set `isOpeningDialog`. So an embedded preview
|
|
1562
|
+
// that wants a message-list skeleton (real header + composer, skeleton bubbles)
|
|
1563
|
+
// signals it via `isMessagesLoading` — treat that as an open conversation so
|
|
1564
|
+
// the content branch shows the skeleton instead of the new-user welcome.
|
|
1565
|
+
const hasConversation =
|
|
1566
|
+
hasMessages || isOpeningDialog || isViewingArchived || (previewMode && isMessagesLoading)
|
|
1561
1567
|
// Opening a dialog whose history hasn't arrived yet — show a message-list
|
|
1562
1568
|
// skeleton instead of an empty thread so the open reads as "loading" rather
|
|
1563
1569
|
// than a blank flash before the bubbles stream in.
|
|
@@ -1680,6 +1686,10 @@ function EmbeddableChatInner({
|
|
|
1680
1686
|
className="flex flex-1 min-h-0 flex-col animate-in fade-in-0 duration-200"
|
|
1681
1687
|
>
|
|
1682
1688
|
<ChatPanelHeader
|
|
1689
|
+
// Embedded previews (the hero demo tabs) keep the small bar at
|
|
1690
|
+
// every width — the phone-sized full-screen header (large title)
|
|
1691
|
+
// is wrong in a small embedded panel.
|
|
1692
|
+
compact={previewMode}
|
|
1683
1693
|
// Guide-mode empty state shows a back-chevron + "Mingo Guide"
|
|
1684
1694
|
// (back returns to the default Mingo welcome); an open
|
|
1685
1695
|
// conversation shows back + the dialog title; the Mingo list
|
|
@@ -374,8 +374,14 @@ function updateTrailingAssistant(
|
|
|
374
374
|
* here because the unified contract surfaces errors as banners, not
|
|
375
375
|
* inline messages. (Hosts that need inline error bubbles can extend
|
|
376
376
|
* the contract later.)
|
|
377
|
+
*
|
|
378
|
+
* Exported for host reuse (e.g. scripted marketing demos that rehydrate a
|
|
379
|
+
* stored `HistoricalMessage[]` via `processHistoricalMessagesWithErrors` and
|
|
380
|
+
* feed a `previewMode` EmbeddableChat). This mapper carries the segment/content
|
|
381
|
+
* shape only; author identity (name/avatar/authorType/timestamp) that a host
|
|
382
|
+
* needs for the demo is re-attached by the host from the same processed rows.
|
|
377
383
|
*/
|
|
378
|
-
function mapProcessedToUnified(
|
|
384
|
+
export function mapProcessedToUnified(
|
|
379
385
|
processed: Array<{
|
|
380
386
|
id: string
|
|
381
387
|
role: 'user' | 'assistant' | 'error'
|
|
@@ -145,6 +145,10 @@ export interface QuickActionChipButtonProps {
|
|
|
145
145
|
* Tag's `selected` variant (pink border + pink-secondary fill), overriding
|
|
146
146
|
* `variant`. Used by chip groups acting as tabs (OpenFrame categories). */
|
|
147
147
|
selected?: boolean
|
|
148
|
+
/** Accent for the `selected` skin: `'cyan'` uses the cyan twin, anything else
|
|
149
|
+
* (default) uses pink. Lets an agent's chip group match its own theme accent
|
|
150
|
+
* (fae pink / mingo cyan) instead of the fixed pink. */
|
|
151
|
+
selectedAccent?: QuickActionAccent
|
|
148
152
|
/** Chip scale, forwarded to `Tag` — `'large'` is the Figma "Feature Item"
|
|
149
153
|
* 48px chip (h3 bold label, 24px icon box). Default `'default'` (32px). */
|
|
150
154
|
size?: 'default' | 'large'
|
|
@@ -226,6 +230,7 @@ export function QuickActionChipButton({
|
|
|
226
230
|
lozenge,
|
|
227
231
|
variant = 'outline',
|
|
228
232
|
selected = false,
|
|
233
|
+
selectedAccent,
|
|
229
234
|
size = 'default',
|
|
230
235
|
onSelect,
|
|
231
236
|
onHoverStart,
|
|
@@ -235,7 +240,7 @@ export function QuickActionChipButton({
|
|
|
235
240
|
}: QuickActionChipButtonProps) {
|
|
236
241
|
const resolvedIcon = renderQuickActionIcon(icon)
|
|
237
242
|
const resolvedLabel = composeChipLabel(label, lozenge)
|
|
238
|
-
const tagVariant = selected ? 'selected' : variant
|
|
243
|
+
const tagVariant = selected ? (selectedAccent === 'cyan' ? 'selectedCyan' : 'selected') : variant
|
|
239
244
|
if (!interactive) {
|
|
240
245
|
return <Tag variant={tagVariant} size={size} icon={resolvedIcon} label={resolvedLabel} className={className} />
|
|
241
246
|
}
|
|
@@ -83,6 +83,7 @@ export * from './chevron-button'
|
|
|
83
83
|
export * from './circular-progress'
|
|
84
84
|
export { CheckIcon, CheckCircleIcon as LucideCheckCircleIcon, XIcon as LucideXIcon, MinusIcon, XCircleIcon } from './custom-icons'
|
|
85
85
|
export * from './dashboard-info-card'
|
|
86
|
+
export * from './info-card-row'
|
|
86
87
|
export * from './device-card'
|
|
87
88
|
export * from './device-card-compact'
|
|
88
89
|
export * from './entity-image'
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type * as React from 'react'
|
|
2
|
+
import { cn } from '../../utils/cn'
|
|
3
|
+
|
|
4
|
+
export interface InfoCardRowSection {
|
|
5
|
+
title: string
|
|
6
|
+
caption: string
|
|
7
|
+
icon?: React.ReactNode
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface InfoCardRowProps {
|
|
11
|
+
lead: InfoCardRowSection
|
|
12
|
+
stats: [InfoCardRowSection, InfoCardRowSection]
|
|
13
|
+
className?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const CELL =
|
|
17
|
+
'flex items-center gap-[var(--spacing-system-s)] min-w-0 p-[var(--spacing-system-xsf)] md:p-[var(--spacing-system-m)] min-h-[60px] md:min-h-[76px]'
|
|
18
|
+
|
|
19
|
+
function StatCell({ section, className }: { section: InfoCardRowSection; className?: string }) {
|
|
20
|
+
return (
|
|
21
|
+
<div className={cn(CELL, 'flex-1', className)}>
|
|
22
|
+
<div className="flex min-w-0 flex-1 flex-col">
|
|
23
|
+
<p className="truncate text-h5 uppercase text-ods-text-secondary">{section.title}</p>
|
|
24
|
+
<div className="flex min-w-0 items-center gap-[var(--spacing-system-s)]">
|
|
25
|
+
<p className="min-w-0 flex-1 truncate text-h3 text-ods-text-primary">
|
|
26
|
+
{section.caption}
|
|
27
|
+
</p>
|
|
28
|
+
{section.icon && (
|
|
29
|
+
<div className="flex size-6 shrink-0 items-center justify-center md:hidden">
|
|
30
|
+
{section.icon}
|
|
31
|
+
</div>
|
|
32
|
+
)}
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
{section.icon && (
|
|
36
|
+
<div className="hidden size-6 shrink-0 items-center justify-center md:flex">
|
|
37
|
+
{section.icon}
|
|
38
|
+
</div>
|
|
39
|
+
)}
|
|
40
|
+
</div>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function InfoCardRow({ lead, stats, className }: InfoCardRowProps) {
|
|
45
|
+
return (
|
|
46
|
+
<div
|
|
47
|
+
className={cn(
|
|
48
|
+
'flex shrink-0 flex-col overflow-hidden rounded-md border border-ods-border bg-ods-card md:flex-row',
|
|
49
|
+
className,
|
|
50
|
+
)}
|
|
51
|
+
>
|
|
52
|
+
<div className={cn(CELL, 'border-b border-ods-border md:flex-1 md:border-b-0 md:border-r')}>
|
|
53
|
+
{lead.icon && (
|
|
54
|
+
<div className="flex size-10 shrink-0 items-center justify-center overflow-hidden rounded-sm border border-ods-border [&>*]:size-full [&_img]:object-cover">
|
|
55
|
+
{lead.icon}
|
|
56
|
+
</div>
|
|
57
|
+
)}
|
|
58
|
+
<div className="flex min-w-0 flex-1 flex-col justify-center">
|
|
59
|
+
<p className="truncate text-h3 text-ods-text-primary">{lead.title}</p>
|
|
60
|
+
<p className="truncate text-h6 text-ods-text-secondary">{lead.caption}</p>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<div className="flex md:flex-1">
|
|
65
|
+
<StatCell section={stats[0]} className="border-r border-ods-border" />
|
|
66
|
+
<StatCell section={stats[1]} />
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
@@ -58,6 +58,13 @@ const tagVariants = cva(
|
|
|
58
58
|
"hover:bg-[var(--ods-flamingo-pink-secondary-hover)] hover:border-[var(--ods-flamingo-pink-base)]",
|
|
59
59
|
"active:bg-[var(--ods-flamingo-pink-secondary-action)]",
|
|
60
60
|
],
|
|
61
|
+
// Cyan twin of `selected` (Mingo's accent) — same active-chip skin in the
|
|
62
|
+
// cyan theme so agent chip groups can match their own accent.
|
|
63
|
+
selectedCyan: [
|
|
64
|
+
"bg-[var(--ods-flamingo-cyan-secondary)] text-[var(--ods-system-greys-white)] border border-[var(--ods-flamingo-cyan-base)]",
|
|
65
|
+
"hover:bg-[var(--ods-flamingo-cyan-secondary-hover)] hover:border-[var(--ods-flamingo-cyan-base)]",
|
|
66
|
+
"active:bg-[var(--ods-flamingo-cyan-secondary-action)]",
|
|
67
|
+
],
|
|
61
68
|
// Matches the EntityTagBadges / StatusBadge tag skin (ods-card + ods-border,
|
|
62
69
|
// mono uppercase) so the tag-editor chips render identically to the public
|
|
63
70
|
// tag badges. Used for FilterChipData variant 'tag' (see search-input).
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/nextjs-vite'
|
|
2
|
+
import { InfoCardRow } from '../components/ui/info-card-row'
|
|
3
|
+
import { CheckCircleIcon } from '../components/icons-v2-generated/signs-and-symbols/check-circle-icon'
|
|
4
|
+
import { TagIcon } from '../components/icons-v2-generated/shopping/tag-icon'
|
|
5
|
+
|
|
6
|
+
// Stand-in company logo — the lead `icon` fills the 40px bordered avatar box.
|
|
7
|
+
// In real usage this is typically an `<img>` logo (rendered with object-cover).
|
|
8
|
+
const companyLogo = (
|
|
9
|
+
<div className="size-full" style={{ background: 'linear-gradient(135deg, #5EFAF0, #F357BB)' }} />
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
const meta = {
|
|
13
|
+
title: 'UI/InfoCardRow',
|
|
14
|
+
component: InfoCardRow,
|
|
15
|
+
parameters: {
|
|
16
|
+
layout: 'padded',
|
|
17
|
+
docs: {
|
|
18
|
+
description: {
|
|
19
|
+
component:
|
|
20
|
+
'A responsive summary row: a 50%-width lead section (icon + title/caption) and two 25%-width stat sections (title + caption + right icon). On mobile the two stat sections wrap into their own row beneath the lead (76px rows on desktop/tablet, 60px on mobile), and each stat icon drops onto the caption line. Neighbouring sections share inner borders; only the outer edges are rounded. Each section takes a required `title` and `caption`, and an optional `icon`.',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
tags: ['autodocs'],
|
|
25
|
+
argTypes: {
|
|
26
|
+
lead: { control: false, description: 'Lead section (50% width): { title, caption, icon? }' },
|
|
27
|
+
stats: {
|
|
28
|
+
control: false,
|
|
29
|
+
description: 'Two stat sections (25% width each): { title, caption, icon? }',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
decorators: [
|
|
33
|
+
(Story) => (
|
|
34
|
+
<div className="w-full max-w-[1024px]">
|
|
35
|
+
<Story />
|
|
36
|
+
</div>
|
|
37
|
+
),
|
|
38
|
+
],
|
|
39
|
+
} satisfies Meta<typeof InfoCardRow>
|
|
40
|
+
|
|
41
|
+
export default meta
|
|
42
|
+
type Story = StoryObj<typeof meta>
|
|
43
|
+
|
|
44
|
+
/** Full row — lead logo + name/devices, and two stats with icons. Matches Figma. */
|
|
45
|
+
export const Default: Story = {
|
|
46
|
+
args: {
|
|
47
|
+
lead: { title: 'Precision Accounting Partners', caption: '847 devices', icon: companyLogo },
|
|
48
|
+
stats: [
|
|
49
|
+
{
|
|
50
|
+
title: 'Resolved this week',
|
|
51
|
+
caption: '312',
|
|
52
|
+
icon: <CheckCircleIcon className="text-ods-success" />,
|
|
53
|
+
},
|
|
54
|
+
{ title: 'Open tickets', caption: '2', icon: <TagIcon className="text-ods-text-secondary" /> },
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Icons omitted — text-only sections. */
|
|
60
|
+
export const WithoutIcons: Story = {
|
|
61
|
+
args: {
|
|
62
|
+
lead: { title: 'Precision Accounting Partners', caption: '847 devices' },
|
|
63
|
+
stats: [
|
|
64
|
+
{ title: 'Resolved this week', caption: '312' },
|
|
65
|
+
{ title: 'Open tickets', caption: '2' },
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
}
|