ADD: ValueCards

This commit is contained in:
Robert Kossessa
2024-03-09 17:59:34 +01:00
parent 84e4ff05b7
commit 1ed8284996
9 changed files with 287 additions and 95 deletions

View File

@@ -1,6 +1,6 @@
<template>
<Collapsible v-model:open="collapse" :default-open="true">
<div class="flex h-12 w-full bg-zinc-900">
<div class="flex h-12 w-full border-b border-zinc-800 bg-zinc-900 hover:bg-zinc-800">
<div
class="flex flex-1 items-center px-4"
:class="{ 'cursor-pointer hover:bg-zinc-800': showToggle }"
@@ -17,7 +17,7 @@
</div>
<CollapsibleTrigger
v-if="foldable"
class="flex aspect-square h-12 items-center justify-center hover:bg-zinc-800"
class="flex aspect-square h-12 items-center justify-center"
>
<ChevronLeft class="chevrot mt-0.5 size-4 text-muted-foreground transition-transform" />
</CollapsibleTrigger>

View File

@@ -15,34 +15,41 @@
variant="outline"
role="combobox"
:aria-expanded="open"
class="my-2 w-full justify-between"
class="my-2 w-full min-w-0 justify-between"
>
<ScrambleText :text="value ? actionOptions[value].label : 'Select an action...'" />
<ScrambleText
class="overflow-hidden text-ellipsis text-nowrap"
:text="
inputValue ? actionTypeOptions[inputValue].label : 'Select an action type...'
"
/>
<ChevronsUpDown class="ml-2 size-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent class="p-0" :style="{ width: `${width * 1.125}px` }">
<PopoverContent class="p-0" :style="{ width: `${width + 35}px` }">
<Command>
<CommandInput class="h-9" placeholder="Search actions..." />
<CommandInput class="h-9" placeholder="Search action types..." />
<CommandEmpty>
<ScrambleText scramble-on-mount text="No actions found." />
<ScrambleText scramble-on-mount text="No action types found." />
</CommandEmpty>
<CommandList>
<CommandGroup>
<CommandItem
v-for="(action, key) in actionOptions"
v-for="(actionType, key) in actionTypeOptions"
:key="key"
:value="action"
:value="actionType"
@select="
() => {
value = key
inputValue = key
open = false
}
"
>
{{ action.label }}
{{ actionType.label }}
<Check
:class="cn('ml-auto h-4 w-4', value === key ? 'opacity-100' : 'opacity-0')"
:class="
cn('ml-auto h-4 w-4', inputValue === key ? 'opacity-100' : 'opacity-0')
"
/>
</CommandItem>
</CommandGroup>
@@ -70,7 +77,11 @@
</div>
</div>
<Separator />
<component :is="actionOptions[value]?.component ? actionOptions[value]?.component : WIP" />
<component
:is="
actionTypeOptions[inputValue]?.component ? actionTypeOptions[inputValue]?.component : WIP
"
/>
</div>
</template>
@@ -102,7 +113,7 @@ defineProps({
}
})
const actionOptions = ref({
const actionTypeOptions = ref({
sendKey: { label: 'Press Key or Combination', component: SendKeyAction },
sendString: { label: 'Type a String', component: SendStringAction },
sendMouse: { label: 'Move, Scroll or Click', component: 'SendMouseAction' },
@@ -110,9 +121,6 @@ const actionOptions = ref({
sendMidi: { label: 'Send a MIDI Message', component: 'SendMidiAction' },
sendOsc: { label: 'Send an OSC Message', component: 'SendOscAction' },
sendSerial: { label: 'Send a Serial Message', component: 'SendSerialAction' },
controlMedia: { label: 'Control Media Playback', component: 'ControlMediaAction' },
controlSystem: { label: 'Control your OS', component: 'ControlSystemAction' },
runProgram: { label: 'Start a Program', component: 'RunProgramAction' },
changeProfile: { label: 'Change Device Profile', component: 'ChangeProfileAction' }
})
@@ -120,6 +128,6 @@ const comboboxButton = ref(null)
const { width } = useElementSize(comboboxButton)
const open = ref(false)
const value = ref('')
const inputValue = ref('')
const confirmDelete = ref(false)
</script>

View File

@@ -1,5 +1,5 @@
<template>
<div class="flex flex-col gap-2">
<div class="flex flex-col" :class="{ 'gap-2': actions.length }">
<draggable
key="actionsDraggable"
class="flex flex-col gap-2"

View File

@@ -2,7 +2,7 @@
<div class="flex flex-col p-4">
<Button
class="flex-1"
:class="{ 'bg-orange-600 hover:bg-orange-500': isCapturing }"
:class="{ 'bg-orange-700 hover:bg-orange-600': isCapturing }"
@click="toggleCapture"
>
{{ isCapturing ? 'Capturing Keyboard Input' : 'Capture Keyboard Input' }}

View File

@@ -23,7 +23,7 @@ import { PanelBottomClose, PanelBottomOpen, Clock2 } from 'lucide-vue-next'
import ConfigSection from '@renderer/components/common/ConfigSection.vue'
import { useStore } from '@renderer/store'
import { ref } from 'vue'
import ActionGroup from '../actions/ActionGroup.vue'
import ActionGroup from '@renderer/components/config/actions/ActionGroup.vue'
const store = useStore()
const actionsPressed = ref([

View File

@@ -1,82 +1,23 @@
<template>
<ConfigSection title="Knob Mapping" :icon-component="PlusCircle">
<div class="my-4 px-8">
<span class="font-mono text-sm text-muted-foreground">Control:</span>
<Popover v-model:open="open">
<PopoverTrigger as-child>
<Button
ref="comboboxButton"
variant="outline"
role="combobox"
:aria-expanded="open"
class="my-2 w-full justify-between"
>
<ScrambleText :text="value ? knobMappingOptions[value] : 'Select an action...'" />
<ChevronsUpDown class="ml-2 size-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent class="p-0" :style="{ width: $refs.comboboxButton?.$el.offsetWidth }">
<Command>
<CommandInput class="h-9" placeholder="Search actions..." />
<CommandEmpty>
<ScrambleText scramble-on-mount text="No actions found." />
</CommandEmpty>
<CommandList>
<CommandGroup>
<CommandItem
v-for="(action, key) in knobMappingOptions"
:key="key"
:value="action"
@select="
() => {
value = key
open = false
}
"
>
{{ action }}
<Check
:class="cn('ml-auto h-4 w-4', value === key ? 'opacity-100' : 'opacity-0')"
/>
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
</div>
<WIP />
<ConfigSection title="Knob Values" :icon-component="PlusCircle">
<template #title>
<span class="text-zinc-500">&nbsp;({{ values.length }})</span></template
>
<ValueGroup :values="values" class="p-2" />
</ConfigSection>
</template>
<script setup>
import { PlusCircle, ChevronsUpDown, Check } from 'lucide-vue-next'
import { PlusCircle } from 'lucide-vue-next'
import ConfigSection from '@renderer/components/common/ConfigSection.vue'
import WIP from '@renderer/components/WIP.vue'
import { Popover, PopoverTrigger, PopoverContent } from '@renderer/components/ui/popover'
import { Button } from '@renderer/components/ui/button'
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList
} from '@renderer/components/ui/command'
import { ref } from 'vue'
import { cn } from '@renderer/lib/utils'
import ScrambleText from '@renderer/components/common/ScrambleText.vue'
import ValueGroup from '@renderer/components/config/values/ValueGroup.vue'
const knobMappingOptions = ref({
sendKey: 'Send a Key for each Step',
controlMidi: 'Control a MIDI Value',
controlOsc: 'Control an OSC Value',
controlVolume: 'Control your OS Volume',
moveMouse: 'Move the Mouse',
scrollMouse: 'Scroll the Mouse'
})
const comboboxButton = ref(null)
const open = ref(false)
const value = ref('')
const values = ref([
{
id: '1'
},
{
id: '2'
}
])
</script>

View File

@@ -0,0 +1,33 @@
<template>
<ConfigSection title="Every Step" :icon-component="CircleDashed">
<template #title>
<span class="text-zinc-500">&nbsp;({{ actionsEvery.length }})</span></template
>
<ActionGroup :actions="actionsEvery" class="p-2" />
</ConfigSection>
<ConfigSection title="Clockwise step" :icon-component="RotateCw">
<template #title>
<span class="text-zinc-500">&nbsp;({{ actionsCw.length }})</span></template
>
<ActionGroup :actions="actionsCw" class="p-2" />
</ConfigSection>
<ConfigSection title="Counterclockwise Step" :icon-component="RotateCcw">
<template #title>
<span class="text-zinc-500">&nbsp;({{ actionsCcw.length }})</span></template
>
<ActionGroup :actions="actionsCcw" class="p-2" />
</ConfigSection>
</template>
<script setup lang="ts">
import ConfigSection from '@renderer/components/common/ConfigSection.vue'
import ActionGroup from '@renderer/components/config/actions/ActionGroup.vue'
import { RotateCw, RotateCcw, CircleDashed } from 'lucide-vue-next'
import { ref } from 'vue'
const actionsEvery = ref([
{
id: '1'
}
])
const actionsCw = ref([])
const actionsCcw = ref([])
</script>

View File

@@ -0,0 +1,168 @@
<template>
<div class="overflow-hidden rounded-lg border border-zinc-800 bg-zinc-900/50">
<div class="p-4">
<span class="font-mono text-sm text-muted-foreground"
>Value{{ valueIndex ? ` ${valueIndex}` : '' }}:</span
>
<span class="float-end mx-2 w-4 cursor-grab">
<GripHorizontal class="value-handle mb-0.5 inline-block size-4 text-zinc-600" />
</span>
<div class="flex items-center gap-2">
<Popover v-model:open="open">
<PopoverTrigger as-child>
<Button
ref="comboboxButton"
variant="outline"
role="combobox"
:aria-expanded="open"
class="my-2 w-full min-w-0 justify-between"
>
<ScrambleText
class="overflow-hidden text-ellipsis text-nowrap"
:text="inputValue ? valueTypeOptions[inputValue].label : 'Select a value type...'"
/>
<ChevronsUpDown class="ml-2 size-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent class="p-0" :style="{ width: `${width + 35}px` }">
<Command>
<CommandInput class="h-9" placeholder="Search value types..." />
<CommandEmpty>
<ScrambleText scramble-on-mount text="No value types found." />
</CommandEmpty>
<CommandList>
<CommandGroup>
<CommandItem
v-for="(valueType, key) in valueTypeOptions"
:key="key"
:value="valueType"
@select="
() => {
inputValue = key
open = false
}
"
>
{{ valueType.label }}
<Check
:class="
cn('ml-auto h-4 w-4', inputValue === key ? 'opacity-100' : 'opacity-0')
"
/>
</CommandItem>
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
<Button
v-if="!confirmDelete"
class="aspect-square border border-zinc-800 bg-transparent p-1 text-muted-foreground hover:bg-orange-900 hover:text-zinc-100"
@click="confirmDelete = true"
><Trash2 class="size-4"
/></Button>
<template v-else>
<Button
class="aspect-square bg-orange-950 p-1 text-zinc-200 hover:bg-orange-900 hover:text-zinc-100"
><Check class="size-4"
/></Button>
<Button
class="aspect-square border border-zinc-800 bg-transparent p-1 text-zinc-200 hover:bg-zinc-800 hover:text-zinc-100"
@click="confirmDelete = false"
><X class="size-4"
/></Button>
</template>
</div>
<span class="font-mono text-sm text-muted-foreground">Conditions:</span>
<div class="flex gap-2 py-2">
<Button
v-for="(condition, key) in conditions"
:key="key"
class="font-heading flex flex-1 basis-1/4 items-center justify-center"
:class="{
'border border-zinc-200 bg-zinc-300': condition === true,
'border border-zinc-800 bg-transparent text-muted-foreground hover:border-zinc-700 hover:bg-zinc-800 hover:text-zinc-300':
condition === false
}"
@click="cycleCondition(key)"
>
<span class="mr-0.5">{{ key.toUpperCase() }}:</span>
<PanelBottomClose v-if="condition === true" class="size-4" />
<PanelBottomOpen v-else-if="condition === false" class="size-4" />
<HelpCircle v-else class="size-4" />
</Button>
</div>
</div>
<Separator />
<component
:is="valueTypeOptions[inputValue]?.component ? valueTypeOptions[inputValue]?.component : WIP"
/>
</div>
</template>
<script setup lang="ts">
import WIP from '@renderer/components/WIP.vue'
import { Popover, PopoverTrigger, PopoverContent } from '@renderer/components/ui/popover'
import { Button } from '@renderer/components/ui/button'
import { Separator } from '@renderer/components/ui/separator'
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList
} from '@renderer/components/ui/command'
import { ref } from 'vue'
import { cn } from '@renderer/lib/utils'
import ScrambleText from '@renderer/components/common/ScrambleText.vue'
import {
ChevronsUpDown,
Check,
GripHorizontal,
Trash2,
X,
PanelBottomClose,
PanelBottomOpen,
HelpCircle
} from 'lucide-vue-next'
import { useElementSize } from '@vueuse/core'
import TriggerActionsValue from '@renderer/components/config/values/TriggerActionsValue.vue'
defineProps({
valueIndex: {
type: Number,
required: false
}
})
const valueTypeOptions = ref({
controlMouse: { label: 'Move or Scroll the Mouse', component: 'ControlMouseValue' },
controlGamepad: { label: 'Control a Gamepad Axis', component: 'ControlGamepadValue' },
controlMidi: { label: 'Control a MIDI Value', component: 'ControlMidiValue' },
controlOsc: { label: 'Control an OSC Value', component: 'ControlOscValue' },
controlSerial: { label: 'Control a Value over Serial', component: 'ControlSerialValue' },
controlProfile: { label: 'Switch Device Profiles', component: 'ControlProfileValue' },
triggerActions: { label: 'Trigger Actions on Rotation', component: TriggerActionsValue }
})
const conditions = ref({
a: true,
b: false,
c: false,
d: false
})
const cycleCondition = (key: string) => {
const condition = conditions.value[key]
if (condition === true) conditions.value[key] = false
else conditions.value[key] = true
}
const comboboxButton = ref(null)
const { width } = useElementSize(comboboxButton)
const open = ref(false)
const inputValue = ref('')
const confirmDelete = ref(false)
</script>

View File

@@ -0,0 +1,42 @@
<template>
<div class="flex flex-col" :class="{ 'gap-2': values.length }">
<draggable
key="valuesDraggable"
class="flex flex-col gap-2"
group="knobValues"
item-key="id"
handle=".value-handle"
:list="values"
v-bind="dragOptions"
>
<template #item="dragValue">
<div :key="dragValue.element.id">
<ValueCard :value-index="dragValue.index + 1" />
</div>
</template>
</draggable>
<button
class="flex flex-1 items-center justify-center rounded-lg border border-zinc-800 bg-zinc-900/50 p-2 text-sm text-muted-foreground hover:bg-zinc-800 hover:text-zinc-200"
>
<Plus class="mr-2" /> Add a value
</button>
</div>
</template>
<script setup lang="ts">
import { Plus } from 'lucide-vue-next'
import ValueCard from '@renderer/components/config/values/ValueCard.vue'
import draggable from 'vuedraggable'
import { ref } from 'vue'
defineProps({
values: {
type: Array,
required: true
}
})
const dragOptions = ref({
ghostClass: 'ghost',
animation: 150,
direction: 'vertical'
})
</script>