Skip to content

Commit b7c8e1f

Browse files
Merge pull request galaxyproject#22094 from dannon/fix/long-history-name-overflow
Fix long history names pushing items off panel
2 parents 08fc848 + 142c54a commit b7c8e1f

File tree

2 files changed

+76
-7
lines changed

2 files changed

+76
-7
lines changed

client/src/components/Common/Heading.vue

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script setup lang="ts">
22
import { faAngleDoubleDown, faAngleDoubleUp } from "@fortawesome/free-solid-svg-icons";
33
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
4-
import { computed } from "vue";
4+
import { computed, nextTick, onMounted, onUpdated, ref } from "vue";
55
66
import type { IconLike } from "@/components/icons/galaxyIcons";
77
@@ -21,6 +21,7 @@ interface Props {
2121
size?: "xs" | "sm" | "md" | "lg" | "xl" | "text";
2222
icon?: IconLike;
2323
truncate?: boolean;
24+
clamp?: number;
2425
collapse?: "open" | "closed" | "none";
2526
}
2627
@@ -52,6 +53,34 @@ const element = computed(() => {
5253
}
5354
return "h1";
5455
});
56+
57+
const headingRef = ref<HTMLElement | null>(null);
58+
const isClamped = ref(false);
59+
60+
function checkClamped() {
61+
if (headingRef.value && props.clamp) {
62+
isClamped.value = headingRef.value.scrollHeight > headingRef.value.clientHeight;
63+
} else {
64+
isClamped.value = false;
65+
}
66+
}
67+
68+
const clampTooltip = computed(() => {
69+
if (isClamped.value) {
70+
return headingRef.value?.textContent?.trim() ?? null;
71+
}
72+
return null;
73+
});
74+
75+
const clampStyle = computed(() => {
76+
if (props.clamp) {
77+
return { webkitLineClamp: props.clamp };
78+
}
79+
return undefined;
80+
});
81+
82+
onMounted(checkClamped);
83+
onUpdated(() => nextTick(checkClamped));
5584
</script>
5685

5786
<template>
@@ -63,12 +92,16 @@ const element = computed(() => {
6392
<div v-else class="stripe"></div>
6493
<component
6594
:is="element"
95+
ref="headingRef"
96+
:title="clampTooltip"
6697
:class="[
6798
sizeClass,
6899
props.bold ? 'font-weight-bold' : '',
69100
collapsible ? 'collapsible' : '',
70101
props.truncate ? 'truncate' : '',
102+
props.clamp ? 'clamp' : '',
71103
]"
104+
:style="clampStyle"
72105
@click="$emit('click')">
73106
<slot />
74107
</component>
@@ -77,14 +110,18 @@ const element = computed(() => {
77110
<component
78111
:is="element"
79112
v-else
113+
ref="headingRef"
114+
:title="clampTooltip"
80115
class="heading word-wrap-break"
81116
:class="[
82117
sizeClass,
83118
props.bold ? 'font-weight-bold' : '',
84119
props.inline ? 'inline' : '',
85120
collapsible ? 'collapsible' : '',
86121
props.truncate ? 'truncate' : '',
122+
props.clamp ? 'clamp' : '',
87123
]"
124+
:style="clampStyle"
88125
@click="$emit('click')">
89126
<GButton v-if="collapsible" transparent size="small" icon-only inline>
90127
<FontAwesomeIcon v-if="collapsed" fixed-width :icon="faAngleDoubleDown" />
@@ -100,7 +137,7 @@ const element = computed(() => {
100137
101138
// prettier-ignore
102139
h1, h2, h3, h4, h5, h6 {
103-
&:not(.truncate) {
140+
&:not(.truncate):not(.clamp) {
104141
display: flex;
105142
}
106143
align-items: center;
@@ -116,6 +153,12 @@ h1, h2, h3, h4, h5, h6 {
116153
text-overflow: ellipsis;
117154
white-space: nowrap;
118155
}
156+
157+
&.clamp {
158+
display: -webkit-box;
159+
-webkit-box-orient: vertical;
160+
overflow: hidden;
161+
}
119162
}
120163
121164
.collapsible {

client/src/components/History/Layout/DetailsLayout.vue

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ import { faPen, faSave, faUndo } from "@fortawesome/free-solid-svg-icons";
33
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
44
import { BButton, BFormInput, BFormTextarea } from "bootstrap-vue";
55
import { storeToRefs } from "pinia";
6-
import { computed, ref } from "vue";
6+
import { computed, nextTick, onMounted, ref, watch } from "vue";
77
88
import { useUserStore } from "@/stores/userStore";
99
import l from "@/utils/localization";
1010
1111
import type { DetailsLayoutSummarized } from "./types";
1212
1313
import ClickToEdit from "@/components/Collections/common/ClickToEdit.vue";
14+
import Heading from "@/components/Common/Heading.vue";
1415
import TextSummary from "@/components/Common/TextSummary.vue";
1516
import StatelessTags from "@/components/TagsMultiselect/StatelessTags.vue";
1617
@@ -40,6 +41,8 @@ const userStore = useUserStore();
4041
const { isAnonymous } = storeToRefs(userStore);
4142
4243
const nameRef = ref<HTMLInputElement | null>(null);
44+
const clickToEditRef = ref<InstanceType<typeof ClickToEdit> | null>(null);
45+
const clickToEditClamped = ref(false);
4346
4447
const editing = ref(false);
4548
const textSelected = ref(false);
@@ -104,6 +107,19 @@ function onToggle() {
104107
}
105108
}
106109
110+
function checkClickToEditClamped() {
111+
const el = clickToEditRef.value?.$el;
112+
if (el) {
113+
clickToEditClamped.value = el.scrollHeight > el.clientHeight;
114+
}
115+
}
116+
117+
onMounted(checkClickToEditClamped);
118+
watch(
119+
() => props.name,
120+
() => nextTick(checkClickToEditClamped),
121+
);
122+
107123
function selectText() {
108124
if (!textSelected.value) {
109125
nameRef.value?.select();
@@ -121,17 +137,19 @@ function selectText() {
121137
<template v-if="!summarized && !editing">
122138
<ClickToEdit
123139
v-if="renameable"
140+
ref="clickToEditRef"
124141
v-model="clickToEditName"
142+
v-b-tooltip.hover="clickToEditClamped ? name : ''"
125143
component="h3"
126144
title="..."
127145
data-description="name display"
128146
no-save-on-blur
129-
class="my-2 w-100" />
130-
<h3 v-else class="my-2 w-100">
147+
class="name-display my-2 w-100" />
148+
<Heading v-else h3 :clamp="2" class="my-2 w-100">
131149
{{ props.name || "..." }}
132-
</h3>
150+
</Heading>
133151
</template>
134-
<div v-else style="max-width: 80%">
152+
<div v-else class="overflow-hidden" style="max-width: 80%">
135153
<TextSummary
136154
:description="name"
137155
data-description="name display"
@@ -233,6 +251,14 @@ function selectText() {
233251
</template>
234252

235253
<style lang="scss" scoped>
254+
.name-display :deep(h3),
255+
h3.name-display {
256+
display: -webkit-box;
257+
-webkit-box-orient: vertical;
258+
-webkit-line-clamp: 2;
259+
overflow: hidden;
260+
}
261+
236262
.summarized-details {
237263
margin-left: 0.5rem;
238264
max-width: 15rem;

0 commit comments

Comments
 (0)