@botiverse/raft-ui 0.50.0 → 0.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -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 -17053
- 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 -1222
- package/dist/wip.mjs +43 -3079
- package/package.json +8 -1
- package/dist/inbox-item-B4nNvpp1.d.mts +0 -1784
- package/dist/inbox-item-DHJOrxB8.mjs +0 -7454
- package/dist/style.css +0 -1395
|
@@ -0,0 +1,1401 @@
|
|
|
1
|
+
import { cn } from "../../cn.mjs";
|
|
2
|
+
import { useMergedRef } from "../../lib/use-merged-ref.mjs";
|
|
3
|
+
import { EMPTY_ARRAY, ITEM_KEY_ATTR, NON_VIRTUAL_VISIBLE_ITEMS_STYLE, RENDER_THRASHING_WINDOW_MS, VIRTUAL_VISIBLE_ITEMS_STYLE } from "./utils/constants.mjs";
|
|
4
|
+
import { FenwickTree } from "./utils/fenwick-tree.mjs";
|
|
5
|
+
import { findItemsAnchor, getAnchoredRange } from "./utils/items-anchor.mjs";
|
|
6
|
+
import { useEventCallback } from "./hooks/use-event-callback.mjs";
|
|
7
|
+
import { useLatestRef } from "./hooks/use-latest-ref.mjs";
|
|
8
|
+
import { useLayoutEffectWithPrevious } from "./hooks/use-layout-effect-with-previous.mjs";
|
|
9
|
+
import { useMemoWithPrevious } from "./hooks/use-memo-with-previous.mjs";
|
|
10
|
+
import { useNonPassiveWheel } from "./hooks/use-non-passive-wheel.mjs";
|
|
11
|
+
import { ColumnsContext, ItemColumns } from "./ItemColumns.mjs";
|
|
12
|
+
import { createLatestInNextFrame, getWheelDeltaScale, isCoarsePointer, isIOS } from "./utils/platform.mjs";
|
|
13
|
+
import { getAlignedScrollTop } from "./utils/align.mjs";
|
|
14
|
+
import { measureRowSpans, measureRowStart } from "./utils/measure.mjs";
|
|
15
|
+
import { createTypicalSizeCache, getTypicalSize, recordMeasuredSize } from "./utils/size-cache.mjs";
|
|
16
|
+
import { getHorizontalRange, getNeededEnd, getOverscannedRange, getVerticalRange, haveSameKeys, isHiddenByDisplayNone, px } from "./utils/range.mjs";
|
|
17
|
+
import { getResizeObserverDelegate } from "./resize-observer/resize-observer-delegate.mjs";
|
|
18
|
+
import { ResizeObserverWrapper } from "./resize-observer/ResizeObserverWrapper.mjs";
|
|
19
|
+
import { useIosMomentumCompensation } from "./hooks/use-ios-momentum-compensation.mjs";
|
|
20
|
+
import { useKeyboardScroll } from "./hooks/use-keyboard-scroll.mjs";
|
|
21
|
+
import { useNonVirtualContentResize } from "./hooks/use-non-virtual-content-resize.mjs";
|
|
22
|
+
import { useScrollAnchor } from "./hooks/use-scroll-anchor.mjs";
|
|
23
|
+
import { VirtualListContext } from "./VirtualListContext.mjs";
|
|
24
|
+
import { createElement, useCallback, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
25
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
26
|
+
import { flushSync } from "react-dom";
|
|
27
|
+
//#region src/components/virtual-list/VirtualList.tsx
|
|
28
|
+
/**
|
|
29
|
+
* The most range updates one aligned jump goes through before it stops (each one mounts and measures the
|
|
30
|
+
* rows the previous position brought into view; it is usually stable after 2-3).
|
|
31
|
+
*/
|
|
32
|
+
const MAX_ALIGN_STEPS = 10;
|
|
33
|
+
function VirtualList(props) {
|
|
34
|
+
const { virtual = true, sessionId, showScrollbar = false, columns = EMPTY_ARRAY, renderCol, renderItemWithCols, items = EMPTY_ARRAY, itemMinSize, itemEstimatedSize, itemResizable = false, itemsStyle, visibleItemsTag = "div", visibleItemsProps, ignoreItemResize = false, onScroll, onWheel, onResize, defaultScrollKey, defaultScrollIndex, getKey, paddingTop = 0, paddingBottom = 0, children: renderItemChild, ItemComponent, empty, emptyAlign = "start", className, style, itemsClassName, footer, footerAfterBottomPadding, header, domRef, ref: _ref, onContentResize, onTransformChange, componentRef, printable, catchWheel = false, onRenderAnomaly, keyboardPageBottomOcclusion = 0, focusable = true, keepAnchorOnItemsChange = false, canAnchorItem, onKeyDown: onNativeKeyDown, ...nativeProps } = props;
|
|
35
|
+
const listElRef = useRef(null);
|
|
36
|
+
const [mergedListRefSetter] = useMergedRef(domRef, listElRef);
|
|
37
|
+
const itemsElRef = useRef(null);
|
|
38
|
+
const getVisibleItemsEl = useCallback(() => itemsElRef.current?.children[0] || null, []);
|
|
39
|
+
const [[renderReason, renderCallback, renderVersion], setRenderState] = useState([
|
|
40
|
+
null,
|
|
41
|
+
null,
|
|
42
|
+
0
|
|
43
|
+
]);
|
|
44
|
+
const viewportRef = useRef({
|
|
45
|
+
listHeight: void 0,
|
|
46
|
+
scrollTop: 0,
|
|
47
|
+
scrollLeft: 0,
|
|
48
|
+
listWidth: 0
|
|
49
|
+
});
|
|
50
|
+
const rangeRef = useRef({
|
|
51
|
+
prependIndexes: [],
|
|
52
|
+
verticalStartIndex: 0,
|
|
53
|
+
verticalEndIndex: -1,
|
|
54
|
+
horizontalStartIndex: 0,
|
|
55
|
+
horizontalEndIndex: -1
|
|
56
|
+
});
|
|
57
|
+
/**
|
|
58
|
+
* The rows kept mounted outside the range (absolutely positioned at their offsets): the rows holding
|
|
59
|
+
* the text selection and the row holding focus (`focusedKeyRef`).
|
|
60
|
+
*/
|
|
61
|
+
const keptKeysRef = useRef([]);
|
|
62
|
+
/** The key of the row that holds focus (`document.activeElement` is in it), tracked by focusin / focusout. */
|
|
63
|
+
const focusedKeyRef = useRef(null);
|
|
64
|
+
/** The measured rows' sizes, by key (see `utils/size-cache.ts`). */
|
|
65
|
+
const keyToHeightOffsetRef = useRef(/* @__PURE__ */ new Map());
|
|
66
|
+
/** Bumped whenever `keyToHeightOffsetRef` changes (see `getTypicalSize`). */
|
|
67
|
+
const measuredVersionRef = useRef(0);
|
|
68
|
+
const typicalSizeRef = useRef(createTypicalSizeCache());
|
|
69
|
+
const leadingMarginRef = useRef(0);
|
|
70
|
+
/**
|
|
71
|
+
* Where the first mounted row in flow starts in the visible-items element (its top margin there,
|
|
72
|
+
* since nothing above it is mounted), as last written by `applyLayoutStyles`.
|
|
73
|
+
*/
|
|
74
|
+
const firstRowStartRef = useRef(0);
|
|
75
|
+
const pendingScrollTaskRef = useRef(null);
|
|
76
|
+
const pendingScrollTaskTimeoutRef = useRef(null);
|
|
77
|
+
const pendingScrollTaskRafRef = useRef(null);
|
|
78
|
+
const wheelScrollingRef = useRef(false);
|
|
79
|
+
/** Whether the view last moved up (see `updateRange`). */
|
|
80
|
+
const scrollingUpRef = useRef(false);
|
|
81
|
+
/** Clears `wheelScrollingRef` in the next frame; this list's own, so other lists cannot replace it. */
|
|
82
|
+
const [runLatestInNextFrame] = useState(createLatestInNextFrame);
|
|
83
|
+
/** The wheel is only taken over (catchWheel) where the pointer is not coarse. */
|
|
84
|
+
const mayUseWheelRef = useRef(typeof document === "undefined" || !isCoarsePointer());
|
|
85
|
+
const reportRenderAnomaly = useEventCallback((anomaly) => {
|
|
86
|
+
onRenderAnomaly?.(anomaly);
|
|
87
|
+
});
|
|
88
|
+
const forcedRenderTimesRef = useRef([]);
|
|
89
|
+
/**
|
|
90
|
+
* The offset applied by the iOS compensation, created here (not in its hook) because
|
|
91
|
+
* `keepAnchorOnItemsChange` reads it while rendering, before the hook is called.
|
|
92
|
+
*/
|
|
93
|
+
const compensationYRef = useRef(0);
|
|
94
|
+
/** The items, Fenwick tree and session of the last commit (the tree is updated in place by measurements). */
|
|
95
|
+
const committedLayoutRef = useRef(null);
|
|
96
|
+
const itemsAnchor = useMemo(() => {
|
|
97
|
+
const committed = committedLayoutRef.current;
|
|
98
|
+
const listEl = listElRef.current;
|
|
99
|
+
if (!keepAnchorOnItemsChange || !virtual || committed === null || listEl === null || committed.items === items || committed.sessionId !== sessionId) return null;
|
|
100
|
+
return findItemsAnchor({
|
|
101
|
+
prevItems: committed.items,
|
|
102
|
+
prevTree: committed.tree,
|
|
103
|
+
prevPrependIndexes: rangeRef.current.prependIndexes,
|
|
104
|
+
nextItems: items,
|
|
105
|
+
getKey,
|
|
106
|
+
canAnchor: canAnchorItem,
|
|
107
|
+
top: listEl.scrollTop + compensationYRef.current - paddingTop - leadingMarginRef.current
|
|
108
|
+
});
|
|
109
|
+
}, [items]);
|
|
110
|
+
/** The anchor whose range has been committed to `rangeRef` (so it is applied once). */
|
|
111
|
+
const appliedItemsAnchorRef = useRef(null);
|
|
112
|
+
const pendingItemsAnchor = itemsAnchor !== null && itemsAnchor !== appliedItemsAnchorRef.current ? itemsAnchor : null;
|
|
113
|
+
let renderedRange = rangeRef.current;
|
|
114
|
+
if (pendingItemsAnchor !== null) {
|
|
115
|
+
const [verticalStartIndex, verticalEndIndex] = getAnchoredRange(renderedRange.verticalStartIndex, renderedRange.verticalEndIndex, pendingItemsAnchor.index, items.length);
|
|
116
|
+
renderedRange = {
|
|
117
|
+
...renderedRange,
|
|
118
|
+
prependIndexes: pendingItemsAnchor.prependIndexes,
|
|
119
|
+
verticalStartIndex,
|
|
120
|
+
verticalEndIndex
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
const { prependIndexes, verticalStartIndex: startIndex, verticalEndIndex: endIndex, horizontalStartIndex: colStartIndex, horizontalEndIndex: colEndIndex } = renderedRange;
|
|
124
|
+
const keyToIndexRef = useRef(void 0);
|
|
125
|
+
const prevSessionIdRef = useRef(sessionId);
|
|
126
|
+
const sessionChangedRef = useRef(false);
|
|
127
|
+
const lastTransformSignatureRef = useRef(null);
|
|
128
|
+
const clearPendingScrollTask = useCallback(() => {
|
|
129
|
+
if (pendingScrollTaskTimeoutRef.current !== null) {
|
|
130
|
+
window.clearTimeout(pendingScrollTaskTimeoutRef.current);
|
|
131
|
+
pendingScrollTaskTimeoutRef.current = null;
|
|
132
|
+
}
|
|
133
|
+
if (pendingScrollTaskRafRef.current != null) {
|
|
134
|
+
window.cancelAnimationFrame(pendingScrollTaskRafRef.current);
|
|
135
|
+
pendingScrollTaskRafRef.current = null;
|
|
136
|
+
}
|
|
137
|
+
pendingScrollTaskRef.current = null;
|
|
138
|
+
}, []);
|
|
139
|
+
useMemo(() => {
|
|
140
|
+
if (prevSessionIdRef.current === sessionId) return;
|
|
141
|
+
prevSessionIdRef.current = sessionId;
|
|
142
|
+
sessionChangedRef.current = true;
|
|
143
|
+
clearPendingScrollTask();
|
|
144
|
+
wheelScrollingRef.current = false;
|
|
145
|
+
keptKeysRef.current = [];
|
|
146
|
+
focusedKeyRef.current = null;
|
|
147
|
+
keyToHeightOffsetRef.current.clear();
|
|
148
|
+
measuredVersionRef.current += 1;
|
|
149
|
+
leadingMarginRef.current = 0;
|
|
150
|
+
keyToIndexRef.current = /* @__PURE__ */ new Map();
|
|
151
|
+
rangeRef.current = {
|
|
152
|
+
prependIndexes: [],
|
|
153
|
+
verticalStartIndex: 0,
|
|
154
|
+
verticalEndIndex: -1,
|
|
155
|
+
horizontalStartIndex: 0,
|
|
156
|
+
horizontalEndIndex: -1
|
|
157
|
+
};
|
|
158
|
+
const viewport = viewportRef.current;
|
|
159
|
+
viewport.scrollTop = 0;
|
|
160
|
+
viewport.scrollLeft = 0;
|
|
161
|
+
const listEl = listElRef.current;
|
|
162
|
+
if (listEl != null) {
|
|
163
|
+
listEl.scrollTop = 0;
|
|
164
|
+
listEl.scrollLeft = 0;
|
|
165
|
+
}
|
|
166
|
+
}, [sessionId, clearPendingScrollTask]);
|
|
167
|
+
useMemo(() => {
|
|
168
|
+
lastTransformSignatureRef.current = null;
|
|
169
|
+
}, [sessionId]);
|
|
170
|
+
useMemo(() => {
|
|
171
|
+
const keyToIndex = /* @__PURE__ */ new Map();
|
|
172
|
+
items.forEach((item, index) => {
|
|
173
|
+
keyToIndex.set(getKey(item), index);
|
|
174
|
+
});
|
|
175
|
+
keyToIndexRef.current = keyToIndex;
|
|
176
|
+
const keptKeys = [];
|
|
177
|
+
keptKeysRef.current.forEach((key) => {
|
|
178
|
+
if (keyToIndexRef.current.has(key)) keptKeys.push(key);
|
|
179
|
+
});
|
|
180
|
+
keptKeysRef.current = keptKeys;
|
|
181
|
+
}, [
|
|
182
|
+
sessionId,
|
|
183
|
+
items,
|
|
184
|
+
getKey
|
|
185
|
+
]);
|
|
186
|
+
const totalColumnsWidth = useMemo(() => {
|
|
187
|
+
if (renderCol == null && renderItemWithCols == null || columns.length === 0) return;
|
|
188
|
+
let width = 0;
|
|
189
|
+
columns.forEach((column) => {
|
|
190
|
+
width += column.width;
|
|
191
|
+
});
|
|
192
|
+
return width;
|
|
193
|
+
}, [
|
|
194
|
+
sessionId,
|
|
195
|
+
renderCol,
|
|
196
|
+
renderItemWithCols,
|
|
197
|
+
columns
|
|
198
|
+
]);
|
|
199
|
+
/**
|
|
200
|
+
* Rebuilt whenever the items or the session change: `itemEstimatedSize` for every item plus the
|
|
201
|
+
* measured offsets cached by key, so measurements survive re-renders and reordering.
|
|
202
|
+
*/
|
|
203
|
+
const fenwickTree = useMemo(() => {
|
|
204
|
+
if (!virtual) return new FenwickTree(0, 0);
|
|
205
|
+
const tree = new FenwickTree(items.length, itemEstimatedSize);
|
|
206
|
+
const heightOffsets = keyToHeightOffsetRef.current;
|
|
207
|
+
items.forEach((item, index) => {
|
|
208
|
+
const key = getKey(item);
|
|
209
|
+
const offset = heightOffsets.get(key);
|
|
210
|
+
if (offset !== void 0) tree.add(index, offset);
|
|
211
|
+
});
|
|
212
|
+
return tree;
|
|
213
|
+
}, [
|
|
214
|
+
sessionId,
|
|
215
|
+
items,
|
|
216
|
+
itemEstimatedSize,
|
|
217
|
+
getKey,
|
|
218
|
+
virtual
|
|
219
|
+
]);
|
|
220
|
+
const visibleItems = useMemo(() => {
|
|
221
|
+
if (!virtual) return items;
|
|
222
|
+
if (endIndex < startIndex) return [];
|
|
223
|
+
const visible = [];
|
|
224
|
+
const lastIndex = Math.min(endIndex, items.length - 1);
|
|
225
|
+
for (let i = startIndex; i <= lastIndex; ++i) visible.push(items[i]);
|
|
226
|
+
return visible;
|
|
227
|
+
}, [
|
|
228
|
+
sessionId,
|
|
229
|
+
startIndex,
|
|
230
|
+
endIndex,
|
|
231
|
+
items,
|
|
232
|
+
virtual
|
|
233
|
+
]);
|
|
234
|
+
const xFenwickTree = useMemo(() => {
|
|
235
|
+
if (columns.length === 0) return null;
|
|
236
|
+
const tree = new FenwickTree(columns.length, 0);
|
|
237
|
+
columns.forEach((column, index) => {
|
|
238
|
+
tree.add(index, column.width);
|
|
239
|
+
});
|
|
240
|
+
return tree;
|
|
241
|
+
}, [sessionId, columns]);
|
|
242
|
+
const getColumnLeft = useEventCallback((index) => xFenwickTree !== null ? xFenwickTree.sum(index) : 0);
|
|
243
|
+
const columnsContextValue = useMemo(() => ({
|
|
244
|
+
startIndex: colStartIndex,
|
|
245
|
+
endIndex: colEndIndex,
|
|
246
|
+
columns,
|
|
247
|
+
renderCol,
|
|
248
|
+
renderItemWithCols,
|
|
249
|
+
getLeft: getColumnLeft
|
|
250
|
+
}), [
|
|
251
|
+
colStartIndex,
|
|
252
|
+
colEndIndex,
|
|
253
|
+
columns,
|
|
254
|
+
renderCol,
|
|
255
|
+
renderItemWithCols,
|
|
256
|
+
getColumnLeft
|
|
257
|
+
]);
|
|
258
|
+
/** Where the rows start in the spacer's content: `paddingTop` plus the first row's top margin. */
|
|
259
|
+
const getRowsTop = useEventCallback(() => paddingTop + leadingMarginRef.current);
|
|
260
|
+
/**
|
|
261
|
+
* The mounted rows in flow, in order: the measured elements of the rendered range. Kept rows (outside
|
|
262
|
+
* the range, absolutely positioned) are left out: they take no space in the flow.
|
|
263
|
+
*/
|
|
264
|
+
const getFlowRows = useEventCallback(() => {
|
|
265
|
+
const rows = [];
|
|
266
|
+
const visibleItemsEl = getVisibleItemsEl();
|
|
267
|
+
if (visibleItemsEl === null) return rows;
|
|
268
|
+
for (const el of Array.from(visibleItemsEl.children)) {
|
|
269
|
+
const key = el.getAttribute(ITEM_KEY_ATTR);
|
|
270
|
+
const index = key === null ? void 0 : keyToIndexRef.current.get(key);
|
|
271
|
+
if (key !== null && index !== void 0 && index >= startIndex && index <= endIndex && el instanceof HTMLElement) rows.push({
|
|
272
|
+
el,
|
|
273
|
+
key,
|
|
274
|
+
index
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
return rows;
|
|
278
|
+
});
|
|
279
|
+
/**
|
|
280
|
+
* Writes the layout without a render: the spacer height (the total size plus the first row's top
|
|
281
|
+
* margin; a min-height with itemResizable, so the real content may exceed it) and the visible items'
|
|
282
|
+
* translateY. It writes no CSS variable on the visible items: custom properties inherit, so one
|
|
283
|
+
* written there on every scroll restyles every row. Content that must follow the rows' offset uses
|
|
284
|
+
* `onTransformChange`.
|
|
285
|
+
*
|
|
286
|
+
* Unlike the reference implementation (DeepSeek web): the translateY puts the border box of the first
|
|
287
|
+
* mounted row at its offset (`firstIndex`, or the first row in the DOM), not the element's top. The visible-items element is a
|
|
288
|
+
* block formatting context, so the first mounted row's top margin is inside it, above the row, and
|
|
289
|
+
* does not collapse with the row before it (not mounted): the element starts that margin higher. The
|
|
290
|
+
* collapsed gap above that row is already in the size of the row before it (see `leadingMarginRef`),
|
|
291
|
+
* so the rows sit exactly where the tree has them whichever row is first, and moving the window's top
|
|
292
|
+
* edge moves no row. Read before anything is written, so it forces no extra layout.
|
|
293
|
+
*/
|
|
294
|
+
const applyLayoutStyles = useEventCallback((firstIndex) => {
|
|
295
|
+
if (!virtual) return;
|
|
296
|
+
let index = Math.min(firstIndex, items.length);
|
|
297
|
+
let firstRowStart = 0;
|
|
298
|
+
const itemsEl = itemsElRef.current;
|
|
299
|
+
const visibleItemsEl = itemsEl?.children[0];
|
|
300
|
+
const firstRow = itemResizable ? getFlowRows()[0] : void 0;
|
|
301
|
+
if (firstRow !== void 0 && visibleItemsEl instanceof HTMLElement) {
|
|
302
|
+
index = firstRow.index;
|
|
303
|
+
firstRowStart = measureRowStart(visibleItemsEl, firstRow.el);
|
|
304
|
+
}
|
|
305
|
+
firstRowStartRef.current = firstRowStart;
|
|
306
|
+
const leadingMargin = leadingMarginRef.current;
|
|
307
|
+
if (itemsEl) {
|
|
308
|
+
const height = px(fenwickTree.sum() + leadingMargin) || "";
|
|
309
|
+
if (itemResizable) {
|
|
310
|
+
itemsEl.style.height = "";
|
|
311
|
+
itemsEl.style.minHeight = height;
|
|
312
|
+
} else {
|
|
313
|
+
const changed = itemsEl.style.height !== height || itemsEl.style.minHeight !== "";
|
|
314
|
+
itemsEl.style.height = height;
|
|
315
|
+
itemsEl.style.minHeight = "";
|
|
316
|
+
if (changed) onContentResize?.();
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (visibleItemsEl instanceof HTMLElement) {
|
|
320
|
+
const offset = px(leadingMargin + fenwickTree.sum(index) - firstRowStart);
|
|
321
|
+
visibleItemsEl.style.transform = `translateY(${offset})`;
|
|
322
|
+
const signature = `${index}|${offset}`;
|
|
323
|
+
if (lastTransformSignatureRef.current !== signature) {
|
|
324
|
+
lastTransformSignatureRef.current = signature;
|
|
325
|
+
onTransformChange?.();
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
useLayoutEffect(() => {
|
|
330
|
+
if (!virtual) {
|
|
331
|
+
const itemsEl = itemsElRef.current;
|
|
332
|
+
if (itemsEl) {
|
|
333
|
+
itemsEl.style.height = "";
|
|
334
|
+
itemsEl.style.minHeight = "";
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}, [virtual]);
|
|
338
|
+
/** The size rows typically are: the mean of the measured rows (`getTypicalSize`). */
|
|
339
|
+
const getTypicalRowSize = useEventCallback(() => getTypicalSize(typicalSizeRef.current, keyToHeightOffsetRef.current, measuredVersionRef.current, itemEstimatedSize, itemMinSize));
|
|
340
|
+
/**
|
|
341
|
+
* The viewport sync. The range starts at the item at `scrollTop + compensation - paddingTop` and
|
|
342
|
+
* holds as many items as fit at `itemMinSize`, plus one, and is overscanned in batches
|
|
343
|
+
* (`getOverscannedRange`). Items holding the text selection, and the item
|
|
344
|
+
* holding focus, stay
|
|
345
|
+
* mounted ("kept"). When the new range is inside the old one, only the layout styles are written;
|
|
346
|
+
* otherwise the items entering above the old start are recorded (`prependIndexes`) and the range is
|
|
347
|
+
* rendered with flushSync, now or in a microtask (`deferRenderToMicrotask`). At 120 forced renders
|
|
348
|
+
* within a second a "renderThrashing" anomaly is reported. `afterRender` runs after the layout styles
|
|
349
|
+
* of the new range are applied.
|
|
350
|
+
*/
|
|
351
|
+
const updateRange = useEventCallback((reason, deferRenderToMicrotask, afterRender) => {
|
|
352
|
+
if (!virtual) return;
|
|
353
|
+
const listEl = listElRef.current;
|
|
354
|
+
if (listEl == null) return;
|
|
355
|
+
const viewport = viewportRef.current;
|
|
356
|
+
if (viewport.listHeight === void 0) {
|
|
357
|
+
applyLayoutStyles(startIndex);
|
|
358
|
+
afterRender?.();
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
const scrollTop = listEl.scrollTop;
|
|
362
|
+
const effectiveScrollTop = scrollTop + compensationYRef.current;
|
|
363
|
+
const scrollLeft = listEl.scrollLeft;
|
|
364
|
+
const range = rangeRef.current;
|
|
365
|
+
const prevRange = {
|
|
366
|
+
startIndex: range.verticalStartIndex,
|
|
367
|
+
endIndex: range.verticalEndIndex
|
|
368
|
+
};
|
|
369
|
+
const viewRange = getVerticalRange({
|
|
370
|
+
fenwickTree,
|
|
371
|
+
scrollTop: effectiveScrollTop,
|
|
372
|
+
listHeight: viewport.listHeight,
|
|
373
|
+
itemMinSize,
|
|
374
|
+
itemCount: items.length,
|
|
375
|
+
paddingTop: getRowsTop()
|
|
376
|
+
});
|
|
377
|
+
const overscanned = viewport.listHeight > 0;
|
|
378
|
+
if (scrollTop !== viewport.scrollTop) scrollingUpRef.current = scrollTop < viewport.scrollTop;
|
|
379
|
+
const nextRange = overscanned ? getOverscannedRange({
|
|
380
|
+
fenwickTree,
|
|
381
|
+
view: viewRange,
|
|
382
|
+
prev: prevRange,
|
|
383
|
+
neededEnd: getNeededEnd({
|
|
384
|
+
fenwickTree,
|
|
385
|
+
startIndex: viewRange.startIndex,
|
|
386
|
+
viewTop: effectiveScrollTop - getRowsTop(),
|
|
387
|
+
listHeight: viewport.listHeight,
|
|
388
|
+
itemMinSize,
|
|
389
|
+
itemCount: items.length,
|
|
390
|
+
isMeasured: (index) => {
|
|
391
|
+
const item = items[index];
|
|
392
|
+
return item !== void 0 && keyToHeightOffsetRef.current.has(getKey(item));
|
|
393
|
+
}
|
|
394
|
+
}),
|
|
395
|
+
viewTop: effectiveScrollTop - getRowsTop(),
|
|
396
|
+
moved: Math.abs(scrollTop - viewport.scrollTop),
|
|
397
|
+
movingUp: scrollingUpRef.current,
|
|
398
|
+
settling: pendingScrollTaskRef.current !== null,
|
|
399
|
+
plannedSize: (index) => {
|
|
400
|
+
const item = items[index];
|
|
401
|
+
return item !== void 0 && keyToHeightOffsetRef.current.has(getKey(item)) ? fenwickTree.get(index) : getTypicalRowSize();
|
|
402
|
+
},
|
|
403
|
+
listHeight: viewport.listHeight,
|
|
404
|
+
itemCount: items.length
|
|
405
|
+
}) : viewRange;
|
|
406
|
+
const rangeChanged = prevRange.startIndex !== nextRange.startIndex || prevRange.endIndex !== nextRange.endIndex;
|
|
407
|
+
const nextInsidePrev = !overscanned && nextRange.startIndex >= prevRange.startIndex && prevRange.endIndex >= nextRange.endIndex;
|
|
408
|
+
const prevColRange = {
|
|
409
|
+
startIndex: range.horizontalStartIndex,
|
|
410
|
+
endIndex: range.horizontalEndIndex
|
|
411
|
+
};
|
|
412
|
+
const nextColRange = getHorizontalRange({
|
|
413
|
+
xFenwickTree,
|
|
414
|
+
scrollLeft,
|
|
415
|
+
listWidth: viewport.listWidth,
|
|
416
|
+
columnCount: columns.length
|
|
417
|
+
});
|
|
418
|
+
const colRangeChanged = prevColRange.startIndex !== nextColRange.startIndex || prevColRange.endIndex !== nextColRange.endIndex;
|
|
419
|
+
viewport.scrollTop = scrollTop;
|
|
420
|
+
viewport.scrollLeft = scrollLeft;
|
|
421
|
+
let keptKeysChanged = false;
|
|
422
|
+
if (typeof window !== "undefined" && reason !== "sessionChanged") {
|
|
423
|
+
const { startIndex: nextStart, endIndex: nextEnd } = nextRange;
|
|
424
|
+
const keptKeys = [];
|
|
425
|
+
const keep = (key) => {
|
|
426
|
+
const index = keyToIndexRef.current.get(key);
|
|
427
|
+
if (index !== void 0 && (!(index >= nextStart) || !(index <= nextEnd)) && !keptKeys.includes(key)) keptKeys.push(key);
|
|
428
|
+
};
|
|
429
|
+
const focusedKey = focusedKeyRef.current;
|
|
430
|
+
if (focusedKey !== null) {
|
|
431
|
+
const active = document.activeElement;
|
|
432
|
+
if (active !== null && listEl.contains(active)) keep(focusedKey);
|
|
433
|
+
else focusedKeyRef.current = null;
|
|
434
|
+
}
|
|
435
|
+
const selection = window.getSelection();
|
|
436
|
+
if (selection != null && !selection.isCollapsed && selection.rangeCount > 0) listEl.querySelectorAll(`[${ITEM_KEY_ATTR}]`).forEach((itemEl) => {
|
|
437
|
+
if (!selection.containsNode(itemEl, true) && itemEl.getElementsByTagName("input").length !== 0) return;
|
|
438
|
+
const key = itemEl.getAttribute(ITEM_KEY_ATTR);
|
|
439
|
+
if (key !== null) keep(key);
|
|
440
|
+
});
|
|
441
|
+
if (!haveSameKeys(keptKeys, keptKeysRef.current)) {
|
|
442
|
+
keptKeysRef.current = keptKeys;
|
|
443
|
+
keptKeysChanged = true;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (reason !== "sessionChanged" && !keptKeysChanged && (!rangeChanged || nextInsidePrev) && !colRangeChanged) {
|
|
447
|
+
if (nextInsidePrev) applyLayoutStyles(prevRange.startIndex);
|
|
448
|
+
else applyLayoutStyles(nextRange.startIndex);
|
|
449
|
+
afterRender?.();
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
if (reason !== "sessionChanged") {
|
|
453
|
+
const entering = [];
|
|
454
|
+
if (pendingScrollTaskRef.current?.type === void 0 && nextRange.endIndex >= prevRange.startIndex) for (let i = nextRange.startIndex; i < prevRange.startIndex; ++i) entering.push(i);
|
|
455
|
+
range.prependIndexes = entering;
|
|
456
|
+
} else range.prependIndexes = [];
|
|
457
|
+
range.verticalStartIndex = nextRange.startIndex;
|
|
458
|
+
range.verticalEndIndex = nextRange.endIndex;
|
|
459
|
+
range.horizontalStartIndex = nextColRange.startIndex;
|
|
460
|
+
range.horizontalEndIndex = nextColRange.endIndex;
|
|
461
|
+
const now = Date.now();
|
|
462
|
+
const renderTimes = forcedRenderTimesRef.current;
|
|
463
|
+
renderTimes.push(now);
|
|
464
|
+
const windowStart = now - RENDER_THRASHING_WINDOW_MS;
|
|
465
|
+
while (renderTimes.length > 0 && renderTimes[0] < windowStart) renderTimes.shift();
|
|
466
|
+
if (renderTimes.length >= 120) {
|
|
467
|
+
reportRenderAnomaly({
|
|
468
|
+
type: "renderThrashing",
|
|
469
|
+
message: `${renderTimes.length} forced renders within ${RENDER_THRASHING_WINDOW_MS}ms`,
|
|
470
|
+
payload: {
|
|
471
|
+
renderCount: renderTimes.length,
|
|
472
|
+
windowMs: RENDER_THRASHING_WINDOW_MS,
|
|
473
|
+
triggerReason: reason,
|
|
474
|
+
itemCount: items.length
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
renderTimes.length = 0;
|
|
478
|
+
}
|
|
479
|
+
if (deferRenderToMicrotask) Promise.resolve().then(() => {
|
|
480
|
+
flushSync(() => {
|
|
481
|
+
setRenderState((state) => [
|
|
482
|
+
reason,
|
|
483
|
+
afterRender,
|
|
484
|
+
state[2] + 1
|
|
485
|
+
]);
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
else flushSync(() => {
|
|
489
|
+
setRenderState((state) => [
|
|
490
|
+
reason,
|
|
491
|
+
afterRender,
|
|
492
|
+
state[2] + 1
|
|
493
|
+
]);
|
|
494
|
+
});
|
|
495
|
+
});
|
|
496
|
+
/**
|
|
497
|
+
* A pending scroll task (to the top, the bottom, an index, or a custom task) stays armed for one
|
|
498
|
+
* animation frame plus one task, so that item sizes measured right after the jump replay it instead
|
|
499
|
+
* of being compensated.
|
|
500
|
+
*/
|
|
501
|
+
const schedulePendingScrollTaskExpiry = useCallback(() => {
|
|
502
|
+
pendingScrollTaskRafRef.current = requestAnimationFrame(() => {
|
|
503
|
+
pendingScrollTaskRafRef.current = null;
|
|
504
|
+
pendingScrollTaskTimeoutRef.current = window.setTimeout(() => {
|
|
505
|
+
pendingScrollTaskRef.current = null;
|
|
506
|
+
pendingScrollTaskTimeoutRef.current = null;
|
|
507
|
+
});
|
|
508
|
+
});
|
|
509
|
+
}, []);
|
|
510
|
+
const scrollToPosition = useEventCallback((left, top, behavior) => {
|
|
511
|
+
listElRef.current?.scrollTo({
|
|
512
|
+
left,
|
|
513
|
+
top,
|
|
514
|
+
behavior
|
|
515
|
+
});
|
|
516
|
+
});
|
|
517
|
+
/**
|
|
518
|
+
* Scrolls to the offset of `index` in the Fenwick tree (an estimate for items not measured yet);
|
|
519
|
+
* an "instant" jump is armed as the pending task, to be replayed after measurements. `ensureVisibleOnly`
|
|
520
|
+
* (the `debounce` option) scrolls only as far as needed to show the item. With virtualization off the
|
|
521
|
+
* offset is read from the DOM (`getBoundingClientRect` of the index-th child).
|
|
522
|
+
*/
|
|
523
|
+
const scrollToIndex = useEventCallback((index, behavior, ensureVisibleOnly, isReplay, scrollMarginTop = 0, scrollMarginBottom = 0) => {
|
|
524
|
+
if (!virtual) {
|
|
525
|
+
const listEl = listElRef.current;
|
|
526
|
+
const itemsEl = itemsElRef.current;
|
|
527
|
+
if (!itemsEl || !listEl) return;
|
|
528
|
+
const visibleItemsEl = itemsEl.firstElementChild;
|
|
529
|
+
if (!visibleItemsEl) return;
|
|
530
|
+
const itemEl = visibleItemsEl.children.item(index);
|
|
531
|
+
if (!(itemEl instanceof HTMLElement)) return;
|
|
532
|
+
const listRect = listEl.getBoundingClientRect();
|
|
533
|
+
const itemRect = itemEl.getBoundingClientRect();
|
|
534
|
+
const itemTop = itemRect.top - listRect.top + listEl.scrollTop;
|
|
535
|
+
if (ensureVisibleOnly) {
|
|
536
|
+
const { scrollTop, offsetHeight } = listEl;
|
|
537
|
+
const itemHeight = itemRect.height;
|
|
538
|
+
if (itemTop > scrollTop) {
|
|
539
|
+
if (!(itemTop + itemHeight <= scrollTop + offsetHeight)) listEl.scrollTo({
|
|
540
|
+
left: 0,
|
|
541
|
+
top: itemTop + itemHeight - offsetHeight + scrollMarginBottom,
|
|
542
|
+
behavior
|
|
543
|
+
});
|
|
544
|
+
} else listEl.scrollTo({
|
|
545
|
+
left: 0,
|
|
546
|
+
top: itemTop - scrollMarginTop,
|
|
547
|
+
behavior
|
|
548
|
+
});
|
|
549
|
+
} else listEl.scrollTo({
|
|
550
|
+
left: 0,
|
|
551
|
+
top: itemTop,
|
|
552
|
+
behavior
|
|
553
|
+
});
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
const targetTop = fenwickTree.sum(index) + getRowsTop();
|
|
557
|
+
if (!isReplay) {
|
|
558
|
+
clearPendingScrollTask();
|
|
559
|
+
if (behavior === "instant") {
|
|
560
|
+
const item = items[index];
|
|
561
|
+
pendingScrollTaskRef.current = {
|
|
562
|
+
type: "index",
|
|
563
|
+
key: item === void 0 ? void 0 : getKey(item),
|
|
564
|
+
index,
|
|
565
|
+
debounce: ensureVisibleOnly
|
|
566
|
+
};
|
|
567
|
+
schedulePendingScrollTaskExpiry();
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
if (ensureVisibleOnly) {
|
|
571
|
+
const listEl = listElRef.current;
|
|
572
|
+
if (listEl) {
|
|
573
|
+
const { scrollTop, offsetHeight } = listEl;
|
|
574
|
+
if (targetTop > scrollTop) {
|
|
575
|
+
const itemSize = fenwickTree.get(index);
|
|
576
|
+
if (!(targetTop + itemSize <= scrollTop + offsetHeight)) {
|
|
577
|
+
listEl.scrollTo({
|
|
578
|
+
left: 0,
|
|
579
|
+
top: targetTop + itemSize - offsetHeight + scrollMarginBottom,
|
|
580
|
+
behavior
|
|
581
|
+
});
|
|
582
|
+
updateRange("listScroll", true, null);
|
|
583
|
+
}
|
|
584
|
+
} else {
|
|
585
|
+
listEl.scrollTo({
|
|
586
|
+
left: 0,
|
|
587
|
+
top: targetTop - scrollMarginTop,
|
|
588
|
+
behavior
|
|
589
|
+
});
|
|
590
|
+
updateRange("listScroll", true, null);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
} else {
|
|
594
|
+
listElRef.current?.scrollTo({
|
|
595
|
+
left: 0,
|
|
596
|
+
top: targetTop,
|
|
597
|
+
behavior
|
|
598
|
+
});
|
|
599
|
+
updateRange("listScroll", true, null);
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
/**
|
|
603
|
+
* Measures the mounted rows in flow from layout, synchronously, and writes the sizes that changed into
|
|
604
|
+
* the Fenwick tree and the key -> offset cache, and the first row's top margin when the first row is
|
|
605
|
+
* mounted (`leadingMarginRef`). Reads everything first (`measureRowSpans`), then writes: no layout is
|
|
606
|
+
* forced in between. O(mounted rows).
|
|
607
|
+
*
|
|
608
|
+
* Unlike the reference implementation (DeepSeek web): it measured each row's own `offsetHeight`,
|
|
609
|
+
* which in block flow misses the margins between rows and a child's margins that collapse through a row, so the rows drifted from
|
|
610
|
+
* their offsets. A row's size here is the space it takes in the flow: from where its border box starts
|
|
611
|
+
* to where the next row's starts (the last mounted row: to the end of the rows), so collapsed margins
|
|
612
|
+
* are counted once, in the row above them, and the sizes add up to the laid-out height. A row's size
|
|
613
|
+
* depends on the next row's top margin, so every measurement measures the whole run, not the row that
|
|
614
|
+
* resized. The last mounted row's bottom margin does not collapse with the next row (not mounted): its
|
|
615
|
+
* size is provisional until the next row mounts, which corrects it without moving any mounted row
|
|
616
|
+
* (they are laid out by the browser, positioned by the first row's offset alone). Kept rows are not
|
|
617
|
+
* measured: absolutely positioned, they take no space in the flow, and keep the size they were
|
|
618
|
+
* measured at in it.
|
|
619
|
+
*/
|
|
620
|
+
const measureFlowRun = useEventCallback(() => {
|
|
621
|
+
const resizes = [];
|
|
622
|
+
const visibleItemsEl = getVisibleItemsEl();
|
|
623
|
+
if (!virtual || ignoreItemResize || !itemResizable || !(visibleItemsEl instanceof HTMLElement)) return {
|
|
624
|
+
resizes,
|
|
625
|
+
leadingMarginDelta: 0
|
|
626
|
+
};
|
|
627
|
+
const rows = getFlowRows();
|
|
628
|
+
if (rows.length === 0) return {
|
|
629
|
+
resizes,
|
|
630
|
+
leadingMarginDelta: 0
|
|
631
|
+
};
|
|
632
|
+
const { spans, firstStart } = measureRowSpans(visibleItemsEl, rows.map((row) => row.el));
|
|
633
|
+
let leadingMarginDelta = 0;
|
|
634
|
+
if (rows[0].index === 0 && firstStart !== leadingMarginRef.current) {
|
|
635
|
+
leadingMarginDelta = firstStart - leadingMarginRef.current;
|
|
636
|
+
leadingMarginRef.current = firstStart;
|
|
637
|
+
}
|
|
638
|
+
rows.forEach(({ key, index }, i) => {
|
|
639
|
+
const size = spans[i];
|
|
640
|
+
const previousSize = fenwickTree.get(index);
|
|
641
|
+
if (recordMeasuredSize(keyToHeightOffsetRef.current, key, size, itemEstimatedSize)) measuredVersionRef.current += 1;
|
|
642
|
+
if (size === previousSize) return;
|
|
643
|
+
fenwickTree.add(index, size - previousSize);
|
|
644
|
+
resizes.push({
|
|
645
|
+
index,
|
|
646
|
+
key,
|
|
647
|
+
delta: size - previousSize
|
|
648
|
+
});
|
|
649
|
+
});
|
|
650
|
+
return {
|
|
651
|
+
resizes,
|
|
652
|
+
leadingMarginDelta
|
|
653
|
+
};
|
|
654
|
+
});
|
|
655
|
+
/**
|
|
656
|
+
* Measures the mounted rows now (`measureFlowRun`) and applies the layout; returns whether any size
|
|
657
|
+
* changed. Used by the aligned jump, which cannot wait for the batch (a microtask) or the
|
|
658
|
+
* ResizeObserver (after layout, possibly a frame later) to learn the sizes.
|
|
659
|
+
*/
|
|
660
|
+
const measureMountedItems = useEventCallback(() => {
|
|
661
|
+
const { resizes, leadingMarginDelta } = measureFlowRun();
|
|
662
|
+
const changed = resizes.length !== 0 || leadingMarginDelta !== 0;
|
|
663
|
+
if (changed) {
|
|
664
|
+
applyLayoutStyles(startIndex);
|
|
665
|
+
onContentResize?.();
|
|
666
|
+
}
|
|
667
|
+
return changed;
|
|
668
|
+
});
|
|
669
|
+
/**
|
|
670
|
+
* Where item `index` is, in the list's scroll coordinates: read from the DOM when
|
|
671
|
+
* it is mounted (exact), else its offset in the Fenwick tree (estimates for unmeasured items).
|
|
672
|
+
*/
|
|
673
|
+
const getItemExtent = useEventCallback((index) => {
|
|
674
|
+
const listEl = listElRef.current;
|
|
675
|
+
const item = items[index];
|
|
676
|
+
const visibleItemsEl = getVisibleItemsEl();
|
|
677
|
+
if (listEl && item !== void 0 && visibleItemsEl && itemResizable) {
|
|
678
|
+
const key = getKey(item);
|
|
679
|
+
for (const itemEl of Array.from(visibleItemsEl.children)) if (itemEl.getAttribute("data-virtual-list-item-key") === key) {
|
|
680
|
+
const rect = itemEl.getBoundingClientRect();
|
|
681
|
+
const listTop = listEl.getBoundingClientRect().top + listEl.clientTop;
|
|
682
|
+
return {
|
|
683
|
+
top: rect.top - listTop + listEl.scrollTop,
|
|
684
|
+
size: rect.height,
|
|
685
|
+
measured: true
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return {
|
|
690
|
+
top: fenwickTree.sum(index) + getRowsTop(),
|
|
691
|
+
size: fenwickTree.get(index),
|
|
692
|
+
measured: !itemResizable
|
|
693
|
+
};
|
|
694
|
+
});
|
|
695
|
+
/** Bumped by every aligned jump: a chain of steps stops when a newer jump has started. */
|
|
696
|
+
const alignRunRef = useRef(0);
|
|
697
|
+
/**
|
|
698
|
+
* One step of an aligned jump: measure the mounted items, work out the exact
|
|
699
|
+
* `scrollTop` for the item from the DOM, set it, and update the range. If the range changes, the rows it
|
|
700
|
+
* brings into view are rendered (in a microtask, still before the browser paints) and the next step runs
|
|
701
|
+
* from the render callback (a layout effect) to measure them; the jump is done when a step finds the item
|
|
702
|
+
* mounted, no size changed and `scrollTop` already right. Bounded by `MAX_ALIGN_STEPS`. The item is
|
|
703
|
+
* found by its key at every step: rows inserted above it in between move its index.
|
|
704
|
+
*/
|
|
705
|
+
const alignStep = useEventCallback((run, key, align, scrollMarginTop, scrollMarginBottom, remaining) => {
|
|
706
|
+
const listEl = listElRef.current;
|
|
707
|
+
const index = keyToIndexRef.current.get(key);
|
|
708
|
+
if (listEl == null || run !== alignRunRef.current || index === void 0) return;
|
|
709
|
+
const sizesChanged = measureMountedItems();
|
|
710
|
+
const { top: itemTop, size: itemSize, measured } = getItemExtent(index);
|
|
711
|
+
const viewportHeight = listEl.clientHeight;
|
|
712
|
+
const target = Math.round(getAlignedScrollTop({
|
|
713
|
+
itemTop,
|
|
714
|
+
itemSize,
|
|
715
|
+
viewportHeight,
|
|
716
|
+
align,
|
|
717
|
+
scrollMarginTop,
|
|
718
|
+
scrollMarginBottom,
|
|
719
|
+
maxScrollTop: listEl.scrollHeight - viewportHeight
|
|
720
|
+
}));
|
|
721
|
+
const moved = Math.abs(listEl.scrollTop - target) >= .5;
|
|
722
|
+
if (moved) listEl.scrollTo({
|
|
723
|
+
left: listEl.scrollLeft,
|
|
724
|
+
top: target,
|
|
725
|
+
behavior: "instant"
|
|
726
|
+
});
|
|
727
|
+
viewportRef.current.scrollTop = listEl.scrollTop;
|
|
728
|
+
if (!moved && !sizesChanged && measured || remaining <= 0) return;
|
|
729
|
+
updateRange("listScroll", true, () => {
|
|
730
|
+
alignStep(run, key, align, scrollMarginTop, scrollMarginBottom, remaining - 1);
|
|
731
|
+
});
|
|
732
|
+
});
|
|
733
|
+
/**
|
|
734
|
+
* `scrollTo({ index | key, align })`. Instant (or "auto"): the aligned steps above, armed as the pending
|
|
735
|
+
* task like the other instant jumps (item sizes reported after the jump replay it, for one frame plus one
|
|
736
|
+
* task). Smooth: one scroll to the position worked out from the current layout (estimates for rows not
|
|
737
|
+
* measured yet), like the smooth index jump. Virtualization off: read from the DOM.
|
|
738
|
+
*/
|
|
739
|
+
const scrollToAligned = useEventCallback((index, align, behavior, isReplay, scrollMarginTop, scrollMarginBottom) => {
|
|
740
|
+
const listEl = listElRef.current;
|
|
741
|
+
if (listEl == null) return;
|
|
742
|
+
if (!virtual) {
|
|
743
|
+
const itemEl = getVisibleItemsEl()?.children.item(index);
|
|
744
|
+
if (!itemEl) return;
|
|
745
|
+
const rect = itemEl.getBoundingClientRect();
|
|
746
|
+
const listTop = listEl.getBoundingClientRect().top + listEl.clientTop;
|
|
747
|
+
const top = getAlignedScrollTop({
|
|
748
|
+
itemTop: rect.top - listTop + listEl.scrollTop,
|
|
749
|
+
itemSize: rect.height,
|
|
750
|
+
viewportHeight: listEl.clientHeight,
|
|
751
|
+
align,
|
|
752
|
+
scrollMarginTop,
|
|
753
|
+
scrollMarginBottom,
|
|
754
|
+
maxScrollTop: listEl.scrollHeight - listEl.clientHeight
|
|
755
|
+
});
|
|
756
|
+
listEl.scrollTo({
|
|
757
|
+
left: 0,
|
|
758
|
+
top: Math.round(top),
|
|
759
|
+
behavior
|
|
760
|
+
});
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
const item = items[index];
|
|
764
|
+
if (item === void 0) return;
|
|
765
|
+
const key = getKey(item);
|
|
766
|
+
const smooth = behavior === "smooth";
|
|
767
|
+
if (!isReplay) {
|
|
768
|
+
clearPendingScrollTask();
|
|
769
|
+
if (!smooth) {
|
|
770
|
+
pendingScrollTaskRef.current = {
|
|
771
|
+
type: "align",
|
|
772
|
+
key,
|
|
773
|
+
align,
|
|
774
|
+
scrollMarginTop,
|
|
775
|
+
scrollMarginBottom
|
|
776
|
+
};
|
|
777
|
+
schedulePendingScrollTaskExpiry();
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
const run = ++alignRunRef.current;
|
|
781
|
+
if (smooth) {
|
|
782
|
+
const { top: itemTop, size: itemSize } = getItemExtent(index);
|
|
783
|
+
const top = getAlignedScrollTop({
|
|
784
|
+
itemTop,
|
|
785
|
+
itemSize,
|
|
786
|
+
viewportHeight: listEl.clientHeight,
|
|
787
|
+
align,
|
|
788
|
+
scrollMarginTop,
|
|
789
|
+
scrollMarginBottom,
|
|
790
|
+
maxScrollTop: listEl.scrollHeight - listEl.clientHeight
|
|
791
|
+
});
|
|
792
|
+
listEl.scrollTo({
|
|
793
|
+
left: 0,
|
|
794
|
+
top: Math.round(top),
|
|
795
|
+
behavior
|
|
796
|
+
});
|
|
797
|
+
updateRange("listScroll", true, null);
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
alignStep(run, key, align, scrollMarginTop, scrollMarginBottom, MAX_ALIGN_STEPS);
|
|
801
|
+
});
|
|
802
|
+
const scrollTo = useEventCallback((options, topIfNumber) => {
|
|
803
|
+
if (typeof options == "number") {
|
|
804
|
+
scrollToPosition(options, topIfNumber, "instant");
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
const { left, top, index, key, position, behavior = "instant", debounce = true, scrollMarginTop = 0, scrollMarginBottom = 0, align } = options;
|
|
808
|
+
if (left !== void 0 || top !== void 0) scrollToPosition(left, top, behavior);
|
|
809
|
+
else if (align !== void 0 && (index !== void 0 || key !== void 0)) {
|
|
810
|
+
const alignIndex = index ?? keyToIndexRef.current.get(key);
|
|
811
|
+
if (alignIndex !== void 0) scrollToAligned(alignIndex, align, behavior, false, scrollMarginTop, scrollMarginBottom);
|
|
812
|
+
} else if (index !== void 0) scrollToIndex(index, behavior, debounce, false, scrollMarginTop, scrollMarginBottom);
|
|
813
|
+
else if (key !== void 0) {
|
|
814
|
+
const keyIndex = keyToIndexRef.current.get(key);
|
|
815
|
+
if (keyIndex !== void 0) scrollToIndex(keyIndex, behavior, debounce, false, scrollMarginTop, scrollMarginBottom);
|
|
816
|
+
} else if (position) {
|
|
817
|
+
if (virtual) {
|
|
818
|
+
clearPendingScrollTask();
|
|
819
|
+
if (behavior === "instant") {
|
|
820
|
+
pendingScrollTaskRef.current = { type: position };
|
|
821
|
+
schedulePendingScrollTaskExpiry();
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
if (position === "bottom") {
|
|
825
|
+
scrollToPosition(0, listElRef.current?.scrollHeight || 2e9, behavior);
|
|
826
|
+
if (virtual) updateRange("listScroll", true, null);
|
|
827
|
+
} else if (position === "top") scrollToPosition(0, 0, behavior);
|
|
828
|
+
}
|
|
829
|
+
});
|
|
830
|
+
const replayPendingScrollTask = useEventCallback(() => {
|
|
831
|
+
const task = pendingScrollTaskRef.current;
|
|
832
|
+
if (task) {
|
|
833
|
+
if (task.type === "top") scrollToPosition(0, 0, "instant");
|
|
834
|
+
else if (task.type === "bottom") scrollToPosition(0, listElRef.current?.scrollHeight || 2e9, "instant");
|
|
835
|
+
else if (task.type === "index") {
|
|
836
|
+
const index = task.key === void 0 ? task.index : keyToIndexRef.current.get(task.key);
|
|
837
|
+
if (index !== void 0) scrollToIndex(index, "instant", task.debounce, true);
|
|
838
|
+
} else if (task.type === "align") {
|
|
839
|
+
const index = keyToIndexRef.current.get(task.key);
|
|
840
|
+
if (index === void 0) return;
|
|
841
|
+
scrollToAligned(index, task.align, "instant", true, task.scrollMarginTop, task.scrollMarginBottom);
|
|
842
|
+
} else if (task.type === "custom") task.task();
|
|
843
|
+
}
|
|
844
|
+
});
|
|
845
|
+
/**
|
|
846
|
+
* Runs `task` now (after a range update) and again after every batch of item resizes while it is
|
|
847
|
+
* armed (one frame plus one task).
|
|
848
|
+
*/
|
|
849
|
+
const submitScrollAdjustingTask = useCallback((task) => {
|
|
850
|
+
clearPendingScrollTask();
|
|
851
|
+
pendingScrollTaskRef.current = {
|
|
852
|
+
type: "custom",
|
|
853
|
+
task
|
|
854
|
+
};
|
|
855
|
+
schedulePendingScrollTaskExpiry();
|
|
856
|
+
updateRange("listScroll", true, replayPendingScrollTask);
|
|
857
|
+
}, [
|
|
858
|
+
replayPendingScrollTask,
|
|
859
|
+
clearPendingScrollTask,
|
|
860
|
+
schedulePendingScrollTaskExpiry,
|
|
861
|
+
updateRange
|
|
862
|
+
]);
|
|
863
|
+
const syncViewport = useCallback(() => {
|
|
864
|
+
updateRange("listScroll", false, null);
|
|
865
|
+
}, [updateRange]);
|
|
866
|
+
const restoreItemsAnchor = useEventCallback((anchor) => {
|
|
867
|
+
appliedItemsAnchorRef.current = anchor;
|
|
868
|
+
rangeRef.current = renderedRange;
|
|
869
|
+
const listEl = listElRef.current;
|
|
870
|
+
const index = keyToIndexRef.current.get(anchor.key);
|
|
871
|
+
const pendingType = pendingScrollTaskRef.current?.type;
|
|
872
|
+
if (listEl == null || index === void 0 || pendingType !== void 0 && pendingType !== "index" && pendingType !== "align") return;
|
|
873
|
+
applyLayoutStyles(startIndex);
|
|
874
|
+
const diff = fenwickTree.sum(index) + anchor.delta + getRowsTop() - (listEl.scrollTop + compensationYRef.current);
|
|
875
|
+
if (diff !== 0 && !addCompensation(diff)) listEl.scrollBy(0, diff);
|
|
876
|
+
viewportRef.current.scrollTop = listEl.scrollTop;
|
|
877
|
+
});
|
|
878
|
+
useLayoutEffect(() => {
|
|
879
|
+
if (pendingItemsAnchor !== null && !sessionChangedRef.current) restoreItemsAnchor(pendingItemsAnchor);
|
|
880
|
+
if (sessionChangedRef.current) {
|
|
881
|
+
updateRange("sessionChanged", true, replayPendingScrollTask);
|
|
882
|
+
sessionChangedRef.current = false;
|
|
883
|
+
} else if (itemResizable) {
|
|
884
|
+
const heightBefore = listElRef.current?.offsetHeight;
|
|
885
|
+
applyLayoutStyles(startIndex);
|
|
886
|
+
if (heightBefore === listElRef.current?.offsetHeight) updateRange("itemsChanged", true, replayPendingScrollTask);
|
|
887
|
+
} else updateRange("itemsChanged", true, replayPendingScrollTask);
|
|
888
|
+
}, [
|
|
889
|
+
sessionId,
|
|
890
|
+
items,
|
|
891
|
+
replayPendingScrollTask,
|
|
892
|
+
updateRange,
|
|
893
|
+
itemResizable,
|
|
894
|
+
applyLayoutStyles
|
|
895
|
+
]);
|
|
896
|
+
useLayoutEffect(() => {
|
|
897
|
+
committedLayoutRef.current = {
|
|
898
|
+
items,
|
|
899
|
+
tree: fenwickTree,
|
|
900
|
+
sessionId
|
|
901
|
+
};
|
|
902
|
+
});
|
|
903
|
+
useLayoutEffect(() => {
|
|
904
|
+
applyLayoutStyles(startIndex);
|
|
905
|
+
renderCallback?.();
|
|
906
|
+
}, [
|
|
907
|
+
renderReason,
|
|
908
|
+
renderCallback,
|
|
909
|
+
renderVersion,
|
|
910
|
+
applyLayoutStyles
|
|
911
|
+
]);
|
|
912
|
+
const getOffsetTopOfKey = useEventCallback((key) => {
|
|
913
|
+
const index = keyToIndexRef.current.get(key);
|
|
914
|
+
if (index !== void 0) return fenwickTree.sum(index) + getRowsTop();
|
|
915
|
+
});
|
|
916
|
+
const refreshListWidth = useCallback(() => {
|
|
917
|
+
const listEl = listElRef.current;
|
|
918
|
+
if (listEl) listEl.style.setProperty("--virtual-list-width", `${listEl.offsetWidth}px`);
|
|
919
|
+
}, [listElRef]);
|
|
920
|
+
const imperativeApi = useMemo(() => ({
|
|
921
|
+
listElRef,
|
|
922
|
+
itemsElRef,
|
|
923
|
+
scrollTo,
|
|
924
|
+
syncViewport,
|
|
925
|
+
getOffsetTopOfKey,
|
|
926
|
+
submitScrollAdjustingTask,
|
|
927
|
+
refreshListWidth
|
|
928
|
+
}), [
|
|
929
|
+
getOffsetTopOfKey,
|
|
930
|
+
scrollTo,
|
|
931
|
+
syncViewport,
|
|
932
|
+
submitScrollAdjustingTask,
|
|
933
|
+
refreshListWidth
|
|
934
|
+
]);
|
|
935
|
+
useImperativeHandle(componentRef, () => imperativeApi, [imperativeApi]);
|
|
936
|
+
const itemResizeQueueRef = useRef([]);
|
|
937
|
+
const enqueueItemResize = useEventCallback((entry) => {
|
|
938
|
+
itemResizeQueueRef.current.push(entry);
|
|
939
|
+
});
|
|
940
|
+
const clearItemResizeQueue = useEventCallback(() => {
|
|
941
|
+
itemResizeQueueRef.current = [];
|
|
942
|
+
});
|
|
943
|
+
const batchResizeScheduledRef = useRef(false);
|
|
944
|
+
/**
|
|
945
|
+
* Applies the queued item measurements (in place of the browser's scroll anchoring): the mounted rows
|
|
946
|
+
* are measured (`measureFlowRun`: all of them, since a row's size depends on its neighbour) and each
|
|
947
|
+
* size change goes into the Fenwick tree and the key -> offset cache. With no pending scroll task,
|
|
948
|
+
* the size changes of items measured for the FIRST time among those that entered above the old start
|
|
949
|
+
* of the range (`prependIndexes`), and of rows above the view, are scrolled away (`scrollBy`, or
|
|
950
|
+
* through the iOS compensation during a touch scroll); with a pending task, the task is replayed.
|
|
951
|
+
*/
|
|
952
|
+
const handleItemBatchResize = useEventCallback(() => {
|
|
953
|
+
if (!virtual) return;
|
|
954
|
+
const queue = itemResizeQueueRef.current;
|
|
955
|
+
if (ignoreItemResize) return;
|
|
956
|
+
const listElBefore = listElRef.current;
|
|
957
|
+
const viewTop = listElBefore == null ? 0 : listElBefore.scrollTop + compensationYRef.current - getRowsTop();
|
|
958
|
+
const viewTopIndex = listElBefore == null ? 0 : fenwickTree.getBound(viewTop);
|
|
959
|
+
const isFirstOfKey = /* @__PURE__ */ new Map();
|
|
960
|
+
for (const { key, isFirst } of queue) if (!isFirstOfKey.has(key)) isFirstOfKey.set(key, isFirst);
|
|
961
|
+
const { resizes, leadingMarginDelta } = measureFlowRun();
|
|
962
|
+
if (resizes.length === 0 && leadingMarginDelta === 0 && pendingScrollTaskRef.current === null) {
|
|
963
|
+
clearItemResizeQueue();
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
const scrollTopBefore = listElBefore == null ? 0 : listElBefore.scrollTop;
|
|
967
|
+
const effectiveTopBefore = scrollTopBefore + compensationYRef.current;
|
|
968
|
+
applyLayoutStyles(startIndex);
|
|
969
|
+
const listEl = listElRef.current;
|
|
970
|
+
let contentMoved = true;
|
|
971
|
+
if (listEl != null) {
|
|
972
|
+
const pendingTask = pendingScrollTaskRef.current;
|
|
973
|
+
if (pendingTask?.type === void 0) {
|
|
974
|
+
const compensates = (index, key) => key !== void 0 && isFirstOfKey.get(key) === true && prependIndexes.includes(index) || index < viewTopIndex;
|
|
975
|
+
let compensation = 0;
|
|
976
|
+
let uncompensated = false;
|
|
977
|
+
for (const { index, key, delta } of resizes) if (compensates(index, key)) compensation += delta;
|
|
978
|
+
else if (delta !== 0) uncompensated = true;
|
|
979
|
+
const firstItem = items[0];
|
|
980
|
+
if (leadingMarginDelta !== 0 && (viewTop > 0 || compensates(0, firstItem === void 0 ? void 0 : getKey(firstItem)))) compensation += leadingMarginDelta;
|
|
981
|
+
else if (leadingMarginDelta !== 0) uncompensated = true;
|
|
982
|
+
if (compensation !== 0) {
|
|
983
|
+
const remaining = compensation - (listEl.scrollTop - scrollTopBefore);
|
|
984
|
+
if (remaining !== 0 && !addCompensation(remaining)) listEl.scrollBy(0, remaining);
|
|
985
|
+
}
|
|
986
|
+
const effectiveTop = listEl.scrollTop + compensationYRef.current;
|
|
987
|
+
contentMoved = uncompensated || Math.abs(effectiveTop - (effectiveTopBefore + compensation)) >= 1;
|
|
988
|
+
} else {
|
|
989
|
+
onContentResize?.();
|
|
990
|
+
replayPendingScrollTask();
|
|
991
|
+
}
|
|
992
|
+
const viewport = viewportRef.current;
|
|
993
|
+
viewport.scrollTop = listEl.scrollTop;
|
|
994
|
+
viewport.scrollLeft = listEl.scrollLeft;
|
|
995
|
+
if (pendingTask?.type === void 0 && contentMoved) onContentResize?.();
|
|
996
|
+
} else if (contentMoved) onContentResize?.();
|
|
997
|
+
clearItemResizeQueue();
|
|
998
|
+
updateRange("batchItemResize", false, null);
|
|
999
|
+
});
|
|
1000
|
+
/** Measurements of items that just mounted are applied together, in one microtask. */
|
|
1001
|
+
const scheduleItemBatchResize = useEventCallback(() => {
|
|
1002
|
+
if (!batchResizeScheduledRef.current) {
|
|
1003
|
+
Promise.resolve().then(() => {
|
|
1004
|
+
handleItemBatchResize();
|
|
1005
|
+
batchResizeScheduledRef.current = false;
|
|
1006
|
+
});
|
|
1007
|
+
batchResizeScheduledRef.current = true;
|
|
1008
|
+
}
|
|
1009
|
+
});
|
|
1010
|
+
/**
|
|
1011
|
+
* Ignores scroll events while a flush of the iOS compensation suppresses them; otherwise updates
|
|
1012
|
+
* the range (replaying a pending task) and calls `onScroll`.
|
|
1013
|
+
*/
|
|
1014
|
+
const handleListScroll = useEventCallback((event) => {
|
|
1015
|
+
const scrollTop = event.target.scrollTop;
|
|
1016
|
+
if (!isScrollSuppressedWithForceRestore(scrollTop)) {
|
|
1017
|
+
if (!wheelScrollingRef.current) updateRange("listScroll", false, pendingScrollTaskRef.current ? replayPendingScrollTask : null);
|
|
1018
|
+
onScroll?.(event.nativeEvent);
|
|
1019
|
+
}
|
|
1020
|
+
});
|
|
1021
|
+
/**
|
|
1022
|
+
* With `catchWheel` (and a fine pointer): scrolls by the wheel itself (`preventDefault`, then
|
|
1023
|
+
* `scrollTop +=`) and updates the range at once, so the rows are rendered in the same frame.
|
|
1024
|
+
*/
|
|
1025
|
+
const handleWheel = useEventCallback((wheelEvent) => {
|
|
1026
|
+
if (catchWheel) {
|
|
1027
|
+
if (mayUseWheelRef.current) {
|
|
1028
|
+
const listEl = listElRef.current;
|
|
1029
|
+
if (listEl != null) {
|
|
1030
|
+
const scale = getWheelDeltaScale();
|
|
1031
|
+
const deltaX = wheelEvent.deltaX / scale;
|
|
1032
|
+
const deltaY = wheelEvent.deltaY / scale;
|
|
1033
|
+
if (deltaX === 0 && (listEl.scrollTop === 0 && deltaY <= 0 || listEl.scrollTop + listEl.offsetHeight >= listEl.scrollHeight && deltaY >= 0)) return;
|
|
1034
|
+
wheelEvent.preventDefault();
|
|
1035
|
+
listEl.scrollTop += deltaY;
|
|
1036
|
+
listEl.scrollLeft += deltaX;
|
|
1037
|
+
syncViewport();
|
|
1038
|
+
wheelScrollingRef.current = true;
|
|
1039
|
+
runLatestInNextFrame(() => {
|
|
1040
|
+
wheelScrollingRef.current = false;
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
onWheel?.(wheelEvent);
|
|
1045
|
+
}
|
|
1046
|
+
});
|
|
1047
|
+
useNonPassiveWheel(useCallback(() => listElRef.current, []), handleWheel, catchWheel);
|
|
1048
|
+
const iosCompensationEnabled = isIOS && virtual;
|
|
1049
|
+
const { addCompensation, isScrollSuppressedWithForceRestore } = useIosMomentumCompensation({
|
|
1050
|
+
enabled: iosCompensationEnabled,
|
|
1051
|
+
listElRef,
|
|
1052
|
+
itemsElRef,
|
|
1053
|
+
syncViewport,
|
|
1054
|
+
compensationYRef
|
|
1055
|
+
});
|
|
1056
|
+
/**
|
|
1057
|
+
* The list's own resize: stores its height and width, sets `--virtual-list-width` and updates
|
|
1058
|
+
* the range. Skipped while the list is hidden with `display: none`.
|
|
1059
|
+
*/
|
|
1060
|
+
const handleListResize = useEventCallback((listEl, deferRender) => {
|
|
1061
|
+
if (isHiddenByDisplayNone(listEl)) return;
|
|
1062
|
+
const height = listEl.offsetHeight;
|
|
1063
|
+
const width = listEl.offsetWidth;
|
|
1064
|
+
const viewport = viewportRef.current;
|
|
1065
|
+
if (listElRef.current) listElRef.current.style.setProperty("--virtual-list-width", `${width}px`);
|
|
1066
|
+
if (renderCol == null && renderItemWithCols == null) {
|
|
1067
|
+
if (height === viewport.listHeight) return;
|
|
1068
|
+
} else if (height === viewport.listHeight && width === viewport.listWidth) return;
|
|
1069
|
+
viewport.listHeight = height;
|
|
1070
|
+
viewport.listWidth = width;
|
|
1071
|
+
updateRange("listResize", deferRender, replayPendingScrollTask);
|
|
1072
|
+
});
|
|
1073
|
+
const onListResizeObserved = useEventCallback(({ entry }) => {
|
|
1074
|
+
handleListResize(entry.target, false);
|
|
1075
|
+
onResize?.(entry);
|
|
1076
|
+
});
|
|
1077
|
+
const onListRefUpdated = useEventCallback((el) => {
|
|
1078
|
+
if (el) handleListResize(el, true);
|
|
1079
|
+
});
|
|
1080
|
+
/**
|
|
1081
|
+
* Tracks the row holding focus: focusin finds the row the target is in (the visible-items element's
|
|
1082
|
+
* child it is inside); focusout to an element outside the list releases it, and the range is updated so
|
|
1083
|
+
* the row unmounts if it is out of the range. A focusout to nowhere (`relatedTarget` null) does not
|
|
1084
|
+
* release it: switching to another window or tab fires one while the element keeps focus, and the row
|
|
1085
|
+
* must still be there when the person comes back. When focus did go to the page instead (a click on
|
|
1086
|
+
* nothing focusable), the next range update finds `document.activeElement` outside the list and
|
|
1087
|
+
* releases the row then.
|
|
1088
|
+
*/
|
|
1089
|
+
const handleFocusIn = useEventCallback((event) => {
|
|
1090
|
+
const visibleItemsEl = getVisibleItemsEl();
|
|
1091
|
+
let el = event.target instanceof Element ? event.target : null;
|
|
1092
|
+
while (el !== null && el.parentElement !== visibleItemsEl) el = el.parentElement;
|
|
1093
|
+
focusedKeyRef.current = el?.getAttribute("data-virtual-list-item-key") ?? null;
|
|
1094
|
+
});
|
|
1095
|
+
const handleFocusOut = useEventCallback((event) => {
|
|
1096
|
+
const listEl = listElRef.current;
|
|
1097
|
+
const next = event.relatedTarget;
|
|
1098
|
+
if (listEl === null || next === null || next instanceof Node && listEl.contains(next)) return;
|
|
1099
|
+
if (focusedKeyRef.current !== null) {
|
|
1100
|
+
focusedKeyRef.current = null;
|
|
1101
|
+
updateRange("listScroll", true, null);
|
|
1102
|
+
}
|
|
1103
|
+
});
|
|
1104
|
+
useLayoutEffect(() => {
|
|
1105
|
+
const listEl = listElRef.current;
|
|
1106
|
+
if (listEl === null) return;
|
|
1107
|
+
listEl.addEventListener("focusin", handleFocusIn);
|
|
1108
|
+
listEl.addEventListener("focusout", handleFocusOut);
|
|
1109
|
+
return () => {
|
|
1110
|
+
listEl.removeEventListener("focusin", handleFocusIn);
|
|
1111
|
+
listEl.removeEventListener("focusout", handleFocusOut);
|
|
1112
|
+
};
|
|
1113
|
+
}, [handleFocusIn, handleFocusOut]);
|
|
1114
|
+
useLayoutEffect(() => {
|
|
1115
|
+
if (defaultScrollIndex != null) scrollTo({ index: defaultScrollIndex });
|
|
1116
|
+
else if (defaultScrollKey != null) scrollTo({ key: defaultScrollKey });
|
|
1117
|
+
}, [sessionId]);
|
|
1118
|
+
const itemsContainerStyle = useMemo(() => ({
|
|
1119
|
+
boxSizing: "content-box",
|
|
1120
|
+
width: px(totalColumnsWidth) ?? void 0,
|
|
1121
|
+
paddingTop: px(paddingTop) ?? void 0,
|
|
1122
|
+
...typeof itemsStyle == "object" ? itemsStyle : {}
|
|
1123
|
+
}), [
|
|
1124
|
+
totalColumnsWidth,
|
|
1125
|
+
paddingTop,
|
|
1126
|
+
itemsStyle
|
|
1127
|
+
]);
|
|
1128
|
+
const [keptItemsBefore, keptItemsAfter] = useMemo(() => {
|
|
1129
|
+
if (!virtual) return [EMPTY_ARRAY, EMPTY_ARRAY];
|
|
1130
|
+
const before = [];
|
|
1131
|
+
const after = [];
|
|
1132
|
+
keptKeysRef.current.forEach((key) => {
|
|
1133
|
+
const index = keyToIndexRef.current.get(key);
|
|
1134
|
+
if (index === void 0) return;
|
|
1135
|
+
const item = items[index];
|
|
1136
|
+
if (item !== void 0) {
|
|
1137
|
+
if (index < startIndex) before.push(item);
|
|
1138
|
+
else if (index > endIndex) after.push(item);
|
|
1139
|
+
}
|
|
1140
|
+
});
|
|
1141
|
+
return [before, after];
|
|
1142
|
+
}, [
|
|
1143
|
+
virtual,
|
|
1144
|
+
keptKeysRef.current,
|
|
1145
|
+
items,
|
|
1146
|
+
startIndex,
|
|
1147
|
+
endIndex
|
|
1148
|
+
]);
|
|
1149
|
+
/**
|
|
1150
|
+
* Renders one item. With `itemResizable` it is wrapped in a ResizeObserverWrapper: when the ref is
|
|
1151
|
+
* set the item gets `data-virtual-list-item-key` and is measured in the next microtask batch
|
|
1152
|
+
* (`isFirst`); later sizes come from the ResizeObserver. The React key is `sessionId:key`, so a new
|
|
1153
|
+
* session remounts every item.
|
|
1154
|
+
*/
|
|
1155
|
+
const renderItem = useCallback((item, absolute) => {
|
|
1156
|
+
const key = getKey(item);
|
|
1157
|
+
const index = keyToIndexRef.current.get(key);
|
|
1158
|
+
const reactKey = sessionId !== void 0 ? `${sessionId}:${key}` : key;
|
|
1159
|
+
const absoluteStyle = absolute ? {
|
|
1160
|
+
position: "absolute",
|
|
1161
|
+
top: px(fenwickTree.sum(index) - fenwickTree.sum(startIndex) + firstRowStartRef.current),
|
|
1162
|
+
left: 0,
|
|
1163
|
+
right: 0
|
|
1164
|
+
} : void 0;
|
|
1165
|
+
const colsNode = renderCol != null ? /* @__PURE__ */ jsx(ItemColumns, {
|
|
1166
|
+
index,
|
|
1167
|
+
item
|
|
1168
|
+
}) : void 0;
|
|
1169
|
+
const itemWithColsNode = renderItemWithCols != null ? /* @__PURE__ */ jsx(ItemColumns, {
|
|
1170
|
+
index,
|
|
1171
|
+
item
|
|
1172
|
+
}) : void 0;
|
|
1173
|
+
if (itemResizable) return /* @__PURE__ */ jsx(ResizeObserverWrapper, {
|
|
1174
|
+
simulateRefUpdateTrigger: sessionId,
|
|
1175
|
+
disabled: !virtual,
|
|
1176
|
+
onRefUpdate: (el) => {
|
|
1177
|
+
if (el) {
|
|
1178
|
+
el.setAttribute(ITEM_KEY_ATTR, key);
|
|
1179
|
+
if (!ignoreItemResize && virtual) {
|
|
1180
|
+
enqueueItemResize({
|
|
1181
|
+
key,
|
|
1182
|
+
target: el,
|
|
1183
|
+
isFirst: true
|
|
1184
|
+
});
|
|
1185
|
+
scheduleItemBatchResize();
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
},
|
|
1189
|
+
onResize: ({ entry, isFirst }) => {
|
|
1190
|
+
if (!ignoreItemResize) {
|
|
1191
|
+
enqueueItemResize({
|
|
1192
|
+
key,
|
|
1193
|
+
target: entry.target,
|
|
1194
|
+
isFirst
|
|
1195
|
+
});
|
|
1196
|
+
getResizeObserverDelegate().runAfterEntries(handleItemBatchResize);
|
|
1197
|
+
}
|
|
1198
|
+
},
|
|
1199
|
+
children: ({ ref }) => {
|
|
1200
|
+
if (ItemComponent) return /* @__PURE__ */ jsx(ItemComponent, {
|
|
1201
|
+
domRef: ref,
|
|
1202
|
+
item,
|
|
1203
|
+
index,
|
|
1204
|
+
renderedCols: colsNode,
|
|
1205
|
+
renderedItemWithCols: itemWithColsNode,
|
|
1206
|
+
style: absoluteStyle
|
|
1207
|
+
});
|
|
1208
|
+
else if (renderItemChild == null) return;
|
|
1209
|
+
else return renderItemChild({
|
|
1210
|
+
key: void 0,
|
|
1211
|
+
domRef: ref,
|
|
1212
|
+
item,
|
|
1213
|
+
index,
|
|
1214
|
+
renderedCols: colsNode,
|
|
1215
|
+
renderedItemWithCols: itemWithColsNode,
|
|
1216
|
+
style: absoluteStyle
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
}, reactKey);
|
|
1220
|
+
else if (ItemComponent) return /* @__PURE__ */ jsx(ItemComponent, {
|
|
1221
|
+
domRef: null,
|
|
1222
|
+
item,
|
|
1223
|
+
index,
|
|
1224
|
+
renderedCols: colsNode,
|
|
1225
|
+
renderedItemWithCols: itemWithColsNode,
|
|
1226
|
+
style: absoluteStyle
|
|
1227
|
+
}, reactKey);
|
|
1228
|
+
else if (renderItemChild == null) return;
|
|
1229
|
+
else return renderItemChild({
|
|
1230
|
+
key: reactKey,
|
|
1231
|
+
domRef: null,
|
|
1232
|
+
item,
|
|
1233
|
+
index,
|
|
1234
|
+
renderedCols: colsNode,
|
|
1235
|
+
renderedItemWithCols: itemWithColsNode,
|
|
1236
|
+
style: absoluteStyle
|
|
1237
|
+
});
|
|
1238
|
+
}, [
|
|
1239
|
+
virtual,
|
|
1240
|
+
getKey,
|
|
1241
|
+
fenwickTree,
|
|
1242
|
+
startIndex,
|
|
1243
|
+
renderCol,
|
|
1244
|
+
renderItemWithCols,
|
|
1245
|
+
itemResizable,
|
|
1246
|
+
sessionId,
|
|
1247
|
+
ignoreItemResize,
|
|
1248
|
+
enqueueItemResize,
|
|
1249
|
+
scheduleItemBatchResize,
|
|
1250
|
+
handleItemBatchResize,
|
|
1251
|
+
ItemComponent,
|
|
1252
|
+
renderItemChild
|
|
1253
|
+
]);
|
|
1254
|
+
const renderInputs = useMemo(() => ({}), [
|
|
1255
|
+
virtual,
|
|
1256
|
+
getKey,
|
|
1257
|
+
renderCol,
|
|
1258
|
+
renderItemWithCols,
|
|
1259
|
+
itemResizable,
|
|
1260
|
+
sessionId,
|
|
1261
|
+
ignoreItemResize,
|
|
1262
|
+
ItemComponent,
|
|
1263
|
+
renderItemChild
|
|
1264
|
+
]);
|
|
1265
|
+
const [flowElements] = useState(() => /* @__PURE__ */ new Map());
|
|
1266
|
+
const renderedVisibleItems = useMemo(() => {
|
|
1267
|
+
const nodes = visibleItems.map((item) => {
|
|
1268
|
+
const key = getKey(item);
|
|
1269
|
+
const index = keyToIndexRef.current.get(key);
|
|
1270
|
+
const cached = flowElements.get(key);
|
|
1271
|
+
if (cached !== void 0 && cached.item === item && cached.index === index && cached.inputs === renderInputs) return cached.node;
|
|
1272
|
+
const node = renderItem(item, false);
|
|
1273
|
+
flowElements.set(key, {
|
|
1274
|
+
item,
|
|
1275
|
+
index,
|
|
1276
|
+
inputs: renderInputs,
|
|
1277
|
+
node
|
|
1278
|
+
});
|
|
1279
|
+
return node;
|
|
1280
|
+
});
|
|
1281
|
+
if (flowElements.size > visibleItems.length) {
|
|
1282
|
+
const inRange = new Set(visibleItems.map(getKey));
|
|
1283
|
+
for (const key of flowElements.keys()) if (!inRange.has(key)) flowElements.delete(key);
|
|
1284
|
+
}
|
|
1285
|
+
return nodes;
|
|
1286
|
+
}, [
|
|
1287
|
+
renderItem,
|
|
1288
|
+
visibleItems,
|
|
1289
|
+
getKey,
|
|
1290
|
+
flowElements,
|
|
1291
|
+
renderInputs
|
|
1292
|
+
]);
|
|
1293
|
+
const renderedKeptBefore = useMemo(() => keptItemsBefore.map((item) => renderItem(item, true)), [renderItem, keptItemsBefore]);
|
|
1294
|
+
const renderedKeptAfter = useMemo(() => keptItemsAfter.map((item) => renderItem(item, true)), [renderItem, keptItemsAfter]);
|
|
1295
|
+
useNonVirtualContentResize(itemsElRef, onContentResize, !virtual);
|
|
1296
|
+
const onContentResizeRef = useLatestRef(onContentResize);
|
|
1297
|
+
const hasItems = items.length !== 0;
|
|
1298
|
+
const footerNode = useMemo(() => typeof footer != "function" ? footer : /* @__PURE__ */ jsx(ResizeObserverWrapper, {
|
|
1299
|
+
onResize: () => {
|
|
1300
|
+
onContentResizeRef.current?.();
|
|
1301
|
+
},
|
|
1302
|
+
onUnmounted: () => {
|
|
1303
|
+
onContentResizeRef.current?.();
|
|
1304
|
+
},
|
|
1305
|
+
children: ({ ref }) => footer({ domRef: ref })
|
|
1306
|
+
}), [footer, onContentResizeRef]);
|
|
1307
|
+
const emptyNode = useMemo(() => typeof empty != "function" ? empty : /* @__PURE__ */ jsx(ResizeObserverWrapper, {
|
|
1308
|
+
onResize: () => {
|
|
1309
|
+
onContentResizeRef.current?.();
|
|
1310
|
+
},
|
|
1311
|
+
onUnmounted: () => {
|
|
1312
|
+
onContentResizeRef.current?.();
|
|
1313
|
+
},
|
|
1314
|
+
children: ({ ref }) => empty({ domRef: ref })
|
|
1315
|
+
}), [empty, onContentResizeRef]);
|
|
1316
|
+
const { snapshotAnchorElementStateAndSaveToRef, restoreScrollStateFromAnchor, clearAnchor } = useScrollAnchor(listElRef, getVisibleItemsEl);
|
|
1317
|
+
useMemoWithPrevious((previous) => {
|
|
1318
|
+
if (!previous || !itemResizable) return;
|
|
1319
|
+
const [wasVirtual, previousSessionId] = previous;
|
|
1320
|
+
if (previousSessionId === sessionId) {
|
|
1321
|
+
if (wasVirtual && !virtual) snapshotAnchorElementStateAndSaveToRef();
|
|
1322
|
+
else {
|
|
1323
|
+
const visibleItemsEl = getVisibleItemsEl();
|
|
1324
|
+
if (!(visibleItemsEl instanceof HTMLElement)) return;
|
|
1325
|
+
const rows = [];
|
|
1326
|
+
for (const el of Array.from(visibleItemsEl.children)) {
|
|
1327
|
+
const key = el.getAttribute(ITEM_KEY_ATTR);
|
|
1328
|
+
if (key !== null && el instanceof HTMLElement) rows.push({
|
|
1329
|
+
el,
|
|
1330
|
+
key
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1333
|
+
const { spans, firstStart } = measureRowSpans(visibleItemsEl, rows.map((row) => row.el));
|
|
1334
|
+
if (rows[0] !== void 0 && keyToIndexRef.current.get(rows[0].key) === 0) leadingMarginRef.current = firstStart;
|
|
1335
|
+
rows.forEach(({ key }, i) => {
|
|
1336
|
+
if (recordMeasuredSize(keyToHeightOffsetRef.current, key, spans[i], itemEstimatedSize)) measuredVersionRef.current += 1;
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
}, [virtual, sessionId]);
|
|
1341
|
+
useLayoutEffectWithPrevious((previous) => {
|
|
1342
|
+
if (!itemResizable || !previous) return;
|
|
1343
|
+
const [wasVirtual, previousSessionId] = previous;
|
|
1344
|
+
if (previousSessionId === sessionId && wasVirtual && !virtual) {
|
|
1345
|
+
restoreScrollStateFromAnchor();
|
|
1346
|
+
clearAnchor();
|
|
1347
|
+
}
|
|
1348
|
+
}, [virtual, sessionId]);
|
|
1349
|
+
const { onKeyDown: onKeyboardScroll } = useKeyboardScroll(scrollTo, { pageBottomOcclusion: keyboardPageBottomOcclusion });
|
|
1350
|
+
/** The consumer's `onKeyDown` first: a key it takes (`preventDefault`) does not scroll the list. */
|
|
1351
|
+
const onKeyDown = useCallback((event) => {
|
|
1352
|
+
onNativeKeyDown?.(event);
|
|
1353
|
+
onKeyboardScroll(event);
|
|
1354
|
+
}, [onNativeKeyDown, onKeyboardScroll]);
|
|
1355
|
+
const emptyCentered = items.length === 0 && emptyAlign === "center";
|
|
1356
|
+
const rootClassName = cn("r-virtual-list", columns.length !== 0 && "r-virtual-list--x-scrollable", showScrollbar && "r-virtual-list--show-scrollbar", printable === true && "r-virtual-list--printable", emptyCentered && "r-virtual-list--empty-centered", iosCompensationEnabled && "r-virtual-list--ios-compensation-enabled", className);
|
|
1357
|
+
return /* @__PURE__ */ jsx(ResizeObserverWrapper, {
|
|
1358
|
+
forwardedRef: mergedListRefSetter,
|
|
1359
|
+
onRefUpdate: onListRefUpdated,
|
|
1360
|
+
onResize: onListResizeObserved,
|
|
1361
|
+
children: ({ ref }) => /* @__PURE__ */ jsxs("div", {
|
|
1362
|
+
...nativeProps,
|
|
1363
|
+
"data-slot": "virtual-list",
|
|
1364
|
+
className: rootClassName,
|
|
1365
|
+
style,
|
|
1366
|
+
onScroll: handleListScroll,
|
|
1367
|
+
ref,
|
|
1368
|
+
onKeyDown,
|
|
1369
|
+
tabIndex: focusable ? 0 : -1,
|
|
1370
|
+
children: [
|
|
1371
|
+
header,
|
|
1372
|
+
hasItems ? /* @__PURE__ */ jsx("div", {
|
|
1373
|
+
"data-slot": "virtual-list-items",
|
|
1374
|
+
ref: itemsElRef,
|
|
1375
|
+
className: itemsClassName,
|
|
1376
|
+
style: itemsContainerStyle,
|
|
1377
|
+
children: /* @__PURE__ */ jsx(VirtualListContext.Provider, {
|
|
1378
|
+
value: imperativeApi,
|
|
1379
|
+
children: /* @__PURE__ */ jsx(ColumnsContext.Provider, {
|
|
1380
|
+
value: columnsContextValue,
|
|
1381
|
+
children: createElement(visibleItemsTag, {
|
|
1382
|
+
"data-slot": "virtual-list-visible-items",
|
|
1383
|
+
style: virtual ? VIRTUAL_VISIBLE_ITEMS_STYLE : NON_VIRTUAL_VISIBLE_ITEMS_STYLE,
|
|
1384
|
+
...visibleItemsProps
|
|
1385
|
+
}, ...renderedKeptBefore, ...renderedVisibleItems, ...renderedKeptAfter)
|
|
1386
|
+
})
|
|
1387
|
+
})
|
|
1388
|
+
}, "items") : /* @__PURE__ */ jsx("div", {
|
|
1389
|
+
"data-slot": "virtual-list-empty",
|
|
1390
|
+
className: cn("r-virtual-list__empty", emptyCentered && "r-virtual-list__empty--center"),
|
|
1391
|
+
children: emptyNode
|
|
1392
|
+
}, "empty"),
|
|
1393
|
+
footerNode,
|
|
1394
|
+
paddingBottom ? /* @__PURE__ */ jsx("div", { style: { paddingBottom } }) : null,
|
|
1395
|
+
footerAfterBottomPadding
|
|
1396
|
+
]
|
|
1397
|
+
})
|
|
1398
|
+
});
|
|
1399
|
+
}
|
|
1400
|
+
//#endregion
|
|
1401
|
+
export { VirtualList };
|