|
@@ -1,5 +1,5 @@
|
|
|
<template>
|
|
<template>
|
|
|
- <div class="flex min-h-56.5 flex-col justify-between">
|
|
|
|
|
|
|
+ <div class="flex min-h-57 flex-col justify-between">
|
|
|
<div class="flex flex-col gap-2">
|
|
<div class="flex flex-col gap-2">
|
|
|
<InputText
|
|
<InputText
|
|
|
v-model="model.value"
|
|
v-model="model.value"
|
|
@@ -8,34 +8,44 @@
|
|
|
placeholder="Filter…"
|
|
placeholder="Filter…"
|
|
|
/>
|
|
/>
|
|
|
|
|
|
|
|
- <div class="w-full bg-primary-200 px-2 py-1">
|
|
|
|
|
- {{ matchMode }}
|
|
|
|
|
|
|
+ <div
|
|
|
|
|
+ class="flex justify-center rounded-md border border-primary bg-primary px-2 py-1
|
|
|
|
|
+ text-primary-contrast transition-colors duration-200 select-none"
|
|
|
|
|
+ :class="[
|
|
|
|
|
+ hovered && 'border-primary-emphasis bg-primary-emphasis',
|
|
|
|
|
+ activated && 'border-primary-emphasis-alt bg-primary-emphasis-alt'
|
|
|
|
|
+ ]"
|
|
|
|
|
+ >
|
|
|
|
|
+ {{ badge }}
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <SelectButton
|
|
|
|
|
- v-model="model.matchMode"
|
|
|
|
|
- :options="matchModeOptions"
|
|
|
|
|
- optionLabel="name"
|
|
|
|
|
- optionValue="value"
|
|
|
|
|
- dataKey="value"
|
|
|
|
|
- :pt="{
|
|
|
|
|
- root: `*:rounded-none *:not-last:border-r-0 *:first:rounded-s-md *:last:rounded-e-md
|
|
|
|
|
- *:focus-visible:relative *:focus-visible:z-10`,
|
|
|
|
|
- pcToggleButton: {
|
|
|
|
|
- root: `grow border-primary-200! bg-transparent! p-0! text-primary!
|
|
|
|
|
- hover:border-primary-300! hover:bg-primary-50! dark:border-primary-700!
|
|
|
|
|
- dark:hover:border-primary-600! dark:hover:bg-primary/5!
|
|
|
|
|
- p-checked:border-primary-400! p-checked:bg-primary-100!
|
|
|
|
|
- dark:p-checked:border-primary-500! dark:p-checked:bg-primary/15!`,
|
|
|
|
|
- content: 'px-0! py-0.5! p-checked:bg-transparent!'
|
|
|
|
|
- }
|
|
|
|
|
- }"
|
|
|
|
|
- >
|
|
|
|
|
- <!--suppress VueUnrecognizedSlot -->
|
|
|
|
|
- <template #option="slotProps">
|
|
|
|
|
- <Icon :name="slotProps.option.icon" />
|
|
|
|
|
|
|
+ <ButtonGroup class="flex flex-row">
|
|
|
|
|
+ <template v-for="{ name, icon, matchMode } in matchModes">
|
|
|
|
|
+ <Button
|
|
|
|
|
+ v-if="!isCurrentMatchMode(matchMode)"
|
|
|
|
|
+ size="sm"
|
|
|
|
|
+ variant="outlined"
|
|
|
|
|
+ class="grow px-0! py-1.5! hover:bg-primary/15"
|
|
|
|
|
+ @click="changeMatchMode(matchMode)"
|
|
|
|
|
+ @pointerdown="activate()"
|
|
|
|
|
+ @pointerup="deactivate()"
|
|
|
|
|
+ @pointerenter="hover(name)"
|
|
|
|
|
+ @pointerleave="unhover()"
|
|
|
|
|
+ >
|
|
|
|
|
+ <Icon :name="icon" />
|
|
|
|
|
+ </Button>
|
|
|
|
|
+
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-else
|
|
|
|
|
+ class="relative inline-flex grow items-center justify-center gap-2
|
|
|
|
|
+ overflow-hidden rounded-md border border-primary-400 bg-primary-100 px-0 py-1.5
|
|
|
|
|
+ text-sm text-primary transition-colors duration-200 select-none
|
|
|
|
|
+ dark:border-primary-500 dark:bg-primary/15"
|
|
|
|
|
+ >
|
|
|
|
|
+ <Icon :name="icon" />
|
|
|
|
|
+ </div>
|
|
|
</template>
|
|
</template>
|
|
|
- </SelectButton>
|
|
|
|
|
|
|
+ </ButtonGroup>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<Button
|
|
<Button
|
|
@@ -43,55 +53,65 @@
|
|
|
variant="outlined"
|
|
variant="outlined"
|
|
|
size="small"
|
|
size="small"
|
|
|
fluid
|
|
fluid
|
|
|
- :disabled="!hasFilter()"
|
|
|
|
|
- @click="resetFilter"
|
|
|
|
|
|
|
+ :disabled="!hasFilterFor(props.field)"
|
|
|
|
|
+ @click="resetFilterFor(props.field)"
|
|
|
>
|
|
>
|
|
|
- Clear
|
|
|
|
|
|
|
+ Reset
|
|
|
</Button>
|
|
</Button>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup>
|
|
<script setup>
|
|
|
-import { FilterMatchMode } from "@primevue/core/api"
|
|
|
|
|
-
|
|
|
|
|
const model = defineModel()
|
|
const model = defineModel()
|
|
|
|
|
|
|
|
const props = defineProps({
|
|
const props = defineProps({
|
|
|
- filterCallback: {
|
|
|
|
|
- type: Function,
|
|
|
|
|
- required: true
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- hasFilter: {
|
|
|
|
|
- type: Function,
|
|
|
|
|
- required: true
|
|
|
|
|
- },
|
|
|
|
|
-
|
|
|
|
|
- resetFilter: {
|
|
|
|
|
- type: Function,
|
|
|
|
|
|
|
+ field: {
|
|
|
|
|
+ type: String,
|
|
|
required: true
|
|
required: true
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
-const matchModeOptions = ref([
|
|
|
|
|
- {
|
|
|
|
|
- name: "Starts with",
|
|
|
|
|
- icon: "ph:arrow-line-left",
|
|
|
|
|
- value: FilterMatchMode.STARTS_WITH
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- name: "Contains",
|
|
|
|
|
- icon: "ph:arrows-out-line-horizontal",
|
|
|
|
|
- value: FilterMatchMode.CONTAINS
|
|
|
|
|
|
|
+const { hasFilterFor, resetFilterFor } = useFiltersStore()
|
|
|
|
|
+
|
|
|
|
|
+const badge = ref()
|
|
|
|
|
+const hovered = ref(false)
|
|
|
|
|
+const activated = ref(false)
|
|
|
|
|
+
|
|
|
|
|
+watch(
|
|
|
|
|
+ () => model.value.matchMode,
|
|
|
|
|
+ (matchMode) => {
|
|
|
|
|
+ badge.value = nameFromMatchMode(matchMode)
|
|
|
|
|
+ hovered.value = false
|
|
|
},
|
|
},
|
|
|
- {
|
|
|
|
|
- name: "Ends with",
|
|
|
|
|
- icon: "ph:arrow-line-right",
|
|
|
|
|
- value: FilterMatchMode.ENDS_WITH
|
|
|
|
|
- }
|
|
|
|
|
-])
|
|
|
|
|
|
|
+ { immediate: true }
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+function isCurrentMatchMode(matchMode) {
|
|
|
|
|
+ return matchMode === model.value.matchMode
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function hover(name) {
|
|
|
|
|
+ hovered.value = true
|
|
|
|
|
+ badge.value = name
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function unhover() {
|
|
|
|
|
+ hovered.value = false
|
|
|
|
|
+ badge.value = nameFromMatchMode(model.value.matchMode)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function activate() {
|
|
|
|
|
+ activated.value = true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function deactivate() {
|
|
|
|
|
+ activated.value = false
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
-const matchMode = ref("Contains")
|
|
|
|
|
|
|
+function changeMatchMode(matchMode) {
|
|
|
|
|
+ model.value.matchMode = matchMode
|
|
|
|
|
+ deactivate()
|
|
|
|
|
+}
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped></style>
|
|
<style scoped></style>
|