Skip to content

Commit c49c743

Browse files
committed
feat: part list table rework
1 parent ba76897 commit c49c743

3 files changed

Lines changed: 455 additions & 51 deletions

File tree

vitepress/docs/src/pages/how_to_build/DiyTable.vue

Lines changed: 220 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,31 @@
11
<template>
2-
<div>
2+
<div class="table-container">
33
<table>
44
<thead>
55
<tr>
6-
<th>Component</th>
7-
<th>Choice</th>
8-
<th>Amount</th>
9-
<th>Cost</th>
10-
<th>Cost All</th>
11-
<th>Links</th>
6+
<th class="component-col">Component</th>
7+
<th class="choice-col">Choice</th>
8+
<th class="amount-col">Amount</th>
9+
<th class="cost-col">Cost</th>
10+
<th class="cost-all-col">Cost All</th>
11+
<th class="links-col">Links</th>
1212
</tr>
1313
</thead>
1414
<tbody>
1515
<tr v-for="component in components" :key="component.name">
16-
<th>{{ component.name }}</th>
17-
<td>
18-
<select v-if="component.choices.length > 1" v-model="component.selectedChoice" @change="updatePrices">
19-
<option v-for="(choice, index) in component.choices" :key="index" :value="index">{{ choice.name }}</option>
20-
</select>
16+
<th class="component-col">{{ component.name }}</th>
17+
<td class="choice-col">
18+
<div v-if="component.choices.length > 1" class="custom-select" @click="toggleDropdown(component)">
19+
<div class="selected-option">{{ component.choices[component.selectedChoice].name }}</div>
20+
<div v-if="component.isOpen" class="options">
21+
<div v-for="(choice, index) in component.choices"
22+
:key="index"
23+
@click="selectOption(component, index)"
24+
class="option">
25+
{{ choice.name }}
26+
</div>
27+
</div>
28+
</div>
2129
<span v-else>{{ component.choices[0].name }}</span>
2230
</td>
2331
<td>{{ component.selectedChoice ? component.choices[component.selectedChoice].amount(tracker) : 0 }}</td>
@@ -71,7 +79,8 @@ export default {
7179
7280
7381
],
74-
selectedChoice: 1
82+
selectedChoice: 1,
83+
isOpen: false
7584
},
7685
{
7786
name: 'Camera',
@@ -100,7 +109,8 @@ export default {
100109
}
101110
102111
],
103-
selectedChoice: 1
112+
selectedChoice: 1,
113+
isOpen: false
104114
},
105115
{
106116
name: 'IR LEDs',
@@ -134,7 +144,8 @@ export default {
134144
links: '<a href="https://store.eyetrackvr.dev/products/v4-1-lite-diy-led-kit">ETVR Store</a> Must assemble yourself'
135145
},
136146
],
137-
selectedChoice: 2
147+
selectedChoice: 2,
148+
isOpen: false
138149
},
139150
{
140151
name: 'USB Hub',
@@ -175,7 +186,8 @@ export default {
175186
links: '<a href="https://www.amazon.com/uni-Charging-Thunderbolt-Aluminum-Pixelbook/dp/B08SVZFFPP/">Amazon</a> Great for Quest due to power passthrough'
176187
},
177188
],
178-
selectedChoice: 1
189+
selectedChoice: 1,
190+
isOpen: false
179191
},
180192
{
181193
name: '3D Printed Mounts',
@@ -195,7 +207,8 @@ export default {
195207
links: '<a href="https://jlc3dp.com/3d-printing-quote">JLC3DP</a> '
196208
},
197209
],
198-
selectedChoice: 0
210+
selectedChoice: 0,
211+
isOpen: false
199212
},
200213
201214
@@ -217,18 +230,201 @@ export default {
217230
});
218231
this.total = total;
219232
},
220-
switchSelect(event, component) {
221-
this.$set(component, 'selectedChoice', event.target.value);
233+
toggleDropdown(component) {
234+
// Close all other dropdowns
235+
this.components.forEach(comp => {
236+
if (comp !== component) {
237+
comp.isOpen = false;
238+
}
239+
});
240+
// Toggle the clicked dropdown
241+
component.isOpen = !component.isOpen;
242+
},
243+
isOptionsAbove(component) {
244+
if (this.$refs[`select-${component.name}`]) {
245+
const rect = this.$refs[`select-${component.name}`].getBoundingClientRect();
246+
const spaceBelow = window.innerHeight - rect.bottom;
247+
return spaceBelow < 200 && rect.top > 200;
248+
}
249+
return false;
250+
},
251+
selectOption(component, index) {
252+
component.selectedChoice = index;
253+
component.isOpen = false; // This line closes the dropdown
222254
this.updatePrices();
255+
},
256+
closeAllDropdowns() {
257+
this.components.forEach(component => {
258+
component.isOpen = false;
259+
});
260+
},
261+
// Add this new method
262+
handleOutsideClick(event) {
263+
if (!event.target.closest('.custom-select')) {
264+
this.closeAllDropdowns();
223265
}
266+
}
224267
},
225-
mounted() {
226-
this.updatePrices();
227-
},
268+
mounted() {
269+
this.updatePrices();
270+
document.addEventListener('click', this.handleOutsideClick);
271+
},
272+
beforeUnmount() {
273+
document.removeEventListener('click', this.handleOutsideClick);
274+
},
228275
};
229276
</script>
277+
278+
279+
280+
230281
<style scoped>
231-
/* Your styles here */
232-
</style>
282+
.table-container {
283+
width: 100%;
284+
overflow-x: auto;
285+
}
286+
287+
table {
288+
width: 100%;
289+
border-collapse: collapse;
290+
table-layout: fixed;
291+
}
292+
293+
th, td {
294+
padding: 5px;
295+
border: 1px solid #ddd;
296+
word-wrap: break-word;
297+
vertical-align: middle;
298+
text-align: center;
299+
}
300+
301+
.component-col { width: 15%; }
302+
.choice-col { width: 25%; }
303+
.amount-col { width: 7%; }
304+
.cost-col { width: 10%; }
305+
.cost-all-col { width: 13%; }
306+
.links-col { width: 30%; }
307+
308+
.multi-line-select {
309+
width: 100%;
310+
white-space: normal;
311+
word-wrap: break-word;
312+
height: auto;
313+
text-align: center;
314+
}
315+
316+
select {
317+
width: 100%;
318+
padding: 5px;
319+
box-sizing: border-box;
320+
white-space: normal;
321+
height: auto;
322+
text-align: center;
323+
text-align-last: center;
324+
}
325+
326+
/* This targets Webkit browsers like Chrome and Safari */
327+
select option {
328+
white-space: normal;
329+
word-wrap: break-word;
330+
}
331+
332+
/* This targets Firefox */
333+
select:-moz-focusring {
334+
color: transparent;
335+
text-shadow: 0 0 0 #000;
336+
}
337+
338+
@media (max-width: 768px) {
339+
.table-container {
340+
overflow-x: auto;
341+
}
342+
343+
table {
344+
table-layout: auto;
345+
}
346+
347+
th, td {
348+
white-space: normal;
349+
}
350+
351+
.component-col, .choice-col, .amount-col, .cost-col, .cost-all-col, .links-col {
352+
width: auto;
353+
}
354+
}
355+
356+
.custom-select {
357+
position: relative;
358+
width: 100%;
359+
cursor: pointer;
360+
z-index: 1;
361+
}
362+
363+
.custom-select:hover,
364+
.custom-select:focus-within {
365+
z-index: 2;
366+
}
367+
368+
.selected-option {
369+
border: 1px solid #444;
370+
padding: 5px;
371+
background-color: #333;
372+
min-height: 30px;
373+
display: flex;
374+
align-items: center;
375+
justify-content: center;
376+
text-align: center;
377+
color: #545454;
378+
}
379+
380+
.options {
381+
position: absolute;
382+
left: 0;
383+
right: 0;
384+
background-color: #222;
385+
border: 1px solid #444;
386+
max-height: 200px;
387+
overflow-y: auto;
388+
z-index: 1000;
389+
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
390+
top: 100%;
391+
}
392+
.options-above {
393+
bottom: 100%;
394+
top: auto;
395+
}
396+
.option {
397+
padding: 5px;
398+
border-bottom: 1px solid #444;
399+
color: #545454;
400+
background-color: #333;
401+
}
402+
403+
.option:last-child {
404+
border-bottom: none;
405+
}
406+
407+
.option:hover {
408+
background-color: #1e1e20;
409+
}
410+
411+
/* Ensure text is visible in all states */
412+
.custom-select, .selected-option, .option {
413+
color: #fff;
414+
}
415+
416+
/* Add some contrast to the selected option */
417+
.selected-option {
418+
background-color: #1e1e20;
419+
}
233420
421+
/* Improve visibility of options */
422+
.options {
423+
background-color: #222;
424+
}
234425
426+
/* Add a subtle hover effect */
427+
.option:hover {
428+
background-color: #555;
429+
}
430+
</style>

0 commit comments

Comments
 (0)