@flamingo-stack/openframe-frontend-core 0.0.449-snapshot.20260718143201 → 0.0.449

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.
Files changed (142) hide show
  1. package/dist/{chunk-DQ5U6DCR.cjs → chunk-47AMTR2S.cjs} +18 -18
  2. package/dist/{chunk-DQ5U6DCR.cjs.map → chunk-47AMTR2S.cjs.map} +1 -1
  3. package/dist/{chunk-PCW2TRZ4.js → chunk-4IMXFDRI.js} +3 -3
  4. package/dist/{chunk-XITG5ICQ.cjs → chunk-4S4RR7XC.cjs} +40 -40
  5. package/dist/{chunk-XITG5ICQ.cjs.map → chunk-4S4RR7XC.cjs.map} +1 -1
  6. package/dist/{chunk-HXCQHMS5.js → chunk-5SJGFLL2.js} +6032 -6386
  7. package/dist/chunk-5SJGFLL2.js.map +1 -0
  8. package/dist/{chunk-6RA4NRBA.cjs → chunk-6TS4AIDX.cjs} +15 -15
  9. package/dist/{chunk-6RA4NRBA.cjs.map → chunk-6TS4AIDX.cjs.map} +1 -1
  10. package/dist/{chunk-QF3NQW7C.cjs → chunk-AN7MCJTV.cjs} +29 -122
  11. package/dist/chunk-AN7MCJTV.cjs.map +1 -0
  12. package/dist/{chunk-J5BYYZVM.cjs → chunk-AZKVNRZP.cjs} +32 -32
  13. package/dist/{chunk-J5BYYZVM.cjs.map → chunk-AZKVNRZP.cjs.map} +1 -1
  14. package/dist/{chunk-VUDZOCPW.js → chunk-BSBWBQ7M.js} +6 -6
  15. package/dist/{chunk-26JSJMMZ.cjs → chunk-DJP5HYFG.cjs} +7 -7
  16. package/dist/{chunk-26JSJMMZ.cjs.map → chunk-DJP5HYFG.cjs.map} +1 -1
  17. package/dist/{chunk-5HW5HMNF.cjs → chunk-DRKQNXSZ.cjs} +5 -5
  18. package/dist/{chunk-5HW5HMNF.cjs.map → chunk-DRKQNXSZ.cjs.map} +1 -1
  19. package/dist/{chunk-ROBGJH52.cjs → chunk-EOFENGSZ.cjs} +12 -12
  20. package/dist/{chunk-ROBGJH52.cjs.map → chunk-EOFENGSZ.cjs.map} +1 -1
  21. package/dist/{chunk-N5M72LJX.cjs → chunk-H56AEHFC.cjs} +1266 -1620
  22. package/dist/chunk-H56AEHFC.cjs.map +1 -0
  23. package/dist/{chunk-RQHHACP2.js → chunk-HQ7S6JG5.js} +3 -3
  24. package/dist/{chunk-K3GXIGN6.js → chunk-JAASDJOF.js} +5 -5
  25. package/dist/{chunk-5OY7HYII.cjs → chunk-KKJRBMG3.cjs} +73 -73
  26. package/dist/{chunk-5OY7HYII.cjs.map → chunk-KKJRBMG3.cjs.map} +1 -1
  27. package/dist/{chunk-VLDZ4363.cjs → chunk-LQTX7EJS.cjs} +29 -29
  28. package/dist/{chunk-VLDZ4363.cjs.map → chunk-LQTX7EJS.cjs.map} +1 -1
  29. package/dist/{chunk-4VSTLJDO.js → chunk-LWT6G75Q.js} +2 -2
  30. package/dist/{chunk-4SZDO3TE.js → chunk-MWRFPLNB.js} +3 -3
  31. package/dist/{chunk-3ITY2LBM.js → chunk-NPBTNWRA.js} +4 -4
  32. package/dist/{chunk-UPRY4I63.cjs → chunk-ROCJMSJI.cjs} +3 -3
  33. package/dist/{chunk-UPRY4I63.cjs.map → chunk-ROCJMSJI.cjs.map} +1 -1
  34. package/dist/{chunk-MLFFBQ5B.js → chunk-TKGCTDQY.js} +87 -180
  35. package/dist/chunk-TKGCTDQY.js.map +1 -0
  36. package/dist/{chunk-M3AKWFAO.js → chunk-UNQVTGVG.js} +2 -2
  37. package/dist/{chunk-HYIHXXF3.js → chunk-VSWFYFIG.js} +5 -5
  38. package/dist/{chunk-6GJKSYM2.cjs → chunk-WYPROXL4.cjs} +73 -73
  39. package/dist/{chunk-6GJKSYM2.cjs.map → chunk-WYPROXL4.cjs.map} +1 -1
  40. package/dist/{chunk-76EN3GEH.cjs → chunk-X754WDWO.cjs} +40 -40
  41. package/dist/{chunk-76EN3GEH.cjs.map → chunk-X754WDWO.cjs.map} +1 -1
  42. package/dist/{chunk-N53FC732.js → chunk-XLFAFH2B.js} +2 -2
  43. package/dist/{chunk-EQWE64RJ.js → chunk-YHNBJ3F3.js} +2 -2
  44. package/dist/{chunk-CM5XKHHS.js → chunk-YSNMV53O.js} +6 -6
  45. package/dist/components/case-studies/index.cjs +9 -9
  46. package/dist/components/case-studies/index.js +3 -3
  47. package/dist/components/chat/chat-quick-action-row.d.ts +1 -26
  48. package/dist/components/chat/chat-quick-action-row.d.ts.map +1 -1
  49. package/dist/components/chat/index.cjs +3 -15
  50. package/dist/components/chat/index.cjs.map +1 -1
  51. package/dist/components/chat/index.d.ts +0 -1
  52. package/dist/components/chat/index.d.ts.map +1 -1
  53. package/dist/components/chat/index.js +2 -14
  54. package/dist/components/chat/quick-action-chip.d.ts +4 -45
  55. package/dist/components/chat/quick-action-chip.d.ts.map +1 -1
  56. package/dist/components/chat/quick-action-marquee.d.ts +9 -24
  57. package/dist/components/chat/quick-action-marquee.d.ts.map +1 -1
  58. package/dist/components/contact/index.cjs +4 -4
  59. package/dist/components/contact/index.js +3 -3
  60. package/dist/components/content-loading-container.d.ts +1 -1
  61. package/dist/components/docs/index.cjs +6 -6
  62. package/dist/components/docs/index.js +5 -5
  63. package/dist/components/embeds/index.cjs +4 -4
  64. package/dist/components/embeds/index.js +3 -3
  65. package/dist/components/faq/index.cjs +5 -5
  66. package/dist/components/faq/index.js +4 -4
  67. package/dist/components/features/cards-strip.d.ts.map +1 -1
  68. package/dist/components/features/index.cjs +3 -3
  69. package/dist/components/features/index.js +2 -2
  70. package/dist/components/form.d.ts +1 -1
  71. package/dist/components/form.d.ts.map +1 -1
  72. package/dist/components/help-center-pages/index.cjs +22 -22
  73. package/dist/components/help-center-pages/index.js +13 -13
  74. package/dist/components/index.cjs +135 -159
  75. package/dist/components/index.cjs.map +1 -1
  76. package/dist/components/index.js +11 -35
  77. package/dist/components/index.js.map +1 -1
  78. package/dist/components/navigation/index.cjs +3 -3
  79. package/dist/components/navigation/index.js +2 -2
  80. package/dist/components/onboarding-guides/index.cjs +7 -7
  81. package/dist/components/onboarding-guides/index.js +6 -6
  82. package/dist/components/related-content/index.cjs +5 -5
  83. package/dist/components/related-content/index.js +4 -4
  84. package/dist/components/tickets/index.cjs +6 -6
  85. package/dist/components/tickets/index.js +5 -5
  86. package/dist/components/ui/index.cjs +3 -27
  87. package/dist/components/ui/index.cjs.map +1 -1
  88. package/dist/components/ui/index.d.ts +0 -1
  89. package/dist/components/ui/index.d.ts.map +1 -1
  90. package/dist/components/ui/index.js +2 -26
  91. package/dist/hooks/index.cjs +2 -6
  92. package/dist/hooks/index.cjs.map +1 -1
  93. package/dist/hooks/index.js +1 -5
  94. package/dist/hooks/ui/index.d.ts +0 -2
  95. package/dist/hooks/ui/index.d.ts.map +1 -1
  96. package/dist/index.cjs +3 -31
  97. package/dist/index.cjs.map +1 -1
  98. package/dist/index.js +2 -30
  99. package/dist/tailwind.config.cjs +8 -1
  100. package/dist/tailwind.config.cjs.map +1 -1
  101. package/dist/tailwind.config.js +8 -1
  102. package/dist/tailwind.config.js.map +1 -1
  103. package/package.json +1 -1
  104. package/src/components/chat/chat-quick-action-row.tsx +3 -47
  105. package/src/components/chat/index.ts +0 -1
  106. package/src/components/chat/quick-action-chip.tsx +13 -81
  107. package/src/components/chat/quick-action-marquee.tsx +47 -54
  108. package/src/components/features/cards-strip.tsx +158 -88
  109. package/src/components/ui/index.ts +0 -1
  110. package/src/hooks/ui/index.ts +0 -2
  111. package/src/stories/QuickActionMarquee.stories.tsx +7 -8
  112. package/tailwind.config.ts +7 -0
  113. package/dist/chunk-HXCQHMS5.js.map +0 -1
  114. package/dist/chunk-MLFFBQ5B.js.map +0 -1
  115. package/dist/chunk-N5M72LJX.cjs.map +0 -1
  116. package/dist/chunk-QF3NQW7C.cjs.map +0 -1
  117. package/dist/components/chat/quick-action-wall.d.ts +0 -96
  118. package/dist/components/chat/quick-action-wall.d.ts.map +0 -1
  119. package/dist/components/ui/marquee-wall.d.ts +0 -159
  120. package/dist/components/ui/marquee-wall.d.ts.map +0 -1
  121. package/dist/hooks/ui/use-marquee-engine.d.ts +0 -73
  122. package/dist/hooks/ui/use-marquee-engine.d.ts.map +0 -1
  123. package/dist/hooks/ui/use-suppress-clone-focus.d.ts +0 -24
  124. package/dist/hooks/ui/use-suppress-clone-focus.d.ts.map +0 -1
  125. package/src/components/chat/quick-action-wall.tsx +0 -247
  126. package/src/components/ui/marquee-wall.tsx +0 -522
  127. package/src/hooks/ui/use-marquee-engine.ts +0 -158
  128. package/src/hooks/ui/use-suppress-clone-focus.ts +0 -61
  129. package/src/stories/MarqueeWall.stories.tsx +0 -132
  130. package/src/stories/QuickActionWall.stories.tsx +0 -116
  131. /package/dist/{chunk-PCW2TRZ4.js.map → chunk-4IMXFDRI.js.map} +0 -0
  132. /package/dist/{chunk-VUDZOCPW.js.map → chunk-BSBWBQ7M.js.map} +0 -0
  133. /package/dist/{chunk-RQHHACP2.js.map → chunk-HQ7S6JG5.js.map} +0 -0
  134. /package/dist/{chunk-K3GXIGN6.js.map → chunk-JAASDJOF.js.map} +0 -0
  135. /package/dist/{chunk-4VSTLJDO.js.map → chunk-LWT6G75Q.js.map} +0 -0
  136. /package/dist/{chunk-4SZDO3TE.js.map → chunk-MWRFPLNB.js.map} +0 -0
  137. /package/dist/{chunk-3ITY2LBM.js.map → chunk-NPBTNWRA.js.map} +0 -0
  138. /package/dist/{chunk-M3AKWFAO.js.map → chunk-UNQVTGVG.js.map} +0 -0
  139. /package/dist/{chunk-HYIHXXF3.js.map → chunk-VSWFYFIG.js.map} +0 -0
  140. /package/dist/{chunk-N53FC732.js.map → chunk-XLFAFH2B.js.map} +0 -0
  141. /package/dist/{chunk-EQWE64RJ.js.map → chunk-YHNBJ3F3.js.map} +0 -0
  142. /package/dist/{chunk-CM5XKHHS.js.map → chunk-YSNMV53O.js.map} +0 -0
@@ -1,61 +0,0 @@
1
- 'use client'
2
-
3
- import { useEffect, useRef } from 'react'
4
-
5
- // Every focusable a clone might contain — matched so it can be pulled out of the
6
- // keyboard tab order (see useSuppressCloneFocus). `[contenteditable]:not(
7
- // [contenteditable="false"])` catches the empty/`"true"`/`"plaintext-only"`
8
- // editable forms; `summary` is focusable inside a <details>.
9
- const CLONE_FOCUSABLE_SELECTOR =
10
- 'a[href],button,input,select,textarea,iframe,summary,audio[controls],video[controls],[tabindex],[contenteditable]:not([contenteditable="false"])'
11
-
12
- /**
13
- * Keep a marquee clone's content pointer-CLICKABLE while taking it out of the
14
- * accessibility tree and the keyboard tab order. Shared by every endless-loop
15
- * surface that renders a second track copy (<CardsStrip> managed cells,
16
- * <MarqueeWall> clone copies).
17
- *
18
- * `inert` (the previous approach) does all three at once: a11y-hidden,
19
- * un-focusable AND un-clickable. That third effect was the "news marquee stops
20
- * being clickable after you scroll it for a while" bug — the 2-copy endless
21
- * loop ALWAYS paints clone cards inside the viewport near the copy seam, so a
22
- * user who manually scrolls and parks there is looking at real-looking cards
23
- * that silently swallow every click. Clones must therefore stay clickable (a
24
- * click on ANY visible card must open its link — mouse, ⌘-click, middle-click
25
- * and context-menu all handled natively by the clone's own anchor).
26
- *
27
- * The a11y contract is still met without `inert`: the caller keeps
28
- * `aria-hidden` on the clone wrapper (out of the a11y tree) and this hook
29
- * forces every focusable descendant to `tabindex="-1"` (out of the tab order →
30
- * no duplicate tab stops, no aria-hidden-focus violation). Re-applied on
31
- * subtree mutation. This is the standard loop-clone treatment used by
32
- * Swiper/Splide.
33
- */
34
- export function useSuppressCloneFocus(active: boolean) {
35
- const ref = useRef<HTMLDivElement | null>(null)
36
- useEffect(() => {
37
- const root = ref.current
38
- if (!root || !active) return
39
- const suppress = () => {
40
- root.querySelectorAll<HTMLElement>(CLONE_FOCUSABLE_SELECTOR).forEach(el => {
41
- // Only stamp the ones not already stamped — avoids touching the DOM (and
42
- // clobbering an intentional tabindex) on every mutation.
43
- if (el.getAttribute('tabindex') !== '-1') el.setAttribute('tabindex', '-1')
44
- })
45
- }
46
- suppress()
47
- // Watch structure AND the attributes that can turn a descendant tabbable
48
- // (an anchor gaining `href`, React restoring `tabindex="0"`, an element
49
- // becoming editable, media gaining `controls`). The `!== '-1'` guard above
50
- // makes our own tabindex writes a no-op, so the observer never loops.
51
- const mo = new MutationObserver(suppress)
52
- mo.observe(root, {
53
- childList: true,
54
- subtree: true,
55
- attributes: true,
56
- attributeFilter: ['href', 'tabindex', 'contenteditable', 'controls'],
57
- })
58
- return () => mo.disconnect()
59
- }, [active])
60
- return ref
61
- }
@@ -1,132 +0,0 @@
1
- import type { Meta, StoryObj } from '@storybook/nextjs-vite'
2
- import { MarqueeWall, useMarqueeSync } from '../components/ui/marquee-wall'
3
-
4
- // Arbitrary-children demo content: simple lockup tiles (the wall is
5
- // content-agnostic — chips, logos, feed rows all ride the same engine).
6
- const TILES = [
7
- 'Datto RMM',
8
- 'ConnectWise',
9
- 'HaloPSA',
10
- 'SentinelOne',
11
- 'Proofpoint',
12
- 'Veeam',
13
- 'Auvik',
14
- 'IT Glue',
15
- ]
16
-
17
- function Tile({ label }: { label: string }) {
18
- return (
19
- <div className="flex h-16 w-48 shrink-0 items-center justify-center rounded-md border border-ods-border bg-ods-card text-h6 text-ods-text-secondary">
20
- {label}
21
- </div>
22
- )
23
- }
24
-
25
- const meta: Meta<typeof MarqueeWall> = {
26
- title: 'UI/MarqueeWall',
27
- component: MarqueeWall,
28
- parameters: {
29
- layout: 'padded',
30
- docs: {
31
- description: {
32
- component:
33
- 'THE endless-scroll wall container — a clipped area whose content loops as a marquee along either axis, fading where the clip cuts. Transform-driven on the shared `useMarqueeEngine` core (no inner scroller). Auto-static when the content fits or under `prefers-reduced-motion`. `useMarqueeSync` pins multiple walls to one position driver; `trackId` publishes live offsets for FLIP-morph coordination.',
34
- },
35
- },
36
- },
37
- decorators: [
38
- (Story) => (
39
- <div className="bg-ods-bg p-[var(--spacing-system-xl)]">
40
- <Story />
41
- </div>
42
- ),
43
- ],
44
- }
45
-
46
- export default meta
47
- type Story = StoryObj<typeof MarqueeWall>
48
-
49
- /** Horizontal loop of arbitrary children with the default right fade. */
50
- export const Horizontal: Story = {
51
- render: () => (
52
- <MarqueeWall fade="right" copyGap="var(--spacing-system-mf)" className="w-full" contentClassName="flex items-center gap-[var(--spacing-system-mf)]">
53
- {TILES.map(t => (
54
- <Tile key={t} label={t} />
55
- ))}
56
- </MarqueeWall>
57
- ),
58
- }
59
-
60
- /** Vertical loop (bottom fade → bottom→top travel) inside a height cap. */
61
- export const Vertical: Story = {
62
- render: () => (
63
- <div className="w-64 rounded-md border border-ods-border bg-ods-card p-[var(--spacing-system-mf)]">
64
- <MarqueeWall
65
- fade="bottom"
66
- fadeColor="var(--color-bg-card)"
67
- copyGap="var(--spacing-system-mf)"
68
- className="max-h-[240px]"
69
- contentClassName="flex flex-col gap-[var(--spacing-system-mf)]"
70
- >
71
- {TILES.map(t => (
72
- <Tile key={t} label={t} />
73
- ))}
74
- </MarqueeWall>
75
- </div>
76
- ),
77
- }
78
-
79
- /** Two walls pinned to ONE position driver via `useMarqueeSync` — scroll
80
- * stays pixel-locked (the FreeTrialCTA resolve-strip mechanism). */
81
- export const SyncedPair: Story = {
82
- render: function SyncedPairStory() {
83
- const sync = useMarqueeSync()
84
- return (
85
- <div className="flex flex-col gap-[var(--spacing-system-mf)]">
86
- {[0, 1].map(row => (
87
- <MarqueeWall
88
- key={row}
89
- sync={sync}
90
- axis="x"
91
- copyGap="var(--spacing-system-mf)"
92
- className="w-full"
93
- contentClassName="flex items-center gap-[var(--spacing-system-mf)]"
94
- >
95
- {TILES.map(t => (
96
- <Tile key={t} label={t} />
97
- ))}
98
- </MarqueeWall>
99
- ))}
100
- </div>
101
- )
102
- },
103
- }
104
-
105
- /** Plain mode — the consumer's opt-out: same wall, no motion, no fades. */
106
- export const Plain: Story = {
107
- render: () => (
108
- <MarqueeWall mode="plain" className="w-full" contentClassName="flex items-center gap-[var(--spacing-system-mf)]">
109
- {TILES.map(t => (
110
- <Tile key={t} label={t} />
111
- ))}
112
- </MarqueeWall>
113
- ),
114
- }
115
-
116
- /** Reverse travel (content moves right) with a custom-size leading fade. */
117
- export const Reversed: Story = {
118
- render: () => (
119
- <MarqueeWall
120
- reverse
121
- fade={['left', 'right']}
122
- fadeSize={{ left: 48 }}
123
- copyGap="var(--spacing-system-mf)"
124
- className="w-full"
125
- contentClassName="flex items-center gap-[var(--spacing-system-mf)]"
126
- >
127
- {TILES.map(t => (
128
- <Tile key={t} label={t} />
129
- ))}
130
- </MarqueeWall>
131
- ),
132
- }
@@ -1,116 +0,0 @@
1
- import type { Meta, StoryObj } from '@storybook/nextjs-vite'
2
- import { QuickActionWall, interleave } from '../components/chat/quick-action-wall'
3
- import type { QuickActionChip } from '../components/chat/chat-quick-action-row'
4
-
5
- // A mixed IT/SEC stream (per-chip themes + lozenges) and a fae wall — the two
6
- // wall registers: category-classified deck panels vs agent-themed hero walls.
7
- const IT_ACTIONS: ReadonlyArray<QuickActionChip> = [
8
- { id: 'reset-password', label: 'Reset a password', icon: { name: 'key' }, theme: 'it' },
9
- { id: 'new-laptop', label: 'Provision a new laptop', icon: { name: 'laptop' }, theme: 'it' },
10
- { id: 'printer', label: 'Fix the printer', icon: { name: 'printer' }, theme: 'it' },
11
- { id: 'vpn', label: 'VPN will not connect', icon: { name: 'globe' }, theme: 'it' },
12
- { id: 'disk-full', label: 'Disk almost full', icon: { name: 'hard-drive' }, theme: 'it' },
13
- { id: 'onboard', label: 'Onboard a new hire', icon: { name: 'user-plus' }, theme: 'it' },
14
- ]
15
-
16
- const SEC_ACTIONS: ReadonlyArray<QuickActionChip> = [
17
- { id: 'phishing', label: 'Phishing report triage', icon: { name: 'fish' }, theme: 'sec' },
18
- { id: 'mfa', label: 'Enforce MFA', icon: { name: 'shield-check' }, theme: 'sec' },
19
- { id: 'patch', label: 'Patch the CVE', icon: { name: 'bug' }, theme: 'sec' },
20
- { id: 'offboard', label: 'Revoke access on exit', icon: { name: 'user-minus' }, theme: 'sec' },
21
- { id: 'edr-alert', label: 'EDR alert review', icon: { name: 'radar' }, theme: 'sec' },
22
- ]
23
-
24
- const FAE_ACTIONS: ReadonlyArray<QuickActionChip> = [
25
- { id: 'how-to-start', label: 'How to start', icon: { name: 'fae' } },
26
- { id: 'connect-device', label: 'Connect device', icon: { name: 'search' }, theme: 'fae' },
27
- { id: 'find-device', label: 'Find device', icon: { name: 'compass' }, theme: 'fae' },
28
- { id: 'remote-connection', label: 'Remote connection', icon: { name: 'rocket' }, theme: 'fae' },
29
- { id: 'run-scripts', label: 'Run scripts', icon: { name: 'bracket-curly' }, theme: 'fae' },
30
- { id: 'device-software', label: 'Device software', icon: { name: 'package' }, theme: 'fae' },
31
- ]
32
-
33
- const meta: Meta<typeof QuickActionWall> = {
34
- title: 'Chat/QuickActionWall',
35
- component: QuickActionWall,
36
- parameters: {
37
- layout: 'padded',
38
- docs: {
39
- description: {
40
- component:
41
- 'THE quick-action chip wall: a themed pile of the shared chip on the shared `<MarqueeWall>` engine. Static-with-fade when content fits (or `prefers-reduced-motion`); an endless marquee when it overflows — the axis follows the fade (`bottom` fade → content travels bottom→top; `right` fade → right→left). Themes: `fae`/`mingo` (agent identities) and `it`/`sec` (classification pair, with lozenges). Interactive chips pause the track on hover; clone-copy chips stay clickable with wrapper-level a11y. `rows` renders the brick-wall mode (stacked independent row marquees).',
42
- },
43
- },
44
- },
45
- decorators: [
46
- (Story) => (
47
- <div className="bg-ods-bg p-[var(--spacing-system-xl)]">
48
- <Story />
49
- </div>
50
- ),
51
- ],
52
- }
53
-
54
- export default meta
55
- type Story = StoryObj<typeof QuickActionWall>
56
-
57
- /** Vertical wall (bottom fade → bottom→top marquee): the deck-panel register —
58
- * mixed IT/SEC stream with lozenges, decorative chips. */
59
- export const VerticalMixedStream: Story = {
60
- render: () => (
61
- <div className="rounded-md border border-ods-border bg-ods-card p-[var(--spacing-system-mf)]">
62
- <QuickActionWall
63
- chips={interleave(IT_ACTIONS, SEC_ACTIONS)}
64
- lozenges
65
- fade="bottom"
66
- fadeColor="var(--color-bg-card)"
67
- className="max-h-[180px]"
68
- />
69
- </div>
70
- ),
71
- }
72
-
73
- /** Horizontal wall (right fade): the hero-tab register — a wide fae wall
74
- * drifting right→left, interactive chips (hover pauses the track). */
75
- export const HorizontalInteractive: Story = {
76
- render: () => (
77
- <QuickActionWall
78
- chips={FAE_ACTIONS.map((c, i) => ({ ...c, selected: i === 0, selectedAccent: 'pink', onSelect: () => console.log(c.id) }))}
79
- fade="right"
80
- className="w-full"
81
- contentClassName="w-[1400px]"
82
- />
83
- ),
84
- }
85
-
86
- /** Plain mode — the consumer's opt-out: same chips and layout, no marquee,
87
- * no blur. */
88
- export const Plain: Story = {
89
- render: () => (
90
- <div className="rounded-md border border-ods-border bg-ods-card p-[var(--spacing-system-mf)]">
91
- <QuickActionWall
92
- chips={interleave(IT_ACTIONS, SEC_ACTIONS)}
93
- mode="plain"
94
- lozenges
95
- className="max-h-[180px]"
96
- />
97
- </div>
98
- ),
99
- }
100
-
101
- /** Loading: skeleton chips (1:1 geometry, lozenge slots reserved) — static
102
- * until real chips land. */
103
- export const Loading: Story = {
104
- render: () => (
105
- <div className="rounded-md border border-ods-border bg-ods-card p-[var(--spacing-system-mf)]">
106
- <QuickActionWall
107
- chips={[]}
108
- loading
109
- lozenges
110
- fade="bottom"
111
- fadeColor="var(--color-bg-card)"
112
- className="max-h-[180px]"
113
- />
114
- </div>
115
- ),
116
- }