@krizaka/ui 3.53.0 → 3.55.0
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/README.md +1 -0
- package/dist/account-menu.d.ts +6 -2
- package/dist/account-menu.js +8 -1
- package/dist/account-menu.js.map +1 -1
- package/dist/account-settings.js +28 -88
- package/dist/account-settings.js.map +1 -1
- package/dist/anatomy-plate.d.ts +4 -1
- package/dist/anatomy-plate.js +4 -0
- package/dist/anatomy-plate.js.map +1 -1
- package/dist/app-header/search.d.ts +63 -0
- package/dist/app-header/search.js +70 -0
- package/dist/app-header/search.js.map +1 -0
- package/dist/app-header.d.ts +88 -4
- package/dist/app-header.js +63 -5
- package/dist/app-header.js.map +1 -1
- package/dist/app-shell.js +31 -40
- package/dist/app-shell.js.map +1 -1
- package/dist/argument-card.js +3 -3
- package/dist/bird-plate.d.ts +3 -1
- package/dist/bird-plate.js +2 -2
- package/dist/bird-plate.js.map +1 -1
- package/dist/blocks.css +202 -27
- package/dist/chapter.d.ts +20 -3
- package/dist/chapter.js +82 -31
- package/dist/chapter.js.map +1 -1
- package/dist/chat.js +2 -2
- package/dist/chat.js.map +1 -1
- package/dist/checkbox.js +1 -1
- package/dist/checkbox.js.map +1 -1
- package/dist/chip.js +5 -98
- package/dist/chip.js.map +1 -1
- package/dist/choice-grid.js +28 -88
- package/dist/choice-grid.js.map +1 -1
- package/dist/{chunk-YGIN2YIY.js → chunk-3X3OF4TI.js} +2 -2
- package/dist/chunk-3X3OF4TI.js.map +1 -0
- package/dist/chunk-5VEVYDVC.js +11 -0
- package/dist/chunk-5VEVYDVC.js.map +1 -0
- package/dist/chunk-7U5TXA5Q.js +105 -0
- package/dist/chunk-7U5TXA5Q.js.map +1 -0
- package/dist/{chunk-KBQD6HE3.js → chunk-AEP3K4YD.js} +3 -3
- package/dist/{chunk-KBQD6HE3.js.map → chunk-AEP3K4YD.js.map} +1 -1
- package/dist/chunk-BDIU5LZB.js +18 -0
- package/dist/chunk-BDIU5LZB.js.map +1 -0
- package/dist/chunk-CSRKYZ53.js +39 -0
- package/dist/chunk-CSRKYZ53.js.map +1 -0
- package/dist/chunk-HEKDKM5Y.js +128 -0
- package/dist/chunk-HEKDKM5Y.js.map +1 -0
- package/dist/chunk-KGLRZMCY.js +16 -0
- package/dist/chunk-KGLRZMCY.js.map +1 -0
- package/dist/chunk-KID32IRJ.js +18 -0
- package/dist/chunk-KID32IRJ.js.map +1 -0
- package/dist/{chunk-ZQW7LF4Y.js → chunk-OIMXKRXF.js} +2 -2
- package/dist/{chunk-YHKTYFOQ.js → chunk-QWKCNSPF.js} +271 -47
- package/dist/chunk-QWKCNSPF.js.map +1 -0
- package/dist/{chunk-4LYKBSOG.js → chunk-USPOIQV5.js} +2 -2
- package/dist/chunk-USPOIQV5.js.map +1 -0
- package/dist/{chunk-7NRSPHAF.js → chunk-VB7MWE2N.js} +2 -2
- package/dist/{chunk-7NRSPHAF.js.map → chunk-VB7MWE2N.js.map} +1 -1
- package/dist/chunk-VIPSZXO4.js +103 -0
- package/dist/chunk-VIPSZXO4.js.map +1 -0
- package/dist/chunk-YCZ7N6Q6.js +385 -0
- package/dist/chunk-YCZ7N6Q6.js.map +1 -0
- package/dist/code-block.js +81 -98
- package/dist/code-block.js.map +1 -1
- package/dist/combobox.js +2 -2
- package/dist/component-page.d.ts +4 -1
- package/dist/component-page.js +5 -0
- package/dist/component-page.js.map +1 -1
- package/dist/continue-prompt.d.ts +4 -1
- package/dist/continue-prompt.js +4 -0
- package/dist/continue-prompt.js.map +1 -1
- package/dist/data-table.d.ts +60 -4
- package/dist/data-table.js +19 -7
- package/dist/date-picker.js +2 -2
- package/dist/date-picker.js.map +1 -1
- package/dist/detail-layout.d.ts +28 -17
- package/dist/detail-layout.js +73 -16
- package/dist/detail-layout.js.map +1 -1
- package/dist/doodle.d.ts +21 -0
- package/dist/doodle.js +39 -0
- package/dist/doodle.js.map +1 -0
- package/dist/drop-overlay.d.ts +12 -4
- package/dist/drop-overlay.js +16 -6
- package/dist/drop-overlay.js.map +1 -1
- package/dist/elephant.d.ts +2 -0
- package/dist/elephant.js +4 -0
- package/dist/elephant.js.map +1 -1
- package/dist/field.js +1 -1
- package/dist/field.js.map +1 -1
- package/dist/filter-bar.js +2 -2
- package/dist/force-update-screen.d.ts +5 -1
- package/dist/force-update-screen.js +4 -0
- package/dist/force-update-screen.js.map +1 -1
- package/dist/gallery-page.d.ts +4 -1
- package/dist/gallery-page.js +4 -0
- package/dist/gallery-page.js.map +1 -1
- package/dist/genui.js +5 -5
- package/dist/image-cropper.d.ts +10 -2
- package/dist/image-cropper.js +4 -0
- package/dist/image-cropper.js.map +1 -1
- package/dist/kanban.d.ts +3 -0
- package/dist/kanban.js +4 -0
- package/dist/kanban.js.map +1 -1
- package/dist/landscape.d.ts +4 -1
- package/dist/landscape.js +4 -0
- package/dist/landscape.js.map +1 -1
- package/dist/list.js +1 -1
- package/dist/maintenance-screen.d.ts +5 -1
- package/dist/maintenance-screen.js +4 -0
- package/dist/maintenance-screen.js.map +1 -1
- package/dist/map.js +6 -0
- package/dist/map.js.map +1 -1
- package/dist/markdown.d.ts +5 -1
- package/dist/markdown.js +12 -5
- package/dist/markdown.js.map +1 -1
- package/dist/match-dialog.d.ts +4 -1
- package/dist/match-dialog.js +4 -0
- package/dist/match-dialog.js.map +1 -1
- package/dist/media-capture.d.ts +6 -1
- package/dist/media-capture.js +4 -0
- package/dist/media-capture.js.map +1 -1
- package/dist/meter.js +5 -121
- package/dist/meter.js.map +1 -1
- package/dist/native/map.js +6 -0
- package/dist/native/map.js.map +1 -1
- package/dist/native/markdown.d.ts +9 -2
- package/dist/native/markdown.js +46 -7
- package/dist/native/markdown.js.map +1 -1
- package/dist/native/media-capture.d.ts +1 -0
- package/dist/native/media-capture.js +5 -0
- package/dist/native/media-capture.js.map +1 -1
- package/dist/native/vertical-video.d.ts +8 -2
- package/dist/native/vertical-video.js +6 -0
- package/dist/native/vertical-video.js.map +1 -1
- package/dist/native.d.ts +94 -4
- package/dist/native.js +1236 -1110
- package/dist/native.js.map +1 -1
- package/dist/nav-bar/me.d.ts +32 -0
- package/dist/nav-bar/me.js +23 -0
- package/dist/nav-bar/me.js.map +1 -0
- package/dist/nav-bar.d.ts +60 -2
- package/dist/nav-bar.js +92 -48
- package/dist/nav-bar.js.map +1 -1
- package/dist/notification-preferences.d.ts +151 -0
- package/dist/notification-preferences.js +77 -0
- package/dist/notification-preferences.js.map +1 -0
- package/dist/page-header.js +1 -1
- package/dist/page-header.js.map +1 -1
- package/dist/pointer-light.js +5 -0
- package/dist/pointer-light.js.map +1 -1
- package/dist/product-page.d.ts +4 -1
- package/dist/product-page.js +4 -0
- package/dist/product-page.js.map +1 -1
- package/dist/profile-card.d.ts +119 -1
- package/dist/profile-card.js +68 -21
- package/dist/profile-card.js.map +1 -1
- package/dist/promise-card.d.ts +4 -1
- package/dist/promise-card.js +4 -0
- package/dist/promise-card.js.map +1 -1
- package/dist/quota-meter.d.ts +113 -0
- package/dist/quota-meter.js +58 -0
- package/dist/quota-meter.js.map +1 -0
- package/dist/radio-group.js +6 -8
- package/dist/radio-group.js.map +1 -1
- package/dist/rail-nav.d.ts +18 -2
- package/dist/rail-nav.js +25 -2
- package/dist/rail-nav.js.map +1 -1
- package/dist/retro-tv.d.ts +30 -0
- package/dist/retro-tv.js +124 -0
- package/dist/retro-tv.js.map +1 -0
- package/dist/router.d.ts +12 -1
- package/dist/router.js +85 -8
- package/dist/router.js.map +1 -1
- package/dist/scroll-stage.d.ts +5 -1
- package/dist/scroll-stage.js +5 -0
- package/dist/scroll-stage.js.map +1 -1
- package/dist/search-field.js +1 -1
- package/dist/showcase.d.ts +9 -2
- package/dist/showcase.js +5 -0
- package/dist/showcase.js.map +1 -1
- package/dist/signature.d.ts +19 -0
- package/dist/signature.js +39 -0
- package/dist/signature.js.map +1 -0
- package/dist/site-footer.js +1 -1
- package/dist/sites.js +1 -1
- package/dist/sites.js.map +1 -1
- package/dist/slot.d.ts +1 -1
- package/dist/stat.js +1 -1
- package/dist/stat.js.map +1 -1
- package/dist/status-page.d.ts +4 -1
- package/dist/status-page.js +5 -0
- package/dist/status-page.js.map +1 -1
- package/dist/stuck.d.ts +27 -0
- package/dist/stuck.js +26 -0
- package/dist/stuck.js.map +1 -0
- package/dist/swipe-deck.d.ts +9 -2
- package/dist/swipe-deck.js +5 -0
- package/dist/swipe-deck.js.map +1 -1
- package/dist/switch.js +4 -32
- package/dist/switch.js.map +1 -1
- package/dist/tabs.d.ts +47 -8
- package/dist/tabs.js +62 -12
- package/dist/tabs.js.map +1 -1
- package/dist/tag-input.js +2 -2
- package/dist/tag-selector.js +1 -1
- package/dist/terminal-replay.d.ts +4 -1
- package/dist/terminal-replay.js +4 -0
- package/dist/terminal-replay.js.map +1 -1
- package/dist/tree-view.js +4 -0
- package/dist/tree-view.js.map +1 -1
- package/dist/update-prompt.d.ts +5 -1
- package/dist/update-prompt.js +4 -0
- package/dist/update-prompt.js.map +1 -1
- package/dist/video-player.js +7 -376
- package/dist/video-player.js.map +1 -1
- package/dist/video-trimmer.d.ts +6 -1
- package/dist/video-trimmer.js +4 -0
- package/dist/video-trimmer.js.map +1 -1
- package/package.json +143 -31
- package/registry/account-menu.json +9 -8
- package/registry/account-settings.json +2 -2
- package/registry/achievement.json +3 -4
- package/registry/action-bar.json +3 -3
- package/registry/action-sheet.json +1 -0
- package/registry/activity-feed.json +2 -1
- package/registry/agenda.json +1 -1
- package/registry/alert.json +1 -1
- package/registry/anatomy-plate.json +13 -5
- package/registry/app-header.json +470 -23
- package/registry/app-shell.json +2 -2
- package/registry/argument-card.json +1 -1
- package/registry/audio-player.json +1 -1
- package/registry/auth-layout.json +1 -1
- package/registry/avatar.json +3 -2
- package/registry/badge.json +2 -1
- package/registry/bird-plate.json +16 -2
- package/registry/button.json +7 -6
- package/registry/captcha.json +1 -1
- package/registry/card-grid.json +1 -1
- package/registry/card.json +7 -6
- package/registry/catalogue-page.json +3 -4
- package/registry/changelog-page.json +3 -5
- package/registry/chapter.json +46 -10
- package/registry/chat.json +3 -2
- package/registry/checkbox.json +4 -3
- package/registry/chip-link.json +2 -2
- package/registry/chip-row.json +2 -2
- package/registry/chip.json +3 -2
- package/registry/choice-grid.json +3 -4
- package/registry/code-block.json +1 -1
- package/registry/combobox.json +3 -2
- package/registry/command.json +2 -2
- package/registry/comparison-table.json +1 -1
- package/registry/component-page.json +14 -5
- package/registry/confirm-button.json +4 -4
- package/registry/contact-page.json +1 -1
- package/registry/continue-prompt.json +13 -5
- package/registry/countdown.json +4 -3
- package/registry/cover-hero.json +3 -4
- package/registry/cta-panel.json +1 -1
- package/registry/dashboard-layout.json +1 -1
- package/registry/data-table.json +63 -7
- package/registry/date-picker.json +5 -4
- package/registry/decision-list.json +1 -1
- package/registry/description-list.json +2 -1
- package/registry/detail-layout.json +30 -6
- package/registry/dialog.json +7 -6
- package/registry/docs-article-page.json +3 -4
- package/registry/docs-hub-page.json +1 -1
- package/registry/docs-layout.json +1 -1
- package/registry/docs-nav.json +1 -1
- package/registry/document-viewer.json +3 -5
- package/registry/doodle.json +209 -0
- package/registry/drop-overlay.json +30 -16
- package/registry/dropdown-menu.json +2 -2
- package/registry/elephant.json +15 -7
- package/registry/empty-state.json +2 -1
- package/registry/engine-flow.json +1 -1
- package/registry/error-page.json +3 -5
- package/registry/examples/app-header/family.d.ts +4 -0
- package/registry/examples/app-header/family.js +41 -0
- package/registry/examples/app-header/family.tsx +37 -0
- package/registry/examples/app-header/native/family.d.ts +4 -0
- package/registry/examples/app-header/native/family.dark.png +0 -0
- package/registry/examples/app-header/native/family.js +25 -0
- package/registry/examples/app-header/native/family.light.png +0 -0
- package/registry/examples/app-header/native/family.tsx +33 -0
- package/registry/examples/chapter/album.d.ts +4 -0
- package/registry/examples/chapter/album.js +34 -0
- package/registry/examples/chapter/album.tsx +30 -0
- package/registry/examples/chapter/in-ruled-page.d.ts +4 -0
- package/registry/examples/chapter/in-ruled-page.js +23 -0
- package/registry/examples/chapter/in-ruled-page.tsx +19 -0
- package/registry/examples/data-table/applicant-files.d.ts +4 -0
- package/registry/examples/data-table/applicant-files.js +24 -0
- package/registry/examples/data-table/applicant-files.tsx +23 -0
- package/registry/examples/data-table/checks.d.ts +4 -0
- package/registry/examples/data-table/checks.js +25 -0
- package/registry/examples/data-table/checks.tsx +24 -0
- package/registry/examples/detail-layout/profile.d.ts +4 -0
- package/registry/examples/detail-layout/profile.js +26 -0
- package/registry/examples/detail-layout/profile.tsx +21 -0
- package/registry/examples/doodle/kinds.d.ts +4 -0
- package/registry/examples/doodle/kinds.js +17 -0
- package/registry/examples/doodle/kinds.tsx +14 -0
- package/registry/examples/doodle/margin.d.ts +4 -0
- package/registry/examples/doodle/margin.js +17 -0
- package/registry/examples/doodle/margin.tsx +17 -0
- package/registry/examples/markdown/native/basic.dark.png +0 -0
- package/registry/examples/markdown/native/basic.light.png +0 -0
- package/registry/examples/markdown/native/wide-table.d.ts +4 -0
- package/registry/examples/markdown/native/wide-table.dark.png +0 -0
- package/registry/examples/markdown/native/wide-table.js +23 -0
- package/registry/examples/markdown/native/wide-table.light.png +0 -0
- package/registry/examples/markdown/native/wide-table.tsx +22 -0
- package/registry/examples/markdown/wide-table.d.ts +4 -0
- package/registry/examples/markdown/wide-table.js +28 -0
- package/registry/examples/markdown/wide-table.tsx +23 -0
- package/registry/examples/nav-bar/family-product.js +2 -4
- package/registry/examples/nav-bar/family-product.tsx +3 -5
- package/registry/examples/notification-preferences/default.d.ts +4 -0
- package/registry/examples/notification-preferences/default.js +29 -0
- package/registry/examples/notification-preferences/default.tsx +28 -0
- package/registry/examples/profile-card/banner.d.ts +4 -0
- package/registry/examples/profile-card/banner.js +28 -0
- package/registry/examples/profile-card/banner.tsx +24 -0
- package/registry/examples/profile-card/native/banner.d.ts +4 -0
- package/registry/examples/profile-card/native/banner.dark.png +0 -0
- package/registry/examples/profile-card/native/banner.js +22 -0
- package/registry/examples/profile-card/native/banner.light.png +0 -0
- package/registry/examples/profile-card/native/banner.tsx +18 -0
- package/registry/examples/quota-meter/default.d.ts +4 -0
- package/registry/examples/quota-meter/default.js +26 -0
- package/registry/examples/quota-meter/default.tsx +20 -0
- package/registry/examples/quota-meter/full.d.ts +4 -0
- package/registry/examples/quota-meter/full.js +25 -0
- package/registry/examples/quota-meter/full.tsx +18 -0
- package/registry/examples/rail-nav/shortcuts.d.ts +4 -0
- package/registry/examples/rail-nav/shortcuts.js +49 -0
- package/registry/examples/rail-nav/shortcuts.tsx +40 -0
- package/registry/examples/retro-tv/cabinet.d.ts +4 -0
- package/registry/examples/retro-tv/cabinet.js +12 -0
- package/registry/examples/retro-tv/cabinet.tsx +7 -0
- package/registry/examples/retro-tv/portable.d.ts +4 -0
- package/registry/examples/retro-tv/portable.js +12 -0
- package/registry/examples/retro-tv/portable.tsx +7 -0
- package/registry/examples/signature/both.d.ts +4 -0
- package/registry/examples/signature/both.js +11 -0
- package/registry/examples/signature/both.tsx +6 -0
- package/registry/examples/signature/variants.d.ts +4 -0
- package/registry/examples/signature/variants.js +14 -0
- package/registry/examples/signature/variants.tsx +11 -0
- package/registry/examples/stuck/card.d.ts +4 -0
- package/registry/examples/stuck/card.js +13 -0
- package/registry/examples/stuck/card.tsx +14 -0
- package/registry/examples/stuck/holds.d.ts +4 -0
- package/registry/examples/stuck/holds.js +12 -0
- package/registry/examples/stuck/holds.tsx +16 -0
- package/registry/examples/stuck/tag.d.ts +4 -0
- package/registry/examples/stuck/tag.js +12 -0
- package/registry/examples/stuck/tag.tsx +13 -0
- package/registry/examples/tabs/binder.d.ts +4 -0
- package/registry/examples/tabs/binder.js +22 -0
- package/registry/examples/tabs/binder.tsx +20 -0
- package/registry/examples/tabs/native/binder.d.ts +4 -0
- package/registry/examples/tabs/native/binder.dark.png +0 -0
- package/registry/examples/tabs/native/binder.js +26 -0
- package/registry/examples/tabs/native/binder.light.png +0 -0
- package/registry/examples/tabs/native/binder.tsx +22 -0
- package/registry/fab.json +1 -0
- package/registry/field.json +3 -2
- package/registry/file-upload.json +6 -11
- package/registry/filter-bar.json +2 -2
- package/registry/flock.json +3 -4
- package/registry/footnote.json +2 -3
- package/registry/force-update-screen.json +14 -5
- package/registry/form.json +5 -4
- package/registry/gallery-page.json +14 -5
- package/registry/genui.json +2 -2
- package/registry/glossary-term.json +1 -1
- package/registry/glossary.json +1 -1
- package/registry/glow-card.json +2 -2
- package/registry/goal-gauge.json +2 -1
- package/registry/header.json +1 -0
- package/registry/how-it-works-page.json +3 -5
- package/registry/image-cropper.json +13 -5
- package/registry/image-viewer.json +3 -4
- package/registry/index.json +306 -91
- package/registry/iso.json +1 -1
- package/registry/kanban.json +13 -5
- package/registry/kbd.json +1 -1
- package/registry/kpi-explained.json +2 -2
- package/registry/landing-page.json +3 -5
- package/registry/landscape.json +13 -5
- package/registry/language-switch.json +1 -1
- package/registry/legal-page.json +1 -1
- package/registry/link-card.json +1 -1
- package/registry/list.json +2 -1
- package/registry/loader.json +2 -1
- package/registry/longform-page.json +1 -1
- package/registry/maintenance-screen.json +14 -5
- package/registry/map.json +3 -2
- package/registry/markdown.json +86 -9
- package/registry/marketing-layout.json +1 -1
- package/registry/marquee.json +1 -1
- package/registry/mascot-quote.json +1 -1
- package/registry/mascot-state.json +12 -11
- package/registry/mascots.json +2 -1
- package/registry/match-dialog.json +14 -5
- package/registry/media-capture.json +14 -5
- package/registry/media-gallery.json +3 -5
- package/registry/media-grid.json +1 -1
- package/registry/media-picker.json +2 -3
- package/registry/meta-row.json +2 -1
- package/registry/meter.json +4 -3
- package/registry/nav-bar.json +100 -9
- package/registry/next-actions.json +1 -1
- package/registry/notification-preferences.json +250 -0
- package/registry/number-ticker.json +2 -1
- package/registry/onboarding.json +1 -0
- package/registry/opening-hours.json +1 -1
- package/registry/otp-input.json +1 -0
- package/registry/page-header.json +2 -2
- package/registry/page-hero.json +1 -1
- package/registry/page-index.json +1 -1
- package/registry/page.json +3 -3
- package/registry/pagination.json +2 -1
- package/registry/permission-prompt.json +2 -2
- package/registry/photo-grid.json +4 -4
- package/registry/pointer-light.json +6 -2
- package/registry/popover.json +2 -2
- package/registry/pricing-page.json +4 -6
- package/registry/product-page.json +14 -5
- package/registry/profile-card.json +232 -8
- package/registry/progress.json +4 -3
- package/registry/promise-card.json +13 -5
- package/registry/pull-to-refresh.json +1 -0
- package/registry/quota-meter.json +308 -0
- package/registry/radio-group.json +4 -3
- package/registry/rail-nav.json +48 -10
- package/registry/recipes.json +1 -1
- package/registry/retro-tv.json +478 -0
- package/registry/router.json +3 -3
- package/registry/screen.json +1 -0
- package/registry/scroll-stage.json +14 -5
- package/registry/search-field.json +3 -2
- package/registry/section-backdrop.json +1 -1
- package/registry/segmented.json +1 -0
- package/registry/separator.json +2 -2
- package/registry/service-card.json +1 -1
- package/registry/settings-layout.json +2 -1
- package/registry/showcase.json +14 -5
- package/registry/signature.json +185 -0
- package/registry/site-footer.json +3 -3
- package/registry/sites.json +2 -2
- package/registry/skeleton.json +2 -1
- package/registry/skip-link.json +1 -1
- package/registry/slider.json +5 -4
- package/registry/slot-picker.json +1 -1
- package/registry/slot.json +2 -2
- package/registry/specimen-tag.json +1 -1
- package/registry/spinner.json +4 -3
- package/registry/splash-handoff.json +1 -0
- package/registry/split-view.json +1 -1
- package/registry/stage-banner.json +2 -3
- package/registry/stat.json +2 -2
- package/registry/status-page.json +13 -5
- package/registry/step-progress.json +2 -1
- package/registry/step-showcase.json +3 -5
- package/registry/stepper.json +2 -1
- package/registry/sticky-action.json +1 -1
- package/registry/stuck.json +219 -0
- package/registry/swipe-deck.json +14 -5
- package/registry/swipeable-row.json +1 -0
- package/registry/switch.json +3 -2
- package/registry/tab-bar.json +1 -0
- package/registry/tabs.json +154 -22
- package/registry/tag-input.json +1 -1
- package/registry/tag-selector.json +2 -1
- package/registry/taped-photo.json +1 -1
- package/registry/terminal-replay.json +14 -5
- package/registry/text-style.json +5 -5
- package/registry/theme.json +2 -1
- package/registry/toast.json +2 -1
- package/registry/toc.json +1 -1
- package/registry/tooltip.json +2 -2
- package/registry/tree-view.json +6 -2
- package/registry/txt.json +1 -0
- package/registry/update-prompt.json +14 -5
- package/registry/upload-queue.json +1 -1
- package/registry/verified-badge.json +6 -6
- package/registry/vertical-video.json +8 -3
- package/registry/video-player.json +5 -6
- package/registry/video-trimmer.json +13 -5
- package/registry/virtual.json +2 -2
- package/registry/waveform.json +4 -3
- package/dist/chunk-4LYKBSOG.js.map +0 -1
- package/dist/chunk-HHFWK2HH.js +0 -163
- package/dist/chunk-HHFWK2HH.js.map +0 -1
- package/dist/chunk-R3LTSMS5.js +0 -75
- package/dist/chunk-R3LTSMS5.js.map +0 -1
- package/dist/chunk-YGIN2YIY.js.map +0 -1
- package/dist/chunk-YHKTYFOQ.js.map +0 -1
- /package/dist/{chunk-ZQW7LF4Y.js.map → chunk-OIMXKRXF.js.map} +0 -0
package/dist/markdown.d.ts
CHANGED
|
@@ -22,6 +22,10 @@ type MarkdownProps = Omit<React.ComponentProps<"div">, "children"> & {
|
|
|
22
22
|
children: string;
|
|
23
23
|
/** The text is still arriving: the unfinished end is closed and a caret marks the end. */
|
|
24
24
|
streaming?: boolean;
|
|
25
|
+
/** Marks a generated document with a quiet chip, when `generatedLabel` is supplied. */
|
|
26
|
+
generated?: boolean;
|
|
27
|
+
/** The chip's words, passed translated by the host; no chip is rendered without them. */
|
|
28
|
+
generatedLabel?: string;
|
|
25
29
|
/** Gives every heading an `id`, for `#section` links. */
|
|
26
30
|
headingIds?: boolean;
|
|
27
31
|
/** The words of the copy button of the code blocks, passed translated. Without them a code block has no copy button. */
|
|
@@ -33,6 +37,6 @@ type MarkdownProps = Omit<React.ComponentProps<"div">, "children"> & {
|
|
|
33
37
|
linkAs?: React.ElementType<MarkdownLinkProps>;
|
|
34
38
|
};
|
|
35
39
|
/** A Markdown text on the tokens: headings, paragraphs, lists (and tasks), quotes, tables, code, links, images, footnotes. */
|
|
36
|
-
declare function Markdown({ children, streaming, headingIds, codeLabels, linkAs, className, ...props }: MarkdownProps): React.JSX.Element;
|
|
40
|
+
declare function Markdown({ children, streaming, generated, generatedLabel, headingIds, codeLabels, linkAs, className, ...props }: MarkdownProps): React.JSX.Element;
|
|
37
41
|
|
|
38
42
|
export { Markdown, type MarkdownLinkProps, type MarkdownProps, type ParseOptions, healStreaming, parseSource };
|
package/dist/markdown.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/markdown/markdown.tsx
|
|
2
|
+
import { Badge } from "@krizaka/ui/badge";
|
|
2
3
|
import { CodeBlock } from "@krizaka/ui/code-block";
|
|
3
4
|
|
|
4
5
|
// src/cn.ts
|
|
@@ -49,6 +50,9 @@ function healStreaming(source) {
|
|
|
49
50
|
function parseSource(source, { streaming = false, headingIds = false } = {}) {
|
|
50
51
|
return parseMarkdown(streaming ? healStreaming(source) : source, { headingIds, ...streaming && { extensions: [streamingMarkdownExtension()] } }).children;
|
|
51
52
|
}
|
|
53
|
+
function textOf(nodes) {
|
|
54
|
+
return nodes.map((node) => node.children ? textOf(node.children) : node.value ?? "").join("");
|
|
55
|
+
}
|
|
52
56
|
|
|
53
57
|
// src/markdown/markdown.tsx
|
|
54
58
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -121,9 +125,9 @@ function Block({ node, ctx }) {
|
|
|
121
125
|
case "blockquote":
|
|
122
126
|
return /* @__PURE__ */ jsx("blockquote", { children: /* @__PURE__ */ jsx(Blocks, { nodes: node.children, ctx }) });
|
|
123
127
|
case "table":
|
|
124
|
-
return /* @__PURE__ */ jsx("div", { className: "kz-md-table", children: /* @__PURE__ */ jsxs("table", { children: [
|
|
125
|
-
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: node.header.map((cell, i) => /* @__PURE__ */ jsx("th", { scope: "col", style: { textAlign: node.align[i] ?? void 0 }, children: /* @__PURE__ */ jsx(Inlines, { nodes: cell.children, ctx }) }, i)) }) }),
|
|
126
|
-
/* @__PURE__ */ jsx("tbody", { children: node.rows.map((row, r) => /* @__PURE__ */ jsx("tr", { children: row.map((cell, i) => /* @__PURE__ */ jsx("td", { style: { textAlign: node.align[i] ?? void 0 }, children: /* @__PURE__ */ jsx(Inlines, { nodes: cell.children, ctx }) }, i)) }, r)) })
|
|
128
|
+
return /* @__PURE__ */ jsx("div", { className: "kz-md-table", role: "region", tabIndex: 0, "aria-label": node.header.map((cell) => textOf(cell.children)).join(", "), children: /* @__PURE__ */ jsxs("table", { children: [
|
|
129
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: node.header.map((cell, i) => /* @__PURE__ */ jsx("th", { scope: "col", style: { textAlign: node.align[i] ?? void 0 }, children: /* @__PURE__ */ jsx("div", { className: "kz-md-cell", children: /* @__PURE__ */ jsx(Inlines, { nodes: cell.children, ctx }) }) }, i)) }) }),
|
|
130
|
+
/* @__PURE__ */ jsx("tbody", { children: node.rows.map((row, r) => /* @__PURE__ */ jsx("tr", { children: row.map((cell, i) => /* @__PURE__ */ jsx("td", { style: { textAlign: node.align[i] ?? void 0 }, children: /* @__PURE__ */ jsx("div", { className: "kz-md-cell", children: /* @__PURE__ */ jsx(Inlines, { nodes: cell.children, ctx }) }) }, i)) }, r)) })
|
|
127
131
|
] }) });
|
|
128
132
|
case "thematicBreak":
|
|
129
133
|
return /* @__PURE__ */ jsx("hr", {});
|
|
@@ -139,9 +143,12 @@ function Block({ node, ctx }) {
|
|
|
139
143
|
function ListItem({ blocks, ctx }) {
|
|
140
144
|
return blocks.map((block, i) => block.type === "paragraph" && blocks.length === 1 ? /* @__PURE__ */ jsx(Inlines, { nodes: block.children, ctx }, i) : /* @__PURE__ */ jsx(Block, { node: block, ctx }, i));
|
|
141
145
|
}
|
|
142
|
-
function Markdown({ children, streaming, headingIds, codeLabels, linkAs, className, ...props }) {
|
|
146
|
+
function Markdown({ children, streaming, generated, generatedLabel, headingIds, codeLabels, linkAs, className, ...props }) {
|
|
143
147
|
const blocks = parseSource(children, { streaming, headingIds });
|
|
144
|
-
return /* @__PURE__ */
|
|
148
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("kz-md", className), "data-streaming": streaming ? "" : void 0, "aria-busy": streaming || void 0, ...props, children: [
|
|
149
|
+
generated && generatedLabel ? /* @__PURE__ */ jsx(Badge, { children: generatedLabel }) : null,
|
|
150
|
+
/* @__PURE__ */ jsx(Blocks, { nodes: blocks, ctx: { codeLabels, linkAs } })
|
|
151
|
+
] });
|
|
145
152
|
}
|
|
146
153
|
export {
|
|
147
154
|
Markdown,
|
package/dist/markdown.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/markdown/markdown.tsx","../src/cn.ts","../src/markdown/parse.ts"],"sourcesContent":["// Server-safe (no hook, no context): a Markdown text rendered on the tokens, from TanStack Markdown's AST. Raw HTML is\n// never enabled (it is shown as text), executable URLs are dropped, links to another site carry `rel=\"noopener\n// noreferrer\"`, and every fenced block is a `CodeBlock` (the same grammars, the same colours). `streaming` is for a\n// text that arrives in pieces — an AI answer: the unfinished end is closed and a caret marks where it goes on.\nimport { CodeBlock } from \"@krizaka/ui/code-block\";\nimport * as React from \"react\";\n\nimport { cn } from \"../cn\";\nimport { type BlockNode, type InlineNode, parseSource, textOf } from \"./parse\";\n\nexport type MarkdownLinkProps = React.ComponentProps<\"a\">;\n\nexport type MarkdownProps = Omit<React.ComponentProps<\"div\">, \"children\"> & {\n /** The Markdown source. */\n children: string;\n /** The text is still arriving: the unfinished end is closed and a caret marks the end. */\n streaming?: boolean;\n /** Gives every heading an `id`, for `#section` links. */\n headingIds?: boolean;\n /** The words of the copy button of the code blocks, passed translated. Without them a code block has no copy button. */\n codeLabels?: { copy: string; copied: string };\n /** Your router's link (Next's `Link`) for the links that stay on the site (`/docs`, `#section`); the others are plain `a`. */\n linkAs?: React.ElementType<MarkdownLinkProps>;\n};\n\ntype Context = Pick<MarkdownProps, \"codeLabels\" | \"linkAs\">;\n\nconst external = (href: string) => /^[a-z][a-z0-9+.-]*:/i.test(href);\n\nfunction Inlines({ nodes, ctx }: { nodes: readonly InlineNode[]; ctx: Context }) {\n return nodes.map((node, i) => <Inline key={i} node={node} ctx={ctx} />);\n}\n\nfunction Inline({ node, ctx }: { node: InlineNode; ctx: Context }): React.ReactNode {\n switch (node.type) {\n case \"text\":\n return node.value;\n case \"inlineCode\":\n return <code>{node.value}</code>;\n case \"strong\":\n return (\n <strong>\n <Inlines nodes={node.children} ctx={ctx} />\n </strong>\n );\n case \"emphasis\":\n return (\n <em>\n <Inlines nodes={node.children} ctx={ctx} />\n </em>\n );\n case \"strike\":\n return (\n <del>\n <Inlines nodes={node.children} ctx={ctx} />\n </del>\n );\n case \"break\":\n return <br />;\n case \"link\": {\n const Link = external(node.href) ? \"a\" : (ctx.linkAs ?? \"a\");\n const props = external(node.href) && !node.href.startsWith(\"mailto:\") && !node.href.startsWith(\"tel:\") ? { rel: \"noopener noreferrer\" } : {};\n return (\n <Link href={node.href} title={node.title} {...props}>\n <Inlines nodes={node.children} ctx={ctx} />\n </Link>\n );\n }\n case \"image\":\n return <img src={node.src} alt={node.alt} title={node.title} loading=\"lazy\" />;\n case \"footnoteReference\":\n return (\n <sup>\n <a id={`kz-fnref-${node.id}`} href={`#kz-fn-${node.id}`} data-footnote-ref=\"\">\n {node.number}\n </a>\n </sup>\n );\n // Raw HTML is never enabled by the parser: it arrives as text. A node of this kind is shown as text too.\n case \"inlineHtml\":\n return node.value;\n default:\n return null;\n }\n}\n\nfunction Blocks({ nodes, ctx }: { nodes: readonly BlockNode[]; ctx: Context }) {\n return nodes.map((node, i) => <Block key={i} node={node} ctx={ctx} />);\n}\n\nfunction Block({ node, ctx }: { node: BlockNode; ctx: Context }): React.ReactNode {\n switch (node.type) {\n case \"heading\": {\n const Tag = `h${node.depth}` as const;\n return (\n <Tag id={node.id}>\n <Inlines nodes={node.children} ctx={ctx} />\n </Tag>\n );\n }\n case \"paragraph\":\n return (\n <p>\n <Inlines nodes={node.children} ctx={ctx} />\n </p>\n );\n case \"code\":\n return (\n <CodeBlock\n code={node.value}\n language={node.lang ?? \"text\"}\n file={node.title ?? node.file}\n copyLabel={ctx.codeLabels?.copy}\n copiedLabel={ctx.codeLabels?.copied}\n annotations={node.highlightLines?.map((line) => ({ lines: line, kind: \"highlight\" as const }))}\n />\n );\n case \"list\": {\n const Tag = node.ordered ? \"ol\" : \"ul\";\n const tasks = node.items.some((item) => item.checked !== undefined);\n return (\n <Tag start={node.ordered && node.start !== undefined && node.start !== 1 ? node.start : undefined} data-tasks={tasks ? \"\" : undefined}>\n {node.items.map((item, i) => (\n <li key={i}>\n {item.checked !== undefined ? (\n <label>\n <input type=\"checkbox\" checked={item.checked} disabled readOnly />{\" \"}\n <ListItem blocks={item.children} ctx={ctx} />\n </label>\n ) : (\n <ListItem blocks={item.children} ctx={ctx} />\n )}\n </li>\n ))}\n </Tag>\n );\n }\n case \"blockquote\":\n return (\n <blockquote>\n <Blocks nodes={node.children} ctx={ctx} />\n </blockquote>\n );\n case \"table\":\n return (\n <div className=\"kz-md-table\">\n <table>\n <thead>\n <tr>\n {node.header.map((cell, i) => (\n <th key={i} scope=\"col\" style={{ textAlign: node.align[i] ?? undefined }}>\n <Inlines nodes={cell.children} ctx={ctx} />\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {node.rows.map((row, r) => (\n <tr key={r}>\n {row.map((cell, i) => (\n <td key={i} style={{ textAlign: node.align[i] ?? undefined }}>\n <Inlines nodes={cell.children} ctx={ctx} />\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n case \"thematicBreak\":\n return <hr />;\n case \"footnotes\":\n return (\n <section data-footnotes=\"\">\n <ol>\n {node.items.map((item) => (\n <li key={item.id} id={`kz-fn-${item.id}`}>\n <Blocks nodes={item.children} ctx={ctx} />\n </li>\n ))}\n </ol>\n </section>\n );\n // Raw HTML (never enabled) arrives as a paragraph of text; extensions are not enabled either. Shown as text.\n case \"html\":\n return <p>{node.value}</p>;\n default:\n return null;\n }\n}\n\n/** A list item: a lone paragraph is inline (a tight list), anything else keeps its blocks. */\nfunction ListItem({ blocks, ctx }: { blocks: readonly BlockNode[]; ctx: Context }) {\n return blocks.map((block, i) => (block.type === \"paragraph\" && blocks.length === 1 ? <Inlines key={i} nodes={block.children} ctx={ctx} /> : <Block key={i} node={block} ctx={ctx} />));\n}\n\n/** A Markdown text on the tokens: headings, paragraphs, lists (and tasks), quotes, tables, code, links, images, footnotes. */\nexport function Markdown({ children, streaming, headingIds, codeLabels, linkAs, className, ...props }: MarkdownProps) {\n const blocks = parseSource(children, { streaming, headingIds });\n return (\n <div className={cn(\"kz-md\", className)} data-streaming={streaming ? \"\" : undefined} aria-busy={streaming || undefined} {...props}>\n <Blocks nodes={blocks} ctx={{ codeLabels, linkAs }} />\n </div>\n );\n}\n\nexport { textOf };\n","import { type ClassValue, clsx } from \"clsx\";\nimport { cnMerge, type TWMergeConfig } from \"tailwind-variants\";\n\nexport type { ClassValue };\n\n/**\n * The preset's own utilities, told to the merge engine: the material (BRAND.md §6: `bg-sheen`, `bg-fill-shade`,\n * `bg-halo` are background images; `shadow-raised` · `-floating` · `-overlay` · `-glow` are shadows; `inset-shadow-edge`\n * · `-edge-fill` · `-well` are inset shadows). Unknown, tailwind-merge would take `bg-sheen` for a colour and drop the\n * `bg-surface-2` it lies on.\n */\nexport const twMergeConfig: TWMergeConfig = {\n extend: {\n classGroups: {\n \"bg-image\": [\"bg-sheen\", \"bg-fill-shade\", \"bg-halo\"],\n shadow: [\"shadow-raised\", \"shadow-floating\", \"shadow-overlay\", \"shadow-glow\", \"shadow-glow-lg\"],\n \"inset-shadow\": [\"inset-shadow-edge\", \"inset-shadow-edge-fill\", \"inset-shadow-well\"],\n },\n },\n};\n\nconst mergeConfig = { twMergeConfig };\n\n/**\n * Merges Tailwind classes: the last one wins — a product's `className` overrides the primitive's. The merge engine is\n * tailwind-merge as tailwind-variants ships it (the one `tv` uses), so the platform carries a single copy of it.\n * tailwind-variants bundles that engine since 3.3 (it never imports the `tailwind-merge` package, an optional peer):\n * hence `tailwind-variants ^3.3.1` and no `tailwind-merge` dependency — `scripts/consumer.mjs` checks it on a clean\n * install of the packed package.\n */\nexport function cn(...inputs: ClassValue[]): string {\n return cnMerge(clsx(inputs))(mergeConfig) ?? \"\";\n}\n\n","// The shared half of Markdown, web and React Native: the source becomes TanStack Markdown's serializable AST\n// (`@tanstack/markdown/parser`) — raw HTML is never enabled (it stays text), executable URLs are dropped (`javascript:`,\n// `data:`), and a text that is still arriving is healed so the half-written end reads like the finished one.\nimport type { BlockNode } from \"@tanstack/markdown\";\nimport { streamingMarkdownExtension } from \"@tanstack/markdown/extensions/streaming\";\nimport { parseMarkdown } from \"@tanstack/markdown/parser\";\n\nexport type { BlockNode, InlineNode, ListItemNode, TableNode } from \"@tanstack/markdown\";\n\nexport type ParseOptions = {\n /** The text is still arriving (an AI answer): the unfinished end is closed, so it never shows `**` or a half link. */\n streaming?: boolean;\n /** Gives every heading an `id` (`#section` links, a table of contents). */\n headingIds?: boolean;\n};\n\nconst FENCE = /^ {0,3}(```|~~~)/gm;\n\n/** A link or image cut at its end: `[label`, `[label]`, `[label](http…` become `label`, an image is dropped. Linear: one scan of the last `[`. */\nfunction healLink(text: string): string {\n const open = text.lastIndexOf(\"[\");\n if (open < 0) return text;\n const rest = text.slice(open + 1);\n if (rest.includes(\"\\n\")) return text;\n const image = open > 0 && text[open - 1] === \"!\";\n const close = rest.indexOf(\"]\");\n const after = close < 0 ? \"\" : rest.slice(close + 1);\n const cut = close < 0 || after === \"\" || (after.startsWith(\"(\") && !after.includes(\")\"));\n if (!cut) return text;\n const head = text.slice(0, image ? open - 1 : open);\n return image ? head : head + (close < 0 ? rest : rest.slice(0, close));\n}\n\n/**\n * Closes what a text cut in the middle leaves open: an emphasis (`**`, `~~`) or a code span, a link or image whose\n * address has not arrived. Inside an open code fence nothing is healed — it is shown as code as it comes.\n */\nexport function healStreaming(source: string): string {\n if (((source.match(FENCE) ?? []).length & 1) === 1) return source;\n let out = healLink(source);\n const cut = out.lastIndexOf(\"\\n\\n\");\n const block = cut < 0 ? out : out.slice(cut + 2);\n const open = (marker: string) => (block.split(marker).length - 1) % 2 === 1;\n // A marker just opened, with nothing after it yet, is dropped rather than shown as an empty emphasis.\n const close = (marker: string) => (out.endsWith(marker) ? out.slice(0, -marker.length) : `${out}${marker}`);\n if (open(\"`\")) return close(\"`\");\n for (const marker of [\"**\", \"~~\"]) if (open(marker)) out = close(marker);\n return out;\n}\n\n/** The blocks of a Markdown source. */\nexport function parseSource(source: string, { streaming = false, headingIds = false }: ParseOptions = {}): BlockNode[] {\n return parseMarkdown(streaming ? healStreaming(source) : source, { headingIds, ...(streaming && { extensions: [streamingMarkdownExtension()] }) }).children;\n}\n\n/** The text of inline nodes (a heading's label, an image's alt). */\nexport function textOf(nodes: readonly { type: string; value?: string; children?: readonly unknown[] }[]): string {\n return nodes\n .map((node) => (node.children ? textOf(node.children as typeof nodes) : (node.value ?? \"\")))\n .join(\"\");\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/markdown/markdown.tsx","../src/cn.ts","../src/markdown/parse.ts"],"sourcesContent":["// Server-safe (no hook, no context): a Markdown text rendered on the tokens, from TanStack Markdown's AST. Raw HTML is\n// never enabled (it is shown as text), executable URLs are dropped, links to another site carry `rel=\"noopener\n// noreferrer\"`, and every fenced block is a `CodeBlock` (the same grammars, the same colours). `streaming` is for a\n// text that arrives in pieces — an AI answer: the unfinished end is closed and a caret marks where it goes on.\nimport { Badge } from \"@krizaka/ui/badge\";\nimport { CodeBlock } from \"@krizaka/ui/code-block\";\nimport * as React from \"react\";\n\nimport { cn } from \"../cn\";\nimport { type BlockNode, type InlineNode, parseSource, textOf } from \"./parse\";\n\nexport type MarkdownLinkProps = React.ComponentProps<\"a\">;\n\nexport type MarkdownProps = Omit<React.ComponentProps<\"div\">, \"children\"> & {\n /** The Markdown source. */\n children: string;\n /** The text is still arriving: the unfinished end is closed and a caret marks the end. */\n streaming?: boolean;\n /** Marks a generated document with a quiet chip, when `generatedLabel` is supplied. */\n generated?: boolean;\n /** The chip's words, passed translated by the host; no chip is rendered without them. */\n generatedLabel?: string;\n /** Gives every heading an `id`, for `#section` links. */\n headingIds?: boolean;\n /** The words of the copy button of the code blocks, passed translated. Without them a code block has no copy button. */\n codeLabels?: { copy: string; copied: string };\n /** Your router's link (Next's `Link`) for the links that stay on the site (`/docs`, `#section`); the others are plain `a`. */\n linkAs?: React.ElementType<MarkdownLinkProps>;\n};\n\ntype Context = Pick<MarkdownProps, \"codeLabels\" | \"linkAs\">;\n\nconst external = (href: string) => /^[a-z][a-z0-9+.-]*:/i.test(href);\n\nfunction Inlines({ nodes, ctx }: { nodes: readonly InlineNode[]; ctx: Context }) {\n return nodes.map((node, i) => <Inline key={i} node={node} ctx={ctx} />);\n}\n\nfunction Inline({ node, ctx }: { node: InlineNode; ctx: Context }): React.ReactNode {\n switch (node.type) {\n case \"text\":\n return node.value;\n case \"inlineCode\":\n return <code>{node.value}</code>;\n case \"strong\":\n return (\n <strong>\n <Inlines nodes={node.children} ctx={ctx} />\n </strong>\n );\n case \"emphasis\":\n return (\n <em>\n <Inlines nodes={node.children} ctx={ctx} />\n </em>\n );\n case \"strike\":\n return (\n <del>\n <Inlines nodes={node.children} ctx={ctx} />\n </del>\n );\n case \"break\":\n return <br />;\n case \"link\": {\n const Link = external(node.href) ? \"a\" : (ctx.linkAs ?? \"a\");\n const props = external(node.href) && !node.href.startsWith(\"mailto:\") && !node.href.startsWith(\"tel:\") ? { rel: \"noopener noreferrer\" } : {};\n return (\n <Link href={node.href} title={node.title} {...props}>\n <Inlines nodes={node.children} ctx={ctx} />\n </Link>\n );\n }\n case \"image\":\n return <img src={node.src} alt={node.alt} title={node.title} loading=\"lazy\" />;\n case \"footnoteReference\":\n return (\n <sup>\n <a id={`kz-fnref-${node.id}`} href={`#kz-fn-${node.id}`} data-footnote-ref=\"\">\n {node.number}\n </a>\n </sup>\n );\n // Raw HTML is never enabled by the parser: it arrives as text. A node of this kind is shown as text too.\n case \"inlineHtml\":\n return node.value;\n default:\n return null;\n }\n}\n\nfunction Blocks({ nodes, ctx }: { nodes: readonly BlockNode[]; ctx: Context }) {\n return nodes.map((node, i) => <Block key={i} node={node} ctx={ctx} />);\n}\n\nfunction Block({ node, ctx }: { node: BlockNode; ctx: Context }): React.ReactNode {\n switch (node.type) {\n case \"heading\": {\n const Tag = `h${node.depth}` as const;\n return (\n <Tag id={node.id}>\n <Inlines nodes={node.children} ctx={ctx} />\n </Tag>\n );\n }\n case \"paragraph\":\n return (\n <p>\n <Inlines nodes={node.children} ctx={ctx} />\n </p>\n );\n case \"code\":\n return (\n <CodeBlock\n code={node.value}\n language={node.lang ?? \"text\"}\n file={node.title ?? node.file}\n copyLabel={ctx.codeLabels?.copy}\n copiedLabel={ctx.codeLabels?.copied}\n annotations={node.highlightLines?.map((line) => ({ lines: line, kind: \"highlight\" as const }))}\n />\n );\n case \"list\": {\n const Tag = node.ordered ? \"ol\" : \"ul\";\n const tasks = node.items.some((item) => item.checked !== undefined);\n return (\n <Tag start={node.ordered && node.start !== undefined && node.start !== 1 ? node.start : undefined} data-tasks={tasks ? \"\" : undefined}>\n {node.items.map((item, i) => (\n <li key={i}>\n {item.checked !== undefined ? (\n <label>\n <input type=\"checkbox\" checked={item.checked} disabled readOnly />{\" \"}\n <ListItem blocks={item.children} ctx={ctx} />\n </label>\n ) : (\n <ListItem blocks={item.children} ctx={ctx} />\n )}\n </li>\n ))}\n </Tag>\n );\n }\n case \"blockquote\":\n return (\n <blockquote>\n <Blocks nodes={node.children} ctx={ctx} />\n </blockquote>\n );\n case \"table\":\n return (\n <div className=\"kz-md-table\" role=\"region\" tabIndex={0} aria-label={node.header.map((cell) => textOf(cell.children)).join(\", \")}>\n <table>\n <thead>\n <tr>\n {node.header.map((cell, i) => (\n <th key={i} scope=\"col\" style={{ textAlign: node.align[i] ?? undefined }}>\n <div className=\"kz-md-cell\"><Inlines nodes={cell.children} ctx={ctx} /></div>\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {node.rows.map((row, r) => (\n <tr key={r}>\n {row.map((cell, i) => (\n <td key={i} style={{ textAlign: node.align[i] ?? undefined }}>\n <div className=\"kz-md-cell\"><Inlines nodes={cell.children} ctx={ctx} /></div>\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n case \"thematicBreak\":\n return <hr />;\n case \"footnotes\":\n return (\n <section data-footnotes=\"\">\n <ol>\n {node.items.map((item) => (\n <li key={item.id} id={`kz-fn-${item.id}`}>\n <Blocks nodes={item.children} ctx={ctx} />\n </li>\n ))}\n </ol>\n </section>\n );\n // Raw HTML (never enabled) arrives as a paragraph of text; extensions are not enabled either. Shown as text.\n case \"html\":\n return <p>{node.value}</p>;\n default:\n return null;\n }\n}\n\n/** A list item: a lone paragraph is inline (a tight list), anything else keeps its blocks. */\nfunction ListItem({ blocks, ctx }: { blocks: readonly BlockNode[]; ctx: Context }) {\n return blocks.map((block, i) => (block.type === \"paragraph\" && blocks.length === 1 ? <Inlines key={i} nodes={block.children} ctx={ctx} /> : <Block key={i} node={block} ctx={ctx} />));\n}\n\n/** A Markdown text on the tokens: headings, paragraphs, lists (and tasks), quotes, tables, code, links, images, footnotes. */\nexport function Markdown({ children, streaming, generated, generatedLabel, headingIds, codeLabels, linkAs, className, ...props }: MarkdownProps) {\n const blocks = parseSource(children, { streaming, headingIds });\n return (\n <div className={cn(\"kz-md\", className)} data-streaming={streaming ? \"\" : undefined} aria-busy={streaming || undefined} {...props}>\n {generated && generatedLabel ? <Badge>{generatedLabel}</Badge> : null}\n <Blocks nodes={blocks} ctx={{ codeLabels, linkAs }} />\n </div>\n );\n}\n\nexport { textOf };\n","import { type ClassValue, clsx } from \"clsx\";\nimport { cnMerge, type TWMergeConfig } from \"tailwind-variants\";\n\nexport type { ClassValue };\n\n/**\n * The preset's own utilities, told to the merge engine: the material (BRAND.md §6: `bg-sheen`, `bg-fill-shade`,\n * `bg-halo` are background images; `shadow-raised` · `-floating` · `-overlay` · `-glow` are shadows; `inset-shadow-edge`\n * · `-edge-fill` · `-well` are inset shadows). Unknown, tailwind-merge would take `bg-sheen` for a colour and drop the\n * `bg-surface-2` it lies on.\n */\nexport const twMergeConfig: TWMergeConfig = {\n extend: {\n classGroups: {\n \"bg-image\": [\"bg-sheen\", \"bg-fill-shade\", \"bg-halo\"],\n shadow: [\"shadow-raised\", \"shadow-floating\", \"shadow-overlay\", \"shadow-glow\", \"shadow-glow-lg\"],\n \"inset-shadow\": [\"inset-shadow-edge\", \"inset-shadow-edge-fill\", \"inset-shadow-well\"],\n },\n },\n};\n\nconst mergeConfig = { twMergeConfig };\n\n/**\n * Merges Tailwind classes: the last one wins — a product's `className` overrides the primitive's. The merge engine is\n * tailwind-merge as tailwind-variants ships it (the one `tv` uses), so the platform carries a single copy of it.\n * tailwind-variants bundles that engine since 3.3 (it never imports the `tailwind-merge` package, an optional peer):\n * hence `tailwind-variants ^3.3.1` and no `tailwind-merge` dependency — `scripts/consumer.mjs` checks it on a clean\n * install of the packed package.\n */\nexport function cn(...inputs: ClassValue[]): string {\n return cnMerge(clsx(inputs))(mergeConfig) ?? \"\";\n}\n\n","// The shared half of Markdown, web and React Native: the source becomes TanStack Markdown's serializable AST\n// (`@tanstack/markdown/parser`) — raw HTML is never enabled (it stays text), executable URLs are dropped (`javascript:`,\n// `data:`), and a text that is still arriving is healed so the half-written end reads like the finished one.\nimport type { BlockNode } from \"@tanstack/markdown\";\nimport { streamingMarkdownExtension } from \"@tanstack/markdown/extensions/streaming\";\nimport { parseMarkdown } from \"@tanstack/markdown/parser\";\n\nexport type { BlockNode, InlineNode, ListItemNode, TableNode } from \"@tanstack/markdown\";\n\nexport type ParseOptions = {\n /** The text is still arriving (an AI answer): the unfinished end is closed, so it never shows `**` or a half link. */\n streaming?: boolean;\n /** Gives every heading an `id` (`#section` links, a table of contents). */\n headingIds?: boolean;\n};\n\nconst FENCE = /^ {0,3}(```|~~~)/gm;\n\n/** A link or image cut at its end: `[label`, `[label]`, `[label](http…` become `label`, an image is dropped. Linear: one scan of the last `[`. */\nfunction healLink(text: string): string {\n const open = text.lastIndexOf(\"[\");\n if (open < 0) return text;\n const rest = text.slice(open + 1);\n if (rest.includes(\"\\n\")) return text;\n const image = open > 0 && text[open - 1] === \"!\";\n const close = rest.indexOf(\"]\");\n const after = close < 0 ? \"\" : rest.slice(close + 1);\n const cut = close < 0 || after === \"\" || (after.startsWith(\"(\") && !after.includes(\")\"));\n if (!cut) return text;\n const head = text.slice(0, image ? open - 1 : open);\n return image ? head : head + (close < 0 ? rest : rest.slice(0, close));\n}\n\n/**\n * Closes what a text cut in the middle leaves open: an emphasis (`**`, `~~`) or a code span, a link or image whose\n * address has not arrived. Inside an open code fence nothing is healed — it is shown as code as it comes.\n */\nexport function healStreaming(source: string): string {\n if (((source.match(FENCE) ?? []).length & 1) === 1) return source;\n let out = healLink(source);\n const cut = out.lastIndexOf(\"\\n\\n\");\n const block = cut < 0 ? out : out.slice(cut + 2);\n const open = (marker: string) => (block.split(marker).length - 1) % 2 === 1;\n // A marker just opened, with nothing after it yet, is dropped rather than shown as an empty emphasis.\n const close = (marker: string) => (out.endsWith(marker) ? out.slice(0, -marker.length) : `${out}${marker}`);\n if (open(\"`\")) return close(\"`\");\n for (const marker of [\"**\", \"~~\"]) if (open(marker)) out = close(marker);\n return out;\n}\n\n/** The blocks of a Markdown source. */\nexport function parseSource(source: string, { streaming = false, headingIds = false }: ParseOptions = {}): BlockNode[] {\n return parseMarkdown(streaming ? healStreaming(source) : source, { headingIds, ...(streaming && { extensions: [streamingMarkdownExtension()] }) }).children;\n}\n\n/** The text of inline nodes (a heading's label, an image's alt). */\nexport function textOf(nodes: readonly { type: string; value?: string; children?: readonly unknown[] }[]): string {\n return nodes\n .map((node) => (node.children ? textOf(node.children as typeof nodes) : (node.value ?? \"\")))\n .join(\"\");\n}\n"],"mappings":"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","names":[]}
|
package/dist/match-dialog.d.ts
CHANGED
|
@@ -26,7 +26,10 @@ type MatchDialogProps = {
|
|
|
26
26
|
/** Where it is drawn (the page's body by default): give an element inside a brand scope (`.brand-…`) so the dialog wears the brand too. */
|
|
27
27
|
container?: HTMLElement | null;
|
|
28
28
|
};
|
|
29
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* Two people choose each other: the avatars meet at a heart in the brand gradient, the next step under them.
|
|
31
|
+
* @deprecated Since 3.55 (ADR-006, krizaka-ui#674): no product uses it. Removed in 4.0.
|
|
32
|
+
*/
|
|
30
33
|
declare function MatchDialog({ open, onOpenChange, title, description, people, primaryLabel, onPrimary, secondaryLabel, container }: MatchDialogProps): React.JSX.Element;
|
|
31
34
|
|
|
32
35
|
export { MatchDialog, type MatchDialogProps };
|
package/dist/match-dialog.js
CHANGED
|
@@ -3,6 +3,9 @@ import "./chunk-NPF3OHE2.js";
|
|
|
3
3
|
import {
|
|
4
4
|
Avatar
|
|
5
5
|
} from "./chunk-KB7OLYIJ.js";
|
|
6
|
+
import {
|
|
7
|
+
warnDeprecated
|
|
8
|
+
} from "./chunk-BDIU5LZB.js";
|
|
6
9
|
import "./chunk-JXLDUDTD.js";
|
|
7
10
|
import {
|
|
8
11
|
Button,
|
|
@@ -139,6 +142,7 @@ var Dialog = {
|
|
|
139
142
|
// src/match-dialog/match-dialog.tsx
|
|
140
143
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
141
144
|
function MatchDialog({ open, onOpenChange, title, description, people, primaryLabel, onPrimary, secondaryLabel, container }) {
|
|
145
|
+
warnDeprecated("match-dialog");
|
|
142
146
|
return /* @__PURE__ */ jsx2(Dialog.Root, { open, onOpenChange, children: /* @__PURE__ */ jsxs2(Dialog.Content, { hideClose: true, size: "sm", container, className: "overflow-hidden border-0 bg-gradient-to-br from-accent to-accent-2 text-center text-on-accent", children: [
|
|
143
147
|
/* @__PURE__ */ jsx2("style", { href: "krizaka-match", precedence: "default", children: "@media (prefers-reduced-motion:no-preference){[data-kz-match] [data-side=a]{animation:kz-match-a .55s cubic-bezier(.3,1.6,.5,1) both}[data-kz-match] [data-side=b]{animation:kz-match-b .55s cubic-bezier(.3,1.6,.5,1) both}[data-kz-match] [data-heart]{animation:kz-match-heart .45s .25s cubic-bezier(.3,1.6,.5,1) both}}@keyframes kz-match-a{from{transform:translateX(-60px) rotate(-8deg);opacity:0}}@keyframes kz-match-b{from{transform:translateX(60px) rotate(8deg);opacity:0}}@keyframes kz-match-heart{from{transform:scale(0)}}" }),
|
|
144
148
|
/* @__PURE__ */ jsxs2("div", { "data-kz-match": "", className: "flex flex-col items-center gap-3 px-2 pb-2 pt-6", children: [
|
package/dist/match-dialog.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/dialog/dialog.tsx","../src/dialog/return-focus.ts","../src/match-dialog/match-dialog.tsx"],"sourcesContent":["// Client (a portal). Radix does the focus trap, the scroll lock, Escape, the outside click, aria-modal, the\n// aria-labelledby / aria-describedby wiring and the focus return: this file only holds the shape.\nimport { Dialog as DialogPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { IconButton } from \"../button/button\";\nimport { useReturnFocus } from \"./return-focus\";\n\nexport const dialog = tv({\n slots: {\n overlay: \"kz-overlay fixed inset-0 z-50 bg-overlay\",\n content:\n \"kz-dialog fixed z-50 flex flex-col overflow-hidden border border-border-default bg-surface-1 text-fg shadow-lg focus:outline-hidden\",\n close: \"absolute right-3 top-3 h-9 w-9\",\n header: \"flex flex-col gap-1 px-5 pb-2 pr-14 pt-5\",\n title: \"text-base font-bold text-fg\",\n description: \"text-sm text-fg-secondary\",\n body: \"min-h-0 flex-1 overflow-y-auto px-5 pb-5\",\n footer:\n \"flex flex-wrap items-center justify-end gap-2 border-t border-border-subtle px-5 py-4 pb-[max(1rem,env(safe-area-inset-bottom))]\",\n },\n variants: {\n placement: {\n center: { content: \"left-1/2 top-1/2 max-h-[85dvh] w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 rounded-xl\" },\n /* A sheet: anchored at the bottom on a phone, centred from `sm` up. */\n bottom: {\n content:\n \"inset-x-0 bottom-0 max-h-[92dvh] rounded-t-[1.75rem] sm:bottom-auto sm:left-1/2 sm:top-1/2 sm:w-full \" +\n \"sm:-translate-x-1/2 sm:-translate-y-1/2 sm:rounded-[1.75rem]\",\n },\n right: { content: \"inset-y-0 right-0 h-full w-full max-w-md rounded-none border-y-0 border-r-0\" },\n /* A navigation drawer: full height, from the left, clear of the notch. */\n left: { content: \"inset-y-0 left-0 h-full w-full max-w-md rounded-none border-y-0 border-l-0 pl-[env(safe-area-inset-left)]\" },\n },\n size: { sm: { content: \"sm:max-w-sm\" }, md: { content: \"sm:max-w-md\" }, lg: { content: \"sm:max-w-2xl\" } },\n },\n defaultVariants: { placement: \"center\", size: \"md\" },\n});\nconst s = dialog();\n\nexport type DialogVariants = VariantProps<typeof dialog>;\n\nfunction CloseIcon() {\n return (\n <svg viewBox=\"0 0 24 24\" className=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n );\n}\n\ntype CloseButtonProps =\n | {\n /** The accessible name of the close button — passed translated. */\n closeLabel: string;\n /** Leaves the close button out (the content brings its own way out, or none: see `dismissible`). */\n hideClose?: false;\n }\n | { closeLabel?: string; hideClose: true };\n\nexport type DialogContentProps = React.ComponentProps<typeof DialogPrimitive.Content> &\n DialogVariants &\n CloseButtonProps & {\n /**\n * center · bottom (a sheet on a phone, centred from `sm` up) · right (a side panel, full height) · left (a\n * navigation drawer, full height).\n * @default \"center\"\n */\n placement?: DialogVariants[\"placement\"];\n /**\n * sm · md · lg: the width of the dialog.\n * @default \"md\"\n */\n size?: DialogVariants[\"size\"];\n /**\n * `false`: Escape and a click outside do not close it (a gate the user must answer: age, terms). It still closes\n * through `open` / `onOpenChange` and any `Dialog.Close` inside. Default `true`.\n */\n dismissible?: boolean;\n /** The element the portal renders into (default: document.body). */\n container?: HTMLElement | null;\n };\n\nconst prevent = (event: Event) => event.preventDefault();\n\n/** Radix's content, mounted only while open: it remembers the opener then and gives focus back on close (WCAG 2.4.3). */\nfunction ReturnFocusContent({ onCloseAutoFocus, ...props }: React.ComponentProps<typeof DialogPrimitive.Content>) {\n return <DialogPrimitive.Content onCloseAutoFocus={useReturnFocus(onCloseAutoFocus)} {...props} />;\n}\n\n/** The dialog itself, portalled over a dimmed overlay, with a close button. Give it a `Dialog.Title`. */\nexport function DialogContent({\n placement,\n size,\n closeLabel,\n hideClose,\n dismissible = true,\n container,\n className,\n children,\n onEscapeKeyDown,\n onPointerDownOutside,\n onInteractOutside,\n ...props\n}: DialogContentProps) {\n const v = dialog({ placement, size });\n return (\n <DialogPrimitive.Portal container={container}>\n <DialogPrimitive.Overlay className={v.overlay()} />\n <ReturnFocusContent\n data-placement={placement ?? \"center\"}\n data-dismissible={dismissible ? undefined : \"false\"}\n className={v.content({ className })}\n onEscapeKeyDown={dismissible ? onEscapeKeyDown : chain(onEscapeKeyDown, prevent)}\n onPointerDownOutside={dismissible ? onPointerDownOutside : chain(onPointerDownOutside, prevent)}\n onInteractOutside={dismissible ? onInteractOutside : chain(onInteractOutside, prevent)}\n {...props}\n >\n {children}\n {!hideClose && (\n <DialogPrimitive.Close asChild>\n <IconButton label={closeLabel as string} variant=\"ghost\" shape=\"pill\" className={v.close()}>\n <CloseIcon />\n </IconButton>\n </DialogPrimitive.Close>\n )}\n </ReturnFocusContent>\n </DialogPrimitive.Portal>\n );\n}\n\nfunction chain<E extends Event>(handler: ((event: E) => void) | undefined, then: (event: E) => void) {\n return (event: E) => {\n handler?.(event);\n then(event);\n };\n}\n\nexport function DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.header({ className })} {...props} />;\n}\n\nexport function DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {\n return <DialogPrimitive.Title className={s.title({ className })} {...props} />;\n}\n\nexport function DialogDescription({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Description>) {\n return <DialogPrimitive.Description className={s.description({ className })} {...props} />;\n}\n\nexport function DialogBody({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.body({ className })} {...props} />;\n}\n\nexport function DialogFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.footer({ className })} {...props} />;\n}\n\n/** `Dialog.Root/Trigger/Content/Header/Title/Description/Body/Footer/Close`. `open`/`defaultOpen` + `onOpenChange`. */\nexport const Dialog = {\n Root: DialogPrimitive.Root,\n Trigger: DialogPrimitive.Trigger,\n Close: DialogPrimitive.Close,\n Content: DialogContent,\n Header: DialogHeader,\n Title: DialogTitle,\n Description: DialogDescription,\n Body: DialogBody,\n Footer: DialogFooter,\n};\n\n/** `Omit` that keeps the close-button union (`closeLabel` required unless `hideClose`). */\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;\n\nexport type SheetProps = DistributiveOmit<DialogContentProps, \"placement\">;\n\n/** A sheet is a dialog anchored at the bottom (centred from `sm` up): `Dialog.Content placement=\"bottom\"`. */\nexport function Sheet(props: SheetProps) {\n return <DialogContent placement=\"bottom\" {...props} />;\n}\n","// Client. WCAG 2.4.3: when a dialog closes, focus goes back to what opened it. Radix only does it for `Dialog.Trigger`;\n// most app dialogs are controlled (`open` / `onOpenChange`) and opened by a plain button, so on Escape focus fell to\n// <body>. The content remembers the focused element when it mounts (it is still the opener: Radix moves focus in an\n// effect) and gives it back on close, unless the consumer's `onCloseAutoFocus` prevented the default.\nimport * as React from \"react\";\n\n/** The handler for `onCloseAutoFocus`: yours first, then focus back on the opener if it is still on the page. */\nexport function useReturnFocus(onCloseAutoFocus?: (event: Event) => void) {\n const [opener] = React.useState<Element | null>(() => (typeof document === \"undefined\" ? null : document.activeElement));\n return React.useCallback(\n (event: Event) => {\n onCloseAutoFocus?.(event);\n if (event.defaultPrevented) return;\n if (opener instanceof HTMLElement && opener !== document.body && opener.isConnected) {\n event.preventDefault();\n opener.focus();\n }\n },\n [opener, onCloseAutoFocus],\n );\n}\n","// Client (a portal). The moment two people choose each other — a match, a pair, a deal: a dialog in the brand\n// gradient, the two avatars meeting at a heart, the next step. The entrance plays once; still under reduced motion.\nimport * as React from \"react\";\n\nimport { Avatar } from \"../avatar\";\nimport { Button } from \"../button\";\nimport { Dialog } from \"../dialog\";\n\nexport type MatchDialogProps = {\n /** Shown (controlled). */\n open: boolean;\n /** Called when it closes. */\n onOpenChange: (open: boolean) => void;\n /** The headline (“It's a match!”) — passed translated; the dialog's name. */\n title: string;\n /** A line under it. */\n description?: string;\n /** The two people: avatar URLs and names. */\n people: readonly [{ src?: string | null; name: string }, { src?: string | null; name: string }];\n /** The main action (“Send a message”). */\n primaryLabel: string;\n /** What it does. */\n onPrimary: () => void;\n /** The way back (“Keep swiping”). */\n secondaryLabel: string;\n /** Where it is drawn (the page's body by default): give an element inside a brand scope (`.brand-…`) so the dialog wears the brand too. */\n container?: HTMLElement | null;\n};\n\n/** Two people choose each other: the avatars meet at a heart in the brand gradient, the next step under them. */\nexport function MatchDialog({ open, onOpenChange, title, description, people, primaryLabel, onPrimary, secondaryLabel, container }: MatchDialogProps) {\n return (\n <Dialog.Root open={open} onOpenChange={onOpenChange}>\n <Dialog.Content hideClose size=\"sm\" container={container} className=\"overflow-hidden border-0 bg-gradient-to-br from-accent to-accent-2 text-center text-on-accent\">\n <style href=\"krizaka-match\" precedence=\"default\">\n {\"@media (prefers-reduced-motion:no-preference){[data-kz-match] [data-side=a]{animation:kz-match-a .55s cubic-bezier(.3,1.6,.5,1) both}[data-kz-match] [data-side=b]{animation:kz-match-b .55s cubic-bezier(.3,1.6,.5,1) both}[data-kz-match] [data-heart]{animation:kz-match-heart .45s .25s cubic-bezier(.3,1.6,.5,1) both}}@keyframes kz-match-a{from{transform:translateX(-60px) rotate(-8deg);opacity:0}}@keyframes kz-match-b{from{transform:translateX(60px) rotate(8deg);opacity:0}}@keyframes kz-match-heart{from{transform:scale(0)}}\"}\n </style>\n <div data-kz-match=\"\" className=\"flex flex-col items-center gap-3 px-2 pb-2 pt-6\">\n <Dialog.Title className=\"font-display text-4xl font-black text-on-accent\">{title}</Dialog.Title>\n {description ? <Dialog.Description className=\"max-w-xs text-sm text-on-accent\">{description}</Dialog.Description> : null}\n <div className=\"relative my-6 flex h-36 w-64 items-center justify-center\" aria-hidden>\n <span data-side=\"a\" className=\"absolute left-6 -rotate-6 rounded-full ring-4 ring-on-accent\">\n <Avatar src={people[0].src ?? null} alt=\"\" fallback={people[0].name.charAt(0)} className=\"h-28 w-28 text-3xl\" />\n </span>\n <span data-side=\"b\" className=\"absolute right-6 rotate-6 rounded-full ring-4 ring-on-accent\">\n <Avatar src={people[1].src ?? null} alt=\"\" fallback={people[1].name.charAt(0)} className=\"h-28 w-28 text-3xl\" />\n </span>\n <span data-heart=\"\" className=\"absolute -bottom-1 flex h-12 w-12 items-center justify-center rounded-full bg-on-accent text-accent shadow-lg\">\n <svg viewBox=\"0 0 24 24\" className=\"h-6 w-6\" fill=\"currentColor\">\n <path d=\"M12 20s-7-4.4-7-10a4 4 0 0 1 7-2.6A4 4 0 0 1 19 10c0 5.6-7 10-7 10z\" />\n </svg>\n </span>\n </div>\n <div className=\"flex w-full flex-col gap-2\">\n <Button\n size=\"lg\"\n onClick={() => {\n onOpenChange(false);\n onPrimary();\n }}\n >\n {primaryLabel}\n </Button>\n <Dialog.Close asChild>\n <Button size=\"lg\" variant=\"ghost\" className=\"text-on-accent hover:bg-on-accent/15\">\n {secondaryLabel}\n </Button>\n </Dialog.Close>\n </div>\n </div>\n </Dialog.Content>\n </Dialog.Root>\n );\n}\n"],"mappings":"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","names":["jsx","jsxs"]}
|
|
1
|
+
{"version":3,"sources":["../src/dialog/dialog.tsx","../src/dialog/return-focus.ts","../src/match-dialog/match-dialog.tsx"],"sourcesContent":["// Client (a portal). Radix does the focus trap, the scroll lock, Escape, the outside click, aria-modal, the\n// aria-labelledby / aria-describedby wiring and the focus return: this file only holds the shape.\nimport { Dialog as DialogPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { IconButton } from \"../button/button\";\nimport { useReturnFocus } from \"./return-focus\";\n\nexport const dialog = tv({\n slots: {\n overlay: \"kz-overlay fixed inset-0 z-50 bg-overlay\",\n content:\n \"kz-dialog fixed z-50 flex flex-col overflow-hidden border border-border-default bg-surface-1 text-fg shadow-lg focus:outline-hidden\",\n close: \"absolute right-3 top-3 h-9 w-9\",\n header: \"flex flex-col gap-1 px-5 pb-2 pr-14 pt-5\",\n title: \"text-base font-bold text-fg\",\n description: \"text-sm text-fg-secondary\",\n body: \"min-h-0 flex-1 overflow-y-auto px-5 pb-5\",\n footer:\n \"flex flex-wrap items-center justify-end gap-2 border-t border-border-subtle px-5 py-4 pb-[max(1rem,env(safe-area-inset-bottom))]\",\n },\n variants: {\n placement: {\n center: { content: \"left-1/2 top-1/2 max-h-[85dvh] w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 rounded-xl\" },\n /* A sheet: anchored at the bottom on a phone, centred from `sm` up. */\n bottom: {\n content:\n \"inset-x-0 bottom-0 max-h-[92dvh] rounded-t-[1.75rem] sm:bottom-auto sm:left-1/2 sm:top-1/2 sm:w-full \" +\n \"sm:-translate-x-1/2 sm:-translate-y-1/2 sm:rounded-[1.75rem]\",\n },\n right: { content: \"inset-y-0 right-0 h-full w-full max-w-md rounded-none border-y-0 border-r-0\" },\n /* A navigation drawer: full height, from the left, clear of the notch. */\n left: { content: \"inset-y-0 left-0 h-full w-full max-w-md rounded-none border-y-0 border-l-0 pl-[env(safe-area-inset-left)]\" },\n },\n size: { sm: { content: \"sm:max-w-sm\" }, md: { content: \"sm:max-w-md\" }, lg: { content: \"sm:max-w-2xl\" } },\n },\n defaultVariants: { placement: \"center\", size: \"md\" },\n});\nconst s = dialog();\n\nexport type DialogVariants = VariantProps<typeof dialog>;\n\nfunction CloseIcon() {\n return (\n <svg viewBox=\"0 0 24 24\" className=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n );\n}\n\ntype CloseButtonProps =\n | {\n /** The accessible name of the close button — passed translated. */\n closeLabel: string;\n /** Leaves the close button out (the content brings its own way out, or none: see `dismissible`). */\n hideClose?: false;\n }\n | { closeLabel?: string; hideClose: true };\n\nexport type DialogContentProps = React.ComponentProps<typeof DialogPrimitive.Content> &\n DialogVariants &\n CloseButtonProps & {\n /**\n * center · bottom (a sheet on a phone, centred from `sm` up) · right (a side panel, full height) · left (a\n * navigation drawer, full height).\n * @default \"center\"\n */\n placement?: DialogVariants[\"placement\"];\n /**\n * sm · md · lg: the width of the dialog.\n * @default \"md\"\n */\n size?: DialogVariants[\"size\"];\n /**\n * `false`: Escape and a click outside do not close it (a gate the user must answer: age, terms). It still closes\n * through `open` / `onOpenChange` and any `Dialog.Close` inside. Default `true`.\n */\n dismissible?: boolean;\n /** The element the portal renders into (default: document.body). */\n container?: HTMLElement | null;\n };\n\nconst prevent = (event: Event) => event.preventDefault();\n\n/** Radix's content, mounted only while open: it remembers the opener then and gives focus back on close (WCAG 2.4.3). */\nfunction ReturnFocusContent({ onCloseAutoFocus, ...props }: React.ComponentProps<typeof DialogPrimitive.Content>) {\n return <DialogPrimitive.Content onCloseAutoFocus={useReturnFocus(onCloseAutoFocus)} {...props} />;\n}\n\n/** The dialog itself, portalled over a dimmed overlay, with a close button. Give it a `Dialog.Title`. */\nexport function DialogContent({\n placement,\n size,\n closeLabel,\n hideClose,\n dismissible = true,\n container,\n className,\n children,\n onEscapeKeyDown,\n onPointerDownOutside,\n onInteractOutside,\n ...props\n}: DialogContentProps) {\n const v = dialog({ placement, size });\n return (\n <DialogPrimitive.Portal container={container}>\n <DialogPrimitive.Overlay className={v.overlay()} />\n <ReturnFocusContent\n data-placement={placement ?? \"center\"}\n data-dismissible={dismissible ? undefined : \"false\"}\n className={v.content({ className })}\n onEscapeKeyDown={dismissible ? onEscapeKeyDown : chain(onEscapeKeyDown, prevent)}\n onPointerDownOutside={dismissible ? onPointerDownOutside : chain(onPointerDownOutside, prevent)}\n onInteractOutside={dismissible ? onInteractOutside : chain(onInteractOutside, prevent)}\n {...props}\n >\n {children}\n {!hideClose && (\n <DialogPrimitive.Close asChild>\n <IconButton label={closeLabel as string} variant=\"ghost\" shape=\"pill\" className={v.close()}>\n <CloseIcon />\n </IconButton>\n </DialogPrimitive.Close>\n )}\n </ReturnFocusContent>\n </DialogPrimitive.Portal>\n );\n}\n\nfunction chain<E extends Event>(handler: ((event: E) => void) | undefined, then: (event: E) => void) {\n return (event: E) => {\n handler?.(event);\n then(event);\n };\n}\n\nexport function DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.header({ className })} {...props} />;\n}\n\nexport function DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {\n return <DialogPrimitive.Title className={s.title({ className })} {...props} />;\n}\n\nexport function DialogDescription({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Description>) {\n return <DialogPrimitive.Description className={s.description({ className })} {...props} />;\n}\n\nexport function DialogBody({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.body({ className })} {...props} />;\n}\n\nexport function DialogFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return <div className={s.footer({ className })} {...props} />;\n}\n\n/** `Dialog.Root/Trigger/Content/Header/Title/Description/Body/Footer/Close`. `open`/`defaultOpen` + `onOpenChange`. */\nexport const Dialog = {\n Root: DialogPrimitive.Root,\n Trigger: DialogPrimitive.Trigger,\n Close: DialogPrimitive.Close,\n Content: DialogContent,\n Header: DialogHeader,\n Title: DialogTitle,\n Description: DialogDescription,\n Body: DialogBody,\n Footer: DialogFooter,\n};\n\n/** `Omit` that keeps the close-button union (`closeLabel` required unless `hideClose`). */\ntype DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;\n\nexport type SheetProps = DistributiveOmit<DialogContentProps, \"placement\">;\n\n/** A sheet is a dialog anchored at the bottom (centred from `sm` up): `Dialog.Content placement=\"bottom\"`. */\nexport function Sheet(props: SheetProps) {\n return <DialogContent placement=\"bottom\" {...props} />;\n}\n","// Client. WCAG 2.4.3: when a dialog closes, focus goes back to what opened it. Radix only does it for `Dialog.Trigger`;\n// most app dialogs are controlled (`open` / `onOpenChange`) and opened by a plain button, so on Escape focus fell to\n// <body>. The content remembers the focused element when it mounts (it is still the opener: Radix moves focus in an\n// effect) and gives it back on close, unless the consumer's `onCloseAutoFocus` prevented the default.\nimport * as React from \"react\";\n\n/** The handler for `onCloseAutoFocus`: yours first, then focus back on the opener if it is still on the page. */\nexport function useReturnFocus(onCloseAutoFocus?: (event: Event) => void) {\n const [opener] = React.useState<Element | null>(() => (typeof document === \"undefined\" ? null : document.activeElement));\n return React.useCallback(\n (event: Event) => {\n onCloseAutoFocus?.(event);\n if (event.defaultPrevented) return;\n if (opener instanceof HTMLElement && opener !== document.body && opener.isConnected) {\n event.preventDefault();\n opener.focus();\n }\n },\n [opener, onCloseAutoFocus],\n );\n}\n","// Client (a portal). The moment two people choose each other — a match, a pair, a deal: a dialog in the brand\n// gradient, the two avatars meeting at a heart, the next step. The entrance plays once; still under reduced motion.\nimport * as React from \"react\";\n\nimport { Avatar } from \"../avatar\";\nimport { Button } from \"../button\";\nimport { warnDeprecated } from \"../deprecated\";\nimport { Dialog } from \"../dialog\";\n\nexport type MatchDialogProps = {\n /** Shown (controlled). */\n open: boolean;\n /** Called when it closes. */\n onOpenChange: (open: boolean) => void;\n /** The headline (“It's a match!”) — passed translated; the dialog's name. */\n title: string;\n /** A line under it. */\n description?: string;\n /** The two people: avatar URLs and names. */\n people: readonly [{ src?: string | null; name: string }, { src?: string | null; name: string }];\n /** The main action (“Send a message”). */\n primaryLabel: string;\n /** What it does. */\n onPrimary: () => void;\n /** The way back (“Keep swiping”). */\n secondaryLabel: string;\n /** Where it is drawn (the page's body by default): give an element inside a brand scope (`.brand-…`) so the dialog wears the brand too. */\n container?: HTMLElement | null;\n};\n\n/**\n * Two people choose each other: the avatars meet at a heart in the brand gradient, the next step under them.\n * @deprecated Since 3.55 (ADR-006, krizaka-ui#674): no product uses it. Removed in 4.0.\n */\nexport function MatchDialog({ open, onOpenChange, title, description, people, primaryLabel, onPrimary, secondaryLabel, container }: MatchDialogProps) {\n warnDeprecated(\"match-dialog\");\n return (\n <Dialog.Root open={open} onOpenChange={onOpenChange}>\n <Dialog.Content hideClose size=\"sm\" container={container} className=\"overflow-hidden border-0 bg-gradient-to-br from-accent to-accent-2 text-center text-on-accent\">\n <style href=\"krizaka-match\" precedence=\"default\">\n {\"@media (prefers-reduced-motion:no-preference){[data-kz-match] [data-side=a]{animation:kz-match-a .55s cubic-bezier(.3,1.6,.5,1) both}[data-kz-match] [data-side=b]{animation:kz-match-b .55s cubic-bezier(.3,1.6,.5,1) both}[data-kz-match] [data-heart]{animation:kz-match-heart .45s .25s cubic-bezier(.3,1.6,.5,1) both}}@keyframes kz-match-a{from{transform:translateX(-60px) rotate(-8deg);opacity:0}}@keyframes kz-match-b{from{transform:translateX(60px) rotate(8deg);opacity:0}}@keyframes kz-match-heart{from{transform:scale(0)}}\"}\n </style>\n <div data-kz-match=\"\" className=\"flex flex-col items-center gap-3 px-2 pb-2 pt-6\">\n <Dialog.Title className=\"font-display text-4xl font-black text-on-accent\">{title}</Dialog.Title>\n {description ? <Dialog.Description className=\"max-w-xs text-sm text-on-accent\">{description}</Dialog.Description> : null}\n <div className=\"relative my-6 flex h-36 w-64 items-center justify-center\" aria-hidden>\n <span data-side=\"a\" className=\"absolute left-6 -rotate-6 rounded-full ring-4 ring-on-accent\">\n <Avatar src={people[0].src ?? null} alt=\"\" fallback={people[0].name.charAt(0)} className=\"h-28 w-28 text-3xl\" />\n </span>\n <span data-side=\"b\" className=\"absolute right-6 rotate-6 rounded-full ring-4 ring-on-accent\">\n <Avatar src={people[1].src ?? null} alt=\"\" fallback={people[1].name.charAt(0)} className=\"h-28 w-28 text-3xl\" />\n </span>\n <span data-heart=\"\" className=\"absolute -bottom-1 flex h-12 w-12 items-center justify-center rounded-full bg-on-accent text-accent shadow-lg\">\n <svg viewBox=\"0 0 24 24\" className=\"h-6 w-6\" fill=\"currentColor\">\n <path d=\"M12 20s-7-4.4-7-10a4 4 0 0 1 7-2.6A4 4 0 0 1 19 10c0 5.6-7 10-7 10z\" />\n </svg>\n </span>\n </div>\n <div className=\"flex w-full flex-col gap-2\">\n <Button\n size=\"lg\"\n onClick={() => {\n onOpenChange(false);\n onPrimary();\n }}\n >\n {primaryLabel}\n </Button>\n <Dialog.Close asChild>\n <Button size=\"lg\" variant=\"ghost\" className=\"text-on-accent hover:bg-on-accent/15\">\n {secondaryLabel}\n </Button>\n </Dialog.Close>\n </div>\n </div>\n </Dialog.Content>\n </Dialog.Root>\n );\n}\n"],"mappings":"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","names":["jsx","jsxs"]}
|
package/dist/media-capture.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as tailwind_variants from 'tailwind-variants';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
|
|
4
|
+
/** @deprecated Since 3.55 (ADR-006, krizaka-ui#674): no product uses it. Removed in 4.0. */
|
|
4
5
|
declare const mediaCapture: tailwind_variants.TVReturnType<{
|
|
5
6
|
[key: string]: {
|
|
6
7
|
[key: string]: tailwind_variants.ClassValue | {
|
|
@@ -126,6 +127,7 @@ type MediaCaptureLabels = {
|
|
|
126
127
|
photoMode: string;
|
|
127
128
|
videoMode: string;
|
|
128
129
|
};
|
|
130
|
+
/** @deprecated Since 3.55 (ADR-006, krizaka-ui#674): no product uses it. Removed in 4.0. */
|
|
129
131
|
declare const mediaCaptureLabels: MediaCaptureLabels;
|
|
130
132
|
type MediaCaptureProps = {
|
|
131
133
|
/** What it captures: `photo` (default), `video`, or `both` (a switch above the preview). */
|
|
@@ -149,7 +151,10 @@ type MediaCaptureProps = {
|
|
|
149
151
|
/** Classes merged on the root. */
|
|
150
152
|
className?: string;
|
|
151
153
|
};
|
|
152
|
-
/**
|
|
154
|
+
/**
|
|
155
|
+
* The camera in the page: photo or clip, front or back, with a file-picker way out when the camera cannot start.
|
|
156
|
+
* @deprecated Since 3.55 (ADR-006, krizaka-ui#674): no product uses it. Removed in 4.0.
|
|
157
|
+
*/
|
|
153
158
|
declare function MediaCapture({ mode, facing: initialFacing, audio, maxDuration, autoStart, onCapture, onError, aspectRatio, labels: given, className, }: MediaCaptureProps): React.JSX.Element;
|
|
154
159
|
|
|
155
160
|
export { type CaptureResult, MediaCapture, type MediaCaptureLabels, type MediaCaptureProps, mediaCapture, mediaCaptureLabels };
|
package/dist/media-capture.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import {
|
|
3
|
+
warnDeprecated
|
|
4
|
+
} from "./chunk-BDIU5LZB.js";
|
|
2
5
|
import {
|
|
3
6
|
formatClock,
|
|
4
7
|
playSafely,
|
|
@@ -58,6 +61,7 @@ function MediaCapture({
|
|
|
58
61
|
labels: given,
|
|
59
62
|
className
|
|
60
63
|
}) {
|
|
64
|
+
warnDeprecated("media-capture");
|
|
61
65
|
const labels = { ...mediaCaptureLabels, ...given };
|
|
62
66
|
const s = mediaCapture();
|
|
63
67
|
const reduce = usePrefersReducedMotion();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/media-capture/media-capture.tsx"],"sourcesContent":["// Client. The camera in the page: asks for it only when the person presses the button (never on load), shows the\n// preview (mirrored for the front camera), takes a photo (a canvas → JPEG `Blob`) or records a clip (MediaRecorder,\n// a timer, a maximum length), switches cameras. Every dead end has a way out: permission refused, no camera, no\n// `getUserMedia` (an old browser, an insecure page) → the file picker with `capture`, which opens the phone's own\n// camera. The tracks stop when it unmounts or the page is hidden.\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { CameraGlyph, FlipGlyph } from \"../glyphs\";\nimport { formatClock, playSafely, usePrefersReducedMotion } from \"../media\";\n\nexport const mediaCapture = tv({\n slots: {\n root: \"theme-dark relative isolate flex w-full flex-col overflow-hidden rounded-xl bg-media text-fg-on-media\",\n stage: \"relative w-full\",\n video: \"absolute inset-0 h-full w-full object-cover data-[mirrored]:-scale-x-100\",\n panel: \"absolute inset-0 flex flex-col items-center justify-center gap-3 p-6 text-center text-sm\",\n panelIcon: \"flex h-12 w-12 items-center justify-center rounded-full bg-fg-on-media/15\",\n primary:\n \"inline-flex h-10 items-center justify-center gap-2 rounded-lg bg-accent px-4 text-sm font-semibold text-on-accent hover:bg-accent-hover \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-media\",\n secondary:\n \"inline-flex h-10 items-center justify-center gap-2 rounded-lg border border-fg-on-media/40 px-4 text-sm font-semibold text-fg-on-media hover:bg-fg-on-media/10 \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n bar: \"absolute inset-x-0 bottom-0 flex items-center justify-center gap-6 bg-linear-to-t from-scrim-strong to-transparent px-4 pb-4 pt-10\",\n shutter:\n \"relative inline-flex h-16 w-16 items-center justify-center rounded-full border-4 border-fg-on-media bg-transparent \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-media \" +\n \"after:h-12 after:w-12 after:rounded-full after:bg-fg-on-media after:transition-all motion-reduce:after:transition-none \" +\n \"data-[mode=video]:after:bg-danger data-[recording]:after:h-6 data-[recording]:after:w-6 data-[recording]:after:rounded-md\",\n round:\n \"inline-flex h-11 w-11 items-center justify-center rounded-full bg-scrim-strong text-fg-on-media hover:bg-scrim \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring disabled:invisible\",\n modes: \"absolute inset-x-0 top-3 flex justify-center\",\n mode:\n \"rounded-full px-3 py-1 text-xs font-semibold text-fg-on-media/80 aria-pressed:bg-fg-on-media aria-pressed:text-media \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n timer:\n \"absolute left-3 top-3 inline-flex items-center gap-1.5 rounded-full bg-scrim-strong px-2.5 py-1 font-mono text-xs tabular-nums \" +\n \"before:h-2 before:w-2 before:rounded-full before:bg-danger before:animate-pulse motion-reduce:before:animate-none\",\n },\n});\n\nexport type CaptureResult = {\n /** The photo (JPEG) or the clip (WebM / MP4, as the browser records). */\n blob: Blob;\n /** `photo` or `video`. */\n kind: \"photo\" | \"video\";\n /** An object URL of the blob, to preview it (revoke it when done). */\n url: string;\n};\n\nexport type MediaCaptureLabels = {\n /** The accessible name of the camera (\"Camera\"). */\n region: string;\n /** Why the camera is asked for, before the request (\"Use your camera to take a photo\"). */\n prompt: string;\n /** The button that asks (\"Turn on the camera\"). */\n start: string;\n /** Permission refused (\"Camera access is blocked. Allow it in your browser settings, or choose a file.\"). */\n denied: string;\n /** No camera, or no access possible here (\"No camera is available.\"). */\n unavailable: string;\n /** The way out: the file picker (\"Choose a file\"). */\n fallback: string;\n takePhoto: string;\n startRecording: string;\n stopRecording: string;\n switchCamera: string;\n photoMode: string;\n videoMode: string;\n};\n\nexport const mediaCaptureLabels: MediaCaptureLabels = {\n region: \"Camera\",\n prompt: \"Use your camera to take a photo or record a clip.\",\n start: \"Turn on the camera\",\n denied: \"Camera access is blocked. Allow it in your browser settings, or choose a file.\",\n unavailable: \"No camera is available here.\",\n fallback: \"Choose a file\",\n takePhoto: \"Take photo\",\n startRecording: \"Start recording\",\n stopRecording: \"Stop recording\",\n switchCamera: \"Switch camera\",\n photoMode: \"Photo\",\n videoMode: \"Video\",\n};\n\nexport type MediaCaptureProps = {\n /** What it captures: `photo` (default), `video`, or `both` (a switch above the preview). */\n mode?: \"photo\" | \"video\" | \"both\";\n /** The camera at first: `user` (front, mirrored, default) or `environment` (back). */\n facing?: \"user\" | \"environment\";\n /** Records the microphone with a clip. Default `true`. */\n audio?: boolean;\n /** The longest clip, in seconds: recording stops by itself. */\n maxDuration?: number;\n /** Starts the camera at mount (only when the permission is already granted — never a prompt on load). */\n autoStart?: boolean;\n /** Called with a photo taken or a clip recorded (or a file picked in the fallback). */\n onCapture: (result: CaptureResult) => void;\n /** Called when the camera cannot start (`NotAllowedError`, `NotFoundError`…). */\n onError?: (error: unknown) => void;\n /** The width / height ratio of the preview. Default `\"3 / 4\"`. */\n aspectRatio?: string;\n /** The words, passed translated; English by default. */\n labels?: Partial<MediaCaptureLabels>;\n /** Classes merged on the root. */\n className?: string;\n};\n\ntype State = \"idle\" | \"starting\" | \"live\" | \"denied\" | \"unavailable\";\n\nconst supported = () => typeof navigator !== \"undefined\" && Boolean(navigator.mediaDevices?.getUserMedia);\nconst recorderType = () =>\n typeof MediaRecorder === \"undefined\" ? undefined : [\"video/webm;codecs=vp9,opus\", \"video/webm\", \"video/mp4\"].find((t) => MediaRecorder.isTypeSupported?.(t));\n\n/** The camera in the page: photo or clip, front or back, with a file-picker way out when the camera cannot start. */\nexport function MediaCapture({\n mode = \"photo\",\n facing: initialFacing = \"user\",\n audio = true,\n maxDuration,\n autoStart,\n onCapture,\n onError,\n aspectRatio = \"3 / 4\",\n labels: given,\n className,\n}: MediaCaptureProps) {\n const labels = { ...mediaCaptureLabels, ...given };\n const s = mediaCapture();\n const reduce = usePrefersReducedMotion();\n const video = React.useRef<HTMLVideoElement>(null);\n const stream = React.useRef<MediaStream | null>(null);\n const recorder = React.useRef<MediaRecorder | null>(null);\n const file = React.useRef<HTMLInputElement>(null);\n const [state, setState] = React.useState<State>(\"idle\");\n const [facing, setFacing] = React.useState(initialFacing);\n const [kind, setKind] = React.useState<\"photo\" | \"video\">(mode === \"video\" ? \"video\" : \"photo\");\n const [recording, setRecording] = React.useState(false);\n const [elapsed, setElapsed] = React.useState(0);\n const [cameras, setCameras] = React.useState(0);\n\n const stop = React.useCallback(() => {\n if (recorder.current?.state === \"recording\") recorder.current.stop();\n stream.current?.getTracks().forEach((track) => track.stop());\n stream.current = null;\n }, []);\n\n const start = React.useCallback(\n async (side: \"user\" | \"environment\") => {\n if (!supported()) {\n setState(\"unavailable\");\n return;\n }\n setState(\"starting\");\n stop();\n try {\n const media = await navigator.mediaDevices.getUserMedia({ video: { facingMode: side }, audio: mode !== \"photo\" && audio });\n stream.current = media;\n if (video.current) {\n video.current.srcObject = media;\n playSafely(video.current);\n }\n const devices = await navigator.mediaDevices.enumerateDevices?.().catch(() => []);\n setCameras((devices ?? []).filter((d) => d.kind === \"videoinput\").length);\n setState(\"live\");\n } catch (error) {\n const name = (error as { name?: string })?.name;\n setState(name === \"NotAllowedError\" || name === \"SecurityError\" ? \"denied\" : \"unavailable\");\n onError?.(error);\n }\n },\n [audio, mode, onError, stop],\n );\n\n React.useEffect(() => {\n if (!autoStart || !supported() || !navigator.permissions?.query) return;\n let live = true;\n navigator.permissions\n .query({ name: \"camera\" as PermissionName })\n .then((status) => {\n if (live && status.state === \"granted\") void start(initialFacing);\n })\n .catch(() => undefined);\n return () => {\n live = false;\n };\n }, [autoStart, initialFacing, start]);\n\n React.useEffect(() => {\n const hide = () => {\n if (!document.hidden) return;\n stop();\n setState((current) => (current === \"live\" ? \"idle\" : current));\n };\n document.addEventListener(\"visibilitychange\", hide);\n return () => {\n document.removeEventListener(\"visibilitychange\", hide);\n stop();\n };\n }, [stop]);\n\n React.useEffect(() => {\n if (!recording) return;\n const began = Date.now();\n const timer = setInterval(() => {\n const seconds = (Date.now() - began) / 1000;\n setElapsed(seconds);\n if (maxDuration && seconds >= maxDuration) recorder.current?.stop();\n }, 250);\n return () => clearInterval(timer);\n }, [recording, maxDuration]);\n\n const deliver = (blob: Blob, captured: \"photo\" | \"video\") => onCapture({ blob, kind: captured, url: URL.createObjectURL(blob) });\n\n const takePhoto = () => {\n const element = video.current;\n if (!element?.videoWidth) return;\n const canvas = document.createElement(\"canvas\");\n canvas.width = element.videoWidth;\n canvas.height = element.videoHeight;\n const context = canvas.getContext(\"2d\");\n if (!context) return;\n if (facing === \"user\") {\n context.translate(canvas.width, 0);\n context.scale(-1, 1);\n }\n context.drawImage(element, 0, 0);\n canvas.toBlob((blob) => blob && deliver(blob, \"photo\"), \"image/jpeg\", 0.92);\n };\n\n const toggleRecording = () => {\n if (recording) {\n recorder.current?.stop();\n return;\n }\n if (!stream.current) return;\n const chunks: Blob[] = [];\n const type = recorderType();\n const next = new MediaRecorder(stream.current, type ? { mimeType: type } : undefined);\n next.ondataavailable = (event) => event.data.size && chunks.push(event.data);\n next.onstop = () => {\n setRecording(false);\n setElapsed(0);\n deliver(new Blob(chunks, { type: next.mimeType || type || \"video/webm\" }), \"video\");\n };\n recorder.current = next;\n next.start(1000);\n setRecording(true);\n };\n\n const flip = () => {\n const side = facing === \"user\" ? \"environment\" : \"user\";\n setFacing(side);\n void start(side);\n };\n\n const accept = mode === \"video\" ? \"video/*\" : mode === \"photo\" ? \"image/*\" : \"image/*,video/*\";\n const fallback = (\n <>\n <button type=\"button\" className={s.secondary()} onClick={() => file.current?.click()}>\n {labels.fallback}\n </button>\n <input\n ref={file}\n type=\"file\"\n accept={accept}\n capture={facing}\n tabIndex={-1}\n aria-hidden\n className=\"sr-only\"\n onChange={(event) => {\n const picked = event.target.files?.[0];\n if (picked) deliver(picked, picked.type.startsWith(\"video/\") ? \"video\" : \"photo\");\n event.target.value = \"\";\n }}\n />\n </>\n );\n\n return (\n <section aria-label={labels.region} className={s.root({ className })}>\n <div className={s.stage()} style={{ aspectRatio }}>\n <video ref={video} muted playsInline aria-hidden data-mirrored={facing === \"user\" ? \"\" : undefined} className={s.video()} hidden={state !== \"live\"} />\n {state !== \"live\" && (\n <div className={s.panel()} role={state === \"denied\" || state === \"unavailable\" ? \"alert\" : undefined}>\n <span className={s.panelIcon()} aria-hidden>\n <CameraGlyph className=\"h-6 w-6\" />\n </span>\n <p className=\"max-w-xs\">{state === \"denied\" ? labels.denied : state === \"unavailable\" ? labels.unavailable : labels.prompt}</p>\n <div className=\"flex flex-wrap justify-center gap-2\">\n {(state === \"idle\" || state === \"starting\") && (\n <button type=\"button\" className={s.primary()} disabled={state === \"starting\"} onClick={() => void start(facing)}>\n <CameraGlyph className=\"h-4 w-4\" />\n {labels.start}\n </button>\n )}\n {fallback}\n </div>\n </div>\n )}\n {state === \"live\" && mode === \"both\" && !recording && (\n <div className={s.modes()}>\n <div className=\"flex gap-1 rounded-full bg-scrim-strong p-1\">\n <button type=\"button\" aria-pressed={kind === \"photo\"} className={s.mode()} onClick={() => setKind(\"photo\")}>\n {labels.photoMode}\n </button>\n <button type=\"button\" aria-pressed={kind === \"video\"} className={s.mode()} onClick={() => setKind(\"video\")}>\n {labels.videoMode}\n </button>\n </div>\n </div>\n )}\n {recording && (\n <span className={s.timer()} role=\"timer\" aria-live=\"off\">\n {formatClock(elapsed)}\n {maxDuration ? ` / ${formatClock(maxDuration)}` : \"\"}\n </span>\n )}\n {state === \"live\" && (\n <div className={s.bar()}>\n <span className=\"h-11 w-11\" />\n <button\n type=\"button\"\n className={s.shutter()}\n data-mode={kind}\n data-recording={recording ? \"\" : undefined}\n data-reduce={reduce ? \"\" : undefined}\n aria-label={kind === \"photo\" ? labels.takePhoto : recording ? labels.stopRecording : labels.startRecording}\n onClick={kind === \"photo\" ? takePhoto : toggleRecording}\n />\n <button type=\"button\" className={s.round()} aria-label={labels.switchCamera} disabled={cameras < 2 || recording} onClick={flip}>\n <FlipGlyph className=\"h-5 w-5\" />\n </button>\n </div>\n )}\n </div>\n </section>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/media-capture/media-capture.tsx"],"sourcesContent":["// Client. The camera in the page: asks for it only when the person presses the button (never on load), shows the\n// preview (mirrored for the front camera), takes a photo (a canvas → JPEG `Blob`) or records a clip (MediaRecorder,\n// a timer, a maximum length), switches cameras. Every dead end has a way out: permission refused, no camera, no\n// `getUserMedia` (an old browser, an insecure page) → the file picker with `capture`, which opens the phone's own\n// camera. The tracks stop when it unmounts or the page is hidden.\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { warnDeprecated } from \"../deprecated\";\nimport { CameraGlyph, FlipGlyph } from \"../glyphs\";\nimport { formatClock, playSafely, usePrefersReducedMotion } from \"../media\";\n\n/** @deprecated Since 3.55 (ADR-006, krizaka-ui#674): no product uses it. Removed in 4.0. */\nexport const mediaCapture = tv({\n slots: {\n root: \"theme-dark relative isolate flex w-full flex-col overflow-hidden rounded-xl bg-media text-fg-on-media\",\n stage: \"relative w-full\",\n video: \"absolute inset-0 h-full w-full object-cover data-[mirrored]:-scale-x-100\",\n panel: \"absolute inset-0 flex flex-col items-center justify-center gap-3 p-6 text-center text-sm\",\n panelIcon: \"flex h-12 w-12 items-center justify-center rounded-full bg-fg-on-media/15\",\n primary:\n \"inline-flex h-10 items-center justify-center gap-2 rounded-lg bg-accent px-4 text-sm font-semibold text-on-accent hover:bg-accent-hover \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-media\",\n secondary:\n \"inline-flex h-10 items-center justify-center gap-2 rounded-lg border border-fg-on-media/40 px-4 text-sm font-semibold text-fg-on-media hover:bg-fg-on-media/10 \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n bar: \"absolute inset-x-0 bottom-0 flex items-center justify-center gap-6 bg-linear-to-t from-scrim-strong to-transparent px-4 pb-4 pt-10\",\n shutter:\n \"relative inline-flex h-16 w-16 items-center justify-center rounded-full border-4 border-fg-on-media bg-transparent \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-media \" +\n \"after:h-12 after:w-12 after:rounded-full after:bg-fg-on-media after:transition-all motion-reduce:after:transition-none \" +\n \"data-[mode=video]:after:bg-danger data-[recording]:after:h-6 data-[recording]:after:w-6 data-[recording]:after:rounded-md\",\n round:\n \"inline-flex h-11 w-11 items-center justify-center rounded-full bg-scrim-strong text-fg-on-media hover:bg-scrim \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring disabled:invisible\",\n modes: \"absolute inset-x-0 top-3 flex justify-center\",\n mode:\n \"rounded-full px-3 py-1 text-xs font-semibold text-fg-on-media/80 aria-pressed:bg-fg-on-media aria-pressed:text-media \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n timer:\n \"absolute left-3 top-3 inline-flex items-center gap-1.5 rounded-full bg-scrim-strong px-2.5 py-1 font-mono text-xs tabular-nums \" +\n \"before:h-2 before:w-2 before:rounded-full before:bg-danger before:animate-pulse motion-reduce:before:animate-none\",\n },\n});\n\nexport type CaptureResult = {\n /** The photo (JPEG) or the clip (WebM / MP4, as the browser records). */\n blob: Blob;\n /** `photo` or `video`. */\n kind: \"photo\" | \"video\";\n /** An object URL of the blob, to preview it (revoke it when done). */\n url: string;\n};\n\nexport type MediaCaptureLabels = {\n /** The accessible name of the camera (\"Camera\"). */\n region: string;\n /** Why the camera is asked for, before the request (\"Use your camera to take a photo\"). */\n prompt: string;\n /** The button that asks (\"Turn on the camera\"). */\n start: string;\n /** Permission refused (\"Camera access is blocked. Allow it in your browser settings, or choose a file.\"). */\n denied: string;\n /** No camera, or no access possible here (\"No camera is available.\"). */\n unavailable: string;\n /** The way out: the file picker (\"Choose a file\"). */\n fallback: string;\n takePhoto: string;\n startRecording: string;\n stopRecording: string;\n switchCamera: string;\n photoMode: string;\n videoMode: string;\n};\n\n/** @deprecated Since 3.55 (ADR-006, krizaka-ui#674): no product uses it. Removed in 4.0. */\nexport const mediaCaptureLabels: MediaCaptureLabels = {\n region: \"Camera\",\n prompt: \"Use your camera to take a photo or record a clip.\",\n start: \"Turn on the camera\",\n denied: \"Camera access is blocked. Allow it in your browser settings, or choose a file.\",\n unavailable: \"No camera is available here.\",\n fallback: \"Choose a file\",\n takePhoto: \"Take photo\",\n startRecording: \"Start recording\",\n stopRecording: \"Stop recording\",\n switchCamera: \"Switch camera\",\n photoMode: \"Photo\",\n videoMode: \"Video\",\n};\n\nexport type MediaCaptureProps = {\n /** What it captures: `photo` (default), `video`, or `both` (a switch above the preview). */\n mode?: \"photo\" | \"video\" | \"both\";\n /** The camera at first: `user` (front, mirrored, default) or `environment` (back). */\n facing?: \"user\" | \"environment\";\n /** Records the microphone with a clip. Default `true`. */\n audio?: boolean;\n /** The longest clip, in seconds: recording stops by itself. */\n maxDuration?: number;\n /** Starts the camera at mount (only when the permission is already granted — never a prompt on load). */\n autoStart?: boolean;\n /** Called with a photo taken or a clip recorded (or a file picked in the fallback). */\n onCapture: (result: CaptureResult) => void;\n /** Called when the camera cannot start (`NotAllowedError`, `NotFoundError`…). */\n onError?: (error: unknown) => void;\n /** The width / height ratio of the preview. Default `\"3 / 4\"`. */\n aspectRatio?: string;\n /** The words, passed translated; English by default. */\n labels?: Partial<MediaCaptureLabels>;\n /** Classes merged on the root. */\n className?: string;\n};\n\ntype State = \"idle\" | \"starting\" | \"live\" | \"denied\" | \"unavailable\";\n\nconst supported = () => typeof navigator !== \"undefined\" && Boolean(navigator.mediaDevices?.getUserMedia);\nconst recorderType = () =>\n typeof MediaRecorder === \"undefined\" ? undefined : [\"video/webm;codecs=vp9,opus\", \"video/webm\", \"video/mp4\"].find((t) => MediaRecorder.isTypeSupported?.(t));\n\n/**\n * The camera in the page: photo or clip, front or back, with a file-picker way out when the camera cannot start.\n * @deprecated Since 3.55 (ADR-006, krizaka-ui#674): no product uses it. Removed in 4.0.\n */\nexport function MediaCapture({\n mode = \"photo\",\n facing: initialFacing = \"user\",\n audio = true,\n maxDuration,\n autoStart,\n onCapture,\n onError,\n aspectRatio = \"3 / 4\",\n labels: given,\n className,\n}: MediaCaptureProps) {\n warnDeprecated(\"media-capture\");\n const labels = { ...mediaCaptureLabels, ...given };\n const s = mediaCapture();\n const reduce = usePrefersReducedMotion();\n const video = React.useRef<HTMLVideoElement>(null);\n const stream = React.useRef<MediaStream | null>(null);\n const recorder = React.useRef<MediaRecorder | null>(null);\n const file = React.useRef<HTMLInputElement>(null);\n const [state, setState] = React.useState<State>(\"idle\");\n const [facing, setFacing] = React.useState(initialFacing);\n const [kind, setKind] = React.useState<\"photo\" | \"video\">(mode === \"video\" ? \"video\" : \"photo\");\n const [recording, setRecording] = React.useState(false);\n const [elapsed, setElapsed] = React.useState(0);\n const [cameras, setCameras] = React.useState(0);\n\n const stop = React.useCallback(() => {\n if (recorder.current?.state === \"recording\") recorder.current.stop();\n stream.current?.getTracks().forEach((track) => track.stop());\n stream.current = null;\n }, []);\n\n const start = React.useCallback(\n async (side: \"user\" | \"environment\") => {\n if (!supported()) {\n setState(\"unavailable\");\n return;\n }\n setState(\"starting\");\n stop();\n try {\n const media = await navigator.mediaDevices.getUserMedia({ video: { facingMode: side }, audio: mode !== \"photo\" && audio });\n stream.current = media;\n if (video.current) {\n video.current.srcObject = media;\n playSafely(video.current);\n }\n const devices = await navigator.mediaDevices.enumerateDevices?.().catch(() => []);\n setCameras((devices ?? []).filter((d) => d.kind === \"videoinput\").length);\n setState(\"live\");\n } catch (error) {\n const name = (error as { name?: string })?.name;\n setState(name === \"NotAllowedError\" || name === \"SecurityError\" ? \"denied\" : \"unavailable\");\n onError?.(error);\n }\n },\n [audio, mode, onError, stop],\n );\n\n React.useEffect(() => {\n if (!autoStart || !supported() || !navigator.permissions?.query) return;\n let live = true;\n navigator.permissions\n .query({ name: \"camera\" as PermissionName })\n .then((status) => {\n if (live && status.state === \"granted\") void start(initialFacing);\n })\n .catch(() => undefined);\n return () => {\n live = false;\n };\n }, [autoStart, initialFacing, start]);\n\n React.useEffect(() => {\n const hide = () => {\n if (!document.hidden) return;\n stop();\n setState((current) => (current === \"live\" ? \"idle\" : current));\n };\n document.addEventListener(\"visibilitychange\", hide);\n return () => {\n document.removeEventListener(\"visibilitychange\", hide);\n stop();\n };\n }, [stop]);\n\n React.useEffect(() => {\n if (!recording) return;\n const began = Date.now();\n const timer = setInterval(() => {\n const seconds = (Date.now() - began) / 1000;\n setElapsed(seconds);\n if (maxDuration && seconds >= maxDuration) recorder.current?.stop();\n }, 250);\n return () => clearInterval(timer);\n }, [recording, maxDuration]);\n\n const deliver = (blob: Blob, captured: \"photo\" | \"video\") => onCapture({ blob, kind: captured, url: URL.createObjectURL(blob) });\n\n const takePhoto = () => {\n const element = video.current;\n if (!element?.videoWidth) return;\n const canvas = document.createElement(\"canvas\");\n canvas.width = element.videoWidth;\n canvas.height = element.videoHeight;\n const context = canvas.getContext(\"2d\");\n if (!context) return;\n if (facing === \"user\") {\n context.translate(canvas.width, 0);\n context.scale(-1, 1);\n }\n context.drawImage(element, 0, 0);\n canvas.toBlob((blob) => blob && deliver(blob, \"photo\"), \"image/jpeg\", 0.92);\n };\n\n const toggleRecording = () => {\n if (recording) {\n recorder.current?.stop();\n return;\n }\n if (!stream.current) return;\n const chunks: Blob[] = [];\n const type = recorderType();\n const next = new MediaRecorder(stream.current, type ? { mimeType: type } : undefined);\n next.ondataavailable = (event) => event.data.size && chunks.push(event.data);\n next.onstop = () => {\n setRecording(false);\n setElapsed(0);\n deliver(new Blob(chunks, { type: next.mimeType || type || \"video/webm\" }), \"video\");\n };\n recorder.current = next;\n next.start(1000);\n setRecording(true);\n };\n\n const flip = () => {\n const side = facing === \"user\" ? \"environment\" : \"user\";\n setFacing(side);\n void start(side);\n };\n\n const accept = mode === \"video\" ? \"video/*\" : mode === \"photo\" ? \"image/*\" : \"image/*,video/*\";\n const fallback = (\n <>\n <button type=\"button\" className={s.secondary()} onClick={() => file.current?.click()}>\n {labels.fallback}\n </button>\n <input\n ref={file}\n type=\"file\"\n accept={accept}\n capture={facing}\n tabIndex={-1}\n aria-hidden\n className=\"sr-only\"\n onChange={(event) => {\n const picked = event.target.files?.[0];\n if (picked) deliver(picked, picked.type.startsWith(\"video/\") ? \"video\" : \"photo\");\n event.target.value = \"\";\n }}\n />\n </>\n );\n\n return (\n <section aria-label={labels.region} className={s.root({ className })}>\n <div className={s.stage()} style={{ aspectRatio }}>\n <video ref={video} muted playsInline aria-hidden data-mirrored={facing === \"user\" ? \"\" : undefined} className={s.video()} hidden={state !== \"live\"} />\n {state !== \"live\" && (\n <div className={s.panel()} role={state === \"denied\" || state === \"unavailable\" ? \"alert\" : undefined}>\n <span className={s.panelIcon()} aria-hidden>\n <CameraGlyph className=\"h-6 w-6\" />\n </span>\n <p className=\"max-w-xs\">{state === \"denied\" ? labels.denied : state === \"unavailable\" ? labels.unavailable : labels.prompt}</p>\n <div className=\"flex flex-wrap justify-center gap-2\">\n {(state === \"idle\" || state === \"starting\") && (\n <button type=\"button\" className={s.primary()} disabled={state === \"starting\"} onClick={() => void start(facing)}>\n <CameraGlyph className=\"h-4 w-4\" />\n {labels.start}\n </button>\n )}\n {fallback}\n </div>\n </div>\n )}\n {state === \"live\" && mode === \"both\" && !recording && (\n <div className={s.modes()}>\n <div className=\"flex gap-1 rounded-full bg-scrim-strong p-1\">\n <button type=\"button\" aria-pressed={kind === \"photo\"} className={s.mode()} onClick={() => setKind(\"photo\")}>\n {labels.photoMode}\n </button>\n <button type=\"button\" aria-pressed={kind === \"video\"} className={s.mode()} onClick={() => setKind(\"video\")}>\n {labels.videoMode}\n </button>\n </div>\n </div>\n )}\n {recording && (\n <span className={s.timer()} role=\"timer\" aria-live=\"off\">\n {formatClock(elapsed)}\n {maxDuration ? ` / ${formatClock(maxDuration)}` : \"\"}\n </span>\n )}\n {state === \"live\" && (\n <div className={s.bar()}>\n <span className=\"h-11 w-11\" />\n <button\n type=\"button\"\n className={s.shutter()}\n data-mode={kind}\n data-recording={recording ? \"\" : undefined}\n data-reduce={reduce ? \"\" : undefined}\n aria-label={kind === \"photo\" ? labels.takePhoto : recording ? labels.stopRecording : labels.startRecording}\n onClick={kind === \"photo\" ? takePhoto : toggleRecording}\n />\n <button type=\"button\" className={s.round()} aria-label={labels.switchCamera} disabled={cameras < 2 || recording} onClick={flip}>\n <FlipGlyph className=\"h-5 w-5\" />\n </button>\n </div>\n )}\n </div>\n </section>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AAKA,YAAY,WAAW;AACvB,SAAS,UAAU;AAsQf,mBACE,KADF;AA/PG,IAAM,eAAe,GAAG;AAAA,EAC7B,OAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO;AAAA,IACP,OAAO;AAAA,IACP,WAAW;AAAA,IACX,SACE;AAAA,IAEF,WACE;AAAA,IAEF,KAAK;AAAA,IACL,SACE;AAAA,IAIF,OACE;AAAA,IAEF,OAAO;AAAA,IACP,MACE;AAAA,IAEF,OACE;AAAA,EAEJ;AACF,CAAC;AAiCM,IAAM,qBAAyC;AAAA,EACpD,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,UAAU;AAAA,EACV,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,cAAc;AAAA,EACd,WAAW;AAAA,EACX,WAAW;AACb;AA2BA,IAAM,YAAY,MAAM,OAAO,cAAc,eAAe,QAAQ,UAAU,cAAc,YAAY;AACxG,IAAM,eAAe,MACnB,OAAO,kBAAkB,cAAc,SAAY,CAAC,8BAA8B,cAAc,WAAW,EAAE,KAAK,CAAC,MAAM,cAAc,kBAAkB,CAAC,CAAC;AAMtJ,SAAS,aAAa;AAAA,EAC3B,OAAO;AAAA,EACP,QAAQ,gBAAgB;AAAA,EACxB,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,QAAQ;AAAA,EACR;AACF,GAAsB;AACpB,iBAAe,eAAe;AAC9B,QAAM,SAAS,EAAE,GAAG,oBAAoB,GAAG,MAAM;AACjD,QAAM,IAAI,aAAa;AACvB,QAAM,SAAS,wBAAwB;AACvC,QAAM,QAAc,aAAyB,IAAI;AACjD,QAAM,SAAe,aAA2B,IAAI;AACpD,QAAM,WAAiB,aAA6B,IAAI;AACxD,QAAM,OAAa,aAAyB,IAAI;AAChD,QAAM,CAAC,OAAO,QAAQ,IAAU,eAAgB,MAAM;AACtD,QAAM,CAAC,QAAQ,SAAS,IAAU,eAAS,aAAa;AACxD,QAAM,CAAC,MAAM,OAAO,IAAU,eAA4B,SAAS,UAAU,UAAU,OAAO;AAC9F,QAAM,CAAC,WAAW,YAAY,IAAU,eAAS,KAAK;AACtD,QAAM,CAAC,SAAS,UAAU,IAAU,eAAS,CAAC;AAC9C,QAAM,CAAC,SAAS,UAAU,IAAU,eAAS,CAAC;AAE9C,QAAM,OAAa,kBAAY,MAAM;AACnC,QAAI,SAAS,SAAS,UAAU,YAAa,UAAS,QAAQ,KAAK;AACnE,WAAO,SAAS,UAAU,EAAE,QAAQ,CAAC,UAAU,MAAM,KAAK,CAAC;AAC3D,WAAO,UAAU;AAAA,EACnB,GAAG,CAAC,CAAC;AAEL,QAAM,QAAc;AAAA,IAClB,OAAO,SAAiC;AACtC,UAAI,CAAC,UAAU,GAAG;AAChB,iBAAS,aAAa;AACtB;AAAA,MACF;AACA,eAAS,UAAU;AACnB,WAAK;AACL,UAAI;AACF,cAAM,QAAQ,MAAM,UAAU,aAAa,aAAa,EAAE,OAAO,EAAE,YAAY,KAAK,GAAG,OAAO,SAAS,WAAW,MAAM,CAAC;AACzH,eAAO,UAAU;AACjB,YAAI,MAAM,SAAS;AACjB,gBAAM,QAAQ,YAAY;AAC1B,qBAAW,MAAM,OAAO;AAAA,QAC1B;AACA,cAAM,UAAU,MAAM,UAAU,aAAa,mBAAmB,EAAE,MAAM,MAAM,CAAC,CAAC;AAChF,oBAAY,WAAW,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,MAAM;AACxE,iBAAS,MAAM;AAAA,MACjB,SAAS,OAAO;AACd,cAAM,OAAQ,OAA6B;AAC3C,iBAAS,SAAS,qBAAqB,SAAS,kBAAkB,WAAW,aAAa;AAC1F,kBAAU,KAAK;AAAA,MACjB;AAAA,IACF;AAAA,IACA,CAAC,OAAO,MAAM,SAAS,IAAI;AAAA,EAC7B;AAEA,EAAM,gBAAU,MAAM;AACpB,QAAI,CAAC,aAAa,CAAC,UAAU,KAAK,CAAC,UAAU,aAAa,MAAO;AACjE,QAAI,OAAO;AACX,cAAU,YACP,MAAM,EAAE,MAAM,SAA2B,CAAC,EAC1C,KAAK,CAAC,WAAW;AAChB,UAAI,QAAQ,OAAO,UAAU,UAAW,MAAK,MAAM,aAAa;AAAA,IAClE,CAAC,EACA,MAAM,MAAM,MAAS;AACxB,WAAO,MAAM;AACX,aAAO;AAAA,IACT;AAAA,EACF,GAAG,CAAC,WAAW,eAAe,KAAK,CAAC;AAEpC,EAAM,gBAAU,MAAM;AACpB,UAAM,OAAO,MAAM;AACjB,UAAI,CAAC,SAAS,OAAQ;AACtB,WAAK;AACL,eAAS,CAAC,YAAa,YAAY,SAAS,SAAS,OAAQ;AAAA,IAC/D;AACA,aAAS,iBAAiB,oBAAoB,IAAI;AAClD,WAAO,MAAM;AACX,eAAS,oBAAoB,oBAAoB,IAAI;AACrD,WAAK;AAAA,IACP;AAAA,EACF,GAAG,CAAC,IAAI,CAAC;AAET,EAAM,gBAAU,MAAM;AACpB,QAAI,CAAC,UAAW;AAChB,UAAM,QAAQ,KAAK,IAAI;AACvB,UAAM,QAAQ,YAAY,MAAM;AAC9B,YAAM,WAAW,KAAK,IAAI,IAAI,SAAS;AACvC,iBAAW,OAAO;AAClB,UAAI,eAAe,WAAW,YAAa,UAAS,SAAS,KAAK;AAAA,IACpE,GAAG,GAAG;AACN,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,WAAW,WAAW,CAAC;AAE3B,QAAM,UAAU,CAAC,MAAY,aAAgC,UAAU,EAAE,MAAM,MAAM,UAAU,KAAK,IAAI,gBAAgB,IAAI,EAAE,CAAC;AAE/H,QAAM,YAAY,MAAM;AACtB,UAAM,UAAU,MAAM;AACtB,QAAI,CAAC,SAAS,WAAY;AAC1B,UAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,WAAO,QAAQ,QAAQ;AACvB,WAAO,SAAS,QAAQ;AACxB,UAAM,UAAU,OAAO,WAAW,IAAI;AACtC,QAAI,CAAC,QAAS;AACd,QAAI,WAAW,QAAQ;AACrB,cAAQ,UAAU,OAAO,OAAO,CAAC;AACjC,cAAQ,MAAM,IAAI,CAAC;AAAA,IACrB;AACA,YAAQ,UAAU,SAAS,GAAG,CAAC;AAC/B,WAAO,OAAO,CAAC,SAAS,QAAQ,QAAQ,MAAM,OAAO,GAAG,cAAc,IAAI;AAAA,EAC5E;AAEA,QAAM,kBAAkB,MAAM;AAC5B,QAAI,WAAW;AACb,eAAS,SAAS,KAAK;AACvB;AAAA,IACF;AACA,QAAI,CAAC,OAAO,QAAS;AACrB,UAAM,SAAiB,CAAC;AACxB,UAAM,OAAO,aAAa;AAC1B,UAAM,OAAO,IAAI,cAAc,OAAO,SAAS,OAAO,EAAE,UAAU,KAAK,IAAI,MAAS;AACpF,SAAK,kBAAkB,CAAC,UAAU,MAAM,KAAK,QAAQ,OAAO,KAAK,MAAM,IAAI;AAC3E,SAAK,SAAS,MAAM;AAClB,mBAAa,KAAK;AAClB,iBAAW,CAAC;AACZ,cAAQ,IAAI,KAAK,QAAQ,EAAE,MAAM,KAAK,YAAY,QAAQ,aAAa,CAAC,GAAG,OAAO;AAAA,IACpF;AACA,aAAS,UAAU;AACnB,SAAK,MAAM,GAAI;AACf,iBAAa,IAAI;AAAA,EACnB;AAEA,QAAM,OAAO,MAAM;AACjB,UAAM,OAAO,WAAW,SAAS,gBAAgB;AACjD,cAAU,IAAI;AACd,SAAK,MAAM,IAAI;AAAA,EACjB;AAEA,QAAM,SAAS,SAAS,UAAU,YAAY,SAAS,UAAU,YAAY;AAC7E,QAAM,WACJ,iCACE;AAAA,wBAAC,YAAO,MAAK,UAAS,WAAW,EAAE,UAAU,GAAG,SAAS,MAAM,KAAK,SAAS,MAAM,GAChF,iBAAO,UACV;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,MAAK;AAAA,QACL;AAAA,QACA,SAAS;AAAA,QACT,UAAU;AAAA,QACV,eAAW;AAAA,QACX,WAAU;AAAA,QACV,UAAU,CAAC,UAAU;AACnB,gBAAM,SAAS,MAAM,OAAO,QAAQ,CAAC;AACrC,cAAI,OAAQ,SAAQ,QAAQ,OAAO,KAAK,WAAW,QAAQ,IAAI,UAAU,OAAO;AAChF,gBAAM,OAAO,QAAQ;AAAA,QACvB;AAAA;AAAA,IACF;AAAA,KACF;AAGF,SACE,oBAAC,aAAQ,cAAY,OAAO,QAAQ,WAAW,EAAE,KAAK,EAAE,UAAU,CAAC,GACjE,+BAAC,SAAI,WAAW,EAAE,MAAM,GAAG,OAAO,EAAE,YAAY,GAC9C;AAAA,wBAAC,WAAM,KAAK,OAAO,OAAK,MAAC,aAAW,MAAC,eAAW,MAAC,iBAAe,WAAW,SAAS,KAAK,QAAW,WAAW,EAAE,MAAM,GAAG,QAAQ,UAAU,QAAQ;AAAA,IACnJ,UAAU,UACT,qBAAC,SAAI,WAAW,EAAE,MAAM,GAAG,MAAM,UAAU,YAAY,UAAU,gBAAgB,UAAU,QACzF;AAAA,0BAAC,UAAK,WAAW,EAAE,UAAU,GAAG,eAAW,MACzC,8BAAC,eAAY,WAAU,WAAU,GACnC;AAAA,MACA,oBAAC,OAAE,WAAU,YAAY,oBAAU,WAAW,OAAO,SAAS,UAAU,gBAAgB,OAAO,cAAc,OAAO,QAAO;AAAA,MAC3H,qBAAC,SAAI,WAAU,uCACX;AAAA,mBAAU,UAAU,UAAU,eAC9B,qBAAC,YAAO,MAAK,UAAS,WAAW,EAAE,QAAQ,GAAG,UAAU,UAAU,YAAY,SAAS,MAAM,KAAK,MAAM,MAAM,GAC5G;AAAA,8BAAC,eAAY,WAAU,WAAU;AAAA,UAChC,OAAO;AAAA,WACV;AAAA,QAED;AAAA,SACH;AAAA,OACF;AAAA,IAED,UAAU,UAAU,SAAS,UAAU,CAAC,aACvC,oBAAC,SAAI,WAAW,EAAE,MAAM,GACtB,+BAAC,SAAI,WAAU,+CACb;AAAA,0BAAC,YAAO,MAAK,UAAS,gBAAc,SAAS,SAAS,WAAW,EAAE,KAAK,GAAG,SAAS,MAAM,QAAQ,OAAO,GACtG,iBAAO,WACV;AAAA,MACA,oBAAC,YAAO,MAAK,UAAS,gBAAc,SAAS,SAAS,WAAW,EAAE,KAAK,GAAG,SAAS,MAAM,QAAQ,OAAO,GACtG,iBAAO,WACV;AAAA,OACF,GACF;AAAA,IAED,aACC,qBAAC,UAAK,WAAW,EAAE,MAAM,GAAG,MAAK,SAAQ,aAAU,OAChD;AAAA,kBAAY,OAAO;AAAA,MACnB,cAAc,MAAM,YAAY,WAAW,CAAC,KAAK;AAAA,OACpD;AAAA,IAED,UAAU,UACT,qBAAC,SAAI,WAAW,EAAE,IAAI,GACpB;AAAA,0BAAC,UAAK,WAAU,aAAY;AAAA,MAC5B;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAW,EAAE,QAAQ;AAAA,UACrB,aAAW;AAAA,UACX,kBAAgB,YAAY,KAAK;AAAA,UACjC,eAAa,SAAS,KAAK;AAAA,UAC3B,cAAY,SAAS,UAAU,OAAO,YAAY,YAAY,OAAO,gBAAgB,OAAO;AAAA,UAC5F,SAAS,SAAS,UAAU,YAAY;AAAA;AAAA,MAC1C;AAAA,MACA,oBAAC,YAAO,MAAK,UAAS,WAAW,EAAE,MAAM,GAAG,cAAY,OAAO,cAAc,UAAU,UAAU,KAAK,WAAW,SAAS,MACxH,8BAAC,aAAU,WAAU,WAAU,GACjC;AAAA,OACF;AAAA,KAEJ,GACF;AAEJ;","names":[]}
|
package/dist/meter.js
CHANGED
|
@@ -1,124 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
var MeterLevel = { Ok: "ok", Success: "success", Warning: "warning", Danger: "danger" };
|
|
7
|
-
var clamp01 = (n) => Math.min(1, Math.max(0, n));
|
|
8
|
-
var percent = (ratio) => `${Math.round(ratio * 100)} %`;
|
|
9
|
-
function levelOf(ratio, thresholds, optimum) {
|
|
10
|
-
if (!thresholds) return MeterLevel.Ok;
|
|
11
|
-
const good = optimum === MeterOptimum.Low ? MeterLevel.Success : MeterLevel.Danger;
|
|
12
|
-
const bad = optimum === MeterOptimum.Low ? MeterLevel.Danger : MeterLevel.Success;
|
|
13
|
-
if (ratio >= thresholds.high) return bad;
|
|
14
|
-
return ratio >= thresholds.low ? MeterLevel.Warning : good;
|
|
15
|
-
}
|
|
16
|
-
function meterModel({ value = 0, min = 0, max = 100, valueText, thresholds, optimum = MeterOptimum.Low, parts }) {
|
|
17
|
-
const span = max - min || 1;
|
|
18
|
-
const ratio = clamp01((value - min) / span);
|
|
19
|
-
const shares = (parts ?? []).map((p, i) => ({ ...p, i, ratio: clamp01(p.value / span), words: p.valueText ?? percent(p.value / span) }));
|
|
20
|
-
let start = 0;
|
|
21
|
-
const arcs = parts ? shares.map((p) => {
|
|
22
|
-
const arc = { part: p.i, len: p.ratio, start };
|
|
23
|
-
start += p.ratio;
|
|
24
|
-
return arc;
|
|
25
|
-
}) : [{ part: void 0, len: ratio, start: 0 }];
|
|
26
|
-
return { ratio, level: levelOf(ratio, thresholds, optimum), words: valueText ?? percent(ratio), shares, arcs };
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// src/meter/meter.tsx
|
|
30
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
31
|
-
var meter = tv({
|
|
32
|
-
slots: {
|
|
33
|
-
root: "flex w-full flex-col gap-1.5",
|
|
34
|
-
header: "flex items-baseline gap-2 text-xs",
|
|
35
|
-
caption: "min-w-0 flex-1 truncate font-medium text-fg",
|
|
36
|
-
figure: "shrink-0 tabular-nums text-fg-secondary",
|
|
37
|
-
track: "relative flex h-2 w-full gap-0.5 overflow-hidden rounded-sm bg-surface-3",
|
|
38
|
-
fill: "h-full rounded-sm transition-[width] duration-700 motion-reduce:transition-none",
|
|
39
|
-
legend: "mt-1 flex flex-wrap gap-x-4 gap-y-1 text-xs text-fg-secondary",
|
|
40
|
-
swatch: "inline-block h-2 w-2 shrink-0 rounded-full",
|
|
41
|
-
ring: "relative inline-flex shrink-0 items-center justify-center",
|
|
42
|
-
center: "absolute inset-0 flex flex-col items-center justify-center text-center"
|
|
43
|
-
},
|
|
44
|
-
variants: {
|
|
45
|
-
size: { sm: { track: "h-1.5", ring: "h-14 w-14" }, md: { ring: "h-24 w-24" }, lg: { track: "h-3", ring: "h-36 w-36" } }
|
|
46
|
-
},
|
|
47
|
-
defaultVariants: { size: "md" }
|
|
48
|
-
});
|
|
49
|
-
var BG = { ok: "bg-accent", success: "bg-success", warning: "bg-warning", danger: "bg-danger" };
|
|
50
|
-
var STROKE = { ok: "stroke-accent", success: "stroke-success", warning: "stroke-warning", danger: "stroke-danger" };
|
|
51
|
-
var PART_BG = ["bg-accent", "bg-fg-secondary", "bg-accent-2", "bg-border-strong"];
|
|
52
|
-
var PART_STROKE = ["stroke-accent", "stroke-fg-secondary", "stroke-accent-2", "stroke-border-strong"];
|
|
53
|
-
function Meter({
|
|
54
|
-
label,
|
|
55
|
-
value = 0,
|
|
56
|
-
min = 0,
|
|
57
|
-
max = 100,
|
|
58
|
-
valueText,
|
|
59
|
-
thresholds,
|
|
60
|
-
optimum = "low",
|
|
61
|
-
parts,
|
|
62
|
-
variant = "bar",
|
|
63
|
-
size = "md",
|
|
64
|
-
showLabel,
|
|
65
|
-
className,
|
|
66
|
-
children,
|
|
67
|
-
...props
|
|
68
|
-
}) {
|
|
69
|
-
const s = meter({ size });
|
|
70
|
-
const { ratio, level, words, shares, arcs } = meterModel({ value, min, max, valueText, thresholds, optimum, parts });
|
|
71
|
-
const a11y = parts ? { role: "group", "aria-label": label } : { role: "meter", "aria-label": label, "aria-valuemin": min, "aria-valuemax": max, "aria-valuenow": Math.min(max, Math.max(min, value)), "aria-valuetext": words };
|
|
72
|
-
const legend = parts && /* @__PURE__ */ jsx("ul", { className: s.legend(), children: shares.map((p) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-1.5", children: [
|
|
73
|
-
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: [s.swatch(), PART_BG[p.i % PART_BG.length]].join(" ") }),
|
|
74
|
-
/* @__PURE__ */ jsx("span", { className: "text-fg", children: p.label }),
|
|
75
|
-
/* @__PURE__ */ jsx("span", { className: "tabular-nums", children: p.words })
|
|
76
|
-
] }, p.i)) });
|
|
77
|
-
if (variant === "ring") {
|
|
78
|
-
const r = 44;
|
|
79
|
-
return /* @__PURE__ */ jsxs("div", { ...a11y, "data-level": parts ? void 0 : level, className: ["inline-flex flex-col items-center gap-2", className].filter(Boolean).join(" "), ...props, children: [
|
|
80
|
-
/* @__PURE__ */ jsxs("div", { className: s.ring(), children: [
|
|
81
|
-
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 100 100", className: "h-full w-full -rotate-90", "aria-hidden": true, children: [
|
|
82
|
-
/* @__PURE__ */ jsx("circle", { cx: "50", cy: "50", r, fill: "none", strokeWidth: "9", className: "stroke-border-default" }),
|
|
83
|
-
arcs.map((arc, i) => {
|
|
84
|
-
const length = Math.max(0, arc.len * 100 - (parts ? 1 : 0));
|
|
85
|
-
return /* @__PURE__ */ jsx(
|
|
86
|
-
"circle",
|
|
87
|
-
{
|
|
88
|
-
cx: "50",
|
|
89
|
-
cy: "50",
|
|
90
|
-
r,
|
|
91
|
-
fill: "none",
|
|
92
|
-
strokeWidth: "9",
|
|
93
|
-
pathLength: 100,
|
|
94
|
-
strokeDasharray: `${length} ${100 - length}`,
|
|
95
|
-
strokeDashoffset: -arc.start * 100,
|
|
96
|
-
className: [arc.part === void 0 ? STROKE[level] : PART_STROKE[arc.part % PART_STROKE.length], "transition-[stroke-dasharray] duration-700 motion-reduce:transition-none"].join(" ")
|
|
97
|
-
},
|
|
98
|
-
i
|
|
99
|
-
);
|
|
100
|
-
})
|
|
101
|
-
] }),
|
|
102
|
-
children !== void 0 && /* @__PURE__ */ jsx("div", { className: s.center(), children })
|
|
103
|
-
] }),
|
|
104
|
-
legend
|
|
105
|
-
] });
|
|
106
|
-
}
|
|
107
|
-
return /* @__PURE__ */ jsxs("div", { ...a11y, "data-level": parts ? void 0 : level, className: s.root({ className }), ...props, children: [
|
|
108
|
-
showLabel && /* @__PURE__ */ jsxs("div", { "aria-hidden": true, className: s.header(), children: [
|
|
109
|
-
/* @__PURE__ */ jsx("span", { className: s.caption(), children: label }),
|
|
110
|
-
!parts && /* @__PURE__ */ jsx("span", { className: s.figure(), children: words })
|
|
111
|
-
] }),
|
|
112
|
-
/* @__PURE__ */ jsx("div", { "aria-hidden": true, className: s.track(), children: parts ? shares.map((p) => /* @__PURE__ */ jsx("span", { className: [s.fill(), PART_BG[p.i % PART_BG.length]].join(" "), style: { width: `${p.ratio * 100}%` } }, p.i)) : /* @__PURE__ */ jsx("span", { className: [s.fill(), BG[level]].join(" "), style: { width: `${ratio * 100}%` } }) }),
|
|
113
|
-
legend
|
|
114
|
-
] });
|
|
115
|
-
}
|
|
116
|
-
function MeterValue({ value, caption }) {
|
|
117
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
118
|
-
/* @__PURE__ */ jsx("span", { className: "font-display text-lg font-semibold text-fg tabular-nums", children: value }),
|
|
119
|
-
caption && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-fg-secondary", children: caption })
|
|
120
|
-
] });
|
|
121
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
Meter,
|
|
3
|
+
MeterValue,
|
|
4
|
+
meter
|
|
5
|
+
} from "./chunk-HEKDKM5Y.js";
|
|
122
6
|
export {
|
|
123
7
|
Meter,
|
|
124
8
|
MeterValue,
|