Skip to content

Commit 0fd388b

Browse files
committed
Separated the collapsible comparison file select to its own component for ease of reuse in other blocks
1 parent c73cb52 commit 0fd388b

File tree

2 files changed

+189
-106
lines changed

2 files changed

+189
-106
lines changed
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
<template>
2+
<div>
3+
<div class="comparison-header collapsible" :class="{ expanded: isExpanded }">
4+
<font-awesome-icon
5+
:icon="['fas', 'chevron-right']"
6+
fixed-width
7+
class="collapse-arrow"
8+
@click="toggleExpanded"
9+
/>
10+
<span class="comparison-title" @click="toggleExpanded">{{ title }}</span>
11+
</div>
12+
<div
13+
ref="contentContainer"
14+
class="comparison-content-container"
15+
:style="{ 'max-height': maxHeight }"
16+
>
17+
<div class="form-row align-items-center mb-2">
18+
<FileMultiSelectDropdown
19+
v-model="internalFileModel"
20+
:item_id="item_id"
21+
:block_id="block_id"
22+
:extensions="extensions"
23+
:update-block-on-change="false"
24+
:exclude-file-ids="excludeFileIds"
25+
/>
26+
</div>
27+
<div class="form-row mt-2 mb-3">
28+
<button class="btn btn-primary btn-sm" @click="applySelection">
29+
{{ applyButtonText }}
30+
</button>
31+
</div>
32+
</div>
33+
</div>
34+
</template>
35+
36+
<script>
37+
import FileMultiSelectDropdown from "@/components/FileMultiSelectDropdown";
38+
39+
export default {
40+
components: {
41+
FileMultiSelectDropdown,
42+
},
43+
props: {
44+
item_id: {
45+
type: String,
46+
required: true,
47+
},
48+
block_id: {
49+
type: String,
50+
required: true,
51+
},
52+
extensions: {
53+
type: Array,
54+
required: true,
55+
},
56+
excludeFileIds: {
57+
type: Array,
58+
default: () => [],
59+
},
60+
modelValue: {
61+
type: Array,
62+
default: () => [],
63+
},
64+
title: {
65+
type: String,
66+
default: "Comparison Files",
67+
},
68+
applyButtonText: {
69+
type: String,
70+
default: "Apply Comparison Files",
71+
},
72+
initiallyExpanded: {
73+
type: Boolean,
74+
default: false,
75+
},
76+
},
77+
emits: ["update:modelValue", "apply"],
78+
data() {
79+
return {
80+
internalFileModel: [],
81+
maxHeight: "0px",
82+
paddingHeight: 18,
83+
isExpanded: false,
84+
};
85+
},
86+
watch: {
87+
modelValue(newVal) {
88+
this.internalFileModel = newVal.slice();
89+
},
90+
},
91+
mounted() {
92+
// Initialize internal model from prop
93+
this.internalFileModel = this.modelValue.slice();
94+
95+
// Set initial expanded state
96+
this.isExpanded = this.initiallyExpanded;
97+
98+
// Initialize collapse state
99+
var contentContainer = this.$refs.contentContainer;
100+
if (contentContainer) {
101+
if (this.isExpanded) {
102+
this.maxHeight = "none";
103+
contentContainer.style.overflow = "visible";
104+
} else {
105+
this.maxHeight = "0px";
106+
}
107+
108+
contentContainer.addEventListener("transitionend", () => {
109+
if (this.isExpanded) {
110+
this.maxHeight = "none";
111+
}
112+
});
113+
}
114+
},
115+
methods: {
116+
toggleExpanded() {
117+
var content = this.$refs.contentContainer;
118+
if (!this.isExpanded) {
119+
this.maxHeight = content.scrollHeight + 2 * this.paddingHeight + "px";
120+
this.isExpanded = true;
121+
content.style.overflow = "visible";
122+
} else {
123+
content.style.overflow = "hidden";
124+
requestAnimationFrame(() => {
125+
this.maxHeight = content.scrollHeight + "px";
126+
requestAnimationFrame(() => {
127+
this.maxHeight = "0px";
128+
this.isExpanded = false;
129+
});
130+
});
131+
}
132+
},
133+
applySelection() {
134+
this.$emit("update:modelValue", this.internalFileModel.slice());
135+
this.$emit("apply", this.internalFileModel.slice());
136+
},
137+
},
138+
};
139+
</script>
140+
141+
<style scoped>
142+
.comparison-header {
143+
display: flex;
144+
align-items: center;
145+
margin-top: 1rem;
146+
margin-bottom: 0.5rem;
147+
cursor: pointer;
148+
}
149+
150+
.comparison-title {
151+
margin-left: 0.5em;
152+
font-size: 1rem;
153+
font-weight: 500;
154+
color: #004175;
155+
}
156+
157+
.collapse-arrow {
158+
color: #004175;
159+
transition: all 0.4s;
160+
cursor: pointer;
161+
}
162+
163+
.collapse-arrow:hover {
164+
color: #7ca7ca;
165+
}
166+
167+
.expanded .collapse-arrow {
168+
-webkit-transform: rotate(90deg);
169+
-moz-transform: rotate(90deg);
170+
transform: rotate(90deg);
171+
}
172+
173+
.comparison-content-container {
174+
overflow: hidden;
175+
max-height: none;
176+
transition: max-height 0.4s ease-in-out;
177+
}
178+
</style>

webapp/src/components/datablocks/CycleBlock.vue

Lines changed: 11 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -33,36 +33,15 @@
3333
<div v-if="mode === 'multi'" class="form-row mt-2">
3434
<button class="btn btn-primary btn-sm" @click="applyMultiSelect">Apply Selection</button>
3535
</div>
36-
<div class="comparison-header collapsible" :class="{ expanded: comparisonEnabled }">
37-
<font-awesome-icon
38-
:icon="['fas', 'chevron-right']"
39-
fixed-width
40-
class="collapse-arrow"
41-
@click="toggleComparison"
42-
/>
43-
<span class="comparison-title" @click="toggleComparison">Comparison Files</span>
44-
</div>
45-
<div
46-
ref="comparisonContainer"
47-
class="comparison-content-container"
48-
:style="{ 'max-height': comparisonMaxHeight }"
49-
>
50-
<div class="form-row align-items-center mb-2">
51-
<FileMultiSelectDropdown
52-
v-model="comparisonFileModel"
53-
:item_id="item_id"
54-
:block_id="block_id"
55-
:extensions="blockInfo.attributes.accepted_file_extensions"
56-
:update-block-on-change="false"
57-
:exclude-file-ids="file_ids"
58-
/>
59-
</div>
60-
<div class="form-row mt-2 mb-3">
61-
<button class="btn btn-primary btn-sm" @click="applyComparisonFiles">
62-
Apply Comparison Files
63-
</button>
64-
</div>
65-
</div>
36+
<CollapsibleComparisonFileSelect
37+
v-model="comparisonFileModel"
38+
:item_id="item_id"
39+
:block_id="block_id"
40+
:extensions="blockInfo.attributes.accepted_file_extensions"
41+
:exclude-file-ids="file_ids"
42+
:initially-expanded="comparisonFileModel.length > 0"
43+
@apply="applyComparisonFiles"
44+
/>
6645
<div>
6746
<div class="form-row">
6847
<div class="input-group form-inline">
@@ -205,6 +184,7 @@ import DataBlockBase from "@/components/datablocks/DataBlockBase";
205184
import FileSelectDropdown from "@/components/FileSelectDropdown";
206185
import FileMultiSelectDropdown from "@/components/FileMultiSelectDropdown";
207186
import BokehPlot from "@/components/BokehPlot";
187+
import CollapsibleComparisonFileSelect from "@/components/CollapsibleComparisonFileSelect";
208188
209189
import { updateBlockFromServer } from "@/server_fetch_utils.js";
210190
import { createComputedSetterForBlockField } from "@/field_utils.js";
@@ -215,6 +195,7 @@ export default {
215195
FileSelectDropdown,
216196
FileMultiSelectDropdown,
217197
BokehPlot,
198+
CollapsibleComparisonFileSelect,
218199
},
219200
props: {
220201
item_id: {
@@ -236,9 +217,6 @@ export default {
236217
isReplotButtonDisplayed: false,
237218
pending_file_ids: [],
238219
pending_comparison_file_ids: [],
239-
comparisonMaxHeight: "0px",
240-
padding_height: 18,
241-
comparisonEnabled: false,
242220
};
243221
},
244222
computed: {
@@ -313,25 +291,6 @@ export default {
313291
// Initialize pending comparison files from persisted state
314292
if (this.comparison_file_ids && Array.isArray(this.comparison_file_ids)) {
315293
this.pending_comparison_file_ids = this.comparison_file_ids.slice();
316-
// Auto-expand if there are comparison files
317-
this.comparisonEnabled = this.comparison_file_ids.length > 0;
318-
}
319-
320-
// Initialize comparison section collapse state
321-
var comparisonContent = this.$refs.comparisonContainer;
322-
if (comparisonContent) {
323-
if (this.comparisonEnabled) {
324-
this.comparisonMaxHeight = "none";
325-
comparisonContent.style.overflow = "visible";
326-
} else {
327-
this.comparisonMaxHeight = "0px";
328-
}
329-
330-
comparisonContent.addEventListener("transitionend", () => {
331-
if (this.comparisonEnabled) {
332-
this.comparisonMaxHeight = "none";
333-
}
334-
});
335294
}
336295
},
337296
methods: {
@@ -385,23 +344,6 @@ export default {
385344
this.comparison_file_ids = this.pending_comparison_file_ids.slice();
386345
this.updateBlock();
387346
},
388-
toggleComparison() {
389-
var content = this.$refs.comparisonContainer;
390-
if (!this.comparisonEnabled) {
391-
this.comparisonMaxHeight = content.scrollHeight + 2 * this.padding_height + "px";
392-
this.comparisonEnabled = true;
393-
content.style.overflow = "visible";
394-
} else {
395-
content.style.overflow = "hidden";
396-
requestAnimationFrame(() => {
397-
this.comparisonMaxHeight = content.scrollHeight + "px";
398-
requestAnimationFrame(() => {
399-
this.comparisonMaxHeight = "0px";
400-
this.comparisonEnabled = false;
401-
});
402-
});
403-
}
404-
},
405347
updateBlock() {
406348
updateBlockFromServer(
407349
this.item_id,
@@ -445,41 +387,4 @@ export default {
445387
border-bottom: 2px dotted #0c5460;
446388
text-decoration: none;
447389
}
448-
449-
.comparison-header {
450-
display: flex;
451-
align-items: center;
452-
margin-top: 1rem;
453-
margin-bottom: 0.5rem;
454-
cursor: pointer;
455-
}
456-
457-
.comparison-title {
458-
margin-left: 0.5em;
459-
font-size: 1rem;
460-
font-weight: 500;
461-
color: #004175;
462-
}
463-
464-
.collapse-arrow {
465-
color: #004175;
466-
transition: all 0.4s;
467-
cursor: pointer;
468-
}
469-
470-
.collapse-arrow:hover {
471-
color: #7ca7ca;
472-
}
473-
474-
.expanded .collapse-arrow {
475-
-webkit-transform: rotate(90deg);
476-
-moz-transform: rotate(90deg);
477-
transform: rotate(90deg);
478-
}
479-
480-
.comparison-content-container {
481-
overflow: hidden;
482-
max-height: none;
483-
transition: max-height 0.4s ease-in-out;
484-
}
485390
</style>

0 commit comments

Comments
 (0)