@krizaka/ui 3.30.0 → 3.32.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 +4 -8
- package/dist/action-bar.js +3 -3
- package/dist/action-bar.js.map +1 -1
- package/dist/argument-card.d.ts +1 -1
- package/dist/argument-card.js +2 -2
- package/dist/argument-card.js.map +1 -1
- package/dist/blocks.css +19 -60
- package/dist/{button-BmCrLCq7.d.ts → button-Cm9pFH3P.d.ts} +10 -9
- package/dist/button.d.ts +11 -10
- package/dist/button.js +1 -1
- package/dist/captcha.js +1 -1
- package/dist/card-grid.d.ts +5 -12
- package/dist/card-grid.js +4 -12
- package/dist/card-grid.js.map +1 -1
- package/dist/card.d.ts +7 -7
- package/dist/card.js +2 -2
- package/dist/card.js.map +1 -1
- package/dist/{chunk-GYKQ52HX.js → chunk-2AJ4NJUF.js} +2 -2
- package/dist/{chunk-MQPC3P4T.js → chunk-2DGLWTPS.js} +3 -3
- package/dist/chunk-2DGLWTPS.js.map +1 -0
- package/dist/{chunk-DZU6RMYD.js → chunk-3DHNN5V7.js} +15 -9
- package/dist/chunk-3DHNN5V7.js.map +1 -0
- package/dist/{chunk-TFJDIOFK.js → chunk-4E5IUZHR.js} +3 -3
- package/dist/chunk-4E5IUZHR.js.map +1 -0
- package/dist/{chunk-HAQWKORZ.js → chunk-4JO5GSSD.js} +1 -37
- package/dist/chunk-4JO5GSSD.js.map +1 -0
- package/dist/{chunk-APNIVNIX.js → chunk-IBDTAMPF.js} +3 -4
- package/dist/chunk-IBDTAMPF.js.map +1 -0
- package/dist/{chunk-LYOPPBMC.js → chunk-KIBICUZK.js} +2 -2
- package/dist/{chunk-HBZPJ3EP.js → chunk-NJFCSVPT.js} +2 -2
- package/dist/{chunk-KM6VAKIE.js → chunk-RE5WJ4QP.js} +2 -2
- package/dist/chunk-RE5WJ4QP.js.map +1 -0
- package/dist/{chunk-CHOJUCVE.js → chunk-SJS6RJIT.js} +15 -9
- package/dist/chunk-SJS6RJIT.js.map +1 -0
- package/dist/{chunk-6ZYRUOTA.js → chunk-UV4HBVH7.js} +15 -9
- package/dist/chunk-UV4HBVH7.js.map +1 -0
- package/dist/command.js +2 -2
- package/dist/confirm-button.d.ts +1 -1
- package/dist/confirm-button.js +1 -1
- package/dist/continue-prompt.js +14 -8
- package/dist/continue-prompt.js.map +1 -1
- package/dist/data-table.js +2 -2
- package/dist/dialog.js +2 -2
- package/dist/engine-flow.d.ts +25 -2
- package/dist/engine-flow.js +27 -6
- package/dist/engine-flow.js.map +1 -1
- package/dist/force-update-screen.js +2 -2
- package/dist/force-update-screen.js.map +1 -1
- package/dist/genui.js +3 -3
- package/dist/glow-card.d.ts +5 -4
- package/dist/glow-card.js +2 -8
- package/dist/glow-card.js.map +1 -1
- package/dist/image-cropper.js +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/iso.d.ts +1 -1
- package/dist/iso.js.map +1 -1
- package/dist/landscape.js +4 -4
- package/dist/landscape.js.map +1 -1
- package/dist/link-card.d.ts +3 -6
- package/dist/link-card.js +1 -1
- package/dist/maintenance-screen.js +1 -1
- package/dist/map.d.ts +50 -5
- package/dist/map.js +202 -66
- package/dist/map.js.map +1 -1
- package/dist/match-dialog.js +1 -1
- package/dist/media-gallery.js +1 -1
- package/dist/media-gallery.js.map +1 -1
- package/dist/media-grid.js +2 -2
- package/dist/motion.css +52 -122
- package/dist/motion.d.ts +2 -2
- package/dist/motion.js +1 -1
- package/dist/native/map.js +64 -64
- package/dist/native/map.js.map +1 -1
- package/dist/native.js +30 -30
- package/dist/native.js.map +1 -1
- package/dist/nav-bar.js +2 -2
- package/dist/pointer-light.d.ts +6 -9
- package/dist/pointer-light.js +9 -5
- package/dist/pointer-light.js.map +1 -1
- package/dist/radio-group.js +2 -2
- package/dist/radio-group.js.map +1 -1
- package/dist/service-card.js +1 -1
- package/dist/service-card.js.map +1 -1
- package/dist/site-footer.js +2 -2
- package/dist/slider.js +1 -1
- package/dist/slider.js.map +1 -1
- package/dist/story.css +5 -12
- package/dist/theme.d.ts +1 -1
- package/dist/theme.js +1 -1
- package/dist/update-prompt.js +2 -2
- package/dist/update-prompt.js.map +1 -1
- package/dist/vertical-video.js +1 -1
- package/dist/video-player.js +2 -2
- package/dist/video-player.js.map +1 -1
- package/package.json +8 -8
- package/registry/action-bar.json +1 -1
- package/registry/app-shell.json +4 -4
- package/registry/argument-card.json +7 -16
- package/registry/auth-layout.json +4 -4
- package/registry/button.json +20 -20
- package/registry/card-grid.json +21 -24
- package/registry/card.json +7 -7
- package/registry/component-page.json +2 -2
- package/registry/confirm-button.json +6 -6
- package/registry/contact-page.json +2 -2
- package/registry/cover-hero.json +3 -3
- package/registry/dashboard-layout.json +2 -2
- package/registry/detail-layout.json +4 -4
- package/registry/docs-hub-page.json +2 -2
- package/registry/empty-state.json +1 -1
- package/registry/engine-flow.json +74 -3
- package/registry/examples/app-shell/admin-console.js +1 -1
- package/registry/examples/app-shell/admin-console.tsx +1 -1
- package/registry/examples/app-shell/basic.js +1 -1
- package/registry/examples/app-shell/basic.tsx +1 -1
- package/registry/examples/app-shell/desktop.js +1 -1
- package/registry/examples/app-shell/desktop.tsx +1 -1
- package/registry/examples/app-shell/orochia-studio.js +1 -1
- package/registry/examples/app-shell/orochia-studio.tsx +1 -1
- package/registry/examples/argument-card/hover-quiet.js +1 -1
- package/registry/examples/argument-card/hover-quiet.tsx +1 -1
- package/registry/examples/auth-layout/onboarding.js +2 -2
- package/registry/examples/auth-layout/onboarding.tsx +2 -2
- package/registry/examples/auth-layout/sign-in.js +2 -2
- package/registry/examples/auth-layout/sign-in.tsx +2 -2
- package/registry/examples/auth-layout/verify.js +1 -1
- package/registry/examples/auth-layout/verify.tsx +1 -1
- package/registry/examples/avatar/native/presence.dark.png +0 -0
- package/registry/examples/avatar/native/presence.light.png +0 -0
- package/registry/examples/badge/native/tones.dark.png +0 -0
- package/registry/examples/badge/native/tones.light.png +0 -0
- package/registry/examples/button/native/disabled.dark.png +0 -0
- package/registry/examples/button/native/disabled.light.png +0 -0
- package/registry/examples/button/native/gradient.dark.png +0 -0
- package/registry/examples/button/native/gradient.light.png +0 -0
- package/registry/examples/button/native/icon-only.dark.png +0 -0
- package/registry/examples/button/native/icon-only.light.png +0 -0
- package/registry/examples/button/native/icon.dark.png +0 -0
- package/registry/examples/button/native/icon.light.png +0 -0
- package/registry/examples/button/native/loading.dark.png +0 -0
- package/registry/examples/button/native/loading.light.png +0 -0
- package/registry/examples/button/native/primary.dark.png +0 -0
- package/registry/examples/button/native/primary.light.png +0 -0
- package/registry/examples/button/native/sizes.dark.png +0 -0
- package/registry/examples/button/native/sizes.light.png +0 -0
- package/registry/examples/button/signature.js +2 -2
- package/registry/examples/button/signature.tsx +4 -4
- package/registry/examples/card/interactive-link.tsx +1 -1
- package/registry/examples/card/native/body.dark.png +0 -0
- package/registry/examples/card/native/body.light.png +0 -0
- package/registry/examples/card/native/default.dark.png +0 -0
- package/registry/examples/card/native/default.light.png +0 -0
- package/registry/examples/card/native/elevated.dark.png +0 -0
- package/registry/examples/card/native/elevated.light.png +0 -0
- package/registry/examples/card-grid/hover-quiet.js +1 -1
- package/registry/examples/card-grid/hover-quiet.tsx +1 -1
- package/registry/examples/chat/native/assistant.dark.png +0 -0
- package/registry/examples/chat/native/assistant.light.png +0 -0
- package/registry/examples/chat/native/conversation.dark.png +0 -0
- package/registry/examples/chat/native/conversation.light.png +0 -0
- package/registry/examples/chat/native/inbox.dark.png +0 -0
- package/registry/examples/chat/native/inbox.light.png +0 -0
- package/registry/examples/checkbox/native/terms.dark.png +0 -0
- package/registry/examples/checkbox/native/terms.light.png +0 -0
- package/registry/examples/chip/native/multiple.dark.png +0 -0
- package/registry/examples/chip/native/multiple.light.png +0 -0
- package/registry/examples/chip/native/single.dark.png +0 -0
- package/registry/examples/chip/native/single.light.png +0 -0
- package/registry/examples/chip/native/toggle.dark.png +0 -0
- package/registry/examples/chip/native/toggle.light.png +0 -0
- package/registry/examples/component-page/button.js +2 -2
- package/registry/examples/component-page/button.tsx +2 -2
- package/registry/examples/contact-page/team.js +1 -1
- package/registry/examples/contact-page/team.tsx +1 -1
- package/registry/examples/cover-hero/plain.js +1 -1
- package/registry/examples/cover-hero/plain.tsx +1 -1
- package/registry/examples/cover-hero/shop.js +1 -1
- package/registry/examples/cover-hero/shop.tsx +1 -1
- package/registry/examples/dashboard-layout/orazaka-eric.js +2 -2
- package/registry/examples/dashboard-layout/orazaka-eric.tsx +2 -2
- package/registry/examples/date-picker/native/calendar.dark.png +0 -0
- package/registry/examples/date-picker/native/calendar.light.png +0 -0
- package/registry/examples/date-picker/native/day-strip.dark.png +0 -0
- package/registry/examples/date-picker/native/day-strip.light.png +0 -0
- package/registry/examples/date-picker/native/month-year.dark.png +0 -0
- package/registry/examples/date-picker/native/month-year.light.png +0 -0
- package/registry/examples/date-picker/native/time-slots.dark.png +0 -0
- package/registry/examples/date-picker/native/time-slots.light.png +0 -0
- package/registry/examples/description-list/native/job-details.dark.png +0 -0
- package/registry/examples/description-list/native/job-details.light.png +0 -0
- package/registry/examples/detail-layout/checkout.js +1 -1
- package/registry/examples/detail-layout/checkout.tsx +1 -1
- package/registry/examples/detail-layout/job.js +1 -1
- package/registry/examples/detail-layout/job.tsx +1 -1
- package/registry/examples/detail-layout/video.js +2 -2
- package/registry/examples/detail-layout/video.tsx +2 -2
- package/registry/examples/dialog/native/sheet.dark.png +0 -0
- package/registry/examples/dialog/native/sheet.light.png +0 -0
- package/registry/examples/docs-hub-page/krizaka.js +1 -1
- package/registry/examples/docs-hub-page/krizaka.tsx +1 -1
- package/registry/examples/empty-state/library.js +1 -1
- package/registry/examples/empty-state/library.tsx +1 -1
- package/registry/examples/empty-state/native/default.dark.png +0 -0
- package/registry/examples/empty-state/native/default.light.png +0 -0
- package/registry/examples/fab/native/extended.dark.png +0 -0
- package/registry/examples/fab/native/extended.light.png +0 -0
- package/registry/examples/force-update-screen/native/default.dark.png +0 -0
- package/registry/examples/force-update-screen/native/default.light.png +0 -0
- package/registry/examples/form/native/amount.dark.png +0 -0
- package/registry/examples/form/native/amount.light.png +0 -0
- package/registry/examples/form/native/audience.dark.png +0 -0
- package/registry/examples/form/native/audience.light.png +0 -0
- package/registry/examples/form/native/profile.dark.png +0 -0
- package/registry/examples/form/native/profile.light.png +0 -0
- package/registry/examples/form/native/settings.dark.png +0 -0
- package/registry/examples/form/native/settings.light.png +0 -0
- package/registry/examples/form/native/sign-up.dark.png +0 -0
- package/registry/examples/form/native/sign-up.light.png +0 -0
- package/registry/examples/form/native/upload.dark.png +0 -0
- package/registry/examples/form/native/upload.light.png +0 -0
- package/registry/examples/glow-card/spotlight.tsx +1 -1
- package/registry/examples/goal-gauge/native/default.dark.png +0 -0
- package/registry/examples/goal-gauge/native/default.light.png +0 -0
- package/registry/examples/landing-page/krizaka.js +3 -3
- package/registry/examples/landing-page/krizaka.tsx +3 -3
- package/registry/examples/link-card/hover-quiet.js +1 -1
- package/registry/examples/link-card/hover-quiet.tsx +1 -1
- package/registry/examples/loader/native/block.dark.png +0 -0
- package/registry/examples/loader/native/block.light.png +0 -0
- package/registry/examples/map/native/location-picker.dark.png +0 -0
- package/registry/examples/map/native/location-picker.light.png +0 -0
- package/registry/examples/map/route.js +36 -0
- package/registry/examples/map/route.tsx +35 -0
- package/registry/examples/marketing-layout/pricing.js +1 -1
- package/registry/examples/marketing-layout/pricing.tsx +1 -1
- package/registry/examples/marketing-layout/product-page.js +1 -1
- package/registry/examples/marketing-layout/product-page.tsx +2 -2
- package/registry/examples/mascot-state/native/default.dark.png +0 -0
- package/registry/examples/mascot-state/native/default.light.png +0 -0
- package/registry/examples/mascots/native/crane.dark.png +0 -0
- package/registry/examples/mascots/native/crane.light.png +0 -0
- package/registry/examples/mascots/native/default.dark.png +0 -0
- package/registry/examples/mascots/native/default.light.png +0 -0
- package/registry/examples/mascots/native/lovebird.dark.png +0 -0
- package/registry/examples/mascots/native/lovebird.light.png +0 -0
- package/registry/examples/mascots/native/phoenix.dark.png +0 -0
- package/registry/examples/mascots/native/phoenix.light.png +0 -0
- package/registry/examples/match-dialog/native/its-a-match.dark.png +0 -0
- package/registry/examples/match-dialog/native/its-a-match.light.png +0 -0
- package/registry/examples/media-capture/native/photo.dark.png +0 -0
- package/registry/examples/media-capture/native/photo.light.png +0 -0
- package/registry/examples/meta-row/native/video.dark.png +0 -0
- package/registry/examples/meta-row/native/video.light.png +0 -0
- package/registry/examples/meter/native/credits.dark.png +0 -0
- package/registry/examples/meter/native/credits.light.png +0 -0
- package/registry/examples/meter/native/split-ring.dark.png +0 -0
- package/registry/examples/meter/native/split-ring.light.png +0 -0
- package/registry/examples/nav-bar/family-product.js +1 -1
- package/registry/examples/nav-bar/family-product.tsx +1 -1
- package/registry/examples/onboarding/native/first-launch.dark.png +0 -0
- package/registry/examples/onboarding/native/first-launch.light.png +0 -0
- package/registry/examples/permission-prompt/native/blocked.dark.png +0 -0
- package/registry/examples/permission-prompt/native/blocked.light.png +0 -0
- package/registry/examples/permission-prompt/native/camera.dark.png +0 -0
- package/registry/examples/permission-prompt/native/camera.light.png +0 -0
- package/registry/examples/photo-grid/native/profile-photos.dark.png +0 -0
- package/registry/examples/photo-grid/native/profile-photos.light.png +0 -0
- package/registry/examples/pricing-page/credits.js +3 -3
- package/registry/examples/pricing-page/credits.tsx +3 -3
- package/registry/examples/product-page/orazaka.js +2 -2
- package/registry/examples/product-page/orazaka.tsx +2 -2
- package/registry/examples/profile-card/native/default.dark.png +0 -0
- package/registry/examples/profile-card/native/default.light.png +0 -0
- package/registry/examples/progress/native/bar.dark.png +0 -0
- package/registry/examples/progress/native/bar.light.png +0 -0
- package/registry/examples/progress/native/indeterminate.dark.png +0 -0
- package/registry/examples/progress/native/indeterminate.light.png +0 -0
- package/registry/examples/progress/native/ring.dark.png +0 -0
- package/registry/examples/progress/native/ring.light.png +0 -0
- package/registry/examples/radio-group/native/buttons.dark.png +0 -0
- package/registry/examples/radio-group/native/buttons.light.png +0 -0
- package/registry/examples/radio-group/native/cards.dark.png +0 -0
- package/registry/examples/radio-group/native/cards.light.png +0 -0
- package/registry/examples/radio-group/native/list.dark.png +0 -0
- package/registry/examples/radio-group/native/list.light.png +0 -0
- package/registry/examples/screen/native/auth.dark.png +0 -0
- package/registry/examples/screen/native/auth.light.png +0 -0
- package/registry/examples/screen/native/chat.dark.png +0 -0
- package/registry/examples/screen/native/chat.light.png +0 -0
- package/registry/examples/screen/native/profile.dark.png +0 -0
- package/registry/examples/screen/native/profile.light.png +0 -0
- package/registry/examples/screen/native/settings.dark.png +0 -0
- package/registry/examples/screen/native/settings.light.png +0 -0
- package/registry/examples/search-field/native/cancel.dark.png +0 -0
- package/registry/examples/search-field/native/cancel.light.png +0 -0
- package/registry/examples/settings-layout/account.js +1 -1
- package/registry/examples/settings-layout/account.tsx +1 -1
- package/registry/examples/settings-layout/native/account.dark.png +0 -0
- package/registry/examples/settings-layout/native/account.light.png +0 -0
- package/registry/examples/settings-layout/sections.js +1 -1
- package/registry/examples/settings-layout/sections.tsx +1 -1
- package/registry/examples/showcase/native/slides.dark.png +0 -0
- package/registry/examples/showcase/native/slides.light.png +0 -0
- package/registry/examples/slider/native/default.dark.png +0 -0
- package/registry/examples/slider/native/default.light.png +0 -0
- package/registry/examples/slider/native/range.dark.png +0 -0
- package/registry/examples/slider/native/range.light.png +0 -0
- package/registry/examples/slider/native/stepped.dark.png +0 -0
- package/registry/examples/slider/native/stepped.light.png +0 -0
- package/registry/examples/slider/native/vertical.dark.png +0 -0
- package/registry/examples/slider/native/vertical.light.png +0 -0
- package/registry/examples/spinner/native/sizes.dark.png +0 -0
- package/registry/examples/spinner/native/sizes.light.png +0 -0
- package/registry/examples/split-view/messages.js +1 -1
- package/registry/examples/split-view/messages.tsx +1 -1
- package/registry/examples/status-page/building.js +1 -1
- package/registry/examples/status-page/building.tsx +1 -1
- package/registry/examples/step-progress/native/sign-up.dark.png +0 -0
- package/registry/examples/step-progress/native/sign-up.light.png +0 -0
- package/registry/examples/stepper/native/run.dark.png +0 -0
- package/registry/examples/stepper/native/run.light.png +0 -0
- package/registry/examples/sticky-action/phone.js +1 -1
- package/registry/examples/sticky-action/phone.tsx +1 -1
- package/registry/examples/switch/native/default.dark.png +0 -0
- package/registry/examples/switch/native/default.light.png +0 -0
- package/registry/examples/switch/native/settings.dark.png +0 -0
- package/registry/examples/switch/native/settings.light.png +0 -0
- package/registry/examples/tab-bar/native/main-sections.dark.png +0 -0
- package/registry/examples/tab-bar/native/main-sections.light.png +0 -0
- package/registry/examples/tabs/native/sections.dark.png +0 -0
- package/registry/examples/tabs/native/sections.light.png +0 -0
- package/registry/examples/tag-selector/native/interests.dark.png +0 -0
- package/registry/examples/tag-selector/native/interests.light.png +0 -0
- package/registry/examples/toast/native/snackbar.dark.png +0 -0
- package/registry/examples/toast/native/snackbar.light.png +0 -0
- package/registry/examples/toast/native/tones.dark.png +0 -0
- package/registry/examples/toast/native/tones.light.png +0 -0
- package/registry/examples/txt/native/tones.dark.png +0 -0
- package/registry/examples/txt/native/tones.light.png +0 -0
- package/registry/examples/update-prompt/native/default.dark.png +0 -0
- package/registry/examples/update-prompt/native/default.light.png +0 -0
- package/registry/examples/verified-badge/native/verified.dark.png +0 -0
- package/registry/examples/verified-badge/native/verified.light.png +0 -0
- package/registry/examples/vertical-video/native/rail.dark.png +0 -0
- package/registry/examples/vertical-video/native/rail.light.png +0 -0
- package/registry/examples/waveform/native/card.dark.png +0 -0
- package/registry/examples/waveform/native/card.light.png +0 -0
- package/registry/force-update-screen.json +1 -1
- package/registry/form.json +6 -6
- package/registry/glow-card.json +19 -19
- package/registry/index.json +20 -21
- package/registry/iso.json +7 -7
- package/registry/landing-page.json +2 -2
- package/registry/landscape.json +1 -1
- package/registry/link-card.json +15 -24
- package/registry/map.json +235 -6
- package/registry/marketing-layout.json +3 -3
- package/registry/media-gallery.json +1 -1
- package/registry/nav-bar.json +3 -3
- package/registry/pointer-light.json +17 -22
- package/registry/pricing-page.json +2 -2
- package/registry/product-page.json +2 -2
- package/registry/progress.json +2 -2
- package/registry/radio-group.json +1 -1
- package/registry/rail-nav.json +2 -2
- package/registry/service-card.json +1 -1
- package/registry/settings-layout.json +3 -3
- package/registry/site-footer.json +2 -2
- package/registry/slider.json +1 -1
- package/registry/split-view.json +2 -2
- package/registry/status-page.json +1 -1
- package/registry/sticky-action.json +2 -2
- package/registry/update-prompt.json +1 -1
- package/registry/video-player.json +1 -1
- package/dist/chunk-6ZYRUOTA.js.map +0 -1
- package/dist/chunk-AB5D3KBG.js +0 -37
- package/dist/chunk-AB5D3KBG.js.map +0 -1
- package/dist/chunk-APNIVNIX.js.map +0 -1
- package/dist/chunk-CHOJUCVE.js.map +0 -1
- package/dist/chunk-DZU6RMYD.js.map +0 -1
- package/dist/chunk-HAQWKORZ.js.map +0 -1
- package/dist/chunk-KM6VAKIE.js.map +0 -1
- package/dist/chunk-MQPC3P4T.js.map +0 -1
- package/dist/chunk-TFJDIOFK.js.map +0 -1
- package/registry/examples/argument-card/hover-light.js +0 -17
- package/registry/examples/argument-card/hover-light.tsx +0 -19
- package/registry/examples/link-card/hover-light.d.ts +0 -4
- package/registry/examples/link-card/hover-light.js +0 -18
- package/registry/examples/link-card/hover-light.tsx +0 -20
- /package/dist/{chunk-GYKQ52HX.js.map → chunk-2AJ4NJUF.js.map} +0 -0
- /package/dist/{chunk-LYOPPBMC.js.map → chunk-KIBICUZK.js.map} +0 -0
- /package/dist/{chunk-HBZPJ3EP.js.map → chunk-NJFCSVPT.js.map} +0 -0
- /package/registry/examples/{argument-card/hover-light.d.ts → map/route.d.ts} +0 -0
package/dist/video-player.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/video-player/video-player.tsx"],"sourcesContent":["// Client. A `<video>` with its own controls, accessible from end to end: a named region the keyboard reaches (Space /\n// K, arrows, J / L, M, F, C, Home / End, 0–9), every button named by a prop, the seek and volume sliders of\n// `@krizaka/ui/slider` with the time read aloud. HLS plays natively where the browser can (Safari, iOS) and through\n// hls.js elsewhere — loaded only then, by the app's `loadHls` (`() => import(\"hls.js\")`): an optional peer, never in\n// the bundle of a page that does not play HLS. A signed address can be resolved at the last moment (`src` as a\n// function), again after an error; the video pauses when it leaves the screen; autoplay waits under reduced motion.\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { CaptionsGlyph, FullscreenGlyph, PauseGlyph, PipGlyph, PlayGlyph, RetryGlyph, VolumeGlyph } from \"../glyphs\";\nimport { clamp, formatClock, playSafely, useInView, usePrefersReducedMotion } from \"../media\";\nimport { Slider } from \"../slider\";\n\nexport const videoPlayer = tv({\n slots: {\n root:\n \"theme-dark group/player relative isolate w-full overflow-hidden rounded-xl bg-media text-fg-on-media \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring data-[idle]:cursor-none\",\n video: \"absolute inset-0 h-full w-full object-contain\",\n poster: \"pointer-events-none absolute inset-0 h-full w-full object-contain\",\n big:\n \"absolute left-1/2 top-1/2 z-10 inline-flex h-16 w-16 -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full \" +\n \"bg-scrim-strong text-fg-on-media shadow-lg backdrop-blur-sm transition-transform hover:scale-105 motion-reduce:transition-none motion-reduce:hover:scale-100 \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n bar:\n \"absolute inset-x-0 bottom-0 z-20 flex flex-col gap-1 bg-linear-to-t from-scrim-strong via-scrim to-transparent px-3 pb-2 pt-10 \" +\n \"transition-opacity duration-300 motion-reduce:transition-none group-data-[idle]/player:opacity-0 group-focus-within/player:opacity-100\",\n row: \"flex items-center gap-1\",\n button:\n \"inline-flex h-9 min-w-9 shrink-0 items-center justify-center gap-1 rounded-md px-1.5 text-xs font-semibold text-fg-on-media \" +\n \"transition-colors hover:bg-fg-on-media/15 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring aria-pressed:bg-fg-on-media/20\",\n time: \"px-1.5 font-mono text-[11px] tabular-nums text-fg-on-media\",\n volume: \"hidden w-20 items-center sm:flex\",\n status: \"absolute inset-0 z-30 flex flex-col items-center justify-center gap-3 bg-scrim-strong p-6 text-center text-sm\",\n spinner: \"h-10 w-10 animate-spin rounded-full border-2 border-fg-on-media/30 border-t-fg-on-media motion-reduce:animate-none\",\n overlay: \"absolute inset-0 z-30\",\n },\n});\n\n/** What `loadHls` resolves to: the module of hls.js (only the members the player uses are typed). */\nexport type HlsModule = {\n default: {\n new (config?: Record<string, unknown>): {\n loadSource(url: string): void;\n attachMedia(media: HTMLMediaElement): void;\n destroy(): void;\n startLoad(): void;\n recoverMediaError(): void;\n on(event: string, listener: (event: string, data: { fatal?: boolean; type?: string }) => void): void;\n };\n isSupported(): boolean;\n Events: { ERROR: string };\n ErrorTypes: { NETWORK_ERROR: string; MEDIA_ERROR: string };\n };\n};\n\nexport type VideoTrack = {\n /** The WebVTT file. */\n src: string;\n /** Its language, BCP 47 (`\"fr\"`). */\n srcLang: string;\n /** Its name in the menu (\"Français\"), passed translated. */\n label: string;\n /** `subtitles` (default) or `captions` (with the sounds, for deaf viewers). */\n kind?: \"subtitles\" | \"captions\";\n /** Shown from the start. */\n default?: boolean;\n};\n\nexport type VideoPlayerLabels = {\n play: string;\n pause: string;\n mute: string;\n unmute: string;\n volume: string;\n seek: string;\n /** The speed button: `(rate) => \"Speed \" + rate + \"×\"`. */\n speed: (rate: number) => string;\n /** The captions button: `(track) => \"Subtitles: \" + (track ?? \"off\")`, the track's label or `null` when off. */\n captions: (track: string | null) => string;\n fullscreen: string;\n exitFullscreen: string;\n pictureInPicture: string;\n /** Read while the video waits for data. */\n loading: string;\n /** The video cannot play. */\n error: string;\n /** Tries again (resolves a signed `src` again). */\n retry: string;\n /** The seek slider's value: `(time, duration) => \"1:05 of 3:20\"`. */\n time: (time: string, duration: string) => string;\n};\n\nexport const videoPlayerLabels: VideoPlayerLabels = {\n play: \"Play\",\n pause: \"Pause\",\n mute: \"Mute\",\n unmute: \"Unmute\",\n volume: \"Volume\",\n seek: \"Seek\",\n speed: (rate) => `Speed ${rate}×`,\n captions: (track) => `Subtitles: ${track ?? \"off\"}`,\n fullscreen: \"Full screen\",\n exitFullscreen: \"Exit full screen\",\n pictureInPicture: \"Picture in picture\",\n loading: \"Loading\",\n error: \"This video cannot be played.\",\n retry: \"Try again\",\n time: (time, duration) => `${time} of ${duration}`,\n};\n\nexport type VideoPlayerProps = {\n /** The address of the video (MP4, WebM, or an HLS `.m3u8`), or a function that resolves a signed one when needed. */\n src: string | (() => Promise<string>);\n /** `auto` (default): HLS when the address ends in `.m3u8`. */\n type?: \"auto\" | \"hls\" | \"file\";\n /** Loads hls.js when the browser cannot play HLS itself: `() => import(\"hls.js\")`. Without it, HLS needs Safari. */\n loadHls?: () => Promise<HlsModule>;\n /** The accessible name of the player (\"Video: Sunset over Montréal\"), passed translated. */\n title: string;\n /** The picture shown before playback. */\n poster?: string;\n /** Starts playing, muted (browsers refuse sound without a gesture), once on screen and again each time it comes back — not under reduced motion. */\n autoPlay?: boolean;\n /** Starts muted. */\n muted?: boolean;\n /** Plays again from the start at the end. */\n loop?: boolean;\n /** How much to fetch before playback: `none` (a list of many videos), `metadata` (default), `auto`. */\n preload?: \"none\" | \"metadata\" | \"auto\";\n /** Subtitles or captions (WebVTT): the C key and a button cycle through them. */\n tracks?: readonly VideoTrack[];\n /** The speeds offered by the speed button. Default `[0.5, 1, 1.25, 1.5, 2]`. */\n playbackRates?: readonly number[];\n /** Pauses when less than a quarter of the player is on screen. Default `true`. */\n pauseWhenHidden?: boolean;\n /** Shows the controls (default). `false`: a bare video (a background loop) — give it no sound. */\n controls?: boolean;\n /** Covers the video and hides the controls: a gate (sign in, unlock, age check) drawn by the app. */\n overlay?: React.ReactNode;\n /** The width / height ratio of the frame. Default `\"16 / 9\"`; `\"9 / 16\"` for a vertical video. */\n aspectRatio?: string;\n /** Called when playback starts. */\n onPlay?: () => void;\n /** Called when playback pauses. */\n onPause?: () => void;\n /** Called at the end. */\n onEnded?: () => void;\n /** Called as the time moves, in seconds (a few times a second). */\n onTimeUpdate?: (time: number) => void;\n /** Called when the video cannot play (a network failure, an expired address). */\n onError?: (error: unknown) => void;\n /** The words, passed translated; English by default. */\n labels?: Partial<VideoPlayerLabels>;\n /** Classes merged on the root. */\n className?: string;\n};\n\nconst RATES = [0.5, 1, 1.25, 1.5, 2] as const;\nconst isHls = (url: string, type: VideoPlayerProps[\"type\"]) => type === \"hls\" || (type !== \"file\" && /\\.m3u8(\\?|#|$)/i.test(url));\n\n/** Shows the text track `index` of a video (and hides the others); -1 hides them all. */\nfunction showTrack(element: HTMLVideoElement, index: number) {\n const list = element.textTracks;\n for (let i = 0; i < list.length; i += 1) list[i]!.mode = i === index ? \"showing\" : \"disabled\";\n}\n\n/** A video player with accessible controls, HLS on demand, signed sources, subtitles, speed, full screen. */\nexport function VideoPlayer({\n src,\n type = \"auto\",\n loadHls,\n title,\n poster,\n autoPlay,\n muted: initiallyMuted = false,\n loop,\n preload = \"metadata\",\n tracks = [],\n playbackRates = RATES,\n pauseWhenHidden = true,\n controls = true,\n overlay,\n aspectRatio = \"16 / 9\",\n onPlay,\n onPause,\n onEnded,\n onTimeUpdate,\n onError,\n labels: given,\n className,\n}: VideoPlayerProps) {\n const labels = { ...videoPlayerLabels, ...given };\n const s = videoPlayer();\n const root = React.useRef<HTMLDivElement>(null);\n const video = React.useRef<HTMLVideoElement>(null);\n const reduce = usePrefersReducedMotion();\n const [resolved, setResolved] = React.useState<{ attempt: number; url: string } | null>(null);\n const [attempt, setAttempt] = React.useState(0);\n const [wanted, setWanted] = React.useState(false);\n const [playing, setPlaying] = React.useState(false);\n const [time, setTime] = React.useState(0);\n const [duration, setDuration] = React.useState(0);\n const [volume, setVolume] = React.useState(1);\n const [muted, setMuted] = React.useState(initiallyMuted || Boolean(autoPlay));\n const [rate, setRate] = React.useState(1);\n const [caption, setCaption] = React.useState(() => tracks.findIndex((t) => t.default));\n const [fullscreen, setFullscreen] = React.useState(false);\n const [waiting, setWaiting] = React.useState(false);\n const [failed, setFailed] = React.useState(false);\n const [idle, setIdle] = React.useState(false);\n // The poster is an image over the video until the first play: it loads like any image (lazy, decoded once), the\n // same in every browser — the `poster` attribute is painted late and differently.\n const [started, setStarted] = React.useState(false);\n const idleTimer = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n const url = typeof src === \"string\" ? src : resolved?.attempt === attempt ? resolved.url : null;\n const needSource = typeof src === \"function\" && (preload !== \"none\" || wanted || (autoPlay && !reduce));\n\n // A signed source is resolved when it is needed, and again on \"Try again\".\n React.useEffect(() => {\n if (typeof src !== \"function\" || !needSource) return;\n let live = true;\n src().then(\n (next) => live && setResolved({ attempt, url: next }),\n (error: unknown) => {\n if (!live) return;\n setFailed(true);\n onError?.(error);\n },\n );\n return () => {\n live = false;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- `onError` is a callback, not a reason to resolve again\n }, [src, needSource, attempt]);\n\n // Attaches the source: natively, or through hls.js when the browser needs it.\n React.useEffect(() => {\n const element = video.current;\n if (!element || !url) return;\n let hls: InstanceType<HlsModule[\"default\"]> | null = null;\n let live = true;\n const fail = (error: unknown) => {\n if (!live) return;\n setFailed(true);\n onError?.(error);\n };\n if (isHls(url, type) && !element.canPlayType(\"application/vnd.apple.mpegurl\")) {\n if (!loadHls) fail(new Error(\"VideoPlayer: HLS needs `loadHls` in this browser.\"));\n else\n loadHls().then(({ default: Hls }) => {\n if (!live) return;\n if (!Hls.isSupported()) return fail(new Error(\"VideoPlayer: HLS is not supported here.\"));\n hls = new Hls({ enableWorker: true, backBufferLength: 60 });\n hls.on(Hls.Events.ERROR, (_event, data) => {\n if (!data.fatal) return;\n if (data.type === Hls.ErrorTypes.NETWORK_ERROR) hls?.startLoad();\n else if (data.type === Hls.ErrorTypes.MEDIA_ERROR) hls?.recoverMediaError();\n else fail(data);\n });\n hls.loadSource(url);\n hls.attachMedia(element);\n }, fail);\n } else element.src = url;\n return () => {\n live = false;\n hls?.destroy();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- `onError` and `loadHls` are callbacks\n }, [url, type]);\n\n // An autoplay waits for the player to be on screen (nothing is fetched before, with `preload=\"none\"`), and plays\n // again each time it comes back: a page of loops costs only the ones in view.\n const [inView, setInView] = React.useState(() => !pauseWhenHidden || typeof IntersectionObserver === \"undefined\");\n\n // Plays once the source is there, when it was asked for (a click before a signed source resolved).\n React.useEffect(() => {\n const element = video.current;\n if (!element || !url || !wanted) return;\n playSafely(element);\n }, [url, wanted]);\n\n // An autoplay: while in view, never under reduced motion.\n React.useEffect(() => {\n const element = video.current;\n if (!element || !url || !autoPlay || reduce || !inView) return;\n playSafely(element);\n }, [url, autoPlay, reduce, inView]);\n\n React.useEffect(() => {\n if (video.current) showTrack(video.current, caption);\n }, [caption, tracks.length]);\n\n React.useEffect(() => {\n const change = () => setFullscreen(document.fullscreenElement === root.current);\n document.addEventListener(\"fullscreenchange\", change);\n return () => document.removeEventListener(\"fullscreenchange\", change);\n }, []);\n React.useEffect(() => () => clearTimeout(idleTimer.current), []);\n\n useInView(\n root,\n (visible) => {\n setInView(visible);\n if (!visible && video.current && !video.current.paused) video.current.pause();\n },\n pauseWhenHidden,\n );\n\n const play = () => {\n setWanted(true);\n setFailed(false);\n playSafely(video.current);\n };\n const toggle = () => (playing ? video.current?.pause() : play());\n const seek = (to: number) => {\n if (!video.current) return;\n video.current.currentTime = clamp(to, 0, duration || video.current.duration || 0);\n setTime(video.current.currentTime);\n };\n const changeVolume = (to: number) => {\n if (!video.current) return;\n const next = clamp(to, 0, 1);\n video.current.volume = next;\n video.current.muted = next === 0;\n setVolume(next);\n setMuted(next === 0);\n };\n const toggleMute = () => {\n if (!video.current) return;\n if (muted && volume === 0) changeVolume(0.5);\n video.current.muted = !muted;\n setMuted(!muted);\n };\n const cycleRate = () => {\n const next = playbackRates[(playbackRates.indexOf(rate) + 1) % playbackRates.length] ?? 1;\n if (video.current) video.current.playbackRate = next;\n setRate(next);\n };\n const cycleCaption = () => tracks.length > 0 && setCaption((c) => (c + 2) % (tracks.length + 1) - 1);\n const toggleFullscreen = () => {\n const element = root.current;\n if (!element) return;\n if (document.fullscreenElement) void document.exitFullscreen();\n else if (element.requestFullscreen) void element.requestFullscreen();\n else (video.current as HTMLVideoElement & { webkitEnterFullscreen?: () => void })?.webkitEnterFullscreen?.();\n };\n const pip = () => {\n if (document.pictureInPictureElement) void document.exitPictureInPicture();\n else void video.current?.requestPictureInPicture?.();\n };\n const wake = () => {\n setIdle(false);\n clearTimeout(idleTimer.current);\n if (playing) idleTimer.current = setTimeout(() => setIdle(true), 2500);\n };\n\n const onKeyDown = (event: React.KeyboardEvent) => {\n const target = event.target as HTMLElement;\n const inControl = target.getAttribute(\"role\") === \"slider\";\n const key = event.key.length === 1 ? event.key.toLowerCase() : event.key;\n const now = video.current?.currentTime ?? 0;\n const actions: Record<string, (() => void) | undefined> = {\n \" \": target.tagName === \"BUTTON\" ? undefined : toggle,\n k: toggle,\n j: () => seek(now - 10),\n l: () => seek(now + 10),\n m: toggleMute,\n f: toggleFullscreen,\n c: tracks.length ? cycleCaption : undefined,\n ArrowLeft: inControl ? undefined : () => seek(now - 5),\n ArrowRight: inControl ? undefined : () => seek(now + 5),\n ArrowUp: inControl ? undefined : () => changeVolume(volume + 0.1),\n ArrowDown: inControl ? undefined : () => changeVolume(volume - 0.1),\n Home: inControl ? undefined : () => seek(0),\n End: inControl ? undefined : () => seek(duration),\n };\n const action = /^[0-9]$/.test(key) && !inControl ? () => seek((Number(key) / 10) * duration) : actions[key];\n if (!action || event.metaKey || event.ctrlKey || event.altKey) return;\n event.preventDefault();\n action();\n wake();\n };\n\n const showControls = controls && !overlay;\n const pipSupported = typeof document !== \"undefined\" && \"pictureInPictureEnabled\" in document && document.pictureInPictureEnabled;\n\n return (\n <div\n ref={root}\n role=\"region\"\n aria-label={title}\n tabIndex={showControls ? 0 : undefined}\n data-idle={idle && playing ? \"\" : undefined}\n data-state={playing ? \"playing\" : \"paused\"}\n className={s.root({ className })}\n style={{ aspectRatio }}\n onKeyDown={showControls ? onKeyDown : undefined}\n onPointerMove={showControls ? wake : undefined}\n onPointerLeave={() => playing && setIdle(true)}\n >\n <video\n ref={video}\n className={s.video()}\n preload={preload}\n loop={loop}\n muted={muted}\n playsInline\n crossOrigin={tracks.length ? \"anonymous\" : undefined}\n onClick={showControls ? toggle : undefined}\n onPlay={() => {\n setStarted(true);\n setPlaying(true);\n wake();\n onPlay?.();\n }}\n onPause={() => {\n setPlaying(false);\n setIdle(false);\n onPause?.();\n }}\n onEnded={() => onEnded?.()}\n onTimeUpdate={(event) => {\n setTime(event.currentTarget.currentTime);\n onTimeUpdate?.(event.currentTarget.currentTime);\n }}\n onDurationChange={(event) => setDuration(Number.isFinite(event.currentTarget.duration) ? event.currentTarget.duration : 0)}\n onWaiting={() => setWaiting(true)}\n onPlaying={() => setWaiting(false)}\n onCanPlay={() => setWaiting(false)}\n onError={(event) => {\n setFailed(true);\n onError?.(event.currentTarget.error);\n }}\n >\n {tracks.map((track) => (\n <track key={track.src} src={track.src} srcLang={track.srcLang} label={track.label} kind={track.kind ?? \"subtitles\"} default={track.default} />\n ))}\n </video>\n\n {poster && !started ? <img src={poster} alt=\"\" aria-hidden decoding=\"async\" className={s.poster()} /> : null}\n\n {showControls && !playing && !failed && !waiting && (\n <button type=\"button\" className={s.big()} aria-label={labels.play} onClick={play}>\n <PlayGlyph className=\"ml-1 h-7 w-7\" />\n </button>\n )}\n\n {waiting && !failed && (\n <div className=\"pointer-events-none absolute inset-0 z-10 flex items-center justify-center\">\n <span className={s.spinner()} aria-hidden />\n <span role=\"status\" className=\"sr-only\">\n {labels.loading}\n </span>\n </div>\n )}\n\n {failed && (\n <div role=\"alert\" className={s.status()}>\n <p>{labels.error}</p>\n <button\n type=\"button\"\n className={s.button({ className: \"border border-fg-on-media/40 px-3\" })}\n onClick={() => {\n setFailed(false);\n setAttempt((a) => a + 1);\n video.current?.load();\n }}\n >\n <RetryGlyph className=\"h-4 w-4\" />\n {labels.retry}\n </button>\n </div>\n )}\n\n {overlay ? <div className={s.overlay()}>{overlay}</div> : null}\n\n {showControls && (\n <div className={s.bar()}>\n <Slider\n label={labels.seek}\n min={0}\n max={duration || 1}\n step={0.1}\n value={Math.min(time, duration || 1)}\n onValueChange={seek}\n formatValue={(v) => labels.time(formatClock(v), formatClock(duration))}\n disabled={!duration}\n className=\"[&_[role=slider]]:h-4 [&_[role=slider]]:w-4\"\n />\n <div className={s.row()}>\n <button type=\"button\" className={s.button()} aria-label={playing ? labels.pause : labels.play} onClick={toggle}>\n {playing ? <PauseGlyph className=\"h-5 w-5\" /> : <PlayGlyph className=\"h-5 w-5\" />}\n </button>\n <button type=\"button\" className={s.button()} aria-label={muted ? labels.unmute : labels.mute} onClick={toggleMute}>\n <VolumeGlyph muted={muted} className=\"h-5 w-5\" />\n </button>\n <div className={s.volume()}>\n <Slider label={labels.volume} min={0} max={1} step={0.05} value={muted ? 0 : volume} onValueChange={changeVolume} formatValue={(v) => `${Math.round(v * 100)} %`} className=\"w-full\" />\n </div>\n <span className={s.time()} aria-hidden>\n {formatClock(time)} / {formatClock(duration)}\n </span>\n <span className=\"flex-1\" />\n {playbackRates.length > 1 && (\n <button type=\"button\" className={s.button()} aria-label={labels.speed(rate)} onClick={cycleRate}>\n {rate}×\n </button>\n )}\n {tracks.length > 0 && (\n <button type=\"button\" className={s.button()} aria-pressed={caption >= 0} aria-label={labels.captions(caption >= 0 ? (tracks[caption]?.label ?? null) : null)} onClick={cycleCaption}>\n <CaptionsGlyph className=\"h-5 w-5\" />\n </button>\n )}\n {pipSupported && (\n <button type=\"button\" className={s.button()} aria-label={labels.pictureInPicture} onClick={pip}>\n <PipGlyph className=\"h-5 w-5\" />\n </button>\n )}\n <button type=\"button\" className={s.button()} aria-label={fullscreen ? labels.exitFullscreen : labels.fullscreen} onClick={toggleFullscreen}>\n <FullscreenGlyph exit={fullscreen} className=\"h-5 w-5\" />\n </button>\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/video-player/video-player.tsx"],"sourcesContent":["// Client. A `<video>` with its own controls, accessible from end to end: a named region the keyboard reaches (Space /\n// K, arrows, J / L, M, F, C, Home / End, 0–9), every button named by a prop, the seek and volume sliders of\n// `@krizaka/ui/slider` with the time read aloud. HLS plays natively where the browser can (Safari, iOS) and through\n// hls.js elsewhere — loaded only then, by the app's `loadHls` (`() => import(\"hls.js\")`): an optional peer, never in\n// the bundle of a page that does not play HLS. A signed address can be resolved at the last moment (`src` as a\n// function), again after an error; the video pauses when it leaves the screen; autoplay waits under reduced motion.\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { CaptionsGlyph, FullscreenGlyph, PauseGlyph, PipGlyph, PlayGlyph, RetryGlyph, VolumeGlyph } from \"../glyphs\";\nimport { clamp, formatClock, playSafely, useInView, usePrefersReducedMotion } from \"../media\";\nimport { Slider } from \"../slider\";\n\nexport const videoPlayer = tv({\n slots: {\n root:\n \"theme-dark group/player relative isolate w-full overflow-hidden rounded-xl bg-media text-fg-on-media \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring data-[idle]:cursor-none\",\n video: \"absolute inset-0 h-full w-full object-contain\",\n poster: \"pointer-events-none absolute inset-0 h-full w-full object-contain\",\n big:\n \"absolute left-1/2 top-1/2 z-10 inline-flex h-16 w-16 -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full \" +\n \"bg-scrim-strong text-fg-on-media shadow-lg backdrop-blur-sm transition-colors hover:bg-scrim motion-reduce:transition-none \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\",\n bar:\n \"absolute inset-x-0 bottom-0 z-20 flex flex-col gap-1 bg-linear-to-t from-scrim-strong via-scrim to-transparent px-3 pb-2 pt-10 \" +\n \"transition-opacity duration-300 motion-reduce:transition-none group-data-[idle]/player:opacity-0 group-focus-within/player:opacity-100\",\n row: \"flex items-center gap-1\",\n button:\n \"inline-flex h-9 min-w-9 shrink-0 items-center justify-center gap-1 rounded-md px-1.5 text-xs font-semibold text-fg-on-media \" +\n \"transition-colors hover:bg-fg-on-media/15 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring aria-pressed:bg-fg-on-media/20\",\n time: \"px-1.5 font-mono text-[11px] tabular-nums text-fg-on-media\",\n volume: \"hidden w-20 items-center sm:flex\",\n status: \"absolute inset-0 z-30 flex flex-col items-center justify-center gap-3 bg-scrim-strong p-6 text-center text-sm\",\n spinner: \"h-10 w-10 animate-spin rounded-full border-2 border-fg-on-media/30 border-t-fg-on-media motion-reduce:animate-none\",\n overlay: \"absolute inset-0 z-30\",\n },\n});\n\n/** What `loadHls` resolves to: the module of hls.js (only the members the player uses are typed). */\nexport type HlsModule = {\n default: {\n new (config?: Record<string, unknown>): {\n loadSource(url: string): void;\n attachMedia(media: HTMLMediaElement): void;\n destroy(): void;\n startLoad(): void;\n recoverMediaError(): void;\n on(event: string, listener: (event: string, data: { fatal?: boolean; type?: string }) => void): void;\n };\n isSupported(): boolean;\n Events: { ERROR: string };\n ErrorTypes: { NETWORK_ERROR: string; MEDIA_ERROR: string };\n };\n};\n\nexport type VideoTrack = {\n /** The WebVTT file. */\n src: string;\n /** Its language, BCP 47 (`\"fr\"`). */\n srcLang: string;\n /** Its name in the menu (\"Français\"), passed translated. */\n label: string;\n /** `subtitles` (default) or `captions` (with the sounds, for deaf viewers). */\n kind?: \"subtitles\" | \"captions\";\n /** Shown from the start. */\n default?: boolean;\n};\n\nexport type VideoPlayerLabels = {\n play: string;\n pause: string;\n mute: string;\n unmute: string;\n volume: string;\n seek: string;\n /** The speed button: `(rate) => \"Speed \" + rate + \"×\"`. */\n speed: (rate: number) => string;\n /** The captions button: `(track) => \"Subtitles: \" + (track ?? \"off\")`, the track's label or `null` when off. */\n captions: (track: string | null) => string;\n fullscreen: string;\n exitFullscreen: string;\n pictureInPicture: string;\n /** Read while the video waits for data. */\n loading: string;\n /** The video cannot play. */\n error: string;\n /** Tries again (resolves a signed `src` again). */\n retry: string;\n /** The seek slider's value: `(time, duration) => \"1:05 of 3:20\"`. */\n time: (time: string, duration: string) => string;\n};\n\nexport const videoPlayerLabels: VideoPlayerLabels = {\n play: \"Play\",\n pause: \"Pause\",\n mute: \"Mute\",\n unmute: \"Unmute\",\n volume: \"Volume\",\n seek: \"Seek\",\n speed: (rate) => `Speed ${rate}×`,\n captions: (track) => `Subtitles: ${track ?? \"off\"}`,\n fullscreen: \"Full screen\",\n exitFullscreen: \"Exit full screen\",\n pictureInPicture: \"Picture in picture\",\n loading: \"Loading\",\n error: \"This video cannot be played.\",\n retry: \"Try again\",\n time: (time, duration) => `${time} of ${duration}`,\n};\n\nexport type VideoPlayerProps = {\n /** The address of the video (MP4, WebM, or an HLS `.m3u8`), or a function that resolves a signed one when needed. */\n src: string | (() => Promise<string>);\n /** `auto` (default): HLS when the address ends in `.m3u8`. */\n type?: \"auto\" | \"hls\" | \"file\";\n /** Loads hls.js when the browser cannot play HLS itself: `() => import(\"hls.js\")`. Without it, HLS needs Safari. */\n loadHls?: () => Promise<HlsModule>;\n /** The accessible name of the player (\"Video: Sunset over Montréal\"), passed translated. */\n title: string;\n /** The picture shown before playback. */\n poster?: string;\n /** Starts playing, muted (browsers refuse sound without a gesture), once on screen and again each time it comes back — not under reduced motion. */\n autoPlay?: boolean;\n /** Starts muted. */\n muted?: boolean;\n /** Plays again from the start at the end. */\n loop?: boolean;\n /** How much to fetch before playback: `none` (a list of many videos), `metadata` (default), `auto`. */\n preload?: \"none\" | \"metadata\" | \"auto\";\n /** Subtitles or captions (WebVTT): the C key and a button cycle through them. */\n tracks?: readonly VideoTrack[];\n /** The speeds offered by the speed button. Default `[0.5, 1, 1.25, 1.5, 2]`. */\n playbackRates?: readonly number[];\n /** Pauses when less than a quarter of the player is on screen. Default `true`. */\n pauseWhenHidden?: boolean;\n /** Shows the controls (default). `false`: a bare video (a background loop) — give it no sound. */\n controls?: boolean;\n /** Covers the video and hides the controls: a gate (sign in, unlock, age check) drawn by the app. */\n overlay?: React.ReactNode;\n /** The width / height ratio of the frame. Default `\"16 / 9\"`; `\"9 / 16\"` for a vertical video. */\n aspectRatio?: string;\n /** Called when playback starts. */\n onPlay?: () => void;\n /** Called when playback pauses. */\n onPause?: () => void;\n /** Called at the end. */\n onEnded?: () => void;\n /** Called as the time moves, in seconds (a few times a second). */\n onTimeUpdate?: (time: number) => void;\n /** Called when the video cannot play (a network failure, an expired address). */\n onError?: (error: unknown) => void;\n /** The words, passed translated; English by default. */\n labels?: Partial<VideoPlayerLabels>;\n /** Classes merged on the root. */\n className?: string;\n};\n\nconst RATES = [0.5, 1, 1.25, 1.5, 2] as const;\nconst isHls = (url: string, type: VideoPlayerProps[\"type\"]) => type === \"hls\" || (type !== \"file\" && /\\.m3u8(\\?|#|$)/i.test(url));\n\n/** Shows the text track `index` of a video (and hides the others); -1 hides them all. */\nfunction showTrack(element: HTMLVideoElement, index: number) {\n const list = element.textTracks;\n for (let i = 0; i < list.length; i += 1) list[i]!.mode = i === index ? \"showing\" : \"disabled\";\n}\n\n/** A video player with accessible controls, HLS on demand, signed sources, subtitles, speed, full screen. */\nexport function VideoPlayer({\n src,\n type = \"auto\",\n loadHls,\n title,\n poster,\n autoPlay,\n muted: initiallyMuted = false,\n loop,\n preload = \"metadata\",\n tracks = [],\n playbackRates = RATES,\n pauseWhenHidden = true,\n controls = true,\n overlay,\n aspectRatio = \"16 / 9\",\n onPlay,\n onPause,\n onEnded,\n onTimeUpdate,\n onError,\n labels: given,\n className,\n}: VideoPlayerProps) {\n const labels = { ...videoPlayerLabels, ...given };\n const s = videoPlayer();\n const root = React.useRef<HTMLDivElement>(null);\n const video = React.useRef<HTMLVideoElement>(null);\n const reduce = usePrefersReducedMotion();\n const [resolved, setResolved] = React.useState<{ attempt: number; url: string } | null>(null);\n const [attempt, setAttempt] = React.useState(0);\n const [wanted, setWanted] = React.useState(false);\n const [playing, setPlaying] = React.useState(false);\n const [time, setTime] = React.useState(0);\n const [duration, setDuration] = React.useState(0);\n const [volume, setVolume] = React.useState(1);\n const [muted, setMuted] = React.useState(initiallyMuted || Boolean(autoPlay));\n const [rate, setRate] = React.useState(1);\n const [caption, setCaption] = React.useState(() => tracks.findIndex((t) => t.default));\n const [fullscreen, setFullscreen] = React.useState(false);\n const [waiting, setWaiting] = React.useState(false);\n const [failed, setFailed] = React.useState(false);\n const [idle, setIdle] = React.useState(false);\n // The poster is an image over the video until the first play: it loads like any image (lazy, decoded once), the\n // same in every browser — the `poster` attribute is painted late and differently.\n const [started, setStarted] = React.useState(false);\n const idleTimer = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n const url = typeof src === \"string\" ? src : resolved?.attempt === attempt ? resolved.url : null;\n const needSource = typeof src === \"function\" && (preload !== \"none\" || wanted || (autoPlay && !reduce));\n\n // A signed source is resolved when it is needed, and again on \"Try again\".\n React.useEffect(() => {\n if (typeof src !== \"function\" || !needSource) return;\n let live = true;\n src().then(\n (next) => live && setResolved({ attempt, url: next }),\n (error: unknown) => {\n if (!live) return;\n setFailed(true);\n onError?.(error);\n },\n );\n return () => {\n live = false;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- `onError` is a callback, not a reason to resolve again\n }, [src, needSource, attempt]);\n\n // Attaches the source: natively, or through hls.js when the browser needs it.\n React.useEffect(() => {\n const element = video.current;\n if (!element || !url) return;\n let hls: InstanceType<HlsModule[\"default\"]> | null = null;\n let live = true;\n const fail = (error: unknown) => {\n if (!live) return;\n setFailed(true);\n onError?.(error);\n };\n if (isHls(url, type) && !element.canPlayType(\"application/vnd.apple.mpegurl\")) {\n if (!loadHls) fail(new Error(\"VideoPlayer: HLS needs `loadHls` in this browser.\"));\n else\n loadHls().then(({ default: Hls }) => {\n if (!live) return;\n if (!Hls.isSupported()) return fail(new Error(\"VideoPlayer: HLS is not supported here.\"));\n hls = new Hls({ enableWorker: true, backBufferLength: 60 });\n hls.on(Hls.Events.ERROR, (_event, data) => {\n if (!data.fatal) return;\n if (data.type === Hls.ErrorTypes.NETWORK_ERROR) hls?.startLoad();\n else if (data.type === Hls.ErrorTypes.MEDIA_ERROR) hls?.recoverMediaError();\n else fail(data);\n });\n hls.loadSource(url);\n hls.attachMedia(element);\n }, fail);\n } else element.src = url;\n return () => {\n live = false;\n hls?.destroy();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- `onError` and `loadHls` are callbacks\n }, [url, type]);\n\n // An autoplay waits for the player to be on screen (nothing is fetched before, with `preload=\"none\"`), and plays\n // again each time it comes back: a page of loops costs only the ones in view.\n const [inView, setInView] = React.useState(() => !pauseWhenHidden || typeof IntersectionObserver === \"undefined\");\n\n // Plays once the source is there, when it was asked for (a click before a signed source resolved).\n React.useEffect(() => {\n const element = video.current;\n if (!element || !url || !wanted) return;\n playSafely(element);\n }, [url, wanted]);\n\n // An autoplay: while in view, never under reduced motion.\n React.useEffect(() => {\n const element = video.current;\n if (!element || !url || !autoPlay || reduce || !inView) return;\n playSafely(element);\n }, [url, autoPlay, reduce, inView]);\n\n React.useEffect(() => {\n if (video.current) showTrack(video.current, caption);\n }, [caption, tracks.length]);\n\n React.useEffect(() => {\n const change = () => setFullscreen(document.fullscreenElement === root.current);\n document.addEventListener(\"fullscreenchange\", change);\n return () => document.removeEventListener(\"fullscreenchange\", change);\n }, []);\n React.useEffect(() => () => clearTimeout(idleTimer.current), []);\n\n useInView(\n root,\n (visible) => {\n setInView(visible);\n if (!visible && video.current && !video.current.paused) video.current.pause();\n },\n pauseWhenHidden,\n );\n\n const play = () => {\n setWanted(true);\n setFailed(false);\n playSafely(video.current);\n };\n const toggle = () => (playing ? video.current?.pause() : play());\n const seek = (to: number) => {\n if (!video.current) return;\n video.current.currentTime = clamp(to, 0, duration || video.current.duration || 0);\n setTime(video.current.currentTime);\n };\n const changeVolume = (to: number) => {\n if (!video.current) return;\n const next = clamp(to, 0, 1);\n video.current.volume = next;\n video.current.muted = next === 0;\n setVolume(next);\n setMuted(next === 0);\n };\n const toggleMute = () => {\n if (!video.current) return;\n if (muted && volume === 0) changeVolume(0.5);\n video.current.muted = !muted;\n setMuted(!muted);\n };\n const cycleRate = () => {\n const next = playbackRates[(playbackRates.indexOf(rate) + 1) % playbackRates.length] ?? 1;\n if (video.current) video.current.playbackRate = next;\n setRate(next);\n };\n const cycleCaption = () => tracks.length > 0 && setCaption((c) => (c + 2) % (tracks.length + 1) - 1);\n const toggleFullscreen = () => {\n const element = root.current;\n if (!element) return;\n if (document.fullscreenElement) void document.exitFullscreen();\n else if (element.requestFullscreen) void element.requestFullscreen();\n else (video.current as HTMLVideoElement & { webkitEnterFullscreen?: () => void })?.webkitEnterFullscreen?.();\n };\n const pip = () => {\n if (document.pictureInPictureElement) void document.exitPictureInPicture();\n else void video.current?.requestPictureInPicture?.();\n };\n const wake = () => {\n setIdle(false);\n clearTimeout(idleTimer.current);\n if (playing) idleTimer.current = setTimeout(() => setIdle(true), 2500);\n };\n\n const onKeyDown = (event: React.KeyboardEvent) => {\n const target = event.target as HTMLElement;\n const inControl = target.getAttribute(\"role\") === \"slider\";\n const key = event.key.length === 1 ? event.key.toLowerCase() : event.key;\n const now = video.current?.currentTime ?? 0;\n const actions: Record<string, (() => void) | undefined> = {\n \" \": target.tagName === \"BUTTON\" ? undefined : toggle,\n k: toggle,\n j: () => seek(now - 10),\n l: () => seek(now + 10),\n m: toggleMute,\n f: toggleFullscreen,\n c: tracks.length ? cycleCaption : undefined,\n ArrowLeft: inControl ? undefined : () => seek(now - 5),\n ArrowRight: inControl ? undefined : () => seek(now + 5),\n ArrowUp: inControl ? undefined : () => changeVolume(volume + 0.1),\n ArrowDown: inControl ? undefined : () => changeVolume(volume - 0.1),\n Home: inControl ? undefined : () => seek(0),\n End: inControl ? undefined : () => seek(duration),\n };\n const action = /^[0-9]$/.test(key) && !inControl ? () => seek((Number(key) / 10) * duration) : actions[key];\n if (!action || event.metaKey || event.ctrlKey || event.altKey) return;\n event.preventDefault();\n action();\n wake();\n };\n\n const showControls = controls && !overlay;\n const pipSupported = typeof document !== \"undefined\" && \"pictureInPictureEnabled\" in document && document.pictureInPictureEnabled;\n\n return (\n <div\n ref={root}\n role=\"region\"\n aria-label={title}\n tabIndex={showControls ? 0 : undefined}\n data-idle={idle && playing ? \"\" : undefined}\n data-state={playing ? \"playing\" : \"paused\"}\n className={s.root({ className })}\n style={{ aspectRatio }}\n onKeyDown={showControls ? onKeyDown : undefined}\n onPointerMove={showControls ? wake : undefined}\n onPointerLeave={() => playing && setIdle(true)}\n >\n <video\n ref={video}\n className={s.video()}\n preload={preload}\n loop={loop}\n muted={muted}\n playsInline\n crossOrigin={tracks.length ? \"anonymous\" : undefined}\n onClick={showControls ? toggle : undefined}\n onPlay={() => {\n setStarted(true);\n setPlaying(true);\n wake();\n onPlay?.();\n }}\n onPause={() => {\n setPlaying(false);\n setIdle(false);\n onPause?.();\n }}\n onEnded={() => onEnded?.()}\n onTimeUpdate={(event) => {\n setTime(event.currentTarget.currentTime);\n onTimeUpdate?.(event.currentTarget.currentTime);\n }}\n onDurationChange={(event) => setDuration(Number.isFinite(event.currentTarget.duration) ? event.currentTarget.duration : 0)}\n onWaiting={() => setWaiting(true)}\n onPlaying={() => setWaiting(false)}\n onCanPlay={() => setWaiting(false)}\n onError={(event) => {\n setFailed(true);\n onError?.(event.currentTarget.error);\n }}\n >\n {tracks.map((track) => (\n <track key={track.src} src={track.src} srcLang={track.srcLang} label={track.label} kind={track.kind ?? \"subtitles\"} default={track.default} />\n ))}\n </video>\n\n {poster && !started ? <img src={poster} alt=\"\" aria-hidden decoding=\"async\" className={s.poster()} /> : null}\n\n {showControls && !playing && !failed && !waiting && (\n <button type=\"button\" className={s.big()} aria-label={labels.play} onClick={play}>\n <PlayGlyph className=\"ml-1 h-7 w-7\" />\n </button>\n )}\n\n {waiting && !failed && (\n <div className=\"pointer-events-none absolute inset-0 z-10 flex items-center justify-center\">\n <span className={s.spinner()} aria-hidden />\n <span role=\"status\" className=\"sr-only\">\n {labels.loading}\n </span>\n </div>\n )}\n\n {failed && (\n <div role=\"alert\" className={s.status()}>\n <p>{labels.error}</p>\n <button\n type=\"button\"\n className={s.button({ className: \"border border-fg-on-media/40 px-3\" })}\n onClick={() => {\n setFailed(false);\n setAttempt((a) => a + 1);\n video.current?.load();\n }}\n >\n <RetryGlyph className=\"h-4 w-4\" />\n {labels.retry}\n </button>\n </div>\n )}\n\n {overlay ? <div className={s.overlay()}>{overlay}</div> : null}\n\n {showControls && (\n <div className={s.bar()}>\n <Slider\n label={labels.seek}\n min={0}\n max={duration || 1}\n step={0.1}\n value={Math.min(time, duration || 1)}\n onValueChange={seek}\n formatValue={(v) => labels.time(formatClock(v), formatClock(duration))}\n disabled={!duration}\n className=\"[&_[role=slider]]:h-4 [&_[role=slider]]:w-4\"\n />\n <div className={s.row()}>\n <button type=\"button\" className={s.button()} aria-label={playing ? labels.pause : labels.play} onClick={toggle}>\n {playing ? <PauseGlyph className=\"h-5 w-5\" /> : <PlayGlyph className=\"h-5 w-5\" />}\n </button>\n <button type=\"button\" className={s.button()} aria-label={muted ? labels.unmute : labels.mute} onClick={toggleMute}>\n <VolumeGlyph muted={muted} className=\"h-5 w-5\" />\n </button>\n <div className={s.volume()}>\n <Slider label={labels.volume} min={0} max={1} step={0.05} value={muted ? 0 : volume} onValueChange={changeVolume} formatValue={(v) => `${Math.round(v * 100)} %`} className=\"w-full\" />\n </div>\n <span className={s.time()} aria-hidden>\n {formatClock(time)} / {formatClock(duration)}\n </span>\n <span className=\"flex-1\" />\n {playbackRates.length > 1 && (\n <button type=\"button\" className={s.button()} aria-label={labels.speed(rate)} onClick={cycleRate}>\n {rate}×\n </button>\n )}\n {tracks.length > 0 && (\n <button type=\"button\" className={s.button()} aria-pressed={caption >= 0} aria-label={labels.captions(caption >= 0 ? (tracks[caption]?.label ?? null) : null)} onClick={cycleCaption}>\n <CaptionsGlyph className=\"h-5 w-5\" />\n </button>\n )}\n {pipSupported && (\n <button type=\"button\" className={s.button()} aria-label={labels.pictureInPicture} onClick={pip}>\n <PipGlyph className=\"h-5 w-5\" />\n </button>\n )}\n <button type=\"button\" className={s.button()} aria-label={fullscreen ? labels.exitFullscreen : labels.fullscreen} onClick={toggleFullscreen}>\n <FullscreenGlyph exit={fullscreen} className=\"h-5 w-5\" />\n </button>\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@krizaka/ui",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.32.0",
|
|
4
4
|
"description": "The Krizaka front-end platform's components: the primitives (button, card, dialog, toast, menu, field, tabs, chip, switch, slider, command palette, theme, countdown…) on the semantic --kz-* tokens, the animated marks (Krizaka, Orazaka, Orochia, Yasakia, Yororis) and the motion signature — web and /native.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"krizaka",
|
|
@@ -721,7 +721,7 @@
|
|
|
721
721
|
],
|
|
722
722
|
"peerDependencies": {
|
|
723
723
|
"@krizaka/query": ">=0.2.1",
|
|
724
|
-
"@krizaka/tailwind": ">=3.
|
|
724
|
+
"@krizaka/tailwind": ">=3.32.0",
|
|
725
725
|
"@maplibre/maplibre-react-native": ">=11",
|
|
726
726
|
"@shopify/flash-list": ">=2",
|
|
727
727
|
"expo-camera": ">=16",
|
|
@@ -781,8 +781,8 @@
|
|
|
781
781
|
"typescript": "^5.9.0",
|
|
782
782
|
"vitest": "^4.1.11",
|
|
783
783
|
"zod": "^4.6.5",
|
|
784
|
-
"@krizaka/
|
|
785
|
-
"@krizaka/
|
|
784
|
+
"@krizaka/config": "0.4.1",
|
|
785
|
+
"@krizaka/query": "0.2.1"
|
|
786
786
|
},
|
|
787
787
|
"engines": {
|
|
788
788
|
"node": ">=22"
|
|
@@ -2289,7 +2289,7 @@
|
|
|
2289
2289
|
{
|
|
2290
2290
|
"name": "./map",
|
|
2291
2291
|
"path": "dist/map.js",
|
|
2292
|
-
"limit": "
|
|
2292
|
+
"limit": "18 kB",
|
|
2293
2293
|
"gzip": true,
|
|
2294
2294
|
"ignore": [
|
|
2295
2295
|
"tailwind-variants",
|
|
@@ -2464,9 +2464,9 @@
|
|
|
2464
2464
|
"radix-ui": "^1.7.0",
|
|
2465
2465
|
"sonner": "^2.0.8",
|
|
2466
2466
|
"tailwind-variants": "^3.3.1",
|
|
2467
|
-
"@krizaka/
|
|
2468
|
-
"@krizaka/
|
|
2469
|
-
"@krizaka/
|
|
2467
|
+
"@krizaka/intl": "^0.2.1",
|
|
2468
|
+
"@krizaka/tokens": "^3.32.0",
|
|
2469
|
+
"@krizaka/pacer": "^0.1.0"
|
|
2470
2470
|
},
|
|
2471
2471
|
"scripts": {
|
|
2472
2472
|
"build": "node -e \"const fs=require('node:fs');fs.rmSync('dist',{recursive:true,force:true});for(const f of fs.readdirSync('registry/examples',{recursive:true}))if(/\\.(js|d\\.ts)$/.test(f))fs.rmSync('registry/examples/'+f)\" && tsup && node -e \"require('node:fs').copyFileSync('src/motion.css','dist/motion.css');require('node:fs').copyFileSync('src/blocks.css','dist/blocks.css');require('node:fs').copyFileSync('src/story.css','dist/story.css')\" && node scripts/build-registry.mjs",
|
package/registry/action-bar.json
CHANGED
|
@@ -135,7 +135,7 @@
|
|
|
135
135
|
"files": [
|
|
136
136
|
{
|
|
137
137
|
"path": "action-bar/action-bar.tsx",
|
|
138
|
-
"content": "// Server-safe. A row of actions that leave or move on (blocks.css): discuss · propose · report, read the docs, see the\n// code. `buttons`: buttons with their words — the end of a page or a section. `icons`: icon buttons whose words are the\n// accessible name and a tooltip on hover and focus (CSS only: no script, no portal) — the foot of a card.\nimport type * as React from \"react\";\n\nimport { BlockLink, isExternal, type LinkComponent } from \"../block-link\";\nimport { buttonVariants } from \"../button/button\";\nimport { cn } from \"../cn\";\nimport { ExternalGlyph } from \"../glyphs\";\n\nexport interface Action {\n /** Where it leads. An absolute URL opens in a new tab, with the external mark. */\n href: string;\n /** Its words (the accessible name, and the tooltip of an icon action). */\n label: string;\n /** Its icon (a signature icon): decorative. Required by `variant=\"icons\"`. */\n icon?: React.ReactNode;\n}\n\nexport type ActionBarProps = React.HTMLAttributes<HTMLUListElement> & {\n /** The actions, in order of importance. */\n actions: readonly Action[];\n /**\n * `buttons` (with their words) or `icons` (icon buttons, the words in a tooltip).\n * @default \"buttons\"\n */\n variant?: \"buttons\" | \"icons\";\n /**\n * The first action as the primary button (the one action a block is about); the others stay secondary.\n * @default false\n */\n primaryFirst?: boolean;\n /**\n * The buttons' size: `sm` in a card, `md` in a section, `lg` on a hero or a closing call.\n * @default \"md\"\n */\n size?: \"sm\" | \"md\" | \"lg\";\n /** What the actions are about (`Talk about Krizaka Java`): the list's accessible name. */\n label?: string;\n /** The app's link component for internal addresses (Next's `Link`); an `<a>` otherwise. */\n linkAs?: LinkComponent;\n};\n\n/** A row of link actions: buttons with their words, or icon buttons with a tooltip. */\nexport function ActionBar({ actions, variant = \"buttons\", primaryFirst = false, size = \"md\", label, linkAs, className, ...props }: ActionBarProps) {\n const icons = variant === \"icons\";\n return (\n <ul className={cn(\"kz-actions\", className)} data-variant={icons ? \"icons\" : undefined} aria-label={label} {...props}>\n {actions.map((a, i) => {\n const external = isExternal(a.href);\n return (\n <li key={a.href}>\n {icons ? (\n <BlockLink\n href={a.href}\n linkAs={linkAs}\n external={external}\n aria-label={a.label}\n data-tip={a.label}\n className={buttonVariants({ variant: \"ghost\", size: \"icon\",
|
|
138
|
+
"content": "// Server-safe. A row of actions that leave or move on (blocks.css): discuss · propose · report, read the docs, see the\n// code. `buttons`: buttons with their words — the end of a page or a section. `icons`: icon buttons whose words are the\n// accessible name and a tooltip on hover and focus (CSS only: no script, no portal) — the foot of a card.\nimport type * as React from \"react\";\n\nimport { BlockLink, isExternal, type LinkComponent } from \"../block-link\";\nimport { buttonVariants } from \"../button/button\";\nimport { cn } from \"../cn\";\nimport { ExternalGlyph } from \"../glyphs\";\n\nexport interface Action {\n /** Where it leads. An absolute URL opens in a new tab, with the external mark. */\n href: string;\n /** Its words (the accessible name, and the tooltip of an icon action). */\n label: string;\n /** Its icon (a signature icon): decorative. Required by `variant=\"icons\"`. */\n icon?: React.ReactNode;\n}\n\nexport type ActionBarProps = React.HTMLAttributes<HTMLUListElement> & {\n /** The actions, in order of importance. */\n actions: readonly Action[];\n /**\n * `buttons` (with their words) or `icons` (icon buttons, the words in a tooltip).\n * @default \"buttons\"\n */\n variant?: \"buttons\" | \"icons\";\n /**\n * The first action as the primary button (the one action a block is about); the others stay secondary.\n * @default false\n */\n primaryFirst?: boolean;\n /**\n * The buttons' size: `sm` in a card, `md` in a section, `lg` on a hero or a closing call.\n * @default \"md\"\n */\n size?: \"sm\" | \"md\" | \"lg\";\n /** What the actions are about (`Talk about Krizaka Java`): the list's accessible name. */\n label?: string;\n /** The app's link component for internal addresses (Next's `Link`); an `<a>` otherwise. */\n linkAs?: LinkComponent;\n};\n\n/** A row of link actions: buttons with their words, or icon buttons with a tooltip. */\nexport function ActionBar({ actions, variant = \"buttons\", primaryFirst = false, size = \"md\", label, linkAs, className, ...props }: ActionBarProps) {\n const icons = variant === \"icons\";\n return (\n <ul className={cn(\"kz-actions\", className)} data-variant={icons ? \"icons\" : undefined} aria-label={label} {...props}>\n {actions.map((a, i) => {\n const external = isExternal(a.href);\n return (\n <li key={a.href}>\n {icons ? (\n <BlockLink\n href={a.href}\n linkAs={linkAs}\n external={external}\n aria-label={a.label}\n data-tip={a.label}\n className={buttonVariants({ variant: \"ghost\", size: \"icon\", className: size === \"sm\" ? \"h-9 w-9\" : undefined })}\n >\n {a.icon}\n </BlockLink>\n ) : (\n <BlockLink\n href={a.href}\n linkAs={linkAs}\n external={external}\n className={buttonVariants({ variant: primaryFirst && i === 0 ? \"primary\" : \"secondary\", size, className: \"gap-2\" })}\n >\n {a.icon}\n {a.label}\n {external && <ExternalGlyph className=\"h-3 w-3 opacity-70\" />}\n </BlockLink>\n )}\n </li>\n );\n })}\n </ul>\n );\n}\n"
|
|
139
139
|
},
|
|
140
140
|
{
|
|
141
141
|
"path": "action-bar/index.ts",
|
package/registry/app-shell.json
CHANGED
|
@@ -333,7 +333,7 @@
|
|
|
333
333
|
"why": "A creator moves between the sections of an app and reads this week's figures at home.",
|
|
334
334
|
"path": "examples/app-shell/orochia-studio.tsx",
|
|
335
335
|
"module": "@krizaka/ui/registry/examples/app-shell/orochia-studio",
|
|
336
|
-
"code": "import { ProductLogo } from \"@krizaka/ui\";\nimport { AppShell } from \"@krizaka/ui/app-shell\";\nimport { Avatar } from \"@krizaka/ui/avatar\";\nimport { Badge } from \"@krizaka/ui/badge\";\nimport { Button } from \"@krizaka/ui/button\";\nimport { DashboardLayout, Widget } from \"@krizaka/ui/dashboard-layout\";\nimport { Meter } from \"@krizaka/ui/meter\";\nimport { Stat } from \"@krizaka/ui/stat\";\n\nconst icon = (d: string) => (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d={d} />\n </svg>\n);\nconst NAV = [\n { id: \"home\", label: \"Home\", d: \"M4 11 12 4l8 7v9H4z\", current: true },\n { id: \"videos\", label: \"Videos\", d: \"M4 6h12v12H4zM16 10l4-2v8l-4-2\", badge: 2 },\n { id: \"auctions\", label: \"Auctions\", d: \"M5 19h10M8 5l6 6M11 2l6 6M6 13l4-4\" },\n { id: \"challenges\", label: \"Challenges\", d: \"M12 3v18M5 7h10l-2 3 2 3H5\" },\n];\nconst MONEY = [\n { id: \"earnings\", label: \"Earnings\", d: \"M4 18h16M7 14l3-3 3 2 5-6\" },\n { id: \"payouts\", label: \"Payouts\", d: \"M3 7h18v10H3zM12 12h.01\" },\n];\nconst SALES = [\n { what: \"Unlock · “Night shoot, part 2”\", who: \"Léa M.\", net: \"€4.50\" },\n { what: \"Tip\", who: \"Samir K.\", net: \"€9.00\" },\n { what: \"Auction won · “Backstage cut”\", who: \"Inès B.\", net: \"€61.20\" },\n];\n\n// TEMPLATE — Orochia's creator studio, Elena's home: the shell (sidebar of the studio, drawer on a phone) around a\n// dashboard — this week's figures, the payout on its way, the latest sales, the next auction. On a phone the sidebar\n// is a drawer, the stats stack two by two, every widget takes the full width.\nexport default function AppShellOrochiaStudio() {\n return (\n <div className=\"brand-orochia\">\n <AppShell.Root>\n <AppShell.Sidebar\n label=\"Studio\"\n brand={<><ProductLogo id=\"orochia\" size={24} animated={false} /> Orochia</>}\n collapseLabels={[\"Collapse\", \"Expand\"]}\n closeLabel=\"Close the menu\"\n footer={\n <div className=\"flex items-center gap-2.5 px-2 py-2 text-sm\">\n <Avatar fallback=\"EV\" size=\"sm\" />\n <span className=\"min-w-0 truncate font-medium\">Elena Varga</span>\n </div>\n }\n >\n {NAV.map((n) => (\n <AppShell.Item key={n.id} href={`#${n.id}`} icon={icon(n.d)} label={n.label} current={n.current} badge={n.badge} />\n ))}\n <AppShell.Group>Money</AppShell.Group>\n {MONEY.map((n) => (\n <AppShell.Item key={n.id} href={`#${n.id}`} icon={icon(n.d)} label={n.label} />\n ))}\n </AppShell.Sidebar>\n <AppShell.Body>\n <AppShell.Header\n title=\"Studio\"\n menuLabel=\"Open the menu\"\n actions={<Button size=\"sm\" variant=\"primary\"
|
|
336
|
+
"code": "import { ProductLogo } from \"@krizaka/ui\";\nimport { AppShell } from \"@krizaka/ui/app-shell\";\nimport { Avatar } from \"@krizaka/ui/avatar\";\nimport { Badge } from \"@krizaka/ui/badge\";\nimport { Button } from \"@krizaka/ui/button\";\nimport { DashboardLayout, Widget } from \"@krizaka/ui/dashboard-layout\";\nimport { Meter } from \"@krizaka/ui/meter\";\nimport { Stat } from \"@krizaka/ui/stat\";\n\nconst icon = (d: string) => (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d={d} />\n </svg>\n);\nconst NAV = [\n { id: \"home\", label: \"Home\", d: \"M4 11 12 4l8 7v9H4z\", current: true },\n { id: \"videos\", label: \"Videos\", d: \"M4 6h12v12H4zM16 10l4-2v8l-4-2\", badge: 2 },\n { id: \"auctions\", label: \"Auctions\", d: \"M5 19h10M8 5l6 6M11 2l6 6M6 13l4-4\" },\n { id: \"challenges\", label: \"Challenges\", d: \"M12 3v18M5 7h10l-2 3 2 3H5\" },\n];\nconst MONEY = [\n { id: \"earnings\", label: \"Earnings\", d: \"M4 18h16M7 14l3-3 3 2 5-6\" },\n { id: \"payouts\", label: \"Payouts\", d: \"M3 7h18v10H3zM12 12h.01\" },\n];\nconst SALES = [\n { what: \"Unlock · “Night shoot, part 2”\", who: \"Léa M.\", net: \"€4.50\" },\n { what: \"Tip\", who: \"Samir K.\", net: \"€9.00\" },\n { what: \"Auction won · “Backstage cut”\", who: \"Inès B.\", net: \"€61.20\" },\n];\n\n// TEMPLATE — Orochia's creator studio, Elena's home: the shell (sidebar of the studio, drawer on a phone) around a\n// dashboard — this week's figures, the payout on its way, the latest sales, the next auction. On a phone the sidebar\n// is a drawer, the stats stack two by two, every widget takes the full width.\nexport default function AppShellOrochiaStudio() {\n return (\n <div className=\"brand-orochia\">\n <AppShell.Root>\n <AppShell.Sidebar\n label=\"Studio\"\n brand={<><ProductLogo id=\"orochia\" size={24} animated={false} /> Orochia</>}\n collapseLabels={[\"Collapse\", \"Expand\"]}\n closeLabel=\"Close the menu\"\n footer={\n <div className=\"flex items-center gap-2.5 px-2 py-2 text-sm\">\n <Avatar fallback=\"EV\" size=\"sm\" />\n <span className=\"min-w-0 truncate font-medium\">Elena Varga</span>\n </div>\n }\n >\n {NAV.map((n) => (\n <AppShell.Item key={n.id} href={`#${n.id}`} icon={icon(n.d)} label={n.label} current={n.current} badge={n.badge} />\n ))}\n <AppShell.Group>Money</AppShell.Group>\n {MONEY.map((n) => (\n <AppShell.Item key={n.id} href={`#${n.id}`} icon={icon(n.d)} label={n.label} />\n ))}\n </AppShell.Sidebar>\n <AppShell.Body>\n <AppShell.Header\n title=\"Studio\"\n menuLabel=\"Open the menu\"\n actions={<Button size=\"sm\" variant=\"primary\">Upload a video</Button>}\n />\n <AppShell.Main>\n <DashboardLayout headingAs=\"h2\" eyebrow=\"This week\" title=\"Good morning, Elena\" description=\"Three sales since yesterday — your auction ends tonight.\">\n <Widget level={3} title=\"Your week\" span=\"full\">\n <div className=\"grid grid-cols-2 gap-6 md:grid-cols-4\">\n <Stat label=\"Views\" value=\"12.4k\" trend=\"up\" trendLabel=\"+8 %\" hint=\"7 days\" />\n <Stat label=\"Supporters\" value=\"318\" trend=\"up\" trendLabel=\"+21\" hint=\"7 days\" />\n <Stat label=\"Earned\" value=\"€902\" trend=\"up\" trendLabel=\"+14 %\" hint=\"after the 10 % fee\" />\n <Stat label=\"Unlocks\" value=\"146\" trend=\"flat\" trendLabel=\"±0\" hint=\"7 days\" />\n </div>\n </Widget>\n <Widget level={3} title=\"Next payout\" description=\"Every Monday, from €20\" span={2}>\n <Meter label=\"Towards your payout\" value={74} max={100} valueText=\"€74 of €100 goal\" showLabel />\n <p className=\"mt-3 text-sm text-fg-secondary\">€902 will reach your bank on Monday.</p>\n </Widget>\n <Widget level={3} title=\"Auction\" description=\"“Backstage cut”\" span={2} action={<Badge tone=\"accent\" dot>Open</Badge>}>\n <p className=\"font-display text-3xl font-extrabold text-fg\">€68</p>\n <p className=\"text-sm text-fg-secondary\">7 bids · ends tonight at 21:00</p>\n </Widget>\n <Widget level={3} title=\"Latest sales\" span=\"full\" action={<Button size=\"sm\" variant=\"ghost\">See all</Button>}>\n <ul className=\"divide-y divide-border-subtle\">\n {SALES.map((s) => (\n <li key={s.what} className=\"flex items-center justify-between gap-3 py-2.5 text-sm\">\n <span className=\"min-w-0\">\n <span className=\"block truncate font-medium text-fg\">{s.what}</span>\n <span className=\"text-fg-secondary\">{s.who}</span>\n </span>\n <span className=\"font-mono font-semibold text-fg-success\">{s.net}</span>\n </li>\n ))}\n </ul>\n </Widget>\n </DashboardLayout>\n </AppShell.Main>\n </AppShell.Body>\n </AppShell.Root>\n </div>\n );\n}\n"
|
|
337
337
|
},
|
|
338
338
|
{
|
|
339
339
|
"name": "admin-console",
|
|
@@ -344,7 +344,7 @@
|
|
|
344
344
|
"why": "An administrator works a queue: scans it, sorts it and acts on many rows at once.",
|
|
345
345
|
"path": "examples/app-shell/admin-console.tsx",
|
|
346
346
|
"module": "@krizaka/ui/registry/examples/app-shell/admin-console",
|
|
347
|
-
"code": "\"use client\";\n\nimport { ProductLogo } from \"@krizaka/ui\";\nimport { AppShell } from \"@krizaka/ui/app-shell\";\nimport { Badge } from \"@krizaka/ui/badge\";\nimport { Button } from \"@krizaka/ui/button\";\nimport { DashboardLayout, Widget } from \"@krizaka/ui/dashboard-layout\";\nimport { DataTable, type DataTableColumn } from \"@krizaka/ui/data-table\";\n\nconst icon = (d: string) => (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d={d} />\n </svg>\n);\n\ntype Request = { id: string; creator: string; kind: string; status: \"pending\" | \"approved\" | \"refused\"; age: string };\nconst REQUESTS: Request[] = [\n { id: \"v-1042\", creator: \"Elena Varga\", kind: \"Identity check\", status: \"pending\", age: \"12 min\" },\n { id: \"v-1041\", creator: \"Noah Petit\", kind: \"Payout account\", status: \"pending\", age: \"40 min\" },\n { id: \"v-1039\", creator: \"Maya Chen\", kind: \"Identity check\", status: \"approved\", age: \"2 h\" },\n { id: \"v-1036\", creator: \"Tom Lebrun\", kind: \"Age check (18+)\", status: \"refused\", age: \"5 h\" },\n];\nconst tone = { pending: \"warning\", approved: \"success\", refused: \"danger\" } as const;\nconst columns: DataTableColumn<Request>[] = [\n { id: \"creator\", header: \"Creator\", sortable: true, card: \"title\" },\n { id: \"kind\", header: \"Request\" },\n { id: \"status\", header: \"Status\", cell: (r) => <Badge tone={tone[r.status]} dot>{r.status}</Badge> },\n { id: \"age\", header: \"Waiting\", align: \"end\" },\n];\n\n// TEMPLATE — an administration console (Orochia admin): the shell with the console's sections, a queue of requests to\n// review as a sortable table with bulk actions — cards on a phone — and the counts that say how long the queue is.\nexport default function AppShellAdminConsole() {\n return (\n <AppShell.Root>\n <AppShell.Sidebar label=\"Administration\" brand={<><ProductLogo id=\"orochia\" size={24} animated={false} /> Admin</>} closeLabel=\"Close the menu\" collapseLabels={[\"Collapse\", \"Expand\"]}>\n <AppShell.Group>Review</AppShell.Group>\n <AppShell.Item href=\"#verification\" icon={icon(\"M12 3l8 4v5c0 5-3.5 8-8 9-4.5-1-8-4-8-9V7z\")} label=\"Verification\" current badge={2} />\n <AppShell.Item href=\"#moderation\" icon={icon(\"M4 5h16v11H9l-5 4z\")} label=\"Moderation\" badge={5} />\n <AppShell.Group>Money</AppShell.Group>\n <AppShell.Item href=\"#payouts\" icon={icon(\"M3 7h18v10H3zM12 12h.01\")} label=\"Payouts\" />\n <AppShell.Item href=\"#ledger\" icon={icon(\"M5 4h14v16H5zM8 8h8M8 12h8M8 16h5\")} label=\"Ledger\" />\n <AppShell.Group>System</AppShell.Group>\n <AppShell.Item href=\"#backups\" icon={icon(\"M4 7c0-2 16-2 16 0v10c0 2-16 2-16 0zM4 12c0 2 16 2 16 0\")} label=\"Backups\" />\n </AppShell.Sidebar>\n <AppShell.Body>\n <AppShell.Header title=\"Verification\" menuLabel=\"Open the menu\" actions={<Button size=\"sm\" variant=\"secondary\"
|
|
347
|
+
"code": "\"use client\";\n\nimport { ProductLogo } from \"@krizaka/ui\";\nimport { AppShell } from \"@krizaka/ui/app-shell\";\nimport { Badge } from \"@krizaka/ui/badge\";\nimport { Button } from \"@krizaka/ui/button\";\nimport { DashboardLayout, Widget } from \"@krizaka/ui/dashboard-layout\";\nimport { DataTable, type DataTableColumn } from \"@krizaka/ui/data-table\";\n\nconst icon = (d: string) => (\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d={d} />\n </svg>\n);\n\ntype Request = { id: string; creator: string; kind: string; status: \"pending\" | \"approved\" | \"refused\"; age: string };\nconst REQUESTS: Request[] = [\n { id: \"v-1042\", creator: \"Elena Varga\", kind: \"Identity check\", status: \"pending\", age: \"12 min\" },\n { id: \"v-1041\", creator: \"Noah Petit\", kind: \"Payout account\", status: \"pending\", age: \"40 min\" },\n { id: \"v-1039\", creator: \"Maya Chen\", kind: \"Identity check\", status: \"approved\", age: \"2 h\" },\n { id: \"v-1036\", creator: \"Tom Lebrun\", kind: \"Age check (18+)\", status: \"refused\", age: \"5 h\" },\n];\nconst tone = { pending: \"warning\", approved: \"success\", refused: \"danger\" } as const;\nconst columns: DataTableColumn<Request>[] = [\n { id: \"creator\", header: \"Creator\", sortable: true, card: \"title\" },\n { id: \"kind\", header: \"Request\" },\n { id: \"status\", header: \"Status\", cell: (r) => <Badge tone={tone[r.status]} dot>{r.status}</Badge> },\n { id: \"age\", header: \"Waiting\", align: \"end\" },\n];\n\n// TEMPLATE — an administration console (Orochia admin): the shell with the console's sections, a queue of requests to\n// review as a sortable table with bulk actions — cards on a phone — and the counts that say how long the queue is.\nexport default function AppShellAdminConsole() {\n return (\n <AppShell.Root>\n <AppShell.Sidebar label=\"Administration\" brand={<><ProductLogo id=\"orochia\" size={24} animated={false} /> Admin</>} closeLabel=\"Close the menu\" collapseLabels={[\"Collapse\", \"Expand\"]}>\n <AppShell.Group>Review</AppShell.Group>\n <AppShell.Item href=\"#verification\" icon={icon(\"M12 3l8 4v5c0 5-3.5 8-8 9-4.5-1-8-4-8-9V7z\")} label=\"Verification\" current badge={2} />\n <AppShell.Item href=\"#moderation\" icon={icon(\"M4 5h16v11H9l-5 4z\")} label=\"Moderation\" badge={5} />\n <AppShell.Group>Money</AppShell.Group>\n <AppShell.Item href=\"#payouts\" icon={icon(\"M3 7h18v10H3zM12 12h.01\")} label=\"Payouts\" />\n <AppShell.Item href=\"#ledger\" icon={icon(\"M5 4h14v16H5zM8 8h8M8 12h8M8 16h5\")} label=\"Ledger\" />\n <AppShell.Group>System</AppShell.Group>\n <AppShell.Item href=\"#backups\" icon={icon(\"M4 7c0-2 16-2 16 0v10c0 2-16 2-16 0zM4 12c0 2 16 2 16 0\")} label=\"Backups\" />\n </AppShell.Sidebar>\n <AppShell.Body>\n <AppShell.Header title=\"Verification\" menuLabel=\"Open the menu\" actions={<Button size=\"sm\" variant=\"secondary\">Export</Button>} />\n <AppShell.Main>\n <DashboardLayout headingAs=\"h2\" title=\"Requests to review\" description=\"Oldest first. A decision is logged with your name.\">\n <Widget level={3} title=\"Pending\" description=\"identity and payout\">\n <p className=\"font-display text-3xl font-extrabold text-fg\">2</p>\n </Widget>\n <Widget level={3} title=\"Oldest wait\">\n <p className=\"font-display text-3xl font-extrabold text-fg\">40 min</p>\n </Widget>\n <Widget level={3} title=\"Queue\" span=\"full\">\n <DataTable caption=\"Verification requests\" rows={REQUESTS} columns={columns} getRowId={(r) => r.id} rowLabel={(r) => `${r.creator}, ${r.kind}`} selectable />\n </Widget>\n </DashboardLayout>\n </AppShell.Main>\n </AppShell.Body>\n </AppShell.Root>\n );\n}\n"
|
|
348
348
|
},
|
|
349
349
|
{
|
|
350
350
|
"name": "basic",
|
|
@@ -352,7 +352,7 @@
|
|
|
352
352
|
"description": "Sidebar with a group and a count, header with an action, the content.",
|
|
353
353
|
"path": "examples/app-shell/basic.tsx",
|
|
354
354
|
"module": "@krizaka/ui/registry/examples/app-shell/basic",
|
|
355
|
-
"code": "import { ProductLogo } from \"@krizaka/ui\";\nimport { AppShell } from \"@krizaka/ui/app-shell\";\nimport { Button } from \"@krizaka/ui/button\";\n\nconst dot = <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" aria-hidden><circle cx=\"12\" cy=\"12\" r=\"7\" /></svg>;\n\n// The shell alone: the sidebar (it collapses to its icons on a desktop, opens as a drawer on a phone), the header, the\n// content. Your router's links go in `AppShell.Item asChild`.\nexport default function AppShellBasic() {\n return (\n <AppShell.Root className=\"min-h-[32rem]\">\n <AppShell.Sidebar\n label=\"Studio\"\n brand={<><ProductLogo id=\"orochia\" size={24} animated={false} /> Studio</>}\n collapseLabels={[\"Collapse\", \"Expand\"]}\n closeLabel=\"Close the menu\"\n >\n <AppShell.Item href=\"#home\" icon={dot} label=\"Home\" current />\n <AppShell.Item href=\"#videos\" icon={dot} label=\"Videos\" badge={3} />\n <AppShell.Group>Money</AppShell.Group>\n <AppShell.Item href=\"#earnings\" icon={dot} label=\"Earnings\" />\n </AppShell.Sidebar>\n <AppShell.Body>\n <AppShell.Header title=\"Home\" menuLabel=\"Open the menu\" actions={<Button size=\"sm\" variant=\"primary\"
|
|
355
|
+
"code": "import { ProductLogo } from \"@krizaka/ui\";\nimport { AppShell } from \"@krizaka/ui/app-shell\";\nimport { Button } from \"@krizaka/ui/button\";\n\nconst dot = <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" aria-hidden><circle cx=\"12\" cy=\"12\" r=\"7\" /></svg>;\n\n// The shell alone: the sidebar (it collapses to its icons on a desktop, opens as a drawer on a phone), the header, the\n// content. Your router's links go in `AppShell.Item asChild`.\nexport default function AppShellBasic() {\n return (\n <AppShell.Root className=\"min-h-[32rem]\">\n <AppShell.Sidebar\n label=\"Studio\"\n brand={<><ProductLogo id=\"orochia\" size={24} animated={false} /> Studio</>}\n collapseLabels={[\"Collapse\", \"Expand\"]}\n closeLabel=\"Close the menu\"\n >\n <AppShell.Item href=\"#home\" icon={dot} label=\"Home\" current />\n <AppShell.Item href=\"#videos\" icon={dot} label=\"Videos\" badge={3} />\n <AppShell.Group>Money</AppShell.Group>\n <AppShell.Item href=\"#earnings\" icon={dot} label=\"Earnings\" />\n </AppShell.Sidebar>\n <AppShell.Body>\n <AppShell.Header title=\"Home\" menuLabel=\"Open the menu\" actions={<Button size=\"sm\" variant=\"primary\">Upload</Button>} />\n <AppShell.Main>\n <p className=\"text-sm text-fg-secondary\">The screen's content.</p>\n </AppShell.Main>\n </AppShell.Body>\n </AppShell.Root>\n );\n}\n"
|
|
356
356
|
},
|
|
357
357
|
{
|
|
358
358
|
"name": "desktop",
|
|
@@ -360,7 +360,7 @@
|
|
|
360
360
|
"description": "The Orazaka Mac app's window (`desktop`): the title bar moves the window and clears the traffic lights, no drawer, the desktop's density, ⌘1–⌘3 to the sections.",
|
|
361
361
|
"path": "examples/app-shell/desktop.tsx",
|
|
362
362
|
"module": "@krizaka/ui/registry/examples/app-shell/desktop",
|
|
363
|
-
"code": "import { ProductLogo } from \"@krizaka/ui\";\nimport { AppShell } from \"@krizaka/ui/app-shell\";\nimport { Badge } from \"@krizaka/ui/badge\";\nimport { Button } from \"@krizaka/ui/button\";\nimport { Progress } from \"@krizaka/ui/progress\";\n\nconst dot = <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" aria-hidden><circle cx=\"12\" cy=\"12\" r=\"7\" /></svg>;\n\n// The Mac app's window (Tauri 2, ADR-002): `desktop` — the title bar moves the window and clears the traffic lights, no\n// drawer, desktop density. The menu bar answers the same keys (`acceleratorOf(\"mod+1\")` → `CmdOrCtrl+1`).\nexport default function AppShellDesktop() {\n return (\n <AppShell.Root desktop style={{ [\"--kz-window-height\" as string]: \"34rem\" }}>\n <AppShell.Sidebar label=\"Orazaka\" brand={<><ProductLogo id=\"orazaka\" size={22} animated={false} /> Orazaka</>} collapseLabels={[\"Collapse\", \"Expand\"]} closeLabel=\"Close\">\n <AppShell.Item href=\"#workspace\" icon={dot} label=\"Workspace\" shortcut=\"mod+1\" />\n <AppShell.Item href=\"#models\" icon={dot} label=\"Models\" shortcut=\"mod+2\" current />\n <AppShell.Item href=\"#jobs\" icon={dot} label=\"Jobs\" shortcut=\"mod+3\" badge={2} />\n <AppShell.Group>Machine</AppShell.Group>\n <AppShell.Item href=\"#backups\" icon={dot} label=\"Backups\" />\n <AppShell.Item href=\"#logs\" icon={dot} label=\"Logs\" />\n </AppShell.Sidebar>\n <AppShell.Body>\n <AppShell.Header title=\"Models\" menuLabel=\"Open the menu\" actions={<Button size=\"sm\" variant=\"primary\"
|
|
363
|
+
"code": "import { ProductLogo } from \"@krizaka/ui\";\nimport { AppShell } from \"@krizaka/ui/app-shell\";\nimport { Badge } from \"@krizaka/ui/badge\";\nimport { Button } from \"@krizaka/ui/button\";\nimport { Progress } from \"@krizaka/ui/progress\";\n\nconst dot = <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" aria-hidden><circle cx=\"12\" cy=\"12\" r=\"7\" /></svg>;\n\n// The Mac app's window (Tauri 2, ADR-002): `desktop` — the title bar moves the window and clears the traffic lights, no\n// drawer, desktop density. The menu bar answers the same keys (`acceleratorOf(\"mod+1\")` → `CmdOrCtrl+1`).\nexport default function AppShellDesktop() {\n return (\n <AppShell.Root desktop style={{ [\"--kz-window-height\" as string]: \"34rem\" }}>\n <AppShell.Sidebar label=\"Orazaka\" brand={<><ProductLogo id=\"orazaka\" size={22} animated={false} /> Orazaka</>} collapseLabels={[\"Collapse\", \"Expand\"]} closeLabel=\"Close\">\n <AppShell.Item href=\"#workspace\" icon={dot} label=\"Workspace\" shortcut=\"mod+1\" />\n <AppShell.Item href=\"#models\" icon={dot} label=\"Models\" shortcut=\"mod+2\" current />\n <AppShell.Item href=\"#jobs\" icon={dot} label=\"Jobs\" shortcut=\"mod+3\" badge={2} />\n <AppShell.Group>Machine</AppShell.Group>\n <AppShell.Item href=\"#backups\" icon={dot} label=\"Backups\" />\n <AppShell.Item href=\"#logs\" icon={dot} label=\"Logs\" />\n </AppShell.Sidebar>\n <AppShell.Body>\n <AppShell.Header title=\"Models\" menuLabel=\"Open the menu\" actions={<Button size=\"sm\" variant=\"primary\">Add a model</Button>} />\n <AppShell.Main className=\"space-y-4\">\n <div className=\"flex items-center justify-between gap-3 rounded-lg border border-border-subtle bg-surface-1 p-4\">\n <div className=\"min-w-0 flex-1 space-y-2\">\n <p className=\"text-sm font-semibold\">qwen2.5 7B · chat</p>\n <Progress value={64} label=\"Downloading qwen2.5 7B\" />\n </div>\n <Badge tone=\"accent\">3.1 of 4.8 GB</Badge>\n </div>\n <p className=\"text-[13px] text-fg-secondary\">Models run on this Mac (Metal). Nothing leaves your network.</p>\n </AppShell.Main>\n </AppShell.Body>\n </AppShell.Root>\n );\n}\n"
|
|
364
364
|
}
|
|
365
365
|
]
|
|
366
366
|
},
|
|
@@ -41,8 +41,7 @@
|
|
|
41
41
|
"What it does, the bird's quote and the long version are never on the face: a detail page (`more`), or `panelLabel`.",
|
|
42
42
|
"One idea per card; one bird per idea: small in the corner (`mark`), its quote (`rule`) where the card unfolds.",
|
|
43
43
|
"Give each subject its hue with `--kz-hue` on the card (or a wrapper); the brand's accent otherwise.",
|
|
44
|
-
"Secondary actions as an icon `ActionBar` in `actions`; the way in as `more`."
|
|
45
|
-
"`hover=\"quiet\"` for reasons read as prose (a product's argument, a policy): no glow, a lift and a ring on focus. The default `light` keeps the pointer's light."
|
|
44
|
+
"Secondary actions as an icon `ActionBar` in `actions`; the way in as `more`."
|
|
46
45
|
],
|
|
47
46
|
"accessibility": {
|
|
48
47
|
"keyboard": [
|
|
@@ -54,7 +53,7 @@
|
|
|
54
53
|
"notes": [
|
|
55
54
|
"An `article` named by its title; the hook's label is read before the line (visually hidden); the fold is a native `details` element.",
|
|
56
55
|
"The way in is described by the card's title (`aria-describedby`), so several `Read more` links stay distinct.",
|
|
57
|
-
"
|
|
56
|
+
"One quiet hover (krizaka-ui#393): the surface and the hairline go one step up in a short colour transition — nothing moves, no light follows the pointer; a ring on the whole card when a link inside takes the keyboard focus. The panel's chevron stops under `prefers-reduced-motion`."
|
|
58
57
|
]
|
|
59
58
|
},
|
|
60
59
|
"related": [
|
|
@@ -109,7 +108,7 @@
|
|
|
109
108
|
"name": "hover",
|
|
110
109
|
"type": "CardHover",
|
|
111
110
|
"default": null,
|
|
112
|
-
"description": "
|
|
111
|
+
"description": "@deprecated Since 3.31 (krizaka-ui#393): does nothing — every card has the one quiet hover. Removed in 4.0.",
|
|
113
112
|
"required": false
|
|
114
113
|
},
|
|
115
114
|
{
|
|
@@ -203,21 +202,13 @@
|
|
|
203
202
|
"module": "@krizaka/ui/registry/examples/argument-card/grid",
|
|
204
203
|
"code": "import { ArgumentCard } from \"@krizaka/ui/argument-card\";\nimport { CardGrid } from \"@krizaka/ui/card-grid\";\n\nconst reasons = [\n { id: \"local\", title: \"Local inference\", hook: \"Elsewhere, every prompt crosses the ocean.\" },\n { id: \"pipeline\", title: \"One pipeline\", hook: \"Elsewhere, each feature guards itself — or forgets.\" },\n { id: \"telemetry\", title: \"Zero telemetry\", hook: \"Elsewhere, usage data is the price of the free tier.\" },\n];\n\n// A product page's reasons, side by side: each card the same height, the hook at the same place on every card.\nexport default function ArgumentCardGrid() {\n return (\n <CardGrid className=\"w-full max-w-5xl\" min={260} reveal={false}>\n {reasons.map((r) => (\n <ArgumentCard key={r.id} id={`why-${r.id}`} title={r.title} hook={{ label: \"The trap elsewhere\", text: r.hook }} more={{ href: `#${r.id}`, label: \"Details\" }} />\n ))}\n </CardGrid>\n );\n}\n"
|
|
205
204
|
},
|
|
206
|
-
{
|
|
207
|
-
"name": "hover-light",
|
|
208
|
-
"title": "The light hover",
|
|
209
|
-
"description": "`hover=\"light\"`, the default: the pointer's light and the coloured lift, said explicitly.",
|
|
210
|
-
"path": "examples/argument-card/hover-light.tsx",
|
|
211
|
-
"module": "@krizaka/ui/registry/examples/argument-card/hover-light",
|
|
212
|
-
"code": "import { ArgumentCard } from \"@krizaka/ui/argument-card\";\nimport { CardGrid } from \"@krizaka/ui/card-grid\";\n\nconst reasons = [\n { id: \"local\", title: \"Local inference\", hook: \"Elsewhere, every prompt crosses the ocean.\" },\n { id: \"pipeline\", title: \"One pipeline\", hook: \"Elsewhere, each feature guards itself — or forgets.\" },\n { id: \"telemetry\", title: \"Zero telemetry\", hook: \"Elsewhere, usage data is the price of the free tier.\" },\n];\n\n// The default hover, said explicitly: the pointer's light on the reasons.\nexport default function ArgumentCardHoverLight() {\n return (\n <CardGrid className=\"w-full max-w-5xl\" min={260} reveal={false}>\n {reasons.map((r) => (\n <ArgumentCard key={r.id} hover=\"light\" id={`light-${r.id}`} title={r.title} hook={{ label: \"The trap elsewhere\", text: r.hook }} more={{ href: `#${r.id}`, label: \"Details\" }} />\n ))}\n </CardGrid>\n );\n}\n"
|
|
213
|
-
},
|
|
214
205
|
{
|
|
215
206
|
"name": "hover-quiet",
|
|
216
207
|
"title": "The quiet hover",
|
|
217
208
|
"description": "`hover=\"quiet\"` on a grid of reasons: no glow, a lift, a ring on focus.",
|
|
218
209
|
"path": "examples/argument-card/hover-quiet.tsx",
|
|
219
210
|
"module": "@krizaka/ui/registry/examples/argument-card/hover-quiet",
|
|
220
|
-
"code": "import { ArgumentCard } from \"@krizaka/ui/argument-card\";\nimport { CardGrid } from \"@krizaka/ui/card-grid\";\n\nconst reasons = [\n { id: \"local\", title: \"Local inference\", hook: \"Elsewhere, every prompt crosses the ocean.\" },\n { id: \"pipeline\", title: \"One pipeline\", hook: \"Elsewhere, each feature guards itself — or forgets.\" },\n { id: \"telemetry\", title: \"Zero telemetry\", hook: \"Elsewhere, usage data is the price of the free tier.\" },\n];\n\n// The quiet hover on the reasons: no glow, a lift, a ring on focus. The grid is quiet, so every card is.\nexport default function ArgumentCardHoverQuiet() {\n return (\n <CardGrid className=\"w-full max-w-5xl\" min={260} reveal={false}
|
|
211
|
+
"code": "import { ArgumentCard } from \"@krizaka/ui/argument-card\";\nimport { CardGrid } from \"@krizaka/ui/card-grid\";\n\nconst reasons = [\n { id: \"local\", title: \"Local inference\", hook: \"Elsewhere, every prompt crosses the ocean.\" },\n { id: \"pipeline\", title: \"One pipeline\", hook: \"Elsewhere, each feature guards itself — or forgets.\" },\n { id: \"telemetry\", title: \"Zero telemetry\", hook: \"Elsewhere, usage data is the price of the free tier.\" },\n];\n\n// The quiet hover on the reasons: no glow, a lift, a ring on focus. The grid is quiet, so every card is.\nexport default function ArgumentCardHoverQuiet() {\n return (\n <CardGrid className=\"w-full max-w-5xl\" min={260} reveal={false}>\n {reasons.map((r) => (\n <ArgumentCard key={r.id} id={`quiet-${r.id}`} title={r.title} hook={{ label: \"The trap elsewhere\", text: r.hook }} more={{ href: `#${r.id}`, label: \"Details\" }} />\n ))}\n </CardGrid>\n );\n}\n"
|
|
221
212
|
}
|
|
222
213
|
]
|
|
223
214
|
},
|
|
@@ -225,7 +216,7 @@
|
|
|
225
216
|
"files": [
|
|
226
217
|
{
|
|
227
218
|
"path": "argument-card/argument-card.tsx",
|
|
228
|
-
"content": "// Server-safe. A card that argues (blocks.css), compact: who (icon, name, facts) → the trap in one line → the way in.\n// The long version — what it does, the rule a bird stands for, the reasons — waits in a panel the reader opens (a\n// native <details>: keyboard, screen readers, no script). Cards of a row share their height; the panel and the foot\n// sit at the bottom, at the same place on every card.\nimport type * as React from \"react\";\n\nimport { BlockLink, type LinkComponent } from \"../block-link\";\nimport { cn } from \"../cn\";\nimport { ArrowGlyph, ChevronGlyph } from \"../glyphs\";\nimport { type CardHover, cardHoverClass } from \"../link-card/link-card\";\nimport { type Fact, Facts } from \"../page/page\";\n\nexport type ArgumentCardProps = Omit<React.HTMLAttributes<HTMLElement>, \"title\"> & {\n /** The id of the title: the card's accessible name. */\n id: string;\n /** The title: the subject (a building block, a feature, a plan). */\n title: React.ReactNode;\n /** The icon tile (a signature icon): decorative. */\n icon?: React.ReactNode;\n /** A mono line under the title (a repository, a package). */\n meta?: React.ReactNode;\n /** Small, in the top corner: the bird whose rule it is (a `Mascot` of 40 px, still). Decorative. */\n mark?: React.ReactNode;\n /** Compact facts under the line: registry and version, a status said honestly. */\n facts?: readonly Fact[];\n /**\n * The problem in ONE line (≤ 90 characters): what a reader keeps if they read nothing else. `label` (`The trap`) is\n * read by assistive technology before it, not shown.\n */\n hook: { label: React.ReactNode; text: React.ReactNode };\n /** Unfolded only: what it does, as a few short points, with their label (`What it does`). */\n points?: { label: React.ReactNode; items: readonly React.ReactNode[] };\n /** Unfolded only: the rule told by a bird (a `MascotQuote`, `variant=\"inline\"`). */\n rule?: React.ReactNode;\n /** Unfolded only: the long version, as labelled paragraphs. */\n details?: readonly { label: React.ReactNode; text: React.ReactNode }[];\n /**\n * Without a detail page: the words that unfold the card in place (`Details`) — a native `<details>` holding the\n * points, the rule and the long version. Ignored with `more`: the detail page holds them.\n */\n panelLabel?: React.ReactNode;\n /** The way in: a detail page — one short action at the foot (`Details`). With it, the card is only its face. */\n more?: { href: string; label: React.ReactNode };\n /** Discreet secondary actions at the foot, on the right (an `ActionBar` with `variant=\"icons\"`). */\n actions?: React.ReactNode;\n /**\n * The title's level.\n * @default 3\n */\n level?: 2 | 3 | 4;\n /** The app's link component for internal addresses (Next's `Link`); an `<a>` otherwise. */\n linkAs?: LinkComponent;\n /**
|
|
219
|
+
"content": "// Server-safe. A card that argues (blocks.css), compact: who (icon, name, facts) → the trap in one line → the way in.\n// The long version — what it does, the rule a bird stands for, the reasons — waits in a panel the reader opens (a\n// native <details>: keyboard, screen readers, no script). Cards of a row share their height; the panel and the foot\n// sit at the bottom, at the same place on every card.\nimport type * as React from \"react\";\n\nimport { BlockLink, type LinkComponent } from \"../block-link\";\nimport { cn } from \"../cn\";\nimport { ArrowGlyph, ChevronGlyph } from \"../glyphs\";\nimport { type CardHover, cardHoverClass } from \"../link-card/link-card\";\nimport { type Fact, Facts } from \"../page/page\";\n\nexport type ArgumentCardProps = Omit<React.HTMLAttributes<HTMLElement>, \"title\"> & {\n /** The id of the title: the card's accessible name. */\n id: string;\n /** The title: the subject (a building block, a feature, a plan). */\n title: React.ReactNode;\n /** The icon tile (a signature icon): decorative. */\n icon?: React.ReactNode;\n /** A mono line under the title (a repository, a package). */\n meta?: React.ReactNode;\n /** Small, in the top corner: the bird whose rule it is (a `Mascot` of 40 px, still). Decorative. */\n mark?: React.ReactNode;\n /** Compact facts under the line: registry and version, a status said honestly. */\n facts?: readonly Fact[];\n /**\n * The problem in ONE line (≤ 90 characters): what a reader keeps if they read nothing else. `label` (`The trap`) is\n * read by assistive technology before it, not shown.\n */\n hook: { label: React.ReactNode; text: React.ReactNode };\n /** Unfolded only: what it does, as a few short points, with their label (`What it does`). */\n points?: { label: React.ReactNode; items: readonly React.ReactNode[] };\n /** Unfolded only: the rule told by a bird (a `MascotQuote`, `variant=\"inline\"`). */\n rule?: React.ReactNode;\n /** Unfolded only: the long version, as labelled paragraphs. */\n details?: readonly { label: React.ReactNode; text: React.ReactNode }[];\n /**\n * Without a detail page: the words that unfold the card in place (`Details`) — a native `<details>` holding the\n * points, the rule and the long version. Ignored with `more`: the detail page holds them.\n */\n panelLabel?: React.ReactNode;\n /** The way in: a detail page — one short action at the foot (`Details`). With it, the card is only its face. */\n more?: { href: string; label: React.ReactNode };\n /** Discreet secondary actions at the foot, on the right (an `ActionBar` with `variant=\"icons\"`). */\n actions?: React.ReactNode;\n /**\n * The title's level.\n * @default 3\n */\n level?: 2 | 3 | 4;\n /** The app's link component for internal addresses (Next's `Link`); an `<a>` otherwise. */\n linkAs?: LinkComponent;\n /** @deprecated Since 3.31 (krizaka-ui#393): does nothing — every card has the one quiet hover. Removed in 4.0. */\n hover?: CardHover;\n};\n\n/**\n * A card that argues, compact (about a third of a full card): who (icon, name, meta; the bird small in the corner) →\n * the problem in one line → compact facts → one short action. What it does, the bird's rule and the long version are\n * never on the face: they live on the detail page (`more`) or in the card unfolded in place (`panelLabel`).\n */\nexport function ArgumentCard({ id, title, icon, meta, mark, facts, hook, points, rule, details, panelLabel, more, actions, level = 3, linkAs, hover, className, ...props }: ArgumentCardProps) {\n const H = `h${level}` as \"h2\" | \"h3\" | \"h4\";\n const fold = !more && panelLabel && (points || rule || (details && details.length > 0));\n return (\n <article className={cn(\"kz-block-card kz-arg\", cardHoverClass(hover), className)} aria-labelledby={id} {...props}>\n <div className=\"kz-arg-head\">\n {icon && (\n <span className=\"kz-block-icon\" aria-hidden>\n {icon}\n </span>\n )}\n <div className=\"kz-arg-id\">\n <H id={id} className=\"kz-arg-title\">\n {title}\n </H>\n {meta && <p className=\"kz-arg-meta\">{meta}</p>}\n </div>\n {mark && (\n <span className=\"kz-arg-mark\" aria-hidden>\n {mark}\n </span>\n )}\n </div>\n <p className=\"kz-arg-line\">\n <span className=\"kz-sr-only\">{hook.label} — </span>\n {hook.text}\n </p>\n {facts && facts.length > 0 && <Facts items={facts} />}\n {fold && (\n <details className=\"kz-arg-panel\">\n <summary>\n {panelLabel} <ChevronGlyph className=\"h-3.5 w-3.5\" />\n </summary>\n <div className=\"kz-arg-panel-body\">\n {points && (\n <div>\n <p className=\"kz-label\">{points.label}</p>\n <ul className=\"kz-list\">\n {points.items.map((item, i) => (\n <li key={i}>{item}</li>\n ))}\n </ul>\n </div>\n )}\n {details && details.length > 0 && (\n <dl>\n {details.map((d, i) => (\n <div key={i}>\n <dt className=\"kz-label\">{d.label}</dt>\n <dd>{d.text}</dd>\n </div>\n ))}\n </dl>\n )}\n {rule}\n </div>\n </details>\n )}\n {(more || actions) && (\n <div className=\"kz-arg-foot\">\n {more ? (\n <BlockLink href={more.href} linkAs={linkAs} className=\"kz-arg-more\" aria-describedby={id}>\n {more.label} <ArrowGlyph className=\"h-3.5 w-3.5\" />\n </BlockLink>\n ) : (\n <span />\n )}\n {actions}\n </div>\n )}\n </article>\n );\n}\n"
|
|
229
220
|
},
|
|
230
221
|
{
|
|
231
222
|
"path": "argument-card/index.ts",
|
|
@@ -233,7 +224,7 @@
|
|
|
233
224
|
},
|
|
234
225
|
{
|
|
235
226
|
"path": "argument-card/meta.ts",
|
|
236
|
-
"content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Argument card\",\n summary:\n \"A compact card that argues — about a third of a full card: who (icon, name, the bird in the corner), the trap in one line, facts and one short action; the long version lives on a detail page or unfolds in place.\",\n status: \"beta\",\n category: \"blocks\",\n platforms: \"web\",\n parity: { native: { status: \"n/a\", reason: \"A block of a public site's page; an app screen on a phone is built from Screen, Header and Card.\", use: \"card\" }, desktop: { status: \"n/a\", reason: \"A public site's page, not a window of the Mac app.\" } },\n whenToUse: [\n \"The reasons of a product page, the building blocks of an open-source page, the options of a decision.\",\n \"When each subject has a problem to state before its solution, and a reader who scans before reading.\",\n ],\n whenNotToUse: [\n { when: \"When the card only leads somewhere.\", use: \"link-card\" },\n { when: \"For the decisions behind a choice, compared side by side.\", use: \"decision-list\" },\n ],\n bestPractices: [\n \"The hook is ONE line (≤ 90 characters, regular weight) a reader keeps in five seconds — never three bold lines.\",\n \"What it does, the bird's quote and the long version are never on the face: a detail page (`more`), or `panelLabel`.\",\n \"One idea per card; one bird per idea: small in the corner (`mark`), its quote (`rule`) where the card unfolds.\",\n \"Give each subject its hue with `--kz-hue` on the card (or a wrapper); the brand's accent otherwise.\",\n \"Secondary actions as an icon `ActionBar` in `actions`; the way in as `more`.\",\n
|
|
227
|
+
"content": "import type { ComponentMeta } from \"../meta\";\n\nexport const meta = {\n title: \"Argument card\",\n summary:\n \"A compact card that argues — about a third of a full card: who (icon, name, the bird in the corner), the trap in one line, facts and one short action; the long version lives on a detail page or unfolds in place.\",\n status: \"beta\",\n category: \"blocks\",\n platforms: \"web\",\n parity: { native: { status: \"n/a\", reason: \"A block of a public site's page; an app screen on a phone is built from Screen, Header and Card.\", use: \"card\" }, desktop: { status: \"n/a\", reason: \"A public site's page, not a window of the Mac app.\" } },\n whenToUse: [\n \"The reasons of a product page, the building blocks of an open-source page, the options of a decision.\",\n \"When each subject has a problem to state before its solution, and a reader who scans before reading.\",\n ],\n whenNotToUse: [\n { when: \"When the card only leads somewhere.\", use: \"link-card\" },\n { when: \"For the decisions behind a choice, compared side by side.\", use: \"decision-list\" },\n ],\n bestPractices: [\n \"The hook is ONE line (≤ 90 characters, regular weight) a reader keeps in five seconds — never three bold lines.\",\n \"What it does, the bird's quote and the long version are never on the face: a detail page (`more`), or `panelLabel`.\",\n \"One idea per card; one bird per idea: small in the corner (`mark`), its quote (`rule`) where the card unfolds.\",\n \"Give each subject its hue with `--kz-hue` on the card (or a wrapper); the brand's accent otherwise.\",\n \"Secondary actions as an icon `ActionBar` in `actions`; the way in as `more`.\",\n ],\n accessibility: {\n keyboard: [{ keys: \"Enter / Space\", action: \"On the panel's summary, opens or closes the long version.\" }],\n notes: [\n \"An `article` named by its title; the hook's label is read before the line (visually hidden); the fold is a native `details` element.\",\n \"The way in is described by the card's title (`aria-describedby`), so several `Read more` links stay distinct.\",\n \"One quiet hover (krizaka-ui#393): the surface and the hairline go one step up in a short colour transition — nothing moves, no light follows the pointer; a ring on the whole card when a link inside takes the keyboard focus. The panel's chevron stops under `prefers-reduced-motion`.\",\n ],\n },\n related: [\"card-grid\", \"mascot-quote\", \"action-bar\"],\n web: {\n imports: [\"ArgumentCard\"],\n examples: [\n { name: \"building-block\", title: \"A building block\", description: \"Compact: the duck in the corner, the trap in one line, facts, one short action and an icon action.\" },\n { name: \"unfold\", title: \"Unfolded in place\", description: \"No detail page: `panelLabel` unfolds what it does, the cost and the pigeon's rule.\" },\n { name: \"grid\", title: \"Three reasons\", description: \"A product's reasons side by side in a grid: the hook and the way in, no panel.\" },\n { name: \"hover-quiet\", title: \"The quiet hover\", description: \"`hover=\\\"quiet\\\"` on a grid of reasons: no glow, a lift, a ring on focus.\" },\n ],\n },\n} satisfies ComponentMeta;\n"
|
|
237
228
|
},
|
|
238
229
|
{
|
|
239
230
|
"path": "block-link.tsx",
|
|
@@ -302,7 +293,7 @@
|
|
|
302
293
|
"name": "hover",
|
|
303
294
|
"type": "CardHover",
|
|
304
295
|
"default": null,
|
|
305
|
-
"description": "
|
|
296
|
+
"description": "@deprecated Since 3.31 (krizaka-ui#393): does nothing — every card has the one quiet hover. Removed in 4.0.",
|
|
306
297
|
"required": false
|
|
307
298
|
},
|
|
308
299
|
{
|
|
@@ -129,7 +129,7 @@
|
|
|
129
129
|
"why": "A returning person signs in fast, with the product's promise beside the form.",
|
|
130
130
|
"path": "examples/auth-layout/sign-in.tsx",
|
|
131
131
|
"module": "@krizaka/ui/registry/examples/auth-layout/sign-in",
|
|
132
|
-
"code": "import { ProductLogo } from \"@krizaka/ui\";\nimport { AuthLayout } from \"@krizaka/ui/auth-layout\";\nimport { Button } from \"@krizaka/ui/button\";\nimport { Field, Input } from \"@krizaka/ui/field\";\nimport { Mascot } from \"@krizaka/ui/mascots\";\n\n// TEMPLATE — Orochia's sign-in: the brand panel (its gradient, the promise, a creator's sentence) beside a calm form;\n// on a phone one column, the mark above the form, the fields at full width with 44 px targets.\nexport default function AuthLayoutSignIn() {\n return (\n <div className=\"brand-orochia\">\n <AuthLayout\n brand={<><ProductLogo id=\"orochia\" size={28} animated={false} /> Orochia</>}\n promise=\"Your fans pay you, not an algorithm.\"\n aside={\n <div className=\"flex items-end gap-3\">\n <Mascot bird=\"flamingo\" mood=\"welcome\" size={64} />\n <p>“90 % of every tip, unlock and auction reaches me — on Monday.” — Elena, creator</p>\n </div>\n }\n asideFooter=\"© Krizaka · Montréal\"\n title=\"Welcome back\"\n description=\"Sign in to your studio.\"\n footer={<>No account yet? <a className=\"font-semibold text-fg-accent\" href=\"#sign-up\">Create one</a></>}\n legal={<>By signing in you accept the <a className=\"underline\" href=\"#terms\">terms</a> and the <a className=\"underline\" href=\"#privacy\">privacy policy</a>.</>}\n >\n <form className=\"flex flex-col gap-4\">\n <Field.Root>\n <Field.Label htmlFor=\"sign-in-email\">Email</Field.Label>\n <Input id=\"sign-in-email\" type=\"email\" autoComplete=\"email\" placeholder=\"you@example.com\" />\n </Field.Root>\n <Field.Root>\n <Field.Label htmlFor=\"sign-in-password\">Password</Field.Label>\n <Input id=\"sign-in-password\" type=\"password\" autoComplete=\"current-password\" />\n <Field.Hint><a className=\"text-fg-accent\" href=\"#reset\">Forgot it?</a></Field.Hint>\n </Field.Root>\n <Button type=\"submit\" variant=\"primary\" size=\"lg\"
|
|
132
|
+
"code": "import { ProductLogo } from \"@krizaka/ui\";\nimport { AuthLayout } from \"@krizaka/ui/auth-layout\";\nimport { Button } from \"@krizaka/ui/button\";\nimport { Field, Input } from \"@krizaka/ui/field\";\nimport { Mascot } from \"@krizaka/ui/mascots\";\n\n// TEMPLATE — Orochia's sign-in: the brand panel (its gradient, the promise, a creator's sentence) beside a calm form;\n// on a phone one column, the mark above the form, the fields at full width with 44 px targets.\nexport default function AuthLayoutSignIn() {\n return (\n <div className=\"brand-orochia\">\n <AuthLayout\n brand={<><ProductLogo id=\"orochia\" size={28} animated={false} /> Orochia</>}\n promise=\"Your fans pay you, not an algorithm.\"\n aside={\n <div className=\"flex items-end gap-3\">\n <Mascot bird=\"flamingo\" mood=\"welcome\" size={64} />\n <p>“90 % of every tip, unlock and auction reaches me — on Monday.” — Elena, creator</p>\n </div>\n }\n asideFooter=\"© Krizaka · Montréal\"\n title=\"Welcome back\"\n description=\"Sign in to your studio.\"\n footer={<>No account yet? <a className=\"font-semibold text-fg-accent\" href=\"#sign-up\">Create one</a></>}\n legal={<>By signing in you accept the <a className=\"underline\" href=\"#terms\">terms</a> and the <a className=\"underline\" href=\"#privacy\">privacy policy</a>.</>}\n >\n <form className=\"flex flex-col gap-4\">\n <Field.Root>\n <Field.Label htmlFor=\"sign-in-email\">Email</Field.Label>\n <Input id=\"sign-in-email\" type=\"email\" autoComplete=\"email\" placeholder=\"you@example.com\" />\n </Field.Root>\n <Field.Root>\n <Field.Label htmlFor=\"sign-in-password\">Password</Field.Label>\n <Input id=\"sign-in-password\" type=\"password\" autoComplete=\"current-password\" />\n <Field.Hint><a className=\"text-fg-accent\" href=\"#reset\">Forgot it?</a></Field.Hint>\n </Field.Root>\n <Button type=\"submit\" variant=\"primary\" size=\"lg\" className=\"w-full\">\n Sign in\n </Button>\n <Button type=\"button\" variant=\"secondary\" size=\"lg\" className=\"w-full\">\n Use a passkey\n </Button>\n </form>\n </AuthLayout>\n </div>\n );\n}\n"
|
|
133
133
|
},
|
|
134
134
|
{
|
|
135
135
|
"name": "onboarding",
|
|
@@ -140,7 +140,7 @@
|
|
|
140
140
|
"why": "A new person completes a setup one step at a time, knowing how many remain.",
|
|
141
141
|
"path": "examples/auth-layout/onboarding.tsx",
|
|
142
142
|
"module": "@krizaka/ui/registry/examples/auth-layout/onboarding",
|
|
143
|
-
"code": "import { ProductLogo } from \"@krizaka/ui\";\nimport { AuthLayout } from \"@krizaka/ui/auth-layout\";\nimport { Button } from \"@krizaka/ui/button\";\nimport { Field, Input } from \"@krizaka/ui/field\";\nimport { Stepper } from \"@krizaka/ui/stepper\";\n\nconst labels = { done: \"Done\", current: \"Current step\", upcoming: \"Upcoming\", error: \"Failed\" };\n\n// TEMPLATE — onboarding a creator (Orochia): the steps said in words above the form of the current one; the brand panel\n// says why it is worth it. On a phone the steps stay horizontal and compact above the fields.\nexport default function AuthLayoutOnboarding() {\n return (\n <div className=\"brand-orochia\">\n <AuthLayout\n brand={<><ProductLogo id=\"orochia\" size={28} animated={false} /> Orochia</>}\n promise=\"Three steps, then your first video.\"\n aside={<p>Your identity is checked once, by a person, and never shown to your fans.</p>}\n asideFooter=\"© Krizaka · Montréal\"\n title=\"Your payout account\"\n description=\"Where your earnings go every Monday.\"\n >\n <Stepper\n aria-label=\"Onboarding\"\n orientation=\"horizontal\"\n labels={labels}\n current={1}\n steps={[{ title: \"Profile\" }, { title: \"Payouts\" }, { title: \"Identity\" }]}\n />\n <form className=\"flex flex-col gap-4\">\n <Field.Root>\n <Field.Label htmlFor=\"onboarding-holder\">Account holder</Field.Label>\n <Input id=\"onboarding-holder\" autoComplete=\"name\" defaultValue=\"Elena Varga\" />\n </Field.Root>\n <Field.Root>\n <Field.Label htmlFor=\"onboarding-iban\">IBAN</Field.Label>\n <Input id=\"onboarding-iban\" inputMode=\"text\" placeholder=\"FR76 …\" aria-describedby=\"onboarding-iban-hint\" />\n <Field.Hint id=\"onboarding-iban-hint\">Checked before your first payout.</Field.Hint>\n </Field.Root>\n <div className=\"flex gap-2\">\n <Button type=\"button\" variant=\"secondary\" size=\"lg\"
|
|
143
|
+
"code": "import { ProductLogo } from \"@krizaka/ui\";\nimport { AuthLayout } from \"@krizaka/ui/auth-layout\";\nimport { Button } from \"@krizaka/ui/button\";\nimport { Field, Input } from \"@krizaka/ui/field\";\nimport { Stepper } from \"@krizaka/ui/stepper\";\n\nconst labels = { done: \"Done\", current: \"Current step\", upcoming: \"Upcoming\", error: \"Failed\" };\n\n// TEMPLATE — onboarding a creator (Orochia): the steps said in words above the form of the current one; the brand panel\n// says why it is worth it. On a phone the steps stay horizontal and compact above the fields.\nexport default function AuthLayoutOnboarding() {\n return (\n <div className=\"brand-orochia\">\n <AuthLayout\n brand={<><ProductLogo id=\"orochia\" size={28} animated={false} /> Orochia</>}\n promise=\"Three steps, then your first video.\"\n aside={<p>Your identity is checked once, by a person, and never shown to your fans.</p>}\n asideFooter=\"© Krizaka · Montréal\"\n title=\"Your payout account\"\n description=\"Where your earnings go every Monday.\"\n >\n <Stepper\n aria-label=\"Onboarding\"\n orientation=\"horizontal\"\n labels={labels}\n current={1}\n steps={[{ title: \"Profile\" }, { title: \"Payouts\" }, { title: \"Identity\" }]}\n />\n <form className=\"flex flex-col gap-4\">\n <Field.Root>\n <Field.Label htmlFor=\"onboarding-holder\">Account holder</Field.Label>\n <Input id=\"onboarding-holder\" autoComplete=\"name\" defaultValue=\"Elena Varga\" />\n </Field.Root>\n <Field.Root>\n <Field.Label htmlFor=\"onboarding-iban\">IBAN</Field.Label>\n <Input id=\"onboarding-iban\" inputMode=\"text\" placeholder=\"FR76 …\" aria-describedby=\"onboarding-iban-hint\" />\n <Field.Hint id=\"onboarding-iban-hint\">Checked before your first payout.</Field.Hint>\n </Field.Root>\n <div className=\"flex gap-2\">\n <Button type=\"button\" variant=\"secondary\" size=\"lg\" className=\"flex-1\">Back</Button>\n <Button type=\"submit\" variant=\"primary\" size=\"lg\" className=\"flex-1\">Continue</Button>\n </div>\n </form>\n </AuthLayout>\n </div>\n );\n}\n"
|
|
144
144
|
},
|
|
145
145
|
{
|
|
146
146
|
"name": "verify",
|
|
@@ -148,7 +148,7 @@
|
|
|
148
148
|
"description": "The smallest entry screen: a code.",
|
|
149
149
|
"path": "examples/auth-layout/verify.tsx",
|
|
150
150
|
"module": "@krizaka/ui/registry/examples/auth-layout/verify",
|
|
151
|
-
"code": "import { ProductLogo } from \"@krizaka/ui\";\nimport { AuthLayout } from \"@krizaka/ui/auth-layout\";\nimport { Button } from \"@krizaka/ui/button\";\nimport { Field, Input } from \"@krizaka/ui/field\";\n\n// The smallest entry screen: no promise, the brand panel still carries the product's gradient on a desktop.\nexport default function AuthLayoutVerify() {\n return (\n <AuthLayout brand={<><ProductLogo id=\"krizaka\" size={28} animated={false} /> Krizaka</>} title=\"Check your email\" description=\"We sent a 6-digit code to eric@tremblay.ca.\">\n <form className=\"flex flex-col gap-4\">\n <Field.Root>\n <Field.Label htmlFor=\"verify-code\">Code</Field.Label>\n <Input id=\"verify-code\" inputMode=\"numeric\" autoComplete=\"one-time-code\" maxLength={6} />\n </Field.Root>\n <Button type=\"submit\" variant=\"primary\" size=\"lg\"
|
|
151
|
+
"code": "import { ProductLogo } from \"@krizaka/ui\";\nimport { AuthLayout } from \"@krizaka/ui/auth-layout\";\nimport { Button } from \"@krizaka/ui/button\";\nimport { Field, Input } from \"@krizaka/ui/field\";\n\n// The smallest entry screen: no promise, the brand panel still carries the product's gradient on a desktop.\nexport default function AuthLayoutVerify() {\n return (\n <AuthLayout brand={<><ProductLogo id=\"krizaka\" size={28} animated={false} /> Krizaka</>} title=\"Check your email\" description=\"We sent a 6-digit code to eric@tremblay.ca.\">\n <form className=\"flex flex-col gap-4\">\n <Field.Root>\n <Field.Label htmlFor=\"verify-code\">Code</Field.Label>\n <Input id=\"verify-code\" inputMode=\"numeric\" autoComplete=\"one-time-code\" maxLength={6} />\n </Field.Root>\n <Button type=\"submit\" variant=\"primary\" size=\"lg\" className=\"w-full\">Verify</Button>\n </form>\n </AuthLayout>\n );\n}\n"
|
|
152
152
|
}
|
|
153
153
|
]
|
|
154
154
|
},
|
|
@@ -179,7 +179,7 @@
|
|
|
179
179
|
],
|
|
180
180
|
"demo": {
|
|
181
181
|
"path": "examples/auth-layout/sign-in.tsx",
|
|
182
|
-
"content": "import { ProductLogo } from \"@krizaka/ui\";\nimport { AuthLayout } from \"@krizaka/ui/auth-layout\";\nimport { Button } from \"@krizaka/ui/button\";\nimport { Field, Input } from \"@krizaka/ui/field\";\nimport { Mascot } from \"@krizaka/ui/mascots\";\n\n// TEMPLATE — Orochia's sign-in: the brand panel (its gradient, the promise, a creator's sentence) beside a calm form;\n// on a phone one column, the mark above the form, the fields at full width with 44 px targets.\nexport default function AuthLayoutSignIn() {\n return (\n <div className=\"brand-orochia\">\n <AuthLayout\n brand={<><ProductLogo id=\"orochia\" size={28} animated={false} /> Orochia</>}\n promise=\"Your fans pay you, not an algorithm.\"\n aside={\n <div className=\"flex items-end gap-3\">\n <Mascot bird=\"flamingo\" mood=\"welcome\" size={64} />\n <p>“90 % of every tip, unlock and auction reaches me — on Monday.” — Elena, creator</p>\n </div>\n }\n asideFooter=\"© Krizaka · Montréal\"\n title=\"Welcome back\"\n description=\"Sign in to your studio.\"\n footer={<>No account yet? <a className=\"font-semibold text-fg-accent\" href=\"#sign-up\">Create one</a></>}\n legal={<>By signing in you accept the <a className=\"underline\" href=\"#terms\">terms</a> and the <a className=\"underline\" href=\"#privacy\">privacy policy</a>.</>}\n >\n <form className=\"flex flex-col gap-4\">\n <Field.Root>\n <Field.Label htmlFor=\"sign-in-email\">Email</Field.Label>\n <Input id=\"sign-in-email\" type=\"email\" autoComplete=\"email\" placeholder=\"you@example.com\" />\n </Field.Root>\n <Field.Root>\n <Field.Label htmlFor=\"sign-in-password\">Password</Field.Label>\n <Input id=\"sign-in-password\" type=\"password\" autoComplete=\"current-password\" />\n <Field.Hint><a className=\"text-fg-accent\" href=\"#reset\">Forgot it?</a></Field.Hint>\n </Field.Root>\n <Button type=\"submit\" variant=\"primary\" size=\"lg\"
|
|
182
|
+
"content": "import { ProductLogo } from \"@krizaka/ui\";\nimport { AuthLayout } from \"@krizaka/ui/auth-layout\";\nimport { Button } from \"@krizaka/ui/button\";\nimport { Field, Input } from \"@krizaka/ui/field\";\nimport { Mascot } from \"@krizaka/ui/mascots\";\n\n// TEMPLATE — Orochia's sign-in: the brand panel (its gradient, the promise, a creator's sentence) beside a calm form;\n// on a phone one column, the mark above the form, the fields at full width with 44 px targets.\nexport default function AuthLayoutSignIn() {\n return (\n <div className=\"brand-orochia\">\n <AuthLayout\n brand={<><ProductLogo id=\"orochia\" size={28} animated={false} /> Orochia</>}\n promise=\"Your fans pay you, not an algorithm.\"\n aside={\n <div className=\"flex items-end gap-3\">\n <Mascot bird=\"flamingo\" mood=\"welcome\" size={64} />\n <p>“90 % of every tip, unlock and auction reaches me — on Monday.” — Elena, creator</p>\n </div>\n }\n asideFooter=\"© Krizaka · Montréal\"\n title=\"Welcome back\"\n description=\"Sign in to your studio.\"\n footer={<>No account yet? <a className=\"font-semibold text-fg-accent\" href=\"#sign-up\">Create one</a></>}\n legal={<>By signing in you accept the <a className=\"underline\" href=\"#terms\">terms</a> and the <a className=\"underline\" href=\"#privacy\">privacy policy</a>.</>}\n >\n <form className=\"flex flex-col gap-4\">\n <Field.Root>\n <Field.Label htmlFor=\"sign-in-email\">Email</Field.Label>\n <Input id=\"sign-in-email\" type=\"email\" autoComplete=\"email\" placeholder=\"you@example.com\" />\n </Field.Root>\n <Field.Root>\n <Field.Label htmlFor=\"sign-in-password\">Password</Field.Label>\n <Input id=\"sign-in-password\" type=\"password\" autoComplete=\"current-password\" />\n <Field.Hint><a className=\"text-fg-accent\" href=\"#reset\">Forgot it?</a></Field.Hint>\n </Field.Root>\n <Button type=\"submit\" variant=\"primary\" size=\"lg\" className=\"w-full\">\n Sign in\n </Button>\n <Button type=\"button\" variant=\"secondary\" size=\"lg\" className=\"w-full\">\n Use a passkey\n </Button>\n </form>\n </AuthLayout>\n </div>\n );\n}\n"
|
|
183
183
|
},
|
|
184
184
|
"props": [
|
|
185
185
|
{
|