@flamingo-stack/openframe-frontend-core 0.0.410-snapshot.20260711165901 → 0.0.410

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 (124) hide show
  1. package/dist/{chunk-SOLI7V4I.js → chunk-5HOII3WB.js} +4 -4
  2. package/dist/{chunk-4FNDOTW2.js → chunk-7Q52HP45.js} +77 -139
  3. package/dist/chunk-7Q52HP45.js.map +1 -0
  4. package/dist/{chunk-ZZWFS7CD.cjs → chunk-A3RW7M6B.cjs} +29 -29
  5. package/dist/{chunk-ZZWFS7CD.cjs.map → chunk-A3RW7M6B.cjs.map} +1 -1
  6. package/dist/{chunk-G2G7HLEM.cjs → chunk-CUN4CJUN.cjs} +3 -3
  7. package/dist/{chunk-G2G7HLEM.cjs.map → chunk-CUN4CJUN.cjs.map} +1 -1
  8. package/dist/{chunk-NJSBYUJZ.js → chunk-E4J5KKHH.js} +3 -3
  9. package/dist/{chunk-VTTPNZX3.cjs → chunk-F4KE5AZO.cjs} +40 -40
  10. package/dist/{chunk-VTTPNZX3.cjs.map → chunk-F4KE5AZO.cjs.map} +1 -1
  11. package/dist/{chunk-B2BERJ4M.js → chunk-IK5AR6QG.js} +6 -6
  12. package/dist/{chunk-YVJ2XBFU.js → chunk-IVM47EGI.js} +398 -389
  13. package/dist/chunk-IVM47EGI.js.map +1 -0
  14. package/dist/{chunk-MHNBDSWH.js → chunk-J6M2G6TD.js} +6 -6
  15. package/dist/{chunk-BRHCGXOH.cjs → chunk-KPGKNUJF.cjs} +40 -40
  16. package/dist/{chunk-BRHCGXOH.cjs.map → chunk-KPGKNUJF.cjs.map} +1 -1
  17. package/dist/{chunk-XVPWVG6N.cjs → chunk-KRLBVBYU.cjs} +18 -18
  18. package/dist/{chunk-XVPWVG6N.cjs.map → chunk-KRLBVBYU.cjs.map} +1 -1
  19. package/dist/{chunk-XBC5AES6.js → chunk-KZPVN7AZ.js} +2 -2
  20. package/dist/{chunk-HXRULNWA.js → chunk-L6OPVKVR.js} +5 -5
  21. package/dist/{chunk-FXZO4EKN.js → chunk-LQWYXIKB.js} +5 -5
  22. package/dist/{chunk-H4ISMWHG.cjs → chunk-NEMUCNCG.cjs} +73 -73
  23. package/dist/{chunk-H4ISMWHG.cjs.map → chunk-NEMUCNCG.cjs.map} +1 -1
  24. package/dist/{chunk-DGIF3YDV.js → chunk-NO2KT42M.js} +2 -2
  25. package/dist/{chunk-6AB36PRE.js → chunk-O5W26SCL.js} +2 -2
  26. package/dist/{chunk-3U4IZF7H.cjs → chunk-RL344I7V.cjs} +7 -7
  27. package/dist/{chunk-3U4IZF7H.cjs.map → chunk-RL344I7V.cjs.map} +1 -1
  28. package/dist/{chunk-MF5CVIAC.cjs → chunk-RPGFIUN3.cjs} +32 -32
  29. package/dist/{chunk-MF5CVIAC.cjs.map → chunk-RPGFIUN3.cjs.map} +1 -1
  30. package/dist/{chunk-BKYV2ALJ.cjs → chunk-SOALFYU6.cjs} +591 -582
  31. package/dist/chunk-SOALFYU6.cjs.map +1 -0
  32. package/dist/{chunk-3SNDISEN.cjs → chunk-STGWQFYT.cjs} +30 -92
  33. package/dist/chunk-STGWQFYT.cjs.map +1 -0
  34. package/dist/{chunk-VXM2GCKS.cjs → chunk-T6ZUBXPA.cjs} +12 -12
  35. package/dist/{chunk-VXM2GCKS.cjs.map → chunk-T6ZUBXPA.cjs.map} +1 -1
  36. package/dist/{chunk-SMLAYAIW.cjs → chunk-V3EZQWHQ.cjs} +5 -5
  37. package/dist/{chunk-SMLAYAIW.cjs.map → chunk-V3EZQWHQ.cjs.map} +1 -1
  38. package/dist/{chunk-Z4RKJOFH.js → chunk-VUW4G6SU.js} +3 -3
  39. package/dist/{chunk-SZ27SV6T.cjs → chunk-WGOATV3P.cjs} +73 -73
  40. package/dist/{chunk-SZ27SV6T.cjs.map → chunk-WGOATV3P.cjs.map} +1 -1
  41. package/dist/{chunk-RALT6D4H.cjs → chunk-YRKPMYI3.cjs} +15 -15
  42. package/dist/{chunk-RALT6D4H.cjs.map → chunk-YRKPMYI3.cjs.map} +1 -1
  43. package/dist/{chunk-TYCWPT33.js → chunk-ZF5O3KUQ.js} +3 -3
  44. package/dist/{chunk-SAJM3PDE.js → chunk-ZMFQWMM7.js} +2 -2
  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/index.cjs +3 -3
  48. package/dist/components/chat/index.js +2 -2
  49. package/dist/components/contact/index.cjs +4 -4
  50. package/dist/components/contact/index.js +3 -3
  51. package/dist/components/docs/index.cjs +6 -6
  52. package/dist/components/docs/index.js +5 -5
  53. package/dist/components/embeds/index.cjs +4 -4
  54. package/dist/components/embeds/index.js +3 -3
  55. package/dist/components/faq/index.cjs +5 -5
  56. package/dist/components/faq/index.js +4 -4
  57. package/dist/components/features/index.cjs +3 -3
  58. package/dist/components/features/index.js +2 -2
  59. package/dist/components/help-center-pages/index.cjs +22 -22
  60. package/dist/components/help-center-pages/index.js +13 -13
  61. package/dist/components/index.cjs +134 -136
  62. package/dist/components/index.cjs.map +1 -1
  63. package/dist/components/index.js +11 -13
  64. package/dist/components/index.js.map +1 -1
  65. package/dist/components/navigation/header.d.ts.map +1 -1
  66. package/dist/components/navigation/index.cjs +3 -5
  67. package/dist/components/navigation/index.cjs.map +1 -1
  68. package/dist/components/navigation/index.d.ts +1 -1
  69. package/dist/components/navigation/index.d.ts.map +1 -1
  70. package/dist/components/navigation/index.js +2 -4
  71. package/dist/components/navigation/mobile-burger-menu.d.ts.map +1 -1
  72. package/dist/components/navigation/mobile-nav-panel.d.ts +0 -2
  73. package/dist/components/navigation/mobile-nav-panel.d.ts.map +1 -1
  74. package/dist/components/navigation/sliding-sidebar.d.ts.map +1 -1
  75. package/dist/components/onboarding-guides/index.cjs +7 -7
  76. package/dist/components/onboarding-guides/index.js +6 -6
  77. package/dist/components/related-content/index.cjs +5 -5
  78. package/dist/components/related-content/index.js +4 -4
  79. package/dist/components/tickets/index.cjs +6 -6
  80. package/dist/components/tickets/index.js +5 -5
  81. package/dist/components/ui/index.cjs +3 -3
  82. package/dist/components/ui/index.js +2 -2
  83. package/dist/components/ui/modal-v2.d.ts.map +1 -1
  84. package/dist/components/ui/modal.d.ts.map +1 -1
  85. package/dist/hooks/index.cjs +2 -6
  86. package/dist/hooks/index.cjs.map +1 -1
  87. package/dist/hooks/index.js +1 -5
  88. package/dist/hooks/ui/index.d.ts +0 -2
  89. package/dist/hooks/ui/index.d.ts.map +1 -1
  90. package/dist/hooks/ui/use-header-height.d.ts +1 -10
  91. package/dist/hooks/ui/use-header-height.d.ts.map +1 -1
  92. package/dist/index.cjs +3 -9
  93. package/dist/index.cjs.map +1 -1
  94. package/dist/index.js +2 -8
  95. package/package.json +1 -1
  96. package/src/components/navigation/header.tsx +0 -5
  97. package/src/components/navigation/index.ts +1 -1
  98. package/src/components/navigation/mobile-burger-menu.tsx +29 -16
  99. package/src/components/navigation/mobile-nav-panel.tsx +35 -32
  100. package/src/components/navigation/sliding-sidebar.tsx +9 -29
  101. package/src/components/ui/modal-v2.tsx +6 -7
  102. package/src/components/ui/modal.tsx +9 -7
  103. package/src/hooks/ui/index.ts +0 -6
  104. package/src/hooks/ui/use-header-height.ts +2 -17
  105. package/dist/chunk-3SNDISEN.cjs.map +0 -1
  106. package/dist/chunk-4FNDOTW2.js.map +0 -1
  107. package/dist/chunk-BKYV2ALJ.cjs.map +0 -1
  108. package/dist/chunk-YVJ2XBFU.js.map +0 -1
  109. package/dist/hooks/ui/use-focus-trap.d.ts +0 -32
  110. package/dist/hooks/ui/use-focus-trap.d.ts.map +0 -1
  111. package/src/hooks/ui/use-focus-trap.ts +0 -110
  112. package/src/stories/MobileNavPanel.stories.tsx +0 -150
  113. /package/dist/{chunk-SOLI7V4I.js.map → chunk-5HOII3WB.js.map} +0 -0
  114. /package/dist/{chunk-NJSBYUJZ.js.map → chunk-E4J5KKHH.js.map} +0 -0
  115. /package/dist/{chunk-B2BERJ4M.js.map → chunk-IK5AR6QG.js.map} +0 -0
  116. /package/dist/{chunk-MHNBDSWH.js.map → chunk-J6M2G6TD.js.map} +0 -0
  117. /package/dist/{chunk-XBC5AES6.js.map → chunk-KZPVN7AZ.js.map} +0 -0
  118. /package/dist/{chunk-HXRULNWA.js.map → chunk-L6OPVKVR.js.map} +0 -0
  119. /package/dist/{chunk-FXZO4EKN.js.map → chunk-LQWYXIKB.js.map} +0 -0
  120. /package/dist/{chunk-DGIF3YDV.js.map → chunk-NO2KT42M.js.map} +0 -0
  121. /package/dist/{chunk-6AB36PRE.js.map → chunk-O5W26SCL.js.map} +0 -0
  122. /package/dist/{chunk-Z4RKJOFH.js.map → chunk-VUW4G6SU.js.map} +0 -0
  123. /package/dist/{chunk-TYCWPT33.js.map → chunk-ZF5O3KUQ.js.map} +0 -0
  124. /package/dist/{chunk-SAJM3PDE.js.map → chunk-ZMFQWMM7.js.map} +0 -0
@@ -1,32 +0,0 @@
1
- import { type RefObject } from 'react';
2
- export interface UseFocusTrapOptions {
3
- /** Called when Escape is pressed while focus is inside the container.
4
- * When omitted, Escape passes through untouched so document-level
5
- * listeners still fire. */
6
- onEscape?: () => void;
7
- /**
8
- * When false, Tab/Shift+Tab are NOT cycled within the container — for
9
- * non-modal dialogs (e.g. SlidingSidebar, whose z-50 header stays clickable
10
- * above the open drawer). Escape / initial focus / guarded restore still apply.
11
- * @default true
12
- */
13
- contain?: boolean;
14
- }
15
- /**
16
- * Focus management for overlay surfaces (mobile nav panel, sliding sidebar,
17
- * burger menu, modals): initial focus on activate, container-scoped Tab
18
- * cycling + Escape, and guarded focus restore on deactivate.
19
- *
20
- * CONTRACT: the container element MUST have `tabIndex={-1}`. After a
21
- * click/tap on a non-focusable area inside the surface, browsers move focus
22
- * to the nearest focusable ancestor — with the container script-focusable
23
- * that ancestor is the container itself, so this keydown listener keeps
24
- * hearing Escape/Tab. Without it, focus falls to `body` and the trap goes
25
- * deaf.
26
- *
27
- * The listener being container-scoped makes it stacking-safe: it only fires
28
- * while focus is inside the surface, so a modal open above never leaks its
29
- * Escape into the surface below.
30
- */
31
- export declare function useFocusTrap(containerRef: RefObject<HTMLElement | null>, active: boolean, { onEscape, contain }?: UseFocusTrapOptions): void;
32
- //# sourceMappingURL=use-focus-trap.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"use-focus-trap.d.ts","sourceRoot":"","sources":["../../../src/hooks/ui/use-focus-trap.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAWzD,MAAM,WAAW,mBAAmB;IAClC;;gCAE4B;IAC5B,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAC1B,YAAY,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EAC3C,MAAM,EAAE,OAAO,EACf,EAAE,QAAQ,EAAE,OAAc,EAAE,GAAE,mBAAwB,GACrD,IAAI,CA8DN"}
@@ -1,110 +0,0 @@
1
- 'use client'
2
-
3
- import { useEffect, useRef, type RefObject } from 'react'
4
-
5
- const FOCUSABLE_SELECTOR = [
6
- 'a[href]',
7
- 'button:not([disabled])',
8
- 'input:not([disabled])',
9
- 'select:not([disabled])',
10
- 'textarea:not([disabled])',
11
- '[tabindex]:not([tabindex="-1"])',
12
- ].join(', ')
13
-
14
- export interface UseFocusTrapOptions {
15
- /** Called when Escape is pressed while focus is inside the container.
16
- * When omitted, Escape passes through untouched so document-level
17
- * listeners still fire. */
18
- onEscape?: () => void
19
- /**
20
- * When false, Tab/Shift+Tab are NOT cycled within the container — for
21
- * non-modal dialogs (e.g. SlidingSidebar, whose z-50 header stays clickable
22
- * above the open drawer). Escape / initial focus / guarded restore still apply.
23
- * @default true
24
- */
25
- contain?: boolean
26
- }
27
-
28
- /**
29
- * Focus management for overlay surfaces (mobile nav panel, sliding sidebar,
30
- * burger menu, modals): initial focus on activate, container-scoped Tab
31
- * cycling + Escape, and guarded focus restore on deactivate.
32
- *
33
- * CONTRACT: the container element MUST have `tabIndex={-1}`. After a
34
- * click/tap on a non-focusable area inside the surface, browsers move focus
35
- * to the nearest focusable ancestor — with the container script-focusable
36
- * that ancestor is the container itself, so this keydown listener keeps
37
- * hearing Escape/Tab. Without it, focus falls to `body` and the trap goes
38
- * deaf.
39
- *
40
- * The listener being container-scoped makes it stacking-safe: it only fires
41
- * while focus is inside the surface, so a modal open above never leaks its
42
- * Escape into the surface below.
43
- */
44
- export function useFocusTrap(
45
- containerRef: RefObject<HTMLElement | null>,
46
- active: boolean,
47
- { onEscape, contain = true }: UseFocusTrapOptions = {}
48
- ): void {
49
- // Latest-ref so unstable inline callbacks never tear the trap down and
50
- // re-yank focus — same stable-callback discipline as `useCloseOnNavigation`.
51
- const onEscapeRef = useRef(onEscape)
52
- onEscapeRef.current = onEscape
53
-
54
- useEffect(() => {
55
- if (!active) return
56
- const container = containerRef.current
57
- if (!container) return
58
-
59
- const previouslyFocused = document.activeElement as HTMLElement | null
60
-
61
- const focusables = () =>
62
- Array.from(container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR))
63
- .filter((el) => el.getClientRects().length > 0)
64
-
65
- const first = focusables()[0]
66
- ;(first ?? container).focus({ preventScroll: true })
67
-
68
- const handleKeyDown = (e: KeyboardEvent) => {
69
- if (e.key === 'Escape') {
70
- // No preventDefault/stopPropagation — pass-through by design.
71
- onEscapeRef.current?.()
72
- return
73
- }
74
- if (e.key !== 'Tab' || !contain) return
75
- const items = focusables()
76
- if (items.length === 0) {
77
- e.preventDefault()
78
- container.focus({ preventScroll: true })
79
- return
80
- }
81
- const firstEl = items[0]
82
- const lastEl = items[items.length - 1]
83
- const current = document.activeElement
84
- if (e.shiftKey && (current === firstEl || current === container)) {
85
- e.preventDefault()
86
- lastEl.focus({ preventScroll: true })
87
- } else if (!e.shiftKey && current === lastEl) {
88
- e.preventDefault()
89
- firstEl.focus({ preventScroll: true })
90
- }
91
- }
92
-
93
- container.addEventListener('keydown', handleKeyDown)
94
-
95
- return () => {
96
- container.removeEventListener('keydown', handleKeyDown)
97
- // Guarded restore: only when focus is still inside the surface (or fell
98
- // to body because the surface unmounted) — never steal focus from a
99
- // dialog that opened meanwhile (e.g. the nav-footer → SSO modal flow).
100
- const current = document.activeElement
101
- if (
102
- previouslyFocused &&
103
- previouslyFocused.isConnected &&
104
- (current === null || current === document.body || container.contains(current))
105
- ) {
106
- previouslyFocused.focus({ preventScroll: true })
107
- }
108
- }
109
- }, [containerRef, active, contain])
110
- }
@@ -1,150 +0,0 @@
1
- import type { Meta, StoryObj } from '@storybook/nextjs-vite'
2
- import { useState, type ReactNode } from 'react'
3
- import { Button } from '../components/ui/button'
4
- import { StatusBadge } from '../components/ui/status-badge'
5
- import { MobileNavPanel } from '../components/navigation/mobile-nav-panel'
6
- import type { MobileNavConfig } from '../types/navigation'
7
-
8
- const meta: Meta<typeof MobileNavPanel> = {
9
- title: 'Navigation/MobileNavPanel',
10
- component: MobileNavPanel,
11
- parameters: {
12
- layout: 'fullscreen',
13
- },
14
- }
15
-
16
- export default meta
17
- type Story = StoryObj<typeof meta>
18
-
19
- const baseSections: MobileNavConfig['sections'] = [
20
- {
21
- title: 'Product',
22
- items: [
23
- { id: 'home', label: 'Home', href: '#', isActive: true },
24
- { id: 'features', label: 'Features', href: '#' },
25
- { id: 'pricing', label: 'Pricing', href: '#' },
26
- ],
27
- },
28
- {
29
- title: 'Community',
30
- items: [
31
- { id: 'blog', label: 'Blog', href: '#' },
32
- { id: 'podcasts', label: 'Podcasts', href: '#' },
33
- ],
34
- },
35
- ]
36
-
37
- function PanelHarness({
38
- config,
39
- children,
40
- }: {
41
- config: Omit<MobileNavConfig, 'onClose'>
42
- children?: ReactNode
43
- }) {
44
- const [open, setOpen] = useState(true)
45
-
46
- return (
47
- <div className="min-h-screen bg-ods-bg p-4">
48
- <Button variant="outline" onClick={() => setOpen(true)}>
49
- Open menu
50
- </Button>
51
- {children}
52
- <MobileNavPanel
53
- isOpen={open}
54
- config={{ ...config, onClose: () => setOpen(false) }}
55
- />
56
- </div>
57
- )
58
- }
59
-
60
- /** Default chrome (openmsp-style): bg-ods-card + border, auth footer. */
61
- export const Default: Story = {
62
- render: function Render() {
63
- return (
64
- <PanelHarness
65
- config={{
66
- sections: baseSections,
67
- footer: (
68
- <Button className="w-full" variant="outline">
69
- Sign Up
70
- </Button>
71
- ),
72
- }}
73
- />
74
- )
75
- },
76
- }
77
-
78
- /** Flamingo-style custom chrome via config.className (mobileNavClassName knob). */
79
- export const CustomChrome: Story = {
80
- render: function Render() {
81
- return (
82
- <PanelHarness
83
- config={{
84
- sections: baseSections,
85
- className: 'bg-ods-bg border border-ods-border',
86
- }}
87
- />
88
- )
89
- },
90
- }
91
-
92
- /** TMCG-style disabled auth footer with a status badge. */
93
- export const DisabledFooter: Story = {
94
- render: function Render() {
95
- return (
96
- <PanelHarness
97
- config={{
98
- sections: baseSections,
99
- footer: (
100
- <Button className="w-full cursor-not-allowed opacity-75" variant="outline" disabled>
101
- <span className="flex items-center justify-center gap-2 w-full">
102
- Member Login
103
- <StatusBadge text="Coming Soon" variant="button" colorScheme="cyan" />
104
- </span>
105
- </Button>
106
- ),
107
- }}
108
- />
109
- )
110
- },
111
- }
112
-
113
- /**
114
- * Regression story for the reported bug shape: a SHORT visible viewport with
115
- * a long menu. The panel must fit inside the fold (svh-based max-height), the
116
- * footer must stay visible, and the list must scroll internally to the last
117
- * item. View in a ~660px-tall viewport (or the story's constrained frame).
118
- */
119
- export const ShortViewportLongList: Story = {
120
- render: function Render() {
121
- const longSections: MobileNavConfig['sections'] = [
122
- {
123
- title: 'All entries',
124
- items: Array.from({ length: 30 }, (_, i) => ({
125
- id: `item-${i + 1}`,
126
- label: i === 29 ? 'Last entry (must be reachable)' : `Menu entry ${i + 1}`,
127
- href: '#',
128
- })),
129
- },
130
- ]
131
-
132
- return (
133
- <PanelHarness
134
- config={{
135
- sections: longSections,
136
- footer: (
137
- <Button className="w-full" variant="outline">
138
- Sign Up
139
- </Button>
140
- ),
141
- }}
142
- >
143
- <p className="mt-4 text-sm text-ods-text-secondary">
144
- Resize the viewport short (~660px): the footer stays pinned and the
145
- list scrolls to "Last entry".
146
- </p>
147
- </PanelHarness>
148
- )
149
- },
150
- }