mirror of
https://github.com/EyeTrackVR/EyeTrackVR-Docs.git
synced 2025-11-04 14:49:44 +08:00
docs: update part list tables, move out of beta
This commit is contained in:
parent
7ed813f90d
commit
5c92fecfc4
@ -46,7 +46,7 @@ const theme = {
|
|||||||
text: "How to Build",
|
text: "How to Build",
|
||||||
collapsible: true,
|
collapsible: true,
|
||||||
items: [
|
items: [
|
||||||
{ text: "Parts List (BETA)", link: "/how_to_build/part_list" },
|
{ text: "Parts List", link: "/how_to_build/part_list" },
|
||||||
{
|
{
|
||||||
text: "Preparing Cameras",
|
text: "Preparing Cameras",
|
||||||
link: "/how_to_build/preparing_cameras"
|
link: "/how_to_build/preparing_cameras"
|
||||||
|
|||||||
@ -12,15 +12,20 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="component in components" :key="component.name">
|
<tr v-for="(component, index) in components" :key="component.name">
|
||||||
<th class="component-col">{{ component.name }}</th>
|
<th class="component-col">{{ component.name }}</th>
|
||||||
<td class="choice-col">
|
<td class="choice-col">
|
||||||
<div v-if="component.choices.length > 1" class="custom-select" @click="toggleDropdown(component)">
|
<div v-if="component.choices.length > 1" class="custom-select" @click="toggleDropdown(component, index)" :ref="`select-${component.name}`">
|
||||||
<div class="selected-option">{{ component.choices[component.selectedChoice].name }}</div>
|
<div class="selected-option">
|
||||||
<div v-if="component.isOpen" class="options">
|
{{ component.choices[component.selectedChoice].name }}
|
||||||
<div v-for="(choice, index) in component.choices"
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="dropdown-arrow" :class="{ 'rotate': component.isOpen }">
|
||||||
:key="index"
|
<polyline points="6 9 12 15 18 9"></polyline>
|
||||||
@click="selectOption(component, index)"
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div v-if="component.isOpen" class="options" :class="{ 'options-above': isOptionsAbove(index) }">
|
||||||
|
<div v-for="(choice, choiceIndex) in component.choices"
|
||||||
|
:key="choiceIndex"
|
||||||
|
@click="selectOption(component, choiceIndex)"
|
||||||
class="option">
|
class="option">
|
||||||
{{ choice.name }}
|
{{ choice.name }}
|
||||||
</div>
|
</div>
|
||||||
@ -36,11 +41,12 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p>Total: ${{ total.toFixed(2) }}</p>
|
<p>Total: ${{ total.toFixed(2) }}</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
tracker: 2,
|
tracker: 2,
|
||||||
components: [
|
components: [
|
||||||
@ -218,13 +224,11 @@ export default {
|
|||||||
selectedChoice: 0,
|
selectedChoice: 0,
|
||||||
isOpen: false
|
isOpen: false
|
||||||
},
|
},
|
||||||
|
|
||||||
// Add other components similarly
|
|
||||||
],
|
],
|
||||||
total: 0,
|
total: 0,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updatePrices() {
|
updatePrices() {
|
||||||
let total = 0;
|
let total = 0;
|
||||||
this.components.forEach(component => {
|
this.components.forEach(component => {
|
||||||
@ -237,54 +241,42 @@ export default {
|
|||||||
});
|
});
|
||||||
this.total = total;
|
this.total = total;
|
||||||
},
|
},
|
||||||
toggleDropdown(component) {
|
toggleDropdown(component, index) {
|
||||||
// Close all other dropdowns
|
|
||||||
this.components.forEach(comp => {
|
this.components.forEach(comp => {
|
||||||
if (comp !== component) {
|
if (comp !== component) {
|
||||||
comp.isOpen = false;
|
comp.isOpen = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Toggle the clicked dropdown
|
|
||||||
component.isOpen = !component.isOpen;
|
component.isOpen = !component.isOpen;
|
||||||
},
|
},
|
||||||
isOptionsAbove(component) {
|
isOptionsAbove(index) {
|
||||||
if (this.$refs[`select-${component.name}`]) {
|
return index >= this.components.length - 2;
|
||||||
const rect = this.$refs[`select-${component.name}`].getBoundingClientRect();
|
|
||||||
const spaceBelow = window.innerHeight - rect.bottom;
|
|
||||||
return spaceBelow < 200 && rect.top > 200;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
},
|
||||||
selectOption(component, index) {
|
selectOption(component, index) {
|
||||||
component.selectedChoice = index;
|
component.selectedChoice = index;
|
||||||
component.isOpen = false; // This line closes the dropdown
|
component.isOpen = false;
|
||||||
this.updatePrices();
|
this.updatePrices();
|
||||||
},
|
},
|
||||||
closeAllDropdowns() {
|
closeAllDropdowns() {
|
||||||
this.components.forEach(component => {
|
this.components.forEach(component => {
|
||||||
component.isOpen = false;
|
component.isOpen = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// Add this new method
|
handleOutsideClick(event) {
|
||||||
handleOutsideClick(event) {
|
if (!event.target.closest('.custom-select')) {
|
||||||
if (!event.target.closest('.custom-select')) {
|
this.closeAllDropdowns();
|
||||||
this.closeAllDropdowns();
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
mounted() {
|
||||||
mounted() {
|
this.updatePrices();
|
||||||
this.updatePrices();
|
document.addEventListener('click', this.handleOutsideClick);
|
||||||
document.addEventListener('click', this.handleOutsideClick);
|
},
|
||||||
},
|
beforeUnmount() {
|
||||||
beforeUnmount() {
|
document.removeEventListener('click', this.handleOutsideClick);
|
||||||
document.removeEventListener('click', this.handleOutsideClick);
|
},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.table-container {
|
.table-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -312,34 +304,82 @@ th, td {
|
|||||||
.cost-all-col { width: 13%; }
|
.cost-all-col { width: 13%; }
|
||||||
.links-col { width: 30%; }
|
.links-col { width: 30%; }
|
||||||
|
|
||||||
.multi-line-select {
|
.custom-select {
|
||||||
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
white-space: normal;
|
cursor: pointer;
|
||||||
word-wrap: break-word;
|
z-index: 1;
|
||||||
height: auto;
|
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
.custom-select:hover,
|
||||||
width: 100%;
|
.custom-select:focus-within {
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-option {
|
||||||
|
border: 1px solid #999;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
box-sizing: border-box;
|
background-color: #666;
|
||||||
white-space: normal;
|
min-height: 30px;
|
||||||
height: auto;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
text-align-last: center;
|
color: #fff;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This targets Webkit browsers like Chrome and Safari */
|
.selected-option:hover {
|
||||||
select option {
|
background-color: #444;
|
||||||
white-space: normal;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This targets Firefox */
|
.options {
|
||||||
select:-moz-focusring {
|
position: absolute;
|
||||||
color: transparent;
|
left: 0;
|
||||||
text-shadow: 0 0 0 #000;
|
right: 0;
|
||||||
|
background-color: #222;
|
||||||
|
border: 1px solid #444;
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
z-index: 1000;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||||||
|
top: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options-above {
|
||||||
|
bottom: 100%;
|
||||||
|
top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option {
|
||||||
|
padding: 5px;
|
||||||
|
border-bottom: 1px solid #444;
|
||||||
|
color: #fff;
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option:hover {
|
||||||
|
background-color: #1e1e20;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-arrow {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
min-width: 16px;
|
||||||
|
min-height: 16px;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-arrow.rotate {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select[aria-expanded="true"] .dropdown-arrow {
|
||||||
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
@ -359,79 +399,4 @@ select:-moz-focusring {
|
|||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-select {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
cursor: pointer;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-select:hover,
|
|
||||||
.custom-select:focus-within {
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected-option {
|
|
||||||
border: 1px solid #444;
|
|
||||||
padding: 5px;
|
|
||||||
background-color: #333;
|
|
||||||
min-height: 30px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
text-align: center;
|
|
||||||
color: #545454;
|
|
||||||
}
|
|
||||||
|
|
||||||
.options {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
background-color: #222;
|
|
||||||
border: 1px solid #444;
|
|
||||||
max-height: 200px;
|
|
||||||
overflow-y: auto;
|
|
||||||
z-index: 1000;
|
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
|
||||||
top: 100%;
|
|
||||||
}
|
|
||||||
.options-above {
|
|
||||||
bottom: 100%;
|
|
||||||
top: auto;
|
|
||||||
}
|
|
||||||
.option {
|
|
||||||
padding: 5px;
|
|
||||||
border-bottom: 1px solid #444;
|
|
||||||
color: #545454;
|
|
||||||
background-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.option:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.option:hover {
|
|
||||||
background-color: #1e1e20;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ensure text is visible in all states */
|
|
||||||
.custom-select, .selected-option, .option {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add some contrast to the selected option */
|
|
||||||
.selected-option {
|
|
||||||
background-color: #1e1e20;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Improve visibility of options */
|
|
||||||
.options {
|
|
||||||
background-color: #222;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add a subtle hover effect */
|
|
||||||
.option:hover {
|
|
||||||
background-color: #555;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
@ -12,15 +12,20 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="component in components" :key="component.name">
|
<tr v-for="(component, index) in components" :key="component.name">
|
||||||
<th class="component-col">{{ component.name }}</th>
|
<th class="component-col">{{ component.name }}</th>
|
||||||
<td class="choice-col">
|
<td class="choice-col">
|
||||||
<div v-if="component.choices.length > 1" class="custom-select" @click="toggleDropdown(component)">
|
<div v-if="component.choices.length > 1" class="custom-select" @click="toggleDropdown(component, index)" :ref="`select-${component.name}`">
|
||||||
<div class="selected-option">{{ component.choices[component.selectedChoice].name }}</div>
|
<div class="selected-option">
|
||||||
<div v-if="component.isOpen" class="options">
|
{{ component.choices[component.selectedChoice].name }}
|
||||||
<div v-for="(choice, index) in component.choices"
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="dropdown-arrow" :class="{ 'rotate': component.isOpen }">
|
||||||
:key="index"
|
<polyline points="6 9 12 15 18 9"></polyline>
|
||||||
@click="selectOption(component, index)"
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div v-if="component.isOpen" class="options" :class="{ 'options-above': isOptionsAbove(index) }">
|
||||||
|
<div v-for="(choice, choiceIndex) in component.choices"
|
||||||
|
:key="choiceIndex"
|
||||||
|
@click="selectOption(component, choiceIndex)"
|
||||||
class="option">
|
class="option">
|
||||||
{{ choice.name }}
|
{{ choice.name }}
|
||||||
</div>
|
</div>
|
||||||
@ -36,14 +41,15 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p>Total: ${{ total.toFixed(2) }}</p>
|
<p>Total: ${{ total.toFixed(2) }}</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
tracker: 2,
|
tracker: 2,
|
||||||
components: [
|
components: [
|
||||||
{
|
{
|
||||||
name: 'Type A USB Breakout',
|
name: 'Type A USB Breakout',
|
||||||
choices: [
|
choices: [
|
||||||
@ -313,12 +319,11 @@ export default {
|
|||||||
selectedChoice: 0,
|
selectedChoice: 0,
|
||||||
isOpen: false
|
isOpen: false
|
||||||
},
|
},
|
||||||
// Add other components similarly
|
|
||||||
],
|
],
|
||||||
total: 0,
|
total: 0,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updatePrices() {
|
updatePrices() {
|
||||||
let total = 0;
|
let total = 0;
|
||||||
this.components.forEach(component => {
|
this.components.forEach(component => {
|
||||||
@ -331,54 +336,42 @@ export default {
|
|||||||
});
|
});
|
||||||
this.total = total;
|
this.total = total;
|
||||||
},
|
},
|
||||||
toggleDropdown(component) {
|
toggleDropdown(component, index) {
|
||||||
// Close all other dropdowns
|
|
||||||
this.components.forEach(comp => {
|
this.components.forEach(comp => {
|
||||||
if (comp !== component) {
|
if (comp !== component) {
|
||||||
comp.isOpen = false;
|
comp.isOpen = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Toggle the clicked dropdown
|
|
||||||
component.isOpen = !component.isOpen;
|
component.isOpen = !component.isOpen;
|
||||||
},
|
},
|
||||||
isOptionsAbove(component) {
|
isOptionsAbove(index) {
|
||||||
if (this.$refs[`select-${component.name}`]) {
|
return index >= this.components.length - 2;
|
||||||
const rect = this.$refs[`select-${component.name}`].getBoundingClientRect();
|
|
||||||
const spaceBelow = window.innerHeight - rect.bottom;
|
|
||||||
return spaceBelow < 200 && rect.top > 200;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
},
|
||||||
selectOption(component, index) {
|
selectOption(component, index) {
|
||||||
component.selectedChoice = index;
|
component.selectedChoice = index;
|
||||||
component.isOpen = false; // This line closes the dropdown
|
component.isOpen = false;
|
||||||
this.updatePrices();
|
this.updatePrices();
|
||||||
},
|
},
|
||||||
closeAllDropdowns() {
|
closeAllDropdowns() {
|
||||||
this.components.forEach(component => {
|
this.components.forEach(component => {
|
||||||
component.isOpen = false;
|
component.isOpen = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// Add this new method
|
handleOutsideClick(event) {
|
||||||
handleOutsideClick(event) {
|
if (!event.target.closest('.custom-select')) {
|
||||||
if (!event.target.closest('.custom-select')) {
|
this.closeAllDropdowns();
|
||||||
this.closeAllDropdowns();
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
mounted() {
|
||||||
mounted() {
|
this.updatePrices();
|
||||||
this.updatePrices();
|
document.addEventListener('click', this.handleOutsideClick);
|
||||||
document.addEventListener('click', this.handleOutsideClick);
|
},
|
||||||
},
|
beforeUnmount() {
|
||||||
beforeUnmount() {
|
document.removeEventListener('click', this.handleOutsideClick);
|
||||||
document.removeEventListener('click', this.handleOutsideClick);
|
},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.table-container {
|
.table-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -406,34 +399,82 @@ th, td {
|
|||||||
.cost-all-col { width: 13%; }
|
.cost-all-col { width: 13%; }
|
||||||
.links-col { width: 30%; }
|
.links-col { width: 30%; }
|
||||||
|
|
||||||
.multi-line-select {
|
.custom-select {
|
||||||
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
white-space: normal;
|
cursor: pointer;
|
||||||
word-wrap: break-word;
|
z-index: 1;
|
||||||
height: auto;
|
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
.custom-select:hover,
|
||||||
width: 100%;
|
.custom-select:focus-within {
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-option {
|
||||||
|
border: 1px solid #999;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
box-sizing: border-box;
|
background-color: #666;
|
||||||
white-space: normal;
|
min-height: 30px;
|
||||||
height: auto;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
text-align-last: center;
|
color: #fff;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This targets Webkit browsers like Chrome and Safari */
|
.selected-option:hover {
|
||||||
select option {
|
background-color: #444;
|
||||||
white-space: normal;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This targets Firefox */
|
.options {
|
||||||
select:-moz-focusring {
|
position: absolute;
|
||||||
color: transparent;
|
left: 0;
|
||||||
text-shadow: 0 0 0 #000;
|
right: 0;
|
||||||
|
background-color: #222;
|
||||||
|
border: 1px solid #444;
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
z-index: 1000;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||||||
|
top: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options-above {
|
||||||
|
bottom: 100%;
|
||||||
|
top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option {
|
||||||
|
padding: 5px;
|
||||||
|
border-bottom: 1px solid #444;
|
||||||
|
color: #fff;
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option:hover {
|
||||||
|
background-color: #1e1e20;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-arrow {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
min-width: 16px;
|
||||||
|
min-height: 16px;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-arrow.rotate {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select[aria-expanded="true"] .dropdown-arrow {
|
||||||
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
@ -453,79 +494,4 @@ select:-moz-focusring {
|
|||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-select {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
cursor: pointer;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-select:hover,
|
|
||||||
.custom-select:focus-within {
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected-option {
|
|
||||||
border: 1px solid #444;
|
|
||||||
padding: 5px;
|
|
||||||
background-color: #333;
|
|
||||||
min-height: 30px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
text-align: center;
|
|
||||||
color: #545454;
|
|
||||||
}
|
|
||||||
|
|
||||||
.options {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
background-color: #222;
|
|
||||||
border: 1px solid #444;
|
|
||||||
max-height: 200px;
|
|
||||||
overflow-y: auto;
|
|
||||||
z-index: 1000;
|
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
|
||||||
top: 100%;
|
|
||||||
}
|
|
||||||
.options-above {
|
|
||||||
bottom: 100%;
|
|
||||||
top: auto;
|
|
||||||
}
|
|
||||||
.option {
|
|
||||||
padding: 5px;
|
|
||||||
border-bottom: 1px solid #444;
|
|
||||||
color: #545454;
|
|
||||||
background-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.option:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.option:hover {
|
|
||||||
background-color: #1e1e20;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ensure text is visible in all states */
|
|
||||||
.custom-select, .selected-option, .option {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add some contrast to the selected option */
|
|
||||||
.selected-option {
|
|
||||||
background-color: #1e1e20;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Improve visibility of options */
|
|
||||||
.options {
|
|
||||||
background-color: #222;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add a subtle hover effect */
|
|
||||||
.option:hover {
|
|
||||||
background-color: #555;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
@ -24,28 +24,7 @@ There are two primary categories of trackers supported: wireless and wired.
|
|||||||
It's super important that you really look at all your options and consider your use case before making a decision.
|
It's super important that you really look at all your options and consider your use case before making a decision.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## Option 1: Wireless over WiFi 2.4 GHz
|
## Option 1: Wired over USB Serial
|
||||||
### Wireless Capable ESPs:
|
|
||||||
|
|
||||||
* Seeed Studio XIAO Sense
|
|
||||||
* ESP32-CAM
|
|
||||||
* FREENOVE ESP32-S3-WROOM CAM Board
|
|
||||||
|
|
||||||
This offers a good starting point as the ESP32 CAM boards are cheaper than wired capable trackers.
|
|
||||||
However, they can be a bit more temperamental and if you are using a wired headset, they don't really make sense.
|
|
||||||
|
|
||||||
**Pros:**
|
|
||||||
- Not as experimental
|
|
||||||
- Less cables (power only)
|
|
||||||
- No issues with USB hubs
|
|
||||||
|
|
||||||
**Cons:**
|
|
||||||
- Requires two external antennas for optimal streaming quality
|
|
||||||
- Requires WiFi 2.4 GHz router or access point in reasonable proximity
|
|
||||||
- High risk of radio interference with FBT and other WiFi 2.4 GHz devices
|
|
||||||
- ESP32-CAM uses more power and heats up due to radio power needs
|
|
||||||
|
|
||||||
## Option 2: Wired over USB Serial (in beta)
|
|
||||||
### Wired Capable ESPs:
|
### Wired Capable ESPs:
|
||||||
* Seeed Studio XIAO Sense
|
* Seeed Studio XIAO Sense
|
||||||
* FREENOVE ESP32-S3-WROOM CAM Board
|
* FREENOVE ESP32-S3-WROOM CAM Board
|
||||||
@ -55,10 +34,33 @@ This is our latest setup recommended for users with wired headsets such as the V
|
|||||||
**Pros:**
|
**Pros:**
|
||||||
- Much better performance and higher framerate up to 70 FPS with lower latency
|
- Much better performance and higher framerate up to 70 FPS with lower latency
|
||||||
- No conflicts with advanced FBT setups such as Vive or Tundra Trackers
|
- No conflicts with advanced FBT setups such as Vive or Tundra Trackers
|
||||||
- Can be wireless or wired
|
- Can be wireless *or* wired
|
||||||
- Less soldering required
|
- Less soldering required compared to ESP32-CAM boards
|
||||||
|
|
||||||
**Cons:**
|
**Cons:**
|
||||||
- Requires USB port i.e. on Valve Index and USB hub mounted on your headset
|
- Requires USB port i.e. on Valve Index and USB hub mounted on your headset
|
||||||
- Can be slightly more costly
|
- Can be slightly more costly
|
||||||
- When in use with a Vive Facial Tracker, an MTT USB hub is required
|
- When in use with a Vive Facial Tracker or other bandwidth sensitive components, a MTT USB hub is required
|
||||||
|
- Requires beta app versions until the v2.0 app is released
|
||||||
|
|
||||||
|
## Option 2: Wireless over WiFi 2.4 GHz
|
||||||
|
### Wireless Capable ESPs:
|
||||||
|
|
||||||
|
* Seeed Studio XIAO Sense
|
||||||
|
* ESP32-CAM
|
||||||
|
* FREENOVE ESP32-S3-WROOM CAM Board
|
||||||
|
|
||||||
|
This offers a good starting point as the ESP32-CAM boards are cheaper than wired capable trackers.
|
||||||
|
However, they can be a bit more temperamental and if you are using a wired headset, they don't really make sense.
|
||||||
|
|
||||||
|
**Pros:**
|
||||||
|
- Less cables (power only)
|
||||||
|
- No issues with USB hubs
|
||||||
|
- Easier to flash
|
||||||
|
- Less power draw
|
||||||
|
|
||||||
|
**Cons:**
|
||||||
|
- Requires two external antennas for optimal streaming quality
|
||||||
|
- Requires WiFi 2.4 GHz router or access point in reasonable proximity
|
||||||
|
- High risk of radio interference with FBT and other WiFi 2.4 GHz devices
|
||||||
|
- Uses more power and heats up due to radio power needs
|
||||||
Loading…
Reference in New Issue
Block a user