ADD: Action mapping
Including basic key capturing in renderer
This commit is contained in:
@@ -7,7 +7,7 @@
|
|||||||
@click="toggle = !toggle"
|
@click="toggle = !toggle"
|
||||||
>
|
>
|
||||||
<component :is="iconComponent" v-if="iconComponent" class="mr-2 size-4" />
|
<component :is="iconComponent" v-if="iconComponent" class="mr-2 size-4" />
|
||||||
<h2 class="py-4 text-sm">{{ title }}<slot name="title" /></h2>
|
<h2 class="flex flex-1 items-center py-4 text-sm">{{ title }}<slot name="title" /></h2>
|
||||||
<Switch
|
<Switch
|
||||||
v-if="showToggle"
|
v-if="showToggle"
|
||||||
:checked="toggle"
|
:checked="toggle"
|
||||||
|
|||||||
105
src/renderer/src/components/config/actions/ActionCard.vue
Normal file
105
src/renderer/src/components/config/actions/ActionCard.vue
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
<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"
|
||||||
|
>Action{{ actionIndex ? ` ${actionIndex}` : '' }}:</span
|
||||||
|
>
|
||||||
|
<span class="float-end mx-2 w-4 cursor-grab">
|
||||||
|
<GripHorizontal class="action-handle mb-0.5 inline-block size-4 text-zinc-600" />
|
||||||
|
</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 ? actionOptions[value].label : 'Select an action...'" />
|
||||||
|
<ChevronsUpDown class="ml-2 size-4 shrink-0 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent class="p-0" :style="{ width: `${width * 1.125}px` }">
|
||||||
|
<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 actionOptions"
|
||||||
|
:key="key"
|
||||||
|
:value="action"
|
||||||
|
@select="
|
||||||
|
() => {
|
||||||
|
value = key
|
||||||
|
open = false
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ action.label }}
|
||||||
|
<Check
|
||||||
|
:class="cn('ml-auto h-4 w-4', value === key ? 'opacity-100' : 'opacity-0')"
|
||||||
|
/>
|
||||||
|
</CommandItem>
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
<Separator />
|
||||||
|
<component :is="actionOptions[value]?.component ? actionOptions[value]?.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 SendKeyAction from '@renderer/components/config/actions/SendKeyAction.vue'
|
||||||
|
import SendStringAction from '@renderer/components/config/actions/SendStringAction.vue'
|
||||||
|
import ScrambleText from '@renderer/components/common/ScrambleText.vue'
|
||||||
|
import { ChevronsUpDown, Check, GripHorizontal } from 'lucide-vue-next'
|
||||||
|
import { useElementSize } from '@vueuse/core'
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
actionIndex: {
|
||||||
|
type: Number,
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const actionOptions = ref({
|
||||||
|
sendKey: { label: 'Press Key or Combination', component: SendKeyAction },
|
||||||
|
sendString: { label: 'Type a String', component: SendStringAction },
|
||||||
|
sendMouse: { label: 'Move, Scroll or Click', component: 'SendMouseAction' },
|
||||||
|
sendGamepad: { label: 'Send a Gamepad Input', component: 'SendGamepadAction' },
|
||||||
|
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' }
|
||||||
|
})
|
||||||
|
|
||||||
|
const comboboxButton = ref(null)
|
||||||
|
const { width } = useElementSize(comboboxButton)
|
||||||
|
|
||||||
|
const open = ref(false)
|
||||||
|
const value = ref('')
|
||||||
|
</script>
|
||||||
42
src/renderer/src/components/config/actions/ActionGroup.vue
Normal file
42
src/renderer/src/components/config/actions/ActionGroup.vue
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<draggable
|
||||||
|
key="actionsDraggable"
|
||||||
|
class="flex flex-col gap-2"
|
||||||
|
group="keyActions"
|
||||||
|
item-key="id"
|
||||||
|
handle=".action-handle"
|
||||||
|
:list="actions"
|
||||||
|
v-bind="dragOptions"
|
||||||
|
>
|
||||||
|
<template #item="dragAction">
|
||||||
|
<div :key="dragAction.element.id">
|
||||||
|
<ActionCard :action-index="dragAction.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 an action
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Plus } from 'lucide-vue-next'
|
||||||
|
import ActionCard from '@renderer/components/config/actions/ActionCard.vue'
|
||||||
|
import draggable from 'vuedraggable'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
defineProps({
|
||||||
|
actions: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const dragOptions = ref({
|
||||||
|
ghostClass: 'ghost',
|
||||||
|
animation: 150,
|
||||||
|
direction: 'vertical'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
39
src/renderer/src/components/config/actions/SendKeyAction.vue
Normal file
39
src/renderer/src/components/config/actions/SendKeyAction.vue
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-col p-4">
|
||||||
|
<Button
|
||||||
|
class="flex-1"
|
||||||
|
:class="{ 'bg-orange-600 hover:bg-orange-500': isCapturing }"
|
||||||
|
@click="toggleCapture"
|
||||||
|
>⬤
|
||||||
|
{{ isCapturing ? 'Capturing Keyboard Input' : 'Capture Keyboard Input' }}
|
||||||
|
</Button>
|
||||||
|
<div class="mt-6 text-center font-mono text-sm text-muted-foreground">
|
||||||
|
Key: {{ lastEvent?.key }} | Code: {{ lastEvent?.keyCode }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Button } from '@renderer/components/ui/button'
|
||||||
|
import { ref, Ref } from 'vue'
|
||||||
|
|
||||||
|
const isCapturing = ref(false)
|
||||||
|
const keydownListener = (e: KeyboardEvent) => {
|
||||||
|
lastEvent.value = e
|
||||||
|
}
|
||||||
|
const keyupListener = (e: KeyboardEvent) => {
|
||||||
|
lastEvent.value = e
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleCapture = () => {
|
||||||
|
isCapturing.value = !isCapturing.value
|
||||||
|
// TODO: Do this in the main process
|
||||||
|
if (isCapturing.value) {
|
||||||
|
window.addEventListener('keydown', keydownListener)
|
||||||
|
window.addEventListener('keyup', keyupListener)
|
||||||
|
} else {
|
||||||
|
window.removeEventListener('keydown', keydownListener)
|
||||||
|
window.removeEventListener('keyup', keyupListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const lastEvent: Ref<KeyboardEvent | null> = ref(null)
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<template>
|
||||||
|
<div class="p-4">
|
||||||
|
<Input type="text" placeholder="String to be typed" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Input } from '@renderer/components/ui/input'
|
||||||
|
</script>
|
||||||
@@ -1,92 +1,62 @@
|
|||||||
<template>
|
<template>
|
||||||
<ConfigSection title="Key Mapping" :icon-component="PlusSquare">
|
<ConfigSection :title="`${store.selectedKey} Pressed`" :icon-component="PanelBottomClose">
|
||||||
<template #title
|
<template #title>
|
||||||
><span class="text-zinc-500"> ({{ store.selectedKey }})</span></template
|
<span class="text-zinc-500"> ({{ actionsPressed.length }})</span></template
|
||||||
>
|
>
|
||||||
<div class="my-4 px-8">
|
<ActionGroup :actions="actionsPressed" class="p-2" />
|
||||||
<span class="font-mono text-sm text-muted-foreground">Action:</span>
|
</ConfigSection>
|
||||||
<Popover v-model:open="open">
|
<ConfigSection :title="`${store.selectedKey} Released`" :icon-component="PanelBottomOpen">
|
||||||
<PopoverTrigger as-child>
|
<template #title>
|
||||||
<Button
|
<span class="text-zinc-500"> ({{ actionsReleased.length }})</span></template
|
||||||
ref="comboboxButton"
|
>
|
||||||
variant="outline"
|
<ActionGroup :actions="actionsReleased" class="p-2" />
|
||||||
role="combobox"
|
</ConfigSection>
|
||||||
:aria-expanded="open"
|
<ConfigSection :title="`${store.selectedKey} Held`" :icon-component="Clock2">
|
||||||
class="my-2 w-full justify-between"
|
<template #title>
|
||||||
>
|
<span class="text-zinc-500"> ({{ actionsHeld.length }})</span></template
|
||||||
<ScrambleText :text="value ? actionOptions[value] : 'Select an action...'" />
|
>
|
||||||
<ChevronsUpDown class="ml-2 size-4 shrink-0 opacity-50" />
|
<ActionGroup :actions="actionsHeld" class="p-2" />
|
||||||
</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 actionOptions"
|
|
||||||
: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>
|
</ConfigSection>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { PlusSquare, ChevronsUpDown, Check } from 'lucide-vue-next'
|
import { PanelBottomClose, PanelBottomOpen, Clock2 } from 'lucide-vue-next'
|
||||||
import ConfigSection from '@renderer/components/common/ConfigSection.vue'
|
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 { useStore } from '@renderer/store'
|
import { useStore } from '@renderer/store'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import ActionGroup from '../actions/ActionGroup.vue'
|
||||||
|
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
|
const actionsPressed = ref([
|
||||||
const actionOptions = ref({
|
{
|
||||||
sendKey: 'Press Key or Combination',
|
id: '1'
|
||||||
sendString: 'Type a String',
|
},
|
||||||
sendMouse: 'Move, Scroll or Click',
|
{
|
||||||
sendGamepad: 'Send a Gamepad Input',
|
id: '2'
|
||||||
sendMidi: 'Send a MIDI Message',
|
},
|
||||||
sendOsc: 'Send an OSC Message',
|
{
|
||||||
sendSerial: 'Send a Serial Message',
|
id: '3'
|
||||||
controlMedia: 'Control Media Playback',
|
}
|
||||||
controlSystem: 'Control your OS',
|
])
|
||||||
runProgram: 'Start a Program'
|
const actionsReleased = ref([
|
||||||
})
|
{
|
||||||
|
id: '4'
|
||||||
const comboboxButton = ref(null)
|
},
|
||||||
|
{
|
||||||
const open = ref(false)
|
id: '5'
|
||||||
const value = ref('')
|
},
|
||||||
|
{
|
||||||
|
id: '6'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
const actionsHeld = ref([
|
||||||
|
{
|
||||||
|
id: '7'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '8'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '9'
|
||||||
|
}
|
||||||
|
])
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
class="m-2 flex h-12 overflow-hidden rounded-lg transition-all"
|
class="m-2 flex h-12 overflow-hidden rounded-lg transition-all"
|
||||||
:class="{
|
:class="{
|
||||||
'border border-zinc-100 bg-zinc-300': selected,
|
'border border-zinc-100 bg-zinc-300': selected,
|
||||||
'border border-transparent bg-zinc-900/30 hover:border-zinc-900': !selected,
|
'border border-zinc-800/50 bg-zinc-900/30': !selected,
|
||||||
group: showHoverButtons
|
group: showHoverButtons
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
class="h-full min-w-0 flex-1 rounded-lg bg-transparent pl-8 text-sm transition-all focus-visible:outline-none focus-visible:ring-0"
|
class="h-full min-w-0 flex-1 rounded-lg bg-transparent pl-8 text-sm transition-all focus-visible:outline-none focus-visible:ring-0"
|
||||||
:class="{
|
:class="{
|
||||||
'bg-zinc-300 font-semibold text-black hover:bg-zinc-200': selected,
|
'bg-zinc-300 font-semibold text-black hover:bg-zinc-200': selected,
|
||||||
'text-muted-foreground hover:bg-zinc-900': !selected
|
'text-muted-foreground hover:bg-zinc-800': !selected
|
||||||
}"
|
}"
|
||||||
@blur="onNameInputBlur"
|
@blur="onNameInputBlur"
|
||||||
/>
|
/>
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
type="submit"
|
type="submit"
|
||||||
:class="{
|
:class="{
|
||||||
'bg-zinc-300 text-black hover:bg-zinc-200': selected,
|
'bg-zinc-300 text-black hover:bg-zinc-200': selected,
|
||||||
'text-zinc-100 hover:bg-zinc-900': !selected
|
'text-zinc-100 hover:bg-zinc-800': !selected
|
||||||
}"
|
}"
|
||||||
class="flex aspect-square h-full shrink-0 items-center justify-center rounded-lg transition-all"
|
class="flex aspect-square h-full shrink-0 items-center justify-center rounded-lg transition-all"
|
||||||
>
|
>
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
v-else
|
v-else
|
||||||
:class="{
|
:class="{
|
||||||
'bg-zinc-300 font-semibold text-black hover:bg-zinc-200': selected,
|
'bg-zinc-300 font-semibold text-black hover:bg-zinc-200': selected,
|
||||||
'text-muted-foreground hover:bg-zinc-900': !selected
|
'text-muted-foreground hover:bg-zinc-800': !selected
|
||||||
}"
|
}"
|
||||||
class="flex-1 truncate rounded-lg pr-4 text-left text-sm transition-all"
|
class="flex-1 truncate rounded-lg pr-4 text-left text-sm transition-all"
|
||||||
@click="!editing && $emit('select') && $refs.profileTitle.scramble()"
|
@click="!editing && $emit('select') && $refs.profileTitle.scramble()"
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
<GripHorizontal
|
<GripHorizontal
|
||||||
v-if="draggable"
|
v-if="draggable"
|
||||||
:class="{ 'text-zinc-600': selected, 'text-muted-foreground': !selected }"
|
:class="{ 'text-zinc-600': selected, 'text-muted-foreground': !selected }"
|
||||||
class="mb-0.5 inline-block size-4 opacity-0 transition-all group-hover:opacity-100"
|
class="profile-handle mb-0.5 inline-block size-4 opacity-0 transition-all group-hover:opacity-100"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<ScrambleText
|
<ScrambleText
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
v-if="nameEditable"
|
v-if="nameEditable"
|
||||||
:class="{
|
:class="{
|
||||||
'bg-zinc-300 text-black hover:bg-zinc-200': selected,
|
'bg-zinc-300 text-black hover:bg-zinc-200': selected,
|
||||||
'text-zinc-100 hover:bg-zinc-900': !selected,
|
'text-zinc-100 hover:bg-zinc-800': !selected,
|
||||||
'group-focus-within:w-12 group-hover:w-12': !editing
|
'group-focus-within:w-12 group-hover:w-12': !editing
|
||||||
}"
|
}"
|
||||||
class="flex w-0 shrink-0 items-center justify-center rounded-lg transition-all"
|
class="flex w-0 shrink-0 items-center justify-center rounded-lg transition-all"
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
<button
|
<button
|
||||||
:class="{
|
:class="{
|
||||||
'bg-zinc-300 text-black hover:bg-zinc-200': selected,
|
'bg-zinc-300 text-black hover:bg-zinc-200': selected,
|
||||||
'text-zinc-100 hover:bg-zinc-900': !selected,
|
'text-zinc-100 hover:bg-zinc-800': !selected,
|
||||||
'group-focus-within:w-12 group-hover:w-12': !editing,
|
'group-focus-within:w-12 group-hover:w-12': !editing,
|
||||||
'rounded-l-lg': !nameEditable
|
'rounded-l-lg': !nameEditable
|
||||||
}"
|
}"
|
||||||
@@ -121,7 +121,7 @@
|
|||||||
<button
|
<button
|
||||||
:class="{
|
:class="{
|
||||||
'bg-zinc-300 text-black hover:bg-zinc-200': selected,
|
'bg-zinc-300 text-black hover:bg-zinc-200': selected,
|
||||||
'text-zinc-100 hover:bg-zinc-900': !selected,
|
'text-zinc-100 hover:bg-zinc-800': !selected,
|
||||||
'group-focus-within:w-12 group-hover:w-12': !editing
|
'group-focus-within:w-12 group-hover:w-12': !editing
|
||||||
}"
|
}"
|
||||||
class="flex w-0 shrink-0 items-center justify-center rounded-lg transition-all"
|
class="flex w-0 shrink-0 items-center justify-center rounded-lg transition-all"
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
item-key="name"
|
item-key="name"
|
||||||
:list="store.profileCategories"
|
:list="store.profileCategories"
|
||||||
v-bind="dragOptions"
|
v-bind="dragOptions"
|
||||||
|
handle=".category-handle"
|
||||||
@start="drag = true"
|
@start="drag = true"
|
||||||
@end="drag = false"
|
@end="drag = false"
|
||||||
@change="onCategoryDrop"
|
@change="onCategoryDrop"
|
||||||
@@ -79,45 +80,44 @@
|
|||||||
({{ dragCategory.element.profiles?.length || 0 }})</span
|
({{ dragCategory.element.profiles?.length || 0 }})</span
|
||||||
>
|
>
|
||||||
<span class="float-right mx-4 w-4 cursor-grab text-zinc-600">
|
<span class="float-right mx-4 w-4 cursor-grab text-zinc-600">
|
||||||
<GripHorizontal class="mb-0.5 inline-block size-4" />
|
<GripHorizontal class="category-handle mb-0.5 inline-block size-4" />
|
||||||
</span>
|
</span>
|
||||||
</CollapsibleTrigger>
|
</CollapsibleTrigger>
|
||||||
<CollapsibleContent>
|
<CollapsibleContent>
|
||||||
<TransitionGroup>
|
<draggable
|
||||||
<draggable
|
key="profilesDraggable"
|
||||||
key="profilesDraggable"
|
group="profiles"
|
||||||
group="profiles"
|
item-key="id"
|
||||||
item-key="id"
|
:list="dragCategory.element.profiles"
|
||||||
:list="dragCategory.element.profiles"
|
v-bind="dragOptions"
|
||||||
v-bind="dragOptions"
|
handle=".profile-handle"
|
||||||
@start="drag = true"
|
@start="drag = true"
|
||||||
@end="drag = false"
|
@end="drag = false"
|
||||||
@change="(event) => onProfileDrop(event, dragCategory.index)"
|
@change="(event) => onProfileDrop(event, dragCategory.index)"
|
||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="hideable-header m-2 flex h-12 items-center justify-center">
|
<div class="hideable-header m-2 flex h-12 items-center justify-center">
|
||||||
<MoreHorizontal class="w-4 text-zinc-600" />
|
<MoreHorizontal class="w-4 text-zinc-600" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #item="dragProfile">
|
<template #item="dragProfile">
|
||||||
<div :key="dragProfile.element.name">
|
<div :key="dragProfile.element.name">
|
||||||
<ProfileButton
|
<ProfileButton
|
||||||
:profile="dragProfile.element"
|
:profile="dragProfile.element"
|
||||||
:show-hover-buttons="!drag"
|
:show-hover-buttons="!drag"
|
||||||
:selected="store.selectedProfile?.id === dragProfile.element.id"
|
:selected="store.selectedProfile?.id === dragProfile.element.id"
|
||||||
@select="
|
@select="
|
||||||
() => {
|
() => {
|
||||||
store.selectProfile(dragProfile.element.id)
|
store.selectProfile(dragProfile.element.id)
|
||||||
showProfileConfig = true
|
showProfileConfig = true
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
@duplicate="store.duplicateProfile(dragProfile.element.id)"
|
@duplicate="store.duplicateProfile(dragProfile.element.id)"
|
||||||
@delete="store.removeProfile(dragProfile.element.id)"
|
@delete="store.removeProfile(dragProfile.element.id)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</draggable>
|
</draggable>
|
||||||
</TransitionGroup>
|
|
||||||
</CollapsibleContent>
|
</CollapsibleContent>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user