@botiverse/raft-ui 0.51.0 → 0.52.1
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/CHANGELOG.md +26 -0
- package/dist/components/activity-inbox/activity-inbox-shell.d.mts +12 -0
- package/dist/components/activity-inbox/activity-inbox-shell.mjs +39 -0
- package/dist/components/activity-inbox/activity-inbox-shell.recipe.mjs +14 -0
- package/dist/components/activity-inbox/activity-inbox-sidebar.d.mts +6 -0
- package/dist/components/activity-inbox/activity-inbox-sidebar.mjs +22 -0
- package/dist/components/activity-inbox/activity-inbox-sidebar.recipe.mjs +22 -0
- package/dist/components/activity-inbox/activity-inbox-toolbar.d.mts +9 -0
- package/dist/components/activity-inbox/activity-inbox-toolbar.mjs +29 -0
- package/dist/components/activity-inbox/activity-inbox-toolbar.recipe.mjs +14 -0
- package/dist/components/activity-inbox/index.d.mts +4 -0
- package/dist/components/alert-dialog/alert-dialog.d.mts +41 -0
- package/dist/components/alert-dialog/alert-dialog.mjs +163 -0
- package/dist/components/alert-dialog/index.d.mts +2 -0
- package/dist/components/announcement-dialog/announcement-dialog.d.mts +18 -0
- package/dist/components/announcement-dialog/announcement-dialog.mjs +121 -0
- package/dist/components/announcement-dialog/index.d.mts +3 -0
- package/dist/components/app-rail/app-rail.d.mts +31 -0
- package/dist/components/app-rail/app-rail.mjs +126 -0
- package/dist/components/app-rail/app-rail.recipe.mjs +47 -0
- package/dist/components/app-rail/index.d.mts +2 -0
- package/dist/components/app-shell/app-shell.d.mts +27 -0
- package/dist/components/app-shell/app-shell.mjs +217 -0
- package/dist/components/app-shell/index.d.mts +2 -0
- package/dist/components/avatar/avatar.d.mts +169 -0
- package/dist/components/avatar/avatar.mjs +298 -0
- package/dist/components/avatar/index.d.mts +2 -0
- package/dist/components/badge/badge.d.mts +89 -0
- package/dist/components/badge/badge.mjs +203 -0
- package/dist/components/badge/index.d.mts +2 -0
- package/dist/components/banner/banner.d.mts +141 -0
- package/dist/components/banner/banner.mjs +228 -0
- package/dist/components/banner/index.d.mts +2 -0
- package/dist/components/button/button.d.mts +257 -0
- package/dist/components/button/button.mjs +177 -0
- package/dist/components/button/index.d.mts +2 -0
- package/dist/components/card/card.d.mts +21 -0
- package/dist/components/card/card.mjs +114 -0
- package/dist/components/card/card.recipe.d.mts +105 -0
- package/dist/components/card/card.recipe.mjs +67 -0
- package/dist/components/card/index.d.mts +2 -0
- package/dist/components/checkbox/checkbox-indicator.d.mts +92 -0
- package/dist/components/checkbox/checkbox-indicator.mjs +61 -0
- package/dist/components/checkbox/checkbox.d.mts +9 -0
- package/dist/components/checkbox/checkbox.mjs +40 -0
- package/dist/components/checkbox/index.d.mts +3 -0
- package/dist/components/choice-list/choice-list.d.mts +103 -0
- package/dist/components/choice-list/choice-list.mjs +301 -0
- package/dist/components/choice-list/choice-list.recipe.mjs +44 -0
- package/dist/components/choice-list/index.d.mts +2 -0
- package/dist/components/code-block/code-block.d.mts +30 -0
- package/dist/components/code-block/code-block.mjs +151 -0
- package/dist/components/code-block/code-block.recipe.mjs +47 -0
- package/dist/components/code-block/index.d.mts +2 -0
- package/dist/components/combobox/combobox.d.mts +69 -0
- package/dist/components/combobox/combobox.mjs +438 -0
- package/dist/components/combobox/index.d.mts +2 -0
- package/dist/components/combobox-select/combobox-select.d.mts +4 -0
- package/dist/components/combobox-select/combobox-select.list-side.mjs +16 -0
- package/dist/components/combobox-select/combobox-select.mjs +473 -0
- package/dist/components/combobox-select/combobox-select.options.mjs +21 -0
- package/dist/components/combobox-select/combobox-select.types.d.mts +83 -0
- package/dist/components/combobox-select/index.d.mts +3 -0
- package/dist/components/comment/comment-card.d.mts +40 -0
- package/dist/components/comment/comment-card.mjs +83 -0
- package/dist/components/comment/comment-entry.d.mts +24 -0
- package/dist/components/comment/comment-entry.mjs +75 -0
- package/dist/components/comment/comment-highlight.d.mts +18 -0
- package/dist/components/comment/comment-highlight.mjs +33 -0
- package/dist/components/comment/comment-margin-layout.d.mts +32 -0
- package/dist/components/comment/comment-margin-layout.mjs +44 -0
- package/dist/components/comment/comment-margin.d.mts +29 -0
- package/dist/components/comment/comment-margin.mjs +164 -0
- package/dist/components/comment/comment-marker.d.mts +16 -0
- package/dist/components/comment/comment-marker.mjs +24 -0
- package/dist/components/comment/comment.recipe.mjs +58 -0
- package/dist/components/comment/index.d.mts +8 -0
- package/dist/components/comment/use-comment-margin-fits.d.mts +16 -0
- package/dist/components/comment/use-comment-margin-fits.mjs +34 -0
- package/dist/components/composer/composer-attachment.recipe.mjs +85 -0
- package/dist/components/composer/composer-attachments-scroll-area.d.mts +5 -0
- package/dist/components/composer/composer-attachments-scroll-area.mjs +93 -0
- package/dist/components/composer/composer-attachments.d.mts +27 -0
- package/dist/components/composer/composer-attachments.mjs +147 -0
- package/dist/components/composer/composer-drop-overlay.d.mts +9 -0
- package/dist/components/composer/composer-drop-overlay.mjs +60 -0
- package/dist/components/composer/composer-drop-overlay.recipe.mjs +23 -0
- package/dist/components/composer/composer-root.d.mts +5 -0
- package/dist/components/composer/composer-root.mjs +18 -0
- package/dist/components/composer/composer-suggestion-list.d.mts +139 -0
- package/dist/components/composer/composer-suggestion-list.mjs +160 -0
- package/dist/components/composer/composer-toolbar-label.d.mts +5 -0
- package/dist/components/composer/composer-toolbar-label.mjs +24 -0
- package/dist/components/composer/composer.d.mts +20 -0
- package/dist/components/composer/composer.mjs +88 -0
- package/dist/components/composer/composer.recipe.mjs +29 -0
- package/dist/components/composer/index.d.mts +10 -0
- package/dist/components/composer/sortable-composer-attachments.d.mts +37 -0
- package/dist/components/composer/sortable-composer-attachments.mjs +247 -0
- package/dist/components/composer/use-composer-suggestions.d.mts +83 -0
- package/dist/components/composer/use-composer-suggestions.mjs +207 -0
- package/dist/components/context-menu/context-menu.d.mts +53 -0
- package/dist/components/context-menu/context-menu.mjs +275 -0
- package/dist/components/context-menu/index.d.mts +2 -0
- package/dist/components/conversation-panel/conversation-panel-empty-state.d.mts +9 -0
- package/dist/components/conversation-panel/conversation-panel-empty-state.mjs +57 -0
- package/dist/components/conversation-panel/conversation-panel.d.mts +20 -0
- package/dist/components/conversation-panel/conversation-panel.mjs +63 -0
- package/dist/components/conversation-panel/conversation-panel.recipe.mjs +15 -0
- package/dist/components/conversation-panel/index.d.mts +3 -0
- package/dist/components/copyable-code/copyable-code.d.mts +72 -0
- package/dist/components/copyable-code/copyable-code.mjs +153 -0
- package/dist/components/copyable-code/index.d.mts +2 -0
- package/dist/components/description-list/description-list.d.mts +59 -0
- package/dist/components/description-list/description-list.mjs +96 -0
- package/dist/components/description-list/index.d.mts +2 -0
- package/dist/components/dialog/dialog-sheet-context.mjs +58 -0
- package/dist/components/dialog/dialog-sheet.d.mts +31 -0
- package/dist/components/dialog/dialog-sheet.mjs +194 -0
- package/dist/components/dialog/dialog.d.mts +85 -0
- package/dist/components/dialog/dialog.mjs +371 -0
- package/dist/components/dialog/index.d.mts +3 -0
- package/dist/components/drag/drag-announcements.d.mts +72 -0
- package/dist/components/drag/drag-announcements.mjs +115 -0
- package/dist/components/drag/drag-lift.d.mts +100 -0
- package/dist/components/drag/drag-lift.mjs +276 -0
- package/dist/components/drag/drag-lists.d.mts +98 -0
- package/dist/components/drag/drag-lists.mjs +258 -0
- package/dist/components/drag/drag-sensors.d.mts +87 -0
- package/dist/components/drag/drag-sensors.mjs +169 -0
- package/dist/components/drag/drag.recipe.d.mts +7 -0
- package/dist/components/drag/drag.recipe.mjs +30 -0
- package/dist/components/drag/drop-placeholder.d.mts +41 -0
- package/dist/components/drag/drop-placeholder.mjs +161 -0
- package/dist/components/drag/index.d.mts +8 -0
- package/dist/components/drawer/drawer.d.mts +34 -0
- package/dist/components/drawer/drawer.mjs +202 -0
- package/dist/components/drawer/drawer.recipe.mjs +175 -0
- package/dist/components/drawer/index.d.mts +2 -0
- package/dist/components/dropdown-menu/dropdown-menu.d.mts +62 -0
- package/dist/components/dropdown-menu/dropdown-menu.mjs +319 -0
- package/dist/components/dropdown-menu/index.d.mts +2 -0
- package/dist/components/ellipsis/ellipsis.d.mts +55 -0
- package/dist/components/ellipsis/ellipsis.mjs +320 -0
- package/dist/components/ellipsis/index.d.mts +3 -0
- package/dist/components/empty-state/empty-state.d.mts +15 -0
- package/dist/components/empty-state/empty-state.mjs +102 -0
- package/dist/components/empty-state/index.d.mts +2 -0
- package/dist/components/field/field.d.mts +79 -0
- package/dist/components/field/field.mjs +100 -0
- package/dist/components/field/index.d.mts +2 -0
- package/dist/components/file-preview/file-preview.d.mts +17 -0
- package/dist/components/file-preview/file-preview.mjs +210 -0
- package/dist/components/file-preview/file-preview.recipe.d.mts +99 -0
- package/dist/components/file-preview/file-preview.recipe.mjs +39 -0
- package/dist/components/file-preview/index.d.mts +2 -0
- package/dist/components/files/files.d.mts +35 -0
- package/dist/components/files/files.mjs +69 -0
- package/dist/components/files/files.recipe.mjs +40 -0
- package/dist/components/files/index.d.mts +2 -0
- package/dist/components/filter-chip/filter-chip.d.mts +25 -0
- package/dist/components/filter-chip/filter-chip.mjs +59 -0
- package/dist/components/filter-chip/index.d.mts +2 -0
- package/dist/components/float-button/float-button.d.mts +36 -0
- package/dist/components/float-button/float-button.mjs +116 -0
- package/dist/components/float-button/float-button.position.d.mts +6 -0
- package/dist/components/float-button/float-button.position.mjs +41 -0
- package/dist/components/float-button/index.d.mts +3 -0
- package/dist/components/form-section/form-section.d.mts +16 -0
- package/dist/components/form-section/form-section.mjs +53 -0
- package/dist/components/form-section/index.d.mts +2 -0
- package/dist/components/icon-tile/icon-tile.d.mts +97 -0
- package/dist/components/icon-tile/icon-tile.mjs +49 -0
- package/dist/components/icon-tile/index.d.mts +2 -0
- package/dist/components/icons/action-icons.d.mts +34 -0
- package/dist/components/icons/action-icons.mjs +383 -0
- package/dist/components/icons/conversation-icons.d.mts +12 -0
- package/dist/components/icons/conversation-icons.mjs +147 -0
- package/dist/components/icons/index.d.mts +4 -0
- package/dist/components/icons/object-icons.d.mts +6 -0
- package/dist/components/icons/object-icons.mjs +27 -0
- package/dist/components/inbox-item/inbox-item-leading-badge.d.mts +13 -0
- package/dist/components/inbox-item/inbox-item-leading-badge.mjs +27 -0
- package/dist/components/inbox-item/inbox-item-skeleton.d.mts +46 -0
- package/dist/components/inbox-item/inbox-item-skeleton.mjs +89 -0
- package/dist/components/inbox-item/inbox-item-skeleton.recipe.mjs +20 -0
- package/dist/components/inbox-item/inbox-item.d.mts +71 -0
- package/dist/components/inbox-item/inbox-item.mjs +276 -0
- package/dist/components/inbox-item/inbox-item.recipe.mjs +137 -0
- package/dist/components/inbox-item/index.d.mts +2 -0
- package/dist/components/inline-code/index.d.mts +2 -0
- package/dist/components/inline-code/inline-code.d.mts +8 -0
- package/dist/components/inline-code/inline-code.mjs +30 -0
- package/dist/components/input/index.d.mts +2 -0
- package/dist/components/input/input.d.mts +16 -0
- package/dist/components/input/input.mjs +20 -0
- package/dist/components/input/input.recipe.mjs +12 -0
- package/dist/components/input-group/index.d.mts +2 -0
- package/dist/components/input-group/input-group.d.mts +93 -0
- package/dist/components/input-group/input-group.mjs +178 -0
- package/dist/components/kbd/index.d.mts +2 -0
- package/dist/components/kbd/kbd.d.mts +7 -0
- package/dist/components/kbd/kbd.mjs +34 -0
- package/dist/components/label/index.d.mts +2 -0
- package/dist/components/label/label.d.mts +60 -0
- package/dist/components/label/label.mjs +75 -0
- package/dist/components/legacy-task-panel/index.d.mts +2 -0
- package/dist/components/legacy-task-panel/legacy-task-panel.d.mts +57 -0
- package/dist/components/legacy-task-panel/legacy-task-panel.mjs +234 -0
- package/dist/components/legacy-task-panel/legacy-task-panel.recipe.mjs +30 -0
- package/dist/components/lightbox/index.d.mts +2 -0
- package/dist/components/lightbox/lightbox.d.mts +55 -0
- package/dist/components/lightbox/lightbox.mjs +336 -0
- package/dist/components/list-row/index.d.mts +2 -0
- package/dist/components/list-row/list-row-skeleton.d.mts +21 -0
- package/dist/components/list-row/list-row-skeleton.mjs +43 -0
- package/dist/components/list-row/list-row.d.mts +66 -0
- package/dist/components/list-row/list-row.mjs +167 -0
- package/dist/components/list-row/list-row.recipe.d.mts +126 -0
- package/dist/components/list-row/list-row.recipe.mjs +94 -0
- package/dist/components/live-agent-activity-bar/index.d.mts +2 -0
- package/dist/components/live-agent-activity-bar/live-agent-activity-bar.d.mts +25 -0
- package/dist/components/live-agent-activity-bar/live-agent-activity-bar.mjs +107 -0
- package/dist/components/logo/index.d.mts +2 -0
- package/dist/components/logo/raft-logo.d.mts +9 -0
- package/dist/components/logo/raft-logo.mjs +63 -0
- package/dist/components/markdown/index.d.mts +2 -0
- package/dist/components/markdown/markdown-parts.d.mts +54 -0
- package/dist/components/markdown/markdown-parts.mjs +153 -0
- package/dist/components/markdown/markdown.recipe.mjs +77 -0
- package/dist/components/message-action-card/index.d.mts +2 -0
- package/dist/components/message-action-card/message-action-card-content.recipe.mjs +50 -0
- package/dist/components/message-action-card/message-action-card.d.mts +95 -0
- package/dist/components/message-action-card/message-action-card.mjs +298 -0
- package/dist/components/message-action-card/message-action-card.recipe.mjs +47 -0
- package/dist/components/message-attachment/index.d.mts +5 -0
- package/dist/components/message-attachment/message-attachment.d.mts +27 -0
- package/dist/components/message-attachment/message-attachment.mjs +129 -0
- package/dist/components/message-attachment/message-attachment.recipe.mjs +36 -0
- package/dist/components/message-attachment/message-image-gallery-rows.d.mts +47 -0
- package/dist/components/message-attachment/message-image-gallery-rows.mjs +55 -0
- package/dist/components/message-attachment/message-image-gallery.d.mts +32 -0
- package/dist/components/message-attachment/message-image-gallery.mjs +105 -0
- package/dist/components/message-attachment/message-image-gallery.recipe.mjs +61 -0
- package/dist/components/message-attachment/message-video-preview.d.mts +19 -0
- package/dist/components/message-attachment/message-video-preview.mjs +92 -0
- package/dist/components/message-attachment/message-video-preview.recipe.mjs +33 -0
- package/dist/components/message-embed/index.d.mts +2 -0
- package/dist/components/message-embed/message-embed.d.mts +12 -0
- package/dist/components/message-embed/message-embed.mjs +42 -0
- package/dist/components/message-embed/message-embed.recipe.mjs +14 -0
- package/dist/components/message-forwarded-bundle/index.d.mts +2 -0
- package/dist/components/message-forwarded-bundle/message-forwarded-bundle.d.mts +92 -0
- package/dist/components/message-forwarded-bundle/message-forwarded-bundle.mjs +324 -0
- package/dist/components/message-forwarded-bundle/message-forwarded-bundle.recipe.mjs +81 -0
- package/dist/components/message-item/index.d.mts +13 -0
- package/dist/components/message-item/message-footer.d.mts +20 -0
- package/dist/components/message-item/message-footer.mjs +54 -0
- package/dist/components/message-item/message-footer.recipe.mjs +46 -0
- package/dist/components/message-item/message-item-content.d.mts +22 -0
- package/dist/components/message-item/message-item-content.mjs +81 -0
- package/dist/components/message-item/message-item-delegated.d.mts +38 -0
- package/dist/components/message-item/message-item-delegated.mjs +72 -0
- package/dist/components/message-item/message-item-gutter.d.mts +10 -0
- package/dist/components/message-item/message-item-gutter.mjs +41 -0
- package/dist/components/message-item/message-item-header.d.mts +29 -0
- package/dist/components/message-item/message-item-header.mjs +107 -0
- package/dist/components/message-item/message-item-label.mjs +113 -0
- package/dist/components/message-item/message-item-root.d.mts +25 -0
- package/dist/components/message-item/message-item-root.mjs +39 -0
- package/dist/components/message-item/message-item-toolbar.d.mts +9 -0
- package/dist/components/message-item/message-item-toolbar.mjs +131 -0
- package/dist/components/message-item/message-item.recipe.d.mts +101 -0
- package/dist/components/message-item/message-item.recipe.mjs +114 -0
- package/dist/components/message-item/message-multi-select.d.mts +15 -0
- package/dist/components/message-item/message-multi-select.mjs +49 -0
- package/dist/components/message-item/message-multi-select.recipe.mjs +23 -0
- package/dist/components/message-item/message-reaction.d.mts +39 -0
- package/dist/components/message-item/message-reaction.mjs +188 -0
- package/dist/components/message-item/message-reaction.recipe.mjs +76 -0
- package/dist/components/message-item/message-reference.d.mts +50 -0
- package/dist/components/message-item/message-reference.mjs +111 -0
- package/dist/components/message-item/message-reference.recipe.d.mts +6 -0
- package/dist/components/message-item/message-reference.recipe.mjs +63 -0
- package/dist/components/message-item/message-text-selection-toolbar.d.mts +24 -0
- package/dist/components/message-item/message-text-selection-toolbar.mjs +13 -0
- package/dist/components/message-list/index.d.mts +4 -0
- package/dist/components/message-list/message-list-jump.d.mts +21 -0
- package/dist/components/message-list/message-list-jump.mjs +73 -0
- package/dist/components/message-list/message-list-markers.d.mts +13 -0
- package/dist/components/message-list/message-list-markers.mjs +70 -0
- package/dist/components/message-list/message-list.d.mts +7 -0
- package/dist/components/message-list/message-list.mjs +52 -0
- package/dist/components/message-list/message-list.recipe.mjs +54 -0
- package/dist/components/message-quoted-preview/index.d.mts +2 -0
- package/dist/components/message-quoted-preview/message-quoted-preview.d.mts +40 -0
- package/dist/components/message-quoted-preview/message-quoted-preview.mjs +144 -0
- package/dist/components/message-quoted-preview/message-quoted-preview.recipe.mjs +38 -0
- package/dist/components/message-replies/index.d.mts +2 -0
- package/dist/components/message-replies/message-replies.d.mts +17 -0
- package/dist/components/message-replies/message-replies.mjs +79 -0
- package/dist/components/message-replies/message-replies.recipe.mjs +25 -0
- package/dist/components/message-translation/index.d.mts +2 -0
- package/dist/components/message-translation/message-translation.d.mts +20 -0
- package/dist/components/message-translation/message-translation.mjs +64 -0
- package/dist/components/message-translation/message-translation.recipe.mjs +27 -0
- package/dist/components/meter/index.d.mts +2 -0
- package/dist/components/meter/meter.d.mts +107 -0
- package/dist/components/meter/meter.mjs +118 -0
- package/dist/components/mobile-nav/index.d.mts +3 -0
- package/dist/components/mobile-nav/mobile-nav-server.d.mts +71 -0
- package/dist/components/mobile-nav/mobile-nav-server.mjs +287 -0
- package/dist/components/mobile-nav/mobile-nav.d.mts +12 -0
- package/dist/components/mobile-nav/mobile-nav.mjs +73 -0
- package/dist/components/mode-bar/index.d.mts +2 -0
- package/dist/components/mode-bar/mode-bar.d.mts +33 -0
- package/dist/components/mode-bar/mode-bar.mjs +115 -0
- package/dist/components/notification-center/index.d.mts +2 -0
- package/dist/components/notification-center/notification-center.d.mts +225 -0
- package/dist/components/notification-center/notification-center.mjs +340 -0
- package/dist/components/panel/index.d.mts +7 -0
- package/dist/components/panel/panel-action.d.mts +15 -0
- package/dist/components/panel/panel-action.mjs +23 -0
- package/dist/components/panel/panel-action.recipe.mjs +40 -0
- package/dist/components/panel/panel-header.d.mts +34 -0
- package/dist/components/panel/panel-header.mjs +135 -0
- package/dist/components/panel/panel-header.recipe.mjs +78 -0
- package/dist/components/panel/panel-notice.d.mts +16 -0
- package/dist/components/panel/panel-notice.mjs +77 -0
- package/dist/components/panel/panel-split-action.d.mts +34 -0
- package/dist/components/panel/panel-split-action.mjs +69 -0
- package/dist/components/panel/panel-split-action.recipe.mjs +53 -0
- package/dist/components/panel/panel-toggle-action.d.mts +6 -0
- package/dist/components/panel/panel-toggle-action.mjs +15 -0
- package/dist/components/panel/panel.d.mts +25 -0
- package/dist/components/panel/panel.mjs +96 -0
- package/dist/components/panel/panel.recipe.mjs +24 -0
- package/dist/components/picker-trigger-button/index.d.mts +2 -0
- package/dist/components/picker-trigger-button/picker-trigger-button.d.mts +7 -0
- package/dist/components/picker-trigger-button/picker-trigger-button.mjs +36 -0
- package/dist/components/popover/index.d.mts +2 -0
- package/dist/components/popover/popover.d.mts +41 -0
- package/dist/components/popover/popover.mjs +184 -0
- package/dist/components/preview-card/index.d.mts +2 -0
- package/dist/components/preview-card/preview-card.d.mts +43 -0
- package/dist/components/preview-card/preview-card.mjs +197 -0
- package/dist/components/profile-panel/index.d.mts +2 -0
- package/dist/components/profile-panel/profile-panel.d.mts +14 -0
- package/dist/components/profile-panel/profile-panel.mjs +46 -0
- package/dist/components/profile-panel/profile-panel.recipe.mjs +14 -0
- package/dist/components/progress/index.d.mts +2 -0
- package/dist/components/progress/progress.d.mts +119 -0
- package/dist/components/progress/progress.mjs +139 -0
- package/dist/components/pull-to-refresh/index.d.mts +4 -0
- package/dist/components/pull-to-refresh/pull-to-refresh-gesture.d.mts +30 -0
- package/dist/components/pull-to-refresh/pull-to-refresh-gesture.mjs +134 -0
- package/dist/components/pull-to-refresh/pull-to-refresh-indicator.d.mts +21 -0
- package/dist/components/pull-to-refresh/pull-to-refresh-indicator.mjs +153 -0
- package/dist/components/pull-to-refresh/pull-to-refresh-material-progress.mjs +150 -0
- package/dist/components/pull-to-refresh/use-pull-to-refresh.d.mts +55 -0
- package/dist/components/pull-to-refresh/use-pull-to-refresh.mjs +156 -0
- package/dist/components/radio-group/index.d.mts +2 -0
- package/dist/components/radio-group/radio-group.d.mts +14 -0
- package/dist/components/radio-group/radio-group.mjs +59 -0
- package/dist/components/resizable/index.d.mts +2 -0
- package/dist/components/resizable/resizable.d.mts +15 -0
- package/dist/components/resizable/resizable.mjs +39 -0
- package/dist/components/resizable/resizable.recipe.mjs +24 -0
- package/dist/components/scroll-area/index.d.mts +2 -0
- package/dist/components/scroll-area/scroll-area.d.mts +17 -0
- package/dist/components/scroll-area/scroll-area.mjs +55 -0
- package/dist/components/scroll-area/scroll-area.recipe.mjs +23 -0
- package/dist/components/search/index.d.mts +6 -0
- package/dist/components/search/search-entity.d.mts +24 -0
- package/dist/components/search/search-entity.mjs +106 -0
- package/dist/components/search/search-entity.recipe.mjs +41 -0
- package/dist/components/search/search-message.d.mts +25 -0
- package/dist/components/search/search-message.mjs +115 -0
- package/dist/components/search/search-message.recipe.mjs +32 -0
- package/dist/components/search/search-thread.d.mts +17 -0
- package/dist/components/search/search-thread.mjs +79 -0
- package/dist/components/search/search-thread.recipe.mjs +16 -0
- package/dist/components/search/search-trigger.d.mts +15 -0
- package/dist/components/search/search-trigger.mjs +59 -0
- package/dist/components/search/search.d.mts +23 -0
- package/dist/components/search/search.mjs +88 -0
- package/dist/components/search/search.recipe.mjs +22 -0
- package/dist/components/segmented-control/index.d.mts +2 -0
- package/dist/components/segmented-control/segmented-control.d.mts +38 -0
- package/dist/components/segmented-control/segmented-control.mjs +306 -0
- package/dist/components/select/index.d.mts +2 -0
- package/dist/components/select/select.d.mts +95 -0
- package/dist/components/select/select.mjs +483 -0
- package/dist/components/separator/index.d.mts +2 -0
- package/dist/components/separator/separator.d.mts +10 -0
- package/dist/components/separator/separator.mjs +49 -0
- package/dist/components/sidebar/index.d.mts +5 -0
- package/dist/components/sidebar/sidebar-item.d.mts +64 -0
- package/dist/components/sidebar/sidebar-item.mjs +201 -0
- package/dist/components/sidebar/sidebar-item.recipe.mjs +100 -0
- package/dist/components/sidebar/sidebar-menu.d.mts +13 -0
- package/dist/components/sidebar/sidebar-menu.mjs +67 -0
- package/dist/components/sidebar/sidebar-menu.recipe.mjs +22 -0
- package/dist/components/sidebar/sidebar-section.d.mts +41 -0
- package/dist/components/sidebar/sidebar-section.mjs +164 -0
- package/dist/components/sidebar/sidebar-section.recipe.mjs +45 -0
- package/dist/components/sidebar/sidebar.d.mts +17 -0
- package/dist/components/sidebar/sidebar.mjs +149 -0
- package/dist/components/sidebar/sidebar.recipe.mjs +37 -0
- package/dist/components/skeleton/index.d.mts +2 -0
- package/dist/components/skeleton/skeleton.d.mts +50 -0
- package/dist/components/skeleton/skeleton.mjs +29 -0
- package/dist/components/spinner/index.d.mts +2 -0
- package/dist/components/spinner/spinner.d.mts +77 -0
- package/dist/components/spinner/spinner.mjs +118 -0
- package/dist/components/status/index.d.mts +2 -0
- package/dist/components/status/status.d.mts +143 -0
- package/dist/components/status/status.mjs +56 -0
- package/dist/components/steps/index.d.mts +3 -0
- package/dist/components/steps/otter-indicator.d.mts +17 -0
- package/dist/components/steps/otter-indicator.mjs +331 -0
- package/dist/components/steps/steps.d.mts +50 -0
- package/dist/components/steps/steps.mjs +174 -0
- package/dist/components/switch/index.d.mts +2 -0
- package/dist/components/switch/switch.d.mts +9 -0
- package/dist/components/switch/switch.mjs +50 -0
- package/dist/components/system-message/index.d.mts +2 -0
- package/dist/components/system-message/system-message.d.mts +27 -0
- package/dist/components/system-message/system-message.mjs +113 -0
- package/dist/components/system-message/system-message.recipe.mjs +34 -0
- package/dist/components/table/index.d.mts +2 -0
- package/dist/components/table/table.d.mts +35 -0
- package/dist/components/table/table.mjs +102 -0
- package/dist/components/table/table.recipe.mjs +31 -0
- package/dist/components/tabs/index.d.mts +2 -0
- package/dist/components/tabs/tabs.d.mts +185 -0
- package/dist/components/tabs/tabs.mjs +528 -0
- package/dist/components/task-board/index.d.mts +2 -0
- package/dist/components/task-board/task-board-column.recipe.mjs +46 -0
- package/dist/components/task-board/task-board.d.mts +40 -0
- package/dist/components/task-board/task-board.mjs +155 -0
- package/dist/components/task-card/index.d.mts +2 -0
- package/dist/components/task-card/task-card-status.recipe.mjs +17 -0
- package/dist/components/task-card/task-card.d.mts +30 -0
- package/dist/components/task-card/task-card.mjs +141 -0
- package/dist/components/task-card/task-card.recipe.mjs +39 -0
- package/dist/components/task-dnd/index.d.mts +2 -0
- package/dist/components/task-dnd/task-dnd.d.mts +75 -0
- package/dist/components/task-dnd/task-dnd.mjs +341 -0
- package/dist/components/task-dnd/task-dnd.recipe.mjs +20 -0
- package/dist/components/task-list/index.d.mts +2 -0
- package/dist/components/task-list/task-list-section.recipe.mjs +37 -0
- package/dist/components/task-list/task-list.d.mts +34 -0
- package/dist/components/task-list/task-list.mjs +119 -0
- package/dist/components/tasks/index.d.mts +7 -0
- package/dist/components/tasks/task-chip.d.mts +14 -0
- package/dist/components/tasks/task-chip.mjs +81 -0
- package/dist/components/tasks/task-loading.d.mts +7 -0
- package/dist/components/tasks/task-loading.mjs +32 -0
- package/dist/components/tasks/task-loading.recipe.mjs +8 -0
- package/dist/components/tasks/task-status-badge.d.mts +24 -0
- package/dist/components/tasks/task-status-badge.mjs +24 -0
- package/dist/components/tasks/task-status-icon.d.mts +13 -0
- package/dist/components/tasks/task-status-icon.mjs +90 -0
- package/dist/components/tasks/task-status.recipe.d.mts +62 -0
- package/dist/components/tasks/task-status.recipe.mjs +33 -0
- package/dist/components/tasks/tasks-panel.d.mts +22 -0
- package/dist/components/tasks/tasks-panel.mjs +114 -0
- package/dist/components/tasks/tasks-panel.recipe.mjs +36 -0
- package/dist/components/text/index.d.mts +2 -0
- package/dist/components/text/text.d.mts +186 -0
- package/dist/components/text/text.mjs +114 -0
- package/dist/components/text-link/index.d.mts +2 -0
- package/dist/components/text-link/text-link.d.mts +36 -0
- package/dist/components/text-link/text-link.mjs +60 -0
- package/dist/components/text-selection-toolbar/index.d.mts +3 -0
- package/dist/components/text-selection-toolbar/text-selection-anchor.d.mts +22 -0
- package/dist/components/text-selection-toolbar/text-selection-anchor.mjs +106 -0
- package/dist/components/text-selection-toolbar/text-selection-toolbar.d.mts +20 -0
- package/dist/components/text-selection-toolbar/text-selection-toolbar.mjs +41 -0
- package/dist/components/text-selection-toolbar/text-selection-toolbar.recipe.mjs +19 -0
- package/dist/components/textarea/index.d.mts +2 -0
- package/dist/components/textarea/textarea.d.mts +16 -0
- package/dist/components/textarea/textarea.mjs +62 -0
- package/dist/components/thread-panel/index.d.mts +2 -0
- package/dist/components/thread-panel/thread-panel.d.mts +31 -0
- package/dist/components/thread-panel/thread-panel.mjs +162 -0
- package/dist/components/thread-panel/thread-panel.recipe.mjs +36 -0
- package/dist/components/timeline/index.d.mts +2 -0
- package/dist/components/timeline/timeline.d.mts +44 -0
- package/dist/components/timeline/timeline.mjs +81 -0
- package/dist/components/toast/index.d.mts +2 -0
- package/dist/components/toast/toast.d.mts +263 -0
- package/dist/components/toast/toast.mjs +556 -0
- package/dist/components/toggle-group/index.d.mts +2 -0
- package/dist/components/toggle-group/toggle-group.d.mts +15 -0
- package/dist/components/toggle-group/toggle-group.mjs +64 -0
- package/dist/components/tooltip/index.d.mts +2 -0
- package/dist/components/tooltip/tooltip.d.mts +18 -0
- package/dist/components/tooltip/tooltip.mjs +197 -0
- package/dist/components/virtual-list/ItemColumns.mjs +37 -0
- package/dist/components/virtual-list/VirtualList.d.mts +4 -0
- package/dist/components/virtual-list/VirtualList.mjs +1401 -0
- package/dist/components/virtual-list/VirtualListContext.mjs +5 -0
- package/dist/components/virtual-list/hooks/use-event-callback.mjs +10 -0
- package/dist/components/virtual-list/hooks/use-ios-momentum-compensation.mjs +148 -0
- package/dist/components/virtual-list/hooks/use-keyboard-scroll.mjs +42 -0
- package/dist/components/virtual-list/hooks/use-latest-ref.mjs +10 -0
- package/dist/components/virtual-list/hooks/use-layout-effect-with-previous.mjs +16 -0
- package/dist/components/virtual-list/hooks/use-memo-with-previous.mjs +22 -0
- package/dist/components/virtual-list/hooks/use-non-passive-wheel.mjs +23 -0
- package/dist/components/virtual-list/hooks/use-non-virtual-content-resize.mjs +28 -0
- package/dist/components/virtual-list/hooks/use-scroll-anchor.mjs +55 -0
- package/dist/components/virtual-list/index.d.mts +5 -0
- package/dist/components/virtual-list/public-types.d.mts +341 -0
- package/dist/components/virtual-list/resize-observer/ResizeObserverWrapper.d.mts +5 -0
- package/dist/components/virtual-list/resize-observer/ResizeObserverWrapper.mjs +70 -0
- package/dist/components/virtual-list/resize-observer/resize-observer-delegate.d.mts +6 -0
- package/dist/components/virtual-list/resize-observer/resize-observer-delegate.mjs +54 -0
- package/dist/components/virtual-list/utils/align.mjs +19 -0
- package/dist/components/virtual-list/utils/constants.mjs +30 -0
- package/dist/components/virtual-list/utils/fenwick-tree.mjs +66 -0
- package/dist/components/virtual-list/utils/items-anchor.mjs +49 -0
- package/dist/components/virtual-list/utils/measure.mjs +77 -0
- package/dist/components/virtual-list/utils/platform.mjs +39 -0
- package/dist/components/virtual-list/utils/range.mjs +103 -0
- package/dist/components/virtual-list/utils/size-cache.mjs +35 -0
- package/dist/components/workspace-button/index.d.mts +2 -0
- package/dist/components/workspace-button/workspace-button.d.mts +9 -0
- package/dist/components/workspace-button/workspace-button.mjs +28 -0
- package/dist/index.d.mts +239 -4864
- package/dist/index.mjs +142 -17054
- package/dist/lib/chip-remove.mjs +17 -0
- package/dist/lib/data-attributes.d.mts +5 -0
- package/dist/lib/decision-width.mjs +9 -0
- package/dist/lib/default-native-button.mjs +8 -0
- package/dist/lib/desktop-media.mjs +10 -0
- package/dist/lib/drag-escape.mjs +37 -0
- package/dist/lib/frame-insets.mjs +127 -0
- package/dist/lib/head-edges.mjs +44 -0
- package/dist/lib/hud-text-action.mjs +10 -0
- package/dist/lib/inline-chip.mjs +30 -0
- package/dist/lib/input-modality.d.mts +36 -0
- package/dist/lib/input-modality.mjs +186 -0
- package/dist/lib/list-cell.mjs +21 -0
- package/dist/lib/located.d.mts +25 -0
- package/dist/lib/located.mjs +28 -0
- package/dist/lib/overlay-depth.d.mts +42 -0
- package/dist/lib/overlay-depth.mjs +59 -0
- package/dist/lib/phone-media.mjs +24 -0
- package/dist/lib/presence.d.mts +16 -0
- package/dist/lib/presence.mjs +16 -0
- package/dist/lib/row-tone.d.mts +8 -0
- package/dist/lib/search-hit.mjs +18 -0
- package/dist/lib/stacked-actions.mjs +112 -0
- package/dist/lib/styled-props.d.mts +15 -0
- package/dist/lib/tap-activation.mjs +133 -0
- package/dist/lib/theme/theme-context.d.mts +10 -0
- package/dist/lib/theme/theme-context.mjs +9 -0
- package/dist/lib/theme/theme-provider.d.mts +23 -0
- package/dist/lib/theme/theme-provider.mjs +61 -0
- package/dist/lib/theme/theme-scope.mjs +19 -0
- package/dist/lib/theme/use-theme.d.mts +5 -0
- package/dist/lib/theme/use-theme.mjs +9 -0
- package/dist/lib/touch-drag.d.mts +55 -0
- package/dist/lib/touch-drag.mjs +151 -0
- package/dist/lib/truncation.d.mts +26 -0
- package/dist/lib/truncation.mjs +153 -0
- package/dist/lib/tv.d.mts +2 -0
- package/dist/lib/tv.mjs +14 -0
- package/dist/lib/use-copy-to-clipboard.mjs +58 -0
- package/dist/lib/use-merged-ref.mjs +60 -0
- package/dist/lib/use-mobile-viewport.mjs +17 -0
- package/dist/lib/use-overflow-title.mjs +49 -0
- package/dist/lib/use-scrollbar-size.d.mts +16 -0
- package/dist/lib/use-scrollbar-size.mjs +46 -0
- package/dist/lib/use-truncated.d.mts +20 -0
- package/dist/lib/use-truncated.mjs +34 -0
- package/dist/lib/without-children.d.mts +8 -0
- package/dist/wip.d.mts +61 -1190
- package/dist/wip.mjs +43 -2970
- package/package.json +8 -1
- package/dist/index-C3ymUW98.d.mts +0 -1816
- package/dist/mode-bar-tGnYByXp.mjs +0 -7564
- package/dist/style.css +0 -1395
package/dist/wip.mjs
CHANGED
|
@@ -1,2970 +1,43 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
* phone and never flips; when this is true the field is scrolled up instead.
|
|
45
|
-
*/
|
|
46
|
-
function roomBelowIsShort(field, viewport, rows = 3) {
|
|
47
|
-
return viewport.top + viewport.height - field.bottom - SIDE_OFFSET < rows * ROW + LIST_PADDING;
|
|
48
|
-
}
|
|
49
|
-
//#endregion
|
|
50
|
-
//#region src/components/combobox-select/combobox-select.options.ts
|
|
51
|
-
function isOptionGroup(option) {
|
|
52
|
-
return typeof option === "object" && option !== null && "type" in option && option.type === "group" && "children" in option && Array.isArray(option.children);
|
|
53
|
-
}
|
|
54
|
-
function normalizeOption(option, labelField, valueField, disabledField) {
|
|
55
|
-
const fields = option;
|
|
56
|
-
const value = fields[valueField];
|
|
57
|
-
const label = fields[labelField];
|
|
58
|
-
if (typeof value !== "string" && typeof value !== "number") throw new Error("ComboboxSelect options must have a string or number value.");
|
|
59
|
-
return {
|
|
60
|
-
value,
|
|
61
|
-
label: typeof label === "string" || typeof label === "number" ? String(label) : String(value),
|
|
62
|
-
disabled: Boolean(fields[disabledField]),
|
|
63
|
-
option
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
function optionKey(value) {
|
|
67
|
-
return `${typeof value}:${value}`;
|
|
68
|
-
}
|
|
69
|
-
//#endregion
|
|
70
|
-
//#region src/components/combobox-select/combobox-select.tsx
|
|
71
|
-
const defaultMessages = {
|
|
72
|
-
clear: "Clear selection",
|
|
73
|
-
toggle: "Toggle options",
|
|
74
|
-
selected: "Selected options",
|
|
75
|
-
chipDescription: "Press Backspace or Delete to remove",
|
|
76
|
-
inputDescription: "From the start of the input, press Left Arrow to focus selected options",
|
|
77
|
-
remove: (label) => `Remove ${label}`
|
|
78
|
-
};
|
|
79
|
-
const defaultRenderCreateLabel = (query) => `Create “${query}”`;
|
|
80
|
-
/** An option's or a chip's text, with its whole value as the title when it is cut off. */
|
|
81
|
-
function OverflowText({ children }) {
|
|
82
|
-
return /* @__PURE__ */ jsx("span", {
|
|
83
|
-
ref: useOverflowTitle(),
|
|
84
|
-
className: "r-combobox-select__text",
|
|
85
|
-
children
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
function recordLabel(record, renderLabel) {
|
|
89
|
-
return record.option === void 0 ? record.label : renderLabel?.(record.option) ?? record.label;
|
|
90
|
-
}
|
|
91
|
-
function renderOptionContent({ record, selected, renderLabel, renderOption, renderCreateLabel }) {
|
|
92
|
-
if (record.createQuery !== void 0) return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Plus, { "aria-hidden": true }), /* @__PURE__ */ jsx(OverflowText, { children: renderCreateLabel(record.label) })] });
|
|
93
|
-
const content = record.option === void 0 ? record.label : renderOption?.(record.option, { selected }) ?? recordLabel(record, renderLabel);
|
|
94
|
-
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(OverflowText, { children: content }), /* @__PURE__ */ jsx(Combobox.ItemIndicator, {
|
|
95
|
-
className: "r-combobox-select__indicator",
|
|
96
|
-
children: /* @__PURE__ */ jsx(Check, { "aria-hidden": true })
|
|
97
|
-
})] });
|
|
98
|
-
}
|
|
99
|
-
function renderSelectedChip({ record, index, tagLimit, strings, renderLabel, renderTag }) {
|
|
100
|
-
const label = recordLabel(record, renderLabel);
|
|
101
|
-
const content = record.option === void 0 ? record.label : renderTag?.(record.option) ?? label;
|
|
102
|
-
const overflow = index >= tagLimit;
|
|
103
|
-
return /* @__PURE__ */ jsxs(Combobox.Chip, {
|
|
104
|
-
className: "r-combobox-select__chip",
|
|
105
|
-
"data-overflow": overflow || void 0,
|
|
106
|
-
"aria-label": record.label,
|
|
107
|
-
"aria-description": strings.chipDescription,
|
|
108
|
-
children: [/* @__PURE__ */ jsx(OverflowText, { children: content }), /* @__PURE__ */ jsx(Combobox.ChipRemove, {
|
|
109
|
-
className: "r-combobox-select__remove",
|
|
110
|
-
"aria-label": strings.remove(record.label),
|
|
111
|
-
children: /* @__PURE__ */ jsx(X, { "aria-hidden": true })
|
|
112
|
-
})]
|
|
113
|
-
}, optionKey(record.value));
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* On a phone the list always opens below the field, between the field and the
|
|
117
|
-
* keyboard, as a native autocomplete does, and never flips: which side has more
|
|
118
|
-
* room depends on whether the keyboard happens to be up, so choosing a side made
|
|
119
|
-
* the same field open up one time and down the next.
|
|
120
|
-
*/
|
|
121
|
-
const PHONE_LIST_COLLISION = {
|
|
122
|
-
side: "none",
|
|
123
|
-
align: "shift",
|
|
124
|
-
fallbackAxisSide: "none"
|
|
125
|
-
};
|
|
126
|
-
function ComboboxSelectFramePositioner({ collisionPadding, container, ...props }) {
|
|
127
|
-
const padding = usePopupCollisionPadding(collisionPadding, 8, container);
|
|
128
|
-
return /* @__PURE__ */ jsx(Combobox.Positioner, {
|
|
129
|
-
collisionPadding: padding,
|
|
130
|
-
...props
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Keeps room for the list below the field on a phone: when fewer than three
|
|
135
|
-
* rows fit between the field and the bottom of the visual viewport (the
|
|
136
|
-
* keyboard's top edge while it is up), scrolls the field up to the top of
|
|
137
|
-
* what is visible instead of flipping the list above it.
|
|
138
|
-
*/
|
|
139
|
-
function keepRoomBelow(field) {
|
|
140
|
-
const view = field?.ownerDocument.defaultView;
|
|
141
|
-
if (!field || !view) return;
|
|
142
|
-
const visual = view.visualViewport;
|
|
143
|
-
const viewport = visual ? {
|
|
144
|
-
top: visual.offsetTop,
|
|
145
|
-
height: visual.height
|
|
146
|
-
} : {
|
|
147
|
-
top: 0,
|
|
148
|
-
height: view.innerHeight
|
|
149
|
-
};
|
|
150
|
-
if (roomBelowIsShort(field.getBoundingClientRect(), viewport)) {
|
|
151
|
-
if (!field.style.scrollMarginTop) field.style.scrollMarginTop = "2.5rem";
|
|
152
|
-
field.scrollIntoView({
|
|
153
|
-
block: "start",
|
|
154
|
-
inline: "nearest",
|
|
155
|
-
behavior: "instant"
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
/** Options-driven composition. The stable compound Combobox API stays unchanged. */
|
|
160
|
-
function isItemEqualToValue(a, b) {
|
|
161
|
-
return a.createQuery === b.createQuery && a.value === b.value;
|
|
162
|
-
}
|
|
163
|
-
function itemToStringLabel(record) {
|
|
164
|
-
return record.label;
|
|
165
|
-
}
|
|
166
|
-
function itemToStringValue(record) {
|
|
167
|
-
return String(record.value);
|
|
168
|
-
}
|
|
169
|
-
function ComboboxSelect({ options, multiple, value, defaultValue, onValueChange, labelField = "label", valueField = "value", disabledField = "disabled", fallbackOption, filterable = true, filter, remote = false, onSearch, loading = false, create = false, creatable, onCreate, clearable = false, showArrow = true, clearFilterAfterSelect = true, onClear, maxTagCount, size = "md", status, placeholder = "Select an option", renderLabel, renderOption, renderTag, renderCreateLabel = defaultRenderCreateLabel, emptyContent = "No options found", loadingContent = "Loading…", messages, listMaxHeight, inputProps, portalProps, positionerProps, popupProps, className, style, id: idProp, ref, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, inputValue, defaultInputValue, onInputValueChange, disabled, readOnly, autoHighlight = true, onOpenChange, ...rootProps }) {
|
|
170
|
-
const generatedId = useId();
|
|
171
|
-
const id = idProp ?? generatedId;
|
|
172
|
-
const { resolvedMode } = useTheme();
|
|
173
|
-
const scopedPortalProps = useThemeScopeProps(portalProps ?? {});
|
|
174
|
-
const strings = {
|
|
175
|
-
...defaultMessages,
|
|
176
|
-
...messages
|
|
177
|
-
};
|
|
178
|
-
const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue ?? (multiple ? [] : null));
|
|
179
|
-
const selectedValue = value === void 0 ? uncontrolledValue : value;
|
|
180
|
-
const selectedKeys = useMemo(() => Array.isArray(selectedValue) ? selectedValue : selectedValue == null ? [] : [selectedValue], [selectedValue]);
|
|
181
|
-
const [query, setQuery] = useState(String(defaultInputValue ?? ""));
|
|
182
|
-
const [createdOptions, setCreatedOptions] = useState([]);
|
|
183
|
-
const collator = useMemo(() => new Intl.Collator(void 0, {
|
|
184
|
-
sensitivity: "base",
|
|
185
|
-
usage: "search"
|
|
186
|
-
}), []);
|
|
187
|
-
const normalized = useMemo(() => {
|
|
188
|
-
const normalize = (option) => normalizeOption(option, labelField, valueField, disabledField);
|
|
189
|
-
const groups = [];
|
|
190
|
-
options.forEach((option, index) => {
|
|
191
|
-
if (isOptionGroup(option)) groups.push({
|
|
192
|
-
key: `group:${optionKey(option.key)}`,
|
|
193
|
-
label: option.label,
|
|
194
|
-
items: option.children.map(normalize)
|
|
195
|
-
});
|
|
196
|
-
else groups.push({
|
|
197
|
-
key: `option:${index}`,
|
|
198
|
-
items: [normalize(option)]
|
|
199
|
-
});
|
|
200
|
-
});
|
|
201
|
-
const records = new Map(groups.flatMap((group) => group.items.map((record) => [record.value, record])));
|
|
202
|
-
const created = createdOptions.map(normalize).filter((record) => !records.has(record.value));
|
|
203
|
-
created.forEach((record) => records.set(record.value, record));
|
|
204
|
-
if (created.length) groups.push({
|
|
205
|
-
key: "created",
|
|
206
|
-
items: created
|
|
207
|
-
});
|
|
208
|
-
return {
|
|
209
|
-
groups,
|
|
210
|
-
records
|
|
211
|
-
};
|
|
212
|
-
}, [
|
|
213
|
-
options,
|
|
214
|
-
createdOptions,
|
|
215
|
-
labelField,
|
|
216
|
-
valueField,
|
|
217
|
-
disabledField
|
|
218
|
-
]);
|
|
219
|
-
const [retained, setRetained] = useState(() => /* @__PURE__ */ new Map());
|
|
220
|
-
const selected = useMemo(() => {
|
|
221
|
-
return selectedKeys.map((key) => normalized.records.get(key) ?? retained.get(key) ?? (fallbackOption ? normalizeOption(fallbackOption(key), labelField, valueField, disabledField) : {
|
|
222
|
-
value: key,
|
|
223
|
-
label: String(key),
|
|
224
|
-
disabled: false
|
|
225
|
-
}));
|
|
226
|
-
}, [
|
|
227
|
-
selectedKeys,
|
|
228
|
-
normalized,
|
|
229
|
-
retained,
|
|
230
|
-
fallbackOption,
|
|
231
|
-
labelField,
|
|
232
|
-
valueField,
|
|
233
|
-
disabledField
|
|
234
|
-
]);
|
|
235
|
-
if (retained.size !== selected.length || !selected.every((record) => retained.get(record.value) === record)) setRetained(new Map(selected.map((record) => [record.value, record])));
|
|
236
|
-
const search = String(inputValue ?? query).trim();
|
|
237
|
-
const canCreate = (create || creatable) && filterable && !loading && search !== "" && ![...normalized.records.values(), ...selected].some((record) => record.label.trim().toLocaleLowerCase() === search.toLocaleLowerCase());
|
|
238
|
-
const items = useMemo(() => {
|
|
239
|
-
if (!canCreate) return normalized.groups;
|
|
240
|
-
const candidate = {
|
|
241
|
-
value: search,
|
|
242
|
-
label: search,
|
|
243
|
-
disabled: false,
|
|
244
|
-
createQuery: search
|
|
245
|
-
};
|
|
246
|
-
return [...normalized.groups, {
|
|
247
|
-
key: "create",
|
|
248
|
-
items: [candidate]
|
|
249
|
-
}];
|
|
250
|
-
}, [
|
|
251
|
-
canCreate,
|
|
252
|
-
normalized.groups,
|
|
253
|
-
search
|
|
254
|
-
]);
|
|
255
|
-
const css = `r-combobox-select r-combobox-select--${size}${resolvedMode === "dark" ? " r-combobox-select--dark" : ""}`;
|
|
256
|
-
const tagLimit = maxTagCount === void 0 ? Infinity : Math.max(0, Math.floor(maxTagCount));
|
|
257
|
-
const inputClassName = ["r-combobox-select__input", typeof inputProps?.className === "string" ? inputProps.className : null].filter(Boolean).join(" ");
|
|
258
|
-
const phone = usePhoneMedia();
|
|
259
|
-
const controlClassName = [
|
|
260
|
-
css,
|
|
261
|
-
"r-combobox-select__control has-focus-visible:shadow-focus",
|
|
262
|
-
phone ? "r-combobox-select__control--phone" : null,
|
|
263
|
-
className
|
|
264
|
-
].filter(Boolean).join(" ");
|
|
265
|
-
const positionerClassName = ["r-combobox-select__positioner", typeof positionerProps?.className === "string" ? positionerProps.className : null].filter(Boolean).join(" ");
|
|
266
|
-
const popupClassName = [
|
|
267
|
-
css,
|
|
268
|
-
"r-combobox-select__popup",
|
|
269
|
-
phone ? "r-combobox-select__popup--anchored" : null,
|
|
270
|
-
typeof popupProps?.className === "string" ? popupProps.className : null
|
|
271
|
-
].filter(Boolean).join(" ");
|
|
272
|
-
const inputRef = ref ?? inputProps?.ref;
|
|
273
|
-
const inputLabel = ariaLabel ?? inputProps?.["aria-label"];
|
|
274
|
-
const inputLabelledBy = ariaLabelledBy ?? inputProps?.["aria-labelledby"];
|
|
275
|
-
const inputDescribedBy = ariaDescribedBy ?? inputProps?.["aria-describedby"];
|
|
276
|
-
const inputInvalid = ariaInvalid ?? (status === "error" ? true : inputProps?.["aria-invalid"]);
|
|
277
|
-
const inputDescription = multiple && selected.length ? strings.inputDescription : inputProps?.["aria-description"];
|
|
278
|
-
const inputPlaceholder = multiple && selected.length ? "" : placeholder;
|
|
279
|
-
const inputReadOnly = readOnly || !filterable;
|
|
280
|
-
const controlValue = multiple ? selected : selected[0] ?? null;
|
|
281
|
-
const handleValueChange = useCallback((next, details) => {
|
|
282
|
-
const nextRecords = next === null ? [] : Array.isArray(next) ? next : [next];
|
|
283
|
-
const createRecord = nextRecords.find((record) => record.createQuery !== void 0);
|
|
284
|
-
let newOption;
|
|
285
|
-
let resolved = nextRecords;
|
|
286
|
-
if (createRecord) {
|
|
287
|
-
const created = onCreate ? onCreate(createRecord.label) : {
|
|
288
|
-
[labelField]: createRecord.label,
|
|
289
|
-
[valueField]: createRecord.label
|
|
290
|
-
};
|
|
291
|
-
if (created === null) {
|
|
292
|
-
details.cancel();
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
const record = normalizeOption(created, labelField, valueField, disabledField);
|
|
296
|
-
if (record.disabled) {
|
|
297
|
-
details.cancel();
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
newOption = created;
|
|
301
|
-
const existing = normalized.records.get(record.value) ?? retained.get(record.value);
|
|
302
|
-
resolved = nextRecords.map((item) => item === createRecord ? existing ?? record : item);
|
|
303
|
-
resolved = [...new Map(resolved.map((item) => [item.value, item])).values()];
|
|
304
|
-
}
|
|
305
|
-
const nextValue = multiple ? resolved.map((record) => record.value) : resolved[0]?.value ?? null;
|
|
306
|
-
const nextOptions = multiple ? resolved.map((record) => record.option) : resolved[0]?.option ?? null;
|
|
307
|
-
onValueChange?.(nextValue, nextOptions, details);
|
|
308
|
-
if (details.isCanceled) return;
|
|
309
|
-
if (newOption !== void 0) setCreatedOptions((previous) => [...previous, newOption]);
|
|
310
|
-
setRetained(new Map(resolved.map((record) => [record.value, record])));
|
|
311
|
-
if (value === void 0) setUncontrolledValue(nextValue);
|
|
312
|
-
if (details.reason === "clear-press") onClear?.();
|
|
313
|
-
}, [
|
|
314
|
-
multiple,
|
|
315
|
-
onCreate,
|
|
316
|
-
labelField,
|
|
317
|
-
valueField,
|
|
318
|
-
disabledField,
|
|
319
|
-
normalized.records,
|
|
320
|
-
retained,
|
|
321
|
-
onValueChange,
|
|
322
|
-
value,
|
|
323
|
-
onClear
|
|
324
|
-
]);
|
|
325
|
-
const handleInputValueChange = useCallback((next, details) => {
|
|
326
|
-
onInputValueChange?.(next, details);
|
|
327
|
-
if (details.isCanceled) return;
|
|
328
|
-
const shouldKeepQuery = details.reason === "input-change" || details.reason === "item-press" && !clearFilterAfterSelect;
|
|
329
|
-
setQuery(shouldKeepQuery ? next : "");
|
|
330
|
-
if (details.reason === "input-change" || details.reason === "clear-press") onSearch?.(next);
|
|
331
|
-
}, [
|
|
332
|
-
onInputValueChange,
|
|
333
|
-
clearFilterAfterSelect,
|
|
334
|
-
onSearch
|
|
335
|
-
]);
|
|
336
|
-
const filterOptions = useCallback((record, pattern) => {
|
|
337
|
-
if (record.createQuery !== void 0) return true;
|
|
338
|
-
if (remote || !filterable || filter === false) return true;
|
|
339
|
-
return record.option !== void 0 && typeof filter === "function" ? filter(pattern, record.option) : collator.compare(record.label.slice(0, pattern.length), pattern) === 0 || record.label.toLocaleLowerCase().includes(pattern.toLocaleLowerCase());
|
|
340
|
-
}, [
|
|
341
|
-
remote,
|
|
342
|
-
filterable,
|
|
343
|
-
filter,
|
|
344
|
-
collator
|
|
345
|
-
]);
|
|
346
|
-
const selectedChips = selected.map((record, index) => renderSelectedChip({
|
|
347
|
-
record,
|
|
348
|
-
index,
|
|
349
|
-
tagLimit,
|
|
350
|
-
strings,
|
|
351
|
-
renderLabel,
|
|
352
|
-
renderTag
|
|
353
|
-
}));
|
|
354
|
-
const overflowCount = selected.length > tagLimit ? selected.length - tagLimit : 0;
|
|
355
|
-
const overflowTitle = selected.slice(tagLimit).map((record) => record.label).join(", ");
|
|
356
|
-
const overflow = overflowCount ? /* @__PURE__ */ jsxs("span", {
|
|
357
|
-
className: "r-combobox-select__overflow",
|
|
358
|
-
title: overflowTitle,
|
|
359
|
-
children: ["+", overflowCount]
|
|
360
|
-
}) : null;
|
|
361
|
-
function renderRecord(record) {
|
|
362
|
-
const itemKey = record.createQuery !== void 0 ? "create" : optionKey(record.value);
|
|
363
|
-
const content = renderOptionContent({
|
|
364
|
-
record,
|
|
365
|
-
selected: selected.some((item) => item.value === record.value),
|
|
366
|
-
renderLabel,
|
|
367
|
-
renderOption,
|
|
368
|
-
renderCreateLabel
|
|
369
|
-
});
|
|
370
|
-
return /* @__PURE__ */ jsx(Combobox.Item, {
|
|
371
|
-
value: record,
|
|
372
|
-
disabled: record.disabled || loading,
|
|
373
|
-
className: "r-combobox-select__option",
|
|
374
|
-
children: content
|
|
375
|
-
}, itemKey);
|
|
376
|
-
}
|
|
377
|
-
function renderGroup(group) {
|
|
378
|
-
const groupLabel = group.label == null ? null : /* @__PURE__ */ jsx(Combobox.GroupLabel, {
|
|
379
|
-
className: "r-combobox-select__group-label",
|
|
380
|
-
children: group.label
|
|
381
|
-
});
|
|
382
|
-
return /* @__PURE__ */ jsxs(Combobox.Group, {
|
|
383
|
-
items: group.items,
|
|
384
|
-
children: [groupLabel, /* @__PURE__ */ jsx(Combobox.Collection, { children: renderRecord })]
|
|
385
|
-
}, group.key);
|
|
386
|
-
}
|
|
387
|
-
const input = /* @__PURE__ */ jsx(Combobox.Input, {
|
|
388
|
-
...inputProps,
|
|
389
|
-
ref: inputRef,
|
|
390
|
-
id,
|
|
391
|
-
"data-slot": "combobox-select-input",
|
|
392
|
-
className: inputClassName,
|
|
393
|
-
"aria-label": inputLabel,
|
|
394
|
-
"aria-labelledby": inputLabelledBy,
|
|
395
|
-
"aria-describedby": inputDescribedBy,
|
|
396
|
-
"aria-invalid": inputInvalid,
|
|
397
|
-
"aria-description": inputDescription,
|
|
398
|
-
placeholder: inputPlaceholder,
|
|
399
|
-
readOnly: inputReadOnly
|
|
400
|
-
});
|
|
401
|
-
const controlContent = multiple ? /* @__PURE__ */ jsxs(Combobox.Chips, {
|
|
402
|
-
className: "r-combobox-select__chips",
|
|
403
|
-
"aria-label": strings.selected,
|
|
404
|
-
children: [
|
|
405
|
-
selectedChips,
|
|
406
|
-
overflow,
|
|
407
|
-
input
|
|
408
|
-
]
|
|
409
|
-
}) : input;
|
|
410
|
-
const loadingMessage = loading ? /* @__PURE__ */ jsx("div", {
|
|
411
|
-
className: "r-combobox-select__message",
|
|
412
|
-
children: loadingContent
|
|
413
|
-
}) : null;
|
|
414
|
-
const emptyMessage = !loading ? /* @__PURE__ */ jsx("div", {
|
|
415
|
-
className: "r-combobox-select__message",
|
|
416
|
-
children: emptyContent
|
|
417
|
-
}) : null;
|
|
418
|
-
const loadingIndicator = loading ? /* @__PURE__ */ jsx(LoaderCircle, {
|
|
419
|
-
className: "r-combobox-select__spinner",
|
|
420
|
-
"aria-hidden": true
|
|
421
|
-
}) : null;
|
|
422
|
-
const clearAction = clearable ? /* @__PURE__ */ jsx(Combobox.Clear, {
|
|
423
|
-
className: "r-combobox-select__action",
|
|
424
|
-
"aria-label": strings.clear,
|
|
425
|
-
children: /* @__PURE__ */ jsx(X, { "aria-hidden": true })
|
|
426
|
-
}) : null;
|
|
427
|
-
const triggerAction = showArrow ? /* @__PURE__ */ jsx(Combobox.Trigger, {
|
|
428
|
-
className: "r-combobox-select__action",
|
|
429
|
-
"aria-label": strings.toggle,
|
|
430
|
-
children: /* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": true })
|
|
431
|
-
}) : null;
|
|
432
|
-
const actions = /* @__PURE__ */ jsxs("span", {
|
|
433
|
-
className: "r-combobox-select__actions",
|
|
434
|
-
children: [
|
|
435
|
-
loadingIndicator,
|
|
436
|
-
clearAction,
|
|
437
|
-
triggerAction
|
|
438
|
-
]
|
|
439
|
-
});
|
|
440
|
-
const valueChange = handleValueChange;
|
|
441
|
-
const listHeight = typeof listMaxHeight === "number" ? `${listMaxHeight}px` : listMaxHeight;
|
|
442
|
-
const fieldRef = useRef(null);
|
|
443
|
-
const [phoneOpen, setPhoneOpen] = useState(false);
|
|
444
|
-
const handleOpenChange = useCallback((next, details) => {
|
|
445
|
-
onOpenChange?.(next, details);
|
|
446
|
-
if (phone && !details.isCanceled) setPhoneOpen(next);
|
|
447
|
-
}, [onOpenChange, phone]);
|
|
448
|
-
useEffect(() => {
|
|
449
|
-
if (!phone || !phoneOpen) return;
|
|
450
|
-
const field = fieldRef.current;
|
|
451
|
-
const visual = field?.ownerDocument.defaultView?.visualViewport;
|
|
452
|
-
let frame = 0;
|
|
453
|
-
const check = () => {
|
|
454
|
-
cancelAnimationFrame(frame);
|
|
455
|
-
frame = requestAnimationFrame(() => keepRoomBelow(field));
|
|
456
|
-
};
|
|
457
|
-
check();
|
|
458
|
-
visual?.addEventListener("resize", check);
|
|
459
|
-
return () => {
|
|
460
|
-
cancelAnimationFrame(frame);
|
|
461
|
-
visual?.removeEventListener("resize", check);
|
|
462
|
-
};
|
|
463
|
-
}, [phone, phoneOpen]);
|
|
464
|
-
const popupPropsStyle = popupProps?.style;
|
|
465
|
-
const popupStyle = useMemo(() => {
|
|
466
|
-
if (listHeight === void 0) return popupPropsStyle;
|
|
467
|
-
return (state) => ({
|
|
468
|
-
...typeof popupPropsStyle === "function" ? popupPropsStyle(state) : popupPropsStyle,
|
|
469
|
-
"--combobox-select-list-max-height": listHeight
|
|
470
|
-
});
|
|
471
|
-
}, [listHeight, popupPropsStyle]);
|
|
472
|
-
return /* @__PURE__ */ jsxs(Combobox.Root, {
|
|
473
|
-
...rootProps,
|
|
474
|
-
onOpenChange: handleOpenChange,
|
|
475
|
-
id,
|
|
476
|
-
multiple,
|
|
477
|
-
disabled,
|
|
478
|
-
readOnly,
|
|
479
|
-
items,
|
|
480
|
-
value: controlValue,
|
|
481
|
-
onValueChange: valueChange,
|
|
482
|
-
inputValue,
|
|
483
|
-
defaultInputValue,
|
|
484
|
-
onInputValueChange: handleInputValueChange,
|
|
485
|
-
filter: filterOptions,
|
|
486
|
-
itemToStringLabel,
|
|
487
|
-
itemToStringValue,
|
|
488
|
-
isItemEqualToValue,
|
|
489
|
-
autoHighlight,
|
|
490
|
-
children: [/* @__PURE__ */ jsxs(Combobox.InputGroup, {
|
|
491
|
-
ref: fieldRef,
|
|
492
|
-
"data-slot": "combobox-select",
|
|
493
|
-
"data-status": status,
|
|
494
|
-
className: controlClassName,
|
|
495
|
-
style,
|
|
496
|
-
children: [controlContent, actions]
|
|
497
|
-
}), /* @__PURE__ */ jsx(Combobox.Portal, {
|
|
498
|
-
...scopedPortalProps,
|
|
499
|
-
children: /* @__PURE__ */ jsx(ComboboxSelectFramePositioner, {
|
|
500
|
-
sideOffset: 4,
|
|
501
|
-
align: "start",
|
|
502
|
-
...phone ? {
|
|
503
|
-
side: "bottom",
|
|
504
|
-
collisionAvoidance: PHONE_LIST_COLLISION
|
|
505
|
-
} : null,
|
|
506
|
-
...positionerProps,
|
|
507
|
-
container: portalProps?.container,
|
|
508
|
-
className: positionerClassName,
|
|
509
|
-
children: /* @__PURE__ */ jsxs(Combobox.Popup, {
|
|
510
|
-
initialFocus: false,
|
|
511
|
-
finalFocus: false,
|
|
512
|
-
...popupProps,
|
|
513
|
-
style: popupStyle,
|
|
514
|
-
"data-slot": "combobox-select-popup",
|
|
515
|
-
className: popupClassName,
|
|
516
|
-
children: [
|
|
517
|
-
/* @__PURE__ */ jsx(Combobox.Status, { children: loadingMessage }),
|
|
518
|
-
/* @__PURE__ */ jsx(Combobox.List, {
|
|
519
|
-
className: "r-combobox-select__list",
|
|
520
|
-
"aria-busy": loading,
|
|
521
|
-
children: renderGroup
|
|
522
|
-
}),
|
|
523
|
-
/* @__PURE__ */ jsx(Combobox.Empty, { children: emptyMessage })
|
|
524
|
-
]
|
|
525
|
-
})
|
|
526
|
-
})
|
|
527
|
-
})]
|
|
528
|
-
});
|
|
529
|
-
}
|
|
530
|
-
//#endregion
|
|
531
|
-
//#region src/components/announcement-dialog/announcement-dialog.tsx
|
|
532
|
-
const announcement = tv({ slots: {
|
|
533
|
-
content: ["@container/announcement max-w-xl gap-0 overflow-clip p-0"],
|
|
534
|
-
body: [
|
|
535
|
-
"min-h-0 max-h-[70dvh] overflow-y-auto pb-5",
|
|
536
|
-
"[&_[data-slot=announcement-picture]+[data-slot=announcement-text]]:mt-5",
|
|
537
|
-
"px-5 pt-5"
|
|
538
|
-
],
|
|
539
|
-
header: ["flex min-w-0 items-center justify-between gap-3"],
|
|
540
|
-
label: ["flex min-w-0 items-center gap-2 wrap-break-word", "text-caption text-foreground-muted"],
|
|
541
|
-
dot: ["shrink-0", "size-2.5 rounded-[2px] border-[1.5px] border-sticker-ink bg-attention-400"],
|
|
542
|
-
title: ["mt-3 wrap-break-word normal-case", "text-headline font-bold tracking-normal"],
|
|
543
|
-
picture: ["mt-5 h-44 w-full overflow-hidden border border-line-hairline [&_img]:size-full [&_img]:object-contain", "rounded-container bg-fill-muted"],
|
|
544
|
-
text: [
|
|
545
|
-
"mt-3 min-w-0 wrap-break-word [&_p+p]:mt-3",
|
|
546
|
-
"[&_ul]:list-disc [&_ol]:list-decimal [&_ul]:pl-5 [&_ol]:pl-5 [&_h1]:text-xl [&_h2]:text-lg [&_h3]:text-base [&_h1]:font-bold [&_h2]:font-bold [&_h3]:font-bold [&_pre]:max-w-full [&_pre]:overflow-x-auto",
|
|
547
|
-
"text-subhead text-foreground-muted"
|
|
548
|
-
],
|
|
549
|
-
footer: ["flex shrink-0 flex-wrap items-center justify-between gap-3 border-line-hairline py-4 @max-[28rem]/announcement:flex-col", "border-t-0 px-5 pt-0 pb-5"],
|
|
550
|
-
action: ["ml-auto h-auto min-h-8 min-w-0 max-w-full py-1 whitespace-normal text-center font-bold @max-[28rem]/announcement:ml-0 @max-[28rem]/announcement:w-full"],
|
|
551
|
-
secondary: ["min-w-0 max-w-full whitespace-normal @max-[28rem]/announcement:w-full"]
|
|
552
|
-
} });
|
|
553
|
-
function AnnouncementDialogContent({ className, initialFocus, ref, ...props }) {
|
|
554
|
-
const localRef = useRef(null);
|
|
555
|
-
const setRef = useCallback((node) => {
|
|
556
|
-
localRef.current = node;
|
|
557
|
-
if (typeof ref === "function") return ref(node);
|
|
558
|
-
if (ref) ref.current = node;
|
|
559
|
-
}, [ref]);
|
|
560
|
-
const focusAction = useCallback(() => localRef.current?.querySelector("[data-announcement-action]:not(:disabled)") ?? true, []);
|
|
561
|
-
const { content } = announcement();
|
|
562
|
-
return /* @__PURE__ */ jsx(DialogContent, {
|
|
563
|
-
ref: setRef,
|
|
564
|
-
initialFocus: initialFocus ?? focusAction,
|
|
565
|
-
className: content({ className }),
|
|
566
|
-
...props
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
|
-
function AnnouncementDialogBody({ className, ...props }) {
|
|
570
|
-
const { body } = announcement();
|
|
571
|
-
return /* @__PURE__ */ jsx("div", {
|
|
572
|
-
"data-slot": "announcement-body",
|
|
573
|
-
className: body({ className }),
|
|
574
|
-
...props
|
|
575
|
-
});
|
|
576
|
-
}
|
|
577
|
-
function AnnouncementDialogHeader({ className, ...props }) {
|
|
578
|
-
const { header } = announcement();
|
|
579
|
-
return /* @__PURE__ */ jsx("div", {
|
|
580
|
-
"data-slot": "announcement-header",
|
|
581
|
-
className: header({ className }),
|
|
582
|
-
...props
|
|
583
|
-
});
|
|
584
|
-
}
|
|
585
|
-
function AnnouncementDialogLabel({ className, children, ...props }) {
|
|
586
|
-
const { label, dot } = announcement();
|
|
587
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
588
|
-
"data-slot": "announcement-label",
|
|
589
|
-
className: label({ className }),
|
|
590
|
-
...props,
|
|
591
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
592
|
-
"aria-hidden": "true",
|
|
593
|
-
className: dot()
|
|
594
|
-
}), children]
|
|
595
|
-
});
|
|
596
|
-
}
|
|
597
|
-
function AnnouncementDialogTitle({ className, ...props }) {
|
|
598
|
-
const { title } = announcement();
|
|
599
|
-
return /* @__PURE__ */ jsx(DialogTitle, {
|
|
600
|
-
className: title({ className }),
|
|
601
|
-
...props
|
|
602
|
-
});
|
|
603
|
-
}
|
|
604
|
-
function AnnouncementDialogPicture({ className, ...props }) {
|
|
605
|
-
const { picture } = announcement();
|
|
606
|
-
return /* @__PURE__ */ jsx("div", {
|
|
607
|
-
"data-slot": "announcement-picture",
|
|
608
|
-
className: picture({ className }),
|
|
609
|
-
...props
|
|
610
|
-
});
|
|
611
|
-
}
|
|
612
|
-
function AnnouncementDialogText({ className, ...props }) {
|
|
613
|
-
const { text } = announcement();
|
|
614
|
-
return /* @__PURE__ */ jsx("div", {
|
|
615
|
-
"data-slot": "announcement-text",
|
|
616
|
-
className: text({ className }),
|
|
617
|
-
...props
|
|
618
|
-
});
|
|
619
|
-
}
|
|
620
|
-
function AnnouncementDialogFooter({ className, ...props }) {
|
|
621
|
-
const { footer } = announcement();
|
|
622
|
-
return /* @__PURE__ */ jsx("div", {
|
|
623
|
-
"data-slot": "announcement-footer",
|
|
624
|
-
className: footer({ className }),
|
|
625
|
-
...props
|
|
626
|
-
});
|
|
627
|
-
}
|
|
628
|
-
function AnnouncementDialogAction({ className, ...props }) {
|
|
629
|
-
const { action } = announcement();
|
|
630
|
-
return /* @__PURE__ */ jsx(Button$1, {
|
|
631
|
-
variant: "accent",
|
|
632
|
-
"data-announcement-action": true,
|
|
633
|
-
className: action({ className }),
|
|
634
|
-
...props
|
|
635
|
-
});
|
|
636
|
-
}
|
|
637
|
-
function AnnouncementDialogSecondaryAction({ className, ...props }) {
|
|
638
|
-
const { secondary } = announcement();
|
|
639
|
-
return /* @__PURE__ */ jsx(Button$1, {
|
|
640
|
-
variant: "ghost",
|
|
641
|
-
className: secondary({ className }),
|
|
642
|
-
...props
|
|
643
|
-
});
|
|
644
|
-
}
|
|
645
|
-
//#endregion
|
|
646
|
-
//#region src/components/comment/comment.recipe.ts
|
|
647
|
-
const comment = tv({
|
|
648
|
-
slots: {
|
|
649
|
-
margin: ["relative min-w-0"],
|
|
650
|
-
marginItem: [
|
|
651
|
-
"absolute inset-x-0 top-0",
|
|
652
|
-
"data-animate:transition-transform data-animate:duration-150 data-animate:ease-out",
|
|
653
|
-
"motion-reduce:transition-none",
|
|
654
|
-
"data-active:z-10"
|
|
655
|
-
],
|
|
656
|
-
card: [
|
|
657
|
-
"relative flex min-w-0 flex-col gap-2",
|
|
658
|
-
"rounded-container border border-line-hairline p-3",
|
|
659
|
-
"bg-layer-card text-foreground shadow-none outline-none",
|
|
660
|
-
"data-active:border-[1.5px] data-active:border-line-strong data-active:p-[11.5px] data-active:shadow-float"
|
|
661
|
-
],
|
|
662
|
-
cardTrigger: [
|
|
663
|
-
"absolute -inset-px z-10 cursor-pointer",
|
|
664
|
-
"rounded-container border-0 bg-transparent p-0",
|
|
665
|
-
"hover:bg-fill-subtle active:bg-fill-medium",
|
|
666
|
-
"outline-none focus-visible:shadow-focus"
|
|
667
|
-
],
|
|
668
|
-
cardQuote: ["sr-only"],
|
|
669
|
-
cardMeta: [
|
|
670
|
-
"flex min-w-0 items-center gap-1",
|
|
671
|
-
"font-sans text-caption text-foreground-hint",
|
|
672
|
-
"[&_svg]:size-3.5 [&_svg]:shrink-0",
|
|
673
|
-
"[button&]:enabled:cursor-pointer [button&]:border-0 [button&]:bg-transparent [button&]:p-0 [button&]:text-start",
|
|
674
|
-
"[button&]:decoration-[1.5px] [button&]:underline-offset-[3px] [button&]:enabled:hover:underline",
|
|
675
|
-
"[button&]:rounded-chip [button&]:outline-none [button&]:focus-visible:shadow-focus"
|
|
676
|
-
],
|
|
677
|
-
entry: ["flex min-w-0 flex-col gap-1", "[[data-slot=comment-entry]+&]:mt-1"],
|
|
678
|
-
entryHeader: ["flex min-w-0 items-center gap-1.5", "*:data-[slot=avatar]:me-0.5"],
|
|
679
|
-
entryAuthor: ["min-w-0 truncate", "text-subhead font-bold text-foreground"],
|
|
680
|
-
entryTime: ["shrink-0 whitespace-nowrap", "font-mono text-mono-meta tabular-nums text-foreground-hint"],
|
|
681
|
-
entryBody: [
|
|
682
|
-
"wrap-break-word select-text",
|
|
683
|
-
"text-callout text-foreground",
|
|
684
|
-
"selection:bg-selection"
|
|
685
|
-
],
|
|
686
|
-
marker: [
|
|
687
|
-
"relative inline-flex h-6 shrink-0 items-center gap-1 align-top whitespace-nowrap",
|
|
688
|
-
"rounded-chip border border-line-hairline py-0 ps-[3px] pe-1.5",
|
|
689
|
-
"font-mono text-mono-meta font-bold tabular-nums text-foreground",
|
|
690
|
-
"bg-layer-card [--avatar-group-surface:var(--layer-card)]",
|
|
691
|
-
"cursor-pointer hover:overlay-medium active:overlay-medium",
|
|
692
|
-
"data-active:border-[1.5px] data-active:border-line-strong data-active:bg-brand-soft data-active:ps-[2.5px] data-active:pe-[5.5px]",
|
|
693
|
-
"data-popup-open:border-[1.5px] data-popup-open:border-line-strong data-popup-open:bg-brand-soft data-popup-open:ps-[2.5px] data-popup-open:pe-[5.5px]",
|
|
694
|
-
"data-active:[--avatar-group-surface:var(--brand-soft)] data-popup-open:[--avatar-group-surface:var(--brand-soft)]",
|
|
695
|
-
"outline-none focus-visible:shadow-focus",
|
|
696
|
-
"after:absolute after:-inset-x-1 after:-inset-y-2.5"
|
|
697
|
-
]
|
|
698
|
-
},
|
|
699
|
-
variants: { clamp: { true: { entryBody: ["line-clamp-2"] } } }
|
|
700
|
-
});
|
|
701
|
-
//#endregion
|
|
702
|
-
//#region src/components/comment/comment-card.tsx
|
|
703
|
-
/**
|
|
704
|
-
* The card of one discussion: its comments, a line such as "2 replies", and
|
|
705
|
-
* the reply box when it is open. A `div`; which parts it holds (collapsed or
|
|
706
|
-
* open) is the caller's choice. To open it with a click, add
|
|
707
|
-
* `CommentCardTrigger`.
|
|
708
|
-
*/
|
|
709
|
-
function CommentCard({ className, render, active = false, ...props }) {
|
|
710
|
-
const { card } = comment();
|
|
711
|
-
const defaultProps = {
|
|
712
|
-
"data-slot": "comment-card",
|
|
713
|
-
"data-active": active ? "" : void 0,
|
|
714
|
-
"aria-current": active ? "true" : void 0,
|
|
715
|
-
className: card({ className })
|
|
716
|
-
};
|
|
717
|
-
return useRender({
|
|
718
|
-
defaultTagName: "div",
|
|
719
|
-
render,
|
|
720
|
-
props: mergeProps(defaultProps, props)
|
|
721
|
-
});
|
|
722
|
-
}
|
|
723
|
-
/**
|
|
724
|
-
* A button laid over the whole card, for a collapsed card that opens when it
|
|
725
|
-
* is clicked or chosen with the keyboard. It has no text of its own: give it
|
|
726
|
-
* an `aria-label` ("Comment from Martin").
|
|
727
|
-
*/
|
|
728
|
-
function CommentCardTrigger({ className, render, ...props }) {
|
|
729
|
-
const { cardTrigger } = comment();
|
|
730
|
-
const defaultProps = {
|
|
731
|
-
"data-slot": "comment-card-trigger",
|
|
732
|
-
type: "button",
|
|
733
|
-
className: cardTrigger({ className })
|
|
734
|
-
};
|
|
735
|
-
return useRender({
|
|
736
|
-
defaultTagName: "button",
|
|
737
|
-
render,
|
|
738
|
-
props: mergeProps(defaultProps, props)
|
|
739
|
-
});
|
|
740
|
-
}
|
|
741
|
-
/**
|
|
742
|
-
* A short line about the discussion, in the caller's words: how many replies
|
|
743
|
-
* it has, that it is about the whole document, or that the passage it was on
|
|
744
|
-
* can no longer be found. When choosing it does something (makes this the
|
|
745
|
-
* discussion being read), render it as a button,
|
|
746
|
-
* `render={<button type="button" />}`: it then underlines on hover, as
|
|
747
|
-
* clickable text, and takes the focus ring.
|
|
748
|
-
*/
|
|
749
|
-
function CommentCardMeta({ className, render, ...props }) {
|
|
750
|
-
const { cardMeta } = comment();
|
|
751
|
-
const defaultProps = {
|
|
752
|
-
"data-slot": "comment-card-meta",
|
|
753
|
-
className: cardMeta({ className })
|
|
754
|
-
};
|
|
755
|
-
return useRender({
|
|
756
|
-
defaultTagName: "div",
|
|
757
|
-
render,
|
|
758
|
-
props: mergeProps(defaultProps, props)
|
|
759
|
-
});
|
|
760
|
-
}
|
|
761
|
-
/**
|
|
762
|
-
* The passage the discussion is about, in the caller's words (usually the
|
|
763
|
-
* quoted text). Hidden on screen, where the card's place beside the
|
|
764
|
-
* highlighted passage says it; give it an `id` and point the
|
|
765
|
-
* `aria-describedby` of `CommentCardTrigger` or `CommentMarker` at it, so a
|
|
766
|
-
* screen reader says which passage a card is about.
|
|
767
|
-
*/
|
|
768
|
-
function CommentCardQuote({ className, render, ...props }) {
|
|
769
|
-
const { cardQuote } = comment();
|
|
770
|
-
const defaultProps = {
|
|
771
|
-
"data-slot": "comment-card-quote",
|
|
772
|
-
className: cardQuote({ className })
|
|
773
|
-
};
|
|
774
|
-
return useRender({
|
|
775
|
-
defaultTagName: "span",
|
|
776
|
-
render,
|
|
777
|
-
props: mergeProps(defaultProps, props)
|
|
778
|
-
});
|
|
779
|
-
}
|
|
780
|
-
//#endregion
|
|
781
|
-
//#region src/components/comment/comment-entry.tsx
|
|
782
|
-
/** One comment in a discussion: its header, then its text. */
|
|
783
|
-
function CommentEntry({ className, render, ...props }) {
|
|
784
|
-
const { entry } = comment();
|
|
785
|
-
const defaultProps = {
|
|
786
|
-
"data-slot": "comment-entry",
|
|
787
|
-
className: entry({ className })
|
|
788
|
-
};
|
|
789
|
-
return useRender({
|
|
790
|
-
defaultTagName: "div",
|
|
791
|
-
render,
|
|
792
|
-
props: mergeProps(defaultProps, props)
|
|
793
|
-
});
|
|
794
|
-
}
|
|
795
|
-
/** One line: the author's `Avatar` (`size="xs"`), `CommentEntryAuthor`, then `CommentEntryTime`. */
|
|
796
|
-
function CommentEntryHeader({ className, render, ...props }) {
|
|
797
|
-
const { entryHeader } = comment();
|
|
798
|
-
const defaultProps = {
|
|
799
|
-
"data-slot": "comment-entry-header",
|
|
800
|
-
className: entryHeader({ className })
|
|
801
|
-
};
|
|
802
|
-
return useRender({
|
|
803
|
-
defaultTagName: "div",
|
|
804
|
-
render,
|
|
805
|
-
props: mergeProps(defaultProps, props)
|
|
806
|
-
});
|
|
807
|
-
}
|
|
808
|
-
/** Who wrote the comment, in bold. It truncates; the time after it doesn't. */
|
|
809
|
-
function CommentEntryAuthor({ className, render, ...props }) {
|
|
810
|
-
const { entryAuthor } = comment();
|
|
811
|
-
const defaultProps = {
|
|
812
|
-
"data-slot": "comment-entry-author",
|
|
813
|
-
className: entryAuthor({ className })
|
|
814
|
-
};
|
|
815
|
-
return useRender({
|
|
816
|
-
defaultTagName: "span",
|
|
817
|
-
render,
|
|
818
|
-
props: mergeProps(defaultProps, props)
|
|
819
|
-
});
|
|
820
|
-
}
|
|
821
|
-
/**
|
|
822
|
-
* When the comment was written, right after the author. A `<time>`: pass the
|
|
823
|
-
* moment in `dateTime` (ISO 8601).
|
|
824
|
-
*/
|
|
825
|
-
function CommentEntryTime({ className, render, ...props }) {
|
|
826
|
-
const { entryTime } = comment();
|
|
827
|
-
const defaultProps = {
|
|
828
|
-
"data-slot": "comment-entry-time",
|
|
829
|
-
className: entryTime({ className })
|
|
830
|
-
};
|
|
831
|
-
return useRender({
|
|
832
|
-
defaultTagName: "time",
|
|
833
|
-
render,
|
|
834
|
-
props: mergeProps(defaultProps, props)
|
|
835
|
-
});
|
|
836
|
-
}
|
|
837
|
-
/** What the comment says. */
|
|
838
|
-
function CommentEntryBody({ className, render, clamp = false, ...props }) {
|
|
839
|
-
const { entryBody } = comment({ clamp });
|
|
840
|
-
const defaultProps = {
|
|
841
|
-
"data-slot": "comment-entry-body",
|
|
842
|
-
"data-clamp": clamp ? "" : void 0,
|
|
843
|
-
className: entryBody({ className })
|
|
844
|
-
};
|
|
845
|
-
return useRender({
|
|
846
|
-
defaultTagName: "div",
|
|
847
|
-
render,
|
|
848
|
-
props: mergeProps(defaultProps, props)
|
|
849
|
-
});
|
|
850
|
-
}
|
|
851
|
-
//#endregion
|
|
852
|
-
//#region src/components/comment/comment-highlight.ts
|
|
853
|
-
/** The highlight of every passage that has comments (`::highlight(raft-comment)` in styles.css). */
|
|
854
|
-
const COMMENT_HIGHLIGHT = "raft-comment";
|
|
855
|
-
/** The highlight of the passage of the open discussion (`::highlight(raft-comment-active)`). */
|
|
856
|
-
const COMMENT_HIGHLIGHT_ACTIVE = "raft-comment-active";
|
|
857
|
-
/**
|
|
858
|
-
* Highlights the passages comments are on with the CSS Custom Highlight API,
|
|
859
|
-
* so the document's DOM is never changed: no `<mark>` is wrapped around its
|
|
860
|
-
* text. Register the ranges of every anchored comment under
|
|
861
|
-
* `COMMENT_HIGHLIGHT`, and the open discussion's under
|
|
862
|
-
* `COMMENT_HIGHLIGHT_ACTIVE`, which paints over it. Passing no ranges clears
|
|
863
|
-
* the name. Returns a function that clears it.
|
|
864
|
-
*
|
|
865
|
-
* Where the browser has no `CSS.highlights`, it does nothing: the passages are
|
|
866
|
-
* not highlighted, and the cards still say where they belong.
|
|
867
|
-
*/
|
|
868
|
-
function setCommentHighlights(name, ranges) {
|
|
869
|
-
const registry = typeof CSS === "undefined" ? void 0 : CSS.highlights;
|
|
870
|
-
if (!registry || typeof Highlight === "undefined") return () => {};
|
|
871
|
-
const list = [...ranges];
|
|
872
|
-
if (list.length === 0) {
|
|
873
|
-
registry.delete(name);
|
|
874
|
-
return () => {};
|
|
875
|
-
}
|
|
876
|
-
const highlight = new Highlight(...list);
|
|
877
|
-
if (name === "raft-comment-active") highlight.priority = 1;
|
|
878
|
-
registry.set(name, highlight);
|
|
879
|
-
return () => {
|
|
880
|
-
if (registry.get(name) === highlight) registry.delete(name);
|
|
881
|
-
};
|
|
882
|
-
}
|
|
883
|
-
//#endregion
|
|
884
|
-
//#region src/components/comment/comment-margin-layout.ts
|
|
885
|
-
/**
|
|
886
|
-
* Places the cards of a comment margin the way Google Docs does. Cards keep
|
|
887
|
-
* document order (by their anchor's top; equal tops keep the order given) and
|
|
888
|
-
* never overlap: each sits at its anchor, or `gap` below the card before it.
|
|
889
|
-
* The active card is pinned to its anchor instead, the cards above it are
|
|
890
|
-
* pushed up to make room and the cards below it pushed down. Nothing goes
|
|
891
|
-
* above 0: when the cards above the active one don't fit between 0 and its
|
|
892
|
-
* anchor, they stack from 0 and the active card follows them.
|
|
893
|
-
*/
|
|
894
|
-
function resolveCommentMarginLayout(items, { gap = 8, activeId = null } = {}) {
|
|
895
|
-
const order = items.map((item, index) => ({
|
|
896
|
-
item,
|
|
897
|
-
index
|
|
898
|
-
})).sort((a, b) => a.item.top - b.item.top || a.index - b.index).map(({ item }) => item);
|
|
899
|
-
const tops = order.map((item) => item.top);
|
|
900
|
-
const stackDown = (from, floor) => {
|
|
901
|
-
let next = floor;
|
|
902
|
-
for (let i = from; i < order.length; i += 1) {
|
|
903
|
-
tops[i] = Math.max(order[i].top, next);
|
|
904
|
-
next = tops[i] + order[i].height + gap;
|
|
905
|
-
}
|
|
906
|
-
};
|
|
907
|
-
const pinned = activeId == null ? -1 : order.findIndex((item) => item.id === activeId);
|
|
908
|
-
if (pinned < 0) stackDown(0, 0);
|
|
909
|
-
else {
|
|
910
|
-
tops[pinned] = Math.max(order[pinned].top, 0);
|
|
911
|
-
for (let i = pinned - 1; i >= 0; i -= 1) tops[i] = Math.min(order[i].top, tops[i + 1] - gap - order[i].height);
|
|
912
|
-
if (pinned > 0 && tops[0] < 0) stackDown(0, 0);
|
|
913
|
-
else stackDown(pinned + 1, tops[pinned] + order[pinned].height + gap);
|
|
914
|
-
}
|
|
915
|
-
const result = {};
|
|
916
|
-
let height = 0;
|
|
917
|
-
order.forEach((item, i) => {
|
|
918
|
-
result[item.id] = tops[i];
|
|
919
|
-
height = Math.max(height, tops[i] + item.height);
|
|
920
|
-
});
|
|
921
|
-
return {
|
|
922
|
-
tops: result,
|
|
923
|
-
height
|
|
924
|
-
};
|
|
925
|
-
}
|
|
926
|
-
//#endregion
|
|
927
|
-
//#region src/components/comment/comment-margin.tsx
|
|
928
|
-
const CommentMarginContext = createContext(null);
|
|
929
|
-
function useCommentMargin() {
|
|
930
|
-
const context = use(CommentMarginContext);
|
|
931
|
-
if (!context) throw new Error("CommentMarginItem must be used within CommentMargin.");
|
|
932
|
-
return context;
|
|
933
|
-
}
|
|
934
|
-
const heightOf = (element) => element.getBoundingClientRect().height;
|
|
935
|
-
/**
|
|
936
|
-
* The column beside a document that holds its comment cards, each level with
|
|
937
|
-
* the passage it is about. It measures its `CommentMarginItem`s with one
|
|
938
|
-
* `ResizeObserver`, resolves their collisions with
|
|
939
|
-
* `resolveCommentMarginLayout`, and takes the height the lowest card needs.
|
|
940
|
-
* Place it beside the document so that its top is the top the items' `top`
|
|
941
|
-
* is measured from.
|
|
942
|
-
*/
|
|
943
|
-
function CommentMargin({ className, render, style, gap = 8, ...props }) {
|
|
944
|
-
const { margin } = comment();
|
|
945
|
-
const [entries, setEntries] = useState(() => /* @__PURE__ */ new Map());
|
|
946
|
-
const [animate, setAnimate] = useState(false);
|
|
947
|
-
const observer = useRef(null);
|
|
948
|
-
const observed = useRef(/* @__PURE__ */ new Map());
|
|
949
|
-
const patch = useCallback((id, next) => {
|
|
950
|
-
setEntries((current) => {
|
|
951
|
-
const previous = current.get(id);
|
|
952
|
-
if (next === null) {
|
|
953
|
-
if (!previous) return current;
|
|
954
|
-
const without = new Map(current);
|
|
955
|
-
without.delete(id);
|
|
956
|
-
return without;
|
|
957
|
-
}
|
|
958
|
-
if (!previous && next.top === void 0) return current;
|
|
959
|
-
const merged = {
|
|
960
|
-
top: 0,
|
|
961
|
-
height: 0,
|
|
962
|
-
active: false,
|
|
963
|
-
...previous,
|
|
964
|
-
...next
|
|
965
|
-
};
|
|
966
|
-
if (previous && previous.top === merged.top && previous.height === merged.height && previous.active === merged.active) return current;
|
|
967
|
-
const updated = new Map(current);
|
|
968
|
-
updated.set(id, merged);
|
|
969
|
-
return updated;
|
|
970
|
-
});
|
|
971
|
-
}, []);
|
|
972
|
-
const place = useCallback((id, placement) => patch(id, placement), [patch]);
|
|
973
|
-
const observe = useCallback((id, element) => {
|
|
974
|
-
observer.current ??= new ResizeObserver((records) => {
|
|
975
|
-
for (const record of records) {
|
|
976
|
-
const target = observed.current.get(record.target);
|
|
977
|
-
if (target) patch(target, { height: heightOf(record.target) });
|
|
978
|
-
}
|
|
979
|
-
});
|
|
980
|
-
observed.current.set(element, id);
|
|
981
|
-
patch(id, { height: heightOf(element) });
|
|
982
|
-
observer.current.observe(element);
|
|
983
|
-
return () => {
|
|
984
|
-
observer.current?.unobserve(element);
|
|
985
|
-
observed.current.delete(element);
|
|
986
|
-
};
|
|
987
|
-
}, [patch]);
|
|
988
|
-
useEffect(() => () => observer.current?.disconnect(), []);
|
|
989
|
-
const layout = useMemo(() => {
|
|
990
|
-
const items = [...entries].map(([id, { top, height }]) => ({
|
|
991
|
-
id,
|
|
992
|
-
top,
|
|
993
|
-
height
|
|
994
|
-
}));
|
|
995
|
-
const activeId = [...entries].find(([, entry]) => entry.active)?.[0];
|
|
996
|
-
return resolveCommentMarginLayout(items, {
|
|
997
|
-
gap,
|
|
998
|
-
activeId
|
|
999
|
-
});
|
|
1000
|
-
}, [entries, gap]);
|
|
1001
|
-
const measured = entries.size > 0;
|
|
1002
|
-
useEffect(() => {
|
|
1003
|
-
if (!measured) return;
|
|
1004
|
-
const frame = requestAnimationFrame(() => setAnimate(true));
|
|
1005
|
-
return () => cancelAnimationFrame(frame);
|
|
1006
|
-
}, [measured]);
|
|
1007
|
-
const context = useMemo(() => ({
|
|
1008
|
-
tops: layout.tops,
|
|
1009
|
-
animate,
|
|
1010
|
-
place,
|
|
1011
|
-
observe
|
|
1012
|
-
}), [
|
|
1013
|
-
layout.tops,
|
|
1014
|
-
animate,
|
|
1015
|
-
place,
|
|
1016
|
-
observe
|
|
1017
|
-
]);
|
|
1018
|
-
const defaultProps = {
|
|
1019
|
-
"data-slot": "comment-margin",
|
|
1020
|
-
className: margin({ className }),
|
|
1021
|
-
style: {
|
|
1022
|
-
minHeight: layout.height,
|
|
1023
|
-
...style
|
|
1024
|
-
}
|
|
1025
|
-
};
|
|
1026
|
-
const element = useRender({
|
|
1027
|
-
defaultTagName: "div",
|
|
1028
|
-
render,
|
|
1029
|
-
props: mergeProps(defaultProps, props)
|
|
1030
|
-
});
|
|
1031
|
-
return /* @__PURE__ */ jsx(CommentMarginContext, {
|
|
1032
|
-
value: context,
|
|
1033
|
-
children: element
|
|
1034
|
-
});
|
|
1035
|
-
}
|
|
1036
|
-
/**
|
|
1037
|
-
* One card's place in a `CommentMargin`. It sits at `top`, or lower when the
|
|
1038
|
-
* card above it is in the way; the active one stays at `top` and the others
|
|
1039
|
-
* make room. Items with the same `top` keep the order they were first
|
|
1040
|
-
* rendered in. The resolved top is `data-top`, applied as a `translateY`.
|
|
1041
|
-
*/
|
|
1042
|
-
function CommentMarginItem({ className, render, style, top, active = false, ...props }) {
|
|
1043
|
-
const { marginItem } = comment();
|
|
1044
|
-
const { tops, animate, place, observe } = useCommentMargin();
|
|
1045
|
-
const id = useId();
|
|
1046
|
-
const [element, setElement] = useState(null);
|
|
1047
|
-
useLayoutEffect(() => {
|
|
1048
|
-
place(id, {
|
|
1049
|
-
top,
|
|
1050
|
-
active
|
|
1051
|
-
});
|
|
1052
|
-
}, [
|
|
1053
|
-
place,
|
|
1054
|
-
id,
|
|
1055
|
-
top,
|
|
1056
|
-
active
|
|
1057
|
-
]);
|
|
1058
|
-
useLayoutEffect(() => element ? observe(id, element) : void 0, [
|
|
1059
|
-
observe,
|
|
1060
|
-
id,
|
|
1061
|
-
element
|
|
1062
|
-
]);
|
|
1063
|
-
useLayoutEffect(() => () => place(id, null), [place, id]);
|
|
1064
|
-
const resolved = tops[id] ?? Math.max(top, 0);
|
|
1065
|
-
const defaultProps = {
|
|
1066
|
-
"data-slot": "comment-margin-item",
|
|
1067
|
-
"data-top": String(resolved),
|
|
1068
|
-
"data-active": active ? "" : void 0,
|
|
1069
|
-
"data-animate": animate ? "" : void 0,
|
|
1070
|
-
className: marginItem({ className }),
|
|
1071
|
-
style: {
|
|
1072
|
-
...style,
|
|
1073
|
-
transform: `translateY(${resolved}px)`
|
|
1074
|
-
}
|
|
1075
|
-
};
|
|
1076
|
-
return useRender({
|
|
1077
|
-
defaultTagName: "div",
|
|
1078
|
-
render,
|
|
1079
|
-
ref: setElement,
|
|
1080
|
-
props: mergeProps(defaultProps, props)
|
|
1081
|
-
});
|
|
1082
|
-
}
|
|
1083
|
-
//#endregion
|
|
1084
|
-
//#region src/components/comment/comment-marker.tsx
|
|
1085
|
-
/**
|
|
1086
|
-
* Where there is no room for a margin: a small button beside a passage that
|
|
1087
|
-
* has comments, with the authors' avatars (an `AvatarGroup` of up to 3
|
|
1088
|
-
* `size="2xs"` avatars) and the count. It opens the discussion, in a bottom
|
|
1089
|
-
* sheet. Give it an `aria-label` that starts with the count ("2 comments").
|
|
1090
|
-
*/
|
|
1091
|
-
function CommentMarker({ className, render, nativeButton, active = false, ...props }) {
|
|
1092
|
-
const { marker } = comment();
|
|
1093
|
-
return /* @__PURE__ */ jsx(Button, {
|
|
1094
|
-
...props,
|
|
1095
|
-
render,
|
|
1096
|
-
nativeButton: nativeButton ?? defaultNativeButton(render),
|
|
1097
|
-
"data-slot": "comment-marker",
|
|
1098
|
-
"data-active": active ? "" : void 0,
|
|
1099
|
-
className: marker({ className })
|
|
1100
|
-
});
|
|
1101
|
-
}
|
|
1102
|
-
//#endregion
|
|
1103
|
-
//#region src/lib/desktop-media.ts
|
|
1104
|
-
/**
|
|
1105
|
-
* Where the desktop layout and its metrics begin: from 768px, with a mouse or
|
|
1106
|
-
* a trackpad (design/foundations/spacing.md#hit-targets). The same query as
|
|
1107
|
-
* the desktop blocks in styles.css and foundation.css, for code that has to
|
|
1108
|
-
* decide in JavaScript what CSS decides with `md:pointer-fine:`.
|
|
1109
|
-
*/
|
|
1110
|
-
const DESKTOP_MEDIA_QUERY = "(width >= 48rem) and (pointer: fine)";
|
|
1111
|
-
//#endregion
|
|
1112
|
-
//#region src/components/comment/use-comment-margin-fits.ts
|
|
1113
|
-
function subscribeDesktop(onChange) {
|
|
1114
|
-
if (typeof window === "undefined") return () => void 0;
|
|
1115
|
-
const media = window.matchMedia(DESKTOP_MEDIA_QUERY);
|
|
1116
|
-
media.addEventListener("change", onChange);
|
|
1117
|
-
return () => media.removeEventListener("change", onChange);
|
|
1118
|
-
}
|
|
1119
|
-
const getDesktop = () => typeof window !== "undefined" && window.matchMedia("(width >= 48rem) and (pointer: fine)").matches;
|
|
1120
|
-
/**
|
|
1121
|
-
* Whether a viewer has room for a comment margin beside its document: true
|
|
1122
|
-
* only on a desktop (from 768px with a mouse or a trackpad, the query the
|
|
1123
|
-
* desktop metrics use) and while `element`, the viewer that holds the
|
|
1124
|
-
* document and the margin, is at least `minWidth` wide. Otherwise show
|
|
1125
|
-
* `CommentMarker`s and open discussions in a bottom sheet. Cards and markers
|
|
1126
|
-
* are different trees, so this is measured in JavaScript (a
|
|
1127
|
-
* `ResizeObserver` on the element), not decided by a container query.
|
|
1128
|
-
*/
|
|
1129
|
-
function useCommentMarginFits(element, { minWidth = 784 } = {}) {
|
|
1130
|
-
const desktop = useSyncExternalStore(subscribeDesktop, getDesktop, () => false);
|
|
1131
|
-
const [width, setWidth] = useState(0);
|
|
1132
|
-
useLayoutEffect(() => {
|
|
1133
|
-
if (!element) return;
|
|
1134
|
-
const measure = () => setWidth(element.getBoundingClientRect().width);
|
|
1135
|
-
measure();
|
|
1136
|
-
const observer = new ResizeObserver(measure);
|
|
1137
|
-
observer.observe(element);
|
|
1138
|
-
return () => observer.disconnect();
|
|
1139
|
-
}, [element]);
|
|
1140
|
-
return desktop && element !== null && width >= minWidth;
|
|
1141
|
-
}
|
|
1142
|
-
//#endregion
|
|
1143
|
-
//#region src/components/drag/drag-lists.tsx
|
|
1144
|
-
/** dnd-kit's vertical strategy, except that nothing moves while the hand is outside the list. */
|
|
1145
|
-
const dragListStrategy = (args) => args.overIndex < 0 ? null : verticalListSortingStrategy(args);
|
|
1146
|
-
/**
|
|
1147
|
-
* Items in one or more lists that can be reordered and moved between lists
|
|
1148
|
-
* (a sidebar's sections, a board's columns): the lists half of the drag
|
|
1149
|
-
* model (design/patterns/drag-and-drop.md#where-it-will-land). While dragging,
|
|
1150
|
-
* the item stands at its landing spot (its own slot is the place; siblings
|
|
1151
|
-
* slide apart), a list can refuse it, and every step is announced by name.
|
|
1152
|
-
* The keyboard is measured from the item's real place, so the arrows never
|
|
1153
|
-
* skip one after a move into another list.
|
|
1154
|
-
*
|
|
1155
|
-
* Spread `handlers`, `collisionDetection` and `accessibility` on the
|
|
1156
|
-
* `DndContext`, pass `coordinateGetter` to `useDragSensors`, and render
|
|
1157
|
-
* `DragRemeasure version={items}` inside it.
|
|
1158
|
-
*/
|
|
1159
|
-
function useDragLists({ lists, items, onItemsChange, listName, itemName, refuse, collisionDetection, announce, messages }) {
|
|
1160
|
-
const say = useMemo(() => dragMessages(messages), [messages]);
|
|
1161
|
-
const across = useDragAcrossLists(collisionDetection);
|
|
1162
|
-
const itemsRef = useRef(items);
|
|
1163
|
-
useLayoutEffect(() => {
|
|
1164
|
-
itemsRef.current = items;
|
|
1165
|
-
}, [items]);
|
|
1166
|
-
const [activeId, setActiveId] = useState(null);
|
|
1167
|
-
const [originList, setOriginList] = useState(null);
|
|
1168
|
-
const [refusal, setRefusalState] = useState(null);
|
|
1169
|
-
const refusalRef = useRef(refusal);
|
|
1170
|
-
const setRefusal = (next) => {
|
|
1171
|
-
refusalRef.current = next;
|
|
1172
|
-
setRefusalState(next);
|
|
1173
|
-
};
|
|
1174
|
-
const origin = useRef(null);
|
|
1175
|
-
const inserted = useRef(null);
|
|
1176
|
-
const listOf = useCallback((state, id) => lists.includes(id) ? id : lists.find((list) => state[list].includes(String(id))) ?? null, [lists]);
|
|
1177
|
-
const isList = (id) => lists.includes(id);
|
|
1178
|
-
const where = (state, id) => {
|
|
1179
|
-
const list = listOf(state, id);
|
|
1180
|
-
return {
|
|
1181
|
-
place: listName(list),
|
|
1182
|
-
position: state[list].indexOf(id) + 1,
|
|
1183
|
-
count: state[list].length
|
|
1184
|
-
};
|
|
1185
|
-
};
|
|
1186
|
-
const update = (next) => {
|
|
1187
|
-
itemsRef.current = next;
|
|
1188
|
-
onItemsChange(next);
|
|
1189
|
-
};
|
|
1190
|
-
/** Still home: in its own list, at its own index (pass the sortable's projected index). */
|
|
1191
|
-
const isHome = useCallback((id, index) => {
|
|
1192
|
-
const start = origin.current;
|
|
1193
|
-
return start != null && listOf(itemsRef.current, id) === start.list && index === start.index;
|
|
1194
|
-
}, [listOf]);
|
|
1195
|
-
const onDragStart = ({ active, activatorEvent }) => {
|
|
1196
|
-
const id = String(active.id);
|
|
1197
|
-
const list = listOf(items, id);
|
|
1198
|
-
if (!list) return;
|
|
1199
|
-
origin.current = {
|
|
1200
|
-
list,
|
|
1201
|
-
index: items[list].indexOf(id),
|
|
1202
|
-
snapshot: items,
|
|
1203
|
-
keyboard: typeof KeyboardEvent !== "undefined" && activatorEvent instanceof KeyboardEvent
|
|
1204
|
-
};
|
|
1205
|
-
inserted.current = null;
|
|
1206
|
-
setActiveId(id);
|
|
1207
|
-
setOriginList(list);
|
|
1208
|
-
announce(say.pickedUp({
|
|
1209
|
-
name: itemName(id),
|
|
1210
|
-
...where(items, id)
|
|
1211
|
-
}));
|
|
1212
|
-
};
|
|
1213
|
-
const onDragOver = ({ active, over }) => {
|
|
1214
|
-
const start = origin.current;
|
|
1215
|
-
if (!start) return;
|
|
1216
|
-
const id = String(active.id);
|
|
1217
|
-
if (!over) {
|
|
1218
|
-
if (refusalRef.current) setRefusal(null);
|
|
1219
|
-
announce(say.notOver({ name: itemName(id) }));
|
|
1220
|
-
return;
|
|
1221
|
-
}
|
|
1222
|
-
const current = itemsRef.current;
|
|
1223
|
-
const from = listOf(current, id);
|
|
1224
|
-
const to = listOf(current, over.id);
|
|
1225
|
-
if (!from || !to) return;
|
|
1226
|
-
const reason = refuse?.(start.list, to, id) ?? null;
|
|
1227
|
-
if (reason) {
|
|
1228
|
-
setRefusal({
|
|
1229
|
-
list: to,
|
|
1230
|
-
reason
|
|
1231
|
-
});
|
|
1232
|
-
update(start.snapshot);
|
|
1233
|
-
inserted.current = null;
|
|
1234
|
-
announce(say.refused({
|
|
1235
|
-
name: itemName(id),
|
|
1236
|
-
place: listName(to),
|
|
1237
|
-
reason
|
|
1238
|
-
}));
|
|
1239
|
-
return;
|
|
1240
|
-
}
|
|
1241
|
-
if (refusalRef.current) setRefusal(null);
|
|
1242
|
-
if (from !== to) {
|
|
1243
|
-
const fromList = current[from].filter((item) => item !== id);
|
|
1244
|
-
const toList = [...current[to]];
|
|
1245
|
-
let at = toList.indexOf(String(over.id));
|
|
1246
|
-
if (at < 0) at = toList.length;
|
|
1247
|
-
else if (start.keyboard) {
|
|
1248
|
-
if (lists.indexOf(from) > lists.indexOf(to)) at += 1;
|
|
1249
|
-
} else {
|
|
1250
|
-
const rect = active.rect.current.translated;
|
|
1251
|
-
if (rect && rect.top + rect.height / 2 > over.rect.top + over.rect.height / 2) at += 1;
|
|
1252
|
-
}
|
|
1253
|
-
toList.splice(at, 0, id);
|
|
1254
|
-
const next = {
|
|
1255
|
-
...current,
|
|
1256
|
-
[from]: fromList,
|
|
1257
|
-
[to]: toList
|
|
1258
|
-
};
|
|
1259
|
-
across.moved(start.keyboard);
|
|
1260
|
-
update(next);
|
|
1261
|
-
inserted.current = isList(over.id) ? null : over.id;
|
|
1262
|
-
announce(say.over({
|
|
1263
|
-
name: itemName(id),
|
|
1264
|
-
...where(next, id)
|
|
1265
|
-
}));
|
|
1266
|
-
return;
|
|
1267
|
-
}
|
|
1268
|
-
if (over.id === id) {
|
|
1269
|
-
inserted.current = null;
|
|
1270
|
-
return;
|
|
1271
|
-
}
|
|
1272
|
-
const list = current[to];
|
|
1273
|
-
if (inserted.current === over.id) {
|
|
1274
|
-
announce(say.over({
|
|
1275
|
-
name: itemName(id),
|
|
1276
|
-
...where(current, id)
|
|
1277
|
-
}));
|
|
1278
|
-
return;
|
|
1279
|
-
}
|
|
1280
|
-
inserted.current = null;
|
|
1281
|
-
const overIndex = isList(over.id) ? list.length - 1 : list.indexOf(String(over.id));
|
|
1282
|
-
announce(say.over({
|
|
1283
|
-
name: itemName(id),
|
|
1284
|
-
place: listName(to),
|
|
1285
|
-
position: overIndex + 1,
|
|
1286
|
-
count: list.length
|
|
1287
|
-
}));
|
|
1288
|
-
};
|
|
1289
|
-
const finish = (dropped, overId) => {
|
|
1290
|
-
const start = origin.current;
|
|
1291
|
-
const id = activeId;
|
|
1292
|
-
const refused = refusalRef.current;
|
|
1293
|
-
origin.current = null;
|
|
1294
|
-
across.reset();
|
|
1295
|
-
setActiveId(null);
|
|
1296
|
-
setOriginList(null);
|
|
1297
|
-
setRefusal(null);
|
|
1298
|
-
if (!start || !id) return;
|
|
1299
|
-
if (!dropped || overId == null || refused) {
|
|
1300
|
-
update(start.snapshot);
|
|
1301
|
-
const home = {
|
|
1302
|
-
name: itemName(id),
|
|
1303
|
-
...where(start.snapshot, id)
|
|
1304
|
-
};
|
|
1305
|
-
announce(refused && dropped ? say.refusedDrop({
|
|
1306
|
-
...home,
|
|
1307
|
-
reason: refused.reason
|
|
1308
|
-
}) : say.cancelled(home));
|
|
1309
|
-
return;
|
|
1310
|
-
}
|
|
1311
|
-
const current = itemsRef.current;
|
|
1312
|
-
const list = listOf(current, id);
|
|
1313
|
-
const ids = current[list];
|
|
1314
|
-
const fromIndex = ids.indexOf(id);
|
|
1315
|
-
const toIndex = inserted.current === overId ? fromIndex : isList(overId) ? ids.length - 1 : ids.indexOf(String(overId));
|
|
1316
|
-
const next = toIndex >= 0 && toIndex !== fromIndex ? {
|
|
1317
|
-
...current,
|
|
1318
|
-
[list]: arrayMove([...ids], fromIndex, toIndex)
|
|
1319
|
-
} : current;
|
|
1320
|
-
update(next);
|
|
1321
|
-
announce(say.dropped({
|
|
1322
|
-
name: itemName(id),
|
|
1323
|
-
...where(next, id)
|
|
1324
|
-
}));
|
|
1325
|
-
};
|
|
1326
|
-
const accessibility = useMemo(() => ({
|
|
1327
|
-
announcements: SILENT_DRAG_ANNOUNCEMENTS,
|
|
1328
|
-
screenReaderInstructions: dragInstructions(messages)
|
|
1329
|
-
}), [messages]);
|
|
1330
|
-
return {
|
|
1331
|
-
/** The dragged item's id, or null. */
|
|
1332
|
-
activeId,
|
|
1333
|
-
/** The list that refuses the item under the hand, and why. */
|
|
1334
|
-
refusal,
|
|
1335
|
-
/** The list the drag started in, while dragging. */
|
|
1336
|
-
originList,
|
|
1337
|
-
isHome,
|
|
1338
|
-
listOf,
|
|
1339
|
-
collisionDetection: across.collisionDetection,
|
|
1340
|
-
coordinateGetter: across.coordinateGetter,
|
|
1341
|
-
accessibility,
|
|
1342
|
-
handlers: {
|
|
1343
|
-
onDragStart,
|
|
1344
|
-
onDragOver,
|
|
1345
|
-
onDragEnd: ({ over }) => finish(true, over?.id ?? null),
|
|
1346
|
-
onDragCancel: () => finish(false, null)
|
|
1347
|
-
}
|
|
1348
|
-
};
|
|
1349
|
-
}
|
|
1350
|
-
/**
|
|
1351
|
-
* Wraps each item's content in a list. The dragged item's own node is at 40%
|
|
1352
|
-
* while it is still home, and the dashed place (its size, its radius) once it
|
|
1353
|
-
* stands anywhere else. Every other item renders as it is: the wrapper stays
|
|
1354
|
-
* the same, so the item keeps focus when its drag starts.
|
|
1355
|
-
*/
|
|
1356
|
-
function DraggedSlot({ dragging = false, home, radius, className, children }) {
|
|
1357
|
-
const place = dragging && !home;
|
|
1358
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
1359
|
-
"data-slot": "drag-slot",
|
|
1360
|
-
className: "relative",
|
|
1361
|
-
children: [/* @__PURE__ */ jsx("div", {
|
|
1362
|
-
"data-drag-origin": dragging && home ? "" : void 0,
|
|
1363
|
-
className: place ? "invisible" : void 0,
|
|
1364
|
-
children
|
|
1365
|
-
}), place ? /* @__PURE__ */ jsx(DropPlaceholder, { className: cn("absolute inset-0", radius, className) }) : null]
|
|
1366
|
-
});
|
|
1367
|
-
}
|
|
1368
|
-
/** The last announcement, for a polite live region (`DragLiveRegion`). Repeats are dropped. */
|
|
1369
|
-
function useDragAnnouncer() {
|
|
1370
|
-
const [message, setMessage] = useState("");
|
|
1371
|
-
const last = useRef("");
|
|
1372
|
-
return {
|
|
1373
|
-
message,
|
|
1374
|
-
announce: useCallback((text) => {
|
|
1375
|
-
if (text === last.current) return;
|
|
1376
|
-
last.current = text;
|
|
1377
|
-
setMessage(text);
|
|
1378
|
-
}, [])
|
|
1379
|
-
};
|
|
1380
|
-
}
|
|
1381
|
-
/** A polite, visually hidden live region for drag announcements. */
|
|
1382
|
-
function DragLiveRegion({ message }) {
|
|
1383
|
-
return /* @__PURE__ */ jsx("span", {
|
|
1384
|
-
className: "sr-only",
|
|
1385
|
-
role: "status",
|
|
1386
|
-
"aria-live": "polite",
|
|
1387
|
-
"aria-atomic": "true",
|
|
1388
|
-
children: message
|
|
1389
|
-
});
|
|
1390
|
-
}
|
|
1391
|
-
//#endregion
|
|
1392
|
-
//#region src/components/ellipsis/ellipsis.tsx
|
|
1393
|
-
const ellipsis = tv({
|
|
1394
|
-
slots: {
|
|
1395
|
-
root: ["group/ellipsis min-w-0 max-w-full"],
|
|
1396
|
-
text: ["min-w-0"],
|
|
1397
|
-
content: [],
|
|
1398
|
-
spacer: ["float-end h-[calc(100%-1lh)] w-0", "data-hidden:hidden group-data-measuring/ellipsis:hidden"],
|
|
1399
|
-
after: ["data-hidden:hidden group-data-measuring/ellipsis:hidden"]
|
|
1400
|
-
},
|
|
1401
|
-
variants: {
|
|
1402
|
-
multiline: {
|
|
1403
|
-
true: {
|
|
1404
|
-
root: ["flex"],
|
|
1405
|
-
text: ["flex-1", "wrap-break-word"],
|
|
1406
|
-
after: ["float-end clear-end ps-1"]
|
|
1407
|
-
},
|
|
1408
|
-
false: {
|
|
1409
|
-
root: ["inline-flex items-baseline gap-x-1 align-bottom"],
|
|
1410
|
-
after: ["shrink-0"]
|
|
1411
|
-
}
|
|
1412
|
-
},
|
|
1413
|
-
expanded: {
|
|
1414
|
-
true: {},
|
|
1415
|
-
false: {}
|
|
1416
|
-
},
|
|
1417
|
-
clickable: {
|
|
1418
|
-
true: { text: ["cursor-pointer"] },
|
|
1419
|
-
false: {}
|
|
1420
|
-
}
|
|
1421
|
-
},
|
|
1422
|
-
compoundVariants: [
|
|
1423
|
-
{
|
|
1424
|
-
multiline: false,
|
|
1425
|
-
expanded: false,
|
|
1426
|
-
class: { text: ["truncate", "print:overflow-visible print:whitespace-normal"] }
|
|
1427
|
-
},
|
|
1428
|
-
{
|
|
1429
|
-
multiline: false,
|
|
1430
|
-
expanded: true,
|
|
1431
|
-
class: { text: ["wrap-anywhere whitespace-normal"] }
|
|
1432
|
-
},
|
|
1433
|
-
{
|
|
1434
|
-
multiline: true,
|
|
1435
|
-
expanded: false,
|
|
1436
|
-
class: { text: ["line-clamp-(--ellipsis-lines)", "print:line-clamp-none"] }
|
|
1437
|
-
},
|
|
1438
|
-
{
|
|
1439
|
-
multiline: true,
|
|
1440
|
-
expanded: true,
|
|
1441
|
-
class: { text: ["line-clamp-none"] }
|
|
1442
|
-
}
|
|
1443
|
-
]
|
|
1444
|
-
});
|
|
1445
|
-
const EllipsisContext = createContext(null);
|
|
1446
|
-
const INTERACTIVE = "a[href], button, input, select, textarea, summary, [tabindex], [contenteditable]:not([contenteditable='false']), [role='button'], [role='link']";
|
|
1447
|
-
const TOUCH_MEDIA_QUERY = "(hover: none), (pointer: coarse)";
|
|
1448
|
-
function isTouchScreen() {
|
|
1449
|
-
return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia(TOUCH_MEDIA_QUERY).matches;
|
|
1450
|
-
}
|
|
1451
|
-
function contains(ref, target) {
|
|
1452
|
-
return target instanceof Node && ref.current?.contains(target) === true;
|
|
1453
|
-
}
|
|
1454
|
-
function hasSelection(within) {
|
|
1455
|
-
const selection = within.ownerDocument.defaultView?.getSelection();
|
|
1456
|
-
return !!selection && !selection.isCollapsed && selection.anchorNode != null && within.contains(selection.anchorNode);
|
|
1457
|
-
}
|
|
1458
|
-
function useExpanded(expandedProp, defaultExpanded, onExpandedChange) {
|
|
1459
|
-
const [expandedState, setExpandedState] = useState(defaultExpanded);
|
|
1460
|
-
const expanded = expandedProp ?? expandedState;
|
|
1461
|
-
return [expanded, useCallback(() => {
|
|
1462
|
-
const next = !expanded;
|
|
1463
|
-
if (expandedProp === void 0) setExpandedState(next);
|
|
1464
|
-
onExpandedChange?.(next);
|
|
1465
|
-
}, [
|
|
1466
|
-
expanded,
|
|
1467
|
-
expandedProp,
|
|
1468
|
-
onExpandedChange
|
|
1469
|
-
])];
|
|
1470
|
-
}
|
|
1471
|
-
/**
|
|
1472
|
-
* Registers the text with the shared measurer (lib/truncation.ts) and keeps
|
|
1473
|
-
* its answer: whether the text, alone, is cut, and whether it has a control
|
|
1474
|
-
* inside. While it is expanded the last collapsed answer is kept, so the
|
|
1475
|
-
* text and its control stay what they were until it collapses and is
|
|
1476
|
-
* measured again. `layout` names what remounts or reclamps the text.
|
|
1477
|
-
*/
|
|
1478
|
-
function useTruncation(expanded, layout, onNotTruncated) {
|
|
1479
|
-
const rootRef = useRef(null);
|
|
1480
|
-
const textRef = useRef(null);
|
|
1481
|
-
const contentRef = useRef(null);
|
|
1482
|
-
const afterRef = useRef(null);
|
|
1483
|
-
const setTextRef = useCallback((node) => {
|
|
1484
|
-
textRef.current = node;
|
|
1485
|
-
}, []);
|
|
1486
|
-
const [truncation, setTruncation] = useState(void 0);
|
|
1487
|
-
const [interactive, setInteractive] = useState(false);
|
|
1488
|
-
const expandedRef = useRef(expanded);
|
|
1489
|
-
const targetRef = useRef(null);
|
|
1490
|
-
const commit = useCallback((next) => {
|
|
1491
|
-
setInteractive(contentRef.current?.querySelector(INTERACTIVE) != null);
|
|
1492
|
-
if (expandedRef.current) return;
|
|
1493
|
-
setTruncation(next);
|
|
1494
|
-
if (next !== true) onNotTruncated();
|
|
1495
|
-
}, [onNotTruncated]);
|
|
1496
|
-
useLayoutEffect(() => {
|
|
1497
|
-
expandedRef.current = expanded;
|
|
1498
|
-
const root = rootRef.current;
|
|
1499
|
-
const text = textRef.current;
|
|
1500
|
-
const content = contentRef.current;
|
|
1501
|
-
if (!root || !text || !content) return void 0;
|
|
1502
|
-
const target = {
|
|
1503
|
-
observe: () => [text, afterRef.current],
|
|
1504
|
-
watch: () => content,
|
|
1505
|
-
isolate: () => {
|
|
1506
|
-
root.setAttribute("data-measuring", "");
|
|
1507
|
-
return () => root.removeAttribute("data-measuring");
|
|
1508
|
-
},
|
|
1509
|
-
read: () => measureContent(text, content),
|
|
1510
|
-
commit
|
|
1511
|
-
};
|
|
1512
|
-
targetRef.current = target;
|
|
1513
|
-
return observeTruncation(target);
|
|
1514
|
-
}, [
|
|
1515
|
-
commit,
|
|
1516
|
-
expanded,
|
|
1517
|
-
layout
|
|
1518
|
-
]);
|
|
1519
|
-
return {
|
|
1520
|
-
rootRef,
|
|
1521
|
-
textRef,
|
|
1522
|
-
setTextRef,
|
|
1523
|
-
contentRef,
|
|
1524
|
-
afterRef,
|
|
1525
|
-
truncation,
|
|
1526
|
-
interactive,
|
|
1527
|
-
measureNow: useCallback(() => {
|
|
1528
|
-
const target = targetRef.current;
|
|
1529
|
-
if (!target) return void 0;
|
|
1530
|
-
const next = readNow(target);
|
|
1531
|
-
commit(next);
|
|
1532
|
-
return next;
|
|
1533
|
-
}, [commit])
|
|
1534
|
-
};
|
|
1535
|
-
}
|
|
1536
|
-
/** The tooltip's `onOpenChange`: it opens only on a text measured as cut, now. */
|
|
1537
|
-
function useOpenWhenTruncated(setOpen, expanded, afterRef, measureNow) {
|
|
1538
|
-
return useCallback((next, details) => {
|
|
1539
|
-
if (!next) {
|
|
1540
|
-
setOpen(false);
|
|
1541
|
-
return;
|
|
1542
|
-
}
|
|
1543
|
-
if (isTouchScreen() || expanded) return;
|
|
1544
|
-
if (contains(afterRef, details.event?.target)) return;
|
|
1545
|
-
if (measureNow() === true) setOpen(true);
|
|
1546
|
-
}, [
|
|
1547
|
-
afterRef,
|
|
1548
|
-
expanded,
|
|
1549
|
-
measureNow,
|
|
1550
|
-
setOpen
|
|
1551
|
-
]);
|
|
1552
|
-
}
|
|
1553
|
-
/**
|
|
1554
|
-
* Makes the text the button that expands and collapses it, while
|
|
1555
|
-
* `clickable`. It stays one while expanded, `aria-expanded="true"`, so focus
|
|
1556
|
-
* stays on it and it collapses again the same way.
|
|
1557
|
-
*/
|
|
1558
|
-
function useExpandOnClick(clickable, expanded, toggle, textRef) {
|
|
1559
|
-
const onClick = useCallback(() => {
|
|
1560
|
-
if (!clickable) return;
|
|
1561
|
-
if (textRef.current && hasSelection(textRef.current)) return;
|
|
1562
|
-
toggle();
|
|
1563
|
-
}, [
|
|
1564
|
-
clickable,
|
|
1565
|
-
textRef,
|
|
1566
|
-
toggle
|
|
1567
|
-
]);
|
|
1568
|
-
const onKeyDown = useCallback((event) => {
|
|
1569
|
-
if (!clickable || event.target !== event.currentTarget) return;
|
|
1570
|
-
if (event.key === "Enter" || event.key === " ") {
|
|
1571
|
-
event.preventDefault();
|
|
1572
|
-
toggle();
|
|
1573
|
-
}
|
|
1574
|
-
}, [clickable, toggle]);
|
|
1575
|
-
if (!clickable) return {};
|
|
1576
|
-
return {
|
|
1577
|
-
role: "button",
|
|
1578
|
-
tabIndex: 0,
|
|
1579
|
-
"aria-expanded": expanded,
|
|
1580
|
-
onClick,
|
|
1581
|
-
onKeyDown
|
|
1582
|
-
};
|
|
1583
|
-
}
|
|
1584
|
-
function useRootStyle(style, lines) {
|
|
1585
|
-
return useMemo(() => lines > 1 ? {
|
|
1586
|
-
...style,
|
|
1587
|
-
["--ellipsis-lines"]: String(lines)
|
|
1588
|
-
} : style, [style, lines]);
|
|
1589
|
-
}
|
|
1590
|
-
function Ellipsis({ children, lines = 1, tooltip = true, after, expanded: expandedProp, defaultExpanded = false, onExpandedChange, expandOnClick = false, className, style, ...props }) {
|
|
1591
|
-
const textId = useId();
|
|
1592
|
-
const [open, setOpen] = useState(false);
|
|
1593
|
-
const closeTooltip = useCallback(() => setOpen(false), []);
|
|
1594
|
-
const [expanded, toggle] = useExpanded(expandedProp, defaultExpanded, onExpandedChange);
|
|
1595
|
-
const multiline = lines > 1;
|
|
1596
|
-
const hasTooltip = tooltip !== false && tooltip != null;
|
|
1597
|
-
const hasAfter = after != null;
|
|
1598
|
-
const measured = useTruncation(expanded, `${lines} ${hasAfter} ${hasTooltip}`, closeTooltip);
|
|
1599
|
-
const { rootRef, textRef, setTextRef, contentRef, afterRef, truncation } = measured;
|
|
1600
|
-
const handleOpenChange = useOpenWhenTruncated(setOpen, expanded, afterRef, measured.measureNow);
|
|
1601
|
-
const truncated = truncation === true && !expanded;
|
|
1602
|
-
const clickable = expandOnClick && !hasAfter && !measured.interactive && (truncation === true || expanded);
|
|
1603
|
-
const afterNode = typeof after === "function" ? after({
|
|
1604
|
-
truncated,
|
|
1605
|
-
expanded,
|
|
1606
|
-
toggle
|
|
1607
|
-
}) : after;
|
|
1608
|
-
const afterHidden = !truncated && !expanded;
|
|
1609
|
-
const slots = ellipsis({
|
|
1610
|
-
multiline,
|
|
1611
|
-
expanded,
|
|
1612
|
-
clickable
|
|
1613
|
-
});
|
|
1614
|
-
const context = useMemo(() => ({
|
|
1615
|
-
expanded,
|
|
1616
|
-
toggle,
|
|
1617
|
-
textId
|
|
1618
|
-
}), [
|
|
1619
|
-
expanded,
|
|
1620
|
-
toggle,
|
|
1621
|
-
textId
|
|
1622
|
-
]);
|
|
1623
|
-
const rootStyle = useRootStyle(style, lines);
|
|
1624
|
-
const clickProps = useExpandOnClick(clickable, expanded, toggle, textRef);
|
|
1625
|
-
const afterElement = hasAfter ? /* @__PURE__ */ jsx("span", {
|
|
1626
|
-
ref: afterRef,
|
|
1627
|
-
"data-slot": "ellipsis-after",
|
|
1628
|
-
"data-hidden": afterHidden ? "" : void 0,
|
|
1629
|
-
inert: afterHidden,
|
|
1630
|
-
className: slots.after(),
|
|
1631
|
-
onPointerEnter: closeTooltip,
|
|
1632
|
-
children: afterNode
|
|
1633
|
-
}) : null;
|
|
1634
|
-
const floatingAfter = multiline && hasAfter;
|
|
1635
|
-
const textChildren = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
1636
|
-
floatingAfter && /* @__PURE__ */ jsx("span", {
|
|
1637
|
-
"aria-hidden": true,
|
|
1638
|
-
"data-slot": "ellipsis-spacer",
|
|
1639
|
-
"data-hidden": afterHidden ? "" : void 0,
|
|
1640
|
-
className: slots.spacer()
|
|
1641
|
-
}),
|
|
1642
|
-
floatingAfter && afterElement,
|
|
1643
|
-
/* @__PURE__ */ jsx("span", {
|
|
1644
|
-
ref: contentRef,
|
|
1645
|
-
"data-slot": "ellipsis-content",
|
|
1646
|
-
className: slots.content(),
|
|
1647
|
-
children
|
|
1648
|
-
})
|
|
1649
|
-
] });
|
|
1650
|
-
const textProps = {
|
|
1651
|
-
id: textId,
|
|
1652
|
-
"data-slot": "ellipsis-text",
|
|
1653
|
-
"data-truncated": truncated ? "" : void 0,
|
|
1654
|
-
className: slots.text(),
|
|
1655
|
-
...clickProps
|
|
1656
|
-
};
|
|
1657
|
-
const text = hasTooltip ? /* @__PURE__ */ jsxs(Tooltip, {
|
|
1658
|
-
open: open && truncated,
|
|
1659
|
-
onOpenChange: handleOpenChange,
|
|
1660
|
-
children: [/* @__PURE__ */ jsx(TooltipTrigger, {
|
|
1661
|
-
ref: setTextRef,
|
|
1662
|
-
render: /* @__PURE__ */ jsx("span", {}),
|
|
1663
|
-
...textProps,
|
|
1664
|
-
className: slots.text({ class: "select-auto" }),
|
|
1665
|
-
children: textChildren
|
|
1666
|
-
}), /* @__PURE__ */ jsx(TooltipContent, { children: tooltip === true ? children : tooltip })]
|
|
1667
|
-
}) : /* @__PURE__ */ jsx("span", {
|
|
1668
|
-
ref: setTextRef,
|
|
1669
|
-
...textProps,
|
|
1670
|
-
children: textChildren
|
|
1671
|
-
});
|
|
1672
|
-
return /* @__PURE__ */ jsx(EllipsisContext.Provider, {
|
|
1673
|
-
value: context,
|
|
1674
|
-
children: /* @__PURE__ */ jsxs("span", {
|
|
1675
|
-
ref: rootRef,
|
|
1676
|
-
"data-slot": "ellipsis",
|
|
1677
|
-
className: slots.root({ className }),
|
|
1678
|
-
style: rootStyle,
|
|
1679
|
-
...props,
|
|
1680
|
-
children: [text, !multiline ? afterElement : null]
|
|
1681
|
-
})
|
|
1682
|
-
});
|
|
1683
|
-
}
|
|
1684
|
-
/**
|
|
1685
|
-
* The control that expands an Ellipsis and collapses it again, for its
|
|
1686
|
-
* `after`. A real button, with `aria-expanded` and `aria-controls` pointing
|
|
1687
|
-
* at the text. The label is the caller's: "Show More" / "Show Less".
|
|
1688
|
-
*/
|
|
1689
|
-
function EllipsisToggle({ onClick, ...props }) {
|
|
1690
|
-
const context = useContext(EllipsisContext);
|
|
1691
|
-
if (!context) throw new Error("EllipsisToggle must be inside an Ellipsis `after`.");
|
|
1692
|
-
const { expanded, toggle, textId } = context;
|
|
1693
|
-
const handleClick = useCallback((event) => {
|
|
1694
|
-
onClick?.(event);
|
|
1695
|
-
if (!event.defaultPrevented) toggle();
|
|
1696
|
-
}, [onClick, toggle]);
|
|
1697
|
-
return /* @__PURE__ */ jsx(TextLink, {
|
|
1698
|
-
"aria-expanded": expanded,
|
|
1699
|
-
"aria-controls": textId,
|
|
1700
|
-
onClick: handleClick,
|
|
1701
|
-
...props
|
|
1702
|
-
});
|
|
1703
|
-
}
|
|
1704
|
-
//#endregion
|
|
1705
|
-
//#region src/lib/use-truncated.ts
|
|
1706
|
-
/**
|
|
1707
|
-
* Whether the text in the element `ref` points at is cut off by its CSS
|
|
1708
|
-
* (`truncate`, `line-clamp`, a fixed size with `overflow: hidden`): `true`,
|
|
1709
|
-
* `false`, or `undefined` until it is measured or while it has no box.
|
|
1710
|
-
*
|
|
1711
|
-
* It measures fractional sizes with a 0.5px tolerance, past either inline
|
|
1712
|
-
* edge and below the bottom edge, so it needs no line count. It measures again
|
|
1713
|
-
* when the element changes size, when its text changes and when a web font
|
|
1714
|
-
* finishes loading, in one batched frame for every measured element on the
|
|
1715
|
-
* page (lib/truncation.ts), before that frame paints.
|
|
1716
|
-
*
|
|
1717
|
-
* The element is read when the component mounts: keep `ref` on one element
|
|
1718
|
-
* for the component's life, or give the component a `key` that changes with
|
|
1719
|
-
* the element.
|
|
1720
|
-
*/
|
|
1721
|
-
function useTruncated(ref) {
|
|
1722
|
-
const [truncated, setTruncated] = useState(void 0);
|
|
1723
|
-
useLayoutEffect(() => {
|
|
1724
|
-
const element = ref.current;
|
|
1725
|
-
if (!element) return void 0;
|
|
1726
|
-
return observeTruncation({
|
|
1727
|
-
observe: () => [element],
|
|
1728
|
-
watch: () => element,
|
|
1729
|
-
read: () => measureElement(element),
|
|
1730
|
-
commit: setTruncated
|
|
1731
|
-
});
|
|
1732
|
-
}, [ref]);
|
|
1733
|
-
return truncated;
|
|
1734
|
-
}
|
|
1735
|
-
//#endregion
|
|
1736
|
-
//#region src/components/inbox-item/inbox-item-leading-badge.tsx
|
|
1737
|
-
/**
|
|
1738
|
-
* A small badge in the bottom-right corner of `InboxItemLeading`, the corner a
|
|
1739
|
-
* presence light takes: on a thread row, the thread mark, on the channel's
|
|
1740
|
-
* `IconTile` or on the direct message's `Avatar` alike. Put it inside
|
|
1741
|
-
* `InboxItemLeading`, after the tile or avatar, with one icon as its child;
|
|
1742
|
-
* leave the avatar's `AvatarBadge` out, since the corner holds one mark. It
|
|
1743
|
-
* is hidden from assistive technology unless given a `role`.
|
|
1744
|
-
*/
|
|
1745
|
-
function InboxItemLeadingBadge({ className, render, ...props }) {
|
|
1746
|
-
const { itemLeadingBadge } = inboxItem();
|
|
1747
|
-
const defaultProps = {
|
|
1748
|
-
"data-slot": "inbox-item-leading-badge",
|
|
1749
|
-
"aria-hidden": props.role ? void 0 : true,
|
|
1750
|
-
className: itemLeadingBadge({ className })
|
|
1751
|
-
};
|
|
1752
|
-
return useRender({
|
|
1753
|
-
defaultTagName: "span",
|
|
1754
|
-
render,
|
|
1755
|
-
props: mergeProps(defaultProps, props)
|
|
1756
|
-
});
|
|
1757
|
-
}
|
|
1758
|
-
//#endregion
|
|
1759
|
-
//#region src/components/inbox-item/inbox-item-skeleton.recipe.ts
|
|
1760
|
-
const inboxItemSkeleton = tv({ slots: {
|
|
1761
|
-
list: ["flex min-w-0 flex-col gap-0.5"],
|
|
1762
|
-
row: [
|
|
1763
|
-
"[--inbox-item-skeleton-context:5rem] [--inbox-item-skeleton-title:60%] [--inbox-item-skeleton-body:80%]",
|
|
1764
|
-
"nth-[3n+2]:[--inbox-item-skeleton-context:4rem] nth-[3n+2]:[--inbox-item-skeleton-title:45%] nth-[3n+2]:[--inbox-item-skeleton-body:70%]",
|
|
1765
|
-
"nth-[3n]:[--inbox-item-skeleton-context:6rem] nth-[3n]:[--inbox-item-skeleton-title:55%] nth-[3n]:[--inbox-item-skeleton-body:75%]"
|
|
1766
|
-
],
|
|
1767
|
-
leading: ["size-[35px]"],
|
|
1768
|
-
line: ["min-w-0 flex-1"],
|
|
1769
|
-
title: ["pl-0"],
|
|
1770
|
-
bar: ["inline-block h-[0.75em] max-w-full align-middle"],
|
|
1771
|
-
contextBar: ["w-(--inbox-item-skeleton-context)"],
|
|
1772
|
-
titleBar: ["w-(--inbox-item-skeleton-title)"],
|
|
1773
|
-
bodyBar: ["w-(--inbox-item-skeleton-body)"],
|
|
1774
|
-
timestampBar: ["w-9"]
|
|
1775
|
-
} });
|
|
1776
|
-
//#endregion
|
|
1777
|
-
//#region src/components/inbox-item/inbox-item-skeleton.tsx
|
|
1778
|
-
/**
|
|
1779
|
-
* A placeholder for one `InboxItem` while the list loads. It is an
|
|
1780
|
-
* `InboxItem` itself, with the same parts as the loaded row (an optional
|
|
1781
|
-
* leading block, the context line, the title, the excerpt, the timestamp),
|
|
1782
|
-
* each holding a bar instead of text. The padding, the divider and every
|
|
1783
|
-
* line's height are the loaded row's, so nothing moves when the content
|
|
1784
|
-
* arrives. Match `density`, `leading` and `context` to the rows it stands for.
|
|
1785
|
-
* Hidden from assistive technology: mark the region that stays while the
|
|
1786
|
-
* list loads `aria-busy`.
|
|
1787
|
-
*/
|
|
1788
|
-
function InboxItemSkeleton({ density, leading = false, context = false, ...props }) {
|
|
1789
|
-
const slots = inboxItemSkeleton();
|
|
1790
|
-
const bar = (width) => /* @__PURE__ */ jsx(Skeleton, {
|
|
1791
|
-
variant: "line",
|
|
1792
|
-
className: slots.bar({ className: width })
|
|
1793
|
-
});
|
|
1794
|
-
return /* @__PURE__ */ jsxs(InboxItem, {
|
|
1795
|
-
"aria-hidden": true,
|
|
1796
|
-
"data-skeleton": "",
|
|
1797
|
-
density,
|
|
1798
|
-
disabled: true,
|
|
1799
|
-
...props,
|
|
1800
|
-
className: slots.row({ className: props.className }),
|
|
1801
|
-
children: [
|
|
1802
|
-
leading ? /* @__PURE__ */ jsx(InboxItemLeading, { children: /* @__PURE__ */ jsx(Skeleton, { className: slots.leading() }) }) : null,
|
|
1803
|
-
/* @__PURE__ */ jsxs(InboxItemContent, { children: [
|
|
1804
|
-
context ? /* @__PURE__ */ jsx(InboxItemContext, { children: /* @__PURE__ */ jsx("span", {
|
|
1805
|
-
className: slots.line(),
|
|
1806
|
-
children: bar(slots.contextBar())
|
|
1807
|
-
}) }) : null,
|
|
1808
|
-
/* @__PURE__ */ jsx(InboxItemHeading, { children: /* @__PURE__ */ jsx(InboxItemTitle, {
|
|
1809
|
-
className: slots.title(),
|
|
1810
|
-
children: bar(slots.titleBar())
|
|
1811
|
-
}) }),
|
|
1812
|
-
/* @__PURE__ */ jsx(InboxItemBody, { children: bar(slots.bodyBar()) })
|
|
1813
|
-
] }),
|
|
1814
|
-
/* @__PURE__ */ jsx(InboxItemTrailing, { children: /* @__PURE__ */ jsx(InboxItemTimestamp, { children: bar(slots.timestampBar()) }) })
|
|
1815
|
-
]
|
|
1816
|
-
});
|
|
1817
|
-
}
|
|
1818
|
-
/**
|
|
1819
|
-
* The list of `InboxItemSkeleton` rows that stands in for a list of
|
|
1820
|
-
* `InboxItem` rows while it loads, with nothing between or around the rows
|
|
1821
|
-
* that the loaded list doesn't have (no border, no card): only the list's
|
|
1822
|
-
* 2px gap, which the loaded list has too (design/patterns/lists.md). Renders
|
|
1823
|
-
* `count` rows of the given shape, or its children, once `delay` (1 second by
|
|
1824
|
-
* default) has passed. It isn't `aria-busy` itself: it goes away when the
|
|
1825
|
-
* content arrives, so mark the region that stays, such as the list's panel or
|
|
1826
|
-
* viewport, `aria-busy` while it loads.
|
|
1827
|
-
*/
|
|
1828
|
-
function InboxItemSkeletonList({ count = 6, delay = 1e3, density, leading, context, children, className, render, ...props }) {
|
|
1829
|
-
const { list } = inboxItemSkeleton();
|
|
1830
|
-
const shown = useDelayPassed(delay);
|
|
1831
|
-
const defaultProps = {
|
|
1832
|
-
"data-slot": "inbox-item-skeleton-list",
|
|
1833
|
-
"data-waiting": shown ? void 0 : "",
|
|
1834
|
-
className: list({ className }),
|
|
1835
|
-
children: shown ? children ?? Array.from({ length: count }, (_, index) => /* @__PURE__ */ jsx(InboxItemSkeleton, {
|
|
1836
|
-
density,
|
|
1837
|
-
leading,
|
|
1838
|
-
context
|
|
1839
|
-
}, index)) : null
|
|
1840
|
-
};
|
|
1841
|
-
return useRender({
|
|
1842
|
-
defaultTagName: "div",
|
|
1843
|
-
render,
|
|
1844
|
-
props: mergeProps(defaultProps, props)
|
|
1845
|
-
});
|
|
1846
|
-
}
|
|
1847
|
-
/** Whether `delay` milliseconds have passed since mount; at once for `0`. */
|
|
1848
|
-
function useDelayPassed(delay) {
|
|
1849
|
-
const [passed, setPassed] = useState(false);
|
|
1850
|
-
useEffect(() => {
|
|
1851
|
-
if (delay <= 0) return;
|
|
1852
|
-
const timer = setTimeout(() => setPassed(true), delay);
|
|
1853
|
-
return () => clearTimeout(timer);
|
|
1854
|
-
}, [delay]);
|
|
1855
|
-
return passed || delay <= 0;
|
|
1856
|
-
}
|
|
1857
|
-
//#endregion
|
|
1858
|
-
//#region src/components/list-row/list-row-skeleton.tsx
|
|
1859
|
-
const listRowSkeleton = tv({ slots: {
|
|
1860
|
-
row: [
|
|
1861
|
-
"[--list-row-skeleton-title:45%] [--list-row-skeleton-description:75%]",
|
|
1862
|
-
"nth-[3n+2]:[--list-row-skeleton-title:35%] nth-[3n+2]:[--list-row-skeleton-description:60%]",
|
|
1863
|
-
"nth-[3n]:[--list-row-skeleton-title:55%] nth-[3n]:[--list-row-skeleton-description:70%]"
|
|
1864
|
-
],
|
|
1865
|
-
avatar: ["size-[35px] shrink-0"],
|
|
1866
|
-
icon: ["size-5 shrink-0"],
|
|
1867
|
-
bar: ["inline-block h-[0.75em] max-w-full align-middle"],
|
|
1868
|
-
titleBar: ["w-(--list-row-skeleton-title)"],
|
|
1869
|
-
descriptionBar: ["w-(--list-row-skeleton-description)"],
|
|
1870
|
-
metaBar: ["w-9"]
|
|
1871
|
-
} });
|
|
1872
|
-
/**
|
|
1873
|
-
* A placeholder for one `ListRow` while the list loads. It is a display-only
|
|
1874
|
-
* `ListRow` of the same `variant`, with a bar in its title, its time and its
|
|
1875
|
-
* description, so it is the loaded row's size and its divider sits in the
|
|
1876
|
-
* same place. Hidden from assistive technology: mark the region that stays
|
|
1877
|
-
* while the list loads, such as its `ListRowGroup`, `aria-busy`.
|
|
1878
|
-
*/
|
|
1879
|
-
function ListRowSkeleton({ variant = "default", leading = variant === "default", description = variant === "default", className, ...props }) {
|
|
1880
|
-
const slots = listRowSkeleton();
|
|
1881
|
-
const bar = (width) => /* @__PURE__ */ jsx(Skeleton, {
|
|
1882
|
-
variant: "line",
|
|
1883
|
-
className: slots.bar({ className: width })
|
|
1884
|
-
});
|
|
1885
|
-
return /* @__PURE__ */ jsxs(ListRow, {
|
|
1886
|
-
"aria-hidden": true,
|
|
1887
|
-
"data-skeleton": "",
|
|
1888
|
-
interactive: false,
|
|
1889
|
-
variant,
|
|
1890
|
-
...props,
|
|
1891
|
-
className: slots.row({ className }),
|
|
1892
|
-
children: [leading ? /* @__PURE__ */ jsx(Skeleton, { className: variant === "default" ? slots.avatar() : slots.icon() }) : null, /* @__PURE__ */ jsxs(ListRowBody, { children: [/* @__PURE__ */ jsxs(ListRowTop, { children: [/* @__PURE__ */ jsx(ListRowTitle, { children: bar(slots.titleBar()) }), variant === "default" ? /* @__PURE__ */ jsx(ListRowMeta, { children: bar(slots.metaBar()) }) : null] }), description ? /* @__PURE__ */ jsx(ListRowBottom, { children: /* @__PURE__ */ jsx(ListRowDescription, { children: bar(slots.descriptionBar()) }) }) : null] })]
|
|
1893
|
-
});
|
|
1894
|
-
}
|
|
1895
|
-
//#endregion
|
|
1896
|
-
//#region src/components/meter/meter.tsx
|
|
1897
|
-
const meter = tv({
|
|
1898
|
-
slots: {
|
|
1899
|
-
root: [
|
|
1900
|
-
"group/meter grid w-full min-w-0 gap-1.5",
|
|
1901
|
-
"[--meter-indicator:var(--info)]",
|
|
1902
|
-
"[--meter-value:var(--foreground-muted)]",
|
|
1903
|
-
"font-sans"
|
|
1904
|
-
],
|
|
1905
|
-
header: ["flex min-w-0 flex-wrap items-baseline justify-between gap-x-3 gap-y-0.5"],
|
|
1906
|
-
label: ["min-w-0 truncate", "text-footnote font-semibold text-foreground"],
|
|
1907
|
-
value: ["shrink-0 text-right tabular-nums", "text-footnote text-(--meter-value)"],
|
|
1908
|
-
track: [
|
|
1909
|
-
"relative w-full overflow-hidden bg-(--fill-muted)",
|
|
1910
|
-
"h-2 group-data-[size=sm]/meter:h-1.5",
|
|
1911
|
-
"rounded-sticker"
|
|
1912
|
-
],
|
|
1913
|
-
indicator: [
|
|
1914
|
-
"block h-full bg-(--meter-indicator)",
|
|
1915
|
-
"transition-[width,background-color] duration-200 ease-out",
|
|
1916
|
-
"rounded-[inherit] inset-ring-[1.5px] inset-ring-sticker-ink"
|
|
1917
|
-
]
|
|
1918
|
-
},
|
|
1919
|
-
variants: {
|
|
1920
|
-
warning: { true: { root: ["[--meter-indicator:var(--warning)]", "[--meter-value:var(--warning-strong)]"] } },
|
|
1921
|
-
size: {
|
|
1922
|
-
sm: {},
|
|
1923
|
-
md: {}
|
|
1924
|
-
}
|
|
1925
|
-
},
|
|
1926
|
-
defaultVariants: {
|
|
1927
|
-
warning: false,
|
|
1928
|
-
size: "md"
|
|
1929
|
-
}
|
|
1930
|
-
});
|
|
1931
|
-
const MeterTextContext = createContext("");
|
|
1932
|
-
function clamp(value, min, max) {
|
|
1933
|
-
return Math.min(max, Math.max(min, Number.isNaN(value) ? min : value));
|
|
1934
|
-
}
|
|
1935
|
-
/**
|
|
1936
|
-
* How much of an allowance is used, such as a usage limit that resets. The bar
|
|
1937
|
-
* fills as it is used: the info fill, like Progress, until `warnAt` of `max`
|
|
1938
|
-
* is used (80% unless set), then the warning color, and a full warning bar
|
|
1939
|
-
* when used up; never danger, and no success color.
|
|
1940
|
-
*/
|
|
1941
|
-
function Meter({ size = "md", used, max = 100, warnAt, low, valueText, className, ...props }) {
|
|
1942
|
-
const value = clamp(used, 0, max);
|
|
1943
|
-
const share = max > 0 ? value / max : 1;
|
|
1944
|
-
const warnFrom = warnAt !== void 0 ? clamp(warnAt, 0, 1) : low !== void 0 && max > 0 ? 1 - clamp(low, 0, max) / max : .8;
|
|
1945
|
-
const level = share >= 1 ? "full" : share >= warnFrom ? "high" : "normal";
|
|
1946
|
-
const { root } = meter({
|
|
1947
|
-
size,
|
|
1948
|
-
warning: level !== "normal"
|
|
1949
|
-
});
|
|
1950
|
-
return /* @__PURE__ */ jsx(MeterTextContext.Provider, {
|
|
1951
|
-
value: valueText,
|
|
1952
|
-
children: /* @__PURE__ */ jsx(Meter$1.Root, {
|
|
1953
|
-
"data-slot": "meter",
|
|
1954
|
-
"data-size": size,
|
|
1955
|
-
"data-level": level,
|
|
1956
|
-
min: 0,
|
|
1957
|
-
max,
|
|
1958
|
-
value,
|
|
1959
|
-
"aria-valuetext": valueText,
|
|
1960
|
-
className: root({ className }),
|
|
1961
|
-
...props
|
|
1962
|
-
})
|
|
1963
|
-
});
|
|
1964
|
-
}
|
|
1965
|
-
function MeterHeader({ className, ...props }) {
|
|
1966
|
-
const { header } = meter();
|
|
1967
|
-
return /* @__PURE__ */ jsx("div", {
|
|
1968
|
-
"data-slot": "meter-header",
|
|
1969
|
-
className: header({ className }),
|
|
1970
|
-
...props
|
|
1971
|
-
});
|
|
1972
|
-
}
|
|
1973
|
-
function MeterLabel({ className, ...props }) {
|
|
1974
|
-
const { label } = meter();
|
|
1975
|
-
return /* @__PURE__ */ jsx(Meter$1.Label, {
|
|
1976
|
-
"data-slot": "meter-label",
|
|
1977
|
-
className: label({ className }),
|
|
1978
|
-
...props
|
|
1979
|
-
});
|
|
1980
|
-
}
|
|
1981
|
-
/** Shows the meter's `valueText`, at the end of `MeterHeader`. */
|
|
1982
|
-
function MeterValue({ className, ...props }) {
|
|
1983
|
-
const { value } = meter();
|
|
1984
|
-
const valueText = useContext(MeterTextContext);
|
|
1985
|
-
return /* @__PURE__ */ jsx(Meter$1.Value, {
|
|
1986
|
-
"data-slot": "meter-value",
|
|
1987
|
-
className: value({ className }),
|
|
1988
|
-
...props,
|
|
1989
|
-
children: () => valueText
|
|
1990
|
-
});
|
|
1991
|
-
}
|
|
1992
|
-
function MeterTrack({ className, ...props }) {
|
|
1993
|
-
const { track } = meter();
|
|
1994
|
-
return /* @__PURE__ */ jsx(Meter$1.Track, {
|
|
1995
|
-
"data-slot": "meter-track",
|
|
1996
|
-
className: track({ className }),
|
|
1997
|
-
...props
|
|
1998
|
-
});
|
|
1999
|
-
}
|
|
2000
|
-
function MeterIndicator({ className, ...props }) {
|
|
2001
|
-
const { indicator } = meter();
|
|
2002
|
-
return /* @__PURE__ */ jsx(Meter$1.Indicator, {
|
|
2003
|
-
"data-slot": "meter-indicator",
|
|
2004
|
-
className: indicator({ className }),
|
|
2005
|
-
...props
|
|
2006
|
-
});
|
|
2007
|
-
}
|
|
2008
|
-
//#endregion
|
|
2009
|
-
//#region src/components/filter-chip/filter-chip.tsx
|
|
2010
|
-
const filterChip = tv({ slots: {
|
|
2011
|
-
root: [
|
|
2012
|
-
"inline-flex h-8 min-w-0 shrink items-center",
|
|
2013
|
-
"gap-1.5 rounded-control bg-info-soft pr-1 pl-2.5",
|
|
2014
|
-
"font-sans text-control font-semibold text-foreground",
|
|
2015
|
-
"[&>svg]:pointer-events-none [&>svg]:shrink-0",
|
|
2016
|
-
"[&>svg:not([class*='size-'])]:size-3.5 [&>svg:not([class*='stroke-'])]:stroke-2"
|
|
2017
|
-
],
|
|
2018
|
-
label: ["min-w-0 truncate"],
|
|
2019
|
-
remove: [
|
|
2020
|
-
...chipRemoveBase,
|
|
2021
|
-
"relative size-6",
|
|
2022
|
-
"after:absolute after:content-[''] pointer-coarse:after:-inset-2.5"
|
|
2023
|
-
]
|
|
2024
|
-
} });
|
|
2025
|
-
/**
|
|
2026
|
-
* The filter in effect, beside the filter button that chose it: an `info-soft`
|
|
2027
|
-
* chip as high as the button, with an optional 14px icon, a `FilterChipLabel`
|
|
2028
|
-
* and a `FilterChipRemove` × that resets the filter.
|
|
2029
|
-
*/
|
|
2030
|
-
function FilterChip({ className, ...props }) {
|
|
2031
|
-
const { root } = filterChip();
|
|
2032
|
-
return /* @__PURE__ */ jsx("span", {
|
|
2033
|
-
"data-slot": "filter-chip",
|
|
2034
|
-
className: root({ className }),
|
|
2035
|
-
...props
|
|
2036
|
-
});
|
|
2037
|
-
}
|
|
2038
|
-
/** The name of the active filter. It truncates when the row has no room. */
|
|
2039
|
-
function FilterChipLabel({ className, ...props }) {
|
|
2040
|
-
const { label } = filterChip();
|
|
2041
|
-
return /* @__PURE__ */ jsx("span", {
|
|
2042
|
-
"data-slot": "filter-chip-label",
|
|
2043
|
-
className: label({ className }),
|
|
2044
|
-
...props
|
|
2045
|
-
});
|
|
2046
|
-
}
|
|
2047
|
-
/** The × that resets the filter to its default. Shows an X unless given children. */
|
|
2048
|
-
function FilterChipRemove({ className, children, render, nativeButton, ...props }) {
|
|
2049
|
-
const { remove } = filterChip();
|
|
2050
|
-
return /* @__PURE__ */ jsx(Button, {
|
|
2051
|
-
...props,
|
|
2052
|
-
render,
|
|
2053
|
-
nativeButton: nativeButton ?? defaultNativeButton(render),
|
|
2054
|
-
type: "button",
|
|
2055
|
-
"data-slot": "filter-chip-remove",
|
|
2056
|
-
className: remove({ className }),
|
|
2057
|
-
children: children ?? /* @__PURE__ */ jsx(X, { "aria-hidden": true })
|
|
2058
|
-
});
|
|
2059
|
-
}
|
|
2060
|
-
//#endregion
|
|
2061
|
-
//#region src/components/pull-to-refresh/pull-to-refresh-gesture.ts
|
|
2062
|
-
/**
|
|
2063
|
-
* The pull-to-refresh gesture as a pure state machine (pull-to-refresh.md,
|
|
2064
|
-
* "Behavior"). `usePullToRefresh` feeds it the scroller's touch events; this
|
|
2065
|
-
* file owns every decision, so the rules can be tested without a DOM.
|
|
2066
|
-
*/
|
|
2067
|
-
/** How far the indicator has to be pulled, after damping, for a release to refresh. */
|
|
2068
|
-
const PULL_TO_REFRESH_THRESHOLD = 100;
|
|
2069
|
-
/**
|
|
2070
|
-
* The furthest the indicator follows the finger. 40px past the threshold is
|
|
2071
|
-
* enough to show the pull is armed and still going; any further and a long
|
|
2072
|
-
* drag would push the list off a small phone's screen.
|
|
2073
|
-
*/
|
|
2074
|
-
const PULL_TO_REFRESH_MAX_DISTANCE = 140;
|
|
2075
|
-
/** The indicator's height while the refresh runs. */
|
|
2076
|
-
const PULL_TO_REFRESH_HOLD_DISTANCE = 60;
|
|
2077
|
-
/**
|
|
2078
|
-
* The rubber band: the indicator moves this fraction of the finger's travel,
|
|
2079
|
-
* so it lags and the pull reads as resistance. 0.55 is iOS's own
|
|
2080
|
-
* scroll-view constant.
|
|
2081
|
-
*/
|
|
2082
|
-
const PULL_TO_REFRESH_DAMPING = .55;
|
|
2083
|
-
/**
|
|
2084
|
-
* How long the indicator takes to go back, to the hold height or to 0, once
|
|
2085
|
-
* the finger lifts or the refresh ends: ease-out (`ease-slide`), with no
|
|
2086
|
-
* overshoot. Instant with reduced motion.
|
|
2087
|
-
*/
|
|
2088
|
-
const PULL_TO_REFRESH_SNAP_BACK_MS = 300;
|
|
2089
|
-
/** A finger held this long before it pulls is a long press (a menu, a drag), not a pull. */
|
|
2090
|
-
const PULL_TO_REFRESH_LONG_PRESS_MS = 500;
|
|
2091
|
-
/** How far the finger may drift while it could still be a long press. */
|
|
2092
|
-
const PULL_TO_REFRESH_TOLERANCE = 10;
|
|
2093
|
-
/** Where a touch must not start a pull: text entry, and anything the consumer marks. */
|
|
2094
|
-
const PULL_TO_REFRESH_IGNORE_SELECTOR = [
|
|
2095
|
-
"input",
|
|
2096
|
-
"textarea",
|
|
2097
|
-
"select",
|
|
2098
|
-
"[contenteditable=\"\"]",
|
|
2099
|
-
"[contenteditable=\"true\"]",
|
|
2100
|
-
"[contenteditable=\"plaintext-only\"]",
|
|
2101
|
-
"[data-pull-to-refresh-ignore]"
|
|
2102
|
-
].join(", ");
|
|
2103
|
-
const DEFAULT_PULL_GESTURE_CONFIG = {
|
|
2104
|
-
threshold: 100,
|
|
2105
|
-
maxDistance: 140,
|
|
2106
|
-
holdDistance: 60,
|
|
2107
|
-
latch: true
|
|
2108
|
-
};
|
|
2109
|
-
const IDLE_PULL_GESTURE = {
|
|
2110
|
-
phase: "idle",
|
|
2111
|
-
startX: 0,
|
|
2112
|
-
startY: 0,
|
|
2113
|
-
distance: 0,
|
|
2114
|
-
armed: false
|
|
2115
|
-
};
|
|
2116
|
-
/** How far the indicator is for a finger that has travelled `delta` px down. */
|
|
2117
|
-
function pullDistance(delta, maxDistance = 140) {
|
|
2118
|
-
if (delta <= 0) return 0;
|
|
2119
|
-
return Math.min(maxDistance, delta * PULL_TO_REFRESH_DAMPING);
|
|
2120
|
-
}
|
|
2121
|
-
/** How far towards the threshold the pull is, from 0 to 1. */
|
|
2122
|
-
function pullProgress(distance, threshold = 100) {
|
|
2123
|
-
if (threshold <= 0) return 1;
|
|
2124
|
-
return Math.min(1, Math.max(0, distance / threshold));
|
|
2125
|
-
}
|
|
2126
|
-
/**
|
|
2127
|
-
* Which of the spinner's 4 pixels is lit while pulling, clockwise from the
|
|
2128
|
-
* top left: one step per quarter of the threshold, the last from the
|
|
2129
|
-
* threshold on (the same walk as the native apps' indicator).
|
|
2130
|
-
*/
|
|
2131
|
-
function pullStep(progress) {
|
|
2132
|
-
return Math.min(3, Math.floor(Math.max(0, progress) * 4));
|
|
2133
|
-
}
|
|
2134
|
-
/** The state the indicator shows for a gesture. */
|
|
2135
|
-
function pullState(gesture) {
|
|
2136
|
-
if (gesture.phase === "refreshing") return "refreshing";
|
|
2137
|
-
if (gesture.phase !== "pulling") return "idle";
|
|
2138
|
-
return gesture.distance > 0 || gesture.armed ? "pulling" : "idle";
|
|
2139
|
-
}
|
|
2140
|
-
function step(gesture, preventDefault = false, refresh = false, armed = false) {
|
|
2141
|
-
return {
|
|
2142
|
-
gesture,
|
|
2143
|
-
preventDefault,
|
|
2144
|
-
refresh,
|
|
2145
|
-
armed
|
|
2146
|
-
};
|
|
2147
|
-
}
|
|
2148
|
-
/** The next gesture for an event. */
|
|
2149
|
-
function stepPullGesture(gesture, event, config = DEFAULT_PULL_GESTURE_CONFIG) {
|
|
2150
|
-
if (gesture.phase === "refreshing") return step(event.type === "settle" ? IDLE_PULL_GESTURE : gesture);
|
|
2151
|
-
switch (event.type) {
|
|
2152
|
-
case "start":
|
|
2153
|
-
if (event.fingers !== 1 || !event.atTop || event.ignored) return step(IDLE_PULL_GESTURE);
|
|
2154
|
-
return step({
|
|
2155
|
-
...IDLE_PULL_GESTURE,
|
|
2156
|
-
phase: "tracking",
|
|
2157
|
-
startX: event.x,
|
|
2158
|
-
startY: event.y
|
|
2159
|
-
});
|
|
2160
|
-
case "move": {
|
|
2161
|
-
if (gesture.phase === "idle") return step(gesture);
|
|
2162
|
-
if (event.fingers !== 1) return step(IDLE_PULL_GESTURE);
|
|
2163
|
-
if (!event.atTop && !gesture.armed) return step(IDLE_PULL_GESTURE);
|
|
2164
|
-
const dx = event.x - gesture.startX;
|
|
2165
|
-
const dy = event.y - gesture.startY;
|
|
2166
|
-
if (gesture.phase === "tracking") {
|
|
2167
|
-
if (Math.abs(dx) > PULL_TO_REFRESH_TOLERANCE && Math.abs(dx) > dy) return step(IDLE_PULL_GESTURE);
|
|
2168
|
-
if (dy < -6) return step(IDLE_PULL_GESTURE);
|
|
2169
|
-
if (dy <= 6) return step(gesture);
|
|
2170
|
-
}
|
|
2171
|
-
const distance = pullDistance(dy, config.maxDistance);
|
|
2172
|
-
const armed = config.latch && gesture.armed || distance >= config.threshold;
|
|
2173
|
-
return step({
|
|
2174
|
-
...gesture,
|
|
2175
|
-
phase: "pulling",
|
|
2176
|
-
distance,
|
|
2177
|
-
armed
|
|
2178
|
-
}, armed || dy > 6, false, armed && !gesture.armed);
|
|
2179
|
-
}
|
|
2180
|
-
case "hold": return step(gesture.phase === "tracking" ? IDLE_PULL_GESTURE : gesture);
|
|
2181
|
-
case "end":
|
|
2182
|
-
if (gesture.phase === "pulling" && gesture.armed) return step({
|
|
2183
|
-
...gesture,
|
|
2184
|
-
phase: "refreshing",
|
|
2185
|
-
distance: config.holdDistance
|
|
2186
|
-
}, false, true);
|
|
2187
|
-
return step(IDLE_PULL_GESTURE);
|
|
2188
|
-
case "cancel":
|
|
2189
|
-
case "settle": return step(IDLE_PULL_GESTURE);
|
|
2190
|
-
}
|
|
2191
|
-
}
|
|
2192
|
-
//#endregion
|
|
2193
|
-
//#region src/components/pull-to-refresh/use-pull-to-refresh.ts
|
|
2194
|
-
/** A primary pointer that is a finger: the only input pull to refresh answers. */
|
|
2195
|
-
const PULL_TO_REFRESH_POINTER_QUERY = "(pointer: coarse)";
|
|
2196
|
-
/** The one platform check: Android, by its user agent (Client Hints first). */
|
|
2197
|
-
function detectPullToRefreshPlatform() {
|
|
2198
|
-
if (typeof navigator === "undefined") return "default";
|
|
2199
|
-
const hints = navigator.userAgentData;
|
|
2200
|
-
if (hints?.platform) return hints.platform === "Android" ? "android" : "default";
|
|
2201
|
-
return /\bAndroid\b/i.test(navigator.userAgent) ? "android" : "default";
|
|
2202
|
-
}
|
|
2203
|
-
function hapticTick() {
|
|
2204
|
-
if (typeof navigator !== "undefined" && typeof navigator.vibrate === "function") navigator.vibrate(10);
|
|
2205
|
-
}
|
|
2206
|
-
const subscribeNever = () => () => void 0;
|
|
2207
|
-
const defaultSnapshot = () => "default";
|
|
2208
|
-
function subscribeCoarse(onChange) {
|
|
2209
|
-
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return () => void 0;
|
|
2210
|
-
const media = window.matchMedia(PULL_TO_REFRESH_POINTER_QUERY);
|
|
2211
|
-
media.addEventListener("change", onChange);
|
|
2212
|
-
return () => media.removeEventListener("change", onChange);
|
|
2213
|
-
}
|
|
2214
|
-
function coarseSnapshot() {
|
|
2215
|
-
return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(pointer: coarse)").matches;
|
|
2216
|
-
}
|
|
2217
|
-
function ignoredTarget(target) {
|
|
2218
|
-
return target instanceof Element && target.closest(PULL_TO_REFRESH_IGNORE_SELECTOR) !== null;
|
|
2219
|
-
}
|
|
2220
|
-
/**
|
|
2221
|
-
* Pull to refresh for a scroll container on a touch screen
|
|
2222
|
-
* (pull-to-refresh.md). Pulling down from the top of `scrollRef` past the
|
|
2223
|
-
* threshold calls `onRefresh`; render a `PullToRefreshIndicator` with the
|
|
2224
|
-
* result as the scroller's first child.
|
|
2225
|
-
*
|
|
2226
|
-
* On Android it takes Material 3's look (`PullToRefreshPlatform`). It does
|
|
2227
|
-
* nothing on a fine pointer. While it is on, the scroller gets
|
|
2228
|
-
* `overscroll-behavior-y: contain`, so a browser tab's own pull to reload
|
|
2229
|
-
* doesn't run as well. `scrollRef` is read when the hook turns on; a
|
|
2230
|
-
* scroller that is replaced later needs the hook remounted (a `key`).
|
|
2231
|
-
*/
|
|
2232
|
-
function usePullToRefresh(scrollRef, { onRefresh, disabled = false, threshold = DEFAULT_PULL_GESTURE_CONFIG.threshold, maxDistance = DEFAULT_PULL_GESTURE_CONFIG.maxDistance, holdDistance = DEFAULT_PULL_GESTURE_CONFIG.holdDistance, platform: platformOverride }) {
|
|
2233
|
-
const coarse = useSyncExternalStore(subscribeCoarse, coarseSnapshot, () => false);
|
|
2234
|
-
const detected = useSyncExternalStore(subscribeNever, detectPullToRefreshPlatform, defaultSnapshot);
|
|
2235
|
-
const platform = platformOverride ?? detected;
|
|
2236
|
-
const enabled = coarse && !disabled;
|
|
2237
|
-
const [gesture, setGesture] = useState(IDLE_PULL_GESTURE);
|
|
2238
|
-
const gestureRef = useRef(IDLE_PULL_GESTURE);
|
|
2239
|
-
const onRefreshRef = useLatestRef(onRefresh);
|
|
2240
|
-
useEffect(() => {
|
|
2241
|
-
const element = scrollRef.current;
|
|
2242
|
-
if (!enabled || !element) return;
|
|
2243
|
-
const config = {
|
|
2244
|
-
threshold,
|
|
2245
|
-
maxDistance,
|
|
2246
|
-
holdDistance,
|
|
2247
|
-
latch: platform !== "android"
|
|
2248
|
-
};
|
|
2249
|
-
let holdTimer;
|
|
2250
|
-
let live = true;
|
|
2251
|
-
const dispatch = (event, native) => {
|
|
2252
|
-
const next = stepPullGesture(gestureRef.current, event, config);
|
|
2253
|
-
if (next.preventDefault && native?.cancelable) native.preventDefault();
|
|
2254
|
-
if (next.gesture !== gestureRef.current) {
|
|
2255
|
-
gestureRef.current = next.gesture;
|
|
2256
|
-
setGesture(next.gesture);
|
|
2257
|
-
}
|
|
2258
|
-
if (next.gesture.phase !== "tracking") clearTimeout(holdTimer);
|
|
2259
|
-
if (next.armed && platform === "android") hapticTick();
|
|
2260
|
-
if (next.refresh) {
|
|
2261
|
-
const settle = () => {
|
|
2262
|
-
if (live) dispatch({ type: "settle" });
|
|
2263
|
-
};
|
|
2264
|
-
let result;
|
|
2265
|
-
try {
|
|
2266
|
-
result = Promise.resolve(onRefreshRef.current());
|
|
2267
|
-
} catch (error) {
|
|
2268
|
-
result = Promise.reject(error);
|
|
2269
|
-
}
|
|
2270
|
-
result.then(settle, settle);
|
|
2271
|
-
}
|
|
2272
|
-
};
|
|
2273
|
-
const atTop = () => element.scrollTop <= 0;
|
|
2274
|
-
const onTouchStart = (event) => {
|
|
2275
|
-
const touch = event.touches[0];
|
|
2276
|
-
dispatch({
|
|
2277
|
-
type: "start",
|
|
2278
|
-
x: touch?.clientX ?? 0,
|
|
2279
|
-
y: touch?.clientY ?? 0,
|
|
2280
|
-
fingers: event.touches.length,
|
|
2281
|
-
atTop: atTop(),
|
|
2282
|
-
ignored: ignoredTarget(event.target)
|
|
2283
|
-
});
|
|
2284
|
-
if (gestureRef.current.phase === "tracking") {
|
|
2285
|
-
clearTimeout(holdTimer);
|
|
2286
|
-
holdTimer = setTimeout(() => dispatch({ type: "hold" }), PULL_TO_REFRESH_LONG_PRESS_MS);
|
|
2287
|
-
}
|
|
2288
|
-
};
|
|
2289
|
-
const onTouchMove = (event) => {
|
|
2290
|
-
if (event.defaultPrevented && gestureRef.current.phase === "tracking") {
|
|
2291
|
-
dispatch({ type: "cancel" });
|
|
2292
|
-
return;
|
|
2293
|
-
}
|
|
2294
|
-
const touch = event.touches[0];
|
|
2295
|
-
dispatch({
|
|
2296
|
-
type: "move",
|
|
2297
|
-
x: touch?.clientX ?? 0,
|
|
2298
|
-
y: touch?.clientY ?? 0,
|
|
2299
|
-
fingers: event.touches.length,
|
|
2300
|
-
atTop: atTop()
|
|
2301
|
-
}, event);
|
|
2302
|
-
};
|
|
2303
|
-
const onTouchEnd = () => dispatch({ type: "end" });
|
|
2304
|
-
const onCancel = () => dispatch({ type: "cancel" });
|
|
2305
|
-
const previousOverscroll = element.style.overscrollBehaviorY;
|
|
2306
|
-
element.style.overscrollBehaviorY = "contain";
|
|
2307
|
-
element.addEventListener("touchstart", onTouchStart, { passive: true });
|
|
2308
|
-
element.addEventListener("touchmove", onTouchMove, { passive: false });
|
|
2309
|
-
element.addEventListener("touchend", onTouchEnd, { passive: true });
|
|
2310
|
-
element.addEventListener("touchcancel", onCancel, { passive: true });
|
|
2311
|
-
element.addEventListener("contextmenu", onCancel);
|
|
2312
|
-
element.addEventListener("dragstart", onCancel);
|
|
2313
|
-
return () => {
|
|
2314
|
-
live = false;
|
|
2315
|
-
clearTimeout(holdTimer);
|
|
2316
|
-
element.style.overscrollBehaviorY = previousOverscroll;
|
|
2317
|
-
element.removeEventListener("touchstart", onTouchStart);
|
|
2318
|
-
element.removeEventListener("touchmove", onTouchMove);
|
|
2319
|
-
element.removeEventListener("touchend", onTouchEnd);
|
|
2320
|
-
element.removeEventListener("touchcancel", onCancel);
|
|
2321
|
-
element.removeEventListener("contextmenu", onCancel);
|
|
2322
|
-
element.removeEventListener("dragstart", onCancel);
|
|
2323
|
-
gestureRef.current = IDLE_PULL_GESTURE;
|
|
2324
|
-
setGesture(IDLE_PULL_GESTURE);
|
|
2325
|
-
};
|
|
2326
|
-
}, [
|
|
2327
|
-
enabled,
|
|
2328
|
-
scrollRef,
|
|
2329
|
-
onRefreshRef,
|
|
2330
|
-
threshold,
|
|
2331
|
-
maxDistance,
|
|
2332
|
-
holdDistance,
|
|
2333
|
-
platform
|
|
2334
|
-
]);
|
|
2335
|
-
const state = pullState(gesture);
|
|
2336
|
-
const distance = state === "idle" ? 0 : gesture.distance;
|
|
2337
|
-
return {
|
|
2338
|
-
state,
|
|
2339
|
-
distance,
|
|
2340
|
-
progress: state === "refreshing" || gesture.armed ? 1 : pullProgress(distance, threshold),
|
|
2341
|
-
platform
|
|
2342
|
-
};
|
|
2343
|
-
}
|
|
2344
|
-
//#endregion
|
|
2345
|
-
//#region src/components/pull-to-refresh/pull-to-refresh-material-progress.tsx
|
|
2346
|
-
/**
|
|
2347
|
-
* Android's own pull-to-refresh progress, drawn as Material 3 draws it
|
|
2348
|
-
* (Compose's `PullToRefreshDefaults.Indicator`, the one Chrome on Android
|
|
2349
|
-
* shows too): a 2.5px arc of radius 7.5px in a 40px disc. While pulling the
|
|
2350
|
-
* arc grows to 0.8 of the circle and turns a little with the pull, with an
|
|
2351
|
-
* arrowhead at its end, at 0.3 opacity until the pull is armed and fully
|
|
2352
|
-
* opaque from then on. While refreshing it is Material's indeterminate arc,
|
|
2353
|
-
* which grows and shrinks as it turns. Internal: `PullToRefreshIndicator`
|
|
2354
|
-
* renders it on Android.
|
|
2355
|
-
*/
|
|
2356
|
-
/** The disc's centre, in its 40px box. */
|
|
2357
|
-
const CENTER = 20;
|
|
2358
|
-
const MATERIAL_ARC_RADIUS = 7.5;
|
|
2359
|
-
const MATERIAL_STROKE_WIDTH = 2.5;
|
|
2360
|
-
const ARROW_WIDTH = 10;
|
|
2361
|
-
const ARROW_HEIGHT = 5;
|
|
2362
|
-
/** The longest the arc gets while pulling, as a share of the circle. */
|
|
2363
|
-
const MATERIAL_MAX_ARC = .8;
|
|
2364
|
-
const CIRCUMFERENCE = 2 * Math.PI * MATERIAL_ARC_RADIUS;
|
|
2365
|
-
/**
|
|
2366
|
-
* The arc for a pull's progress (0 to 1), as Material computes it: nothing
|
|
2367
|
-
* for the first 40%, then the arc grows to `MATERIAL_MAX_ARC` of the circle
|
|
2368
|
-
* and its start turns from -45° to +27° (12 o'clock is -90°), and the
|
|
2369
|
-
* arrowhead grows with it.
|
|
2370
|
-
*/
|
|
2371
|
-
function materialArc(progress) {
|
|
2372
|
-
const adjusted = Math.max(Math.min(1, progress) - .4, 0) * 5 / 3;
|
|
2373
|
-
const rotation = (-.25 + .4 * adjusted) * .5;
|
|
2374
|
-
return {
|
|
2375
|
-
startAngle: rotation * 360,
|
|
2376
|
-
endAngle: (rotation + adjusted * MATERIAL_MAX_ARC) * 360,
|
|
2377
|
-
arrowScale: Math.min(1, adjusted)
|
|
2378
|
-
};
|
|
2379
|
-
}
|
|
2380
|
-
const point = (degrees) => {
|
|
2381
|
-
const radians = degrees * Math.PI / 180;
|
|
2382
|
-
return `${CENTER + MATERIAL_ARC_RADIUS * Math.cos(radians)} ${CENTER + MATERIAL_ARC_RADIUS * Math.sin(radians)}`;
|
|
2383
|
-
};
|
|
2384
|
-
/** An SVG path for the arc, clockwise from `startAngle` to `endAngle`. */
|
|
2385
|
-
function arcPath({ startAngle, endAngle }) {
|
|
2386
|
-
const sweep = endAngle - startAngle;
|
|
2387
|
-
if (sweep <= .01) return "";
|
|
2388
|
-
const large = sweep > 180 ? 1 : 0;
|
|
2389
|
-
return `M ${point(startAngle)} A ${MATERIAL_ARC_RADIUS} ${MATERIAL_ARC_RADIUS} 0 ${large} 1 ${point(endAngle)}`;
|
|
2390
|
-
}
|
|
2391
|
-
/**
|
|
2392
|
-
* The arrowhead: a triangle whose base straddles the arc at 3 o'clock and
|
|
2393
|
-
* whose tip points clockwise, turned to the arc's end.
|
|
2394
|
-
*/
|
|
2395
|
-
function arrowPoints(scale) {
|
|
2396
|
-
const half = ARROW_WIDTH * scale / 2;
|
|
2397
|
-
const base = 17.5;
|
|
2398
|
-
const x = 27.5;
|
|
2399
|
-
return `${x - half},${base} ${x + half},${base} ${x},${base + ARROW_HEIGHT * scale}`;
|
|
2400
|
-
}
|
|
2401
|
-
const MATERIAL_STYLE = `
|
|
2402
|
-
@keyframes raft-ptr-material-rotate {
|
|
2403
|
-
to {
|
|
2404
|
-
transform: rotate(360deg);
|
|
2405
|
-
}
|
|
2406
|
-
}
|
|
2407
|
-
|
|
2408
|
-
@keyframes raft-ptr-material-dash {
|
|
2409
|
-
0% {
|
|
2410
|
-
stroke-dasharray: 0.4 ${CIRCUMFERENCE};
|
|
2411
|
-
stroke-dashoffset: 0;
|
|
2412
|
-
}
|
|
2413
|
-
50% {
|
|
2414
|
-
stroke-dasharray: ${CIRCUMFERENCE * .7} ${CIRCUMFERENCE};
|
|
2415
|
-
stroke-dashoffset: ${-CIRCUMFERENCE * .1};
|
|
2416
|
-
}
|
|
2417
|
-
100% {
|
|
2418
|
-
stroke-dasharray: ${CIRCUMFERENCE * .7} ${CIRCUMFERENCE};
|
|
2419
|
-
stroke-dashoffset: ${-CIRCUMFERENCE * .99};
|
|
2420
|
-
}
|
|
2421
|
-
}
|
|
2422
|
-
|
|
2423
|
-
[data-slot="pull-to-refresh-material-progress"][data-state="refreshing"] {
|
|
2424
|
-
animation: raft-ptr-material-rotate 1.4s linear infinite;
|
|
2425
|
-
}
|
|
2426
|
-
|
|
2427
|
-
[data-slot="pull-to-refresh-material-progress"][data-state="refreshing"] [data-material-indeterminate] {
|
|
2428
|
-
animation: raft-ptr-material-dash 1.4s ease-in-out infinite;
|
|
2429
|
-
}
|
|
2430
|
-
|
|
2431
|
-
@media (prefers-reduced-motion: reduce) {
|
|
2432
|
-
[data-slot="pull-to-refresh-material-progress"][data-state="refreshing"],
|
|
2433
|
-
[data-slot="pull-to-refresh-material-progress"][data-state="refreshing"] [data-material-indeterminate] {
|
|
2434
|
-
animation: none;
|
|
2435
|
-
}
|
|
2436
|
-
|
|
2437
|
-
[data-slot="pull-to-refresh-material-progress"][data-state="refreshing"] [data-material-indeterminate] {
|
|
2438
|
-
stroke-dasharray: ${CIRCUMFERENCE * .75} ${CIRCUMFERENCE};
|
|
2439
|
-
}
|
|
2440
|
-
}
|
|
2441
|
-
`;
|
|
2442
|
-
const materialStyle = /* @__PURE__ */ jsx("style", {
|
|
2443
|
-
href: "raft-ui-pull-to-refresh-material",
|
|
2444
|
-
precedence: "default",
|
|
2445
|
-
children: MATERIAL_STYLE
|
|
2446
|
-
});
|
|
2447
|
-
const materialProgress = tv({ slots: {
|
|
2448
|
-
root: ["size-10 shrink-0 overflow-visible", "text-info-strong"],
|
|
2449
|
-
determinate: ["transition-opacity duration-150 ease-out motion-reduce:transition-none", "opacity-30 data-armed:opacity-100"],
|
|
2450
|
-
arc: ["fill-none stroke-current [stroke-linecap:butt]"],
|
|
2451
|
-
arrow: ["fill-current"],
|
|
2452
|
-
indeterminate: ["fill-none stroke-current [stroke-linecap:round]"]
|
|
2453
|
-
} });
|
|
2454
|
-
function PullToRefreshMaterialProgress({ state, progress }) {
|
|
2455
|
-
const { root, determinate, arc, arrow, indeterminate } = materialProgress();
|
|
2456
|
-
const refreshing = state === "refreshing";
|
|
2457
|
-
const armed = progress >= 1;
|
|
2458
|
-
const values = materialArc(refreshing ? 1 : progress);
|
|
2459
|
-
return /* @__PURE__ */ jsxs("svg", {
|
|
2460
|
-
"data-slot": "pull-to-refresh-material-progress",
|
|
2461
|
-
"data-state": state,
|
|
2462
|
-
"data-armed": !refreshing && armed ? "" : void 0,
|
|
2463
|
-
viewBox: "0 0 40 40",
|
|
2464
|
-
"aria-hidden": "true",
|
|
2465
|
-
className: root(),
|
|
2466
|
-
children: [materialStyle, refreshing ? /* @__PURE__ */ jsx("circle", {
|
|
2467
|
-
"data-material-indeterminate": "",
|
|
2468
|
-
cx: CENTER,
|
|
2469
|
-
cy: CENTER,
|
|
2470
|
-
r: MATERIAL_ARC_RADIUS,
|
|
2471
|
-
strokeWidth: MATERIAL_STROKE_WIDTH,
|
|
2472
|
-
transform: `rotate(-90 ${CENTER} ${CENTER})`,
|
|
2473
|
-
className: indeterminate()
|
|
2474
|
-
}) : /* @__PURE__ */ jsxs("g", {
|
|
2475
|
-
"data-armed": armed ? "" : void 0,
|
|
2476
|
-
className: determinate(),
|
|
2477
|
-
children: [/* @__PURE__ */ jsx("path", {
|
|
2478
|
-
"data-material-arc": "",
|
|
2479
|
-
d: arcPath(values),
|
|
2480
|
-
strokeWidth: MATERIAL_STROKE_WIDTH,
|
|
2481
|
-
className: arc()
|
|
2482
|
-
}), values.arrowScale > 0 && /* @__PURE__ */ jsx("polygon", {
|
|
2483
|
-
"data-material-arrow": "",
|
|
2484
|
-
points: arrowPoints(values.arrowScale),
|
|
2485
|
-
transform: `rotate(${values.endAngle - 2.5} ${CENTER} ${CENTER})`,
|
|
2486
|
-
className: arrow()
|
|
2487
|
-
})]
|
|
2488
|
-
})]
|
|
2489
|
-
});
|
|
2490
|
-
}
|
|
2491
|
-
//#endregion
|
|
2492
|
-
//#region src/components/pull-to-refresh/pull-to-refresh-indicator.tsx
|
|
2493
|
-
const pullStepStyle = /* @__PURE__ */ jsx("style", {
|
|
2494
|
-
href: "raft-ui-pull-to-refresh",
|
|
2495
|
-
precedence: "default",
|
|
2496
|
-
children: `
|
|
2497
|
-
[data-slot="pull-to-refresh-indicator"][data-pull-step] [data-spinner-pixel],
|
|
2498
|
-
[data-slot="pull-to-refresh-indicator"][data-state="idle"] [data-spinner-pixel] {
|
|
2499
|
-
animation: none;
|
|
2500
|
-
opacity: 0.2;
|
|
2501
|
-
}
|
|
2502
|
-
|
|
2503
|
-
[data-slot="pull-to-refresh-indicator"][data-pull-step="0"] [data-spinner-pixel]:nth-child(1),
|
|
2504
|
-
[data-slot="pull-to-refresh-indicator"][data-pull-step="1"] [data-spinner-pixel]:nth-child(2),
|
|
2505
|
-
[data-slot="pull-to-refresh-indicator"][data-pull-step="2"] [data-spinner-pixel]:nth-child(4),
|
|
2506
|
-
[data-slot="pull-to-refresh-indicator"][data-pull-step="3"] [data-spinner-pixel]:nth-child(3) {
|
|
2507
|
-
opacity: 1;
|
|
2508
|
-
}
|
|
2509
|
-
`
|
|
2510
|
-
});
|
|
2511
|
-
const SNAP_BACK = ["duration-300 ease-slide motion-reduce:transition-none", "data-[state=pulling]:transition-none"];
|
|
2512
|
-
const pullToRefreshIndicator = tv({
|
|
2513
|
-
slots: {
|
|
2514
|
-
root: ["flex w-full shrink-0 justify-center", "text-foreground-icon"],
|
|
2515
|
-
surface: ["flex items-center justify-center"],
|
|
2516
|
-
spinner: [
|
|
2517
|
-
"transition-opacity duration-300 ease-slide motion-reduce:transition-none",
|
|
2518
|
-
"in-data-[state=pulling]:transition-none",
|
|
2519
|
-
"in-data-[state=pulling]:opacity-(--pull-progress) in-data-[state=idle]:opacity-0"
|
|
2520
|
-
],
|
|
2521
|
-
status: ["sr-only"]
|
|
2522
|
-
},
|
|
2523
|
-
variants: { platform: {
|
|
2524
|
-
default: {
|
|
2525
|
-
root: [
|
|
2526
|
-
"relative items-center overflow-hidden",
|
|
2527
|
-
"transition-[height]",
|
|
2528
|
-
...SNAP_BACK
|
|
2529
|
-
],
|
|
2530
|
-
surface: ["size-full"]
|
|
2531
|
-
},
|
|
2532
|
-
android: {
|
|
2533
|
-
root: ["sticky top-0 z-10 h-0 overflow-visible"],
|
|
2534
|
-
surface: [
|
|
2535
|
-
"absolute inset-x-0 top-0 mx-auto size-10 rounded-full bg-layer-popover shadow-float",
|
|
2536
|
-
"translate-y-[calc(var(--pull-distance)-100%)]",
|
|
2537
|
-
"transition-[translate,opacity,scale]",
|
|
2538
|
-
...SNAP_BACK,
|
|
2539
|
-
"data-[state=idle]:opacity-0",
|
|
2540
|
-
"data-[exit=finish]:scale-0 data-[exit=finish]:duration-150 data-[exit=finish]:ease-out"
|
|
2541
|
-
]
|
|
2542
|
-
}
|
|
2543
|
-
} },
|
|
2544
|
-
defaultVariants: { platform: "default" }
|
|
2545
|
-
});
|
|
2546
|
-
/**
|
|
2547
|
-
* How the Android disc leaves once idle: `finish` when a refresh has just
|
|
2548
|
-
* ended (it scales down where it held), `cancel` otherwise (it slides back
|
|
2549
|
-
* up). The finish keeps the distance it held, so it doesn't also slide.
|
|
2550
|
-
* `gone` once the scale-down has ended: the disc stays scaled away and its
|
|
2551
|
-
* arc stops turning.
|
|
2552
|
-
*/
|
|
2553
|
-
function useAndroidExit(state, distance) {
|
|
2554
|
-
const [previous, setPrevious] = useState(state);
|
|
2555
|
-
const [exit, setExit] = useState({
|
|
2556
|
-
kind: "cancel",
|
|
2557
|
-
distance: 0
|
|
2558
|
-
});
|
|
2559
|
-
const [held, setHeld] = useState(distance);
|
|
2560
|
-
if (state !== previous) {
|
|
2561
|
-
setPrevious(state);
|
|
2562
|
-
if (state === "idle") setExit(previous === "refreshing" ? {
|
|
2563
|
-
kind: "finish",
|
|
2564
|
-
distance: held
|
|
2565
|
-
} : {
|
|
2566
|
-
kind: "cancel",
|
|
2567
|
-
distance: 0
|
|
2568
|
-
});
|
|
2569
|
-
}
|
|
2570
|
-
if (state === "refreshing" && distance !== held) setHeld(distance);
|
|
2571
|
-
const settle = () => {
|
|
2572
|
-
if (exit.kind === "finish") setExit({
|
|
2573
|
-
...exit,
|
|
2574
|
-
kind: "gone"
|
|
2575
|
-
});
|
|
2576
|
-
};
|
|
2577
|
-
return {
|
|
2578
|
-
...exit,
|
|
2579
|
-
settle
|
|
2580
|
-
};
|
|
2581
|
-
}
|
|
2582
|
-
/**
|
|
2583
|
-
* The pull's indicator. Render it as the scroller's first child: by default
|
|
2584
|
-
* it is the space the pull opens, with the DS Spinner in it, so the content
|
|
2585
|
-
* moves down with the pull; on Android it is Material 3's own indicator, a
|
|
2586
|
-
* disc with a progress arc that travels down over the content. It holds a
|
|
2587
|
-
* polite live region that announces `label` while refreshing.
|
|
2588
|
-
*/
|
|
2589
|
-
function PullToRefreshIndicator({ pull, label, className, style, ...props }) {
|
|
2590
|
-
const { state, distance, progress, platform } = pull;
|
|
2591
|
-
const { root, surface, spinner, status } = pullToRefreshIndicator({ platform });
|
|
2592
|
-
const android = platform === "android";
|
|
2593
|
-
const exit = useAndroidExit(state, distance);
|
|
2594
|
-
const finishing = android && state === "idle" && exit.kind !== "cancel";
|
|
2595
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
2596
|
-
"data-slot": "pull-to-refresh-indicator",
|
|
2597
|
-
"data-state": state,
|
|
2598
|
-
"data-platform": platform,
|
|
2599
|
-
"data-pull-step": !android && state === "pulling" ? pullStep(progress) : void 0,
|
|
2600
|
-
className: root({ className }),
|
|
2601
|
-
style: {
|
|
2602
|
-
...style,
|
|
2603
|
-
height: android ? void 0 : distance,
|
|
2604
|
-
["--pull-progress"]: progress,
|
|
2605
|
-
["--pull-distance"]: `${finishing ? exit.distance : distance}px`
|
|
2606
|
-
},
|
|
2607
|
-
...props,
|
|
2608
|
-
children: [
|
|
2609
|
-
!android && pullStepStyle,
|
|
2610
|
-
/* @__PURE__ */ jsx("div", {
|
|
2611
|
-
"data-slot": "pull-to-refresh-surface",
|
|
2612
|
-
"data-state": state,
|
|
2613
|
-
"data-exit": finishing ? "finish" : void 0,
|
|
2614
|
-
className: surface(),
|
|
2615
|
-
onTransitionEnd: (event) => {
|
|
2616
|
-
if (event.target === event.currentTarget && event.propertyName === "scale") exit.settle();
|
|
2617
|
-
},
|
|
2618
|
-
children: android ? /* @__PURE__ */ jsx(PullToRefreshMaterialProgress, {
|
|
2619
|
-
state: finishing && exit.kind === "finish" ? "refreshing" : state,
|
|
2620
|
-
progress
|
|
2621
|
-
}) : /* @__PURE__ */ jsx(Spinner, {
|
|
2622
|
-
"aria-hidden": "true",
|
|
2623
|
-
role: "presentation",
|
|
2624
|
-
"aria-label": void 0,
|
|
2625
|
-
className: spinner()
|
|
2626
|
-
})
|
|
2627
|
-
}),
|
|
2628
|
-
/* @__PURE__ */ jsx("span", {
|
|
2629
|
-
role: "status",
|
|
2630
|
-
"aria-live": "polite",
|
|
2631
|
-
className: status(),
|
|
2632
|
-
children: state === "refreshing" ? label : ""
|
|
2633
|
-
})
|
|
2634
|
-
]
|
|
2635
|
-
});
|
|
2636
|
-
}
|
|
2637
|
-
//#endregion
|
|
2638
|
-
//#region src/components/choice-list/choice-list.recipe.ts
|
|
2639
|
-
const choiceList = tv({
|
|
2640
|
-
slots: {
|
|
2641
|
-
root: ["grid min-w-0 gap-2"],
|
|
2642
|
-
searchInput: ["[&::-webkit-search-cancel-button]:appearance-none"],
|
|
2643
|
-
frame: ["flex min-w-0 flex-col overflow-hidden", "rounded-container border border-line-hairline bg-layer-card"],
|
|
2644
|
-
scroller: ["min-h-0 min-w-0 p-1"],
|
|
2645
|
-
rows: ["grid grid-cols-[minmax(0,1fr)_auto] gap-x-0 gap-y-0.5"],
|
|
2646
|
-
group: ["col-span-2 col-start-1 grid grid-cols-subgrid grid-rows-subgrid gap-x-0 gap-y-0.5"],
|
|
2647
|
-
section: ["col-span-2 col-start-1 grid grid-cols-subgrid grid-rows-subgrid"],
|
|
2648
|
-
heading: ["col-span-2 px-2.5 pt-2 pb-1", "font-sans text-caption font-bold text-foreground-hint"],
|
|
2649
|
-
item: [
|
|
2650
|
-
"col-span-2 flex min-w-0 items-start gap-2.5 rounded-chip px-2.5",
|
|
2651
|
-
"min-h-option py-option-inset",
|
|
2652
|
-
"pointer-coarse:min-h-11 pointer-coarse:py-[calc((--spacing(11)-1lh)/2)]",
|
|
2653
|
-
"text-left font-sans text-subhead",
|
|
2654
|
-
"not-data-disabled:hover:bg-brand-soft not-data-disabled:active:overlay-medium",
|
|
2655
|
-
"data-disabled:cursor-not-allowed"
|
|
2656
|
-
],
|
|
2657
|
-
media: ["flex h-lh shrink-0 items-center"],
|
|
2658
|
-
body: ["grid min-w-0 flex-1 gap-0.5"],
|
|
2659
|
-
label: ["truncate font-semibold text-foreground", "in-data-disabled:text-foreground-disabled"],
|
|
2660
|
-
description: ["block truncate font-sans text-footnote font-normal text-foreground-muted", "in-data-disabled:text-foreground-disabled"],
|
|
2661
|
-
action: [
|
|
2662
|
-
"relative z-10 col-start-2 flex h-lh items-center self-start pr-1",
|
|
2663
|
-
"mt-option-inset pointer-coarse:mt-[calc((--spacing(11)-1lh)/2)]",
|
|
2664
|
-
"font-sans text-subhead"
|
|
2665
|
-
],
|
|
2666
|
-
note: ["px-2.5 py-2 font-sans text-callout text-foreground-hint"],
|
|
2667
|
-
footer: ["shrink-0 border-t border-line-seam p-1"]
|
|
2668
|
-
},
|
|
2669
|
-
variants: {
|
|
2670
|
-
scroll: { true: { scroller: ["max-h-72 overflow-y-auto overscroll-contain"] } },
|
|
2671
|
-
fill: { true: {
|
|
2672
|
-
root: ["flex min-h-0 flex-1 flex-col"],
|
|
2673
|
-
frame: ["min-h-0 flex-1"],
|
|
2674
|
-
scroller: ["flex-1 overflow-y-auto overscroll-contain"]
|
|
2675
|
-
} },
|
|
2676
|
-
hasAction: { true: { item: ["pr-11"] } }
|
|
2677
|
-
}
|
|
2678
|
-
});
|
|
2679
|
-
//#endregion
|
|
2680
|
-
//#region src/components/choice-list/choice-list.tsx
|
|
2681
|
-
const SEARCH_ABOVE = 5;
|
|
2682
|
-
const MAX_DESCRIPTION_LINES = 2;
|
|
2683
|
-
function isDrawn(line) {
|
|
2684
|
-
return line !== null && line !== void 0 && line !== false && line !== "";
|
|
2685
|
-
}
|
|
2686
|
-
function descriptionLines(description) {
|
|
2687
|
-
return (Array.isArray(description) ? description : [description]).filter(isDrawn);
|
|
2688
|
-
}
|
|
2689
|
-
function CutLine({ id, slot, className, children }) {
|
|
2690
|
-
const ref = useOverflowTitle();
|
|
2691
|
-
return /* @__PURE__ */ jsx("span", {
|
|
2692
|
-
ref,
|
|
2693
|
-
id,
|
|
2694
|
-
"data-slot": slot,
|
|
2695
|
-
className,
|
|
2696
|
-
children
|
|
2697
|
-
});
|
|
2698
|
-
}
|
|
2699
|
-
const NOT_TEXT_INPUTS = /* @__PURE__ */ new Set([
|
|
2700
|
-
"button",
|
|
2701
|
-
"checkbox",
|
|
2702
|
-
"radio",
|
|
2703
|
-
"submit",
|
|
2704
|
-
"reset",
|
|
2705
|
-
"image",
|
|
2706
|
-
"file",
|
|
2707
|
-
"range",
|
|
2708
|
-
"color"
|
|
2709
|
-
]);
|
|
2710
|
-
function isTextEntry(element) {
|
|
2711
|
-
if (!(element instanceof HTMLElement)) return false;
|
|
2712
|
-
if (element.isContentEditable || element instanceof HTMLTextAreaElement) return true;
|
|
2713
|
-
if (element instanceof HTMLSelectElement) return true;
|
|
2714
|
-
return element instanceof HTMLInputElement && !NOT_TEXT_INPUTS.has(element.type);
|
|
2715
|
-
}
|
|
2716
|
-
function searchTextOf(item) {
|
|
2717
|
-
if (item.searchText !== void 0) return item.searchText;
|
|
2718
|
-
return typeof item.label === "string" ? item.label : "";
|
|
2719
|
-
}
|
|
2720
|
-
function visibleItems(items, needle, pinned, chosen, matchedValues) {
|
|
2721
|
-
if (!needle) return {
|
|
2722
|
-
rows: items,
|
|
2723
|
-
noMatch: false
|
|
2724
|
-
};
|
|
2725
|
-
const matched = matchedValues ? new Set(matchedValues) : null;
|
|
2726
|
-
const matches = (item) => matched ? matched.has(item.value) : searchTextOf(item).toLowerCase().includes(needle);
|
|
2727
|
-
const pinnedSet = new Set(pinned);
|
|
2728
|
-
const isPinned = (item) => pinnedSet.has(item.value);
|
|
2729
|
-
const top = items.filter((item) => isPinned(item) || chosen.includes(item.value) && !matches(item));
|
|
2730
|
-
const byValue = matchedValues ? new Map(items.map((item) => [item.value, item])) : null;
|
|
2731
|
-
const rest = (byValue && matched ? [...matched].flatMap((value) => byValue.get(value) ?? []) : items).filter((item) => !top.includes(item) && matches(item));
|
|
2732
|
-
return {
|
|
2733
|
-
rows: [...top, ...rest],
|
|
2734
|
-
noMatch: !items.some(matches)
|
|
2735
|
-
};
|
|
2736
|
-
}
|
|
2737
|
-
function layoutSections(rows, groups) {
|
|
2738
|
-
const known = new Set(groups?.map((group) => group.value));
|
|
2739
|
-
const sections = groups ? groups.map((group) => ({
|
|
2740
|
-
group,
|
|
2741
|
-
rows: rows.filter((item) => item.group === group.value)
|
|
2742
|
-
})) : [];
|
|
2743
|
-
sections.push({
|
|
2744
|
-
group: null,
|
|
2745
|
-
rows: rows.filter((item) => !groups || item.group === void 0 || !known.has(item.group))
|
|
2746
|
-
});
|
|
2747
|
-
const drawn = sections.filter((section) => section.rows.length > 0);
|
|
2748
|
-
const gridRowOf = /* @__PURE__ */ new Map();
|
|
2749
|
-
let gridRows = 0;
|
|
2750
|
-
for (const section of drawn) {
|
|
2751
|
-
if (section.group) gridRows += 1;
|
|
2752
|
-
for (const item of section.rows) {
|
|
2753
|
-
gridRows += 1;
|
|
2754
|
-
gridRowOf.set(item.value, gridRows);
|
|
2755
|
-
}
|
|
2756
|
-
}
|
|
2757
|
-
return {
|
|
2758
|
-
sections: drawn,
|
|
2759
|
-
gridRowOf,
|
|
2760
|
-
gridRows
|
|
2761
|
-
};
|
|
2762
|
-
}
|
|
2763
|
-
function ChoiceList(props) {
|
|
2764
|
-
const { items, search = "auto", "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, disabled = false, searchLabel, searchPlaceholder, defaultSearchValue = "", searchValue, onSearchValueChange, matchedValues, clearSearchLabel, noMatchLabel, empty, groups, footer, fill = false, className } = props;
|
|
2765
|
-
const value = props.value;
|
|
2766
|
-
const chosen = useMemo(() => value === null ? [] : typeof value === "string" ? [value] : value, [value]);
|
|
2767
|
-
const [ownQuery, setOwnQuery] = useState(defaultSearchValue);
|
|
2768
|
-
const query = searchValue ?? ownQuery;
|
|
2769
|
-
const setQuery = useCallback((next) => {
|
|
2770
|
-
if (searchValue === void 0) setOwnQuery(next);
|
|
2771
|
-
onSearchValueChange?.(next);
|
|
2772
|
-
}, [searchValue, onSearchValueChange]);
|
|
2773
|
-
const [pinned, setPinned] = useState(chosen);
|
|
2774
|
-
const inputRef = useRef(null);
|
|
2775
|
-
const baseId = useId();
|
|
2776
|
-
const searchable = items.length > 0 && (search === "always" || items.length > SEARCH_ABOVE);
|
|
2777
|
-
const [wasSearchable, setWasSearchable] = useState(searchable);
|
|
2778
|
-
if (wasSearchable !== searchable) {
|
|
2779
|
-
setWasSearchable(searchable);
|
|
2780
|
-
if (!searchable) setOwnQuery("");
|
|
2781
|
-
}
|
|
2782
|
-
useEffect(() => {
|
|
2783
|
-
if (!searchable && searchValue) onSearchValueChange?.("");
|
|
2784
|
-
}, [
|
|
2785
|
-
searchable,
|
|
2786
|
-
searchValue,
|
|
2787
|
-
onSearchValueChange
|
|
2788
|
-
]);
|
|
2789
|
-
const [pinnedFor, setPinnedFor] = useState(query);
|
|
2790
|
-
if (pinnedFor !== query) {
|
|
2791
|
-
setPinnedFor(query);
|
|
2792
|
-
setPinned(chosen);
|
|
2793
|
-
}
|
|
2794
|
-
const { rows, noMatch } = visibleItems(items, searchable ? query.trim().toLowerCase() : "", pinned, chosen, matchedValues);
|
|
2795
|
-
const hasAction = rows.some((item) => item.action !== void 0 && item.action !== null);
|
|
2796
|
-
const slots = choiceList({
|
|
2797
|
-
scroll: searchable && !fill,
|
|
2798
|
-
fill
|
|
2799
|
-
});
|
|
2800
|
-
useEffect(() => {
|
|
2801
|
-
if (process.env.NODE_ENV === "production") return;
|
|
2802
|
-
if (!ariaLabel && !ariaLabelledBy) console.warn("raft-ui: a ChoiceList has neither aria-label nor aria-labelledby.");
|
|
2803
|
-
if (items.some((item) => descriptionLines(item.description).length > MAX_DESCRIPTION_LINES)) console.warn(`raft-ui: a ChoiceList item has more than ${MAX_DESCRIPTION_LINES} description lines; only the first ${MAX_DESCRIPTION_LINES} are drawn.`);
|
|
2804
|
-
}, [
|
|
2805
|
-
ariaLabel,
|
|
2806
|
-
ariaLabelledBy,
|
|
2807
|
-
items
|
|
2808
|
-
]);
|
|
2809
|
-
const focusSearch = search === "always" && searchable;
|
|
2810
|
-
const focusedSearch = useRef(false);
|
|
2811
|
-
useEffect(() => {
|
|
2812
|
-
if (!focusSearch || focusedSearch.current) return;
|
|
2813
|
-
focusedSearch.current = true;
|
|
2814
|
-
const input = inputRef.current;
|
|
2815
|
-
if (!input || typeof window.matchMedia !== "function") return;
|
|
2816
|
-
if (!window.matchMedia("(pointer: fine)").matches) return;
|
|
2817
|
-
if (isTextEntry(document.activeElement)) return;
|
|
2818
|
-
input.focus({ preventScroll: true });
|
|
2819
|
-
}, [focusSearch]);
|
|
2820
|
-
const changeQuery = useCallback((event) => {
|
|
2821
|
-
setQuery(event.currentTarget.value);
|
|
2822
|
-
}, [setQuery]);
|
|
2823
|
-
const clearQuery = useCallback(() => {
|
|
2824
|
-
setQuery("");
|
|
2825
|
-
inputRef.current?.focus();
|
|
2826
|
-
}, [setQuery]);
|
|
2827
|
-
const { sections, gridRowOf, gridRows } = layoutSections(rows, groups);
|
|
2828
|
-
const groupProps = {
|
|
2829
|
-
"aria-label": ariaLabel,
|
|
2830
|
-
"aria-labelledby": ariaLabelledBy,
|
|
2831
|
-
disabled,
|
|
2832
|
-
className: slots.group(),
|
|
2833
|
-
style: { gridRow: `1 / span ${gridRows}` }
|
|
2834
|
-
};
|
|
2835
|
-
const rowElement = (item) => {
|
|
2836
|
-
const id = `${baseId}-${items.indexOf(item)}`;
|
|
2837
|
-
const lines = descriptionLines(item.description).slice(0, MAX_DESCRIPTION_LINES);
|
|
2838
|
-
const rowDisabled = disabled || item.disabled === true;
|
|
2839
|
-
const markProps = {
|
|
2840
|
-
value: item.value,
|
|
2841
|
-
disabled: item.disabled,
|
|
2842
|
-
"aria-labelledby": `${id}-label`,
|
|
2843
|
-
"aria-describedby": lines.length > 0 ? `${id}-description` : void 0
|
|
2844
|
-
};
|
|
2845
|
-
return /* @__PURE__ */ jsxs("label", {
|
|
2846
|
-
"data-slot": "choice-list-item",
|
|
2847
|
-
"data-disabled": rowDisabled ? "" : void 0,
|
|
2848
|
-
className: slots.item({ hasAction }),
|
|
2849
|
-
children: [
|
|
2850
|
-
props.multiple ? /* @__PURE__ */ jsx(Checkbox, { ...markProps }) : /* @__PURE__ */ jsx(RadioGroupItem, { ...markProps }),
|
|
2851
|
-
item.media !== void 0 && item.media !== null ? /* @__PURE__ */ jsx("span", {
|
|
2852
|
-
"data-slot": "choice-list-media",
|
|
2853
|
-
className: slots.media(),
|
|
2854
|
-
children: item.media
|
|
2855
|
-
}) : null,
|
|
2856
|
-
/* @__PURE__ */ jsxs("span", {
|
|
2857
|
-
"data-slot": "choice-list-body",
|
|
2858
|
-
className: slots.body(),
|
|
2859
|
-
children: [/* @__PURE__ */ jsx(CutLine, {
|
|
2860
|
-
id: `${id}-label`,
|
|
2861
|
-
slot: "choice-list-label",
|
|
2862
|
-
className: slots.label(),
|
|
2863
|
-
children: item.label
|
|
2864
|
-
}), lines.length > 0 ? /* @__PURE__ */ jsx("span", {
|
|
2865
|
-
id: `${id}-description`,
|
|
2866
|
-
className: "contents",
|
|
2867
|
-
children: lines.map((line, index) => /* @__PURE__ */ jsx(CutLine, {
|
|
2868
|
-
slot: "choice-list-description",
|
|
2869
|
-
className: slots.description(),
|
|
2870
|
-
children: line
|
|
2871
|
-
}, index))
|
|
2872
|
-
}) : null]
|
|
2873
|
-
})
|
|
2874
|
-
]
|
|
2875
|
-
}, item.value);
|
|
2876
|
-
};
|
|
2877
|
-
const rowElements = sections.map((section, index) => {
|
|
2878
|
-
if (!section.group) return section.rows.map(rowElement);
|
|
2879
|
-
const headingId = `${baseId}-group-${index}`;
|
|
2880
|
-
const start = (gridRowOf.get(section.rows[0].value) ?? 1) - 1;
|
|
2881
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
2882
|
-
role: "group",
|
|
2883
|
-
"aria-labelledby": headingId,
|
|
2884
|
-
"data-slot": "choice-list-section",
|
|
2885
|
-
className: slots.section(),
|
|
2886
|
-
style: { gridRow: `${start} / span ${section.rows.length + 1}` },
|
|
2887
|
-
children: [/* @__PURE__ */ jsx("div", {
|
|
2888
|
-
id: headingId,
|
|
2889
|
-
"data-slot": "choice-list-heading",
|
|
2890
|
-
className: slots.heading(),
|
|
2891
|
-
children: section.group.label
|
|
2892
|
-
}), section.rows.map(rowElement)]
|
|
2893
|
-
}, section.group.value);
|
|
2894
|
-
});
|
|
2895
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
2896
|
-
"data-slot": "choice-list",
|
|
2897
|
-
className: slots.root({ className }),
|
|
2898
|
-
children: [searchable ? /* @__PURE__ */ jsxs(InputGroup, {
|
|
2899
|
-
"data-slot": "choice-list-search",
|
|
2900
|
-
children: [
|
|
2901
|
-
/* @__PURE__ */ jsx(InputGroupAddon, { children: /* @__PURE__ */ jsx(Search, { "aria-hidden": true }) }),
|
|
2902
|
-
/* @__PURE__ */ jsx(InputGroupInput, {
|
|
2903
|
-
ref: inputRef,
|
|
2904
|
-
type: "search",
|
|
2905
|
-
value: query,
|
|
2906
|
-
onChange: changeQuery,
|
|
2907
|
-
"aria-label": searchLabel,
|
|
2908
|
-
placeholder: searchPlaceholder,
|
|
2909
|
-
disabled,
|
|
2910
|
-
autoComplete: "off",
|
|
2911
|
-
className: slots.searchInput()
|
|
2912
|
-
}),
|
|
2913
|
-
query ? /* @__PURE__ */ jsx(InputGroupAddon, {
|
|
2914
|
-
align: "inline-end",
|
|
2915
|
-
children: /* @__PURE__ */ jsx(Button$1, {
|
|
2916
|
-
variant: "ghost",
|
|
2917
|
-
size: "icon-md",
|
|
2918
|
-
"aria-label": clearSearchLabel,
|
|
2919
|
-
onClick: clearQuery,
|
|
2920
|
-
children: /* @__PURE__ */ jsx(X, { "aria-hidden": true })
|
|
2921
|
-
})
|
|
2922
|
-
}) : null
|
|
2923
|
-
]
|
|
2924
|
-
}) : null, /* @__PURE__ */ jsxs("div", {
|
|
2925
|
-
"data-slot": "choice-list-frame",
|
|
2926
|
-
className: slots.frame(),
|
|
2927
|
-
children: [/* @__PURE__ */ jsx("div", {
|
|
2928
|
-
"data-slot": "choice-list-scroller",
|
|
2929
|
-
className: slots.scroller(),
|
|
2930
|
-
children: items.length === 0 ? /* @__PURE__ */ jsx("p", {
|
|
2931
|
-
"data-slot": "choice-list-note",
|
|
2932
|
-
className: slots.note(),
|
|
2933
|
-
children: empty
|
|
2934
|
-
}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [rows.length > 0 ? /* @__PURE__ */ jsxs("div", {
|
|
2935
|
-
"data-slot": "choice-list-rows",
|
|
2936
|
-
className: slots.rows(),
|
|
2937
|
-
children: [props.multiple ? /* @__PURE__ */ jsx(CheckboxGroup, {
|
|
2938
|
-
"data-slot": "choice-list-group",
|
|
2939
|
-
value: props.value,
|
|
2940
|
-
onValueChange: props.onValueChange,
|
|
2941
|
-
...groupProps,
|
|
2942
|
-
children: rowElements
|
|
2943
|
-
}) : /* @__PURE__ */ jsx(RadioGroup, {
|
|
2944
|
-
"data-slot": "choice-list-group",
|
|
2945
|
-
value: props.value,
|
|
2946
|
-
onValueChange: props.onValueChange,
|
|
2947
|
-
...groupProps,
|
|
2948
|
-
children: rowElements
|
|
2949
|
-
}), sections.flatMap((section) => section.rows.map((item) => item.action !== void 0 && item.action !== null ? /* @__PURE__ */ jsx("div", {
|
|
2950
|
-
"data-slot": "choice-list-action",
|
|
2951
|
-
className: slots.action(),
|
|
2952
|
-
style: { gridRow: gridRowOf.get(item.value) },
|
|
2953
|
-
children: item.action
|
|
2954
|
-
}, item.value) : null))]
|
|
2955
|
-
}) : null, searchable ? /* @__PURE__ */ jsx("p", {
|
|
2956
|
-
role: "status",
|
|
2957
|
-
"data-slot": "choice-list-status",
|
|
2958
|
-
className: noMatch ? slots.note() : "sr-only",
|
|
2959
|
-
children: noMatch ? noMatchLabel : null
|
|
2960
|
-
}) : null] })
|
|
2961
|
-
}), footer !== void 0 && footer !== null ? /* @__PURE__ */ jsx("div", {
|
|
2962
|
-
"data-slot": "choice-list-footer",
|
|
2963
|
-
className: slots.footer(),
|
|
2964
|
-
children: footer
|
|
2965
|
-
}) : null]
|
|
2966
|
-
})]
|
|
2967
|
-
});
|
|
2968
|
-
}
|
|
2969
|
-
//#endregion
|
|
2970
|
-
export { Dialog as AnnouncementDialog, AnnouncementDialogAction, AnnouncementDialogBody, DialogClose as AnnouncementDialogClose, AnnouncementDialogContent, AnnouncementDialogFooter, AnnouncementDialogHeader, AnnouncementDialogLabel, AnnouncementDialogPicture, AnnouncementDialogSecondaryAction, AnnouncementDialogText, AnnouncementDialogTitle, DialogTrigger as AnnouncementDialogTrigger, AvatarGroupButton, COMMENT_HIGHLIGHT, COMMENT_HIGHLIGHT_ACTIVE, ChoiceList, ComboboxSelect, CommentCard, CommentCardMeta, CommentCardQuote, CommentCardTrigger, CommentEntry, CommentEntryAuthor, CommentEntryBody, CommentEntryHeader, CommentEntryTime, CommentMargin, CommentMarginItem, CommentMarker, DEFAULT_DRAG_MESSAGES, DRAG_AUTO_SCROLL_ZONE_PX, DRAG_KEYBOARD_CODES, DRAG_KEYBOARD_OFFSET_PX, DRAG_LIFT_Z_INDEX, DRAG_SETTLE, DRAG_SIBLING_TRANSITION, DROP_GLIDE, DialogCommit, DialogFooterNote, DialogSheetBack, DialogSheetContent, DragLift, DragLiveRegion, DragRefusalHint, DragRemeasure, DraggedSlot, DropPlaceholder, DropdownMenuLinkItem, Ellipsis, EllipsisToggle, FilterChip, FilterChipLabel, FilterChipRemove, HoldThenMoveTouchSensor, InboxItemLeadingBadge, InboxItemSkeleton, InboxItemSkeletonList, LIFT_CLASS, ListRowSkeleton, MOUSE_DRAG_DISTANCE_PX, MessageItemDelegatedAvatar, MessageItemDelegatedBy, MessageItemDelegatedByButton, MessageItemDelegatedByName, MessageListJumpControls, MessageListJumpPill, MessageListJumpPillDismiss, MessageListJumpPillGroup, MessageQuotedPreviewAuthorDelegatedBy, MessageReferenceIcon, MessageReferenceReadMark, Meter, MeterHeader, MeterIndicator, MeterLabel, MeterTrack, MeterValue, MobileNavServerItem, MobileNavServerPopover, MobileNavServerPopoverContent, MobileNavServerPopoverDestination, MobileNavServerPopoverDestinations, MobileNavServerPopoverGroup, MobileNavServerPopoverList, MobileNavServerPopoverRow, MobileNavServerPopoverRowBody, MobileNavServerPopoverRowDescription, MobileNavServerPopoverRowLabel, ModeBar, ModeBarAction, ModeBarContent, ModeBarDescription, ModeBarTitle, MouseOnlyPointerSensor, ORIGIN_OPACITY, OverlayLayerProvider, PULL_TO_REFRESH_DAMPING, PULL_TO_REFRESH_HOLD_DISTANCE, PULL_TO_REFRESH_MAX_DISTANCE, PULL_TO_REFRESH_POINTER_QUERY, PULL_TO_REFRESH_SNAP_BACK_MS, PULL_TO_REFRESH_THRESHOLD, PopoverActions, PopoverBody, PreviewCardActions, PreviewCardBody, PreviewCardEyebrow, PreviewCardFooter, PreviewCardMeta, PullToRefreshIndicator, TOUCH_DRAG_DISTANCE_PX, TOUCH_DRAG_HOLD_MS, TOUCH_DRAG_TOLERANCE_PX, cancelDrag, createDragAnnouncements, dragInstructions, dragListStrategy, dragMessages, keepGrabInside, listKeyboardCoordinates, resolveCommentMarginLayout, setCommentHighlights, useCommentMarginFits, useDragAcrossLists, useDragAnnouncer, useDragLists, useDragSensors, useDragSiblingTransition, useHoldThenMoveTouchSensor, useMouseOnlyPointerSensor, useParentOverlayKind, usePullToRefresh, useTruncated };
|
|
1
|
+
import "./lib/drag-escape.mjs";
|
|
2
|
+
import { OverlayLayerProvider, useParentOverlayKind } from "./lib/overlay-depth.mjs";
|
|
3
|
+
import { Dialog, DialogClose, DialogFooterNote, DialogSheetBack, DialogTrigger } from "./components/dialog/dialog.mjs";
|
|
4
|
+
import { DialogCommit, DialogSheetContent } from "./components/dialog/dialog-sheet.mjs";
|
|
5
|
+
import { DropdownMenuLinkItem } from "./components/dropdown-menu/dropdown-menu.mjs";
|
|
6
|
+
import { HoldThenMoveTouchSensor, MouseOnlyPointerSensor, TOUCH_DRAG_DISTANCE_PX, TOUCH_DRAG_HOLD_MS, TOUCH_DRAG_TOLERANCE_PX, useHoldThenMoveTouchSensor, useMouseOnlyPointerSensor } from "./lib/touch-drag.mjs";
|
|
7
|
+
import { PopoverActions, PopoverBody } from "./components/popover/popover.mjs";
|
|
8
|
+
import { MobileNavServerItem, MobileNavServerPopover, MobileNavServerPopoverContent, MobileNavServerPopoverDestination, MobileNavServerPopoverDestinations, MobileNavServerPopoverGroup, MobileNavServerPopoverList, MobileNavServerPopoverRow, MobileNavServerPopoverRowBody, MobileNavServerPopoverRowDescription, MobileNavServerPopoverRowLabel } from "./components/mobile-nav/mobile-nav-server.mjs";
|
|
9
|
+
import { PreviewCardActions, PreviewCardBody, PreviewCardEyebrow, PreviewCardFooter, PreviewCardMeta } from "./components/preview-card/preview-card.mjs";
|
|
10
|
+
import { AvatarGroupButton } from "./components/avatar/avatar.mjs";
|
|
11
|
+
import { MessageItemDelegatedAvatar, MessageItemDelegatedBy, MessageItemDelegatedByButton, MessageItemDelegatedByName } from "./components/message-item/message-item-delegated.mjs";
|
|
12
|
+
import { MessageReferenceIcon, MessageReferenceReadMark } from "./components/message-item/message-reference.mjs";
|
|
13
|
+
import { MessageQuotedPreviewAuthorDelegatedBy } from "./components/message-quoted-preview/message-quoted-preview.mjs";
|
|
14
|
+
import { MessageListJumpControls, MessageListJumpPill, MessageListJumpPillDismiss, MessageListJumpPillGroup } from "./components/message-list/message-list-jump.mjs";
|
|
15
|
+
import { DEFAULT_DRAG_MESSAGES, createDragAnnouncements, dragInstructions, dragMessages } from "./components/drag/drag-announcements.mjs";
|
|
16
|
+
import { LIFT_CLASS } from "./components/drag/drag.recipe.mjs";
|
|
17
|
+
import { DRAG_AUTO_SCROLL_ZONE_PX, DRAG_KEYBOARD_OFFSET_PX, DRAG_LIFT_Z_INDEX, DRAG_SETTLE, DRAG_SIBLING_TRANSITION, DROP_GLIDE, DragLift, ORIGIN_OPACITY, cancelDrag, keepGrabInside, useDragSiblingTransition } from "./components/drag/drag-lift.mjs";
|
|
18
|
+
import { DRAG_KEYBOARD_CODES, DragRemeasure, MOUSE_DRAG_DISTANCE_PX, listKeyboardCoordinates, useDragAcrossLists, useDragSensors } from "./components/drag/drag-sensors.mjs";
|
|
19
|
+
import { DragRefusalHint, DropPlaceholder } from "./components/drag/drop-placeholder.mjs";
|
|
20
|
+
import { PanelSplitAction, PanelSplitActionMain, PanelSplitActionMenu, PanelSplitActionToggle } from "./components/panel/panel-split-action.mjs";
|
|
21
|
+
import { ModeBar, ModeBarAction, ModeBarContent, ModeBarDescription, ModeBarTitle } from "./components/mode-bar/mode-bar.mjs";
|
|
22
|
+
import { ComboboxSelect } from "./components/combobox-select/combobox-select.mjs";
|
|
23
|
+
import { AnnouncementDialogAction, AnnouncementDialogBody, AnnouncementDialogContent, AnnouncementDialogFooter, AnnouncementDialogHeader, AnnouncementDialogLabel, AnnouncementDialogPicture, AnnouncementDialogSecondaryAction, AnnouncementDialogText, AnnouncementDialogTitle } from "./components/announcement-dialog/announcement-dialog.mjs";
|
|
24
|
+
import { CommentCard, CommentCardMeta, CommentCardQuote, CommentCardTrigger } from "./components/comment/comment-card.mjs";
|
|
25
|
+
import { CommentEntry, CommentEntryAuthor, CommentEntryBody, CommentEntryHeader, CommentEntryTime } from "./components/comment/comment-entry.mjs";
|
|
26
|
+
import { COMMENT_HIGHLIGHT, COMMENT_HIGHLIGHT_ACTIVE, setCommentHighlights } from "./components/comment/comment-highlight.mjs";
|
|
27
|
+
import { resolveCommentMarginLayout } from "./components/comment/comment-margin-layout.mjs";
|
|
28
|
+
import { CommentMargin, CommentMarginItem } from "./components/comment/comment-margin.mjs";
|
|
29
|
+
import { CommentMarker } from "./components/comment/comment-marker.mjs";
|
|
30
|
+
import { useCommentMarginFits } from "./components/comment/use-comment-margin-fits.mjs";
|
|
31
|
+
import { DragLiveRegion, DraggedSlot, dragListStrategy, useDragAnnouncer, useDragLists } from "./components/drag/drag-lists.mjs";
|
|
32
|
+
import { Ellipsis, EllipsisToggle } from "./components/ellipsis/ellipsis.mjs";
|
|
33
|
+
import { useTruncated } from "./lib/use-truncated.mjs";
|
|
34
|
+
import { InboxItemLeadingBadge } from "./components/inbox-item/inbox-item-leading-badge.mjs";
|
|
35
|
+
import { InboxItemSkeleton, InboxItemSkeletonList } from "./components/inbox-item/inbox-item-skeleton.mjs";
|
|
36
|
+
import { ListRowSkeleton } from "./components/list-row/list-row-skeleton.mjs";
|
|
37
|
+
import { Meter, MeterHeader, MeterIndicator, MeterLabel, MeterTrack, MeterValue } from "./components/meter/meter.mjs";
|
|
38
|
+
import { FilterChip, FilterChipLabel, FilterChipRemove } from "./components/filter-chip/filter-chip.mjs";
|
|
39
|
+
import { PULL_TO_REFRESH_DAMPING, PULL_TO_REFRESH_HOLD_DISTANCE, PULL_TO_REFRESH_MAX_DISTANCE, PULL_TO_REFRESH_SNAP_BACK_MS, PULL_TO_REFRESH_THRESHOLD } from "./components/pull-to-refresh/pull-to-refresh-gesture.mjs";
|
|
40
|
+
import { PULL_TO_REFRESH_POINTER_QUERY, usePullToRefresh } from "./components/pull-to-refresh/use-pull-to-refresh.mjs";
|
|
41
|
+
import { PullToRefreshIndicator } from "./components/pull-to-refresh/pull-to-refresh-indicator.mjs";
|
|
42
|
+
import { ChoiceList } from "./components/choice-list/choice-list.mjs";
|
|
43
|
+
export { Dialog as AnnouncementDialog, AnnouncementDialogAction, AnnouncementDialogBody, DialogClose as AnnouncementDialogClose, AnnouncementDialogContent, AnnouncementDialogFooter, AnnouncementDialogHeader, AnnouncementDialogLabel, AnnouncementDialogPicture, AnnouncementDialogSecondaryAction, AnnouncementDialogText, AnnouncementDialogTitle, DialogTrigger as AnnouncementDialogTrigger, AvatarGroupButton, COMMENT_HIGHLIGHT, COMMENT_HIGHLIGHT_ACTIVE, ChoiceList, ComboboxSelect, CommentCard, CommentCardMeta, CommentCardQuote, CommentCardTrigger, CommentEntry, CommentEntryAuthor, CommentEntryBody, CommentEntryHeader, CommentEntryTime, CommentMargin, CommentMarginItem, CommentMarker, DEFAULT_DRAG_MESSAGES, DRAG_AUTO_SCROLL_ZONE_PX, DRAG_KEYBOARD_CODES, DRAG_KEYBOARD_OFFSET_PX, DRAG_LIFT_Z_INDEX, DRAG_SETTLE, DRAG_SIBLING_TRANSITION, DROP_GLIDE, DialogCommit, DialogFooterNote, DialogSheetBack, DialogSheetContent, DragLift, DragLiveRegion, DragRefusalHint, DragRemeasure, DraggedSlot, DropPlaceholder, DropdownMenuLinkItem, Ellipsis, EllipsisToggle, FilterChip, FilterChipLabel, FilterChipRemove, HoldThenMoveTouchSensor, InboxItemLeadingBadge, InboxItemSkeleton, InboxItemSkeletonList, LIFT_CLASS, ListRowSkeleton, MOUSE_DRAG_DISTANCE_PX, MessageItemDelegatedAvatar, MessageItemDelegatedBy, MessageItemDelegatedByButton, MessageItemDelegatedByName, MessageListJumpControls, MessageListJumpPill, MessageListJumpPillDismiss, MessageListJumpPillGroup, MessageQuotedPreviewAuthorDelegatedBy, MessageReferenceIcon, MessageReferenceReadMark, Meter, MeterHeader, MeterIndicator, MeterLabel, MeterTrack, MeterValue, MobileNavServerItem, MobileNavServerPopover, MobileNavServerPopoverContent, MobileNavServerPopoverDestination, MobileNavServerPopoverDestinations, MobileNavServerPopoverGroup, MobileNavServerPopoverList, MobileNavServerPopoverRow, MobileNavServerPopoverRowBody, MobileNavServerPopoverRowDescription, MobileNavServerPopoverRowLabel, ModeBar, ModeBarAction, ModeBarContent, ModeBarDescription, ModeBarTitle, MouseOnlyPointerSensor, ORIGIN_OPACITY, OverlayLayerProvider, PULL_TO_REFRESH_DAMPING, PULL_TO_REFRESH_HOLD_DISTANCE, PULL_TO_REFRESH_MAX_DISTANCE, PULL_TO_REFRESH_POINTER_QUERY, PULL_TO_REFRESH_SNAP_BACK_MS, PULL_TO_REFRESH_THRESHOLD, PanelSplitAction, PanelSplitActionMain, PanelSplitActionMenu, PanelSplitActionToggle, PopoverActions, PopoverBody, PreviewCardActions, PreviewCardBody, PreviewCardEyebrow, PreviewCardFooter, PreviewCardMeta, PullToRefreshIndicator, TOUCH_DRAG_DISTANCE_PX, TOUCH_DRAG_HOLD_MS, TOUCH_DRAG_TOLERANCE_PX, cancelDrag, createDragAnnouncements, dragInstructions, dragListStrategy, dragMessages, keepGrabInside, listKeyboardCoordinates, resolveCommentMarginLayout, setCommentHighlights, useCommentMarginFits, useDragAcrossLists, useDragAnnouncer, useDragLists, useDragSensors, useDragSiblingTransition, useHoldThenMoveTouchSensor, useMouseOnlyPointerSensor, useParentOverlayKind, usePullToRefresh, useTruncated };
|