diff --git a/package-lock.json b/package-lock.json
index daec580127..e4c8caa547 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,6 +16,7 @@
"@fullcalendar/vue3": "6.1.21",
"@google/model-viewer": "4.3.1",
"@sentry/vue": "10.63.0",
+ "@tanstack/vue-virtual": "^3.13.31",
"@unhead/vue": "3.1.7",
"@vuepic/vue-datepicker": "14.0.0",
"bowser": "2.14.1",
@@ -2771,6 +2772,32 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@tanstack/virtual-core": {
+ "version": "3.17.3",
+ "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.17.3.tgz",
+ "integrity": "sha512-8Np/TFELpI0ySuJoVmjvOrQYXH/8sTX0Biv9szhFhY39xOdAAY+smrMxjxOum/ux3eM8MUJQsEJ0/R0UpvC8dw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/vue-virtual": {
+ "version": "3.13.31",
+ "resolved": "https://registry.npmjs.org/@tanstack/vue-virtual/-/vue-virtual-3.13.31.tgz",
+ "integrity": "sha512-wZMEoSf852jQqaf3Ika1J7PiBae6341LNy/2CxmIyn0XKDQXMuK41wVX+xp6G0yx8jyR95Ef+Tdr13DK7mbJtQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/virtual-core": "3.17.3"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "vue": "^2.7.0 || ^3.0.0"
+ }
+ },
"node_modules/@tybys/wasm-util": {
"version": "0.10.3",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz",
diff --git a/package.json b/package.json
index d3f42f9f66..508efdbe9f 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,7 @@
"@fullcalendar/vue3": "6.1.21",
"@google/model-viewer": "4.3.1",
"@sentry/vue": "10.63.0",
+ "@tanstack/vue-virtual": "^3.13.31",
"@unhead/vue": "3.1.7",
"@vuepic/vue-datepicker": "14.0.0",
"bowser": "2.14.1",
diff --git a/src/components/cells/ValidationCell.vue b/src/components/cells/ValidationCell.vue
index c6f29d99e2..61bf15956b 100644
--- a/src/components/cells/ValidationCell.vue
+++ b/src/components/cells/ValidationCell.vue
@@ -16,6 +16,7 @@
@@ -64,7 +65,7 @@
'font-weight': isDarkTheme ? 'bold' : 'normal'
}"
:key="`avatar-${person.id}`"
- v-for="person in assignees"
+ v-for="person in visibleAssignees"
>
{{ person.initials }}
+
+ +{{ extraAssigneesCount }}
+
@@ -111,6 +119,12 @@ const props = defineProps({
isBorder: { type: Boolean, default: true },
isCastingReady: { type: Boolean, default: false },
left: { type: String, default: '0px' },
+ // Opt-in (0 = no cap, keeps the historical wrapping behavior): show at
+ // most this many assignee avatars and collapse the rest into a "+N"
+ // count on a single non-wrapping line. Virtualized lists (EditList) use
+ // it so a heavily-assigned task can never wrap the avatar stack and
+ // change the row height.
+ maxAssignees: { type: Number, default: 0 },
minimized: { type: Boolean, default: false },
rowX: { type: Number, default: 0 },
selectable: { type: Boolean, default: true },
@@ -151,6 +165,23 @@ const assignees = computed(() =>
)
)
+const visibleAssignees = computed(() =>
+ props.maxAssignees > 0
+ ? assignees.value.slice(0, props.maxAssignees)
+ : assignees.value
+)
+
+const extraAssigneesCount = computed(
+ () => assignees.value.length - visibleAssignees.value.length
+)
+
+const extraAssigneesTitle = computed(() =>
+ assignees.value
+ .slice(props.maxAssignees)
+ .map(person => person.full_name)
+ .join('\n')
+)
+
const priority = computed(() => formatPrioritySymbol(task.value.priority))
const cellStyle = computed(() => {
@@ -276,6 +307,25 @@ defineExpose({
width: 100%;
}
+// Opt-in via the maxAssignees prop: the cell content stays on one line and
+// clips instead of wrapping, so the cell height is constant whatever the
+// status name length or assignee count.
+.status-wrapper.single-line {
+ flex-wrap: nowrap;
+ overflow: hidden;
+
+ > * {
+ flex-shrink: 0;
+ }
+}
+
+.avatar.more-assignees {
+ background: var(--background-alt);
+ color: var(--text);
+ font-size: 9px;
+ font-weight: bold;
+}
+
.full-wrapper {
flex: 1;
}
diff --git a/src/components/lists/AssetList.vue b/src/components/lists/AssetList.vue
index e31d8f36c5..e17bbfe1ae 100644
--- a/src/components/lists/AssetList.vue
+++ b/src/components/lists/AssetList.vue
@@ -240,15 +240,45 @@
-
-
+
+
+ |
+
+
-
-
-
+
+
+ |
+
+