@krizaka/ui 2.2.0 → 2.4.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 +33 -8
- package/dist/audio-player.d.ts +113 -0
- package/dist/audio-player.js +207 -0
- package/dist/audio-player.js.map +1 -0
- package/dist/{button-Cgfkg_3y.d.ts → button-BMAO7dKF.d.ts} +29 -2
- package/dist/button.d.ts +32 -5
- package/dist/button.js +62 -9
- package/dist/button.js.map +1 -1
- package/dist/chip-link.d.ts +53 -0
- package/dist/chip-link.js +32 -0
- package/dist/chip-link.js.map +1 -0
- package/dist/chunk-2USC2YT6.js +17 -0
- package/dist/chunk-2USC2YT6.js.map +1 -0
- package/dist/chunk-43PTCDTL.js +31 -0
- package/dist/chunk-43PTCDTL.js.map +1 -0
- package/dist/chunk-6ISW63JI.js +11 -0
- package/dist/chunk-6ISW63JI.js.map +1 -0
- package/dist/chunk-EUPTXKH7.js +95 -0
- package/dist/chunk-EUPTXKH7.js.map +1 -0
- package/dist/chunk-HAQWKORZ.js +117 -0
- package/dist/chunk-HAQWKORZ.js.map +1 -0
- package/dist/{chunk-FHYZSO2S.js → chunk-R6SDS7JM.js} +5 -3
- package/dist/chunk-R6SDS7JM.js.map +1 -0
- package/dist/chunk-RUPNPMP3.js +103 -0
- package/dist/chunk-RUPNPMP3.js.map +1 -0
- package/dist/chunk-RUPRYJ45.js +82 -0
- package/dist/chunk-RUPRYJ45.js.map +1 -0
- package/dist/chunk-VDR2EP6K.js +49 -0
- package/dist/chunk-VDR2EP6K.js.map +1 -0
- package/dist/chunk-WEVYKZIF.js +33 -0
- package/dist/chunk-WEVYKZIF.js.map +1 -0
- package/dist/chunk-XUMJA5ZI.js +283 -0
- package/dist/chunk-XUMJA5ZI.js.map +1 -0
- package/dist/chunk-YH4LMWRU.js +92 -0
- package/dist/chunk-YH4LMWRU.js.map +1 -0
- package/dist/chunk-YX6CNVJA.js +94 -0
- package/dist/chunk-YX6CNVJA.js.map +1 -0
- package/dist/chunk-ZIXB33X7.js +95 -0
- package/dist/chunk-ZIXB33X7.js.map +1 -0
- package/dist/combobox.d.ts +188 -0
- package/dist/combobox.js +16 -0
- package/dist/combobox.js.map +1 -0
- package/dist/command.d.ts +7 -7
- package/dist/command.js +3 -2
- package/dist/command.js.map +1 -1
- package/dist/confirm-button.d.ts +1 -1
- package/dist/confirm-button.js +2 -1
- package/dist/confirm-button.js.map +1 -1
- package/dist/data-table.d.ts +246 -0
- package/dist/data-table.js +290 -0
- package/dist/data-table.js.map +1 -0
- package/dist/date-picker.d.ts +288 -0
- package/dist/date-picker.js +364 -0
- package/dist/date-picker.js.map +1 -0
- package/dist/{dialog-GQ4Nvuyf.d.ts → dialog-D42QwYb_.d.ts} +11 -1
- package/dist/dialog.d.ts +1 -1
- package/dist/dialog.js +3 -2
- package/dist/dialog.js.map +1 -1
- package/dist/file-upload.d.ts +238 -0
- package/dist/file-upload.js +437 -0
- package/dist/file-upload.js.map +1 -0
- package/dist/filter-bar.d.ts +139 -0
- package/dist/filter-bar.js +155 -0
- package/dist/filter-bar.js.map +1 -0
- package/dist/glow-card.d.ts +20 -0
- package/dist/glow-card.js +30 -0
- package/dist/glow-card.js.map +1 -0
- package/dist/image-cropper.d.ts +115 -0
- package/dist/image-cropper.js +166 -0
- package/dist/image-cropper.js.map +1 -0
- package/dist/index.d.ts +2 -20
- package/dist/index.js +7 -64
- package/dist/index.js.map +1 -1
- package/dist/iso.d.ts +400 -0
- package/dist/iso.js +363 -0
- package/dist/iso.js.map +1 -0
- package/dist/loader.d.ts +206 -0
- package/dist/loader.js +138 -0
- package/dist/loader.js.map +1 -0
- package/dist/marquee.d.ts +33 -0
- package/dist/marquee.js +52 -0
- package/dist/marquee.js.map +1 -0
- package/dist/media-capture.d.ts +155 -0
- package/dist/media-capture.js +242 -0
- package/dist/media-capture.js.map +1 -0
- package/dist/meter.d.ts +146 -0
- package/dist/meter.js +107 -0
- package/dist/meter.js.map +1 -0
- package/dist/motion.css +394 -3
- package/dist/motion.d.ts +46 -0
- package/dist/motion.js +10 -0
- package/dist/motion.js.map +1 -0
- package/dist/native/media-capture.d.ts +49 -0
- package/dist/native/media-capture.js +113 -0
- package/dist/native/media-capture.js.map +1 -0
- package/dist/native/video-player.d.ts +50 -0
- package/dist/native/video-player.js +69 -0
- package/dist/native/video-player.js.map +1 -0
- package/dist/native.d.ts +66 -2
- package/dist/native.js +166 -68
- package/dist/native.js.map +1 -1
- package/dist/nav-bar.d.ts +178 -0
- package/dist/nav-bar.js +157 -0
- package/dist/nav-bar.js.map +1 -0
- package/dist/number-ticker.d.ts +32 -0
- package/dist/number-ticker.js +61 -0
- package/dist/number-ticker.js.map +1 -0
- package/dist/pagination.d.ts +141 -0
- package/dist/pagination.js +165 -0
- package/dist/pagination.js.map +1 -0
- package/dist/progress.d.ts +56 -1
- package/dist/progress.js +72 -25
- package/dist/progress.js.map +1 -1
- package/dist/search-field.d.ts +100 -0
- package/dist/search-field.js +167 -0
- package/dist/search-field.js.map +1 -0
- package/dist/skeleton.d.ts +18 -3
- package/dist/skeleton.js +9 -1
- package/dist/skeleton.js.map +1 -1
- package/dist/spinner.d.ts +21 -5
- package/dist/spinner.js +13 -12
- package/dist/spinner.js.map +1 -1
- package/dist/stepper.d.ts +112 -0
- package/dist/stepper.js +67 -0
- package/dist/stepper.js.map +1 -0
- package/dist/tabs.d.ts +1 -1
- package/dist/tabs.js +14 -8
- package/dist/tabs.js.map +1 -1
- package/dist/tag-input.d.ts +44 -0
- package/dist/tag-input.js +67 -0
- package/dist/tag-input.js.map +1 -0
- package/dist/theme.d.ts +1 -1
- package/dist/theme.js +2 -1
- package/dist/theme.js.map +1 -1
- package/dist/toast.d.ts +39 -4
- package/dist/toast.js +81 -7
- package/dist/toast.js.map +1 -1
- package/dist/video-player.d.ts +204 -0
- package/dist/video-player.js +371 -0
- package/dist/video-player.js.map +1 -0
- package/dist/video-trimmer.d.ts +142 -0
- package/dist/video-trimmer.js +193 -0
- package/dist/video-trimmer.js.map +1 -0
- package/package.json +375 -7
- package/registry/alert.json +1 -1
- package/registry/audio-player.json +282 -0
- package/registry/avatar.json +1 -1
- package/registry/badge.json +1 -1
- package/registry/button.json +232 -26
- package/registry/card.json +1 -1
- package/registry/checkbox.json +1 -1
- package/registry/chip-link.json +158 -0
- package/registry/chip.json +1 -1
- package/registry/combobox.json +475 -0
- package/registry/command.json +1 -1
- package/registry/confirm-button.json +91 -7
- package/registry/countdown.json +1 -1
- package/registry/data-table.json +500 -0
- package/registry/date-picker.json +978 -0
- package/registry/dialog.json +15 -7
- package/registry/dropdown-menu.json +1 -1
- package/registry/empty-state.json +1 -1
- package/registry/examples/audio-player/podcast.js +22 -0
- package/registry/examples/audio-player/podcast.tsx +19 -0
- package/registry/examples/audio-player/voice-note.d.ts +4 -0
- package/registry/examples/audio-player/voice-note.js +12 -0
- package/registry/examples/audio-player/voice-note.tsx +12 -0
- package/registry/examples/button/error.d.ts +4 -0
- package/registry/examples/button/error.js +11 -0
- package/registry/examples/button/error.tsx +10 -0
- package/registry/examples/button/loading.tsx +1 -1
- package/registry/examples/button/save-flow.d.ts +4 -0
- package/registry/examples/button/save-flow.js +28 -0
- package/registry/examples/button/save-flow.tsx +24 -0
- package/registry/examples/button/signature.d.ts +4 -0
- package/registry/examples/button/signature.js +17 -0
- package/registry/examples/button/signature.tsx +19 -0
- package/registry/examples/button/upload-progress.d.ts +4 -0
- package/registry/examples/button/upload-progress.js +23 -0
- package/registry/examples/button/upload-progress.tsx +26 -0
- package/registry/examples/chip-link/filters.d.ts +4 -0
- package/registry/examples/chip-link/filters.js +12 -0
- package/registry/examples/chip-link/filters.tsx +17 -0
- package/registry/examples/combobox/async.d.ts +4 -0
- package/registry/examples/combobox/async.js +23 -0
- package/registry/examples/combobox/async.tsx +22 -0
- package/registry/examples/combobox/creatable.d.ts +4 -0
- package/registry/examples/combobox/creatable.js +32 -0
- package/registry/examples/combobox/creatable.tsx +28 -0
- package/registry/examples/combobox/invalid.d.ts +4 -0
- package/registry/examples/combobox/invalid.js +23 -0
- package/registry/examples/combobox/invalid.tsx +17 -0
- package/registry/examples/combobox/multiple.d.ts +4 -0
- package/registry/examples/combobox/multiple.js +29 -0
- package/registry/examples/combobox/multiple.tsx +26 -0
- package/registry/examples/data-table/cards.d.ts +4 -0
- package/registry/examples/data-table/cards.js +21 -0
- package/registry/examples/data-table/cards.tsx +20 -0
- package/registry/examples/data-table/empty.d.ts +4 -0
- package/registry/examples/data-table/empty.js +26 -0
- package/registry/examples/data-table/empty.tsx +21 -0
- package/registry/examples/data-table/error.d.ts +4 -0
- package/registry/examples/data-table/error.js +28 -0
- package/registry/examples/data-table/error.tsx +29 -0
- package/registry/examples/data-table/jobs.d.ts +4 -0
- package/registry/examples/data-table/jobs.js +37 -0
- package/registry/examples/data-table/jobs.tsx +36 -0
- package/registry/examples/data-table/loading.d.ts +4 -0
- package/registry/examples/data-table/loading.js +16 -0
- package/registry/examples/data-table/loading.tsx +12 -0
- package/registry/examples/data-table/virtualized.d.ts +4 -0
- package/registry/examples/data-table/virtualized.js +21 -0
- package/registry/examples/data-table/virtualized.tsx +20 -0
- package/registry/examples/date-picker/calendar.d.ts +4 -0
- package/registry/examples/date-picker/calendar.js +14 -0
- package/registry/examples/date-picker/calendar.tsx +16 -0
- package/registry/examples/date-picker/french.d.ts +4 -0
- package/registry/examples/date-picker/french.js +20 -0
- package/registry/examples/date-picker/french.tsx +14 -0
- package/registry/examples/date-picker/open.d.ts +4 -0
- package/registry/examples/date-picker/open.js +11 -0
- package/registry/examples/date-picker/open.tsx +8 -0
- package/registry/examples/date-picker/range.d.ts +4 -0
- package/registry/examples/date-picker/range.js +29 -0
- package/registry/examples/date-picker/range.tsx +25 -0
- package/registry/examples/date-picker/schedule.d.ts +4 -0
- package/registry/examples/date-picker/schedule.js +26 -0
- package/registry/examples/date-picker/schedule.tsx +22 -0
- package/registry/examples/dialog/left.d.ts +4 -0
- package/registry/examples/dialog/left.js +22 -0
- package/registry/examples/dialog/left.tsx +32 -0
- package/registry/examples/file-upload/images.d.ts +4 -0
- package/registry/examples/file-upload/images.js +32 -0
- package/registry/examples/file-upload/images.tsx +25 -0
- package/registry/examples/file-upload/states.d.ts +4 -0
- package/registry/examples/file-upload/states.js +28 -0
- package/registry/examples/file-upload/states.tsx +23 -0
- package/registry/examples/file-upload/tus.d.ts +4 -0
- package/registry/examples/file-upload/tus.js +23 -0
- package/registry/examples/file-upload/tus.tsx +26 -0
- package/registry/examples/file-upload/video.d.ts +4 -0
- package/registry/examples/file-upload/video.js +39 -0
- package/registry/examples/file-upload/video.tsx +36 -0
- package/registry/examples/filter-bar/address.d.ts +4 -0
- package/registry/examples/filter-bar/address.js +29 -0
- package/registry/examples/filter-bar/address.tsx +27 -0
- package/registry/examples/filter-bar/jobs.d.ts +4 -0
- package/registry/examples/filter-bar/jobs.js +37 -0
- package/registry/examples/filter-bar/jobs.tsx +37 -0
- package/registry/examples/filter-bar/menu.d.ts +4 -0
- package/registry/examples/filter-bar/menu.js +27 -0
- package/registry/examples/filter-bar/menu.tsx +23 -0
- package/registry/examples/glow-card/link-card.d.ts +4 -0
- package/registry/examples/glow-card/link-card.js +14 -0
- package/registry/examples/glow-card/link-card.tsx +13 -0
- package/registry/examples/glow-card/plans.d.ts +4 -0
- package/registry/examples/glow-card/plans.js +25 -0
- package/registry/examples/glow-card/plans.tsx +26 -0
- package/registry/examples/glow-card/spotlight.d.ts +4 -0
- package/registry/examples/glow-card/spotlight.js +14 -0
- package/registry/examples/glow-card/spotlight.tsx +15 -0
- package/registry/examples/image-cropper/avatar.d.ts +4 -0
- package/registry/examples/image-cropper/avatar.js +13 -0
- package/registry/examples/image-cropper/avatar.tsx +14 -0
- package/registry/examples/image-cropper/banner.d.ts +4 -0
- package/registry/examples/image-cropper/banner.js +11 -0
- package/registry/examples/image-cropper/banner.tsx +10 -0
- package/registry/examples/image-cropper/vertical-video.d.ts +4 -0
- package/registry/examples/image-cropper/vertical-video.js +12 -0
- package/registry/examples/image-cropper/vertical-video.tsx +16 -0
- package/registry/examples/iso/compose.d.ts +4 -0
- package/registry/examples/iso/compose.js +22 -0
- package/registry/examples/iso/compose.tsx +19 -0
- package/registry/examples/iso/orazaka-hero.d.ts +4 -0
- package/registry/examples/iso/orazaka-hero.js +26 -0
- package/registry/examples/iso/orazaka-hero.tsx +21 -0
- package/registry/examples/iso/orochia-hero.d.ts +4 -0
- package/registry/examples/iso/orochia-hero.js +29 -0
- package/registry/examples/iso/orochia-hero.tsx +23 -0
- package/registry/examples/iso/still.d.ts +4 -0
- package/registry/examples/iso/still.js +21 -0
- package/registry/examples/iso/still.tsx +15 -0
- package/registry/examples/loader/endings.d.ts +4 -0
- package/registry/examples/loader/endings.js +14 -0
- package/registry/examples/loader/endings.tsx +11 -0
- package/registry/examples/loader/fetch-flow.d.ts +4 -0
- package/registry/examples/loader/fetch-flow.js +24 -0
- package/registry/examples/loader/fetch-flow.tsx +24 -0
- package/registry/examples/loader/minimal.d.ts +4 -0
- package/registry/examples/loader/minimal.js +11 -0
- package/registry/examples/loader/minimal.tsx +6 -0
- package/registry/examples/loader/overlay.d.ts +4 -0
- package/registry/examples/loader/overlay.js +16 -0
- package/registry/examples/loader/overlay.tsx +13 -0
- package/registry/examples/loader/region-handover.d.ts +4 -0
- package/registry/examples/loader/region-handover.js +42 -0
- package/registry/examples/loader/region-handover.tsx +29 -0
- package/registry/examples/loader/upload-eta.d.ts +4 -0
- package/registry/examples/loader/upload-eta.js +11 -0
- package/registry/examples/loader/upload-eta.tsx +10 -0
- package/registry/examples/marquee/cards.d.ts +4 -0
- package/registry/examples/marquee/cards.js +23 -0
- package/registry/examples/marquee/cards.tsx +21 -0
- package/registry/examples/marquee/logos.d.ts +4 -0
- package/registry/examples/marquee/logos.js +12 -0
- package/registry/examples/marquee/logos.tsx +17 -0
- package/registry/examples/media-capture/clip.d.ts +4 -0
- package/registry/examples/media-capture/clip.js +20 -0
- package/registry/examples/media-capture/clip.tsx +18 -0
- package/registry/examples/media-capture/native/photo.d.ts +4 -0
- package/registry/examples/media-capture/native/photo.dark.png +0 -0
- package/registry/examples/media-capture/native/photo.js +27 -0
- package/registry/examples/media-capture/native/photo.light.png +0 -0
- package/registry/examples/media-capture/native/photo.tsx +20 -0
- package/registry/examples/media-capture/profile-photo.d.ts +4 -0
- package/registry/examples/media-capture/profile-photo.js +24 -0
- package/registry/examples/media-capture/profile-photo.tsx +20 -0
- package/registry/examples/meter/balance.d.ts +4 -0
- package/registry/examples/meter/balance.js +14 -0
- package/registry/examples/meter/balance.tsx +15 -0
- package/registry/examples/meter/quota.d.ts +4 -0
- package/registry/examples/meter/quota.js +15 -0
- package/registry/examples/meter/quota.tsx +12 -0
- package/registry/examples/meter/split.d.ts +4 -0
- package/registry/examples/meter/split.js +18 -0
- package/registry/examples/meter/split.tsx +18 -0
- package/registry/examples/nav-bar/app-bar.d.ts +4 -0
- package/registry/examples/nav-bar/app-bar.js +22 -0
- package/registry/examples/nav-bar/app-bar.tsx +21 -0
- package/registry/examples/nav-bar/menu-open.d.ts +4 -0
- package/registry/examples/nav-bar/menu-open.js +26 -0
- package/registry/examples/nav-bar/menu-open.tsx +26 -0
- package/registry/examples/nav-bar/site.d.ts +4 -0
- package/registry/examples/nav-bar/site.js +31 -0
- package/registry/examples/nav-bar/site.tsx +36 -0
- package/registry/examples/number-ticker/gradient.d.ts +4 -0
- package/registry/examples/number-ticker/gradient.js +11 -0
- package/registry/examples/number-ticker/gradient.tsx +10 -0
- package/registry/examples/number-ticker/live-total.d.ts +4 -0
- package/registry/examples/number-ticker/live-total.js +17 -0
- package/registry/examples/number-ticker/live-total.tsx +21 -0
- package/registry/examples/number-ticker/stats.d.ts +4 -0
- package/registry/examples/number-ticker/stats.js +19 -0
- package/registry/examples/number-ticker/stats.tsx +23 -0
- package/registry/examples/pagination/cursor.d.ts +4 -0
- package/registry/examples/pagination/cursor.js +24 -0
- package/registry/examples/pagination/cursor.tsx +20 -0
- package/registry/examples/pagination/links.d.ts +4 -0
- package/registry/examples/pagination/links.js +11 -0
- package/registry/examples/pagination/links.tsx +6 -0
- package/registry/examples/pagination/native/cursor.d.ts +4 -0
- package/registry/examples/pagination/native/cursor.dark.png +0 -0
- package/registry/examples/pagination/native/cursor.js +19 -0
- package/registry/examples/pagination/native/cursor.light.png +0 -0
- package/registry/examples/pagination/native/cursor.tsx +13 -0
- package/registry/examples/pagination/native/pages.d.ts +4 -0
- package/registry/examples/pagination/native/pages.dark.png +0 -0
- package/registry/examples/pagination/native/pages.js +21 -0
- package/registry/examples/pagination/native/pages.light.png +0 -0
- package/registry/examples/pagination/native/pages.tsx +14 -0
- package/registry/examples/pagination/results.d.ts +4 -0
- package/registry/examples/pagination/results.js +23 -0
- package/registry/examples/pagination/results.tsx +19 -0
- package/registry/examples/pagination/table-footer.d.ts +4 -0
- package/registry/examples/pagination/table-footer.js +31 -0
- package/registry/examples/pagination/table-footer.tsx +27 -0
- package/registry/examples/progress/buffer.d.ts +4 -0
- package/registry/examples/progress/buffer.js +11 -0
- package/registry/examples/progress/buffer.tsx +10 -0
- package/registry/examples/progress/statuses.d.ts +4 -0
- package/registry/examples/progress/statuses.js +14 -0
- package/registry/examples/progress/statuses.tsx +11 -0
- package/registry/examples/progress/steps.d.ts +4 -0
- package/registry/examples/progress/steps.js +14 -0
- package/registry/examples/progress/steps.tsx +13 -0
- package/registry/examples/progress/upload.d.ts +4 -0
- package/registry/examples/progress/upload.js +11 -0
- package/registry/examples/progress/upload.tsx +10 -0
- package/registry/examples/search-field/global.d.ts +4 -0
- package/registry/examples/search-field/global.js +19 -0
- package/registry/examples/search-field/global.tsx +15 -0
- package/registry/examples/search-field/native/default.d.ts +4 -0
- package/registry/examples/search-field/native/default.dark.png +0 -0
- package/registry/examples/search-field/native/default.js +13 -0
- package/registry/examples/search-field/native/default.light.png +0 -0
- package/registry/examples/search-field/native/default.tsx +7 -0
- package/registry/examples/search-field/suggestions.d.ts +4 -0
- package/registry/examples/search-field/suggestions.js +30 -0
- package/registry/examples/search-field/suggestions.tsx +27 -0
- package/registry/examples/search-field/toolbar.d.ts +4 -0
- package/registry/examples/search-field/toolbar.js +17 -0
- package/registry/examples/search-field/toolbar.tsx +21 -0
- package/registry/examples/skeleton/paragraph.d.ts +4 -0
- package/registry/examples/skeleton/paragraph.js +14 -0
- package/registry/examples/skeleton/paragraph.tsx +12 -0
- package/registry/examples/skeleton/shape-of-content.d.ts +4 -0
- package/registry/examples/skeleton/shape-of-content.js +23 -0
- package/registry/examples/skeleton/shape-of-content.tsx +26 -0
- package/registry/examples/spinner/default.js +5 -2
- package/registry/examples/spinner/default.tsx +7 -1
- package/registry/examples/spinner/sizes.js +2 -1
- package/registry/examples/spinner/sizes.tsx +2 -0
- package/registry/examples/spinner/value.d.ts +4 -0
- package/registry/examples/spinner/value.js +11 -0
- package/registry/examples/spinner/value.tsx +12 -0
- package/registry/examples/stepper/checkout.d.ts +4 -0
- package/registry/examples/stepper/checkout.js +22 -0
- package/registry/examples/stepper/checkout.tsx +17 -0
- package/registry/examples/stepper/failed.d.ts +4 -0
- package/registry/examples/stepper/failed.js +26 -0
- package/registry/examples/stepper/failed.tsx +21 -0
- package/registry/examples/stepper/job.d.ts +4 -0
- package/registry/examples/stepper/job.js +32 -0
- package/registry/examples/stepper/job.tsx +28 -0
- package/registry/examples/tag-input/recipients.d.ts +4 -0
- package/registry/examples/tag-input/recipients.js +23 -0
- package/registry/examples/tag-input/recipients.tsx +20 -0
- package/registry/examples/tag-input/video-tags.d.ts +4 -0
- package/registry/examples/tag-input/video-tags.js +26 -0
- package/registry/examples/tag-input/video-tags.tsx +22 -0
- package/registry/examples/toast/grouped.d.ts +4 -0
- package/registry/examples/toast/grouped.js +21 -0
- package/registry/examples/toast/grouped.tsx +23 -0
- package/registry/examples/toast/undo.d.ts +4 -0
- package/registry/examples/toast/undo.js +29 -0
- package/registry/examples/toast/undo.tsx +30 -0
- package/registry/examples/toast/upload-progress.d.ts +4 -0
- package/registry/examples/toast/upload-progress.js +29 -0
- package/registry/examples/toast/upload-progress.tsx +31 -0
- package/registry/examples/video-player/default.d.ts +4 -0
- package/registry/examples/video-player/default.js +12 -0
- package/registry/examples/video-player/default.tsx +8 -0
- package/registry/examples/video-player/gated.d.ts +4 -0
- package/registry/examples/video-player/gated.js +26 -0
- package/registry/examples/video-player/gated.tsx +23 -0
- package/registry/examples/video-player/native/default.d.ts +4 -0
- package/registry/examples/video-player/native/default.dark.png +0 -0
- package/registry/examples/video-player/native/default.js +18 -0
- package/registry/examples/video-player/native/default.light.png +0 -0
- package/registry/examples/video-player/native/default.tsx +12 -0
- package/registry/examples/video-player/signed-source.d.ts +4 -0
- package/registry/examples/video-player/signed-source.js +16 -0
- package/registry/examples/video-player/signed-source.tsx +15 -0
- package/registry/examples/video-player/vertical.d.ts +4 -0
- package/registry/examples/video-player/vertical.js +12 -0
- package/registry/examples/video-player/vertical.tsx +12 -0
- package/registry/examples/video-trimmer/custom.d.ts +4 -0
- package/registry/examples/video-trimmer/custom.js +24 -0
- package/registry/examples/video-trimmer/custom.tsx +26 -0
- package/registry/examples/video-trimmer/filmstrip.d.ts +4 -0
- package/registry/examples/video-trimmer/filmstrip.js +15 -0
- package/registry/examples/video-trimmer/filmstrip.tsx +13 -0
- package/registry/examples/video-trimmer/playhead.d.ts +4 -0
- package/registry/examples/video-trimmer/playhead.js +15 -0
- package/registry/examples/video-trimmer/playhead.tsx +13 -0
- package/registry/field.json +1 -1
- package/registry/file-upload.json +471 -0
- package/registry/filter-bar.json +347 -0
- package/registry/glow-card.json +162 -0
- package/registry/image-cropper.json +313 -0
- package/registry/index.json +445 -20
- package/registry/iso.json +1355 -0
- package/registry/kbd.json +1 -1
- package/registry/loader.json +399 -0
- package/registry/marquee.json +225 -0
- package/registry/mascots.json +3 -3
- package/registry/media-capture.json +347 -0
- package/registry/meter.json +324 -0
- package/registry/nav-bar.json +480 -0
- package/registry/number-ticker.json +219 -0
- package/registry/page-header.json +1 -1
- package/registry/pagination.json +475 -0
- package/registry/popover.json +1 -1
- package/registry/progress.json +114 -9
- package/registry/radio-group.json +1 -1
- package/registry/search-field.json +436 -0
- package/registry/section-backdrop.json +1 -1
- package/registry/separator.json +1 -1
- package/registry/skeleton.json +69 -13
- package/registry/slider.json +1 -1
- package/registry/slot.json +1 -1
- package/registry/spinner.json +91 -27
- package/registry/stat.json +1 -1
- package/registry/stepper.json +174 -0
- package/registry/switch.json +1 -1
- package/registry/tabs.json +11 -7
- package/registry/tag-input.json +363 -0
- package/registry/theme.json +3 -3
- package/registry/toast.json +41 -13
- package/registry/tooltip.json +1 -1
- package/registry/video-player.json +598 -0
- package/registry/video-trimmer.json +385 -0
- package/dist/chunk-FHYZSO2S.js.map +0 -1
- package/dist/chunk-PESA2R4U.js +0 -51
- package/dist/chunk-PESA2R4U.js.map +0 -1
- package/registry/examples/spinner/muted.js +0 -11
- package/registry/examples/spinner/muted.tsx +0 -6
- /package/registry/examples/{spinner/muted.d.ts → audio-player/podcast.d.ts} +0 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
formatClock,
|
|
4
|
+
playSafely,
|
|
5
|
+
usePrefersReducedMotion
|
|
6
|
+
} from "./chunk-EUPTXKH7.js";
|
|
7
|
+
import {
|
|
8
|
+
CameraGlyph,
|
|
9
|
+
FlipGlyph
|
|
10
|
+
} from "./chunk-YX6CNVJA.js";
|
|
11
|
+
|
|
12
|
+
// src/media-capture/media-capture.tsx
|
|
13
|
+
import * as React from "react";
|
|
14
|
+
import { tv } from "tailwind-variants";
|
|
15
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
16
|
+
var mediaCapture = tv({
|
|
17
|
+
slots: {
|
|
18
|
+
root: "theme-dark relative isolate flex w-full flex-col overflow-hidden rounded-xl bg-media text-fg-on-media",
|
|
19
|
+
stage: "relative w-full",
|
|
20
|
+
video: "absolute inset-0 h-full w-full object-cover data-[mirrored]:-scale-x-100",
|
|
21
|
+
panel: "absolute inset-0 flex flex-col items-center justify-center gap-3 p-6 text-center text-sm",
|
|
22
|
+
panelIcon: "flex h-12 w-12 items-center justify-center rounded-full bg-fg-on-media/15",
|
|
23
|
+
primary: "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 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-media",
|
|
24
|
+
secondary: "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 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
|
|
25
|
+
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",
|
|
26
|
+
shutter: "relative inline-flex h-16 w-16 items-center justify-center rounded-full border-4 border-fg-on-media bg-transparent focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-media after:h-12 after:w-12 after:rounded-full after:bg-fg-on-media after:transition-all motion-reduce:after:transition-none data-[mode=video]:after:bg-danger data-[recording]:after:h-6 data-[recording]:after:w-6 data-[recording]:after:rounded-md",
|
|
27
|
+
round: "inline-flex h-11 w-11 items-center justify-center rounded-full bg-scrim-strong text-fg-on-media hover:bg-scrim focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring disabled:invisible",
|
|
28
|
+
modes: "absolute inset-x-0 top-3 flex justify-center",
|
|
29
|
+
mode: "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 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
|
|
30
|
+
timer: "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 before:h-2 before:w-2 before:rounded-full before:bg-danger before:animate-pulse motion-reduce:before:animate-none"
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
var mediaCaptureLabels = {
|
|
34
|
+
region: "Camera",
|
|
35
|
+
prompt: "Use your camera to take a photo or record a clip.",
|
|
36
|
+
start: "Turn on the camera",
|
|
37
|
+
denied: "Camera access is blocked. Allow it in your browser settings, or choose a file.",
|
|
38
|
+
unavailable: "No camera is available here.",
|
|
39
|
+
fallback: "Choose a file",
|
|
40
|
+
takePhoto: "Take photo",
|
|
41
|
+
startRecording: "Start recording",
|
|
42
|
+
stopRecording: "Stop recording",
|
|
43
|
+
switchCamera: "Switch camera",
|
|
44
|
+
photoMode: "Photo",
|
|
45
|
+
videoMode: "Video"
|
|
46
|
+
};
|
|
47
|
+
var supported = () => typeof navigator !== "undefined" && Boolean(navigator.mediaDevices?.getUserMedia);
|
|
48
|
+
var recorderType = () => typeof MediaRecorder === "undefined" ? void 0 : ["video/webm;codecs=vp9,opus", "video/webm", "video/mp4"].find((t) => MediaRecorder.isTypeSupported?.(t));
|
|
49
|
+
function MediaCapture({
|
|
50
|
+
mode = "photo",
|
|
51
|
+
facing: initialFacing = "user",
|
|
52
|
+
audio = true,
|
|
53
|
+
maxDuration,
|
|
54
|
+
autoStart,
|
|
55
|
+
onCapture,
|
|
56
|
+
onError,
|
|
57
|
+
aspectRatio = "3 / 4",
|
|
58
|
+
labels: given,
|
|
59
|
+
className
|
|
60
|
+
}) {
|
|
61
|
+
const labels = { ...mediaCaptureLabels, ...given };
|
|
62
|
+
const s = mediaCapture();
|
|
63
|
+
const reduce = usePrefersReducedMotion();
|
|
64
|
+
const video = React.useRef(null);
|
|
65
|
+
const stream = React.useRef(null);
|
|
66
|
+
const recorder = React.useRef(null);
|
|
67
|
+
const file = React.useRef(null);
|
|
68
|
+
const [state, setState] = React.useState("idle");
|
|
69
|
+
const [facing, setFacing] = React.useState(initialFacing);
|
|
70
|
+
const [kind, setKind] = React.useState(mode === "video" ? "video" : "photo");
|
|
71
|
+
const [recording, setRecording] = React.useState(false);
|
|
72
|
+
const [elapsed, setElapsed] = React.useState(0);
|
|
73
|
+
const [cameras, setCameras] = React.useState(0);
|
|
74
|
+
const stop = React.useCallback(() => {
|
|
75
|
+
if (recorder.current?.state === "recording") recorder.current.stop();
|
|
76
|
+
stream.current?.getTracks().forEach((track) => track.stop());
|
|
77
|
+
stream.current = null;
|
|
78
|
+
}, []);
|
|
79
|
+
const start = React.useCallback(
|
|
80
|
+
async (side) => {
|
|
81
|
+
if (!supported()) {
|
|
82
|
+
setState("unavailable");
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
setState("starting");
|
|
86
|
+
stop();
|
|
87
|
+
try {
|
|
88
|
+
const media = await navigator.mediaDevices.getUserMedia({ video: { facingMode: side }, audio: mode !== "photo" && audio });
|
|
89
|
+
stream.current = media;
|
|
90
|
+
if (video.current) {
|
|
91
|
+
video.current.srcObject = media;
|
|
92
|
+
playSafely(video.current);
|
|
93
|
+
}
|
|
94
|
+
const devices = await navigator.mediaDevices.enumerateDevices?.().catch(() => []);
|
|
95
|
+
setCameras((devices ?? []).filter((d) => d.kind === "videoinput").length);
|
|
96
|
+
setState("live");
|
|
97
|
+
} catch (error) {
|
|
98
|
+
const name = error?.name;
|
|
99
|
+
setState(name === "NotAllowedError" || name === "SecurityError" ? "denied" : "unavailable");
|
|
100
|
+
onError?.(error);
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
[audio, mode, onError, stop]
|
|
104
|
+
);
|
|
105
|
+
React.useEffect(() => {
|
|
106
|
+
if (!autoStart || !supported() || !navigator.permissions?.query) return;
|
|
107
|
+
let live = true;
|
|
108
|
+
navigator.permissions.query({ name: "camera" }).then((status) => {
|
|
109
|
+
if (live && status.state === "granted") void start(initialFacing);
|
|
110
|
+
}).catch(() => void 0);
|
|
111
|
+
return () => {
|
|
112
|
+
live = false;
|
|
113
|
+
};
|
|
114
|
+
}, [autoStart, initialFacing, start]);
|
|
115
|
+
React.useEffect(() => {
|
|
116
|
+
const hide = () => {
|
|
117
|
+
if (!document.hidden) return;
|
|
118
|
+
stop();
|
|
119
|
+
setState((current) => current === "live" ? "idle" : current);
|
|
120
|
+
};
|
|
121
|
+
document.addEventListener("visibilitychange", hide);
|
|
122
|
+
return () => {
|
|
123
|
+
document.removeEventListener("visibilitychange", hide);
|
|
124
|
+
stop();
|
|
125
|
+
};
|
|
126
|
+
}, [stop]);
|
|
127
|
+
React.useEffect(() => {
|
|
128
|
+
if (!recording) return;
|
|
129
|
+
const began = Date.now();
|
|
130
|
+
const timer = setInterval(() => {
|
|
131
|
+
const seconds = (Date.now() - began) / 1e3;
|
|
132
|
+
setElapsed(seconds);
|
|
133
|
+
if (maxDuration && seconds >= maxDuration) recorder.current?.stop();
|
|
134
|
+
}, 250);
|
|
135
|
+
return () => clearInterval(timer);
|
|
136
|
+
}, [recording, maxDuration]);
|
|
137
|
+
const deliver = (blob, captured) => onCapture({ blob, kind: captured, url: URL.createObjectURL(blob) });
|
|
138
|
+
const takePhoto = () => {
|
|
139
|
+
const element = video.current;
|
|
140
|
+
if (!element?.videoWidth) return;
|
|
141
|
+
const canvas = document.createElement("canvas");
|
|
142
|
+
canvas.width = element.videoWidth;
|
|
143
|
+
canvas.height = element.videoHeight;
|
|
144
|
+
const context = canvas.getContext("2d");
|
|
145
|
+
if (!context) return;
|
|
146
|
+
if (facing === "user") {
|
|
147
|
+
context.translate(canvas.width, 0);
|
|
148
|
+
context.scale(-1, 1);
|
|
149
|
+
}
|
|
150
|
+
context.drawImage(element, 0, 0);
|
|
151
|
+
canvas.toBlob((blob) => blob && deliver(blob, "photo"), "image/jpeg", 0.92);
|
|
152
|
+
};
|
|
153
|
+
const toggleRecording = () => {
|
|
154
|
+
if (recording) {
|
|
155
|
+
recorder.current?.stop();
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (!stream.current) return;
|
|
159
|
+
const chunks = [];
|
|
160
|
+
const type = recorderType();
|
|
161
|
+
const next = new MediaRecorder(stream.current, type ? { mimeType: type } : void 0);
|
|
162
|
+
next.ondataavailable = (event) => event.data.size && chunks.push(event.data);
|
|
163
|
+
next.onstop = () => {
|
|
164
|
+
setRecording(false);
|
|
165
|
+
setElapsed(0);
|
|
166
|
+
deliver(new Blob(chunks, { type: next.mimeType || type || "video/webm" }), "video");
|
|
167
|
+
};
|
|
168
|
+
recorder.current = next;
|
|
169
|
+
next.start(1e3);
|
|
170
|
+
setRecording(true);
|
|
171
|
+
};
|
|
172
|
+
const flip = () => {
|
|
173
|
+
const side = facing === "user" ? "environment" : "user";
|
|
174
|
+
setFacing(side);
|
|
175
|
+
void start(side);
|
|
176
|
+
};
|
|
177
|
+
const accept = mode === "video" ? "video/*" : mode === "photo" ? "image/*" : "image/*,video/*";
|
|
178
|
+
const fallback = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
179
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: s.secondary(), onClick: () => file.current?.click(), children: labels.fallback }),
|
|
180
|
+
/* @__PURE__ */ jsx(
|
|
181
|
+
"input",
|
|
182
|
+
{
|
|
183
|
+
ref: file,
|
|
184
|
+
type: "file",
|
|
185
|
+
accept,
|
|
186
|
+
capture: facing,
|
|
187
|
+
tabIndex: -1,
|
|
188
|
+
"aria-hidden": true,
|
|
189
|
+
className: "sr-only",
|
|
190
|
+
onChange: (event) => {
|
|
191
|
+
const picked = event.target.files?.[0];
|
|
192
|
+
if (picked) deliver(picked, picked.type.startsWith("video/") ? "video" : "photo");
|
|
193
|
+
event.target.value = "";
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
)
|
|
197
|
+
] });
|
|
198
|
+
return /* @__PURE__ */ jsx("section", { "aria-label": labels.region, className: s.root({ className }), children: /* @__PURE__ */ jsxs("div", { className: s.stage(), style: { aspectRatio }, children: [
|
|
199
|
+
/* @__PURE__ */ jsx("video", { ref: video, muted: true, playsInline: true, "aria-hidden": true, "data-mirrored": facing === "user" ? "" : void 0, className: s.video(), hidden: state !== "live" }),
|
|
200
|
+
state !== "live" && /* @__PURE__ */ jsxs("div", { className: s.panel(), role: state === "denied" || state === "unavailable" ? "alert" : void 0, children: [
|
|
201
|
+
/* @__PURE__ */ jsx("span", { className: s.panelIcon(), "aria-hidden": true, children: /* @__PURE__ */ jsx(CameraGlyph, { className: "h-6 w-6" }) }),
|
|
202
|
+
/* @__PURE__ */ jsx("p", { className: "max-w-xs", children: state === "denied" ? labels.denied : state === "unavailable" ? labels.unavailable : labels.prompt }),
|
|
203
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap justify-center gap-2", children: [
|
|
204
|
+
(state === "idle" || state === "starting") && /* @__PURE__ */ jsxs("button", { type: "button", className: s.primary(), disabled: state === "starting", onClick: () => void start(facing), children: [
|
|
205
|
+
/* @__PURE__ */ jsx(CameraGlyph, { className: "h-4 w-4" }),
|
|
206
|
+
labels.start
|
|
207
|
+
] }),
|
|
208
|
+
fallback
|
|
209
|
+
] })
|
|
210
|
+
] }),
|
|
211
|
+
state === "live" && mode === "both" && !recording && /* @__PURE__ */ jsx("div", { className: s.modes(), children: /* @__PURE__ */ jsxs("div", { className: "flex gap-1 rounded-full bg-scrim-strong p-1", children: [
|
|
212
|
+
/* @__PURE__ */ jsx("button", { type: "button", "aria-pressed": kind === "photo", className: s.mode(), onClick: () => setKind("photo"), children: labels.photoMode }),
|
|
213
|
+
/* @__PURE__ */ jsx("button", { type: "button", "aria-pressed": kind === "video", className: s.mode(), onClick: () => setKind("video"), children: labels.videoMode })
|
|
214
|
+
] }) }),
|
|
215
|
+
recording && /* @__PURE__ */ jsxs("span", { className: s.timer(), role: "timer", "aria-live": "off", children: [
|
|
216
|
+
formatClock(elapsed),
|
|
217
|
+
maxDuration ? ` / ${formatClock(maxDuration)}` : ""
|
|
218
|
+
] }),
|
|
219
|
+
state === "live" && /* @__PURE__ */ jsxs("div", { className: s.bar(), children: [
|
|
220
|
+
/* @__PURE__ */ jsx("span", { className: "h-11 w-11" }),
|
|
221
|
+
/* @__PURE__ */ jsx(
|
|
222
|
+
"button",
|
|
223
|
+
{
|
|
224
|
+
type: "button",
|
|
225
|
+
className: s.shutter(),
|
|
226
|
+
"data-mode": kind,
|
|
227
|
+
"data-recording": recording ? "" : void 0,
|
|
228
|
+
"data-reduce": reduce ? "" : void 0,
|
|
229
|
+
"aria-label": kind === "photo" ? labels.takePhoto : recording ? labels.stopRecording : labels.startRecording,
|
|
230
|
+
onClick: kind === "photo" ? takePhoto : toggleRecording
|
|
231
|
+
}
|
|
232
|
+
),
|
|
233
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: s.round(), "aria-label": labels.switchCamera, disabled: cameras < 2 || recording, onClick: flip, children: /* @__PURE__ */ jsx(FlipGlyph, { className: "h-5 w-5" }) })
|
|
234
|
+
] })
|
|
235
|
+
] }) });
|
|
236
|
+
}
|
|
237
|
+
export {
|
|
238
|
+
MediaCapture,
|
|
239
|
+
mediaCapture,
|
|
240
|
+
mediaCaptureLabels
|
|
241
|
+
};
|
|
242
|
+
//# sourceMappingURL=media-capture.js.map
|
|
@@ -0,0 +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":[]}
|
package/dist/meter.d.ts
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import * as tailwind_variants from 'tailwind-variants';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
|
|
4
|
+
declare const meter: tailwind_variants.TVReturnType<{
|
|
5
|
+
size: {
|
|
6
|
+
sm: {
|
|
7
|
+
track: string;
|
|
8
|
+
ring: string;
|
|
9
|
+
};
|
|
10
|
+
md: {
|
|
11
|
+
ring: string;
|
|
12
|
+
};
|
|
13
|
+
lg: {
|
|
14
|
+
track: string;
|
|
15
|
+
ring: string;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
}, {
|
|
19
|
+
root: string;
|
|
20
|
+
header: string;
|
|
21
|
+
caption: string;
|
|
22
|
+
figure: string;
|
|
23
|
+
track: string;
|
|
24
|
+
fill: string;
|
|
25
|
+
legend: string;
|
|
26
|
+
swatch: string;
|
|
27
|
+
ring: string;
|
|
28
|
+
center: string;
|
|
29
|
+
}, undefined, {
|
|
30
|
+
size: {
|
|
31
|
+
sm: {
|
|
32
|
+
track: string;
|
|
33
|
+
ring: string;
|
|
34
|
+
};
|
|
35
|
+
md: {
|
|
36
|
+
ring: string;
|
|
37
|
+
};
|
|
38
|
+
lg: {
|
|
39
|
+
track: string;
|
|
40
|
+
ring: string;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
}, {
|
|
44
|
+
root: string;
|
|
45
|
+
header: string;
|
|
46
|
+
caption: string;
|
|
47
|
+
figure: string;
|
|
48
|
+
track: string;
|
|
49
|
+
fill: string;
|
|
50
|
+
legend: string;
|
|
51
|
+
swatch: string;
|
|
52
|
+
ring: string;
|
|
53
|
+
center: string;
|
|
54
|
+
}, tailwind_variants.TVReturnTypeLike<{
|
|
55
|
+
size: {
|
|
56
|
+
sm: {
|
|
57
|
+
track: string;
|
|
58
|
+
ring: string;
|
|
59
|
+
};
|
|
60
|
+
md: {
|
|
61
|
+
ring: string;
|
|
62
|
+
};
|
|
63
|
+
lg: {
|
|
64
|
+
track: string;
|
|
65
|
+
ring: string;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
}, {
|
|
69
|
+
root: string;
|
|
70
|
+
header: string;
|
|
71
|
+
caption: string;
|
|
72
|
+
figure: string;
|
|
73
|
+
track: string;
|
|
74
|
+
fill: string;
|
|
75
|
+
legend: string;
|
|
76
|
+
swatch: string;
|
|
77
|
+
ring: string;
|
|
78
|
+
center: string;
|
|
79
|
+
}>>;
|
|
80
|
+
interface MeterPart {
|
|
81
|
+
/** Its name (“To the creator”) — passed translated. */
|
|
82
|
+
label: string;
|
|
83
|
+
/** Its value, in the meter's unit. */
|
|
84
|
+
value: number;
|
|
85
|
+
/** Its value in words (“€9.00”), in the legend and for assistive technology; default: its percentage. */
|
|
86
|
+
valueText?: string;
|
|
87
|
+
}
|
|
88
|
+
type MeterProps = Omit<React.ComponentProps<"div">, "children"> & {
|
|
89
|
+
/** The accessible name (“Storage”, “Split of a tip”) — passed translated; shown with `showLabel`. */
|
|
90
|
+
label: string;
|
|
91
|
+
/** The value, between `min` and `max` (ignored with `parts`). */
|
|
92
|
+
value?: number;
|
|
93
|
+
/**
|
|
94
|
+
* The bottom of the range.
|
|
95
|
+
* @default 0
|
|
96
|
+
*/
|
|
97
|
+
min?: number;
|
|
98
|
+
/**
|
|
99
|
+
* The top of the range.
|
|
100
|
+
* @default 100
|
|
101
|
+
*/
|
|
102
|
+
max?: number;
|
|
103
|
+
/** The value in words (“42 GB of 50 GB”) — passed translated; default: the percentage. */
|
|
104
|
+
valueText?: string;
|
|
105
|
+
/**
|
|
106
|
+
* Ratios (0–1) where the tone changes: past `high` it is in danger, past `low` in warning — or the reverse with
|
|
107
|
+
* `optimum="high"` (a balance: the lower, the worse). Without thresholds the fill is the brand accent.
|
|
108
|
+
*/
|
|
109
|
+
thresholds?: {
|
|
110
|
+
readonly low: number;
|
|
111
|
+
readonly high: number;
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Which end is good.
|
|
115
|
+
* @default "low"
|
|
116
|
+
*/
|
|
117
|
+
optimum?: "low" | "high";
|
|
118
|
+
/** Split into parts that add up to `max`: a stacked bar or ring, with its legend. */
|
|
119
|
+
parts?: readonly MeterPart[];
|
|
120
|
+
/**
|
|
121
|
+
* bar · ring.
|
|
122
|
+
* @default "bar"
|
|
123
|
+
*/
|
|
124
|
+
variant?: "bar" | "ring";
|
|
125
|
+
/**
|
|
126
|
+
* sm · md · lg: the bar's thickness, the ring's diameter.
|
|
127
|
+
* @default "md"
|
|
128
|
+
*/
|
|
129
|
+
size?: "sm" | "md" | "lg";
|
|
130
|
+
/** Shows the label and the value above the bar. */
|
|
131
|
+
showLabel?: boolean;
|
|
132
|
+
/** A ring's centre: the value, a word. */
|
|
133
|
+
children?: React.ReactNode;
|
|
134
|
+
};
|
|
135
|
+
/** A measure within a range — a quota, a balance, a share — toned by thresholds or split into parts. */
|
|
136
|
+
declare function Meter({ label, value, min, max, valueText, thresholds, optimum, parts, variant, size, showLabel, className, children, ...props }: MeterProps): React.JSX.Element;
|
|
137
|
+
interface MeterValueProps {
|
|
138
|
+
/** The figure. */
|
|
139
|
+
value: React.ReactNode;
|
|
140
|
+
/** A word under it. */
|
|
141
|
+
caption?: React.ReactNode;
|
|
142
|
+
}
|
|
143
|
+
/** For the ring's centre when it shows a figure and a word. */
|
|
144
|
+
declare function MeterValue({ value, caption }: MeterValueProps): React.JSX.Element;
|
|
145
|
+
|
|
146
|
+
export { Meter, type MeterPart, type MeterProps, MeterValue, type MeterValueProps, meter };
|
package/dist/meter.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// src/meter/meter.tsx
|
|
2
|
+
import { tv } from "tailwind-variants";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
var meter = tv({
|
|
5
|
+
slots: {
|
|
6
|
+
root: "flex w-full flex-col gap-1.5",
|
|
7
|
+
header: "flex items-baseline gap-2 text-xs",
|
|
8
|
+
caption: "min-w-0 flex-1 truncate font-medium text-fg",
|
|
9
|
+
figure: "shrink-0 tabular-nums text-fg-secondary",
|
|
10
|
+
track: "relative flex h-2 w-full gap-0.5 overflow-hidden rounded-full bg-surface-3",
|
|
11
|
+
fill: "h-full rounded-full transition-[width] duration-700 motion-reduce:transition-none",
|
|
12
|
+
legend: "mt-1 flex flex-wrap gap-x-4 gap-y-1 text-xs text-fg-secondary",
|
|
13
|
+
swatch: "inline-block h-2 w-2 shrink-0 rounded-full",
|
|
14
|
+
ring: "relative inline-flex shrink-0 items-center justify-center",
|
|
15
|
+
center: "absolute inset-0 flex flex-col items-center justify-center text-center"
|
|
16
|
+
},
|
|
17
|
+
variants: {
|
|
18
|
+
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" } }
|
|
19
|
+
},
|
|
20
|
+
defaultVariants: { size: "md" }
|
|
21
|
+
});
|
|
22
|
+
var BG = { ok: "bg-accent", success: "bg-success", warning: "bg-warning", danger: "bg-danger" };
|
|
23
|
+
var STROKE = { ok: "stroke-accent", success: "stroke-success", warning: "stroke-warning", danger: "stroke-danger" };
|
|
24
|
+
var PART_BG = ["bg-accent", "bg-fg-secondary", "bg-accent-2", "bg-border-strong"];
|
|
25
|
+
var PART_STROKE = ["stroke-accent", "stroke-fg-secondary", "stroke-accent-2", "stroke-border-strong"];
|
|
26
|
+
function Meter({
|
|
27
|
+
label,
|
|
28
|
+
value = 0,
|
|
29
|
+
min = 0,
|
|
30
|
+
max = 100,
|
|
31
|
+
valueText,
|
|
32
|
+
thresholds,
|
|
33
|
+
optimum = "low",
|
|
34
|
+
parts,
|
|
35
|
+
variant = "bar",
|
|
36
|
+
size = "md",
|
|
37
|
+
showLabel,
|
|
38
|
+
className,
|
|
39
|
+
children,
|
|
40
|
+
...props
|
|
41
|
+
}) {
|
|
42
|
+
const s = meter({ size });
|
|
43
|
+
const span = max - min || 1;
|
|
44
|
+
const ratio = Math.min(1, Math.max(0, (value - min) / span));
|
|
45
|
+
const level = !thresholds ? "ok" : ratio >= thresholds.high ? optimum === "low" ? "danger" : "success" : ratio >= thresholds.low ? "warning" : optimum === "low" ? "success" : "danger";
|
|
46
|
+
const words = valueText ?? `${Math.round(ratio * 100)} %`;
|
|
47
|
+
const shares = (parts ?? []).map((p, i) => ({ ...p, i, ratio: Math.min(1, Math.max(0, p.value / span)), words: p.valueText ?? `${Math.round(p.value / span * 100)} %` }));
|
|
48
|
+
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 };
|
|
49
|
+
const legend = parts && /* @__PURE__ */ jsx("ul", { className: s.legend(), children: shares.map((p) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-1.5", children: [
|
|
50
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: [s.swatch(), PART_BG[p.i % PART_BG.length]].join(" ") }),
|
|
51
|
+
/* @__PURE__ */ jsx("span", { className: "text-fg", children: p.label }),
|
|
52
|
+
/* @__PURE__ */ jsx("span", { className: "tabular-nums", children: p.words })
|
|
53
|
+
] }, p.i)) });
|
|
54
|
+
if (variant === "ring") {
|
|
55
|
+
const r = 44;
|
|
56
|
+
let offset = 0;
|
|
57
|
+
const arcs = parts ? shares.map((p) => {
|
|
58
|
+
const arc = { cls: PART_STROKE[p.i % PART_STROKE.length], len: p.ratio * 100, start: offset };
|
|
59
|
+
offset += arc.len;
|
|
60
|
+
return arc;
|
|
61
|
+
}) : [{ cls: STROKE[level], len: ratio * 100, start: 0 }];
|
|
62
|
+
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: [
|
|
63
|
+
/* @__PURE__ */ jsxs("div", { className: s.ring(), children: [
|
|
64
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 100 100", className: "h-full w-full -rotate-90", "aria-hidden": true, children: [
|
|
65
|
+
/* @__PURE__ */ jsx("circle", { cx: "50", cy: "50", r, fill: "none", strokeWidth: "9", className: "stroke-border-default" }),
|
|
66
|
+
arcs.map((a, i) => /* @__PURE__ */ jsx(
|
|
67
|
+
"circle",
|
|
68
|
+
{
|
|
69
|
+
cx: "50",
|
|
70
|
+
cy: "50",
|
|
71
|
+
r,
|
|
72
|
+
fill: "none",
|
|
73
|
+
strokeWidth: "9",
|
|
74
|
+
pathLength: 100,
|
|
75
|
+
strokeDasharray: `${Math.max(0, a.len - (parts ? 1 : 0))} ${100 - Math.max(0, a.len - (parts ? 1 : 0))}`,
|
|
76
|
+
strokeDashoffset: -a.start,
|
|
77
|
+
className: [a.cls, "transition-[stroke-dasharray] duration-700 motion-reduce:transition-none"].join(" ")
|
|
78
|
+
},
|
|
79
|
+
i
|
|
80
|
+
))
|
|
81
|
+
] }),
|
|
82
|
+
children !== void 0 && /* @__PURE__ */ jsx("div", { className: s.center(), children })
|
|
83
|
+
] }),
|
|
84
|
+
legend
|
|
85
|
+
] });
|
|
86
|
+
}
|
|
87
|
+
return /* @__PURE__ */ jsxs("div", { ...a11y, "data-level": parts ? void 0 : level, className: s.root({ className }), ...props, children: [
|
|
88
|
+
showLabel && /* @__PURE__ */ jsxs("div", { "aria-hidden": true, className: s.header(), children: [
|
|
89
|
+
/* @__PURE__ */ jsx("span", { className: s.caption(), children: label }),
|
|
90
|
+
!parts && /* @__PURE__ */ jsx("span", { className: s.figure(), children: words })
|
|
91
|
+
] }),
|
|
92
|
+
/* @__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}%` } }) }),
|
|
93
|
+
legend
|
|
94
|
+
] });
|
|
95
|
+
}
|
|
96
|
+
function MeterValue({ value, caption }) {
|
|
97
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
98
|
+
/* @__PURE__ */ jsx("span", { className: "font-display text-lg font-semibold text-fg tabular-nums", children: value }),
|
|
99
|
+
caption && /* @__PURE__ */ jsx("span", { className: "text-[10px] uppercase tracking-wide text-fg-secondary", children: caption })
|
|
100
|
+
] });
|
|
101
|
+
}
|
|
102
|
+
export {
|
|
103
|
+
Meter,
|
|
104
|
+
MeterValue,
|
|
105
|
+
meter
|
|
106
|
+
};
|
|
107
|
+
//# sourceMappingURL=meter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/meter/meter.tsx"],"sourcesContent":["// Server-safe. A measure within a known range — a quota, a balance, a share — as opposed to the progress of a task.\n// `role=\"meter\"`, its value in words; thresholds turn it to warning then danger (or the reverse when high is good);\n// split into parts (90 % to the creator, 10 % to the platform) it becomes a stacked bar or ring with its legend: the\n// text version of what it draws.\nimport type * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nexport const meter = tv({\n slots: {\n root: \"flex w-full flex-col gap-1.5\",\n header: \"flex items-baseline gap-2 text-xs\",\n caption: \"min-w-0 flex-1 truncate font-medium text-fg\",\n figure: \"shrink-0 tabular-nums text-fg-secondary\",\n track: \"relative flex h-2 w-full gap-0.5 overflow-hidden rounded-full bg-surface-3\",\n fill: \"h-full rounded-full transition-[width] duration-700 motion-reduce:transition-none\",\n legend: \"mt-1 flex flex-wrap gap-x-4 gap-y-1 text-xs text-fg-secondary\",\n swatch: \"inline-block h-2 w-2 shrink-0 rounded-full\",\n ring: \"relative inline-flex shrink-0 items-center justify-center\",\n center: \"absolute inset-0 flex flex-col items-center justify-center text-center\",\n },\n variants: {\n 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\" } },\n },\n defaultVariants: { size: \"md\" },\n});\n\n/** The colour of a fill: by tone (thresholds) or by part (the accent, a neutral, accent-2, a lighter neutral). */\nconst BG = { ok: \"bg-accent\", success: \"bg-success\", warning: \"bg-warning\", danger: \"bg-danger\" } as const;\nconst STROKE = { ok: \"stroke-accent\", success: \"stroke-success\", warning: \"stroke-warning\", danger: \"stroke-danger\" } as const;\nconst PART_BG = [\"bg-accent\", \"bg-fg-secondary\", \"bg-accent-2\", \"bg-border-strong\"];\nconst PART_STROKE = [\"stroke-accent\", \"stroke-fg-secondary\", \"stroke-accent-2\", \"stroke-border-strong\"];\n\nexport interface MeterPart {\n /** Its name (“To the creator”) — passed translated. */\n label: string;\n /** Its value, in the meter's unit. */\n value: number;\n /** Its value in words (“€9.00”), in the legend and for assistive technology; default: its percentage. */\n valueText?: string;\n}\n\nexport type MeterProps = Omit<React.ComponentProps<\"div\">, \"children\"> & {\n /** The accessible name (“Storage”, “Split of a tip”) — passed translated; shown with `showLabel`. */\n label: string;\n /** The value, between `min` and `max` (ignored with `parts`). */\n value?: number;\n /**\n * The bottom of the range.\n * @default 0\n */\n min?: number;\n /**\n * The top of the range.\n * @default 100\n */\n max?: number;\n /** The value in words (“42 GB of 50 GB”) — passed translated; default: the percentage. */\n valueText?: string;\n /**\n * Ratios (0–1) where the tone changes: past `high` it is in danger, past `low` in warning — or the reverse with\n * `optimum=\"high\"` (a balance: the lower, the worse). Without thresholds the fill is the brand accent.\n */\n thresholds?: { readonly low: number; readonly high: number };\n /**\n * Which end is good.\n * @default \"low\"\n */\n optimum?: \"low\" | \"high\";\n /** Split into parts that add up to `max`: a stacked bar or ring, with its legend. */\n parts?: readonly MeterPart[];\n /**\n * bar · ring.\n * @default \"bar\"\n */\n variant?: \"bar\" | \"ring\";\n /**\n * sm · md · lg: the bar's thickness, the ring's diameter.\n * @default \"md\"\n */\n size?: \"sm\" | \"md\" | \"lg\";\n /** Shows the label and the value above the bar. */\n showLabel?: boolean;\n /** A ring's centre: the value, a word. */\n children?: React.ReactNode;\n};\n\n/** A measure within a range — a quota, a balance, a share — toned by thresholds or split into parts. */\nexport function Meter({\n label,\n value = 0,\n min = 0,\n max = 100,\n valueText,\n thresholds,\n optimum = \"low\",\n parts,\n variant = \"bar\",\n size = \"md\",\n showLabel,\n className,\n children,\n ...props\n}: MeterProps) {\n const s = meter({ size });\n const span = max - min || 1;\n const ratio = Math.min(1, Math.max(0, (value - min) / span));\n const level = !thresholds ? \"ok\" : ratio >= thresholds.high ? (optimum === \"low\" ? \"danger\" : \"success\") : ratio >= thresholds.low ? \"warning\" : optimum === \"low\" ? \"success\" : \"danger\";\n const words = valueText ?? `${Math.round(ratio * 100)} %`;\n const shares = (parts ?? []).map((p, i) => ({ ...p, i, ratio: Math.min(1, Math.max(0, p.value / span)), words: p.valueText ?? `${Math.round((p.value / span) * 100)} %` }));\n const a11y = parts\n ? { role: \"group\" as const, \"aria-label\": label }\n : { role: \"meter\" as const, \"aria-label\": label, \"aria-valuemin\": min, \"aria-valuemax\": max, \"aria-valuenow\": Math.min(max, Math.max(min, value)), \"aria-valuetext\": words };\n const legend = parts && (\n <ul className={s.legend()}>\n {shares.map((p) => (\n <li key={p.i} className=\"flex items-center gap-1.5\">\n <span aria-hidden className={[s.swatch(), PART_BG[p.i % PART_BG.length]].join(\" \")} />\n <span className=\"text-fg\">{p.label}</span>\n <span className=\"tabular-nums\">{p.words}</span>\n </li>\n ))}\n </ul>\n );\n\n if (variant === \"ring\") {\n const r = 44;\n let offset = 0;\n const arcs = parts\n ? shares.map((p) => {\n const arc = { cls: PART_STROKE[p.i % PART_STROKE.length]!, len: p.ratio * 100, start: offset };\n offset += arc.len;\n return arc;\n })\n : [{ cls: STROKE[level], len: ratio * 100, start: 0 }];\n return (\n <div {...a11y} data-level={parts ? undefined : level} className={[\"inline-flex flex-col items-center gap-2\", className].filter(Boolean).join(\" \")} {...props}>\n <div className={s.ring()}>\n <svg viewBox=\"0 0 100 100\" className=\"h-full w-full -rotate-90\" aria-hidden>\n <circle cx=\"50\" cy=\"50\" r={r} fill=\"none\" strokeWidth=\"9\" className=\"stroke-border-default\" />\n {arcs.map((a, i) => (\n <circle\n key={i}\n cx=\"50\"\n cy=\"50\"\n r={r}\n fill=\"none\"\n strokeWidth=\"9\"\n pathLength={100}\n strokeDasharray={`${Math.max(0, a.len - (parts ? 1 : 0))} ${100 - Math.max(0, a.len - (parts ? 1 : 0))}`}\n strokeDashoffset={-a.start}\n className={[a.cls, \"transition-[stroke-dasharray] duration-700 motion-reduce:transition-none\"].join(\" \")}\n />\n ))}\n </svg>\n {children !== undefined && <div className={s.center()}>{children}</div>}\n </div>\n {legend}\n </div>\n );\n }\n\n return (\n <div {...a11y} data-level={parts ? undefined : level} className={s.root({ className })} {...props}>\n {showLabel && (\n <div aria-hidden className={s.header()}>\n <span className={s.caption()}>{label}</span>\n {!parts && <span className={s.figure()}>{words}</span>}\n </div>\n )}\n <div aria-hidden className={s.track()}>\n {parts ? (\n shares.map((p) => <span key={p.i} className={[s.fill(), PART_BG[p.i % PART_BG.length]].join(\" \")} style={{ width: `${p.ratio * 100}%` }} />)\n ) : (\n <span className={[s.fill(), BG[level]].join(\" \")} style={{ width: `${ratio * 100}%` }} />\n )}\n </div>\n {legend}\n </div>\n );\n}\n\nexport interface MeterValueProps {\n /** The figure. */\n value: React.ReactNode;\n /** A word under it. */\n caption?: React.ReactNode;\n}\n\n/** For the ring's centre when it shows a figure and a word. */\nexport function MeterValue({ value, caption }: MeterValueProps) {\n return (\n <>\n <span className=\"font-display text-lg font-semibold text-fg tabular-nums\">{value}</span>\n {caption && <span className=\"text-[10px] uppercase tracking-wide text-fg-secondary\">{caption}</span>}\n </>\n );\n}\n"],"mappings":"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","names":[]}
|