@flamingo-stack/openframe-frontend-core 0.0.702 → 0.0.703-snapshot.20261006080901
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-CYYTD5ZK.cjs → chunk-262YQFOS.cjs} +47 -47
- package/dist/{chunk-SAA7B3DA.js → chunk-2G546OND.js} +500 -67
- package/dist/{chunk-6JPQDYYY.js → chunk-2JDVL7UV.js} +2614 -2001
- package/dist/{chunk-ASN65P3F.cjs → chunk-4DQMTBMK.cjs} +15 -15
- package/dist/{chunk-CZ34HXDW.js → chunk-4KGXLVCF.js} +686 -725
- package/dist/{chunk-TCZCMD5Q.js → chunk-5HBHO5TB.js} +1 -1
- package/dist/{chunk-446KS5BG.js → chunk-5QJDM6UV.js} +5 -5
- package/dist/{chunk-UJJWTJ6S.js → chunk-6WQIUQUC.js} +30 -16
- package/dist/{chunk-YR6JPJF4.js → chunk-6XIF2Z7I.js} +4 -4
- package/dist/{chunk-YGRHITPY.cjs → chunk-7JIL3WOH.cjs} +24 -24
- package/dist/{chunk-RS7HWAYX.js → chunk-7LFIKJX2.js} +3 -3
- package/dist/{chunk-4OGAXAEG.cjs → chunk-ABU3TGXA.cjs} +3 -3
- package/dist/{chunk-3WSFIV5G.cjs → chunk-AMYY3PNL.cjs} +69 -55
- package/dist/{chunk-BRXOGCH3.cjs → chunk-BIZNSF3U.cjs} +3 -3
- package/dist/{chunk-2A4XES7R.cjs → chunk-BM5MVGH3.cjs} +3 -3
- package/dist/{chunk-IIMICN7L.cjs → chunk-C3BWIU7R.cjs} +4 -44
- package/dist/{chunk-LZNJH23A.cjs → chunk-C7XGRLT5.cjs} +12 -12
- package/dist/{chunk-6YCXA7CN.js → chunk-CH5232HV.js} +20 -19
- package/dist/{chunk-UIBSSVON.cjs → chunk-D7BTSVMS.cjs} +2 -2
- package/dist/{chunk-BO5ZHDA4.js → chunk-ERCV6HHJ.js} +7 -7
- package/dist/{chunk-5V6O73EH.js → chunk-FLKJYMVB.js} +3 -3
- package/dist/{chunk-Z5HDCZAS.js → chunk-FXENTYE5.js} +15 -15
- package/dist/{chunk-HIXGAY2L.cjs → chunk-HFJW24JJ.cjs} +121 -121
- package/dist/{chunk-HPJ4XS2L.js → chunk-HJAYVKWQ.js} +24 -24
- package/dist/{chunk-PLK6GHN7.js → chunk-I7DFUQMN.js} +10 -10
- package/dist/{chunk-KJ5LWATV.js → chunk-IHU5ANUY.js} +7 -7
- package/dist/{chunk-AEQANLBW.cjs → chunk-IZGDG5X6.cjs} +505 -72
- package/dist/{chunk-2V4Z44G6.js → chunk-JEYRXT5L.js} +17 -17
- package/dist/{chunk-B3LZJ6J3.cjs → chunk-L3IC42IF.cjs} +32 -32
- package/dist/{chunk-RTGPIHVO.cjs → chunk-LDPEQGKT.cjs} +2243 -1630
- package/dist/{chunk-FD4DBTFO.cjs → chunk-LTSTC3N5.cjs} +1253 -1292
- package/dist/{chunk-OGLCD3UL.js → chunk-MFFCUVWG.js} +6 -6
- package/dist/{chunk-3L7KCOGO.cjs → chunk-MGGGBHYD.cjs} +30 -30
- package/dist/{chunk-QFW3RUZS.cjs → chunk-MI3JUJ75.cjs} +33 -33
- package/dist/{chunk-EMCQGFXD.cjs → chunk-MMOODW57.cjs} +28 -27
- package/dist/{chunk-Q76ZTHNV.js → chunk-MP2C3LJ4.js} +1 -1
- package/dist/{chunk-E3DH543A.cjs → chunk-N3TO7JV3.cjs} +98 -98
- package/dist/{chunk-2T6QWNXU.js → chunk-NDESEPW5.js} +99 -11
- package/dist/{chunk-ETFFEA3J.js → chunk-NRLJUBCS.js} +7 -7
- package/dist/{chunk-HUOLB7ZN.cjs → chunk-OHFS7VOU.cjs} +9 -9
- package/dist/{chunk-MWUBBBWZ.js → chunk-QVSFBSWV.js} +10 -10
- package/dist/{chunk-MDBHZBW7.cjs → chunk-RKV5B7EU.cjs} +92 -4
- package/dist/{chunk-3G76LRGL.cjs → chunk-SYXQLETR.cjs} +29 -29
- package/dist/{chunk-FHM7GD5B.cjs → chunk-UK62YTH6.cjs} +28 -14
- package/dist/{chunk-HUMWQMCU.js → chunk-UT4YHQZW.js} +3 -3
- package/dist/{chunk-NGJCWEYX.cjs → chunk-VHCACDKP.cjs} +46 -46
- package/dist/{chunk-5FFNNJM7.js → chunk-VR2ZLFCP.js} +1 -1
- package/dist/{chunk-IZKOQVXO.js → chunk-VTASHWER.js} +14 -14
- package/dist/{chunk-64O4L7WN.js → chunk-WUJNYGO2.js} +11 -51
- package/dist/{chunk-JOY6HVPV.js → chunk-XATTBO5X.js} +25 -11
- package/dist/{chunk-L7KZA7ZD.cjs → chunk-XIFOBQRQ.cjs} +6 -6
- package/dist/{chunk-CXDJUKC6.cjs → chunk-XJKZ4IKT.cjs} +4 -4
- package/dist/{chunk-XCIFHW4E.cjs → chunk-XOYH2YQE.cjs} +2 -2
- package/dist/{chunk-HYFLTFFB.js → chunk-ZQFZSFQI.js} +2 -2
- package/dist/components/case-studies/index.cjs +24 -24
- package/dist/components/case-studies/index.js +10 -10
- package/dist/components/chat/chat-archive-page.d.ts +3 -3
- package/dist/components/chat/chat-archive-page.d.ts.map +1 -1
- package/dist/components/chat/chat-nav-sidebar.d.ts +4 -0
- package/dist/components/chat/chat-nav-sidebar.d.ts.map +1 -1
- package/dist/components/chat/chat-panel-header.d.ts +3 -2
- package/dist/components/chat/chat-panel-header.d.ts.map +1 -1
- package/dist/components/chat/embeddable-chat.d.ts +36 -2
- package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +14 -6
- package/dist/components/chat/index.d.ts +4 -1
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +13 -5
- package/dist/components/chat/mingo-archive-list.d.ts +17 -0
- package/dist/components/chat/mingo-archive-list.d.ts.map +1 -0
- package/dist/components/chat/mingo-chat-header.d.ts +31 -0
- package/dist/components/chat/mingo-chat-header.d.ts.map +1 -0
- package/dist/components/chat/mingo-chat-rail.d.ts +54 -0
- package/dist/components/chat/mingo-chat-rail.d.ts.map +1 -0
- package/dist/components/chat/model-display.d.ts.map +1 -1
- package/dist/components/contact/contact-form.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +11 -11
- package/dist/components/contact/index.js +10 -10
- package/dist/components/content-loading-container.d.ts +1 -1
- package/dist/components/content-loading-container.d.ts.map +1 -1
- package/dist/components/docs/index.cjs +14 -14
- package/dist/components/docs/index.js +13 -13
- package/dist/components/docs/skeletons.d.ts.map +1 -1
- package/dist/components/embeds/index.cjs +11 -11
- package/dist/components/embeds/index.js +10 -10
- package/dist/components/faq/index.cjs +12 -12
- package/dist/components/faq/index.js +11 -11
- package/dist/components/features/figma-prototype-viewer.d.ts.map +1 -1
- package/dist/components/features/index.cjs +10 -10
- package/dist/components/features/index.js +9 -9
- package/dist/components/features/media-gallery-manager.d.ts.map +1 -1
- package/dist/components/features/parallax-image-showcase.d.ts.map +1 -1
- package/dist/components/features/policy-configuration-panel.d.ts.map +1 -1
- package/dist/components/features/section-selector.d.ts.map +1 -1
- package/dist/components/help-center-pages/index.cjs +119 -119
- package/dist/components/help-center-pages/index.js +39 -39
- package/dist/components/help-center-pages/trust-center-sections.d.ts +2 -2
- package/dist/components/help-center-pages/trust-center-sections.d.ts.map +1 -1
- package/dist/components/index.cjs +94 -86
- package/dist/components/index.js +32 -24
- package/dist/components/layout/article-detail-layout.d.ts +1 -1
- package/dist/components/layout/article-detail-layout.d.ts.map +1 -1
- package/dist/components/layout/page-container.d.ts.map +1 -1
- package/dist/components/layout/title-block.d.ts +2 -2
- package/dist/components/layout/title-block.d.ts.map +1 -1
- package/dist/components/loading/page-layout-skeleton.d.ts.map +1 -1
- package/dist/components/loading/unified-skeleton.d.ts.map +1 -1
- package/dist/components/meeting-scheduler/booking-form.d.ts +4 -4
- package/dist/components/meeting-scheduler/booking-form.d.ts.map +1 -1
- package/dist/components/meeting-scheduler/context-panel.d.ts.map +1 -1
- package/dist/components/meeting-scheduler/index.cjs +127 -127
- package/dist/components/meeting-scheduler/index.d.ts +2 -2
- package/dist/components/meeting-scheduler/index.d.ts.map +1 -1
- package/dist/components/meeting-scheduler/index.js +50 -50
- package/dist/components/navigation/app-header.d.ts +4 -0
- package/dist/components/navigation/app-header.d.ts.map +1 -1
- package/dist/components/navigation/app-layout-drawer.d.ts.map +1 -1
- package/dist/components/navigation/app-layout-side-panel.d.ts +142 -0
- package/dist/components/navigation/app-layout-side-panel.d.ts.map +1 -0
- package/dist/components/navigation/app-layout.d.ts +11 -1
- package/dist/components/navigation/app-layout.d.ts.map +1 -1
- package/dist/components/navigation/header-mingo-button.d.ts +4 -1
- package/dist/components/navigation/header-mingo-button.d.ts.map +1 -1
- package/dist/components/navigation/index.cjs +13 -13
- package/dist/components/navigation/index.d.ts +1 -0
- package/dist/components/navigation/index.d.ts.map +1 -1
- package/dist/components/navigation/index.js +12 -12
- package/dist/components/navigation/navigation-sidebar.d.ts +8 -1
- package/dist/components/navigation/navigation-sidebar.d.ts.map +1 -1
- package/dist/components/navigation/sliding-sidebar.d.ts.map +1 -1
- package/dist/components/onboarding-guides/index.cjs +14 -14
- package/dist/components/onboarding-guides/index.js +13 -13
- package/dist/components/persistent-filter-controls.d.ts.map +1 -1
- package/dist/components/related-content/index.cjs +7 -7
- package/dist/components/related-content/index.js +6 -6
- package/dist/components/shared/detail-page-skeleton.d.ts.map +1 -1
- package/dist/components/tickets/help-center-create-form.d.ts +4 -4
- package/dist/components/tickets/index.cjs +15 -15
- package/dist/components/tickets/index.js +14 -14
- package/dist/components/ui/announcement-bar-view.d.ts.map +1 -1
- package/dist/components/ui/breadcrumb.d.ts.map +1 -1
- package/dist/components/ui/checkbox-block.d.ts.map +1 -1
- package/dist/components/ui/dashboard-info-card.d.ts +1 -1
- package/dist/components/ui/dashboard-info-card.d.ts.map +1 -1
- package/dist/components/ui/data-table/data-table-body.d.ts +2 -2
- package/dist/components/ui/data-table/data-table-skeleton.d.ts +2 -2
- package/dist/components/ui/data-table/data-table-skeleton.d.ts.map +1 -1
- package/dist/components/ui/data-table/utils.d.ts +2 -2
- package/dist/components/ui/dialog.d.ts.map +1 -1
- package/dist/components/ui/drawer.d.ts +7 -3
- package/dist/components/ui/drawer.d.ts.map +1 -1
- package/dist/components/ui/entity-image.d.ts +2 -2
- package/dist/components/ui/file-manager/index.cjs +36 -36
- package/dist/components/ui/file-manager/index.js +3 -3
- package/dist/components/ui/filter-modal.d.ts.map +1 -1
- package/dist/components/ui/index.cjs +18 -10
- package/dist/components/ui/index.js +17 -9
- package/dist/components/ui/info-card-row.d.ts.map +1 -1
- package/dist/components/ui/menubar.d.ts +1 -1
- package/dist/components/ui/menubar.d.ts.map +1 -1
- package/dist/components/ui/modal-v2.d.ts.map +1 -1
- package/dist/components/ui/modal.d.ts.map +1 -1
- package/dist/components/ui/no-data/no-data-actions.d.ts.map +1 -1
- package/dist/components/ui/no-data/no-data-styles.d.ts +1 -1
- package/dist/components/ui/no-data/no-data-styles.d.ts.map +1 -1
- package/dist/components/ui/page-actions.d.ts.map +1 -1
- package/dist/components/ui/panel-resize-handle.d.ts +25 -0
- package/dist/components/ui/panel-resize-handle.d.ts.map +1 -0
- package/dist/components/ui/sheet.d.ts.map +1 -1
- package/dist/components/ui/table/table-skeleton.d.ts +1 -1
- package/dist/components/ui/table/table-skeleton.d.ts.map +1 -1
- package/dist/hooks/index.cjs +23 -9
- package/dist/hooks/index.js +24 -10
- package/dist/hooks/ui/index.d.ts +1 -0
- package/dist/hooks/ui/index.d.ts.map +1 -1
- package/dist/hooks/ui/use-content-breakpoint.d.ts +32 -0
- package/dist/hooks/ui/use-content-breakpoint.d.ts.map +1 -0
- package/dist/hooks/ui/use-isomorphic-layout-effect.d.ts +2 -2
- package/dist/hooks/ui/use-isomorphic-layout-effect.d.ts.map +1 -1
- package/dist/hooks/ui/use-media-query.d.ts.map +1 -1
- package/dist/hooks/ui/use-resizable-panel-size.d.ts +46 -0
- package/dist/hooks/ui/use-resizable-panel-size.d.ts.map +1 -0
- package/dist/index.cjs +39 -17
- package/dist/index.js +41 -19
- package/dist/styles/layout-steps.d.ts +30 -0
- package/dist/styles/layout-steps.d.ts.map +1 -0
- package/dist/tailwind.config.cjs +36 -11
- package/dist/tailwind.config.js +36 -11
- package/eslint-config/README.md +9 -0
- package/eslint-config/base.js +17 -1
- package/eslint-config/layout-variants.js +57 -0
- package/package.json +3 -1
- package/src/ODS_TOKEN_RULES.md +40 -0
- package/src/components/chat/chat-archive-page.tsx +9 -7
- package/src/components/chat/chat-nav-sidebar.tsx +10 -4
- package/src/components/chat/chat-panel-header.tsx +8 -5
- package/src/components/chat/embeddable-chat.tsx +686 -494
- package/src/components/chat/index.ts +4 -0
- package/src/components/chat/mingo-archive-list.tsx +108 -0
- package/src/components/chat/mingo-chat-header.tsx +104 -0
- package/src/components/chat/mingo-chat-rail.tsx +361 -0
- package/src/components/chat/model-display.tsx +8 -1
- package/src/components/contact/contact-form.tsx +11 -9
- package/src/components/content-loading-container.tsx +4 -2
- package/src/components/docs/doc-viewer.tsx +7 -7
- package/src/components/docs/skeletons.tsx +7 -4
- package/src/components/dynamic-skeleton.tsx +1 -1
- package/src/components/embeds/claude-embed.tsx +1 -1
- package/src/components/embeds/embed-viewer-frame.tsx +4 -4
- package/src/components/embeds/figma-embed.tsx +2 -2
- package/src/components/embeds/google-sheets-viewer.tsx +1 -1
- package/src/components/embeds/pdf-viewer.tsx +3 -3
- package/src/components/empty-state.tsx +6 -6
- package/src/components/faq/faq-section.tsx +2 -2
- package/src/components/faq-accordion.tsx +1 -1
- package/src/components/features/board/board-column-header.tsx +1 -1
- package/src/components/features/command-box.tsx +3 -3
- package/src/components/features/entity-video-section.tsx +2 -2
- package/src/components/features/figma-prototype-viewer.tsx +4 -1
- package/src/components/features/media-gallery-manager.tsx +3 -1
- package/src/components/features/parallax-image-showcase.tsx +9 -6
- package/src/components/features/policy-configuration-panel.tsx +9 -7
- package/src/components/features/provider-button.tsx +1 -1
- package/src/components/features/section-selector.tsx +11 -6
- package/src/components/features/select-button.tsx +4 -4
- package/src/components/features/seo-editor-preview.tsx +2 -2
- package/src/components/features/video-ratio-tabs.tsx +3 -3
- package/src/components/features/waitlist-form.tsx +1 -1
- package/src/components/help-center-pages/trust-center-page.tsx +1 -1
- package/src/components/help-center-pages/trust-center-sections.tsx +14 -14
- package/src/components/icon-display.tsx +1 -1
- package/src/components/layout/article-detail-layout.tsx +4 -2
- package/src/components/layout/list-page-layout.tsx +2 -2
- package/src/components/layout/page-container.tsx +26 -14
- package/src/components/layout/page-heading.tsx +1 -1
- package/src/components/layout/page-layout.tsx +1 -1
- package/src/components/layout/title-block.tsx +13 -13
- package/src/components/loading/card-skeleton.tsx +11 -11
- package/src/components/loading/category-vendor-selector-skeleton.tsx +1 -1
- package/src/components/loading/content-skeleton.tsx +6 -6
- package/src/components/loading/device-card-skeleton.tsx +1 -1
- package/src/components/loading/margin-report-skeleton.tsx +7 -7
- package/src/components/loading/msp-profile-form-skeleton.tsx +3 -3
- package/src/components/loading/organization-card-skeleton.tsx +2 -2
- package/src/components/loading/page-layout-skeleton.tsx +80 -68
- package/src/components/loading/unified-skeleton.tsx +16 -6
- package/src/components/media-carousel.tsx +3 -3
- package/src/components/meeting-scheduler/booking-form.tsx +9 -8
- package/src/components/meeting-scheduler/context-panel.tsx +8 -6
- package/src/components/meeting-scheduler/directory.tsx +4 -4
- package/src/components/meeting-scheduler/index.tsx +15 -15
- package/src/components/meeting-scheduler/slot-picker.tsx +12 -12
- package/src/components/navigation/app-header.tsx +6 -0
- package/src/components/navigation/app-layout-drawer.tsx +14 -237
- package/src/components/navigation/app-layout-side-panel.tsx +594 -0
- package/src/components/navigation/app-layout.tsx +69 -4
- package/src/components/navigation/header-mingo-button.tsx +13 -3
- package/src/components/navigation/index.ts +5 -0
- package/src/components/navigation/navigation-sidebar.tsx +17 -3
- package/src/components/navigation/sliding-sidebar.tsx +2 -1
- package/src/components/pagination.tsx +3 -3
- package/src/components/persistent-filter-controls.tsx +6 -2
- package/src/components/platform/ScriptArguments.tsx +1 -1
- package/src/components/platform/ScriptInfoSection.tsx +4 -4
- package/src/components/shared/article-author-byline.tsx +1 -1
- package/src/components/shared/delivery/delivery-lists.tsx +2 -2
- package/src/components/shared/delivery/delivery-row.tsx +7 -7
- package/src/components/shared/delivery/delivery-table.tsx +3 -3
- package/src/components/shared/detail-page-skeleton.tsx +11 -5
- package/src/components/shared/dev-section/dev-card-row.tsx +6 -6
- package/src/components/shared/dev-section/dev-section-view.tsx +1 -1
- package/src/components/shared/product-release/product-release-card-skeleton.tsx +8 -8
- package/src/components/shared/product-release/product-release-card.tsx +9 -9
- package/src/components/shared/product-release/product-releases-view.tsx +1 -1
- package/src/components/shared/product-release/release-detail-page.tsx +5 -5
- package/src/components/shared/roadmap/roadmap-grid-skeleton.tsx +1 -1
- package/src/components/shared/roadmap/roadmap-grid.tsx +3 -3
- package/src/components/tickets/help-center-card.tsx +1 -1
- package/src/components/tickets/help-center-create-form.tsx +12 -12
- package/src/components/ui/announcement-bar-view.tsx +2 -10
- package/src/components/ui/benefit-card.tsx +18 -18
- package/src/components/ui/brand-association-grid.tsx +4 -4
- package/src/components/ui/breadcrumb.tsx +4 -1
- package/src/components/ui/card.tsx +1 -1
- package/src/components/ui/checkbox-block.tsx +9 -7
- package/src/components/ui/content-loader.tsx +3 -3
- package/src/components/ui/cursor-pagination.tsx +3 -3
- package/src/components/ui/dashboard-info-card.tsx +13 -9
- package/src/components/ui/data-attribution.tsx +2 -2
- package/src/components/ui/data-table/data-table-body.tsx +2 -2
- package/src/components/ui/data-table/data-table-header.tsx +15 -15
- package/src/components/ui/data-table/data-table-skeleton.tsx +6 -6
- package/src/components/ui/data-table/utils.ts +5 -5
- package/src/components/ui/dialog.tsx +4 -1
- package/src/components/ui/drawer.tsx +43 -189
- package/src/components/ui/duration-input.tsx +1 -1
- package/src/components/ui/entity-image.tsx +3 -3
- package/src/components/ui/feature-card.tsx +10 -10
- package/src/components/ui/file-manager/file-manager-action-bar.tsx +1 -1
- package/src/components/ui/file-manager/file-manager.tsx +1 -1
- package/src/components/ui/filter-modal.tsx +8 -1
- package/src/components/ui/highlight-card.tsx +7 -7
- package/src/components/ui/info-card-row.tsx +10 -6
- package/src/components/ui/modal-v2.tsx +7 -2
- package/src/components/ui/modal.tsx +3 -2
- package/src/components/ui/no-data/no-data-actions.tsx +10 -4
- package/src/components/ui/no-data/no-data-styles.ts +1 -1
- package/src/components/ui/no-data/no-data.tsx +1 -1
- package/src/components/ui/organization-card.tsx +1 -1
- package/src/components/ui/page-actions.tsx +34 -5
- package/src/components/ui/panel-resize-handle.tsx +184 -0
- package/src/components/ui/progress-bar.tsx +2 -2
- package/src/components/ui/query-report-table/query-report-table.tsx +1 -1
- package/src/components/ui/search-input.tsx +2 -2
- package/src/components/ui/service-card.tsx +2 -2
- package/src/components/ui/sheet.tsx +2 -1
- package/src/components/ui/skeleton.tsx +3 -3
- package/src/components/ui/stacked-rows-panel.tsx +4 -4
- package/src/components/ui/tab-navigation.tsx +2 -2
- package/src/components/ui/tab-selector.tsx +2 -2
- package/src/components/ui/table/table-header.tsx +3 -3
- package/src/components/ui/table/table-skeleton.tsx +3 -3
- package/src/components/ui/table/utils.ts +5 -5
- package/src/components/ui/tag-search-input.tsx +3 -3
- package/src/components/ui/tags-manager.tsx +5 -5
- package/src/components/ui/test-run.tsx +2 -2
- package/src/components/ui/ticket-info-section.tsx +1 -1
- package/src/components/ui/ticket-notes-section.tsx +1 -1
- package/src/components/ui/ticket-status-config-row.tsx +15 -15
- package/src/hooks/ui/index.ts +1 -0
- package/src/hooks/ui/use-content-breakpoint.ts +58 -0
- package/src/hooks/ui/use-media-query.ts +3 -2
- package/src/hooks/ui/use-resizable-panel-size.ts +114 -0
- package/src/styles/index.css +2 -0
- package/src/styles/layout-steps.ts +14 -0
- package/src/styles/ods-colors.css +15 -0
- package/src/styles/ods-content-area-tokens.css +27 -0
- package/src/styles/ods-content-area.css +31 -0
- package/src/styles/ods-effects.css +4 -2
- package/src/styles/ods-responsive-tokens.css +14 -4
- package/tailwind.config.ts +42 -7
|
@@ -172,7 +172,7 @@ type StashedDetails = {
|
|
|
172
172
|
/** Context-panel geometry — ONE definition for the loaded card and the
|
|
173
173
|
* loading skeleton, which is what keeps the two footprint-identical. */
|
|
174
174
|
const CONTEXT_PANEL_CLASS =
|
|
175
|
-
'p-[var(--spacing-system-l)] shrink-0 lg:w-[280px] border-b lg:border-b-0 lg:border-r border-ods-border lg:min-h-0 lg:overflow-y-auto';
|
|
175
|
+
'p-[var(--spacing-system-l)] shrink-0 content-lg:w-[280px] border-b content-lg:border-b-0 content-lg:border-r border-ods-border content-lg:min-h-0 content-lg:overflow-y-auto';
|
|
176
176
|
|
|
177
177
|
/**
|
|
178
178
|
* Action panel: the elastic half, scrolling inside the fixed card.
|
|
@@ -182,7 +182,7 @@ const CONTEXT_PANEL_CLASS =
|
|
|
182
182
|
* inset that divider from the card's edges instead of letting it run the full
|
|
183
183
|
* width the way every other rule in this card does.
|
|
184
184
|
*/
|
|
185
|
-
const ACTION_PANEL_CLASS = 'flex-1 min-w-0 flex flex-col md:min-h-0 lg:p-[var(--spacing-system-l)]';
|
|
185
|
+
const ACTION_PANEL_CLASS = 'flex-1 min-w-0 flex flex-col content-md:min-h-0 content-lg:p-[var(--spacing-system-l)]';
|
|
186
186
|
|
|
187
187
|
/**
|
|
188
188
|
* The action panel's inset for the steps that are ONE block — details,
|
|
@@ -195,12 +195,12 @@ const ACTION_PANEL_CLASS = 'flex-1 min-w-0 flex flex-col md:min-h-0 lg:p-[var(--
|
|
|
195
195
|
* sections has to bring the same inset with it — without it the form ran edge
|
|
196
196
|
* to edge on tablet, its inputs touching the card's border.
|
|
197
197
|
*
|
|
198
|
-
* `md:min-h-0 md:overflow-y-auto` comes with it: from `md` up the card states
|
|
198
|
+
* `content-md:min-h-0 content-md:overflow-y-auto` comes with it: from `md` up the card states
|
|
199
199
|
* a height, and these steps have no inner scroller of their own the way the
|
|
200
200
|
* times column does, so this is where a long form gets to scroll instead of
|
|
201
201
|
* being cut off at the card's edge.
|
|
202
202
|
*/
|
|
203
|
-
const PANEL_STEP_CLASS = 'p-[var(--spacing-system-l)] md:min-h-0 md:overflow-y-auto lg:p-0';
|
|
203
|
+
const PANEL_STEP_CLASS = 'p-[var(--spacing-system-l)] content-md:min-h-0 content-md:overflow-y-auto content-lg:p-0';
|
|
204
204
|
/** A step's panel: the inset above on a column that fills the action side. */
|
|
205
205
|
const STEP_PANEL_CLASS = cn('flex flex-1 flex-col', PANEL_STEP_CLASS);
|
|
206
206
|
|
|
@@ -248,7 +248,7 @@ const STEP_PANEL_CLASS = cn('flex flex-1 flex-col', PANEL_STEP_CLASS);
|
|
|
248
248
|
* video take the leftover space at 16:9; a stand-in that just stacks fixed
|
|
249
249
|
* blocks will overflow the shorter of the two.
|
|
250
250
|
*/
|
|
251
|
-
export const MEETING_SCHEDULER_H = 'md:h-[34.375rem] lg:h-[23.75rem]';
|
|
251
|
+
export const MEETING_SCHEDULER_H = 'content-md:h-[34.375rem] content-lg:h-[23.75rem]';
|
|
252
252
|
|
|
253
253
|
/**
|
|
254
254
|
* The box for `flow="details-first"`'s CALENDAR and confirmation stages (and its
|
|
@@ -265,7 +265,7 @@ export const MEETING_SCHEDULER_H = 'md:h-[34.375rem] lg:h-[23.75rem]';
|
|
|
265
265
|
*
|
|
266
266
|
* Hosts read it through `SCHEDULER_FLOW_PRESETS[flow].height`, never directly.
|
|
267
267
|
*/
|
|
268
|
-
export const MEETING_SCHEDULER_DETAILS_FIRST_H = 'h-[50.75rem] md:h-[39.875rem]';
|
|
268
|
+
export const MEETING_SCHEDULER_DETAILS_FIRST_H = 'h-[50.75rem] content-md:h-[39.875rem]';
|
|
269
269
|
|
|
270
270
|
export type SchedulerFlow = 'slot-first' | 'details-first';
|
|
271
271
|
|
|
@@ -314,24 +314,24 @@ export const SCHEDULER_FLOW_PRESETS: Record<
|
|
|
314
314
|
* it on a calendar sized to fit (no scroll) and a times list that scrolls on
|
|
315
315
|
* its own.
|
|
316
316
|
*
|
|
317
|
-
* `md:flex md:flex-col` is what makes that possible at all. A stated height on
|
|
317
|
+
* `content-md:flex content-md:flex-col` is what makes that possible at all. A stated height on
|
|
318
318
|
* a plain block only clips (this card is `overflow-hidden` for its corners) —
|
|
319
319
|
* it is the flex column plus the inner wrapper's `min-h-0` that lets the
|
|
320
320
|
* content shrink into the height instead of being cut off by it.
|
|
321
321
|
*/
|
|
322
322
|
const CARD_CLASS = cn(
|
|
323
323
|
'overflow-hidden rounded-md border border-ods-border bg-ods-card',
|
|
324
|
-
'md:flex md:flex-col',
|
|
324
|
+
'content-md:flex content-md:flex-col',
|
|
325
325
|
// The height is NOT baked in any more: it is chosen per flow at each render
|
|
326
326
|
// site (`cardClass`), because details-first needs a taller box and both
|
|
327
327
|
// constants are module scope.
|
|
328
328
|
);
|
|
329
329
|
|
|
330
|
-
/** Context strip over action panel, side by side from `lg`. `md:flex-1
|
|
331
|
-
* md:min-h-0` is the pair that makes it exactly as tall as the card states —
|
|
330
|
+
/** Context strip over action panel, side by side from `lg`. `content-md:flex-1
|
|
331
|
+
* content-md:min-h-0` is the pair that makes it exactly as tall as the card states —
|
|
332
332
|
* grow into a stated height that content does not reach, shrink into one it
|
|
333
333
|
* overruns — so neither a short stage nor a long one changes the box. */
|
|
334
|
-
const CARD_INNER_CLASS = 'flex flex-col md:min-h-0 md:flex-1 lg:h-full lg:flex-row';
|
|
334
|
+
const CARD_INNER_CLASS = 'flex flex-col content-md:min-h-0 content-md:flex-1 content-lg:h-full content-lg:flex-row';
|
|
335
335
|
|
|
336
336
|
/** The two-line stages (load failure, "booked on HubSpot") — the SAME box as
|
|
337
337
|
* the booking card, because these are stages of one widget in one slot and a
|
|
@@ -340,7 +340,7 @@ const CARD_INNER_CLASS = 'flex flex-col md:min-h-0 md:flex-1 lg:h-full lg:flex-r
|
|
|
340
340
|
* outgrows it. */
|
|
341
341
|
const CARD_DEGRADED_CLASS = cn(
|
|
342
342
|
'flex flex-col items-start gap-[var(--spacing-system-m)] rounded-md border border-ods-border bg-ods-card p-[var(--spacing-system-lf)]',
|
|
343
|
-
'md:justify-center md:overflow-y-auto',
|
|
343
|
+
'content-md:justify-center content-md:overflow-y-auto',
|
|
344
344
|
);
|
|
345
345
|
|
|
346
346
|
/**
|
|
@@ -723,7 +723,7 @@ export function HubSpotMeetingScheduler({
|
|
|
723
723
|
const boxed = !formOnly;
|
|
724
724
|
const cardClass = cn(
|
|
725
725
|
CARD_CLASS,
|
|
726
|
-
boxed ? preset.height : 'md:h-auto',
|
|
726
|
+
boxed ? preset.height : 'content-md:h-auto',
|
|
727
727
|
detailsFirst && 'flex flex-col',
|
|
728
728
|
formOnly && 'bg-ods-bg',
|
|
729
729
|
className,
|
|
@@ -880,11 +880,11 @@ export function HubSpotMeetingScheduler({
|
|
|
880
880
|
/>
|
|
881
881
|
</div>
|
|
882
882
|
) : (
|
|
883
|
-
<div className="flex flex-col gap-[var(--spacing-system-m)] md:min-h-0 md:flex-1">
|
|
883
|
+
<div className="flex flex-col gap-[var(--spacing-system-m)] content-md:min-h-0 content-md:flex-1">
|
|
884
884
|
{bookingError === 'SLOT_TAKEN' && (
|
|
885
885
|
<Alert
|
|
886
886
|
variant="warning"
|
|
887
|
-
className="mx-[var(--spacing-system-l)] mt-[var(--spacing-system-l)] w-auto lg:m-0 lg:w-full"
|
|
887
|
+
className="mx-[var(--spacing-system-l)] mt-[var(--spacing-system-l)] w-auto content-lg:m-0 content-lg:w-full"
|
|
888
888
|
>
|
|
889
889
|
<AlertDescription>
|
|
890
890
|
That time was just taken — pick another slot below. (If you already submitted, check your email for
|
|
@@ -110,7 +110,7 @@ function timeLabelInZone(ms: number, timeZone: string): string {
|
|
|
110
110
|
* 48px targets and the COLUMN widens to house it, rather than the buttons
|
|
111
111
|
* shrinking to fit a narrower column.
|
|
112
112
|
*/
|
|
113
|
-
const CALENDAR_W = 'md:w-[19rem] md:shrink-0 lg:w-[16rem]';
|
|
113
|
+
const CALENDAR_W = 'content-md:w-[19rem] content-md:shrink-0 content-lg:w-[16rem]';
|
|
114
114
|
|
|
115
115
|
/** Column heading ("Select Date & Time" / the chosen day) — body-weight
|
|
116
116
|
* primary, 4px above its content, mirrored by both skeletons. */
|
|
@@ -150,7 +150,7 @@ const CHIP_GRID_CLASS = cn(
|
|
|
150
150
|
// 700px card into a 1500px one and buries whatever the page puts below
|
|
151
151
|
// it. This is the one nested scroller worth having: a small, obviously
|
|
152
152
|
// bounded list with a visible edge, exactly as drawn.
|
|
153
|
-
'max-h-[9.75rem] md:max-h-none',
|
|
153
|
+
'max-h-[9.75rem] content-md:max-h-none',
|
|
154
154
|
// No height of its own, and none borrowed from the calendar either: the card
|
|
155
155
|
// states one height, the panel and this row pass it down, and what is left
|
|
156
156
|
// after the column heading IS the scroll box. A long day scrolls inside it.
|
|
@@ -188,24 +188,24 @@ const CHIP_CLASS = 'w-full px-[var(--spacing-system-xxs)]';
|
|
|
188
188
|
* is worse than the thing it saves.
|
|
189
189
|
*/
|
|
190
190
|
const SLOT_ROW_CLASS = cn(
|
|
191
|
-
'flex min-w-0 flex-col md:flex-row',
|
|
191
|
+
'flex min-w-0 flex-col content-md:flex-row',
|
|
192
192
|
// Default `stretch` at every width, deliberately. Stacked, that is what
|
|
193
193
|
// makes each section span the card — an `items-start` here (it was here, to
|
|
194
194
|
// stop a column growing taller than its month) collapses both sections to
|
|
195
195
|
// their intrinsic WIDTH instead, which on a phone left the calendar and the
|
|
196
196
|
// chips huddled against the left edge with half the card empty. The
|
|
197
197
|
// calendar no longer needs protecting: its height is fixed, not derived.
|
|
198
|
-
'md:min-h-0 md:flex-1 lg:gap-[var(--spacing-system-lf)]',
|
|
198
|
+
'content-md:min-h-0 content-md:flex-1 content-lg:gap-[var(--spacing-system-lf)]',
|
|
199
199
|
);
|
|
200
200
|
|
|
201
201
|
/** Shared by both columns: self-padded until the panel takes over at `lg`.
|
|
202
202
|
* Widths are per-column — the calendar states one (`CALENDAR_W`) and the
|
|
203
203
|
* times take what is left, rather than a 50/50 split that would size the
|
|
204
204
|
* month by the card's width. */
|
|
205
|
-
const COLUMN_BOX = 'p-[var(--spacing-system-l)] lg:p-0';
|
|
205
|
+
const COLUMN_BOX = 'p-[var(--spacing-system-l)] content-lg:p-0';
|
|
206
206
|
|
|
207
207
|
/** The rule between the two, wherever the layout puts it. */
|
|
208
|
-
const COLUMN_DIVIDER = 'border-b border-ods-border md:border-b-0 md:border-r lg:border-0';
|
|
208
|
+
const COLUMN_DIVIDER = 'border-b border-ods-border content-md:border-b-0 content-md:border-r content-lg:border-0';
|
|
209
209
|
|
|
210
210
|
/** Deterministic month caption for a given offset (fixed en-US locale so
|
|
211
211
|
* the SSR and client first-paint markup agree byte-for-byte). */
|
|
@@ -288,7 +288,7 @@ function DayCalendar({ monthOffset, onMonthOffsetChange, selectedDay, onSelectDa
|
|
|
288
288
|
export function SlotPickerSkeleton({ monthOffset = 0 }: { monthOffset?: number }) {
|
|
289
289
|
return (
|
|
290
290
|
<div className={SLOT_ROW_CLASS}>
|
|
291
|
-
<div className={cn(COLUMN_STACK, COLUMN_BOX, COLUMN_DIVIDER, CALENDAR_W, 'md:overflow-y-auto')}>
|
|
291
|
+
<div className={cn(COLUMN_STACK, COLUMN_BOX, COLUMN_DIVIDER, CALENDAR_W, 'content-md:overflow-y-auto')}>
|
|
292
292
|
{/* Needs no data — the heading renders REAL in every loading state. */}
|
|
293
293
|
<p className={COLUMN_HEADING_CLASS}>{SLOT_PICKER_HEADING}</p>
|
|
294
294
|
<DayCalendar
|
|
@@ -299,7 +299,7 @@ export function SlotPickerSkeleton({ monthOffset = 0 }: { monthOffset?: number }
|
|
|
299
299
|
slotsByDay={null}
|
|
300
300
|
/>
|
|
301
301
|
</div>
|
|
302
|
-
<div className={cn(COLUMN_STACK, COLUMN_BOX, 'min-w-0 md:flex-1')}>
|
|
302
|
+
<div className={cn(COLUMN_STACK, COLUMN_BOX, 'min-w-0 content-md:flex-1')}>
|
|
303
303
|
<TimeChipsSkeleton />
|
|
304
304
|
</div>
|
|
305
305
|
</div>
|
|
@@ -320,8 +320,8 @@ export function TimeChipsSkeleton() {
|
|
|
320
320
|
grid's track count follows the card width, so no fixed number
|
|
321
321
|
lands flush and a partial last row is the honest shape anyway. */}
|
|
322
322
|
{Array.from({ length: 16 }, (_, i) => (
|
|
323
|
-
// h-11 md:h-12 — the loaded chip's own default-size height.
|
|
324
|
-
<Skeleton key={i} className={cn(CHIP_CLASS, 'h-11 md:h-12')} />
|
|
323
|
+
// h-11 content-md:h-12 — the loaded chip's own default-size height.
|
|
324
|
+
<Skeleton key={i} className={cn(CHIP_CLASS, 'h-11 content-md:h-12')} />
|
|
325
325
|
))}
|
|
326
326
|
</div>
|
|
327
327
|
</>
|
|
@@ -367,7 +367,7 @@ export function SlotPicker({
|
|
|
367
367
|
|
|
368
368
|
return (
|
|
369
369
|
<div className={SLOT_ROW_CLASS}>
|
|
370
|
-
<div className={cn(COLUMN_STACK, COLUMN_BOX, COLUMN_DIVIDER, CALENDAR_W, 'md:overflow-y-auto')}>
|
|
370
|
+
<div className={cn(COLUMN_STACK, COLUMN_BOX, COLUMN_DIVIDER, CALENDAR_W, 'content-md:overflow-y-auto')}>
|
|
371
371
|
<p className={COLUMN_HEADING_CLASS}>{SLOT_PICKER_HEADING}</p>
|
|
372
372
|
{/* Stays mounted through a month load — `slotsByDay: null` is what
|
|
373
373
|
"nothing selectable yet" looks like, not an unmount. */}
|
|
@@ -379,7 +379,7 @@ export function SlotPicker({
|
|
|
379
379
|
slotsByDay={isLoading ? null : slotsByDay}
|
|
380
380
|
/>
|
|
381
381
|
</div>
|
|
382
|
-
<div className={cn(COLUMN_STACK, COLUMN_BOX, 'min-w-0 md:flex-1')}>
|
|
382
|
+
<div className={cn(COLUMN_STACK, COLUMN_BOX, 'min-w-0 content-md:flex-1')}>
|
|
383
383
|
{isLoading ? (
|
|
384
384
|
<TimeChipsSkeleton />
|
|
385
385
|
) : selectedDay ? (
|
|
@@ -47,6 +47,10 @@ export interface AppHeaderProps {
|
|
|
47
47
|
onMingoAI?: () => void;
|
|
48
48
|
/** Whether the Mingo drawer is currently open (visually pressed state). */
|
|
49
49
|
isMingoAIActive?: boolean;
|
|
50
|
+
/** Swap the Mingo logo for a close "X" while active, beside the wordmark
|
|
51
|
+
* too (icon-only mode always does). For a panel the button both opens and
|
|
52
|
+
* closes. */
|
|
53
|
+
mingoAICloseWhenActive?: boolean;
|
|
50
54
|
// User block
|
|
51
55
|
showUser?: boolean;
|
|
52
56
|
userName?: string;
|
|
@@ -131,6 +135,7 @@ export const AppHeader = React.memo(function AppHeaderImpl({
|
|
|
131
135
|
showMingoAI = false,
|
|
132
136
|
onMingoAI,
|
|
133
137
|
isMingoAIActive = false,
|
|
138
|
+
mingoAICloseWhenActive = false,
|
|
134
139
|
showUser,
|
|
135
140
|
userName,
|
|
136
141
|
userEmail,
|
|
@@ -327,6 +332,7 @@ export const AppHeader = React.memo(function AppHeaderImpl({
|
|
|
327
332
|
<HeaderMingoButton
|
|
328
333
|
onClick={onMingoAI}
|
|
329
334
|
isActive={isMingoAIActive}
|
|
335
|
+
closeWhenActive={mingoAICloseWhenActive}
|
|
330
336
|
iconOnly={!isMdUp}
|
|
331
337
|
disabled={disabled || !onMingoAI}
|
|
332
338
|
className={cn(cellDivider, dimmedClass)}
|
|
@@ -6,9 +6,6 @@ import {
|
|
|
6
6
|
type CSSProperties,
|
|
7
7
|
type ComponentPropsWithoutRef,
|
|
8
8
|
type ComponentRef,
|
|
9
|
-
type KeyboardEvent,
|
|
10
|
-
type PointerEvent,
|
|
11
|
-
type RefObject,
|
|
12
9
|
createContext,
|
|
13
10
|
forwardRef,
|
|
14
11
|
useCallback,
|
|
@@ -18,8 +15,8 @@ import {
|
|
|
18
15
|
useState,
|
|
19
16
|
} from 'react';
|
|
20
17
|
|
|
18
|
+
import { useResizablePanelSize } from '../../hooks/ui/use-resizable-panel-size';
|
|
21
19
|
import { cn } from '../../utils/cn';
|
|
22
|
-
import { clamp } from '../../utils/common';
|
|
23
20
|
import {
|
|
24
21
|
DrawerBody,
|
|
25
22
|
DrawerDescription,
|
|
@@ -29,6 +26,7 @@ import {
|
|
|
29
26
|
OVERLAY_BACKDROP_CLASS,
|
|
30
27
|
type DrawerSide,
|
|
31
28
|
} from '../ui/drawer';
|
|
29
|
+
import { PanelResizeHandle } from '../ui/panel-resize-handle';
|
|
32
30
|
import {
|
|
33
31
|
type AppLayoutDrawerHandle,
|
|
34
32
|
useAppLayoutDrawerContainer,
|
|
@@ -216,25 +214,8 @@ const HORIZONTAL_SIDES: ReadonlySet<DrawerSide> = new Set(['left', 'right']);
|
|
|
216
214
|
*/
|
|
217
215
|
const PERSIST_CLOSED_HOLD = 'data-[state=closed]:fill-mode-forwards';
|
|
218
216
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
isHorizontal: boolean;
|
|
222
|
-
minSize: number;
|
|
223
|
-
maxSize: number;
|
|
224
|
-
defaultSize: number;
|
|
225
|
-
storageKey?: string;
|
|
226
|
-
container: HTMLElement | null;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
function useContainedResizableSize({
|
|
230
|
-
enabled,
|
|
231
|
-
isHorizontal,
|
|
232
|
-
minSize,
|
|
233
|
-
maxSize,
|
|
234
|
-
defaultSize,
|
|
235
|
-
storageKey,
|
|
236
|
-
container,
|
|
237
|
-
}: UseContainedResizableSizeArgs) {
|
|
217
|
+
/** Room the portal container gives the panel along its resize axis, in px. */
|
|
218
|
+
function useContainerExtent(enabled: boolean, isHorizontal: boolean, container: HTMLElement | null): number {
|
|
238
219
|
const [available, setAvailable] = useState(0);
|
|
239
220
|
|
|
240
221
|
useEffect(() => {
|
|
@@ -248,216 +229,7 @@ function useContainedResizableSize({
|
|
|
248
229
|
return () => ro.disconnect();
|
|
249
230
|
}, [enabled, container, isHorizontal]);
|
|
250
231
|
|
|
251
|
-
|
|
252
|
-
(value: number) => {
|
|
253
|
-
// Reserve 40px (the `system-m` outside-edge padding from the wrapper
|
|
254
|
-
// plus a matching gap on the inside edge) so the panel sits symmetrically
|
|
255
|
-
// inside the container. This also keeps the resize grip on-screen at
|
|
256
|
-
// maximum extent.
|
|
257
|
-
const effectiveMax = available > 0 ? Math.min(maxSize, available - 40) : maxSize;
|
|
258
|
-
return clamp(value, minSize, Math.max(minSize, effectiveMax));
|
|
259
|
-
},
|
|
260
|
-
[available, minSize, maxSize],
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
const readInitial = useCallback(() => {
|
|
264
|
-
if (!enabled) return defaultSize;
|
|
265
|
-
if (typeof window === 'undefined') return defaultSize;
|
|
266
|
-
if (!storageKey) return defaultSize;
|
|
267
|
-
try {
|
|
268
|
-
const raw = window.localStorage.getItem(storageKey);
|
|
269
|
-
if (!raw) return defaultSize;
|
|
270
|
-
const parsed = parseFloat(raw);
|
|
271
|
-
if (!Number.isFinite(parsed)) return defaultSize;
|
|
272
|
-
return parsed;
|
|
273
|
-
} catch {
|
|
274
|
-
return defaultSize;
|
|
275
|
-
}
|
|
276
|
-
}, [enabled, storageKey, defaultSize]);
|
|
277
|
-
|
|
278
|
-
const [rawSize, setSizeRaw] = useState<number>(readInitial);
|
|
279
|
-
|
|
280
|
-
// Re-clamp the stored size whenever the container resizes so a previously
|
|
281
|
-
// saved size never overflows after the user shrinks the viewport.
|
|
282
|
-
//
|
|
283
|
-
// Clamped where it is READ rather than written back into state from an
|
|
284
|
-
// effect: `available` is already state (the ResizeObserver above owns it), so
|
|
285
|
-
// the container shrinking re-renders regardless — and deriving here means the
|
|
286
|
-
// panel is never painted overflowing its container for the frame between the
|
|
287
|
-
// resize and the corrective commit. Keeping the raw value also means widening
|
|
288
|
-
// the viewport again restores the size the user actually chose, instead of
|
|
289
|
-
// leaving it permanently trimmed to the narrowest width ever seen.
|
|
290
|
-
const size = enabled ? clampToContainer(rawSize) : rawSize;
|
|
291
|
-
|
|
292
|
-
const setSize = useCallback((next: number) => setSizeRaw(clampToContainer(next)), [clampToContainer]);
|
|
293
|
-
|
|
294
|
-
useEffect(() => {
|
|
295
|
-
if (!enabled || !storageKey || typeof window === 'undefined') return;
|
|
296
|
-
try {
|
|
297
|
-
window.localStorage.setItem(storageKey, String(Math.round(size)));
|
|
298
|
-
} catch {
|
|
299
|
-
// ignore quota / disabled-storage
|
|
300
|
-
}
|
|
301
|
-
}, [enabled, size, storageKey]);
|
|
302
|
-
|
|
303
|
-
return { size, setSize, clampSize: clampToContainer };
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/** Ends a drawer resize drag: commits the size it reached and restores the
|
|
307
|
-
* page's cursor and text selection. No-op when no drag is in progress. */
|
|
308
|
-
function finishDrag(
|
|
309
|
-
startRef: RefObject<{ x: number; y: number; size: number } | null>,
|
|
310
|
-
dragSizeRef: RefObject<number | null>,
|
|
311
|
-
commit: (size: number) => void,
|
|
312
|
-
): void {
|
|
313
|
-
if (!startRef.current) return;
|
|
314
|
-
startRef.current = null;
|
|
315
|
-
if (dragSizeRef.current !== null) commit(dragSizeRef.current);
|
|
316
|
-
dragSizeRef.current = null;
|
|
317
|
-
document.body.style.cursor = '';
|
|
318
|
-
document.body.style.userSelect = '';
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
interface AppLayoutDrawerResizeHandleProps {
|
|
322
|
-
side: DrawerSide;
|
|
323
|
-
size: number;
|
|
324
|
-
minSize: number;
|
|
325
|
-
maxSize: number;
|
|
326
|
-
onSize: (next: number) => void;
|
|
327
|
-
/** Clamp a candidate size to the min/max and the container. */
|
|
328
|
-
clampSize: (next: number) => number;
|
|
329
|
-
/** The panel the size applies to — written directly while dragging. */
|
|
330
|
-
panelRef: RefObject<HTMLDivElement | null>;
|
|
331
|
-
ariaLabel?: string;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
function AppLayoutDrawerResizeHandle({
|
|
335
|
-
side,
|
|
336
|
-
size,
|
|
337
|
-
minSize,
|
|
338
|
-
maxSize,
|
|
339
|
-
onSize,
|
|
340
|
-
clampSize,
|
|
341
|
-
panelRef,
|
|
342
|
-
ariaLabel,
|
|
343
|
-
}: AppLayoutDrawerResizeHandleProps) {
|
|
344
|
-
const isHorizontal = HORIZONTAL_SIDES.has(side);
|
|
345
|
-
const startRef = useRef<{ x: number; y: number; size: number } | null>(null);
|
|
346
|
-
// Size reached by the drag in progress, committed to React state on release.
|
|
347
|
-
const dragSizeRef = useRef<number | null>(null);
|
|
348
|
-
const onSizeRef = useRef(onSize);
|
|
349
|
-
useEffect(() => {
|
|
350
|
-
onSizeRef.current = onSize;
|
|
351
|
-
}, [onSize]);
|
|
352
|
-
// The handle can unmount mid-drag (a persist-mode drawer closing), and then
|
|
353
|
-
// no pointer event ends the drag: end it here, or the panel keeps a width
|
|
354
|
-
// React state never received and the page keeps the resize cursor.
|
|
355
|
-
useEffect(() => () => finishDrag(startRef, dragSizeRef, onSizeRef.current), []);
|
|
356
|
-
|
|
357
|
-
const direction = side === 'right' || side === 'bottom' ? -1 : 1;
|
|
358
|
-
|
|
359
|
-
const handlePointerDown = (e: PointerEvent<HTMLDivElement>) => {
|
|
360
|
-
if (e.button !== 0 && e.pointerType === 'mouse') return;
|
|
361
|
-
e.preventDefault();
|
|
362
|
-
startRef.current = { x: e.clientX, y: e.clientY, size };
|
|
363
|
-
e.currentTarget.setPointerCapture(e.pointerId);
|
|
364
|
-
document.body.style.cursor = isHorizontal ? 'col-resize' : 'row-resize';
|
|
365
|
-
document.body.style.userSelect = 'none';
|
|
366
|
-
};
|
|
367
|
-
|
|
368
|
-
// The drag writes the panel's size straight to the DOM and commits it to
|
|
369
|
-
// state once, on release. Committing every pointermove re-rendered the
|
|
370
|
-
// drawer per event — ~16% of a drag's CPU in WebKit on top of the reflow the
|
|
371
|
-
// resize itself costs. ResizeObserver consumers inside the panel still see
|
|
372
|
-
// every change. A re-render of the drawer mid-drag (e.g. a streamed chunk)
|
|
373
|
-
// does not undo the write: React diffs against its previous render, where
|
|
374
|
-
// the size is unchanged, so it leaves the style alone.
|
|
375
|
-
const handlePointerMove = (e: PointerEvent<HTMLDivElement>) => {
|
|
376
|
-
const start = startRef.current;
|
|
377
|
-
const panel = panelRef.current;
|
|
378
|
-
if (!start || !panel) return;
|
|
379
|
-
const delta = isHorizontal ? e.clientX - start.x : e.clientY - start.y;
|
|
380
|
-
const next = clampSize(start.size + delta * direction);
|
|
381
|
-
dragSizeRef.current = next;
|
|
382
|
-
panel.style[isHorizontal ? 'width' : 'height'] = `${next}px`;
|
|
383
|
-
e.currentTarget.setAttribute('aria-valuenow', String(Math.round(next)));
|
|
384
|
-
};
|
|
385
|
-
|
|
386
|
-
const endDrag = (e: PointerEvent<HTMLDivElement>) => {
|
|
387
|
-
if (!startRef.current) return;
|
|
388
|
-
finishDrag(startRef, dragSizeRef, onSize);
|
|
389
|
-
try {
|
|
390
|
-
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
391
|
-
} catch {
|
|
392
|
-
// ignore — pointer may already be released
|
|
393
|
-
}
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
const handleKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {
|
|
397
|
-
const step = e.shiftKey ? 40 : 16;
|
|
398
|
-
if (isHorizontal) {
|
|
399
|
-
if (e.key === 'ArrowLeft') {
|
|
400
|
-
e.preventDefault();
|
|
401
|
-
onSize(size + step * (side === 'right' ? 1 : -1));
|
|
402
|
-
} else if (e.key === 'ArrowRight') {
|
|
403
|
-
e.preventDefault();
|
|
404
|
-
onSize(size + step * (side === 'right' ? -1 : 1));
|
|
405
|
-
}
|
|
406
|
-
} else {
|
|
407
|
-
if (e.key === 'ArrowUp') {
|
|
408
|
-
e.preventDefault();
|
|
409
|
-
onSize(size + step * (side === 'bottom' ? 1 : -1));
|
|
410
|
-
} else if (e.key === 'ArrowDown') {
|
|
411
|
-
e.preventDefault();
|
|
412
|
-
onSize(size + step * (side === 'bottom' ? -1 : 1));
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
if (e.key === 'Home') {
|
|
416
|
-
e.preventDefault();
|
|
417
|
-
onSize(minSize);
|
|
418
|
-
} else if (e.key === 'End') {
|
|
419
|
-
e.preventDefault();
|
|
420
|
-
onSize(maxSize);
|
|
421
|
-
}
|
|
422
|
-
};
|
|
423
|
-
|
|
424
|
-
const trackPosition =
|
|
425
|
-
side === 'right'
|
|
426
|
-
? 'right-full top-4 bottom-4 w-6 items-center justify-end pr-1'
|
|
427
|
-
: side === 'left'
|
|
428
|
-
? 'left-full top-4 bottom-4 w-6 items-center justify-start pl-1'
|
|
429
|
-
: side === 'bottom'
|
|
430
|
-
? 'bottom-full left-4 right-4 h-6 justify-center items-end pb-1'
|
|
431
|
-
: 'top-full left-4 right-4 h-6 justify-center items-start pt-1';
|
|
432
|
-
|
|
433
|
-
const cursorClass = isHorizontal ? 'cursor-col-resize' : 'cursor-row-resize';
|
|
434
|
-
const gripClass = isHorizontal ? 'h-10 w-1' : 'w-10 h-1';
|
|
435
|
-
|
|
436
|
-
return (
|
|
437
|
-
<div
|
|
438
|
-
role="separator"
|
|
439
|
-
tabIndex={0}
|
|
440
|
-
aria-orientation={isHorizontal ? 'vertical' : 'horizontal'}
|
|
441
|
-
aria-valuenow={Math.round(size)}
|
|
442
|
-
aria-valuemin={minSize}
|
|
443
|
-
aria-valuemax={maxSize}
|
|
444
|
-
aria-label={ariaLabel ?? (isHorizontal ? 'Resize panel width' : 'Resize panel height')}
|
|
445
|
-
onPointerDown={handlePointerDown}
|
|
446
|
-
onPointerMove={handlePointerMove}
|
|
447
|
-
onPointerUp={endDrag}
|
|
448
|
-
onPointerCancel={endDrag}
|
|
449
|
-
onLostPointerCapture={endDrag}
|
|
450
|
-
onKeyDown={handleKeyDown}
|
|
451
|
-
className={cn(
|
|
452
|
-
'group absolute z-20 flex touch-none select-none',
|
|
453
|
-
'outline-none ring-0 focus:outline-none focus:ring-0 focus-visible:outline-none focus-visible:ring-0',
|
|
454
|
-
trackPosition,
|
|
455
|
-
cursorClass,
|
|
456
|
-
)}
|
|
457
|
-
>
|
|
458
|
-
<div aria-hidden className={cn('rounded-full bg-ods-border', gripClass)} />
|
|
459
|
-
</div>
|
|
460
|
-
);
|
|
232
|
+
return available;
|
|
461
233
|
}
|
|
462
234
|
|
|
463
235
|
export interface AppLayoutDrawerContentProps
|
|
@@ -628,14 +400,18 @@ const AppLayoutDrawerContent = forwardRef<ComponentRef<typeof DialogPrimitive.Co
|
|
|
628
400
|
}, [mobileBreakpoint]);
|
|
629
401
|
|
|
630
402
|
const panelRef = useRef<HTMLDivElement>(null);
|
|
631
|
-
|
|
403
|
+
// Reserve 40px (the `system-m` outside-edge padding from the wrapper plus a
|
|
404
|
+
// matching gap on the inside edge) so the panel sits symmetrically inside
|
|
405
|
+
// the container and the resize grip stays on screen at maximum extent.
|
|
406
|
+
const available = useContainerExtent(resizable, isHorizontal, portalContainer);
|
|
407
|
+
const { size, setSize, clampSize } = useResizablePanelSize({
|
|
632
408
|
enabled: resizable,
|
|
633
|
-
isHorizontal,
|
|
634
409
|
minSize,
|
|
635
410
|
maxSize,
|
|
636
411
|
defaultSize: initialSize,
|
|
412
|
+
available,
|
|
413
|
+
reserve: 40,
|
|
637
414
|
storageKey,
|
|
638
|
-
container: portalContainer,
|
|
639
415
|
});
|
|
640
416
|
|
|
641
417
|
const applyInlineSize = resizable && !isMobile;
|
|
@@ -707,7 +483,8 @@ const AppLayoutDrawerContent = forwardRef<ComponentRef<typeof DialogPrimitive.Co
|
|
|
707
483
|
at the container edge. In persist mode it's also non-interactive
|
|
708
484
|
while closed, so drop it entirely until the panel reopens. */}
|
|
709
485
|
{applyInlineSize && (!persist || open) ? (
|
|
710
|
-
<
|
|
486
|
+
<PanelResizeHandle
|
|
487
|
+
variant="inLayout"
|
|
711
488
|
side={resolvedSide}
|
|
712
489
|
size={size}
|
|
713
490
|
minSize={minSize}
|