@flamingo-stack/openframe-frontend-core 0.0.703 → 0.0.704-snapshot.20261006133653
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-3L7KCOGO.cjs → chunk-2QEUFXNS.cjs} +30 -30
- package/dist/{chunk-BMJUKS6S.cjs → chunk-3256UJXS.cjs} +47 -47
- package/dist/{chunk-GZYLZ4MB.js → chunk-4S7TJYRQ.js} +3 -3
- package/dist/{chunk-ENGBNKUJ.js → chunk-5EFZO5EB.js} +675 -569
- package/dist/{chunk-TCZCMD5Q.js → chunk-5HBHO5TB.js} +1 -1
- package/dist/{chunk-BOUPNYUV.js → chunk-64HOMVYC.js} +5 -5
- package/dist/{chunk-YR6JPJF4.js → chunk-6XIF2Z7I.js} +4 -4
- package/dist/{chunk-NU724ZVN.cjs → chunk-7GHB2IK6.cjs} +121 -121
- package/dist/{chunk-GX3OUTFO.cjs → chunk-7ZYIC7NA.cjs} +69 -55
- package/dist/{chunk-53AC23ZN.js → chunk-A6WVQKOZ.js} +24 -24
- package/dist/{chunk-4OGAXAEG.cjs → chunk-ABU3TGXA.cjs} +3 -3
- package/dist/{chunk-RTXTGLGB.cjs → chunk-ACV4DFQ3.cjs} +4 -4
- package/dist/{chunk-RLDAYA2A.cjs → chunk-BH6PJP66.cjs} +29 -29
- package/dist/{chunk-BRXOGCH3.cjs → chunk-BIZNSF3U.cjs} +3 -3
- package/dist/{chunk-2A4XES7R.cjs → chunk-BM5MVGH3.cjs} +3 -3
- package/dist/{chunk-6YCXA7CN.js → chunk-CH5232HV.js} +20 -19
- package/dist/{chunk-UIBSSVON.cjs → chunk-D7BTSVMS.cjs} +2 -2
- package/dist/{chunk-YBGEPKC2.cjs → chunk-F7Y2ZS5U.cjs} +6 -6
- package/dist/{chunk-5V6O73EH.js → chunk-FLKJYMVB.js} +3 -3
- package/dist/{chunk-NMGX2ZTU.js → chunk-IFUQVZF4.js} +17 -17
- package/dist/{chunk-2NE3DNXY.js → chunk-IKCQK67U.js} +2 -2
- package/dist/{chunk-KFDP3DUL.cjs → chunk-IXPUM3AW.cjs} +98 -98
- package/dist/{chunk-QGTDWFT6.js → chunk-IZHZMBIX.js} +2469 -1928
- package/dist/{chunk-67CXGPTW.cjs → chunk-JKEEFQOX.cjs} +505 -72
- package/dist/{chunk-NYSYSKSH.cjs → chunk-M6HTJTFH.cjs} +15 -15
- package/dist/{chunk-OGLCD3UL.js → chunk-MFFCUVWG.js} +6 -6
- package/dist/{chunk-KJ5LWATV.js → chunk-MKXO4Q5P.js} +7 -7
- package/dist/{chunk-EMCQGFXD.cjs → chunk-MMOODW57.cjs} +28 -27
- package/dist/{chunk-Q76ZTHNV.js → chunk-MP2C3LJ4.js} +1 -1
- package/dist/{chunk-J4K5JOLI.js → chunk-MV4HYG3B.js} +25 -11
- package/dist/{chunk-2T6QWNXU.js → chunk-NDESEPW5.js} +99 -11
- package/dist/{chunk-HHFPQNP3.js → chunk-NJDHFC55.js} +15 -15
- package/dist/{chunk-HUOLB7ZN.cjs → chunk-OHFS7VOU.cjs} +9 -9
- package/dist/{chunk-7632TNE7.js → chunk-OSNIZNVH.js} +7 -7
- package/dist/{chunk-UTW4SQDI.js → chunk-OUHMD6EU.js} +10 -10
- package/dist/{chunk-MXHJROXJ.cjs → chunk-P23CRLU4.cjs} +1245 -1139
- package/dist/{chunk-64O4L7WN.js → chunk-PDWVPI3S.js} +11 -55
- package/dist/{chunk-DAW6TDZB.js → chunk-PFC2HWQA.js} +30 -16
- package/dist/{chunk-G55W76S6.js → chunk-POZ7F77D.js} +12 -13
- package/dist/{chunk-PJP5SMOU.js → chunk-PPEZGUL7.js} +500 -67
- package/dist/{chunk-SFGXIFYO.cjs → chunk-QC7Q3XCE.cjs} +24 -24
- package/dist/{chunk-MDBHZBW7.cjs → chunk-RKV5B7EU.cjs} +92 -4
- package/dist/{chunk-5VPIC5MD.cjs → chunk-SAAZEWUR.cjs} +12 -12
- package/dist/{chunk-IIMICN7L.cjs → chunk-TYWLNCNH.cjs} +4 -48
- package/dist/{chunk-HYLIQMZH.cjs → chunk-UBP5RNGK.cjs} +33 -33
- package/dist/{chunk-HUMWQMCU.js → chunk-UT4YHQZW.js} +3 -3
- package/dist/{chunk-BSEIEWM5.cjs → chunk-V3H26CNS.cjs} +28 -14
- package/dist/{chunk-NGJCWEYX.cjs → chunk-VHCACDKP.cjs} +46 -46
- package/dist/{chunk-SL2FOFT2.js → chunk-VMAZCLX4.js} +7 -7
- package/dist/{chunk-5FFNNJM7.js → chunk-VR2ZLFCP.js} +1 -1
- package/dist/{chunk-NCTYD7HL.js → chunk-VYWMEL5F.js} +14 -14
- package/dist/{chunk-HY5YL3U5.cjs → chunk-XBWE423R.cjs} +1994 -1453
- package/dist/{chunk-XCIFHW4E.cjs → chunk-XOYH2YQE.cjs} +2 -2
- package/dist/{chunk-S6V7A5FS.cjs → chunk-ZQZFIE2K.cjs} +32 -33
- 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-input.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-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.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/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/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-input.tsx +5 -1
- 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 +675 -485
- 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-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/platform/SoftwareSourceBadge.tsx +1 -1
- 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 +2 -1
- 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/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/styles/app-globals.css +13 -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
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import type { ReactNode } from 'react';
|
|
5
|
+
import { ContentAreaWidthContext } from '../../hooks/ui/use-content-breakpoint';
|
|
6
|
+
import { useIsomorphicLayoutEffect } from '../../hooks/ui/use-isomorphic-layout-effect';
|
|
5
7
|
import type { NavigationSidebarConfig } from '../../types/navigation';
|
|
6
8
|
import { cn } from '../../utils';
|
|
7
9
|
import { NotificationDrawer } from '../features/notifications/notification-drawer';
|
|
@@ -12,6 +14,7 @@ import {
|
|
|
12
14
|
AppLayoutDrawerCoordinationContext,
|
|
13
15
|
type AppLayoutDrawerHandle,
|
|
14
16
|
} from './app-layout-context';
|
|
17
|
+
import { AppLayoutSidePanel, type AppLayoutSidePanelConfig, useAppLayoutSidePanel } from './app-layout-side-panel';
|
|
15
18
|
import { MobileBurgerMenu, type MobileBurgerMenuProps } from './mobile-burger-menu';
|
|
16
19
|
import { NavigationSidebar } from './navigation-sidebar';
|
|
17
20
|
|
|
@@ -46,6 +49,15 @@ export interface AppLayoutProps {
|
|
|
46
49
|
* area occupy the remaining height below it.
|
|
47
50
|
*/
|
|
48
51
|
topBar?: ReactNode;
|
|
52
|
+
/**
|
|
53
|
+
* A panel docked beside the content (the Mingo chat). It narrows the
|
|
54
|
+
* content instead of covering it, and the content then lays out by its own
|
|
55
|
+
* width: `<main>` becomes an `ods-content-area` whose tokens and
|
|
56
|
+
* `content-md:` / `content-lg:` classes follow the room the panel leaves.
|
|
57
|
+
* Without room to dock (or on a phone) the header's Mingo button opens it
|
|
58
|
+
* over the content area. Optional; without it the layout is unchanged.
|
|
59
|
+
*/
|
|
60
|
+
sidePanel?: AppLayoutSidePanelConfig;
|
|
49
61
|
}
|
|
50
62
|
|
|
51
63
|
export function AppLayout({
|
|
@@ -58,9 +70,38 @@ export function AppLayout({
|
|
|
58
70
|
disabled = false,
|
|
59
71
|
drawer,
|
|
60
72
|
topBar,
|
|
73
|
+
sidePanel,
|
|
61
74
|
}: AppLayoutProps) {
|
|
62
75
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
|
63
76
|
const [drawerContainer, setDrawerContainer] = useState<HTMLDivElement | null>(null);
|
|
77
|
+
const [mainElement, setMainElement] = useState<HTMLElement | null>(null);
|
|
78
|
+
const [contentWidth, setContentWidth] = useState<number | null>(null);
|
|
79
|
+
|
|
80
|
+
const hasSidePanel = sidePanel !== undefined;
|
|
81
|
+
const sidePanelState = useAppLayoutSidePanel(sidePanel, drawerContainer);
|
|
82
|
+
|
|
83
|
+
useIsomorphicLayoutEffect(() => {
|
|
84
|
+
if (!hasSidePanel || !mainElement) return undefined;
|
|
85
|
+
const update = () => setContentWidth(mainElement.clientWidth);
|
|
86
|
+
update();
|
|
87
|
+
const observer = new ResizeObserver(update);
|
|
88
|
+
observer.observe(mainElement);
|
|
89
|
+
return () => observer.disconnect();
|
|
90
|
+
}, [hasSidePanel, mainElement]);
|
|
91
|
+
|
|
92
|
+
// With a side panel the header's Mingo button only exists while the panel
|
|
93
|
+
// cannot dock: it opens the panel over the content and closes it again.
|
|
94
|
+
const resolvedHeaderProps =
|
|
95
|
+
sidePanelState === null
|
|
96
|
+
? headerProps
|
|
97
|
+
: {
|
|
98
|
+
...headerProps,
|
|
99
|
+
showMingoAI: !sidePanelState.canDock,
|
|
100
|
+
onMingoAI: sidePanelState.toggle,
|
|
101
|
+
isMingoAIActive: sidePanelState.isOpen,
|
|
102
|
+
mingoAICloseWhenActive: true,
|
|
103
|
+
};
|
|
104
|
+
const contentCovered = sidePanelState?.coversContent === true;
|
|
64
105
|
|
|
65
106
|
// Mirrors `mobileMenuOpen` so the toggle callback can stay identity-stable.
|
|
66
107
|
// Refreshed after every commit rather than in the render body: the only
|
|
@@ -114,7 +155,7 @@ export function AppLayout({
|
|
|
114
155
|
`relative` so the tablet sidebar (position:absolute) anchors to this
|
|
115
156
|
row — below the topBar — instead of the viewport. */}
|
|
116
157
|
<div className="relative flex min-h-0 flex-1">
|
|
117
|
-
<NavigationSidebar config={sidebarConfig} disabled={disabled} />
|
|
158
|
+
<NavigationSidebar config={sidebarConfig} disabled={disabled} pushOnTablet={hasSidePanel} />
|
|
118
159
|
{/* Mobile Burger Menu - opens below header */}
|
|
119
160
|
<MobileBurgerMenu
|
|
120
161
|
{...mobileBurgerMenuProps}
|
|
@@ -127,7 +168,7 @@ export function AppLayout({
|
|
|
127
168
|
{/* Main Content Area */}
|
|
128
169
|
<div className="flex flex-1 flex-col overflow-hidden">
|
|
129
170
|
<AppHeader
|
|
130
|
-
{...
|
|
171
|
+
{...resolvedHeaderProps}
|
|
131
172
|
isMobileMenuOpen={mobileMenuOpen}
|
|
132
173
|
onToggleMobileMenu={handleToggleMobileMenu}
|
|
133
174
|
disabled={disabled}
|
|
@@ -140,8 +181,32 @@ export function AppLayout({
|
|
|
140
181
|
animation visually AND contains layout overflow so it doesn't
|
|
141
182
|
propagate up to <html>. (Scroll-snap-back below handles the
|
|
142
183
|
browser's programmatic scroll-on-focus side effect.) */}
|
|
143
|
-
<div
|
|
144
|
-
|
|
184
|
+
<div
|
|
185
|
+
ref={setDrawerContainer}
|
|
186
|
+
className={cn('relative flex flex-1 overflow-hidden', sidePanel ? 'flex-row' : 'flex-col')}
|
|
187
|
+
>
|
|
188
|
+
{sidePanel ? (
|
|
189
|
+
// `hidden` while the panel covers the area: the page stays
|
|
190
|
+
// mounted, it just takes no room.
|
|
191
|
+
<main
|
|
192
|
+
ref={setMainElement}
|
|
193
|
+
className={cn(
|
|
194
|
+
'ods-content-area min-w-0 flex-1 overflow-y-auto',
|
|
195
|
+
contentCovered && 'hidden',
|
|
196
|
+
// While the panel is drawn over the page, keep the page's own
|
|
197
|
+
// z-indexed layers (sticky headers, fixed bars) under it.
|
|
198
|
+
sidePanelState?.overlapsContent && 'isolate',
|
|
199
|
+
mainClassName,
|
|
200
|
+
)}
|
|
201
|
+
>
|
|
202
|
+
<ContentAreaWidthContext.Provider value={contentWidth}>
|
|
203
|
+
<div className="ods-content-scope">{children}</div>
|
|
204
|
+
</ContentAreaWidthContext.Provider>
|
|
205
|
+
</main>
|
|
206
|
+
) : (
|
|
207
|
+
<main className={cn('flex-1 overflow-y-auto', mainClassName)}>{children}</main>
|
|
208
|
+
)}
|
|
209
|
+
{sidePanel && sidePanelState && <AppLayoutSidePanel config={sidePanel} state={sidePanelState} />}
|
|
145
210
|
{/* `drawer` slot — rendered here so it sits inside the
|
|
146
211
|
AppLayoutDrawerContainerContext and can portal into this exact
|
|
147
212
|
container. Mount location is irrelevant for visual placement
|
|
@@ -12,6 +12,9 @@ export interface HeaderMingoButtonProps extends React.ButtonHTMLAttributes<HTMLB
|
|
|
12
12
|
* control collapses to a square `HeaderButton`-sized affordance on narrow
|
|
13
13
|
* viewports). Defaults to `false`. */
|
|
14
14
|
iconOnly?: boolean;
|
|
15
|
+
/** Show the close "X" while active with the wordmark too, not only in
|
|
16
|
+
* `iconOnly` mode. Defaults to `false`. */
|
|
17
|
+
closeWhenActive?: boolean;
|
|
15
18
|
className?: string;
|
|
16
19
|
}
|
|
17
20
|
|
|
@@ -25,11 +28,18 @@ export interface HeaderMingoButtonProps extends React.ButtonHTMLAttributes<HTMLB
|
|
|
25
28
|
*
|
|
26
29
|
* Figma: 7532:222103 — `button-full`.
|
|
27
30
|
*/
|
|
28
|
-
export function HeaderMingoButton({
|
|
31
|
+
export function HeaderMingoButton({
|
|
32
|
+
isActive = false,
|
|
33
|
+
iconOnly = false,
|
|
34
|
+
closeWhenActive = false,
|
|
35
|
+
className,
|
|
36
|
+
...props
|
|
37
|
+
}: HeaderMingoButtonProps) {
|
|
38
|
+
const showClose = isActive && (iconOnly || closeWhenActive);
|
|
29
39
|
return (
|
|
30
40
|
<button
|
|
31
41
|
type="button"
|
|
32
|
-
aria-label={
|
|
42
|
+
aria-label={showClose ? 'Close Mingo AI' : 'Mingo AI'}
|
|
33
43
|
aria-pressed={isActive}
|
|
34
44
|
className={cn(
|
|
35
45
|
'flex h-full shrink-0 items-center gap-2 px-4',
|
|
@@ -47,7 +57,7 @@ export function HeaderMingoButton({ isActive = false, iconOnly = false, classNam
|
|
|
47
57
|
)}
|
|
48
58
|
{...props}
|
|
49
59
|
>
|
|
50
|
-
{
|
|
60
|
+
{showClose ? (
|
|
51
61
|
// Mobile, drawer open: the icon-only button doubles as the close
|
|
52
62
|
// affordance, so swap the Mingo logo for an X. Match the other header
|
|
53
63
|
// icons (e.g. notifications): secondary color, w-4 h-4 → md:w-6 h-6.
|
|
@@ -30,6 +30,11 @@ export type { AppHeaderProps, HeaderLoadingCell } from './app-header';
|
|
|
30
30
|
|
|
31
31
|
export { AppLayout, useAppLayoutDrawerContainer } from './app-layout';
|
|
32
32
|
export type { AppLayoutProps } from './app-layout';
|
|
33
|
+
export type {
|
|
34
|
+
AppLayoutSidePanelConfig,
|
|
35
|
+
AppLayoutSidePanelMode,
|
|
36
|
+
AppLayoutSidePanelRenderState,
|
|
37
|
+
} from './app-layout-side-panel';
|
|
33
38
|
|
|
34
39
|
export {
|
|
35
40
|
AppLayoutDrawer,
|
|
@@ -123,9 +123,16 @@ export interface NavigationSidebarProps {
|
|
|
123
123
|
* The collapse/expand toggle button remains interactive.
|
|
124
124
|
*/
|
|
125
125
|
disabled?: boolean;
|
|
126
|
+
/**
|
|
127
|
+
* On tablet, open the expanded sidebar beside the content instead of over
|
|
128
|
+
* it: the slot it holds widens with it and there is no scrim. A layout with
|
|
129
|
+
* a docked side panel needs the content's real width to change. Default
|
|
130
|
+
* false (overlay).
|
|
131
|
+
*/
|
|
132
|
+
pushOnTablet?: boolean;
|
|
126
133
|
}
|
|
127
134
|
|
|
128
|
-
export function NavigationSidebar({ config, disabled = false }: NavigationSidebarProps) {
|
|
135
|
+
export function NavigationSidebar({ config, disabled = false, pushOnTablet = false }: NavigationSidebarProps) {
|
|
129
136
|
const isMdUp = useMdUp() ?? false;
|
|
130
137
|
const isLgUp = useLgUp() ?? false;
|
|
131
138
|
|
|
@@ -320,7 +327,7 @@ export function NavigationSidebar({ config, disabled = false }: NavigationSideba
|
|
|
320
327
|
'fixed inset-0 z-[40] bg-ods-overlay',
|
|
321
328
|
'hidden md:block lg:hidden',
|
|
322
329
|
'transition-opacity duration-300',
|
|
323
|
-
isOverlayOpen ? 'opacity-100' : 'pointer-events-none opacity-0',
|
|
330
|
+
isOverlayOpen && !pushOnTablet ? 'opacity-100' : 'pointer-events-none opacity-0',
|
|
324
331
|
)}
|
|
325
332
|
onClick={closeOverlay}
|
|
326
333
|
aria-hidden="true"
|
|
@@ -332,7 +339,14 @@ export function NavigationSidebar({ config, disabled = false }: NavigationSideba
|
|
|
332
339
|
geometry is: gated on `isTablet`, it was missing from the server HTML
|
|
333
340
|
and from the first client paint — exactly when the sidebar it
|
|
334
341
|
compensates for has already gone `absolute`. */}
|
|
335
|
-
<div
|
|
342
|
+
<div
|
|
343
|
+
className={cn(
|
|
344
|
+
'hidden h-full flex-shrink-0 md:block lg:hidden',
|
|
345
|
+
isOverlayOpen && pushOnTablet ? 'w-56' : 'w-14',
|
|
346
|
+
transitionsEnabled && 'transition-[width] duration-300',
|
|
347
|
+
)}
|
|
348
|
+
aria-hidden="true"
|
|
349
|
+
/>
|
|
336
350
|
|
|
337
351
|
<aside
|
|
338
352
|
className={cn(
|
|
@@ -212,7 +212,8 @@ export function SlidingSidebar({ config }: SlidingSidebarProps) {
|
|
|
212
212
|
role={config.isOpen ? 'dialog' : undefined}
|
|
213
213
|
aria-label={config.isOpen ? 'Navigation sidebar' : undefined}
|
|
214
214
|
className={cn(
|
|
215
|
-
'
|
|
215
|
+
// Window chrome: the window's tokens even when opened from a narrow content area.
|
|
216
|
+
'ods-viewport-layer fixed bottom-0 top-0 z-[46] flex w-72 flex-col border-ods-border bg-ods-card shadow-xl',
|
|
216
217
|
config.position === 'right' ? 'right-0 border-l' : 'left-0 border-r',
|
|
217
218
|
config.className,
|
|
218
219
|
)}
|
|
@@ -74,7 +74,7 @@ const Pagination = ({ currentPage, totalPages, onPageChange, className }: Pagina
|
|
|
74
74
|
onClick={() => onPageChange(currentPage - 1)}
|
|
75
75
|
disabled={currentPage === 1}
|
|
76
76
|
aria-label="Go to previous page"
|
|
77
|
-
className="!h-10 !w-10 md:!h-10 md:!w-10"
|
|
77
|
+
className="!h-10 !w-10 content-md:!h-10 content-md:!w-10"
|
|
78
78
|
>
|
|
79
79
|
<ChevronLeft className="h-4 w-4" />
|
|
80
80
|
</Button>
|
|
@@ -99,7 +99,7 @@ const Pagination = ({ currentPage, totalPages, onPageChange, className }: Pagina
|
|
|
99
99
|
variant={currentPage === page ? 'accent' : 'outline'}
|
|
100
100
|
size="icon"
|
|
101
101
|
onClick={() => onPageChange(page)}
|
|
102
|
-
className="!h-9 !w-9 md:!h-9 md:!w-9"
|
|
102
|
+
className="!h-9 !w-9 content-md:!h-9 content-md:!w-9"
|
|
103
103
|
aria-label={`Page ${page}`}
|
|
104
104
|
aria-current={currentPage === page ? 'page' : undefined}
|
|
105
105
|
>
|
|
@@ -116,7 +116,7 @@ const Pagination = ({ currentPage, totalPages, onPageChange, className }: Pagina
|
|
|
116
116
|
onClick={() => onPageChange(currentPage + 1)}
|
|
117
117
|
disabled={currentPage === totalPages}
|
|
118
118
|
aria-label="Go to next page"
|
|
119
|
-
className="!h-10 !w-10 md:!h-10 md:!w-10"
|
|
119
|
+
className="!h-10 !w-10 content-md:!h-10 content-md:!w-10"
|
|
120
120
|
>
|
|
121
121
|
<ChevronRight className="h-4 w-4" />
|
|
122
122
|
<span className="sr-only">Next page</span>
|
|
@@ -138,7 +138,7 @@ export function PersistentSidebar({ isLoading, children, className }: Persistent
|
|
|
138
138
|
<PersistentFilterControls
|
|
139
139
|
isLoading={isLoading}
|
|
140
140
|
disabledOpacity={0.5}
|
|
141
|
-
className={cn('lg:sticky lg:top-20', className)}
|
|
141
|
+
className={cn('content-lg:sticky content-lg:top-20', className)}
|
|
142
142
|
>
|
|
143
143
|
{children}
|
|
144
144
|
</PersistentFilterControls>
|
|
@@ -156,7 +156,11 @@ interface PersistentMobileDropdownProps {
|
|
|
156
156
|
|
|
157
157
|
export function PersistentMobileDropdown({ isLoading, children, className }: PersistentMobileDropdownProps) {
|
|
158
158
|
return (
|
|
159
|
-
<PersistentFilterControls
|
|
159
|
+
<PersistentFilterControls
|
|
160
|
+
isLoading={isLoading}
|
|
161
|
+
disabledOpacity={0.7}
|
|
162
|
+
className={cn('content-lg:hidden', className)}
|
|
163
|
+
>
|
|
160
164
|
{children}
|
|
161
165
|
</PersistentFilterControls>
|
|
162
166
|
);
|
|
@@ -181,7 +181,7 @@ export const ScriptArguments: React.FC<ScriptArgumentsProps> = ({
|
|
|
181
181
|
onClick={() => handleDelete(arg.id)}
|
|
182
182
|
disabled={disabled}
|
|
183
183
|
aria-label="Delete argument"
|
|
184
|
-
leftIcon={<Trash2 className="size-4 md:size-6" color="var(--color-error)" />}
|
|
184
|
+
leftIcon={<Trash2 className="size-4 content-md:size-6" color="var(--color-error)" />}
|
|
185
185
|
/>
|
|
186
186
|
</div>
|
|
187
187
|
</div>
|
|
@@ -136,15 +136,15 @@ export const ScriptInfoSection: React.FC<ScriptInfoSectionProps> = ({
|
|
|
136
136
|
- Desktop (lg+): 4 columns in single row
|
|
137
137
|
*/}
|
|
138
138
|
{/* First row: Shell Type, Supported Platforms */}
|
|
139
|
-
<div className="grid grid-cols-2 gap-4 border-b border-ods-border px-4 py-4 lg:grid-cols-4 lg:border-b-0">
|
|
139
|
+
<div className="grid grid-cols-2 gap-4 border-b border-ods-border px-4 py-4 content-lg:grid-cols-4 content-lg:border-b-0">
|
|
140
140
|
<InfoCell label="Shell Type" value={shellLabel} />
|
|
141
141
|
<InfoCell label="Supported Platforms" value={platformsLabel} />
|
|
142
142
|
{/* Desktop only: Category and Author in same row */}
|
|
143
|
-
<InfoCell label="Category" value={category} className="hidden lg:flex" />
|
|
144
|
-
{author && <InfoCell label="Added by" value={author.name} avatar={author} className="hidden lg:flex" />}
|
|
143
|
+
<InfoCell label="Category" value={category} className="hidden content-lg:flex" />
|
|
144
|
+
{author && <InfoCell label="Added by" value={author.name} avatar={author} className="hidden content-lg:flex" />}
|
|
145
145
|
</div>
|
|
146
146
|
{/* Second row (mobile/tablet only): Category, Author */}
|
|
147
|
-
<div className="grid grid-cols-2 gap-4 px-4 py-4 lg:hidden">
|
|
147
|
+
<div className="grid grid-cols-2 gap-4 px-4 py-4 content-lg:hidden">
|
|
148
148
|
<InfoCell label="Category" value={category} />
|
|
149
149
|
{author && <InfoCell label="Added by" value={author.name} avatar={author} />}
|
|
150
150
|
</div>
|
|
@@ -111,7 +111,7 @@ export function ArticleAuthorByline({
|
|
|
111
111
|
<div
|
|
112
112
|
className={cn(
|
|
113
113
|
'rounded-lg border border-ods-border bg-ods-card p-6',
|
|
114
|
-
'flex flex-col items-start gap-4 md:flex-row',
|
|
114
|
+
'flex flex-col items-start gap-4 content-md:flex-row',
|
|
115
115
|
className,
|
|
116
116
|
)}
|
|
117
117
|
>
|
|
@@ -202,7 +202,7 @@ export function DeliveryLists({
|
|
|
202
202
|
{/* Completed Tasks Table */}
|
|
203
203
|
{showCompleted && (hasResults || isLoading) && (
|
|
204
204
|
<div className="w-full">
|
|
205
|
-
<h3 className="mb-4 tracking-[-0.48px] text-ods-text-primary text-h2 md:tracking-[-0.56px] lg:tracking-[-0.64px]">
|
|
205
|
+
<h3 className="mb-4 tracking-[-0.48px] text-ods-text-primary text-h2 content-md:tracking-[-0.56px] content-lg:tracking-[-0.64px]">
|
|
206
206
|
Recently Completed<span className="text-ods-accent">:</span>
|
|
207
207
|
</h3>
|
|
208
208
|
<DeliveryTable items={filteredCompleted} isLoading={isLoading} />
|
|
@@ -212,7 +212,7 @@ export function DeliveryLists({
|
|
|
212
212
|
{/* In Progress Tasks Table */}
|
|
213
213
|
{showInProgress && (hasResults || isLoading) && (
|
|
214
214
|
<div className="w-full">
|
|
215
|
-
<h3 className="mb-4 tracking-[-0.48px] text-ods-text-primary text-h2 md:tracking-[-0.56px] lg:tracking-[-0.64px]">
|
|
215
|
+
<h3 className="mb-4 tracking-[-0.48px] text-ods-text-primary text-h2 content-md:tracking-[-0.56px] content-lg:tracking-[-0.64px]">
|
|
216
216
|
Active Tasks<span className="text-ods-accent">:</span>
|
|
217
217
|
</h3>
|
|
218
218
|
<DeliveryTable items={filteredInProgress} isLoading={isLoading} />
|
|
@@ -82,12 +82,12 @@ export function DeliveryRow({ item, href, target, caption, id, className }: Deli
|
|
|
82
82
|
const subtitle = `ACTIVE ${relativeTime}${item.listNames.length > 0 ? `, ${item.listNames.join(', ')}` : ''}, ${item.id}`;
|
|
83
83
|
|
|
84
84
|
const inner = (
|
|
85
|
-
<div className="flex w-full flex-col items-start justify-between gap-[12px] md:flex-row md:gap-[16px]">
|
|
85
|
+
<div className="flex w-full flex-col items-start justify-between gap-[12px] content-md:flex-row content-md:gap-[16px]">
|
|
86
86
|
{/* Left: caption (optional) + title + subtitle + description */}
|
|
87
|
-
<div className="flex w-full min-w-0 flex-1 flex-col gap-[12px] md:w-auto md:gap-[16px]">
|
|
87
|
+
<div className="flex w-full min-w-0 flex-1 flex-col gap-[12px] content-md:w-auto content-md:gap-[16px]">
|
|
88
88
|
{caption && <p className="text-ods-text-secondary text-h5">{caption}</p>}
|
|
89
|
-
<div className="flex min-h-[24px] items-center md:min-h-[24px]">
|
|
90
|
-
<h3 className="line-clamp-2 flex-1 break-words tracking-[-0.36px] text-ods-text-primary text-h3 md:truncate">
|
|
89
|
+
<div className="flex min-h-[24px] items-center content-md:min-h-[24px]">
|
|
90
|
+
<h3 className="line-clamp-2 flex-1 break-words tracking-[-0.36px] text-ods-text-primary text-h3 content-md:truncate">
|
|
91
91
|
{item.title}
|
|
92
92
|
</h3>
|
|
93
93
|
</div>
|
|
@@ -112,7 +112,7 @@ export function DeliveryRow({ item, href, target, caption, id, className }: Deli
|
|
|
112
112
|
assignee owns the trailing slot of the meta row; a column of
|
|
113
113
|
right-aligned orphans under the description reads as clutter
|
|
114
114
|
on small screens). */}
|
|
115
|
-
<div className="flex w-full flex-shrink-0 flex-row items-center gap-2 self-stretch md:w-auto md:flex-col md:items-end md:self-start">
|
|
115
|
+
<div className="flex w-full flex-shrink-0 flex-row items-center gap-2 self-stretch content-md:w-auto content-md:flex-col content-md:items-end content-md:self-start">
|
|
116
116
|
<StatusBadge
|
|
117
117
|
text={item.status.toUpperCase()}
|
|
118
118
|
colorScheme={statusBadgeScheme}
|
|
@@ -127,7 +127,7 @@ export function DeliveryRow({ item, href, target, caption, id, className }: Deli
|
|
|
127
127
|
{item.assignees && item.assignees.length > 0 ? (
|
|
128
128
|
<AvatarStack
|
|
129
129
|
size="xs"
|
|
130
|
-
className="ml-auto md:ml-0"
|
|
130
|
+
className="ml-auto content-md:ml-0"
|
|
131
131
|
people={item.assignees.map(a => ({
|
|
132
132
|
key: a.id,
|
|
133
133
|
name: a.name ?? 'Unknown',
|
|
@@ -140,7 +140,7 @@ export function DeliveryRow({ item, href, target, caption, id, className }: Deli
|
|
|
140
140
|
);
|
|
141
141
|
|
|
142
142
|
const baseClass = cn(
|
|
143
|
-
'block p-[12px] text-inherit no-underline transition-colors duration-150 md:p-[16px]',
|
|
143
|
+
'block p-[12px] text-inherit no-underline transition-colors duration-150 content-md:p-[16px]',
|
|
144
144
|
// `scroll-mt-24` is paid for whether `id` is set or not (it's a
|
|
145
145
|
// single Tailwind utility, no runtime cost). Keeping it
|
|
146
146
|
// unconditional means a future caller adding `id` doesn't also
|
|
@@ -32,10 +32,10 @@ interface DeliveryTableProps {
|
|
|
32
32
|
*/
|
|
33
33
|
function SkeletonRow() {
|
|
34
34
|
return (
|
|
35
|
-
<div className="border-b border-ods-border p-[12px] last:border-b-0 md:p-[16px]">
|
|
36
|
-
<div className="flex w-full flex-col items-start justify-between gap-[12px] md:flex-row md:gap-[16px]">
|
|
35
|
+
<div className="border-b border-ods-border p-[12px] last:border-b-0 content-md:p-[16px]">
|
|
36
|
+
<div className="flex w-full flex-col items-start justify-between gap-[12px] content-md:flex-row content-md:gap-[16px]">
|
|
37
37
|
{/* Left: Title, subtitle, and description skeleton */}
|
|
38
|
-
<div className="flex w-full min-w-0 flex-1 flex-col gap-[12px] md:w-auto md:gap-[16px]">
|
|
38
|
+
<div className="flex w-full min-w-0 flex-1 flex-col gap-[12px] content-md:w-auto content-md:gap-[16px]">
|
|
39
39
|
{/* Title skeleton - responsive */}
|
|
40
40
|
<div className="flex min-h-[24px] items-center">
|
|
41
41
|
<div className="h-[20px] w-full animate-pulse rounded bg-ods-border"></div>
|
|
@@ -18,10 +18,10 @@ export function DetailPageSkeleton({
|
|
|
18
18
|
bare = false,
|
|
19
19
|
}: DetailPageSkeletonProps = {}) {
|
|
20
20
|
const content = (
|
|
21
|
-
<div className="animate-pulse space-y-6 md:space-y-10">
|
|
21
|
+
<div className="animate-pulse space-y-6 content-md:space-y-10">
|
|
22
22
|
{/* Title Block */}
|
|
23
23
|
<div className="flex w-full flex-col gap-6">
|
|
24
|
-
<div className="h-16 w-full max-w-3xl rounded bg-ods-card md:h-20"></div>
|
|
24
|
+
<div className="h-16 w-full max-w-3xl rounded bg-ods-card content-md:h-20"></div>
|
|
25
25
|
</div>
|
|
26
26
|
|
|
27
27
|
{/* Category Tags Skeleton */}
|
|
@@ -33,10 +33,13 @@ export function DetailPageSkeleton({
|
|
|
33
33
|
|
|
34
34
|
{/* Metadata Grid Skeleton */}
|
|
35
35
|
<div
|
|
36
|
-
className={`grid grid-cols-1 md:grid-cols-${metadataColumns} w-full overflow-hidden rounded-md border border-ods-border`}
|
|
36
|
+
className={`grid grid-cols-1 content-md:grid-cols-${metadataColumns} w-full overflow-hidden rounded-md border border-ods-border`}
|
|
37
37
|
>
|
|
38
38
|
{Array.from({ length: metadataColumns }).map((_, i) => (
|
|
39
|
-
<div
|
|
39
|
+
<div
|
|
40
|
+
key={i}
|
|
41
|
+
className="border-b border-ods-border bg-ods-card p-4 last:border-r-0 content-md:border-b-0 content-md:border-r"
|
|
42
|
+
>
|
|
40
43
|
<div className="mb-2 h-6 w-24 rounded bg-ods-border"></div>
|
|
41
44
|
<div className="h-5 w-20 rounded bg-ods-border"></div>
|
|
42
45
|
</div>
|
|
@@ -70,7 +73,10 @@ export function DetailPageSkeleton({
|
|
|
70
73
|
);
|
|
71
74
|
if (bare) return content;
|
|
72
75
|
return (
|
|
73
|
-
<PageLayout
|
|
76
|
+
<PageLayout
|
|
77
|
+
showHeader={false}
|
|
78
|
+
className="mx-auto max-w-[1280px] bg-ods-bg px-6 py-6 content-md:px-20 content-md:py-10"
|
|
79
|
+
>
|
|
74
80
|
{content}
|
|
75
81
|
</PageLayout>
|
|
76
82
|
);
|
|
@@ -48,10 +48,10 @@ export function DevCardRowContent({
|
|
|
48
48
|
rightBadges,
|
|
49
49
|
}: DevCardRowContentProps) {
|
|
50
50
|
return (
|
|
51
|
-
<div className="flex w-full flex-col items-start justify-between gap-[12px] md:flex-row md:gap-[16px]">
|
|
52
|
-
<div className="flex w-full min-w-0 flex-1 flex-col gap-[12px] md:w-auto md:gap-[16px]">
|
|
51
|
+
<div className="flex w-full flex-col items-start justify-between gap-[12px] content-md:flex-row content-md:gap-[16px]">
|
|
52
|
+
<div className="flex w-full min-w-0 flex-1 flex-col gap-[12px] content-md:w-auto content-md:gap-[16px]">
|
|
53
53
|
<div className="flex min-h-[24px] items-center">
|
|
54
|
-
<h3 className="line-clamp-2 flex-1 break-words tracking-[-0.36px] text-ods-text-primary text-h3 md:truncate">
|
|
54
|
+
<h3 className="line-clamp-2 flex-1 break-words tracking-[-0.36px] text-ods-text-primary text-h3 content-md:truncate">
|
|
55
55
|
{title}
|
|
56
56
|
</h3>
|
|
57
57
|
</div>
|
|
@@ -74,9 +74,9 @@ export function DevCardRowContent({
|
|
|
74
74
|
*/
|
|
75
75
|
export function DevCardRowSkeleton() {
|
|
76
76
|
return (
|
|
77
|
-
<div className="border-b border-ods-border p-[12px] last:border-b-0 md:p-[16px]">
|
|
78
|
-
<div className="flex w-full flex-col items-start justify-between gap-[12px] md:flex-row md:gap-[16px]">
|
|
79
|
-
<div className="flex w-full min-w-0 flex-1 flex-col gap-[12px] md:w-auto md:gap-[16px]">
|
|
77
|
+
<div className="border-b border-ods-border p-[12px] last:border-b-0 content-md:p-[16px]">
|
|
78
|
+
<div className="flex w-full flex-col items-start justify-between gap-[12px] content-md:flex-row content-md:gap-[16px]">
|
|
79
|
+
<div className="flex w-full min-w-0 flex-1 flex-col gap-[12px] content-md:w-auto content-md:gap-[16px]">
|
|
80
80
|
<div className="flex min-h-[24px] items-center">
|
|
81
81
|
<div className="h-[20px] w-full animate-pulse rounded bg-ods-border" />
|
|
82
82
|
</div>
|
|
@@ -136,7 +136,7 @@ export function DevSectionView({ sectionKey, hero, preControls, children, showHe
|
|
|
136
136
|
</div>
|
|
137
137
|
) : (
|
|
138
138
|
<div className="flex w-full items-center justify-between">
|
|
139
|
-
<h2 className="font-['Azeret_Mono'] text-[32px] font-semibold leading-[40px] tracking-[-0.64px] text-ods-text-primary md:text-[40px] md:leading-[48px] md:tracking-[-0.8px] lg:text-[48px] lg:leading-[56px] lg:tracking-[-0.96px]">
|
|
139
|
+
<h2 className="font-['Azeret_Mono'] text-[32px] font-semibold leading-[40px] tracking-[-0.64px] text-ods-text-primary content-md:text-[40px] content-md:leading-[48px] content-md:tracking-[-0.8px] content-lg:text-[48px] content-lg:leading-[56px] content-lg:tracking-[-0.96px]">
|
|
140
140
|
{section.hero.title}
|
|
141
141
|
<span className="text-ods-accent">:</span>
|
|
142
142
|
</h2>
|
|
@@ -31,8 +31,8 @@ export function ProductReleaseCardSkeleton({ className, size = 'lg' }: ProductRe
|
|
|
31
31
|
between skeleton state and loaded state. Without this,
|
|
32
32
|
individual placeholder heights underrun the loaded card's
|
|
33
33
|
min-h reservations and the page jumps on resolve. */}
|
|
34
|
-
<div className="flex flex-col gap-4 md:flex-row md:gap-6">
|
|
35
|
-
<div className="aspect-[1200/630] w-full flex-shrink-0 rounded-lg bg-ods-border md:w-[256px]" />
|
|
34
|
+
<div className="flex flex-col gap-4 content-md:flex-row content-md:gap-6">
|
|
35
|
+
<div className="aspect-[1200/630] w-full flex-shrink-0 rounded-lg bg-ods-border content-md:w-[256px]" />
|
|
36
36
|
<div className="flex min-w-0 flex-1 flex-col">
|
|
37
37
|
{/* Version pill — mirrors `flex items-center gap-3 mb-3` in
|
|
38
38
|
the loaded card. The loaded `<span text-lg>` renders at
|
|
@@ -43,16 +43,16 @@ export function ProductReleaseCardSkeleton({ className, size = 'lg' }: ProductRe
|
|
|
43
43
|
</div>
|
|
44
44
|
{/* Title container — SAME min-h as the loaded card so the
|
|
45
45
|
card height contributed by this region matches exactly. */}
|
|
46
|
-
<div className="mb-3 flex min-h-[60px] flex-col justify-start gap-1.5 md:min-h-[72px]">
|
|
47
|
-
<div className="h-[25px] w-3/4 rounded bg-ods-border md:h-[30px]" />
|
|
48
|
-
<div className="h-[25px] w-1/2 rounded bg-ods-border md:h-[30px]" />
|
|
46
|
+
<div className="mb-3 flex min-h-[60px] flex-col justify-start gap-1.5 content-md:min-h-[72px]">
|
|
47
|
+
<div className="h-[25px] w-3/4 rounded bg-ods-border content-md:h-[30px]" />
|
|
48
|
+
<div className="h-[25px] w-1/2 rounded bg-ods-border content-md:h-[30px]" />
|
|
49
49
|
</div>
|
|
50
50
|
{/* Summary container — SAME min-h as the loaded card. The
|
|
51
51
|
3 placeholder lines mirror the rendered 3-line clamp;
|
|
52
52
|
`bg-ods-border/70` keeps summary placeholders slightly
|
|
53
53
|
dimmer than title placeholders (primary vs secondary
|
|
54
54
|
text hierarchy). */}
|
|
55
|
-
<div className="flex min-h-[68px] flex-col justify-start gap-2 md:min-h-[78px]">
|
|
55
|
+
<div className="flex min-h-[68px] flex-col justify-start gap-2 content-md:min-h-[78px]">
|
|
56
56
|
<div className="h-3 w-full rounded bg-ods-border/70" />
|
|
57
57
|
<div className="h-3 w-11/12 rounded bg-ods-border/70" />
|
|
58
58
|
<div className="h-3 w-5/6 rounded bg-ods-border/70" />
|
|
@@ -70,11 +70,11 @@ export function ProductReleaseCardSkeleton({ className, size = 'lg' }: ProductRe
|
|
|
70
70
|
|
|
71
71
|
{/* METADATA GRID: 4-cell placeholder, the loaded cells' own boxes (no surface of
|
|
72
72
|
their own; bars on the page background). Inner heights mirror the loaded cells. */}
|
|
73
|
-
<div className="grid w-full grid-cols-1 overflow-hidden rounded-md border border-ods-border md:grid-cols-4">
|
|
73
|
+
<div className="grid w-full grid-cols-1 overflow-hidden rounded-md border border-ods-border content-md:grid-cols-4">
|
|
74
74
|
{[0, 1, 2].map(i => (
|
|
75
75
|
<div
|
|
76
76
|
key={`cell-${i}`}
|
|
77
|
-
className="flex flex-col gap-3 border-b border-ods-border p-4 md:border-b-0 md:border-r"
|
|
77
|
+
className="flex flex-col gap-3 border-b border-ods-border p-4 content-md:border-b-0 content-md:border-r"
|
|
78
78
|
>
|
|
79
79
|
<div className="flex flex-col gap-2">
|
|
80
80
|
<div className="h-7 w-24 rounded bg-ods-border" />
|
|
@@ -211,8 +211,8 @@ export function ProductReleaseCard({
|
|
|
211
211
|
// skeleton's shape matches the loaded card exactly. The earlier
|
|
212
212
|
// dynamic `gridColsClass` ternary collapsed missing cells and
|
|
213
213
|
// caused 28-56px reflow on resolve.
|
|
214
|
-
const gridColsClass = 'md:grid-cols-4';
|
|
215
|
-
const dividerClass = 'border-b md:border-b-0 md:border-r border-ods-border';
|
|
214
|
+
const gridColsClass = 'content-md:grid-cols-4';
|
|
215
|
+
const dividerClass = 'border-b content-md:border-b-0 content-md:border-r border-ods-border';
|
|
216
216
|
|
|
217
217
|
// THE shared card frame: interactive (accent border on hover) when the card
|
|
218
218
|
// links or clicks, the static frame when it does neither.
|
|
@@ -228,8 +228,8 @@ export function ProductReleaseCard({
|
|
|
228
228
|
const innerLayout = (
|
|
229
229
|
<>
|
|
230
230
|
{/* HERO ZONE — cover LEFT + version pill + title + summary RIGHT */}
|
|
231
|
-
<div className="flex flex-col gap-4 md:flex-row md:gap-6">
|
|
232
|
-
<div className="w-full flex-shrink-0 md:w-[256px]">
|
|
231
|
+
<div className="flex flex-col gap-4 content-md:flex-row content-md:gap-6">
|
|
232
|
+
<div className="w-full flex-shrink-0 content-md:w-[256px]">
|
|
233
233
|
<div className="relative aspect-[1200/630] w-full overflow-hidden rounded-lg bg-ods-bg">
|
|
234
234
|
{coverImage ? (
|
|
235
235
|
<Image
|
|
@@ -256,8 +256,8 @@ export function ProductReleaseCard({
|
|
|
256
256
|
1-line titles don't shrink and the catalog skeleton-to-
|
|
257
257
|
content transition is shift-free. Mirrors the
|
|
258
258
|
onboarding-guide catalog card. */}
|
|
259
|
-
<div className="mb-3 flex min-h-[60px] items-start md:min-h-[72px]">
|
|
260
|
-
<h3 className="line-clamp-2 font-['Azeret_Mono'] text-xl font-semibold leading-tight text-ods-text-primary md:text-2xl">
|
|
259
|
+
<div className="mb-3 flex min-h-[60px] items-start content-md:min-h-[72px]">
|
|
260
|
+
<h3 className="line-clamp-2 font-['Azeret_Mono'] text-xl font-semibold leading-tight text-ods-text-primary content-md:text-2xl">
|
|
261
261
|
{title}
|
|
262
262
|
</h3>
|
|
263
263
|
</div>
|
|
@@ -265,10 +265,10 @@ export function ProductReleaseCard({
|
|
|
265
265
|
summaries at 3 lines; `min-h` reserves the same vertical
|
|
266
266
|
space when content is shorter, so the catalog grid stays
|
|
267
267
|
row-consistent regardless of per-card content length.
|
|
268
|
-
Heights derived from text-sm md:text-base × leading-relaxed
|
|
268
|
+
Heights derived from text-sm content-md:text-base × leading-relaxed
|
|
269
269
|
(1.625): 14×1.625×3 ≈ 68 px mobile, 16×1.625×3 ≈ 78 px desktop. */}
|
|
270
|
-
<div className="min-h-[68px] md:min-h-[78px]">
|
|
271
|
-
<p className="line-clamp-3 font-['DM_Sans'] text-sm leading-relaxed text-ods-text-secondary md:text-base">
|
|
270
|
+
<div className="min-h-[68px] content-md:min-h-[78px]">
|
|
271
|
+
<p className="line-clamp-3 font-['DM_Sans'] text-sm leading-relaxed text-ods-text-secondary content-md:text-base">
|
|
272
272
|
{summary ?? ''}
|
|
273
273
|
</p>
|
|
274
274
|
</div>
|
|
@@ -231,7 +231,7 @@ export function ProductReleasesView({
|
|
|
231
231
|
</div>
|
|
232
232
|
|
|
233
233
|
{/* Pagination — always present at the bottom for consistent spacing. */}
|
|
234
|
-
<div className="mt-6 flex justify-center md:mt-8">
|
|
234
|
+
<div className="mt-6 flex justify-center content-md:mt-8">
|
|
235
235
|
{isLoading ? (
|
|
236
236
|
<div className="m-3 h-12 w-64" />
|
|
237
237
|
) : releases.length > 0 && totalPages > 1 ? (
|
|
@@ -333,14 +333,14 @@ export function ReleaseDetailPage({
|
|
|
333
333
|
titleWrap
|
|
334
334
|
backButton={showBackButton ? { label: backLabel, onClick: () => router.push(backHref) } : undefined}
|
|
335
335
|
>
|
|
336
|
-
<div className="space-y-6 md:space-y-8">
|
|
336
|
+
<div className="space-y-6 content-md:space-y-8">
|
|
337
337
|
{/* Tags — flat product_release_tags[] from entity_tags */}
|
|
338
338
|
<EntityTagBadges tags={release.product_release_tags as TagAssoc[] | undefined} />
|
|
339
339
|
|
|
340
340
|
{/* Metadata Grid */}
|
|
341
|
-
<div className="grid w-full grid-cols-1 overflow-hidden rounded-md border border-ods-border md:grid-cols-4">
|
|
341
|
+
<div className="grid w-full grid-cols-1 overflow-hidden rounded-md border border-ods-border content-md:grid-cols-4">
|
|
342
342
|
{/* Release Type */}
|
|
343
|
-
<div className="flex flex-col gap-3 border-b border-ods-border bg-ods-card p-4 md:border-b-0 md:border-r">
|
|
343
|
+
<div className="flex flex-col gap-3 border-b border-ods-border bg-ods-card p-4 content-md:border-b-0 content-md:border-r">
|
|
344
344
|
<div className="flex flex-col gap-0">
|
|
345
345
|
<p className="text-ods-text-primary text-h4">{releaseType.toLocaleUpperCase()}</p>
|
|
346
346
|
<p className="text-ods-text-secondary text-h6">Release Type</p>
|
|
@@ -348,7 +348,7 @@ export function ReleaseDetailPage({
|
|
|
348
348
|
</div>
|
|
349
349
|
|
|
350
350
|
{/* Release Status */}
|
|
351
|
-
<div className="flex flex-col gap-3 border-b border-ods-border bg-ods-card p-4 md:border-b-0 md:border-r">
|
|
351
|
+
<div className="flex flex-col gap-3 border-b border-ods-border bg-ods-card p-4 content-md:border-b-0 content-md:border-r">
|
|
352
352
|
<div className="flex flex-col gap-0">
|
|
353
353
|
<p className="text-ods-text-primary text-h4">{releaseStatus.toLocaleUpperCase()}</p>
|
|
354
354
|
<p className="text-ods-text-secondary text-h6">Release Status</p>
|
|
@@ -356,7 +356,7 @@ export function ReleaseDetailPage({
|
|
|
356
356
|
</div>
|
|
357
357
|
|
|
358
358
|
{/* Release Date */}
|
|
359
|
-
<div className="flex flex-col gap-3 border-b border-ods-border bg-ods-card p-4 md:border-b-0 md:border-r">
|
|
359
|
+
<div className="flex flex-col gap-3 border-b border-ods-border bg-ods-card p-4 content-md:border-b-0 content-md:border-r">
|
|
360
360
|
<div className="flex flex-col gap-0">
|
|
361
361
|
<p className="text-ods-text-primary text-h4">{formatReleaseDate(releaseDate)}</p>
|
|
362
362
|
<p className="text-ods-text-secondary text-h6">Release Date</p>
|