@krizaka/ui 3.35.0 → 3.37.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/dist/account-menu.js +1 -1
- package/dist/account-menu.js.map +1 -1
- package/dist/activity-feed.js +2 -2
- package/dist/activity-feed.js.map +1 -1
- package/dist/anatomy-plate.d.ts +26 -0
- package/dist/anatomy-plate.js +32 -0
- package/dist/anatomy-plate.js.map +1 -0
- package/dist/app-header.js +1 -1
- package/dist/app-header.js.map +1 -1
- package/dist/app-shell.js +6 -6
- package/dist/app-shell.js.map +1 -1
- package/dist/argument-card.js +1 -1
- package/dist/badge.d.ts +1 -1
- package/dist/badge.js +1 -1
- package/dist/badge.js.map +1 -1
- package/dist/bird-plate.d.ts +38 -0
- package/dist/bird-plate.js +39 -0
- package/dist/bird-plate.js.map +1 -0
- package/dist/blocks.css +243 -46
- package/dist/card.d.ts +6 -6
- package/dist/card.js +2 -2
- package/dist/card.js.map +1 -1
- package/dist/checkbox.js +1 -1
- package/dist/checkbox.js.map +1 -1
- package/dist/chip-row.js +1 -1
- package/dist/chip-row.js.map +1 -1
- package/dist/chip.js +1 -1
- package/dist/chip.js.map +1 -1
- package/dist/{chunk-QDXV544C.js → chunk-4QH6CP2Y.js} +2 -2
- package/dist/chunk-4QH6CP2Y.js.map +1 -0
- package/dist/{chunk-NJFCSVPT.js → chunk-6EAHUURY.js} +2 -2
- package/dist/chunk-6EAHUURY.js.map +1 -0
- package/dist/{chunk-REPTKUJ6.js → chunk-7NRSPHAF.js} +2 -2
- package/dist/chunk-7NRSPHAF.js.map +1 -0
- package/dist/{chunk-CKSHLSDH.js → chunk-AAP3U66V.js} +2 -2
- package/dist/{chunk-7UCPGXKN.js → chunk-C3NF7V5Y.js} +2 -2
- package/dist/{chunk-7UCPGXKN.js.map → chunk-C3NF7V5Y.js.map} +1 -1
- package/dist/chunk-JFJECOZ2.js +338 -0
- package/dist/chunk-JFJECOZ2.js.map +1 -0
- package/dist/{chunk-KIBICUZK.js → chunk-S5LP7KKW.js} +4 -4
- package/dist/{chunk-3S4GHSPZ.js → chunk-VPO27AFS.js} +5 -5
- package/dist/chunk-VPO27AFS.js.map +1 -0
- package/dist/{chunk-2PNLHZLT.js → chunk-WYQ277F2.js} +3 -3
- package/dist/chunk-WYQ277F2.js.map +1 -0
- package/dist/{chunk-7Z4KHPFC.js → chunk-YGIN2YIY.js} +2 -2
- package/dist/{chunk-7Z4KHPFC.js.map → chunk-YGIN2YIY.js.map} +1 -1
- package/dist/{chunk-LZNZLCOE.js → chunk-YKEIOYJ5.js} +20 -6
- package/dist/chunk-YKEIOYJ5.js.map +1 -0
- package/dist/{chunk-2DGLWTPS.js → chunk-YKOLME7R.js} +3 -3
- package/dist/{chunk-2DGLWTPS.js.map → chunk-YKOLME7R.js.map} +1 -1
- package/dist/code-block.js +4 -4
- package/dist/code-block.js.map +1 -1
- package/dist/combobox.js +2 -2
- package/dist/command.js +3 -3
- package/dist/command.js.map +1 -1
- package/dist/comparison-table.js +2 -2
- package/dist/comparison-table.js.map +1 -1
- package/dist/countdown.js +1 -1
- package/dist/countdown.js.map +1 -1
- package/dist/cover-hero.js +15 -6
- package/dist/cover-hero.js.map +1 -1
- package/dist/dashboard-layout.js +1 -1
- package/dist/dashboard-layout.js.map +1 -1
- package/dist/data-table.js +5 -5
- package/dist/date-picker.js +3 -3
- package/dist/date-picker.js.map +1 -1
- package/dist/description-list.js +1 -1
- package/dist/description-list.js.map +1 -1
- package/dist/detail-layout.js +1 -1
- package/dist/detail-layout.js.map +1 -1
- package/dist/dialog.js +1 -1
- package/dist/docs-nav.js +1 -1
- package/dist/docs-nav.js.map +1 -1
- package/dist/dropdown-menu.js +1 -1
- package/dist/dropdown-menu.js.map +1 -1
- package/dist/file-upload.js +3 -3
- package/dist/file-upload.js.map +1 -1
- package/dist/filter-bar.js +2 -2
- package/dist/footnote.d.ts +32 -0
- package/dist/footnote.js +29 -0
- package/dist/footnote.js.map +1 -0
- package/dist/genui.js +6 -6
- package/dist/geometry-DncHaYP8.d.ts +22 -0
- package/dist/language-switch.js +1 -1
- package/dist/list.js +1 -1
- package/dist/loader.js +2 -2
- package/dist/loader.js.map +1 -1
- package/dist/map.js +95 -83
- package/dist/map.js.map +1 -1
- package/dist/marketing-layout.js +1 -1
- package/dist/marketing-layout.js.map +1 -1
- package/dist/mascot-quote.d.ts +2 -21
- package/dist/mascot-quote.js +9 -338
- package/dist/mascot-quote.js.map +1 -1
- package/dist/match-dialog.js +1 -1
- package/dist/match-dialog.js.map +1 -1
- package/dist/media-grid.js +1 -1
- package/dist/meter.js +3 -3
- package/dist/meter.js.map +1 -1
- package/dist/motion.css +28 -41
- package/dist/native/map.js +94 -82
- package/dist/native/map.js.map +1 -1
- package/dist/native.d.ts +8 -1
- package/dist/native.js +57 -45
- package/dist/native.js.map +1 -1
- package/dist/nav-bar.js +12 -12
- package/dist/nav-bar.js.map +1 -1
- package/dist/page-hero.js +1 -1
- package/dist/page-index.d.ts +30 -0
- package/dist/page-index.js +23 -0
- package/dist/page-index.js.map +1 -0
- package/dist/page.d.ts +19 -7
- package/dist/page.js +1 -1
- package/dist/photo-grid.js +2 -2
- package/dist/photo-grid.js.map +1 -1
- package/dist/progress.js +3 -3
- package/dist/progress.js.map +1 -1
- package/dist/radio-group.js +1 -1
- package/dist/radio-group.js.map +1 -1
- package/dist/rail-nav.js +2 -2
- package/dist/rail-nav.js.map +1 -1
- package/dist/search-field.js +1 -1
- package/dist/service-card.js +3 -3
- package/dist/service-card.js.map +1 -1
- package/dist/settings-layout.js +1 -1
- package/dist/settings-layout.js.map +1 -1
- package/dist/site-footer.js +3 -3
- package/dist/site-footer.js.map +1 -1
- package/dist/skip-link.js +1 -1
- package/dist/slot-picker.js +1 -1
- package/dist/slot-picker.js.map +1 -1
- package/dist/specimen-tag.d.ts +52 -0
- package/dist/specimen-tag.js +33 -0
- package/dist/specimen-tag.js.map +1 -0
- package/dist/stat.js +1 -1
- package/dist/stat.js.map +1 -1
- package/dist/step-progress.js +1 -1
- package/dist/step-progress.js.map +1 -1
- package/dist/sticky-action.d.ts +1 -1
- package/dist/sticky-action.js +1 -1
- package/dist/sticky-action.js.map +1 -1
- package/dist/swipe-deck.js +1 -1
- package/dist/swipe-deck.js.map +1 -1
- package/dist/tabs.js +4 -4
- package/dist/tabs.js.map +1 -1
- package/dist/tag-input.js +2 -2
- package/dist/tag-selector.js +1 -1
- package/dist/toast.js +1 -1
- package/dist/toast.js.map +1 -1
- package/package.json +74 -4
- package/registry/account-menu.json +1 -1
- package/registry/achievement.json +1 -1
- package/registry/action-bar.json +1 -1
- package/registry/activity-feed.json +2 -2
- package/registry/alert.json +1 -1
- package/registry/anatomy-plate.json +168 -0
- package/registry/app-header.json +2 -2
- package/registry/app-shell.json +2 -2
- package/registry/argument-card.json +1 -1
- package/registry/audio-player.json +1 -1
- package/registry/auth-layout.json +1 -1
- package/registry/avatar.json +1 -1
- package/registry/badge.json +2 -2
- package/registry/bird-plate.json +221 -0
- package/registry/button.json +1 -1
- package/registry/captcha.json +1 -1
- package/registry/card-grid.json +1 -1
- package/registry/card.json +2 -2
- package/registry/catalogue-page.json +1 -1
- package/registry/changelog-page.json +1 -1
- package/registry/chat.json +3 -3
- package/registry/checkbox.json +2 -2
- package/registry/chip-link.json +1 -1
- package/registry/chip-row.json +2 -2
- package/registry/chip.json +2 -2
- package/registry/choice-grid.json +1 -1
- package/registry/code-block.json +1 -1
- package/registry/combobox.json +1 -1
- package/registry/command.json +2 -2
- package/registry/comparison-table.json +2 -2
- package/registry/component-page.json +1 -1
- package/registry/confirm-button.json +1 -1
- package/registry/contact-page.json +1 -1
- package/registry/continue-prompt.json +1 -1
- package/registry/countdown.json +2 -2
- package/registry/cover-hero.json +2 -2
- package/registry/cta-panel.json +1 -1
- package/registry/dashboard-layout.json +2 -2
- package/registry/data-table.json +1 -1
- package/registry/date-picker.json +4 -4
- package/registry/decision-list.json +1 -1
- package/registry/description-list.json +2 -2
- package/registry/detail-layout.json +2 -2
- package/registry/dialog.json +2 -2
- package/registry/docs-article-page.json +1 -1
- package/registry/docs-hub-page.json +1 -1
- package/registry/docs-layout.json +1 -1
- package/registry/docs-nav.json +2 -2
- package/registry/document-viewer.json +1 -1
- package/registry/dropdown-menu.json +2 -2
- package/registry/empty-state.json +1 -1
- package/registry/engine-flow.json +1 -1
- package/registry/error-page.json +1 -1
- package/registry/examples/action-sheet/native/video-actions.dark.png +0 -0
- package/registry/examples/action-sheet/native/video-actions.light.png +0 -0
- package/registry/examples/activity-feed/native/notifications.dark.png +0 -0
- package/registry/examples/activity-feed/native/notifications.light.png +0 -0
- package/registry/examples/anatomy-plate/button.d.ts +4 -0
- package/registry/examples/anatomy-plate/button.js +25 -0
- package/registry/examples/anatomy-plate/button.tsx +20 -0
- package/registry/examples/avatar/native/group.dark.png +0 -0
- package/registry/examples/avatar/native/group.light.png +0 -0
- package/registry/examples/avatar/native/presence.dark.png +0 -0
- package/registry/examples/avatar/native/presence.light.png +0 -0
- package/registry/examples/avatar/native/sizes.dark.png +0 -0
- package/registry/examples/avatar/native/sizes.light.png +0 -0
- package/registry/examples/badge/native/dot.dark.png +0 -0
- package/registry/examples/badge/native/dot.light.png +0 -0
- package/registry/examples/badge/native/scrim.dark.png +0 -0
- package/registry/examples/badge/native/scrim.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/bird-plate/flock.d.ts +4 -0
- package/registry/examples/bird-plate/flock.js +24 -0
- package/registry/examples/bird-plate/flock.tsx +18 -0
- package/registry/examples/bird-plate/single.d.ts +4 -0
- package/registry/examples/bird-plate/single.js +19 -0
- package/registry/examples/bird-plate/single.tsx +13 -0
- package/registry/examples/button/native/danger.dark.png +0 -0
- package/registry/examples/button/native/danger.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/ghost.dark.png +0 -0
- package/registry/examples/button/native/ghost.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/outline.dark.png +0 -0
- package/registry/examples/button/native/outline.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/secondary.dark.png +0 -0
- package/registry/examples/button/native/secondary.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/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/native/pressable.dark.png +0 -0
- package/registry/examples/card/native/pressable.light.png +0 -0
- 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/removable.dark.png +0 -0
- package/registry/examples/chip/native/removable.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/combobox/native/multiple.dark.png +0 -0
- package/registry/examples/combobox/native/multiple.light.png +0 -0
- package/registry/examples/combobox/native/search.dark.png +0 -0
- package/registry/examples/combobox/native/search.light.png +0 -0
- package/registry/examples/countdown/native/days.dark.png +0 -0
- package/registry/examples/countdown/native/days.light.png +0 -0
- package/registry/examples/countdown/native/hours.dark.png +0 -0
- package/registry/examples/countdown/native/hours.light.png +0 -0
- package/registry/examples/countdown/native/sizes.dark.png +0 -0
- package/registry/examples/countdown/native/sizes.light.png +0 -0
- package/registry/examples/countdown/native/urgent.dark.png +0 -0
- package/registry/examples/countdown/native/urgent.light.png +0 -0
- package/registry/examples/countdown/native/with-label.dark.png +0 -0
- package/registry/examples/countdown/native/with-label.light.png +0 -0
- package/registry/examples/date-picker/native/birth-date-wheel.dark.png +0 -0
- package/registry/examples/date-picker/native/birth-date-wheel.light.png +0 -0
- package/registry/examples/date-picker/native/birth-date.dark.png +0 -0
- package/registry/examples/date-picker/native/birth-date.light.png +0 -0
- 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/date-picker/native/time-wheel.dark.png +0 -0
- package/registry/examples/date-picker/native/time-wheel.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/dialog/native/sheet.dark.png +0 -0
- package/registry/examples/dialog/native/sheet.light.png +0 -0
- 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/field/native/default.dark.png +0 -0
- package/registry/examples/field/native/default.light.png +0 -0
- package/registry/examples/field/native/select.dark.png +0 -0
- package/registry/examples/field/native/select.light.png +0 -0
- package/registry/examples/footnote/notes.d.ts +4 -0
- package/registry/examples/footnote/notes.js +24 -0
- package/registry/examples/footnote/notes.tsx +19 -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/goal-gauge/native/default.dark.png +0 -0
- package/registry/examples/goal-gauge/native/default.light.png +0 -0
- package/registry/examples/header/native/large-title.dark.png +0 -0
- package/registry/examples/header/native/large-title.light.png +0 -0
- package/registry/examples/image-viewer/native/gallery.dark.png +0 -0
- package/registry/examples/image-viewer/native/gallery.light.png +0 -0
- package/registry/examples/list/native/default.dark.png +0 -0
- package/registry/examples/list/native/default.light.png +0 -0
- package/registry/examples/list/native/infinite.dark.png +0 -0
- package/registry/examples/list/native/infinite.light.png +0 -0
- package/registry/examples/list/native/states.dark.png +0 -0
- package/registry/examples/list/native/states.light.png +0 -0
- package/registry/examples/loader/native/block.dark.png +0 -0
- package/registry/examples/loader/native/block.light.png +0 -0
- package/registry/examples/loader/native/branded.dark.png +0 -0
- package/registry/examples/loader/native/branded.light.png +0 -0
- package/registry/examples/loader/native/job-wait.dark.png +0 -0
- package/registry/examples/loader/native/job-wait.light.png +0 -0
- package/registry/examples/maintenance-screen/native/default.dark.png +0 -0
- package/registry/examples/maintenance-screen/native/default.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/native/places.dark.png +0 -0
- package/registry/examples/map/native/places.light.png +0 -0
- package/registry/examples/markdown/native/ai-stream.dark.png +0 -0
- package/registry/examples/markdown/native/ai-stream.light.png +0 -0
- package/registry/examples/markdown/native/basic.dark.png +0 -0
- package/registry/examples/markdown/native/basic.light.png +0 -0
- 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/media-picker/native/story.dark.png +0 -0
- package/registry/examples/media-picker/native/story.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/number-ticker/native/balance.dark.png +0 -0
- package/registry/examples/number-ticker/native/balance.light.png +0 -0
- 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/otp-input/native/invalid.dark.png +0 -0
- package/registry/examples/otp-input/native/invalid.light.png +0 -0
- package/registry/examples/otp-input/native/verify.dark.png +0 -0
- package/registry/examples/otp-input/native/verify.light.png +0 -0
- package/registry/examples/page-index/docs.d.ts +4 -0
- package/registry/examples/page-index/docs.js +33 -0
- package/registry/examples/page-index/docs.tsx +27 -0
- package/registry/examples/pagination/native/cursor.dark.png +0 -0
- package/registry/examples/pagination/native/cursor.light.png +0 -0
- package/registry/examples/pagination/native/pages.dark.png +0 -0
- package/registry/examples/pagination/native/pages.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/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/pull-to-refresh/native/branded.dark.png +0 -0
- package/registry/examples/pull-to-refresh/native/branded.light.png +0 -0
- package/registry/examples/pull-to-refresh/native/feed.dark.png +0 -0
- package/registry/examples/pull-to-refresh/native/feed.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/radio-group/native/segmented.dark.png +0 -0
- package/registry/examples/radio-group/native/segmented.light.png +0 -0
- package/registry/examples/screen/native/app-shell.dark.png +0 -0
- package/registry/examples/screen/native/app-shell.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/feed.dark.png +0 -0
- package/registry/examples/screen/native/feed.light.png +0 -0
- package/registry/examples/screen/native/map.dark.png +0 -0
- package/registry/examples/screen/native/map.light.png +0 -0
- package/registry/examples/screen/native/notifications.dark.png +0 -0
- package/registry/examples/screen/native/notifications.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/search-field/native/default.dark.png +0 -0
- package/registry/examples/search-field/native/default.light.png +0 -0
- package/registry/examples/segmented/native/default.dark.png +0 -0
- package/registry/examples/segmented/native/default.light.png +0 -0
- package/registry/examples/segmented/native/small.dark.png +0 -0
- package/registry/examples/segmented/native/small.light.png +0 -0
- 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/showcase/native/slides.dark.png +0 -0
- package/registry/examples/showcase/native/slides.light.png +0 -0
- package/registry/examples/skeleton/native/shapes.dark.png +0 -0
- package/registry/examples/skeleton/native/shapes.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/specimen-tag/product.d.ts +4 -0
- package/registry/examples/specimen-tag/product.js +28 -0
- package/registry/examples/specimen-tag/product.tsx +25 -0
- package/registry/examples/specimen-tag/stamps.d.ts +4 -0
- package/registry/examples/specimen-tag/stamps.js +16 -0
- package/registry/examples/specimen-tag/stamps.tsx +13 -0
- package/registry/examples/specimen-tag/wire.d.ts +4 -0
- package/registry/examples/specimen-tag/wire.js +16 -0
- package/registry/examples/specimen-tag/wire.tsx +20 -0
- package/registry/examples/spinner/native/sizes.dark.png +0 -0
- package/registry/examples/spinner/native/sizes.light.png +0 -0
- package/registry/examples/splash-handoff/native/default.dark.png +0 -0
- package/registry/examples/splash-handoff/native/default.light.png +0 -0
- 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/swipe-deck/native/discover.dark.png +0 -0
- package/registry/examples/swipe-deck/native/discover.light.png +0 -0
- package/registry/examples/swipeable-row/native/conversation.dark.png +0 -0
- package/registry/examples/swipeable-row/native/conversation.light.png +0 -0
- 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/tabs/native/segmented.dark.png +0 -0
- package/registry/examples/tabs/native/segmented.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/notification.dark.png +0 -0
- package/registry/examples/toast/native/notification.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/txt/native/variants.dark.png +0 -0
- package/registry/examples/txt/native/variants.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/video-player/native/default.dark.png +0 -0
- package/registry/examples/video-player/native/default.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/field.json +1 -1
- package/registry/file-upload.json +1 -1
- package/registry/filter-bar.json +1 -1
- package/registry/footnote.json +220 -0
- package/registry/force-update-screen.json +1 -1
- package/registry/form.json +3 -3
- package/registry/gallery-page.json +1 -1
- package/registry/genui.json +1 -1
- package/registry/glow-card.json +1 -1
- package/registry/goal-gauge.json +1 -1
- package/registry/how-it-works-page.json +1 -1
- package/registry/image-cropper.json +1 -1
- package/registry/index.json +160 -5
- package/registry/iso.json +1 -1
- package/registry/kanban.json +1 -1
- package/registry/kbd.json +1 -1
- package/registry/landing-page.json +1 -1
- package/registry/landscape.json +1 -1
- package/registry/language-switch.json +2 -2
- package/registry/legal-page.json +1 -1
- package/registry/link-card.json +1 -1
- package/registry/list.json +1 -1
- package/registry/loader.json +2 -2
- package/registry/longform-page.json +1 -1
- package/registry/maintenance-screen.json +1 -1
- package/registry/map.json +3 -3
- package/registry/markdown.json +1 -1
- package/registry/marketing-layout.json +1 -1
- package/registry/marquee.json +1 -1
- package/registry/mascot-quote.json +1 -1
- package/registry/mascot-state.json +1 -1
- package/registry/mascots.json +1 -1
- package/registry/match-dialog.json +1 -1
- package/registry/media-capture.json +1 -1
- package/registry/media-gallery.json +1 -1
- package/registry/media-grid.json +1 -1
- package/registry/meta-row.json +1 -1
- package/registry/meter.json +2 -2
- package/registry/nav-bar.json +3 -3
- package/registry/number-ticker.json +1 -1
- package/registry/opening-hours.json +1 -1
- package/registry/page-header.json +1 -1
- package/registry/page-hero.json +1 -1
- package/registry/page-index.json +168 -0
- package/registry/page.json +89 -5
- package/registry/pagination.json +1 -1
- package/registry/photo-grid.json +2 -2
- package/registry/pointer-light.json +1 -1
- package/registry/popover.json +1 -1
- package/registry/pricing-page.json +1 -1
- package/registry/product-page.json +1 -1
- package/registry/profile-card.json +1 -1
- package/registry/progress.json +2 -2
- package/registry/promise-card.json +1 -1
- package/registry/radio-group.json +2 -2
- package/registry/rail-nav.json +3 -3
- package/registry/recipes.json +1 -1
- package/registry/scroll-stage.json +1 -1
- package/registry/search-field.json +2 -2
- package/registry/section-backdrop.json +1 -1
- package/registry/separator.json +1 -1
- package/registry/service-card.json +2 -2
- package/registry/settings-layout.json +2 -2
- package/registry/showcase.json +1 -1
- package/registry/site-footer.json +3 -3
- package/registry/sites.json +1 -1
- package/registry/skeleton.json +1 -1
- package/registry/skip-link.json +2 -2
- package/registry/slider.json +1 -1
- package/registry/slot-picker.json +2 -2
- package/registry/slot.json +1 -1
- package/registry/specimen-tag.json +269 -0
- package/registry/spinner.json +1 -1
- package/registry/split-view.json +1 -1
- package/registry/stage-banner.json +1 -1
- package/registry/stat.json +2 -2
- package/registry/status-page.json +1 -1
- package/registry/step-progress.json +2 -2
- package/registry/step-showcase.json +1 -1
- package/registry/stepper.json +1 -1
- package/registry/sticky-action.json +2 -2
- package/registry/swipe-deck.json +2 -2
- package/registry/switch.json +1 -1
- package/registry/tabs.json +2 -2
- package/registry/tag-input.json +1 -1
- package/registry/tag-selector.json +1 -1
- package/registry/terminal-replay.json +1 -1
- package/registry/theme.json +1 -1
- package/registry/toast.json +2 -2
- package/registry/toc.json +1 -1
- package/registry/tooltip.json +1 -1
- package/registry/tree-view.json +1 -1
- package/registry/update-prompt.json +1 -1
- package/registry/verified-badge.json +1 -1
- package/registry/vertical-video.json +1 -1
- package/registry/video-player.json +1 -1
- package/registry/video-trimmer.json +1 -1
- package/registry/virtual.json +1 -1
- package/registry/waveform.json +1 -1
- package/dist/chunk-2PNLHZLT.js.map +0 -1
- package/dist/chunk-3S4GHSPZ.js.map +0 -1
- package/dist/chunk-LZNZLCOE.js.map +0 -1
- package/dist/chunk-NJFCSVPT.js.map +0 -1
- package/dist/chunk-QDXV544C.js.map +0 -1
- package/dist/chunk-REPTKUJ6.js.map +0 -1
- /package/dist/{chunk-CKSHLSDH.js.map → chunk-AAP3U66V.js.map} +0 -0
- /package/dist/{chunk-KIBICUZK.js.map → chunk-S5LP7KKW.js.map} +0 -0
package/dist/account-menu.js
CHANGED
|
@@ -98,7 +98,7 @@ var menu = tv2({
|
|
|
98
98
|
content: cn(floating, "min-w-48 max-h-(--radix-dropdown-menu-content-available-height) overflow-y-auto overflow-x-hidden overscroll-contain p-1 origin-(--radix-dropdown-menu-content-transform-origin)"),
|
|
99
99
|
item: "relative flex cursor-pointer select-none items-center gap-2 rounded-md px-2.5 py-2 text-sm text-fg outline-hidden transition-colors data-highlighted:bg-surface-3 data-disabled:pointer-events-none data-disabled:opacity-40 [&>svg]:h-4 [&>svg]:w-4 [&>svg]:shrink-0 [&>svg]:text-fg-secondary",
|
|
100
100
|
indicator: "ml-auto flex h-4 w-4 items-center justify-center text-fg-accent",
|
|
101
|
-
label: "px-2.5 pb-1 pt-2 text-[11px] font-semibold
|
|
101
|
+
label: "px-2.5 pb-1 pt-2 text-[11px] font-semibold text-fg-secondary",
|
|
102
102
|
separator: "-mx-1 my-1 h-px bg-border-subtle"
|
|
103
103
|
},
|
|
104
104
|
variants: {
|
package/dist/account-menu.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/avatar/avatar.tsx","../src/avatar/default-avatar.ts","../src/dropdown-menu/dropdown-menu.tsx","../src/floating.ts","../src/account-menu/account-menu.tsx"],"sourcesContent":["// Client: Radix tracks the image's loading state and shows the fallback until it has loaded (or when it fails).\nimport { Avatar as AvatarPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { type DefaultAvatar, defaultAvatarUrl } from \"./default-avatar\";\n\nconst avatar = tv({\n slots: {\n root: \"relative inline-flex shrink-0 overflow-hidden rounded-full bg-surface-3 align-middle\",\n image: \"h-full w-full object-cover\",\n fallback: \"flex h-full w-full items-center justify-center font-semibold uppercase text-fg-secondary\",\n group: \"flex items-center -space-x-2 [&>*]:ring-2 [&>*]:ring-surface-0\",\n },\n variants: {\n size: {\n xs: { root: \"h-5 w-5 text-[9px]\" },\n sm: { root: \"h-8 w-8 text-xs\" },\n md: { root: \"h-10 w-10 text-sm\" },\n lg: { root: \"h-14 w-14 text-base\" },\n xl: { root: \"h-20 w-20 text-lg\" },\n },\n },\n defaultVariants: { size: \"md\" },\n});\n\nexport type AvatarSize = NonNullable<VariantProps<typeof avatar>[\"size\"]>;\n\nexport type AvatarProps = Omit<React.ComponentProps<\"span\">, \"children\"> & {\n /** The image's URL; `null` or nothing shows the `fallback`. */\n src?: string | null;\n /** The image's text alternative (the person's name); empty when a visible name sits next to the avatar. */\n alt?: string;\n /** Shown while the image loads, when it fails or when there is none: initials, an icon. */\n fallback?: React.ReactNode;\n /** The picture to show instead when `src` is missing or fails to load (an image URL). The `fallback` still covers its own loading. */\n fallbackSrc?: string;\n /**\n * The Krizaka default picture when the person has no photo (or it fails): their brand's gradient and a silhouette,\n * one of six tones from `seed` — `defaultAvatar={{ brand: \"orochia\", seed: user.id }}`. `fallbackSrc` wins over it.\n */\n defaultAvatar?: DefaultAvatar;\n /**\n * xs · sm · md · lg · xl.\n * @default \"md\"\n */\n size?: AvatarSize;\n};\n\nexport function AvatarRoot({ src, alt = \"\", fallback, fallbackSrc, defaultAvatar, size, className, ...props }: AvatarProps) {\n const s = avatar({ size });\n const [failed, setFailed] = React.useState<string | null>(null);\n // 512 px for the big sizes, 256 for the others: sharp on a 2x screen either way.\n const backup = fallbackSrc ?? (defaultAvatar ? defaultAvatarUrl(defaultAvatar, { size: size === \"xl\" || size === \"lg\" ? 512 : 256 }) : undefined);\n const shown = src && failed !== src ? src : backup;\n return (\n <AvatarPrimitive.Root data-size={size ?? \"md\"} className={s.root({ className })} {...props}>\n {shown && (\n <AvatarPrimitive.Image\n src={shown}\n alt={alt}\n className={s.image()}\n onLoadingStatusChange={(status) => {\n if (status === \"error\" && shown === src) setFailed(src);\n }}\n />\n )}\n <AvatarPrimitive.Fallback delayMs={shown ? 300 : 0} className={s.fallback()}>\n {fallback}\n </AvatarPrimitive.Fallback>\n </AvatarPrimitive.Root>\n );\n}\n\nexport type AvatarGroupProps = React.ComponentProps<\"div\"> & {\n /** How many avatars are shown; the rest becomes a `+n` avatar. */\n max?: number;\n /** The size of the `+n` avatar (match the avatars of the group). */\n size?: AvatarSize;\n};\n\n/** Overlapping avatars; past `max`, a `+n` avatar counts the rest. */\nexport function AvatarGroup({ max, size, className, children, ...props }: AvatarGroupProps) {\n const items = React.Children.toArray(children);\n const shown = max === undefined ? items : items.slice(0, max);\n const rest = items.length - shown.length;\n return (\n <div className={avatar().group({ className })} {...props}>\n {shown}\n {rest > 0 && <AvatarRoot size={size} fallback={`+${rest}`} />}\n </div>\n );\n}\n\n/** `Avatar` (an image with a fallback) and `Avatar.Group`. */\nexport const Avatar = Object.assign(AvatarRoot, { Group: AvatarGroup });\n","// The default profile pictures of the Krizaka family: what a product shows when a person has no photo.\n// They are generated from this file (tools/export-brand.mjs renders them to SVG and WebP and uploads them to Bunny,\n// `people/defaults/v1/<brand>/…`), so the pictures and the URLs that name them cannot drift apart.\n\nexport type DefaultAvatarBrand = \"krizaka\" | \"orazaka\" | \"orochia\" | \"yasakia\" | \"yororis\" | \"yomizaka\" | \"kachizaka\" | \"akinaza\";\n/** `person`: a neutral silhouette. `plain`: the brand's gradient alone, ready for initials. */\nexport type DefaultAvatarKind = \"person\" | \"plain\";\nexport type DefaultAvatarSize = 256 | 512;\n\nexport const DEFAULT_AVATAR_BRANDS: readonly DefaultAvatarBrand[] = [\"krizaka\", \"orazaka\", \"orochia\", \"yasakia\", \"yororis\", \"yomizaka\", \"kachizaka\", \"akinaza\"];\nexport const DEFAULT_AVATAR_KINDS: readonly DefaultAvatarKind[] = [\"person\", \"plain\"];\nexport const DEFAULT_AVATAR_SIZES: readonly DefaultAvatarSize[] = [256, 512];\n/** How many tones each brand has: a person always gets the same one from their seed, so a list is not one colour. */\nexport const DEFAULT_AVATAR_TONES = 6;\n\n/** Where the set lives. `v1` is the set's version: a changed picture is a new version folder, never an overwrite. */\nexport const DEFAULT_AVATAR_BASE = \"https://krizaka-assets.b-cdn.net/people/defaults/v1\";\n\nexport interface DefaultAvatar {\n /** The brand of the product showing the avatar: its gradient, its hues. */\n brand: DefaultAvatarBrand;\n /** Any stable text or number of the person (their id): the same seed gives the same tone everywhere. */\n seed?: string | number;\n /** Force a tone, 1 to 6, instead of deriving it from `seed`. */\n tone?: number;\n /**\n * `person` (default): a silhouette. `plain`: the gradient alone.\n * @default \"person\"\n */\n kind?: DefaultAvatarKind;\n}\n\n/** The mark gradient of each brand (BRAND.md, `$extensions[\"com.krizaka\"].mark`) as [hue, saturation %, lightness %] and the hue spread of its tones. */\nconst BRANDS: Record<DefaultAvatarBrand, { from: [number, number, number]; to: [number, number, number]; spread: number }> = {\n krizaka: { from: [217, 92, 53], to: [217, 92, 68], spread: 14 },\n orazaka: { from: [26, 90.5, 37.1], to: [26, 92, 45], spread: 7 },\n orochia: { from: [262.1, 83.3, 57.8], to: [328.6, 85.5, 70.2], spread: 12 },\n yasakia: { from: [156, 92, 32], to: [178, 90, 32], spread: 12 },\n yororis: { from: [34, 46, 44], to: [104, 28, 42], spread: 10 },\n yomizaka: { from: [232, 50, 55], to: [248, 50, 58], spread: 10 },\n kachizaka: { from: [226, 80, 56], to: [44, 92, 33], spread: 10 },\n akinaza: { from: [140, 48, 34], to: [22, 60, 46], spread: 10 },\n};\n\n/** White on top of a stop must stay visible: a stop is darkened until the contrast reaches this (WCAG 1.4.11, non-text). */\nconst MIN_CONTRAST = 3.2;\nconst OFFSETS = [0, -1, 1, -2, 2, 3] as const;\n\nfunction hslToHex([h, s, l]: [number, number, number]): string {\n const sat = s / 100;\n const light = l / 100;\n const k = (n: number) => (n + (((h % 360) + 360) % 360) / 30) % 12;\n const a = sat * Math.min(light, 1 - light);\n const channel = (n: number) => Math.round(255 * (light - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)))));\n return `#${[0, 8, 4].map((n) => channel(n).toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n\nconst channelLuminance = (v: number) => (v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4);\nconst whiteContrast = (hex: string) => {\n const [r, g, b] = [1, 3, 5].map((i) => channelLuminance(Number.parseInt(hex.slice(i, i + 2), 16) / 255));\n return 1.05 / (0.2126 * r! + 0.7152 * g! + 0.0722 * b! + 0.05);\n};\n/** The stop, darkened one point of lightness at a time until white reads on it. */\nfunction readable([h, s, l]: [number, number, number]): string {\n let hex = hslToHex([h, s, l]);\n for (let light = l; whiteContrast(hex) < MIN_CONTRAST && light > 10; light -= 1) hex = hslToHex([h, s, light]);\n return hex;\n}\n\n/** The two stops (hex) of tone `tone` (1 to 6) of a brand. */\nexport function defaultAvatarTone(brand: DefaultAvatarBrand, tone: number): readonly [string, string] {\n const b = BRANDS[brand];\n const o = OFFSETS[(clampTone(tone) - 1) % OFFSETS.length] * b.spread;\n return [readable([b.from[0] + o, b.from[1], b.from[2]]), readable([b.to[0] + o, b.to[1], b.to[2]])];\n}\n\nconst clampTone = (tone: number) => Math.min(DEFAULT_AVATAR_TONES, Math.max(1, Math.round(tone)));\n\n/** 1 to 6 from a seed (FNV-1a): stable across renders, devices and products. */\nexport function defaultAvatarToneOf(seed: string | number | undefined): number {\n if (seed === undefined) return 1;\n let h = 0x811c9dc5;\n for (const ch of String(seed)) {\n h ^= ch.codePointAt(0) ?? 0;\n h = Math.imul(h, 0x01000193) >>> 0;\n }\n return (h % DEFAULT_AVATAR_TONES) + 1;\n}\n\nconst resolveTone = (a: DefaultAvatar) => (a.tone === undefined ? defaultAvatarToneOf(a.seed) : clampTone(a.tone));\n\n/** The file name of a picture in the set: `person-3` + 256 → `person-3-256.webp`; `svg` has no size. */\nexport function defaultAvatarFile(kind: DefaultAvatarKind, tone: number, size: DefaultAvatarSize | \"svg\"): string {\n return size === \"svg\" ? `${kind}-${tone}.svg` : `${kind}-${tone}-${size}.webp`;\n}\n\n/** Linear on purpose: a `/\\/+$/` regex backtracks quadratically on a long run of slashes. */\nfunction trimTrailingSlashes(value: string): string {\n let end = value.length;\n while (end > 0 && value.charCodeAt(end - 1) === 47) end--;\n return value.slice(0, end);\n}\n\n/**\n * The URL of a default avatar: `defaultAvatarUrl({ brand: \"orochia\", seed: user.id })`.\n * @param size 256 for lists, 512 for a profile; `\"svg\"` for the vector.\n * @param base Another host or version of the set (a self-hosted copy); the default is the Krizaka CDN.\n */\nexport function defaultAvatarUrl(avatar: DefaultAvatar, { size = 256, base = DEFAULT_AVATAR_BASE }: { size?: DefaultAvatarSize | \"svg\"; base?: string } = {}): string {\n return `${trimTrailingSlashes(base)}/${avatar.brand}/${defaultAvatarFile(avatar.kind ?? \"person\", resolveTone(avatar), size)}`;\n}\n\n/** The SVG source of a default avatar, 512 × 512 and square (the Avatar clips it round). */\nexport function defaultAvatarSvg(avatar: DefaultAvatar): string {\n const [from, to] = defaultAvatarTone(avatar.brand, resolveTone(avatar));\n const person =\n (avatar.kind ?? \"person\") === \"person\"\n ? `<circle cx=\"256\" cy=\"200\" r=\"86\" fill=\"#fff\" fill-opacity=\".92\"/><path d=\"M84 512c0-108 78-182 172-182s172 74 172 182z\" fill=\"#fff\" fill-opacity=\".92\"/>`\n : \"\";\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\" width=\"512\" height=\"512\"><defs><linearGradient id=\"g\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\"><stop offset=\"0\" stop-color=\"${from}\"/><stop offset=\"1\" stop-color=\"${to}\"/></linearGradient><radialGradient id=\"l\" cx=\".28\" cy=\".18\" r=\".85\"><stop offset=\"0\" stop-color=\"#fff\" stop-opacity=\".24\"/><stop offset=\"1\" stop-color=\"#fff\" stop-opacity=\"0\"/></radialGradient></defs><rect width=\"512\" height=\"512\" fill=\"url(#g)\"/><rect width=\"512\" height=\"512\" fill=\"url(#l)\"/>${person}</svg>`;\n}\n","// Client (a portal). Radix does the roving focus, the arrows, Home/End, typeahead, Escape and the focus return.\nimport { DropdownMenu as MenuPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\nimport { floating } from \"../floating\";\n\nexport const menu = tv({\n slots: {\n content: cn(floating, \"min-w-48 max-h-(--radix-dropdown-menu-content-available-height) overflow-y-auto overflow-x-hidden overscroll-contain p-1 origin-(--radix-dropdown-menu-content-transform-origin)\"),\n item:\n \"relative flex cursor-pointer select-none items-center gap-2 rounded-md px-2.5 py-2 text-sm text-fg outline-hidden transition-colors \" +\n \"data-highlighted:bg-surface-3 data-disabled:pointer-events-none data-disabled:opacity-40 [&>svg]:h-4 [&>svg]:w-4 [&>svg]:shrink-0 \" +\n \"[&>svg]:text-fg-secondary\",\n indicator: \"ml-auto flex h-4 w-4 items-center justify-center text-fg-accent\",\n label: \"px-2.5 pb-1 pt-2 text-[11px] font-semibold uppercase tracking-wider text-fg-secondary\",\n separator: \"-mx-1 my-1 h-px bg-border-subtle\",\n },\n variants: {\n // The label reads in the danger text role (≥ 4.5:1 on the menu surface, both themes); the icon and the highlight\n // carry the danger fill.\n tone: { default: {}, danger: { item: \"text-fg-danger data-highlighted:bg-danger/15 [&>svg]:text-danger\" } },\n },\n defaultVariants: { tone: \"default\" },\n});\nconst s = menu();\n\nexport type DropdownMenuContentProps = React.ComponentProps<typeof MenuPrimitive.Content> & {\n /** The element the portal renders into (default: document.body). */\n container?: HTMLElement | null;\n};\n\nexport function DropdownMenuContent({ sideOffset = 6, align = \"end\", collisionPadding = 8, container, className, ...props }: DropdownMenuContentProps) {\n return (\n <MenuPrimitive.Portal container={container}>\n <MenuPrimitive.Content sideOffset={sideOffset} align={align} collisionPadding={collisionPadding} className={s.content({ className })} {...props} />\n </MenuPrimitive.Portal>\n );\n}\n\nexport type DropdownMenuItemProps = React.ComponentProps<typeof MenuPrimitive.Item> & {\n /** `danger` for a destructive action: the icon and the highlight carry the danger, the label stays legible. */\n tone?: \"default\" | \"danger\";\n};\n\n/** An action. `tone=\"danger\"` for a destructive one; `onSelect` runs it (the menu closes). */\nexport function DropdownMenuItem({ tone, className, ...props }: DropdownMenuItemProps) {\n return <MenuPrimitive.Item data-tone={tone ?? \"default\"} className={menu({ tone }).item({ className })} {...props} />;\n}\n\n/** A toggled option: `checked` + `onCheckedChange`. */\nexport function DropdownMenuCheckboxItem({ className, children, ...props }: React.ComponentProps<typeof MenuPrimitive.CheckboxItem>) {\n return (\n <MenuPrimitive.CheckboxItem className={s.item({ className })} {...props}>\n {children}\n <MenuPrimitive.ItemIndicator className={s.indicator()}>\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </MenuPrimitive.ItemIndicator>\n </MenuPrimitive.CheckboxItem>\n );\n}\n\n/** A submenu's trigger: an item that opens `DropdownMenu.SubContent`, with a chevron. */\nexport function DropdownMenuSubTrigger({ className, children, ...props }: React.ComponentProps<typeof MenuPrimitive.SubTrigger>) {\n return (\n <MenuPrimitive.SubTrigger className={s.item({ className: cn(\"data-[state=open]:bg-surface-3\", className) })} {...props}>\n {children}\n <svg className=\"ml-auto !h-4 !w-4 text-fg-secondary\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </MenuPrimitive.SubTrigger>\n );\n}\n\n/** The submenu's panel (same material as the menu). */\nexport function DropdownMenuSubContent({ sideOffset = 4, collisionPadding = 8, className, ...props }: React.ComponentProps<typeof MenuPrimitive.SubContent>) {\n return (\n <MenuPrimitive.Portal>\n <MenuPrimitive.SubContent sideOffset={sideOffset} collisionPadding={collisionPadding} className={s.content({ className: cn(\"max-h-(--radix-dropdown-menu-content-available-height)\", className) })} {...props} />\n </MenuPrimitive.Portal>\n );\n}\n\n/** One choice among several (`DropdownMenu.RadioGroup` holds the value): a language, a sort order. */\nexport function DropdownMenuRadioItem({ className, children, ...props }: React.ComponentProps<typeof MenuPrimitive.RadioItem>) {\n return (\n <MenuPrimitive.RadioItem className={s.item({ className })} {...props}>\n {children}\n <MenuPrimitive.ItemIndicator className={s.indicator()}>\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </MenuPrimitive.ItemIndicator>\n </MenuPrimitive.RadioItem>\n );\n}\n\nexport function DropdownMenuLabel({ className, ...props }: React.ComponentProps<typeof MenuPrimitive.Label>) {\n return <MenuPrimitive.Label className={s.label({ className })} {...props} />;\n}\n\nexport function DropdownMenuSeparator({ className, ...props }: React.ComponentProps<typeof MenuPrimitive.Separator>) {\n return <MenuPrimitive.Separator className={s.separator({ className })} {...props} />;\n}\n\n/** `DropdownMenu.Root/Trigger/Content/Item/CheckboxItem/Label/Separator/Group`. `open`/`defaultOpen` + `onOpenChange`. */\nexport const DropdownMenu = {\n Root: MenuPrimitive.Root,\n Trigger: MenuPrimitive.Trigger,\n Content: DropdownMenuContent,\n Item: DropdownMenuItem,\n CheckboxItem: DropdownMenuCheckboxItem,\n Label: DropdownMenuLabel,\n Separator: DropdownMenuSeparator,\n Group: MenuPrimitive.Group,\n Sub: MenuPrimitive.Sub,\n SubTrigger: DropdownMenuSubTrigger,\n SubContent: DropdownMenuSubContent,\n RadioGroup: MenuPrimitive.RadioGroup,\n RadioItem: DropdownMenuRadioItem,\n};\n","// The one surface of everything that floats next to its trigger (popover, menu, tooltip): a raised surface, a border,\n// the large shadow and the `kz-pop` entrance from the side Radix measured (`--radix-*-transform-origin`).\nexport const floating =\n \"kz-pop z-50 rounded-lg border border-border-default bg-surface-2 text-fg shadow-lg outline-hidden\";\n","// Client (a portal). The signed-in person's menu under their avatar — who you are first (picture, name, e-mail), then\n// the places (profile, settings), the language as a compact submenu, the extras, and Sign out (a quiet item, not a\n// red alert). Built on DropdownMenu, so the keyboard, the focus return and the collisions are Radix's.\nimport * as React from \"react\";\n\nimport { Avatar, type AvatarProps } from \"../avatar/avatar\";\nimport { cn } from \"../cn\";\nimport { DropdownMenu } from \"../dropdown-menu/dropdown-menu\";\n\nexport interface AccountMenuLink {\n id: string;\n label: string;\n href: string;\n /** A signature icon. */\n icon?: React.ReactNode;\n}\n\nexport interface AccountMenuLanguage {\n /** The row's words (`Language`). */\n label: string;\n /** The current value (`en`). */\n value: string;\n options: readonly { value: string; label: string }[];\n onChange: (value: string) => void;\n}\n\nexport interface AccountMenuProps {\n /** The trigger's accessible name (`Account`). */\n label: string;\n /** Their name; falls back to the e-mail. */\n name?: string;\n /** Their e-mail, shown under the name. */\n email?: string;\n /** The picture: `src`, or `defaultAvatar` (`{ brand, seed }`) for the product's default person. */\n avatar?: Omit<AvatarProps, \"size\">;\n /** Show the name beside the avatar in the trigger from the `sm` breakpoint. */\n showName?: boolean;\n /** Where to go: profile, settings… */\n links?: readonly AccountMenuLink[];\n /** The app's link component (Next's `Link`); an `<a>` otherwise. */\n linkAs?: React.ElementType;\n /** The language, as a submenu that shows the current one. */\n language?: AccountMenuLanguage;\n /** Extra rows between the language and Sign out (a theme switch…). */\n children?: React.ReactNode;\n /** The last row: its words, what it does, an optional icon. */\n signOut: { label: string; onSelect: () => void; icon?: React.ReactNode };\n /** Classes for the trigger. */\n className?: string;\n}\n\n/** The account menu: avatar trigger → identity header, links, language submenu, Sign out. */\nexport function AccountMenu({ label, name, email, avatar, showName = true, links = [], linkAs: Link = \"a\", language, children, signOut, className }: AccountMenuProps) {\n const title = name || email || \"\";\n const current = language?.options.find((o) => o.value === language.value)?.label;\n return (\n <DropdownMenu.Root>\n <DropdownMenu.Trigger\n aria-label={label}\n className={cn(\"flex items-center gap-2.5 rounded-lg p-1 transition-colors duration-150 hover:bg-surface-2 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\", className)}\n >\n <Avatar size=\"sm\" fallback={title.charAt(0).toUpperCase() || \"?\"} {...avatar} />\n {/* Drawn by CSS: the name is not text of the button, so its accessible name stays `label` with no label-content-name-mismatch. */}\n {showName && title && <span aria-hidden data-name={title} className=\"hidden max-w-40 truncate text-sm font-medium text-fg-secondary after:content-[attr(data-name)] sm:inline-block\" />}\n </DropdownMenu.Trigger>\n <DropdownMenu.Content align=\"end\" className=\"w-64\">\n <DropdownMenu.Label className=\"flex items-center gap-3 py-2 normal-case tracking-normal\">\n <Avatar size=\"md\" fallback={title.charAt(0).toUpperCase() || \"?\"} {...avatar} />\n <span className=\"min-w-0\">\n <span className=\"block truncate text-sm font-semibold text-fg\">{title}</span>\n {name && email && <span className=\"block truncate text-xs font-normal text-fg-secondary\">{email}</span>}\n </span>\n </DropdownMenu.Label>\n {links.length > 0 && <DropdownMenu.Separator />}\n {links.map((link) => (\n <DropdownMenu.Item key={link.id} asChild>\n <Link href={link.href}>\n {link.icon}\n {link.label}\n </Link>\n </DropdownMenu.Item>\n ))}\n {(language || children) && <DropdownMenu.Separator />}\n {language && (\n <DropdownMenu.Sub>\n <DropdownMenu.SubTrigger>\n {language.label}\n {current && <span className=\"ml-auto mr-1 text-xs text-fg-secondary\">{current}</span>}\n </DropdownMenu.SubTrigger>\n <DropdownMenu.SubContent>\n <DropdownMenu.RadioGroup value={language.value} onValueChange={language.onChange}>\n {language.options.map((o) => (\n <DropdownMenu.RadioItem key={o.value} value={o.value}>\n {o.label}\n </DropdownMenu.RadioItem>\n ))}\n </DropdownMenu.RadioGroup>\n </DropdownMenu.SubContent>\n </DropdownMenu.Sub>\n )}\n {children}\n <DropdownMenu.Separator />\n <DropdownMenu.Item onSelect={() => signOut.onSelect()}>\n {signOut.icon}\n {signOut.label}\n </DropdownMenu.Item>\n </DropdownMenu.Content>\n </DropdownMenu.Root>\n );\n}\n"],"mappings":"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","names":["avatar","s","tv","jsx","jsxs","tv","jsx","jsxs","avatar"]}
|
|
1
|
+
{"version":3,"sources":["../src/avatar/avatar.tsx","../src/avatar/default-avatar.ts","../src/dropdown-menu/dropdown-menu.tsx","../src/floating.ts","../src/account-menu/account-menu.tsx"],"sourcesContent":["// Client: Radix tracks the image's loading state and shows the fallback until it has loaded (or when it fails).\nimport { Avatar as AvatarPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nimport { type DefaultAvatar, defaultAvatarUrl } from \"./default-avatar\";\n\nconst avatar = tv({\n slots: {\n root: \"relative inline-flex shrink-0 overflow-hidden rounded-full bg-surface-3 align-middle\",\n image: \"h-full w-full object-cover\",\n fallback: \"flex h-full w-full items-center justify-center font-semibold uppercase text-fg-secondary\",\n group: \"flex items-center -space-x-2 [&>*]:ring-2 [&>*]:ring-surface-0\",\n },\n variants: {\n size: {\n xs: { root: \"h-5 w-5 text-[9px]\" },\n sm: { root: \"h-8 w-8 text-xs\" },\n md: { root: \"h-10 w-10 text-sm\" },\n lg: { root: \"h-14 w-14 text-base\" },\n xl: { root: \"h-20 w-20 text-lg\" },\n },\n },\n defaultVariants: { size: \"md\" },\n});\n\nexport type AvatarSize = NonNullable<VariantProps<typeof avatar>[\"size\"]>;\n\nexport type AvatarProps = Omit<React.ComponentProps<\"span\">, \"children\"> & {\n /** The image's URL; `null` or nothing shows the `fallback`. */\n src?: string | null;\n /** The image's text alternative (the person's name); empty when a visible name sits next to the avatar. */\n alt?: string;\n /** Shown while the image loads, when it fails or when there is none: initials, an icon. */\n fallback?: React.ReactNode;\n /** The picture to show instead when `src` is missing or fails to load (an image URL). The `fallback` still covers its own loading. */\n fallbackSrc?: string;\n /**\n * The Krizaka default picture when the person has no photo (or it fails): their brand's gradient and a silhouette,\n * one of six tones from `seed` — `defaultAvatar={{ brand: \"orochia\", seed: user.id }}`. `fallbackSrc` wins over it.\n */\n defaultAvatar?: DefaultAvatar;\n /**\n * xs · sm · md · lg · xl.\n * @default \"md\"\n */\n size?: AvatarSize;\n};\n\nexport function AvatarRoot({ src, alt = \"\", fallback, fallbackSrc, defaultAvatar, size, className, ...props }: AvatarProps) {\n const s = avatar({ size });\n const [failed, setFailed] = React.useState<string | null>(null);\n // 512 px for the big sizes, 256 for the others: sharp on a 2x screen either way.\n const backup = fallbackSrc ?? (defaultAvatar ? defaultAvatarUrl(defaultAvatar, { size: size === \"xl\" || size === \"lg\" ? 512 : 256 }) : undefined);\n const shown = src && failed !== src ? src : backup;\n return (\n <AvatarPrimitive.Root data-size={size ?? \"md\"} className={s.root({ className })} {...props}>\n {shown && (\n <AvatarPrimitive.Image\n src={shown}\n alt={alt}\n className={s.image()}\n onLoadingStatusChange={(status) => {\n if (status === \"error\" && shown === src) setFailed(src);\n }}\n />\n )}\n <AvatarPrimitive.Fallback delayMs={shown ? 300 : 0} className={s.fallback()}>\n {fallback}\n </AvatarPrimitive.Fallback>\n </AvatarPrimitive.Root>\n );\n}\n\nexport type AvatarGroupProps = React.ComponentProps<\"div\"> & {\n /** How many avatars are shown; the rest becomes a `+n` avatar. */\n max?: number;\n /** The size of the `+n` avatar (match the avatars of the group). */\n size?: AvatarSize;\n};\n\n/** Overlapping avatars; past `max`, a `+n` avatar counts the rest. */\nexport function AvatarGroup({ max, size, className, children, ...props }: AvatarGroupProps) {\n const items = React.Children.toArray(children);\n const shown = max === undefined ? items : items.slice(0, max);\n const rest = items.length - shown.length;\n return (\n <div className={avatar().group({ className })} {...props}>\n {shown}\n {rest > 0 && <AvatarRoot size={size} fallback={`+${rest}`} />}\n </div>\n );\n}\n\n/** `Avatar` (an image with a fallback) and `Avatar.Group`. */\nexport const Avatar = Object.assign(AvatarRoot, { Group: AvatarGroup });\n","// The default profile pictures of the Krizaka family: what a product shows when a person has no photo.\n// They are generated from this file (tools/export-brand.mjs renders them to SVG and WebP and uploads them to Bunny,\n// `people/defaults/v1/<brand>/…`), so the pictures and the URLs that name them cannot drift apart.\n\nexport type DefaultAvatarBrand = \"krizaka\" | \"orazaka\" | \"orochia\" | \"yasakia\" | \"yororis\" | \"yomizaka\" | \"kachizaka\" | \"akinaza\";\n/** `person`: a neutral silhouette. `plain`: the brand's gradient alone, ready for initials. */\nexport type DefaultAvatarKind = \"person\" | \"plain\";\nexport type DefaultAvatarSize = 256 | 512;\n\nexport const DEFAULT_AVATAR_BRANDS: readonly DefaultAvatarBrand[] = [\"krizaka\", \"orazaka\", \"orochia\", \"yasakia\", \"yororis\", \"yomizaka\", \"kachizaka\", \"akinaza\"];\nexport const DEFAULT_AVATAR_KINDS: readonly DefaultAvatarKind[] = [\"person\", \"plain\"];\nexport const DEFAULT_AVATAR_SIZES: readonly DefaultAvatarSize[] = [256, 512];\n/** How many tones each brand has: a person always gets the same one from their seed, so a list is not one colour. */\nexport const DEFAULT_AVATAR_TONES = 6;\n\n/** Where the set lives. `v1` is the set's version: a changed picture is a new version folder, never an overwrite. */\nexport const DEFAULT_AVATAR_BASE = \"https://krizaka-assets.b-cdn.net/people/defaults/v1\";\n\nexport interface DefaultAvatar {\n /** The brand of the product showing the avatar: its gradient, its hues. */\n brand: DefaultAvatarBrand;\n /** Any stable text or number of the person (their id): the same seed gives the same tone everywhere. */\n seed?: string | number;\n /** Force a tone, 1 to 6, instead of deriving it from `seed`. */\n tone?: number;\n /**\n * `person` (default): a silhouette. `plain`: the gradient alone.\n * @default \"person\"\n */\n kind?: DefaultAvatarKind;\n}\n\n/** The mark gradient of each brand (BRAND.md, `$extensions[\"com.krizaka\"].mark`) as [hue, saturation %, lightness %] and the hue spread of its tones. */\nconst BRANDS: Record<DefaultAvatarBrand, { from: [number, number, number]; to: [number, number, number]; spread: number }> = {\n krizaka: { from: [217, 92, 53], to: [217, 92, 68], spread: 14 },\n orazaka: { from: [26, 90.5, 37.1], to: [26, 92, 45], spread: 7 },\n orochia: { from: [262.1, 83.3, 57.8], to: [328.6, 85.5, 70.2], spread: 12 },\n yasakia: { from: [156, 92, 32], to: [178, 90, 32], spread: 12 },\n yororis: { from: [34, 46, 44], to: [104, 28, 42], spread: 10 },\n yomizaka: { from: [232, 50, 55], to: [248, 50, 58], spread: 10 },\n kachizaka: { from: [226, 80, 56], to: [44, 92, 33], spread: 10 },\n akinaza: { from: [140, 48, 34], to: [22, 60, 46], spread: 10 },\n};\n\n/** White on top of a stop must stay visible: a stop is darkened until the contrast reaches this (WCAG 1.4.11, non-text). */\nconst MIN_CONTRAST = 3.2;\nconst OFFSETS = [0, -1, 1, -2, 2, 3] as const;\n\nfunction hslToHex([h, s, l]: [number, number, number]): string {\n const sat = s / 100;\n const light = l / 100;\n const k = (n: number) => (n + (((h % 360) + 360) % 360) / 30) % 12;\n const a = sat * Math.min(light, 1 - light);\n const channel = (n: number) => Math.round(255 * (light - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)))));\n return `#${[0, 8, 4].map((n) => channel(n).toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n\nconst channelLuminance = (v: number) => (v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4);\nconst whiteContrast = (hex: string) => {\n const [r, g, b] = [1, 3, 5].map((i) => channelLuminance(Number.parseInt(hex.slice(i, i + 2), 16) / 255));\n return 1.05 / (0.2126 * r! + 0.7152 * g! + 0.0722 * b! + 0.05);\n};\n/** The stop, darkened one point of lightness at a time until white reads on it. */\nfunction readable([h, s, l]: [number, number, number]): string {\n let hex = hslToHex([h, s, l]);\n for (let light = l; whiteContrast(hex) < MIN_CONTRAST && light > 10; light -= 1) hex = hslToHex([h, s, light]);\n return hex;\n}\n\n/** The two stops (hex) of tone `tone` (1 to 6) of a brand. */\nexport function defaultAvatarTone(brand: DefaultAvatarBrand, tone: number): readonly [string, string] {\n const b = BRANDS[brand];\n const o = OFFSETS[(clampTone(tone) - 1) % OFFSETS.length] * b.spread;\n return [readable([b.from[0] + o, b.from[1], b.from[2]]), readable([b.to[0] + o, b.to[1], b.to[2]])];\n}\n\nconst clampTone = (tone: number) => Math.min(DEFAULT_AVATAR_TONES, Math.max(1, Math.round(tone)));\n\n/** 1 to 6 from a seed (FNV-1a): stable across renders, devices and products. */\nexport function defaultAvatarToneOf(seed: string | number | undefined): number {\n if (seed === undefined) return 1;\n let h = 0x811c9dc5;\n for (const ch of String(seed)) {\n h ^= ch.codePointAt(0) ?? 0;\n h = Math.imul(h, 0x01000193) >>> 0;\n }\n return (h % DEFAULT_AVATAR_TONES) + 1;\n}\n\nconst resolveTone = (a: DefaultAvatar) => (a.tone === undefined ? defaultAvatarToneOf(a.seed) : clampTone(a.tone));\n\n/** The file name of a picture in the set: `person-3` + 256 → `person-3-256.webp`; `svg` has no size. */\nexport function defaultAvatarFile(kind: DefaultAvatarKind, tone: number, size: DefaultAvatarSize | \"svg\"): string {\n return size === \"svg\" ? `${kind}-${tone}.svg` : `${kind}-${tone}-${size}.webp`;\n}\n\n/** Linear on purpose: a `/\\/+$/` regex backtracks quadratically on a long run of slashes. */\nfunction trimTrailingSlashes(value: string): string {\n let end = value.length;\n while (end > 0 && value.charCodeAt(end - 1) === 47) end--;\n return value.slice(0, end);\n}\n\n/**\n * The URL of a default avatar: `defaultAvatarUrl({ brand: \"orochia\", seed: user.id })`.\n * @param size 256 for lists, 512 for a profile; `\"svg\"` for the vector.\n * @param base Another host or version of the set (a self-hosted copy); the default is the Krizaka CDN.\n */\nexport function defaultAvatarUrl(avatar: DefaultAvatar, { size = 256, base = DEFAULT_AVATAR_BASE }: { size?: DefaultAvatarSize | \"svg\"; base?: string } = {}): string {\n return `${trimTrailingSlashes(base)}/${avatar.brand}/${defaultAvatarFile(avatar.kind ?? \"person\", resolveTone(avatar), size)}`;\n}\n\n/** The SVG source of a default avatar, 512 × 512 and square (the Avatar clips it round). */\nexport function defaultAvatarSvg(avatar: DefaultAvatar): string {\n const [from, to] = defaultAvatarTone(avatar.brand, resolveTone(avatar));\n const person =\n (avatar.kind ?? \"person\") === \"person\"\n ? `<circle cx=\"256\" cy=\"200\" r=\"86\" fill=\"#fff\" fill-opacity=\".92\"/><path d=\"M84 512c0-108 78-182 172-182s172 74 172 182z\" fill=\"#fff\" fill-opacity=\".92\"/>`\n : \"\";\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\" width=\"512\" height=\"512\"><defs><linearGradient id=\"g\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\"><stop offset=\"0\" stop-color=\"${from}\"/><stop offset=\"1\" stop-color=\"${to}\"/></linearGradient><radialGradient id=\"l\" cx=\".28\" cy=\".18\" r=\".85\"><stop offset=\"0\" stop-color=\"#fff\" stop-opacity=\".24\"/><stop offset=\"1\" stop-color=\"#fff\" stop-opacity=\"0\"/></radialGradient></defs><rect width=\"512\" height=\"512\" fill=\"url(#g)\"/><rect width=\"512\" height=\"512\" fill=\"url(#l)\"/>${person}</svg>`;\n}\n","// Client (a portal). Radix does the roving focus, the arrows, Home/End, typeahead, Escape and the focus return.\nimport { DropdownMenu as MenuPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { cn } from \"../cn\";\nimport { floating } from \"../floating\";\n\nexport const menu = tv({\n slots: {\n content: cn(floating, \"min-w-48 max-h-(--radix-dropdown-menu-content-available-height) overflow-y-auto overflow-x-hidden overscroll-contain p-1 origin-(--radix-dropdown-menu-content-transform-origin)\"),\n item:\n \"relative flex cursor-pointer select-none items-center gap-2 rounded-md px-2.5 py-2 text-sm text-fg outline-hidden transition-colors \" +\n \"data-highlighted:bg-surface-3 data-disabled:pointer-events-none data-disabled:opacity-40 [&>svg]:h-4 [&>svg]:w-4 [&>svg]:shrink-0 \" +\n \"[&>svg]:text-fg-secondary\",\n indicator: \"ml-auto flex h-4 w-4 items-center justify-center text-fg-accent\",\n label: \"px-2.5 pb-1 pt-2 text-[11px] font-semibold text-fg-secondary\",\n separator: \"-mx-1 my-1 h-px bg-border-subtle\",\n },\n variants: {\n // The label reads in the danger text role (≥ 4.5:1 on the menu surface, both themes); the icon and the highlight\n // carry the danger fill.\n tone: { default: {}, danger: { item: \"text-fg-danger data-highlighted:bg-danger/15 [&>svg]:text-danger\" } },\n },\n defaultVariants: { tone: \"default\" },\n});\nconst s = menu();\n\nexport type DropdownMenuContentProps = React.ComponentProps<typeof MenuPrimitive.Content> & {\n /** The element the portal renders into (default: document.body). */\n container?: HTMLElement | null;\n};\n\nexport function DropdownMenuContent({ sideOffset = 6, align = \"end\", collisionPadding = 8, container, className, ...props }: DropdownMenuContentProps) {\n return (\n <MenuPrimitive.Portal container={container}>\n <MenuPrimitive.Content sideOffset={sideOffset} align={align} collisionPadding={collisionPadding} className={s.content({ className })} {...props} />\n </MenuPrimitive.Portal>\n );\n}\n\nexport type DropdownMenuItemProps = React.ComponentProps<typeof MenuPrimitive.Item> & {\n /** `danger` for a destructive action: the icon and the highlight carry the danger, the label stays legible. */\n tone?: \"default\" | \"danger\";\n};\n\n/** An action. `tone=\"danger\"` for a destructive one; `onSelect` runs it (the menu closes). */\nexport function DropdownMenuItem({ tone, className, ...props }: DropdownMenuItemProps) {\n return <MenuPrimitive.Item data-tone={tone ?? \"default\"} className={menu({ tone }).item({ className })} {...props} />;\n}\n\n/** A toggled option: `checked` + `onCheckedChange`. */\nexport function DropdownMenuCheckboxItem({ className, children, ...props }: React.ComponentProps<typeof MenuPrimitive.CheckboxItem>) {\n return (\n <MenuPrimitive.CheckboxItem className={s.item({ className })} {...props}>\n {children}\n <MenuPrimitive.ItemIndicator className={s.indicator()}>\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </MenuPrimitive.ItemIndicator>\n </MenuPrimitive.CheckboxItem>\n );\n}\n\n/** A submenu's trigger: an item that opens `DropdownMenu.SubContent`, with a chevron. */\nexport function DropdownMenuSubTrigger({ className, children, ...props }: React.ComponentProps<typeof MenuPrimitive.SubTrigger>) {\n return (\n <MenuPrimitive.SubTrigger className={s.item({ className: cn(\"data-[state=open]:bg-surface-3\", className) })} {...props}>\n {children}\n <svg className=\"ml-auto !h-4 !w-4 text-fg-secondary\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </MenuPrimitive.SubTrigger>\n );\n}\n\n/** The submenu's panel (same material as the menu). */\nexport function DropdownMenuSubContent({ sideOffset = 4, collisionPadding = 8, className, ...props }: React.ComponentProps<typeof MenuPrimitive.SubContent>) {\n return (\n <MenuPrimitive.Portal>\n <MenuPrimitive.SubContent sideOffset={sideOffset} collisionPadding={collisionPadding} className={s.content({ className: cn(\"max-h-(--radix-dropdown-menu-content-available-height)\", className) })} {...props} />\n </MenuPrimitive.Portal>\n );\n}\n\n/** One choice among several (`DropdownMenu.RadioGroup` holds the value): a language, a sort order. */\nexport function DropdownMenuRadioItem({ className, children, ...props }: React.ComponentProps<typeof MenuPrimitive.RadioItem>) {\n return (\n <MenuPrimitive.RadioItem className={s.item({ className })} {...props}>\n {children}\n <MenuPrimitive.ItemIndicator className={s.indicator()}>\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </MenuPrimitive.ItemIndicator>\n </MenuPrimitive.RadioItem>\n );\n}\n\nexport function DropdownMenuLabel({ className, ...props }: React.ComponentProps<typeof MenuPrimitive.Label>) {\n return <MenuPrimitive.Label className={s.label({ className })} {...props} />;\n}\n\nexport function DropdownMenuSeparator({ className, ...props }: React.ComponentProps<typeof MenuPrimitive.Separator>) {\n return <MenuPrimitive.Separator className={s.separator({ className })} {...props} />;\n}\n\n/** `DropdownMenu.Root/Trigger/Content/Item/CheckboxItem/Label/Separator/Group`. `open`/`defaultOpen` + `onOpenChange`. */\nexport const DropdownMenu = {\n Root: MenuPrimitive.Root,\n Trigger: MenuPrimitive.Trigger,\n Content: DropdownMenuContent,\n Item: DropdownMenuItem,\n CheckboxItem: DropdownMenuCheckboxItem,\n Label: DropdownMenuLabel,\n Separator: DropdownMenuSeparator,\n Group: MenuPrimitive.Group,\n Sub: MenuPrimitive.Sub,\n SubTrigger: DropdownMenuSubTrigger,\n SubContent: DropdownMenuSubContent,\n RadioGroup: MenuPrimitive.RadioGroup,\n RadioItem: DropdownMenuRadioItem,\n};\n","// The one surface of everything that floats next to its trigger (popover, menu, tooltip): a raised surface, a border,\n// the large shadow and the `kz-pop` entrance from the side Radix measured (`--radix-*-transform-origin`).\nexport const floating =\n \"kz-pop z-50 rounded-lg border border-border-default bg-surface-2 text-fg shadow-lg outline-hidden\";\n","// Client (a portal). The signed-in person's menu under their avatar — who you are first (picture, name, e-mail), then\n// the places (profile, settings), the language as a compact submenu, the extras, and Sign out (a quiet item, not a\n// red alert). Built on DropdownMenu, so the keyboard, the focus return and the collisions are Radix's.\nimport * as React from \"react\";\n\nimport { Avatar, type AvatarProps } from \"../avatar/avatar\";\nimport { cn } from \"../cn\";\nimport { DropdownMenu } from \"../dropdown-menu/dropdown-menu\";\n\nexport interface AccountMenuLink {\n id: string;\n label: string;\n href: string;\n /** A signature icon. */\n icon?: React.ReactNode;\n}\n\nexport interface AccountMenuLanguage {\n /** The row's words (`Language`). */\n label: string;\n /** The current value (`en`). */\n value: string;\n options: readonly { value: string; label: string }[];\n onChange: (value: string) => void;\n}\n\nexport interface AccountMenuProps {\n /** The trigger's accessible name (`Account`). */\n label: string;\n /** Their name; falls back to the e-mail. */\n name?: string;\n /** Their e-mail, shown under the name. */\n email?: string;\n /** The picture: `src`, or `defaultAvatar` (`{ brand, seed }`) for the product's default person. */\n avatar?: Omit<AvatarProps, \"size\">;\n /** Show the name beside the avatar in the trigger from the `sm` breakpoint. */\n showName?: boolean;\n /** Where to go: profile, settings… */\n links?: readonly AccountMenuLink[];\n /** The app's link component (Next's `Link`); an `<a>` otherwise. */\n linkAs?: React.ElementType;\n /** The language, as a submenu that shows the current one. */\n language?: AccountMenuLanguage;\n /** Extra rows between the language and Sign out (a theme switch…). */\n children?: React.ReactNode;\n /** The last row: its words, what it does, an optional icon. */\n signOut: { label: string; onSelect: () => void; icon?: React.ReactNode };\n /** Classes for the trigger. */\n className?: string;\n}\n\n/** The account menu: avatar trigger → identity header, links, language submenu, Sign out. */\nexport function AccountMenu({ label, name, email, avatar, showName = true, links = [], linkAs: Link = \"a\", language, children, signOut, className }: AccountMenuProps) {\n const title = name || email || \"\";\n const current = language?.options.find((o) => o.value === language.value)?.label;\n return (\n <DropdownMenu.Root>\n <DropdownMenu.Trigger\n aria-label={label}\n className={cn(\"flex items-center gap-2.5 rounded-lg p-1 transition-colors duration-150 hover:bg-surface-2 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\", className)}\n >\n <Avatar size=\"sm\" fallback={title.charAt(0).toUpperCase() || \"?\"} {...avatar} />\n {/* Drawn by CSS: the name is not text of the button, so its accessible name stays `label` with no label-content-name-mismatch. */}\n {showName && title && <span aria-hidden data-name={title} className=\"hidden max-w-40 truncate text-sm font-medium text-fg-secondary after:content-[attr(data-name)] sm:inline-block\" />}\n </DropdownMenu.Trigger>\n <DropdownMenu.Content align=\"end\" className=\"w-64\">\n <DropdownMenu.Label className=\"flex items-center gap-3 py-2 normal-case tracking-normal\">\n <Avatar size=\"md\" fallback={title.charAt(0).toUpperCase() || \"?\"} {...avatar} />\n <span className=\"min-w-0\">\n <span className=\"block truncate text-sm font-semibold text-fg\">{title}</span>\n {name && email && <span className=\"block truncate text-xs font-normal text-fg-secondary\">{email}</span>}\n </span>\n </DropdownMenu.Label>\n {links.length > 0 && <DropdownMenu.Separator />}\n {links.map((link) => (\n <DropdownMenu.Item key={link.id} asChild>\n <Link href={link.href}>\n {link.icon}\n {link.label}\n </Link>\n </DropdownMenu.Item>\n ))}\n {(language || children) && <DropdownMenu.Separator />}\n {language && (\n <DropdownMenu.Sub>\n <DropdownMenu.SubTrigger>\n {language.label}\n {current && <span className=\"ml-auto mr-1 text-xs text-fg-secondary\">{current}</span>}\n </DropdownMenu.SubTrigger>\n <DropdownMenu.SubContent>\n <DropdownMenu.RadioGroup value={language.value} onValueChange={language.onChange}>\n {language.options.map((o) => (\n <DropdownMenu.RadioItem key={o.value} value={o.value}>\n {o.label}\n </DropdownMenu.RadioItem>\n ))}\n </DropdownMenu.RadioGroup>\n </DropdownMenu.SubContent>\n </DropdownMenu.Sub>\n )}\n {children}\n <DropdownMenu.Separator />\n <DropdownMenu.Item onSelect={() => signOut.onSelect()}>\n {signOut.icon}\n {signOut.label}\n </DropdownMenu.Item>\n </DropdownMenu.Content>\n </DropdownMenu.Root>\n );\n}\n"],"mappings":"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","names":["avatar","s","tv","jsx","jsxs","tv","jsx","jsxs","avatar"]}
|
package/dist/activity-feed.js
CHANGED
|
@@ -54,14 +54,14 @@ var activityFeed = tv({
|
|
|
54
54
|
slots: {
|
|
55
55
|
root: "flex flex-col gap-5",
|
|
56
56
|
group: "flex flex-col gap-1",
|
|
57
|
-
heading: "sticky top-0 z-10 bg-surface-0
|
|
57
|
+
heading: "sticky top-0 z-10 bg-surface-0 py-1 text-xs font-semibold text-fg-secondary",
|
|
58
58
|
ol: "m-0 flex list-none flex-col p-0",
|
|
59
59
|
item: "relative flex gap-3 py-2.5",
|
|
60
60
|
marker: "relative mt-1 flex h-2.5 w-2.5 shrink-0 rounded-full bg-[var(--kz-af-tone)] ring-4 ring-surface-0",
|
|
61
61
|
rail: "absolute left-[4.5px] top-5 -bottom-1 w-px bg-border-default",
|
|
62
62
|
body: "flex min-w-0 flex-1 flex-col",
|
|
63
63
|
title: "text-sm text-fg",
|
|
64
|
-
count: "ml-1.5 inline-flex rounded-
|
|
64
|
+
count: "ml-1.5 inline-flex rounded-sm bg-surface-3 px-1.5 text-xs font-semibold tabular-nums text-fg-secondary",
|
|
65
65
|
description: "text-xs text-fg-secondary",
|
|
66
66
|
time: "shrink-0 pt-0.5 text-xs tabular-nums text-fg-secondary",
|
|
67
67
|
leading: "shrink-0"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/activity-feed/activity-feed.tsx","../src/activity-feed/group.ts"],"sourcesContent":["// Client (the clock of “… ago”, the announcement of new events). What happened, newest first: an activity feed\n// grouped by day with folded runs and relative times, announced politely when it is live; and a `Timeline` — one\n// thing's history on a rail, oldest first. Words are props.\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { type ActivityEvent, type ActivityTone, groupActivity, timeAgo } from \"./group\";\n\nexport const activityFeed = tv({\n slots: {\n root: \"flex flex-col gap-5\",\n group: \"flex flex-col gap-1\",\n heading: \"sticky top-0 z-10 bg-surface-0/90 py-1 text-xs font-semibold uppercase tracking-wider text-fg-secondary backdrop-blur-sm\",\n ol: \"m-0 flex list-none flex-col p-0\",\n item: \"relative flex gap-3 py-2.5\",\n marker: \"relative mt-1 flex h-2.5 w-2.5 shrink-0 rounded-full bg-[var(--kz-af-tone)] ring-4 ring-surface-0\",\n rail: \"absolute left-[4.5px] top-5 -bottom-1 w-px bg-border-default\",\n body: \"flex min-w-0 flex-1 flex-col\",\n title: \"text-sm text-fg\",\n count: \"ml-1.5 inline-flex rounded-full bg-surface-3 px-1.5 text-xs font-semibold tabular-nums text-fg-secondary\",\n description: \"text-xs text-fg-secondary\",\n time: \"shrink-0 pt-0.5 text-xs tabular-nums text-fg-secondary\",\n leading: \"shrink-0\",\n },\n});\n\nconst TONE: Record<ActivityTone, string> = {\n neutral: \"var(--kz-border-strong)\",\n accent: \"var(--kz-accent)\",\n success: \"var(--kz-success)\",\n warning: \"var(--kz-warning)\",\n danger: \"var(--kz-danger)\",\n info: \"var(--kz-info)\",\n};\n\nexport type ActivityFeedLabels = {\n /** The heading of today's events. */\n today: string;\n /** The heading of yesterday's. */\n yesterday: string;\n /** A folded run: `(n) => \"×\" + n` (“3 times”). */\n times: (count: number) => string;\n /** Announced when new events arrive in a live feed: `(n) => n + \" new events\"`. */\n newEvents: (count: number) => string;\n};\n\nexport const activityFeedLabels: ActivityFeedLabels = {\n today: \"Today\",\n yesterday: \"Yesterday\",\n times: (count) => `×${count}`,\n newEvents: (count) => (count === 1 ? \"1 new event\" : `${count} new events`),\n};\n\nexport type ActivityFeedProps<T extends ActivityEvent> = {\n /** The events, in any order. */\n events: readonly T[];\n /** The feed's accessible name (“Activity”). */\n \"aria-label\": string;\n /** The locale of the dates and the “… ago”. Default `en-US`. */\n locale?: string;\n /** Now, for the “… ago” and the day headings (pass it from the server for the same text on both sides). */\n now?: Date;\n /** Refresh the “… ago” every minute. Default true. */\n ticking?: boolean;\n /** Fold the runs of the same actor and kind. Default true. */\n fold?: boolean;\n /** Announce new events politely (a stream). */\n live?: boolean;\n /** Before an event's text: its actor's `Avatar`, an icon. */\n leading?: (event: T) => React.ReactNode;\n /** After an event's text: an action (Reply, View). */\n action?: (event: T) => React.ReactNode;\n /** The words, passed translated; English by default. */\n labels?: Partial<ActivityFeedLabels>;\n /** Shown when there is no event (an `EmptyState`). */\n empty?: React.ReactNode;\n /** Classes merged on the root. */\n className?: string;\n};\n\nfunction useNow(initial: Date | undefined, ticking: boolean) {\n const [now, setNow] = React.useState(() => initial ?? new Date());\n React.useEffect(() => {\n if (!ticking) return;\n const timer = setInterval(() => setNow(new Date()), 60_000);\n return () => clearInterval(timer);\n }, [ticking]);\n return now;\n}\n\n/**\n * What happened, newest first: grouped by day, runs folded (“Mina tipped ×3”), “2 min ago” kept fresh, new events\n * announced when `live`. Orazaka's job activity, Orochia's notifications, the console's audit log.\n */\nexport function ActivityFeed<T extends ActivityEvent>({ events, \"aria-label\": label, locale = \"en-US\", now: given, ticking = true, fold = true, live, leading, action, labels: words, empty, className }: ActivityFeedProps<T>) {\n const labels = { ...activityFeedLabels, ...words };\n const s = activityFeed();\n const now = useNow(given, ticking && !given);\n const groups = groupActivity(events, { now, locale, today: labels.today, yesterday: labels.yesterday, fold });\n const seen = React.useRef(events.length);\n const [announcement, setAnnouncement] = React.useState(\"\");\n React.useEffect(() => {\n const added = events.length - seen.current;\n seen.current = events.length;\n if (!live || added <= 0) return;\n const timer = setTimeout(() => setAnnouncement(labels.newEvents(added)), 0);\n return () => clearTimeout(timer);\n }, [events.length, live]); // eslint-disable-line react-hooks/exhaustive-deps -- announce on a new count only\n return (\n <section aria-label={label} className={s.root({ className })}>\n {live ? (\n <p className=\"sr-only\" aria-live=\"polite\">\n {announcement}\n </p>\n ) : null}\n {groups.length === 0 && empty ? empty : null}\n {groups.map((group) => (\n <div key={group.label} className={s.group()}>\n <h3 className={s.heading()}>{group.label}</h3>\n <ol className={s.ol()}>\n {group.events.map(({ event, count }) => {\n const date = new Date(event.at);\n return (\n <li key={event.id} className={s.item()} style={{ [\"--kz-af-tone\" as string]: TONE[event.tone ?? \"neutral\"] }}>\n {leading ? <span className={s.leading()}>{leading(event)}</span> : <span aria-hidden className={s.marker()} />}\n <span className={s.body()}>\n <span className={s.title()}>\n {event.title}\n {count > 1 ? <span className={s.count()}>{labels.times(count)}</span> : null}\n </span>\n {event.description ? <span className={s.description()}>{event.description}</span> : null}\n </span>\n <time dateTime={date.toISOString()} title={date.toLocaleString(locale)} className={s.time()}>\n {timeAgo(date, { now, locale })}\n </time>\n {action ? <span className=\"shrink-0\">{action(event)}</span> : null}\n </li>\n );\n })}\n </ol>\n </div>\n ))}\n </section>\n );\n}\n\nexport type TimelineProps<T extends ActivityEvent> = {\n /** One thing's events, oldest first (a job's run, a payout's life). */\n events: readonly T[];\n /** The timeline's accessible name (“History of the payout”). */\n \"aria-label\": string;\n /** The locale of the times. Default `en-US`. */\n locale?: string;\n /** How a time is written: `time` (14:02, default), `datetime`, `date`. */\n timeStyle?: \"time\" | \"datetime\" | \"date\";\n /** Classes merged on the list. */\n className?: string;\n};\n\n/** One thing's history on a rail, oldest first: each event a marker in its tone, its time and its words. */\nexport function Timeline<T extends ActivityEvent>({ events, \"aria-label\": label, locale = \"en-US\", timeStyle = \"time\", className }: TimelineProps<T>) {\n const s = activityFeed();\n const options: Intl.DateTimeFormatOptions =\n timeStyle === \"time\" ? { hour: \"2-digit\", minute: \"2-digit\" } : timeStyle === \"date\" ? { dateStyle: \"medium\" } : { dateStyle: \"medium\", timeStyle: \"short\" };\n const write = new Intl.DateTimeFormat(locale, options);\n return (\n <ol aria-label={label} className={s.ol({ className })}>\n {events.map((event, i) => {\n const date = new Date(event.at);\n return (\n <li key={event.id} className={s.item()} style={{ [\"--kz-af-tone\" as string]: TONE[event.tone ?? \"neutral\"] }}>\n <span aria-hidden className={s.marker()} />\n {i < events.length - 1 ? <span aria-hidden className={s.rail()} /> : null}\n <span className={s.body()}>\n <span className={s.title()}>{event.title}</span>\n {event.description ? <span className={s.description()}>{event.description}</span> : null}\n </span>\n <time dateTime={date.toISOString()} className={s.time()}>\n {write.format(date)}\n </time>\n </li>\n );\n })}\n </ol>\n );\n}\n","// Shared by the web and React Native ActivityFeed: the events grouped by day (“Today”, “Yesterday”, a date), the same\n// events of one actor in a row folded into one (“Mina tipped 3 times”), and how long ago each happened — Intl only.\n\nexport type ActivityTone = \"neutral\" | \"accent\" | \"success\" | \"warning\" | \"danger\" | \"info\";\n\nexport type ActivityEvent = {\n /** A stable id. */\n id: string;\n /** When it happened: an ISO date-time, an epoch in ms or a `Date`. */\n at: string | number | Date;\n /** What happened, in a sentence — passed translated (“Mina tipped your video $5”). */\n title: string;\n /** A detail under it. */\n description?: string;\n /** Who did it (folds a run of the same `kind` by the same actor). */\n actor?: string;\n /** The kind of event (`tip`, `bid`, `job.done`): with `actor`, what folds. */\n kind?: string;\n /** The tone of its marker: success for a payout, danger for a failure. */\n tone?: ActivityTone;\n};\n\nexport type ActivityGroup<T extends ActivityEvent> = {\n /** The day's heading (“Today”, “Yesterday”, “Monday, Oct 6”). */\n label: string;\n /** Its events, newest first; a folded run is its newest event with `count` > 1. */\n events: { event: T; count: number }[];\n};\n\nconst dayKey = (date: Date) => `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`;\n\n/** Groups events (any order) by day, newest first, folding runs of the same actor and kind when `fold` is set. */\nexport function groupActivity<T extends ActivityEvent>(\n events: readonly T[],\n { now = new Date(), locale = \"en-US\", today = \"Today\", yesterday = \"Yesterday\", fold = true }: { now?: Date; locale?: string; today?: string; yesterday?: string; fold?: boolean } = {},\n): ActivityGroup<T>[] {\n const sorted = [...events].sort((a, b) => new Date(b.at).getTime() - new Date(a.at).getTime());\n const day = new Intl.DateTimeFormat(locale, { weekday: \"long\", month: \"short\", day: \"numeric\" });\n const todayKey = dayKey(now);\n const yesterdayKey = dayKey(new Date(now.getTime() - 86_400_000));\n const groups: ActivityGroup<T>[] = [];\n let current: ActivityGroup<T> | null = null;\n let currentKey = \"\";\n for (const event of sorted) {\n const date = new Date(event.at);\n const key = dayKey(date);\n if (!current || key !== currentKey) {\n currentKey = key;\n current = { label: key === todayKey ? today : key === yesterdayKey ? yesterday : day.format(date), events: [] };\n groups.push(current);\n }\n const last = current.events[current.events.length - 1];\n if (fold && last && event.actor && event.kind && last.event.actor === event.actor && last.event.kind === event.kind) last.count += 1;\n else current.events.push({ event, count: 1 });\n }\n return groups;\n}\n\nconst UNITS: [Intl.RelativeTimeFormatUnit, number][] = [\n [\"second\", 60],\n [\"minute\", 60],\n [\"hour\", 24],\n [\"day\", 7],\n [\"week\", 4.35],\n [\"month\", 12],\n [\"year\", Number.POSITIVE_INFINITY],\n];\n\n/** “2 minutes ago”, “yesterday”: how long ago, in the largest unit that reads well (`style` short: “2 min. ago”). */\nexport function timeAgo(at: string | number | Date, { now = new Date(), locale = \"en-US\", style = \"short\" }: { now?: Date; locale?: string; style?: \"long\" | \"short\" | \"narrow\" } = {}): string {\n let value = (new Date(at).getTime() - now.getTime()) / 1000;\n let unit: Intl.RelativeTimeFormatUnit = \"second\";\n for (const [name, size] of UNITS) {\n unit = name;\n if (Math.abs(value) < size - 0.5) break;\n value /= size;\n }\n return new Intl.RelativeTimeFormat(locale, { numeric: \"auto\", style }).format(Math.round(value) + 0, unit);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/activity-feed/activity-feed.tsx","../src/activity-feed/group.ts"],"sourcesContent":["// Client (the clock of “… ago”, the announcement of new events). What happened, newest first: an activity feed\n// grouped by day with folded runs and relative times, announced politely when it is live; and a `Timeline` — one\n// thing's history on a rail, oldest first. Words are props.\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nimport { type ActivityEvent, type ActivityTone, groupActivity, timeAgo } from \"./group\";\n\nexport const activityFeed = tv({\n slots: {\n root: \"flex flex-col gap-5\",\n group: \"flex flex-col gap-1\",\n heading: \"sticky top-0 z-10 bg-surface-0 py-1 text-xs font-semibold text-fg-secondary\",\n ol: \"m-0 flex list-none flex-col p-0\",\n item: \"relative flex gap-3 py-2.5\",\n marker: \"relative mt-1 flex h-2.5 w-2.5 shrink-0 rounded-full bg-[var(--kz-af-tone)] ring-4 ring-surface-0\",\n rail: \"absolute left-[4.5px] top-5 -bottom-1 w-px bg-border-default\",\n body: \"flex min-w-0 flex-1 flex-col\",\n title: \"text-sm text-fg\",\n count: \"ml-1.5 inline-flex rounded-sm bg-surface-3 px-1.5 text-xs font-semibold tabular-nums text-fg-secondary\",\n description: \"text-xs text-fg-secondary\",\n time: \"shrink-0 pt-0.5 text-xs tabular-nums text-fg-secondary\",\n leading: \"shrink-0\",\n },\n});\n\nconst TONE: Record<ActivityTone, string> = {\n neutral: \"var(--kz-border-strong)\",\n accent: \"var(--kz-accent)\",\n success: \"var(--kz-success)\",\n warning: \"var(--kz-warning)\",\n danger: \"var(--kz-danger)\",\n info: \"var(--kz-info)\",\n};\n\nexport type ActivityFeedLabels = {\n /** The heading of today's events. */\n today: string;\n /** The heading of yesterday's. */\n yesterday: string;\n /** A folded run: `(n) => \"×\" + n` (“3 times”). */\n times: (count: number) => string;\n /** Announced when new events arrive in a live feed: `(n) => n + \" new events\"`. */\n newEvents: (count: number) => string;\n};\n\nexport const activityFeedLabels: ActivityFeedLabels = {\n today: \"Today\",\n yesterday: \"Yesterday\",\n times: (count) => `×${count}`,\n newEvents: (count) => (count === 1 ? \"1 new event\" : `${count} new events`),\n};\n\nexport type ActivityFeedProps<T extends ActivityEvent> = {\n /** The events, in any order. */\n events: readonly T[];\n /** The feed's accessible name (“Activity”). */\n \"aria-label\": string;\n /** The locale of the dates and the “… ago”. Default `en-US`. */\n locale?: string;\n /** Now, for the “… ago” and the day headings (pass it from the server for the same text on both sides). */\n now?: Date;\n /** Refresh the “… ago” every minute. Default true. */\n ticking?: boolean;\n /** Fold the runs of the same actor and kind. Default true. */\n fold?: boolean;\n /** Announce new events politely (a stream). */\n live?: boolean;\n /** Before an event's text: its actor's `Avatar`, an icon. */\n leading?: (event: T) => React.ReactNode;\n /** After an event's text: an action (Reply, View). */\n action?: (event: T) => React.ReactNode;\n /** The words, passed translated; English by default. */\n labels?: Partial<ActivityFeedLabels>;\n /** Shown when there is no event (an `EmptyState`). */\n empty?: React.ReactNode;\n /** Classes merged on the root. */\n className?: string;\n};\n\nfunction useNow(initial: Date | undefined, ticking: boolean) {\n const [now, setNow] = React.useState(() => initial ?? new Date());\n React.useEffect(() => {\n if (!ticking) return;\n const timer = setInterval(() => setNow(new Date()), 60_000);\n return () => clearInterval(timer);\n }, [ticking]);\n return now;\n}\n\n/**\n * What happened, newest first: grouped by day, runs folded (“Mina tipped ×3”), “2 min ago” kept fresh, new events\n * announced when `live`. Orazaka's job activity, Orochia's notifications, the console's audit log.\n */\nexport function ActivityFeed<T extends ActivityEvent>({ events, \"aria-label\": label, locale = \"en-US\", now: given, ticking = true, fold = true, live, leading, action, labels: words, empty, className }: ActivityFeedProps<T>) {\n const labels = { ...activityFeedLabels, ...words };\n const s = activityFeed();\n const now = useNow(given, ticking && !given);\n const groups = groupActivity(events, { now, locale, today: labels.today, yesterday: labels.yesterday, fold });\n const seen = React.useRef(events.length);\n const [announcement, setAnnouncement] = React.useState(\"\");\n React.useEffect(() => {\n const added = events.length - seen.current;\n seen.current = events.length;\n if (!live || added <= 0) return;\n const timer = setTimeout(() => setAnnouncement(labels.newEvents(added)), 0);\n return () => clearTimeout(timer);\n }, [events.length, live]); // eslint-disable-line react-hooks/exhaustive-deps -- announce on a new count only\n return (\n <section aria-label={label} className={s.root({ className })}>\n {live ? (\n <p className=\"sr-only\" aria-live=\"polite\">\n {announcement}\n </p>\n ) : null}\n {groups.length === 0 && empty ? empty : null}\n {groups.map((group) => (\n <div key={group.label} className={s.group()}>\n <h3 className={s.heading()}>{group.label}</h3>\n <ol className={s.ol()}>\n {group.events.map(({ event, count }) => {\n const date = new Date(event.at);\n return (\n <li key={event.id} className={s.item()} style={{ [\"--kz-af-tone\" as string]: TONE[event.tone ?? \"neutral\"] }}>\n {leading ? <span className={s.leading()}>{leading(event)}</span> : <span aria-hidden className={s.marker()} />}\n <span className={s.body()}>\n <span className={s.title()}>\n {event.title}\n {count > 1 ? <span className={s.count()}>{labels.times(count)}</span> : null}\n </span>\n {event.description ? <span className={s.description()}>{event.description}</span> : null}\n </span>\n <time dateTime={date.toISOString()} title={date.toLocaleString(locale)} className={s.time()}>\n {timeAgo(date, { now, locale })}\n </time>\n {action ? <span className=\"shrink-0\">{action(event)}</span> : null}\n </li>\n );\n })}\n </ol>\n </div>\n ))}\n </section>\n );\n}\n\nexport type TimelineProps<T extends ActivityEvent> = {\n /** One thing's events, oldest first (a job's run, a payout's life). */\n events: readonly T[];\n /** The timeline's accessible name (“History of the payout”). */\n \"aria-label\": string;\n /** The locale of the times. Default `en-US`. */\n locale?: string;\n /** How a time is written: `time` (14:02, default), `datetime`, `date`. */\n timeStyle?: \"time\" | \"datetime\" | \"date\";\n /** Classes merged on the list. */\n className?: string;\n};\n\n/** One thing's history on a rail, oldest first: each event a marker in its tone, its time and its words. */\nexport function Timeline<T extends ActivityEvent>({ events, \"aria-label\": label, locale = \"en-US\", timeStyle = \"time\", className }: TimelineProps<T>) {\n const s = activityFeed();\n const options: Intl.DateTimeFormatOptions =\n timeStyle === \"time\" ? { hour: \"2-digit\", minute: \"2-digit\" } : timeStyle === \"date\" ? { dateStyle: \"medium\" } : { dateStyle: \"medium\", timeStyle: \"short\" };\n const write = new Intl.DateTimeFormat(locale, options);\n return (\n <ol aria-label={label} className={s.ol({ className })}>\n {events.map((event, i) => {\n const date = new Date(event.at);\n return (\n <li key={event.id} className={s.item()} style={{ [\"--kz-af-tone\" as string]: TONE[event.tone ?? \"neutral\"] }}>\n <span aria-hidden className={s.marker()} />\n {i < events.length - 1 ? <span aria-hidden className={s.rail()} /> : null}\n <span className={s.body()}>\n <span className={s.title()}>{event.title}</span>\n {event.description ? <span className={s.description()}>{event.description}</span> : null}\n </span>\n <time dateTime={date.toISOString()} className={s.time()}>\n {write.format(date)}\n </time>\n </li>\n );\n })}\n </ol>\n );\n}\n","// Shared by the web and React Native ActivityFeed: the events grouped by day (“Today”, “Yesterday”, a date), the same\n// events of one actor in a row folded into one (“Mina tipped 3 times”), and how long ago each happened — Intl only.\n\nexport type ActivityTone = \"neutral\" | \"accent\" | \"success\" | \"warning\" | \"danger\" | \"info\";\n\nexport type ActivityEvent = {\n /** A stable id. */\n id: string;\n /** When it happened: an ISO date-time, an epoch in ms or a `Date`. */\n at: string | number | Date;\n /** What happened, in a sentence — passed translated (“Mina tipped your video $5”). */\n title: string;\n /** A detail under it. */\n description?: string;\n /** Who did it (folds a run of the same `kind` by the same actor). */\n actor?: string;\n /** The kind of event (`tip`, `bid`, `job.done`): with `actor`, what folds. */\n kind?: string;\n /** The tone of its marker: success for a payout, danger for a failure. */\n tone?: ActivityTone;\n};\n\nexport type ActivityGroup<T extends ActivityEvent> = {\n /** The day's heading (“Today”, “Yesterday”, “Monday, Oct 6”). */\n label: string;\n /** Its events, newest first; a folded run is its newest event with `count` > 1. */\n events: { event: T; count: number }[];\n};\n\nconst dayKey = (date: Date) => `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`;\n\n/** Groups events (any order) by day, newest first, folding runs of the same actor and kind when `fold` is set. */\nexport function groupActivity<T extends ActivityEvent>(\n events: readonly T[],\n { now = new Date(), locale = \"en-US\", today = \"Today\", yesterday = \"Yesterday\", fold = true }: { now?: Date; locale?: string; today?: string; yesterday?: string; fold?: boolean } = {},\n): ActivityGroup<T>[] {\n const sorted = [...events].sort((a, b) => new Date(b.at).getTime() - new Date(a.at).getTime());\n const day = new Intl.DateTimeFormat(locale, { weekday: \"long\", month: \"short\", day: \"numeric\" });\n const todayKey = dayKey(now);\n const yesterdayKey = dayKey(new Date(now.getTime() - 86_400_000));\n const groups: ActivityGroup<T>[] = [];\n let current: ActivityGroup<T> | null = null;\n let currentKey = \"\";\n for (const event of sorted) {\n const date = new Date(event.at);\n const key = dayKey(date);\n if (!current || key !== currentKey) {\n currentKey = key;\n current = { label: key === todayKey ? today : key === yesterdayKey ? yesterday : day.format(date), events: [] };\n groups.push(current);\n }\n const last = current.events[current.events.length - 1];\n if (fold && last && event.actor && event.kind && last.event.actor === event.actor && last.event.kind === event.kind) last.count += 1;\n else current.events.push({ event, count: 1 });\n }\n return groups;\n}\n\nconst UNITS: [Intl.RelativeTimeFormatUnit, number][] = [\n [\"second\", 60],\n [\"minute\", 60],\n [\"hour\", 24],\n [\"day\", 7],\n [\"week\", 4.35],\n [\"month\", 12],\n [\"year\", Number.POSITIVE_INFINITY],\n];\n\n/** “2 minutes ago”, “yesterday”: how long ago, in the largest unit that reads well (`style` short: “2 min. ago”). */\nexport function timeAgo(at: string | number | Date, { now = new Date(), locale = \"en-US\", style = \"short\" }: { now?: Date; locale?: string; style?: \"long\" | \"short\" | \"narrow\" } = {}): string {\n let value = (new Date(at).getTime() - now.getTime()) / 1000;\n let unit: Intl.RelativeTimeFormatUnit = \"second\";\n for (const [name, size] of UNITS) {\n unit = name;\n if (Math.abs(value) < size - 0.5) break;\n value /= size;\n }\n return new Intl.RelativeTimeFormat(locale, { numeric: \"auto\", style }).format(Math.round(value) + 0, unit);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
interface AnatomyPart {
|
|
4
|
+
/** The part's name: `the cut`. */
|
|
5
|
+
label: React.ReactNode;
|
|
6
|
+
/** Why it is there, in one sentence: `45°, the Krizaka chamfer; it points where the action goes.` */
|
|
7
|
+
note: React.ReactNode;
|
|
8
|
+
/** The side of the plate it is written on. @default alternates, left first */
|
|
9
|
+
side?: "left" | "right";
|
|
10
|
+
}
|
|
11
|
+
type AnatomyPlateProps = Omit<React.HTMLAttributes<HTMLElement>, "title"> & {
|
|
12
|
+
/** The plate's title: `Anatomy`. */
|
|
13
|
+
title?: React.ReactNode;
|
|
14
|
+
/** One line under the title, or the scale: `to scale × 1.9`. */
|
|
15
|
+
subtitle?: React.ReactNode;
|
|
16
|
+
/** The parts, numbered in this order. */
|
|
17
|
+
parts: readonly AnatomyPart[];
|
|
18
|
+
/** The specimen: the live component, drawn large. */
|
|
19
|
+
children: React.ReactNode;
|
|
20
|
+
/** The specimen's scale on a wide screen (it is drawn at 1 on a phone). @default 1.6 */
|
|
21
|
+
scale?: number;
|
|
22
|
+
};
|
|
23
|
+
/** A component's anatomy as a naturalist's plate: the specimen in the middle, its numbered parts and their reasons around it. */
|
|
24
|
+
declare function AnatomyPlate({ title, subtitle, parts, children, scale, className, style, ...props }: AnatomyPlateProps): React.JSX.Element;
|
|
25
|
+
|
|
26
|
+
export { type AnatomyPart, AnatomyPlate, type AnatomyPlateProps };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cn
|
|
3
|
+
} from "./chunk-W675RFOA.js";
|
|
4
|
+
|
|
5
|
+
// src/anatomy-plate/anatomy-plate.tsx
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
function AnatomyPlate({ title, subtitle, parts, children, scale = 1.6, className, style, ...props }) {
|
|
8
|
+
const numbered = parts.map((p, i) => ({ ...p, n: i + 1, side: p.side ?? (i % 2 === 0 ? "left" : "right") }));
|
|
9
|
+
const column = (side) => /* @__PURE__ */ jsx("ol", { className: "kz-anatomy-parts", "data-side": side, children: numbered.filter((p) => p.side === side).map((p) => /* @__PURE__ */ jsxs("li", { value: p.n, className: "kz-anatomy-part", children: [
|
|
10
|
+
/* @__PURE__ */ jsx("span", { className: "kz-anatomy-n", "aria-hidden": true, children: p.n }),
|
|
11
|
+
/* @__PURE__ */ jsxs("span", { className: "kz-anatomy-text", children: [
|
|
12
|
+
/* @__PURE__ */ jsx("b", { children: p.label }),
|
|
13
|
+
" ",
|
|
14
|
+
p.note
|
|
15
|
+
] })
|
|
16
|
+
] }, p.n)) });
|
|
17
|
+
return /* @__PURE__ */ jsxs("figure", { className: cn("kz-anatomy", className), style: { ["--kz-anatomy-scale"]: scale, ...style }, ...props, children: [
|
|
18
|
+
(title || subtitle) && /* @__PURE__ */ jsxs("figcaption", { className: "kz-anatomy-head", children: [
|
|
19
|
+
title && /* @__PURE__ */ jsx("span", { className: "kz-plate-title", children: title }),
|
|
20
|
+
subtitle && /* @__PURE__ */ jsx("span", { className: "kz-plate-sub", children: subtitle })
|
|
21
|
+
] }),
|
|
22
|
+
/* @__PURE__ */ jsxs("div", { className: "kz-anatomy-stage", children: [
|
|
23
|
+
column("left"),
|
|
24
|
+
/* @__PURE__ */ jsx("div", { className: "kz-anatomy-specimen", children: /* @__PURE__ */ jsx("div", { className: "kz-anatomy-scale", children }) }),
|
|
25
|
+
column("right")
|
|
26
|
+
] })
|
|
27
|
+
] });
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
AnatomyPlate
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=anatomy-plate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/anatomy-plate/anatomy-plate.tsx"],"sourcesContent":["// Server-safe. The anatomy plate of the Cahier (ADR-004, blocks.css): a component drawn large in the middle of a plate,\n// its parts numbered and named on both sides, a hairline leading from each note to the specimen — the way a field guide\n// shows a bird's anatomy. The notes are real text in an ordered list: the plate is its own text version, and on a phone\n// it stacks (the specimen, then the list).\nimport type * as React from \"react\";\n\nimport { cn } from \"../cn\";\n\nexport interface AnatomyPart {\n /** The part's name: `the cut`. */\n label: React.ReactNode;\n /** Why it is there, in one sentence: `45°, the Krizaka chamfer; it points where the action goes.` */\n note: React.ReactNode;\n /** The side of the plate it is written on. @default alternates, left first */\n side?: \"left\" | \"right\";\n}\n\nexport type AnatomyPlateProps = Omit<React.HTMLAttributes<HTMLElement>, \"title\"> & {\n /** The plate's title: `Anatomy`. */\n title?: React.ReactNode;\n /** One line under the title, or the scale: `to scale × 1.9`. */\n subtitle?: React.ReactNode;\n /** The parts, numbered in this order. */\n parts: readonly AnatomyPart[];\n /** The specimen: the live component, drawn large. */\n children: React.ReactNode;\n /** The specimen's scale on a wide screen (it is drawn at 1 on a phone). @default 1.6 */\n scale?: number;\n};\n\n/** A component's anatomy as a naturalist's plate: the specimen in the middle, its numbered parts and their reasons around it. */\nexport function AnatomyPlate({ title, subtitle, parts, children, scale = 1.6, className, style, ...props }: AnatomyPlateProps) {\n const numbered = parts.map((p, i) => ({ ...p, n: i + 1, side: p.side ?? (i % 2 === 0 ? \"left\" : \"right\") }));\n const column = (side: \"left\" | \"right\") => (\n <ol className=\"kz-anatomy-parts\" data-side={side}>\n {numbered\n .filter((p) => p.side === side)\n .map((p) => (\n <li key={p.n} value={p.n} className=\"kz-anatomy-part\">\n <span className=\"kz-anatomy-n\" aria-hidden>\n {p.n}\n </span>\n <span className=\"kz-anatomy-text\">\n <b>{p.label}</b> {p.note}\n </span>\n </li>\n ))}\n </ol>\n );\n return (\n <figure className={cn(\"kz-anatomy\", className)} style={{ [\"--kz-anatomy-scale\" as string]: scale, ...style }} {...props}>\n {(title || subtitle) && (\n <figcaption className=\"kz-anatomy-head\">\n {title && <span className=\"kz-plate-title\">{title}</span>}\n {subtitle && <span className=\"kz-plate-sub\">{subtitle}</span>}\n </figcaption>\n )}\n <div className=\"kz-anatomy-stage\">\n {column(\"left\")}\n <div className=\"kz-anatomy-specimen\">\n <div className=\"kz-anatomy-scale\">{children}</div>\n </div>\n {column(\"right\")}\n </div>\n </figure>\n );\n}\n"],"mappings":";;;;;AAuCY,cAGA,YAHA;AARL,SAAS,aAAa,EAAE,OAAO,UAAU,OAAO,UAAU,QAAQ,KAAK,WAAW,OAAO,GAAG,MAAM,GAAsB;AAC7H,QAAM,WAAW,MAAM,IAAI,CAAC,GAAG,OAAO,EAAE,GAAG,GAAG,GAAG,IAAI,GAAG,MAAM,EAAE,SAAS,IAAI,MAAM,IAAI,SAAS,SAAS,EAAE;AAC3G,QAAM,SAAS,CAAC,SACd,oBAAC,QAAG,WAAU,oBAAmB,aAAW,MACzC,mBACE,OAAO,CAAC,MAAM,EAAE,SAAS,IAAI,EAC7B,IAAI,CAAC,MACJ,qBAAC,QAAa,OAAO,EAAE,GAAG,WAAU,mBAClC;AAAA,wBAAC,UAAK,WAAU,gBAAe,eAAW,MACvC,YAAE,GACL;AAAA,IACA,qBAAC,UAAK,WAAU,mBACd;AAAA,0BAAC,OAAG,YAAE,OAAM;AAAA,MAAI;AAAA,MAAE,EAAE;AAAA,OACtB;AAAA,OANO,EAAE,CAOX,CACD,GACL;AAEF,SACE,qBAAC,YAAO,WAAW,GAAG,cAAc,SAAS,GAAG,OAAO,EAAE,CAAC,oBAA8B,GAAG,OAAO,GAAG,MAAM,GAAI,GAAG,OAC9G;AAAA,cAAS,aACT,qBAAC,gBAAW,WAAU,mBACnB;AAAA,eAAS,oBAAC,UAAK,WAAU,kBAAkB,iBAAM;AAAA,MACjD,YAAY,oBAAC,UAAK,WAAU,gBAAgB,oBAAS;AAAA,OACxD;AAAA,IAEF,qBAAC,SAAI,WAAU,oBACZ;AAAA,aAAO,MAAM;AAAA,MACd,oBAAC,SAAI,WAAU,uBACb,8BAAC,SAAI,WAAU,oBAAoB,UAAS,GAC9C;AAAA,MACC,OAAO,OAAO;AAAA,OACjB;AAAA,KACF;AAEJ;","names":[]}
|
package/dist/app-header.js
CHANGED
|
@@ -4,7 +4,7 @@ import { tv } from "tailwind-variants";
|
|
|
4
4
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
var appHeader = tv({
|
|
6
6
|
slots: {
|
|
7
|
-
root: "sticky top-0 z-30 flex h-14 w-full shrink-0 items-center gap-3 border-b border-border-subtle bg-surface-1
|
|
7
|
+
root: "sticky top-0 z-30 flex h-14 w-full shrink-0 items-center gap-3 border-b border-border-subtle bg-surface-1 px-4 md:bg-surface-1 md:pl-3 md:pr-5",
|
|
8
8
|
// The rail's mark is 56 px tall and so is this bar: the word shares the mark's line, right after the rail.
|
|
9
9
|
brand: "flex min-w-0 items-center gap-2 font-display text-sm font-bold tracking-tight text-fg no-underline",
|
|
10
10
|
mark: "inline-flex shrink-0 md:hidden",
|
package/dist/app-header.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/app-header/app-header.tsx"],"sourcesContent":["// Server-safe: no hook, no context. The top bar that sits next to a `RailNav`: the product's NAME as text (the mark is the\n// rail's), then optional chips, then the actions. Where the rail is a bottom tab bar (a phone) it has no room for the mark,\n// so the header shows it there and only there: exactly one brand mark is visible at every width.\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nexport const appHeader = tv({\n slots: {\n root: \"sticky top-0 z-30 flex h-14 w-full shrink-0 items-center gap-3 border-b border-border-subtle bg-surface-1
|
|
1
|
+
{"version":3,"sources":["../src/app-header/app-header.tsx"],"sourcesContent":["// Server-safe: no hook, no context. The top bar that sits next to a `RailNav`: the product's NAME as text (the mark is the\n// rail's), then optional chips, then the actions. Where the rail is a bottom tab bar (a phone) it has no room for the mark,\n// so the header shows it there and only there: exactly one brand mark is visible at every width.\nimport * as React from \"react\";\nimport { tv } from \"tailwind-variants\";\n\nexport const appHeader = tv({\n slots: {\n root: \"sticky top-0 z-30 flex h-14 w-full shrink-0 items-center gap-3 border-b border-border-subtle bg-surface-1 px-4 md:bg-surface-1 md:pl-3 md:pr-5\",\n // The rail's mark is 56 px tall and so is this bar: the word shares the mark's line, right after the rail.\n brand: \"flex min-w-0 items-center gap-2 font-display text-sm font-bold tracking-tight text-fg no-underline\",\n mark: \"inline-flex shrink-0 md:hidden\",\n name: \"truncate\",\n chips: \"flex shrink-0 items-center gap-2\",\n actions: \"ml-auto flex shrink-0 items-center gap-2\",\n },\n});\nconst s = appHeader();\n\nexport type AppHeaderProps = Omit<React.ComponentProps<\"header\">, \"children\"> & {\n /** The product's name, as text (`Orazaka`): the only brand element on a desktop. */\n name: string;\n /** The product's mark (`<ProductLogo id=\"orazaka\" size={22} />`): shown below `md` only, where the rail (which carries it) is a bottom bar. */\n mark?: React.ReactNode;\n /** Makes the name (and the mark) a link home: pass your router's link as `homeLink` or give a `href`. */\n href?: string;\n /** Your router's link element for the home link; it receives the name and the mark as children. */\n homeLink?: React.ReactElement<{ className?: string; children?: React.ReactNode }>;\n /** Status chips beside the name (`Badge`): « Local · Sovereign », « Dev », a version. */\n chips?: React.ReactNode;\n /** On the right: theme, notifications, the account menu. */\n actions?: React.ReactNode;\n};\n\n/** The app's top bar: the name next to the rail, the mark on a phone, chips, actions. */\nexport function AppHeader({ name, mark, href, homeLink, chips, actions, className, ...props }: AppHeaderProps) {\n const brand = (\n <>\n {mark && <span data-brand-mark className={s.mark()}>{mark}</span>}\n <span className={s.name()}>{name}</span>\n </>\n );\n return (\n <header className={s.root({ className })} {...props}>\n {homeLink ? (\n React.cloneElement(homeLink, { className: s.brand() }, brand)\n ) : href ? (\n <a href={href} className={s.brand()}>{brand}</a>\n ) : (\n <div className={s.brand()}>{brand}</div>\n )}\n {chips && <div className={s.chips()}>{chips}</div>}\n {actions && <div className={s.actions()}>{actions}</div>}\n </header>\n );\n}\n"],"mappings":";AAGA,YAAY,WAAW;AACvB,SAAS,UAAU;AAiCf,mBACW,KADX;AA/BG,IAAM,YAAY,GAAG;AAAA,EAC1B,OAAO;AAAA,IACL,MAAM;AAAA;AAAA,IAEN,OAAO;AAAA,IACP,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AAAA,EACX;AACF,CAAC;AACD,IAAM,IAAI,UAAU;AAkBb,SAAS,UAAU,EAAE,MAAM,MAAM,MAAM,UAAU,OAAO,SAAS,WAAW,GAAG,MAAM,GAAmB;AAC7G,QAAM,QACJ,iCACG;AAAA,YAAQ,oBAAC,UAAK,mBAAe,MAAC,WAAW,EAAE,KAAK,GAAI,gBAAK;AAAA,IAC1D,oBAAC,UAAK,WAAW,EAAE,KAAK,GAAI,gBAAK;AAAA,KACnC;AAEF,SACE,qBAAC,YAAO,WAAW,EAAE,KAAK,EAAE,UAAU,CAAC,GAAI,GAAG,OAC3C;AAAA,eACO,mBAAa,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,KAAK,IAC1D,OACF,oBAAC,OAAE,MAAY,WAAW,EAAE,MAAM,GAAI,iBAAM,IAE5C,oBAAC,SAAI,WAAW,EAAE,MAAM,GAAI,iBAAM;AAAA,IAEnC,SAAS,oBAAC,SAAI,WAAW,EAAE,MAAM,GAAI,iBAAM;AAAA,IAC3C,WAAW,oBAAC,SAAI,WAAW,EAAE,QAAQ,GAAI,mBAAQ;AAAA,KACpD;AAEJ;","names":[]}
|
package/dist/app-shell.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import {
|
|
3
3
|
SkipLink
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-4QH6CP2Y.js";
|
|
5
5
|
import {
|
|
6
6
|
ChevronGlyph,
|
|
7
7
|
CloseGlyph,
|
|
@@ -133,18 +133,18 @@ var appShell = tv2({
|
|
|
133
133
|
sidebar: "sticky top-0 hidden h-dvh shrink-0 flex-col border-r border-border-subtle bg-surface-1 transition-[width] duration-300 motion-reduce:transition-none md:flex w-64 data-collapsed:w-[4.25rem] [[data-desktop]_&]:static [[data-desktop]_&]:flex [[data-desktop]_&]:h-auto [[data-desktop]_&]:w-60 [[data-desktop]_&]:select-none [[data-desktop]_&]:data-collapsed:w-[4.75rem]",
|
|
134
134
|
titlebar: "h-[var(--kz-titlebar-inset,1.75rem)] shrink-0",
|
|
135
135
|
drawer: "kz-dialog fixed inset-y-0 left-0 z-50 flex w-[min(20rem,88vw)] flex-col border-r border-border-default bg-surface-1 pl-[env(safe-area-inset-left)] shadow-lg focus:outline-hidden",
|
|
136
|
-
overlay: "kz-overlay fixed inset-0 z-50 bg-overlay
|
|
136
|
+
overlay: "kz-overlay fixed inset-0 z-50 bg-overlay",
|
|
137
137
|
brand: "flex h-14 shrink-0 items-center gap-2.5 px-4 font-display text-[15px] font-bold tracking-tight [[data-collapsed]_&]:justify-center [[data-collapsed]_&]:px-0 [[data-desktop]_&]:h-11",
|
|
138
138
|
nav: "flex min-h-0 flex-1 flex-col gap-0.5 overflow-y-auto px-2 py-2",
|
|
139
|
-
group: "mt-3 mb-1 px-3 font-mono text-[10.5px] font-semibold
|
|
140
|
-
item: "group/item relative flex h-10 items-center gap-3 rounded-lg px-3 text-sm font-medium text-fg-secondary no-underline transition-colors hover:bg-surface-2 hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring aria-[current=page]:bg-accent-soft aria-[current=page]:text-fg aria-[current=page]:before:absolute aria-[current=page]:before:inset-y-2 aria-[current=page]:before:-left-2 aria-[current=page]:before:w-1 aria-[current=page]:before:rounded-
|
|
139
|
+
group: "mt-3 mb-1 px-3 font-mono text-[10.5px] font-semibold text-fg-secondary [[data-collapsed]_&]:sr-only",
|
|
140
|
+
item: "group/item relative flex h-10 items-center gap-3 rounded-lg px-3 text-sm font-medium text-fg-secondary no-underline transition-colors hover:bg-surface-2 hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring aria-[current=page]:bg-accent-soft aria-[current=page]:text-fg aria-[current=page]:before:absolute aria-[current=page]:before:inset-y-2 aria-[current=page]:before:-left-2 aria-[current=page]:before:w-1 aria-[current=page]:before:rounded-sm aria-[current=page]:before:bg-accent [[data-collapsed]_&]:justify-center [[data-collapsed]_&]:px-0 [&>svg]:h-[18px] [&>svg]:w-[18px] [&>svg]:shrink-0 [[data-desktop]_&]:h-8 [[data-desktop]_&]:cursor-default [[data-desktop]_&]:rounded-md [[data-desktop]_&]:text-[13px] [[data-desktop]_&_svg]:h-4 [[data-desktop]_&_svg]:w-4",
|
|
141
141
|
itemLabel: "min-w-0 flex-1 truncate [[data-collapsed]_&]:sr-only",
|
|
142
142
|
itemHint: "ml-auto hidden items-center gap-0.5 text-fg-secondary group-hover/item:flex group-focus-visible/item:flex [[data-collapsed]_&]:!hidden",
|
|
143
|
-
itemBadge: "ml-auto inline-flex min-w-5 items-center justify-center rounded-
|
|
143
|
+
itemBadge: "ml-auto inline-flex min-w-5 items-center justify-center rounded-sm bg-accent px-1.5 text-[11px] font-bold text-on-accent [[data-collapsed]_&]:absolute [[data-collapsed]_&]:right-1.5 [[data-collapsed]_&]:top-1 [[data-collapsed]_&]:min-w-4 [[data-collapsed]_&]:px-1 [[data-collapsed]_&]:text-[10px]",
|
|
144
144
|
footer: "shrink-0 border-t border-border-subtle p-2",
|
|
145
145
|
collapse: "hidden h-9 w-full items-center gap-2 rounded-lg px-3 text-xs font-medium text-fg-secondary hover:bg-surface-2 hover:text-fg md:flex focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring [[data-collapsed]_&]:justify-center [[data-collapsed]_&]:px-0",
|
|
146
146
|
body: "flex min-w-0 flex-1 flex-col [[data-desktop]_&]:min-h-0 [[data-desktop]_&]:overflow-y-auto",
|
|
147
|
-
header: "sticky top-0 z-30 flex h-14 shrink-0 items-center gap-3 border-b border-border-subtle bg-surface-0
|
|
147
|
+
header: "sticky top-0 z-30 flex h-14 shrink-0 items-center gap-3 border-b border-border-subtle bg-surface-0 px-4 md:px-6 [[data-desktop]_&]:h-12 [[data-desktop]_&]:select-none [[data-desktop]_&]:px-5",
|
|
148
148
|
menuButton: "inline-flex h-9 w-9 items-center justify-center rounded-lg border border-border-subtle bg-surface-1 text-fg md:hidden [[data-desktop]_&]:hidden focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
|
|
149
149
|
title: "min-w-0 flex-1 truncate font-display text-base font-bold tracking-tight [[data-desktop]_&]:text-[15px]",
|
|
150
150
|
search: "inline-flex h-9 min-w-9 items-center justify-center gap-2 rounded-lg border border-border-subtle bg-surface-1 px-2.5 text-sm text-fg-secondary transition-colors hover:border-border-default hover:text-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring md:w-56 md:justify-start [&>svg]:h-4 [&>svg]:w-4 [&>svg]:shrink-0",
|