Skip to content

Commit 73ab4c6

Browse files
committed
refactor(desk): create c-drawer-filter component
1 parent 9a7c41d commit 73ab4c6

File tree

7 files changed

+153
-130
lines changed

7 files changed

+153
-130
lines changed

packages/desktop/components/v-btn.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,15 @@ const variations = {
5151
'accent': 'bg-accent hover:bg-accent/75',
5252
'danger': 'bg-danger hover:bg-danger/75 text-t-primary',
5353
'info': 'bg-info hover:bg-info/75 text-t-primary',
54+
'warn': 'bg-warn hover:bg-warn/75 text-t-primary',
5455
'b-secondary': 'bg-b-secondary hover:bg-b-secondary/75 text-t-primary',
5556
},
5657
text: {
5758
'accent':
5859
'hover:border-accent/5 hover:bg-accent/5 border border-transparent hover:text-accent',
5960
'danger':
6061
'hover:border-danger/5 hover:bg-danger/5 border border-transparent hover:text-danger',
62+
'warn': 'hover:border-warn/5 hover:bg-warn/5 border border-transparent hover:text-warn',
6163
'info': 'hover:border-info/5 hover:bg-info/5 border border-transparent hover:text-info',
6264
'lines': 'hover:border-lines/5 hover:bg-lines/5 border border-transparent hover:text-lines',
6365
'b-primary':

packages/desktop/components/v-table.vue

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@ const props = defineProps({
2929
},
3030
limit: {
3131
type: [Number, String],
32-
default: 80,
32+
default: 20,
33+
},
34+
loading: {
35+
type: Boolean,
36+
default: false,
3337
},
3438
fixed: {
3539
type: Boolean,
@@ -74,9 +78,11 @@ const visibleItems = computed(() => props.items.slice(0, pagination.value.limit)
7478
</script>
7579
<template>
7680
<table
77-
class="w-full border-lines border-separate border-spacing-0"
78-
:class="[fixed ? 'table-fixed' : '']"
81+
class="w-full border-lines border-separate border-spacing-0 relative"
82+
:class="[fixed ? 'table-fixed' : '', loading ? 'animate-pulse' : '']"
7983
>
84+
<div v-if="loading" class="absolute top-0 left-0 h-[1px] w-full bg-accent animate-pulse" />
85+
8086
<thead>
8187
<slot name="column" :columns="parsedColumns">
8288
<v-tr :class="[headerStick ? 'sticky top-0' : '']" class="bg-b-primary">

packages/desktop/i18n/en-US.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export default {
5757
section: 'Section',
5858
apply: 'Apply',
5959
operation: 'Operation',
60+
clear: 'Clear',
6061
errors: {
6162
workspaceNotFound: '@:workspace not found: {0}',
6263
collectionNotFound: '@:collection not found: {1}',
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<script setup lang="ts">
2+
import { ref } from 'vue'
3+
4+
import { CollectionColumn } from '@/../core/entities/collection'
5+
import { useVModel } from 'vue-wind/composables/v-model'
6+
import { Filter } from '../composables/filter'
7+
8+
import CFilter from './CFilter.vue'
9+
10+
const props = defineProps({
11+
modelValue: {
12+
type: Array as () => Filter[],
13+
default: () => [],
14+
},
15+
columns: {
16+
type: Array as () => CollectionColumn[],
17+
default: () => [],
18+
},
19+
})
20+
21+
const emit = defineEmits(['update:modelValue'])
22+
23+
const filters = useVModel(props, 'modelValue', emit)
24+
25+
const drawer = ref(false)
26+
27+
function clear() {
28+
filters.value = []
29+
}
30+
31+
function add(column: CollectionColumn) {
32+
filters.value.push({
33+
columnId: column.id,
34+
field: column.field,
35+
type: column.type,
36+
config: {},
37+
value: '',
38+
})
39+
}
40+
</script>
41+
42+
<template>
43+
<is-drawer v-model="drawer">
44+
<template #activator="{ attrs }">
45+
<v-btn text size="sm" v-bind="attrs">
46+
<is-icon name="filter" />
47+
</v-btn>
48+
</template>
49+
50+
<v-card-head class="px-4">
51+
<v-card-title class="text-t-secondary mr-auto">
52+
{{ $t('filter', 2) }}
53+
</v-card-title>
54+
55+
<v-btn class="mr-4 text-uppercase" text color="warn" @click="clear">
56+
<is-icon name="brush" class="rotate-180 block" />
57+
</v-btn>
58+
59+
<v-btn color="danger" text @click="drawer = false">
60+
<is-icon name="times" />
61+
</v-btn>
62+
</v-card-head>
63+
64+
<c-filter
65+
v-for="(f, index) in filters"
66+
:key="index"
67+
:model-value="f"
68+
:columns="columns"
69+
@update:model-value="(v) => (filters[index] = v)"
70+
@destroy="filters.splice(index, 1)"
71+
/>
72+
73+
<v-card-content class="flex-wrap">
74+
<div v-if="!filters.length" class="w-full mb-3 text-t-secondary">
75+
{{ $t('noEntity', [$t('filter', 2)]) }}
76+
</div>
77+
78+
<is-menu offset-y close-on-content-click>
79+
<template #activator="{ on }">
80+
<v-btn v-bind="on" class="w-full" color="info">
81+
<is-icon name="plus" class="mr-4" />
82+
{{ $t('addEntity', [$t('filter')]) }}
83+
</v-btn>
84+
</template>
85+
86+
<v-card color="b-secondary">
87+
<is-list-item v-for="c in columns" :key="c.id" @click="add(c)">
88+
{{ c.label }}
89+
</is-list-item>
90+
</v-card>
91+
</is-menu>
92+
</v-card-content>
93+
</is-drawer>
94+
</template>

packages/desktop/modules/collection/components/CTable.vue

Lines changed: 43 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,18 @@ import { useRouter } from 'vue-router'
88
import debounce from 'lodash/debounce'
99
1010
import Item from '@core/entities/item'
11-
import Collection, { CollectionColumn } from '@core/entities/collection'
11+
import Collection from '@core/entities/collection'
1212
import { ViewTable } from '@core/entities/view'
1313
14-
import { toCssMeasurement, useNonReactive } from '@/composables/utils'
14+
import { useNonReactive } from '@/composables/utils'
15+
import { filter } from '@/modules/collection/composables/filter'
16+
import { createBindings } from '@/composables/binding'
1517
import { useStore } from '@/modules/collection/store'
1618
17-
import { Filter, filter } from '@/modules/collection/composables/filter'
18-
1919
import Draggable from 'vuedraggable'
2020
import CColumn from './CColumn.vue'
21-
import CFilter from './CFilter.vue'
21+
import CDrawerFilter from './CDrawerFilter.vue'
2222
import IValue from '@/modules/item/components/IValue.vue'
23-
import { createBindings } from '@/composables/binding'
2423
2524
const props = defineProps({
2625
width: {
@@ -75,22 +74,6 @@ watch(() => props.collectionId, setCollection, {
7574
immediate: true,
7675
})
7776
78-
// set items
79-
const items = ref<Item[]>([])
80-
81-
async function setItems() {
82-
if (!collection.value) {
83-
items.value = []
84-
return
85-
}
86-
store.item
87-
.list({ collectionId: props.collectionId })
88-
.then((r) => (items.value = r.data))
89-
.catch(() => (items.value = []))
90-
}
91-
92-
watch(collection, setItems)
93-
9477
// view
9578
const view = ref<ViewTable & { loading: boolean }>({
9679
loading: false,
@@ -129,15 +112,13 @@ async function setView() {
129112
130113
collection.value.columns
131114
.map((c) => useNonReactive(c))
132-
.forEach((c) => {
133-
const saved = savedColumns.find((s) => s.id === c.id)
134-
115+
.forEach((c) =>
135116
columns.push({
136-
width: 200,
117+
width: 5,
137118
...c,
138-
...saved,
119+
...savedColumns.find((s) => s.id === c.id),
139120
})
140-
})
121+
)
141122
142123
columns.push({
143124
id: '_actions_right',
@@ -171,58 +152,48 @@ watch(collection, setView)
171152
watch(() => view.value.columns, saveView, { deep: true })
172153
watch(() => view.value.filters, saveView, { deep: true })
173154
174-
// filters
155+
// set items
156+
175157
const search = ref({
176158
input: '',
177159
show: false,
178160
onInput: debounce((v) => (search.value.input = v), 100),
179161
})
180162
181-
const filters = ref({
182-
drawer: false,
183-
payload: [] as Filter[],
184-
})
163+
const items = ref<Item[]>([])
164+
const loadingItems = ref(false)
165+
166+
async function setItems() {
167+
if (!collection.value) {
168+
items.value = []
169+
return
170+
}
185171
186-
const filteredItems = computed(() =>
187-
items.value.filter((i) => {
172+
if (loadingItems.value) return
173+
174+
loadingItems.value = true
175+
176+
const raw = await store.item
177+
.list({ collectionId: props.collectionId })
178+
.then((r) => (items.value = r.data))
179+
.catch(() => (items.value = []))
180+
181+
items.value = raw.filter((i) => {
188182
let valid = !!JSON.stringify(i).toLowerCase().includes(search.value.input.toLowerCase())
189183
190184
valid = view.value.filters.reduce((r, f) => r && filter(i, f), valid)
191185
192186
return valid
193187
})
194-
)
195-
196-
function addFilter(column: CollectionColumn) {
197-
filters.value.payload.push({
198-
columnId: column.id,
199-
field: column.field,
200-
type: column.type,
201-
config: {},
202-
value: '',
203-
})
204-
}
205188
206-
function applyFilters() {
207-
view.value.filters = useNonReactive(filters.value.payload)
208-
209-
filters.value.drawer = false
189+
setTimeout(() => (loadingItems.value = false), 800)
210190
}
211191
212-
function cancelFilters() {
213-
filters.value.payload = useNonReactive(view.value.filters)
192+
watch(collection, setItems)
214193
215-
filters.value.drawer = false
216-
}
194+
watch(() => view.value.filters, debounce(setItems, 500), { deep: true })
217195
218-
watch(
219-
() => view.value.loading,
220-
(v) => {
221-
if (v) return
222-
223-
filters.value.payload = useNonReactive(view.value.filters)
224-
}
225-
)
196+
watch(() => search.value.input, debounce(setItems, 1000))
226197
227198
// bindings
228199
const attrs = useAttrs()
@@ -265,74 +236,21 @@ const bindings = computed(() => createBindings(attrs, ['table', 'head']))
265236
<is-icon name="search" />
266237
</v-btn>
267238

268-
<is-drawer v-model="filters.drawer">
269-
<template #activator="{ attrs }">
270-
<v-btn text size="sm" v-bind="attrs">
271-
<is-icon name="filter" />
272-
</v-btn>
273-
</template>
274-
275-
<form @submit.prevent="applyFilters">
276-
<v-card-head class="px-4">
277-
<v-card-title class="text-t-secondary mr-auto">
278-
{{ $t('filter', 2) }}
279-
</v-card-title>
280-
281-
<v-btn class="mr-4" color="danger" @click="cancelFilters">
282-
{{ $t('cancel') }}
283-
</v-btn>
284-
285-
<v-btn class="mr-4" type="submit">
286-
{{ $t('apply') }}
287-
</v-btn>
288-
289-
<v-btn @click="filters.drawer = false">
290-
<is-icon name="times" />
291-
</v-btn>
292-
</v-card-head>
293-
294-
<c-filter
295-
v-for="(f, index) in filters.payload"
296-
:key="index"
297-
:model-value="f"
298-
:columns="collection ? collection.columns : []"
299-
@update:model-value="(v) => (filters[index] = v)"
300-
@destroy="filters.payload.splice(index, 1)"
301-
/>
302-
303-
<v-card-content class="flex-wrap">
304-
<div
305-
v-if="!filters.payload.length"
306-
class="w-full mb-3 text-t-secondary"
307-
>
308-
{{ $t('noEntity', [$t('filter', 2)]) }}
309-
</div>
310-
311-
<is-menu offset-y close-on-content-click>
312-
<template #activator="{ on }">
313-
<v-btn v-bind="on" class="mr-4" color="info">
314-
{{ $t('addEntity', [$t('filter')]) }}
315-
</v-btn>
316-
</template>
239+
<v-btn text size="sm" @click="setItems">
240+
<is-icon name="rotate" />
241+
</v-btn>
317242

318-
<v-card color="b-secondary">
319-
<is-list-item
320-
v-for="c in collection ? collection.columns : []"
321-
:key="c.id"
322-
@click="addFilter(c)"
323-
>
324-
{{ c.label }}
325-
</is-list-item>
326-
</v-card>
327-
</is-menu>
328-
</v-card-content>
329-
</form>
330-
</is-drawer>
243+
<c-drawer-filter v-model="view.filters" :columns="collection?.columns" />
331244
</div>
332245
</v-card-head>
333246

334247
<div class="overflow-auto w-full h-[calc(100%_-_53px)]">
335-
<v-table :items="filteredItems" :columns="view.columns" v-bind="bindings.table">
248+
<v-table
249+
:items="items"
250+
:columns="view.columns"
251+
v-bind="bindings.table"
252+
:loading="loadingItems"
253+
>
336254
<template #column>
337255
<Draggable v-model="view.columns" handle=".drag" item-key="id" tag="v-tr">
338256
<template #item="{ element: c }">
@@ -369,7 +287,6 @@ const bindings = computed(() => createBindings(attrs, ['table', 'head']))
369287
v-for="c in view.columns"
370288
:key="c.id"
371289
no-padding
372-
:width="c.width"
373290
:class="c.id[0] === '_' ? '!border-x-0' : ''"
374291
>
375292
<is-menu v-if="c.id === '_actions_left'" offset-y>

0 commit comments

Comments
 (0)