Files
web-antrean/components/common/SelectionDialog.vue
2026-01-14 08:25:47 +07:00

157 lines
3.6 KiB
Vue

<template>
<v-dialog v-model="dialogModel" max-width="800px" persistent>
<v-card class="dialog-card">
<v-card-title class="dialog-header">
<span class="headline-4">{{ title }}</span>
<v-btn icon variant="text" size="small" class="btn-close" @click="dialogModel = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
<v-divider />
<v-card-text class="dialog-content">
<v-text-field
v-model="searchModel"
:placeholder="searchPlaceholder"
density="compact"
hide-details
class="mb-4"
prepend-inner-icon="mdi-magnify"
/>
<v-row dense>
<v-col
v-for="item in filteredItems"
:key="item.id"
cols="6"
sm="4"
>
<v-card
class="selection-card"
@click="handleSelect(item)"
elevation="0"
>
<v-card-text class="text-center pa-3">
<div class="selection-name">{{ item.name }}</div>
</v-card-text>
</v-card>
</v-col>
</v-row>
</v-card-text>
</v-card>
</v-dialog>
</template>
<script setup>
import { computed } from 'vue';
const props = defineProps({
modelValue: {
type: Boolean,
required: true
},
title: {
type: String,
default: 'Pilih Item'
},
items: {
type: Array,
default: () => []
},
searchQuery: {
type: String,
default: ''
},
searchPlaceholder: {
type: String,
default: 'Cari...'
}
});
const emit = defineEmits(['update:modelValue', 'update:searchQuery', 'select']);
const dialogModel = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
});
const searchModel = computed({
get: () => props.searchQuery,
set: (value) => emit('update:searchQuery', value)
});
const filteredItems = computed(() => {
if (!searchModel.value) return props.items;
const search = searchModel.value.toLowerCase();
return props.items.filter(item =>
item.name.toLowerCase().includes(search)
);
});
const handleSelect = (item) => {
emit('select', item);
dialogModel.value = false;
};
</script>
<style scoped lang="scss">
.dialog-card {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
.dialog-header {
background: linear-gradient(135deg, var(--color-secondary-600) 0%, var(--color-secondary-700) 100%);
color: var(--color-neutral-100);
padding: 20px 24px;
display: flex;
justify-content: space-between;
align-items: center;
}
.headline-4 {
font-size: 20px;
line-height: 28px;
font-weight: 600;
margin: 0;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
.btn-close {
color: var(--color-neutral-100) !important;
transition: all 0.2s ease;
}
.btn-close:hover {
background: rgba(255, 255, 255, 0.2) !important;
transform: rotate(90deg);
}
.dialog-content {
padding: 24px !important;
background: var(--color-neutral-300);
}
.selection-card {
cursor: pointer;
transition: all 0.2s ease;
border: 1px solid var(--color-neutral-400);
background: var(--color-neutral-100);
border-radius: 8px;
}
.selection-card:hover {
border-color: var(--color-secondary-600);
background: var(--color-secondary-300);
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(6, 113, 224, 0.15);
}
.selection-name {
font-size: 14px;
line-height: 20px;
font-weight: 600;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
color: var(--color-neutral-900);
}
</style>