Browse Source

first commit

tripeur 4 years ago
parent
commit
1ae2d6a9e2
1 changed files with 165 additions and 0 deletions
  1. 165 0
      src/components/filteringPanel.vue

+ 165 - 0
src/components/filteringPanel.vue

@@ -0,0 +1,165 @@
+<template>
+  <div class="filter-modal" v-show="show" @click="closeFilter">
+    <div class="filter-panel" ref="panel">
+      <div class="filter-title" v-if="title">{{ title }}</div>
+      <div class="filter" v-for="(f, idx) in filters" :key="idx">
+        <date-picker
+          v-if="f.type == 'date'"
+          :label="f.name"
+          lang="fr"
+          target="day"
+          :modelValue="defaultValues[idx]"
+          @update:modelValue="updateDateFilter($event, idx)"
+        />
+        <date-picker
+          v-if="f.type == 'hour'"
+          :label="f.name"
+          lang="fr"
+          target="hour"
+          :modelValue="defaultValues[idx]"
+          @update:modelValue="updateDateFilter($event, idx)"
+        />
+        <select-chip-input
+          v-if="f.type == 'category'"
+          :label="f.name"
+          :modelValue="defaultValues[idx]"
+          @update:modelValue="updateCategoryFilter($event, idx)"
+          :autocompleteList="autocompleteList[idx]"
+          :placeholder="f.placeholder"
+          secondaryPlaceholder="+1"
+        />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent, PropType } from "vue";
+import SelectChipInput from "@/components/SelectChipInput.vue";
+import DatePicker from "@/components/date-picker.vue";
+import dayjs, { ConfigType } from "dayjs";
+
+export type FilterParam = {
+  name: string;
+  key: string;
+  type: "hour" | "date" | "category";
+  direction?: "bigger" | "smaller";
+  default?: string;
+  placeholder?: string;
+};
+export default defineComponent({
+  components: { DatePicker, SelectChipInput },
+  emits: ["filterChange"],
+  props: {
+    title: { type: String, default: "Apply filter" },
+    data: { type: Array as PropType<Array<Record<string, unknown>>>, default: () => [] },
+    filters: { type: Array as PropType<Array<FilterParam>>, default: () => [] },
+  },
+  data() {
+    return {
+      defaultValues: [] as Array<unknown>,
+      filterFunctions: [] as Array<(o: Record<string, unknown>) => boolean>,
+      show: false,
+    };
+  },
+  computed: {
+    autocompleteList(): Array<Array<unknown>> {
+      return this.filters.map((f) => {
+        return f.type == "category" ? Array.from(new Set(this.data.flatMap((o) => o[f.key]))) : [];
+      });
+    },
+  },
+  watch: {
+    data() {
+      this.emitFilteredValue();
+    },
+    filters() {
+      this.reset();
+    },
+  },
+  methods: {
+    open() {
+      this.show = true;
+    },
+    closeFilter(e: MouseEvent) {
+      if (!(this.$refs.panel as HTMLElement).contains(e.target as Node)) {
+        this.show = false;
+      }
+    },
+    reset() {
+      this.filterFunctions = this.filters.map(() => () => true);
+      this.defaultValues = this.filters.map(this.getDefault);
+    },
+    getDefault(f: FilterParam) {
+      if (["date", "hour"].includes(f.type)) {
+        if (f.default == undefined) return "";
+        const d = dayjs(f.default);
+        return d.isValid() ? d.toISOString() : "";
+      }
+      if (f.type == "category") {
+        if (f.default == undefined) return [];
+        return JSON.parse(f.default) ?? [];
+      }
+    },
+    emitFilteredValue() {
+      this.$emit(
+        "filterChange",
+        this.data.filter((o) => !this.filterFunctions.some((f) => !f(o)))
+      );
+    },
+    updateDateFilter(value: Date, idx: number) {
+      const f = this.filters[idx];
+      const d = dayjs(value);
+      const compareFunction = f.direction == "smaller" ? d.isBefore : d.isAfter;
+      this.filterFunctions[idx] = d.isValid()
+        ? (o) => compareFunction(dayjs(o[f.key] as ConfigType))
+        : () => true;
+      this.defaultValues[idx] = value;
+      this.emitFilteredValue();
+    },
+    updateCategoryFilter(value: Array<unknown>, idx: number) {
+      const f = this.filters[idx];
+
+      this.filterFunctions[idx] =
+        value.length > 0
+          ? (o) => !value.some((k) => !(o[f.key] as Array<unknown>).includes(k))
+          : () => true;
+
+      this.defaultValues[idx] = value;
+      this.emitFilteredValue();
+    },
+  },
+  mounted() {
+    this.reset();
+  },
+});
+</script>
+
+<style scoped>
+.filter-modal {
+  position: fixed;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  background-color: rgba(0, 0, 0, 0.2);
+  z-index: 1300;
+}
+.filter-panel {
+  position: relative;
+  top: 0;
+  left: 0;
+  bottom: 0;
+  overflow-x: auto;
+  background-color: #fff;
+  width: 350px;
+  height: 100vh;
+}
+.filter-title {
+  padding: 16px;
+  font-size: 2rem;
+}
+.filter {
+  padding: 8px 16px;
+}
+</style>