IKHSAN
ENID Baca dalam Bahasa Indonesia

opendesk

Public
Branch
main
Latest commit
08d9f3328 October 2026

MultiSelect.vue

Open raw
<template>
  <div class="flex flex-wrap gap-2 rounded-6 bg-surface-gray-2 p-2">
    <Pill
      v-for="item in items"
      :key="item.value"
      :label="item.label"
      @click="(i) => remove(i)"
    />

    <TextInput
      v-model="input"
      class="w-full"
      :placeholder="placeholder"
      @keyup.enter="add({ label: input, value: input })"
    />
  </div>
</template>

<script setup lang="ts">
import { TextInput, toast } from "frappe-ui";
import { ref, toRefs } from "vue";
import Pill from "./Pill.vue";

type Item = {
  label: string;
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  value: any;
};

const props = defineProps({
  items: {
    type: Array<Item>,
    required: true,
  },
  placeholder: {
    type: String,
    required: false,
    default: "Type...",
  },
  validate: {
    type: Function,
    required: false,
    default: () => false,
  },
});

const emit = defineEmits<{
  (event: "update:items", items: Array<Item>): void;
}>();

const { items } = toRefs(props);
const input = ref("");

function add(item: Item) {
  const err = props.validate(item);

  if (err) {
    toast.error(err);

    return;
  }

  const res = [...items.value, item];
  emit("update:items", res);
  input.value = "";
}

function remove(item: Item) {
  const res = items.value.filter((i) => i.value !== item);
  emit("update:items", res);
}
</script>
bytes
1,381
lines
73
Type
vue
Latest commit
28 October 2026