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