TextFilter.vue 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. <template>
  2. <div class="flex min-h-57 flex-col justify-between">
  3. <div class="flex flex-col gap-2">
  4. <InputText
  5. v-model="model.value"
  6. fluid
  7. type="search"
  8. placeholder="Filter&hellip;"
  9. />
  10. <div
  11. class="flex justify-center rounded-md border border-primary bg-primary px-2 py-1
  12. text-primary-contrast transition-colors duration-200 select-none"
  13. :class="[
  14. hovered && 'border-primary-emphasis bg-primary-emphasis',
  15. activated && 'border-primary-emphasis-alt bg-primary-emphasis-alt'
  16. ]"
  17. >
  18. {{ badge }}
  19. </div>
  20. <ButtonGroup class="flex flex-row">
  21. <template v-for="{ name, icon, matchMode } in matchModes">
  22. <Button
  23. v-if="!isCurrentMatchMode(matchMode)"
  24. size="sm"
  25. variant="outlined"
  26. class="grow px-0! py-1.5! hover:bg-primary/15"
  27. @click="changeMatchMode(matchMode)"
  28. @pointerdown="activate()"
  29. @pointerup="deactivate()"
  30. @pointerenter="hover(name)"
  31. @pointerleave="unhover()"
  32. >
  33. <Icon :name="icon" />
  34. </Button>
  35. <div
  36. v-else
  37. class="relative inline-flex grow items-center justify-center gap-2
  38. overflow-hidden rounded-md border border-primary-400 bg-primary-100 px-0 py-1.5
  39. text-sm text-primary transition-colors duration-200 select-none
  40. dark:border-primary-500 dark:bg-primary/15"
  41. >
  42. <Icon :name="icon" />
  43. </div>
  44. </template>
  45. </ButtonGroup>
  46. </div>
  47. <Button
  48. class="py-0.5!"
  49. variant="outlined"
  50. size="small"
  51. fluid
  52. :disabled="!hasFilterFor(props.field)"
  53. @click="resetFilterFor(props.field)"
  54. >
  55. Reset
  56. </Button>
  57. </div>
  58. </template>
  59. <script setup>
  60. const model = defineModel()
  61. const props = defineProps({
  62. field: {
  63. type: String,
  64. required: true
  65. }
  66. })
  67. const { hasFilterFor, resetFilterFor } = useFiltersStore()
  68. const badge = ref()
  69. const hovered = ref(false)
  70. const activated = ref(false)
  71. watch(
  72. () => model.value.matchMode,
  73. (matchMode) => {
  74. badge.value = nameFromMatchMode(matchMode)
  75. hovered.value = false
  76. },
  77. { immediate: true }
  78. )
  79. function isCurrentMatchMode(matchMode) {
  80. return matchMode === model.value.matchMode
  81. }
  82. function hover(name) {
  83. hovered.value = true
  84. badge.value = name
  85. }
  86. function unhover() {
  87. hovered.value = false
  88. badge.value = nameFromMatchMode(model.value.matchMode)
  89. }
  90. function activate() {
  91. activated.value = true
  92. }
  93. function deactivate() {
  94. activated.value = false
  95. }
  96. function changeMatchMode(matchMode) {
  97. model.value.matchMode = matchMode
  98. deactivate()
  99. }
  100. </script>
  101. <style scoped></style>