<template> <div :class="$style.root" class="_gaps"> <div :class="$style.header"> <MkSelect v-model="type" :class="$style.typeSelect"> <option value="isLocal">{{ i18n.ts._role._condition.isLocal }}</option> <option value="isRemote">{{ i18n.ts._role._condition.isRemote }}</option> <option value="createdLessThan">{{ i18n.ts._role._condition.createdLessThan }}</option> <option value="createdMoreThan">{{ i18n.ts._role._condition.createdMoreThan }}</option> <option value="and">{{ i18n.ts._role._condition.and }}</option> <option value="or">{{ i18n.ts._role._condition.or }}</option> <option value="not">{{ i18n.ts._role._condition.not }}</option> </MkSelect> <button v-if="draggable" class="drag-handle _button" :class="$style.dragHandle"> <i class="ti ti-menu-2"></i> </button> </div> <div v-if="type === 'and' || type === 'or'" :class="$style.values" class="_gaps"> <Sortable v-model="v.values" tag="div" class="_gaps" item-key="id" handle=".drag-handle" :group="{ name: 'roleFormula' }" :animation="150" :swap-threshold="0.5"> <template #item="{element}"> <div :class="$style.item"> <!-- divが無いとエラーになる https://github.com/SortableJS/vue.draggable.next/issues/189 --> <RolesEditorFormula :model-value="element" draggable @update:model-value="updated => valuesItemUpdated(updated)"/> </div> </template> </Sortable> <MkButton rounded style="margin: 0 auto;" @click="addValue"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> </div> <div v-else-if="type === 'not'" :class="$style.item"> <RolesEditorFormula v-model="v.value"/> </div> <MkInput v-else-if="type === 'createdLessThan' || type === 'createdMoreThan'" v-model="v.sec" type="number"> <template #suffix>sec</template> </MkInput> </div> </template> <script lang="ts" setup> import { computed, defineAsyncComponent, ref, watch } from 'vue'; import { v4 as uuid } from 'uuid'; import MkInput from '@/components/MkInput.vue'; import MkSelect from '@/components/MkSelect.vue'; import MkTextarea from '@/components/MkTextarea.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkButton from '@/components/MkButton.vue'; import FormSlot from '@/components/form/slot.vue'; import * as os from '@/os'; import { i18n } from '@/i18n'; import { deepClone } from '@/scripts/clone'; const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default)); const emit = defineEmits<{ (ev: 'update:modelValue', value: any): void; }>(); const props = defineProps<{ modelValue: any; draggable?: boolean; }>(); const v = ref(deepClone(props.modelValue)); watch(() => props.modelValue, () => { if (JSON.stringify(props.modelValue) === JSON.stringify(v.value)) return; v.value = deepClone(props.modelValue); }, { deep: true }); watch(v, () => { emit('update:modelValue', v.value); }, { deep: true }); const type = computed({ get: () => v.value.type, set: (t) => { if (t === 'and') v.value.values = []; if (t === 'or') v.value.values = []; if (t === 'not') v.value.value = { id: uuid(), type: 'isRemote' }; if (t === 'createdLessThan') v.value.sec = 86400; if (t === 'createdMoreThan') v.value.sec = 86400; v.value.type = t; }, }); function addValue() { v.value.values.push({ id: uuid(), type: 'isRemote' }); } function valuesItemUpdated(item) { const i = v.value.values.findIndex(_item => _item.id === item.id); v.value.values[i] = item; } </script> <style lang="scss" module> .root { } .header { display: flex; } .typeSelect { flex: 1; } .dragHandle { cursor: move; margin-left: 10px; } .item { border: solid 2px var(--divider); border-radius: var(--radius); padding: 12px; &:hover { border-color: var(--accent); } } .values { } </style>