@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.
- package/dist/{chunk-SOLI7V4I.js → chunk-5HOII3WB.js} +4 -4
- package/dist/{chunk-4FNDOTW2.js → chunk-7Q52HP45.js} +77 -139
- package/dist/chunk-7Q52HP45.js.map +1 -0
- package/dist/{chunk-ZZWFS7CD.cjs → chunk-A3RW7M6B.cjs} +29 -29
- package/dist/{chunk-ZZWFS7CD.cjs.map → chunk-A3RW7M6B.cjs.map} +1 -1
- package/dist/{chunk-G2G7HLEM.cjs → chunk-CUN4CJUN.cjs} +3 -3
- package/dist/{chunk-G2G7HLEM.cjs.map → chunk-CUN4CJUN.cjs.map} +1 -1
- package/dist/{chunk-NJSBYUJZ.js → chunk-E4J5KKHH.js} +3 -3
- package/dist/{chunk-VTTPNZX3.cjs → chunk-F4KE5AZO.cjs} +40 -40
- package/dist/{chunk-VTTPNZX3.cjs.map → chunk-F4KE5AZO.cjs.map} +1 -1
- package/dist/{chunk-B2BERJ4M.js → chunk-IK5AR6QG.js} +6 -6
- package/dist/{chunk-YVJ2XBFU.js → chunk-IVM47EGI.js} +398 -389
- package/dist/chunk-IVM47EGI.js.map +1 -0
- package/dist/{chunk-MHNBDSWH.js → chunk-J6M2G6TD.js} +6 -6
- package/dist/{chunk-BRHCGXOH.cjs → chunk-KPGKNUJF.cjs} +40 -40
- package/dist/{chunk-BRHCGXOH.cjs.map → chunk-KPGKNUJF.cjs.map} +1 -1
- package/dist/{chunk-XVPWVG6N.cjs → chunk-KRLBVBYU.cjs} +18 -18
- package/dist/{chunk-XVPWVG6N.cjs.map → chunk-KRLBVBYU.cjs.map} +1 -1
- package/dist/{chunk-XBC5AES6.js → chunk-KZPVN7AZ.js} +2 -2
- package/dist/{chunk-HXRULNWA.js → chunk-L6OPVKVR.js} +5 -5
- package/dist/{chunk-FXZO4EKN.js → chunk-LQWYXIKB.js} +5 -5
- package/dist/{chunk-H4ISMWHG.cjs → chunk-NEMUCNCG.cjs} +73 -73
- package/dist/{chunk-H4ISMWHG.cjs.map → chunk-NEMUCNCG.cjs.map} +1 -1
- package/dist/{chunk-DGIF3YDV.js → chunk-NO2KT42M.js} +2 -2
- package/dist/{chunk-6AB36PRE.js → chunk-O5W26SCL.js} +2 -2
- package/dist/{chunk-3U4IZF7H.cjs → chunk-RL344I7V.cjs} +7 -7
- package/dist/{chunk-3U4IZF7H.cjs.map → chunk-RL344I7V.cjs.map} +1 -1
- package/dist/{chunk-MF5CVIAC.cjs → chunk-RPGFIUN3.cjs} +32 -32
- package/dist/{chunk-MF5CVIAC.cjs.map → chunk-RPGFIUN3.cjs.map} +1 -1
- package/dist/{chunk-BKYV2ALJ.cjs → chunk-SOALFYU6.cjs} +591 -582
- package/dist/chunk-SOALFYU6.cjs.map +1 -0
- package/dist/{chunk-3SNDISEN.cjs → chunk-STGWQFYT.cjs} +30 -92
- package/dist/chunk-STGWQFYT.cjs.map +1 -0
- package/dist/{chunk-VXM2GCKS.cjs → chunk-T6ZUBXPA.cjs} +12 -12
- package/dist/{chunk-VXM2GCKS.cjs.map → chunk-T6ZUBXPA.cjs.map} +1 -1
- package/dist/{chunk-SMLAYAIW.cjs → chunk-V3EZQWHQ.cjs} +5 -5
- package/dist/{chunk-SMLAYAIW.cjs.map → chunk-V3EZQWHQ.cjs.map} +1 -1
- package/dist/{chunk-Z4RKJOFH.js → chunk-VUW4G6SU.js} +3 -3
- package/dist/{chunk-SZ27SV6T.cjs → chunk-WGOATV3P.cjs} +73 -73
- package/dist/{chunk-SZ27SV6T.cjs.map → chunk-WGOATV3P.cjs.map} +1 -1
- package/dist/{chunk-RALT6D4H.cjs → chunk-YRKPMYI3.cjs} +15 -15
- package/dist/{chunk-RALT6D4H.cjs.map → chunk-YRKPMYI3.cjs.map} +1 -1
- package/dist/{chunk-TYCWPT33.js → chunk-ZF5O3KUQ.js} +3 -3
- package/dist/{chunk-SAJM3PDE.js → chunk-ZMFQWMM7.js} +2 -2
- package/dist/components/case-studies/index.cjs +9 -9
- package/dist/components/case-studies/index.js +3 -3
- package/dist/components/chat/index.cjs +3 -3
- package/dist/components/chat/index.js +2 -2
- package/dist/components/contact/index.cjs +4 -4
- package/dist/components/contact/index.js +3 -3
- package/dist/components/docs/index.cjs +6 -6
- package/dist/components/docs/index.js +5 -5
- package/dist/components/embeds/index.cjs +4 -4
- package/dist/components/embeds/index.js +3 -3
- package/dist/components/faq/index.cjs +5 -5
- package/dist/components/faq/index.js +4 -4
- package/dist/components/features/index.cjs +3 -3
- package/dist/components/features/index.js +2 -2
- package/dist/components/help-center-pages/index.cjs +22 -22
- package/dist/components/help-center-pages/index.js +13 -13
- package/dist/components/index.cjs +134 -136
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +11 -13
- package/dist/components/index.js.map +1 -1
- package/dist/components/navigation/header.d.ts.map +1 -1
- package/dist/components/navigation/index.cjs +3 -5
- package/dist/components/navigation/index.cjs.map +1 -1
- package/dist/components/navigation/index.d.ts +1 -1
- package/dist/components/navigation/index.d.ts.map +1 -1
- package/dist/components/navigation/index.js +2 -4
- package/dist/components/navigation/mobile-burger-menu.d.ts.map +1 -1
- package/dist/components/navigation/mobile-nav-panel.d.ts +0 -2
- package/dist/components/navigation/mobile-nav-panel.d.ts.map +1 -1
- package/dist/components/navigation/sliding-sidebar.d.ts.map +1 -1
- package/dist/components/onboarding-guides/index.cjs +7 -7
- package/dist/components/onboarding-guides/index.js +6 -6
- package/dist/components/related-content/index.cjs +5 -5
- package/dist/components/related-content/index.js +4 -4
- package/dist/components/tickets/index.cjs +6 -6
- package/dist/components/tickets/index.js +5 -5
- package/dist/components/ui/index.cjs +3 -3
- package/dist/components/ui/index.js +2 -2
- package/dist/components/ui/modal-v2.d.ts.map +1 -1
- package/dist/components/ui/modal.d.ts.map +1 -1
- package/dist/hooks/index.cjs +2 -6
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.js +1 -5
- package/dist/hooks/ui/index.d.ts +0 -2
- package/dist/hooks/ui/index.d.ts.map +1 -1
- package/dist/hooks/ui/use-header-height.d.ts +1 -10
- package/dist/hooks/ui/use-header-height.d.ts.map +1 -1
- package/dist/index.cjs +3 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -8
- package/package.json +1 -1
- package/src/components/navigation/header.tsx +0 -5
- package/src/components/navigation/index.ts +1 -1
- package/src/components/navigation/mobile-burger-menu.tsx +29 -16
- package/src/components/navigation/mobile-nav-panel.tsx +35 -32
- package/src/components/navigation/sliding-sidebar.tsx +9 -29
- package/src/components/ui/modal-v2.tsx +6 -7
- package/src/components/ui/modal.tsx +9 -7
- package/src/hooks/ui/index.ts +0 -6
- package/src/hooks/ui/use-header-height.ts +2 -17
- package/dist/chunk-3SNDISEN.cjs.map +0 -1
- package/dist/chunk-4FNDOTW2.js.map +0 -1
- package/dist/chunk-BKYV2ALJ.cjs.map +0 -1
- package/dist/chunk-YVJ2XBFU.js.map +0 -1
- package/dist/hooks/ui/use-focus-trap.d.ts +0 -32
- package/dist/hooks/ui/use-focus-trap.d.ts.map +0 -1
- package/src/hooks/ui/use-focus-trap.ts +0 -110
- package/src/stories/MobileNavPanel.stories.tsx +0 -150
- /package/dist/{chunk-SOLI7V4I.js.map → chunk-5HOII3WB.js.map} +0 -0
- /package/dist/{chunk-NJSBYUJZ.js.map → chunk-E4J5KKHH.js.map} +0 -0
- /package/dist/{chunk-B2BERJ4M.js.map → chunk-IK5AR6QG.js.map} +0 -0
- /package/dist/{chunk-MHNBDSWH.js.map → chunk-J6M2G6TD.js.map} +0 -0
- /package/dist/{chunk-XBC5AES6.js.map → chunk-KZPVN7AZ.js.map} +0 -0
- /package/dist/{chunk-HXRULNWA.js.map → chunk-L6OPVKVR.js.map} +0 -0
- /package/dist/{chunk-FXZO4EKN.js.map → chunk-LQWYXIKB.js.map} +0 -0
- /package/dist/{chunk-DGIF3YDV.js.map → chunk-NO2KT42M.js.map} +0 -0
- /package/dist/{chunk-6AB36PRE.js.map → chunk-O5W26SCL.js.map} +0 -0
- /package/dist/{chunk-Z4RKJOFH.js.map → chunk-VUW4G6SU.js.map} +0 -0
- /package/dist/{chunk-TYCWPT33.js.map → chunk-ZF5O3KUQ.js.map} +0 -0
- /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
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|