Skip to content

Commit

Permalink
feat(ui): several improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
mesqueeb committed Jul 22, 2023
1 parent 52e7408 commit 183faf8
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 95 deletions.
138 changes: 75 additions & 63 deletions packages/ui/src/components/MagnetarTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -161,65 +161,71 @@ async function setFilter(filterIndex: number, payload: null | FilterState): Prom
await fetchMore()
}
async function setOrderBy(
fieldPath: OPaths<any>,
direction: 'asc' | 'desc' | undefined
): Promise<void> {
async function setOrderBy(fieldPath: OPaths<any>, direction: 'asc' | 'desc' | null): Promise<void> {
clearAllRecords()
if (!direction) orderByState.value.delete(fieldPath)
if (direction) orderByState.value.set(fieldPath, direction)
// it looks better UI wise to delay the actual fetch to prevent UI components from freezing
await new Promise((resolve) => setTimeout(resolve, 200))
await fetchMore()
}
const showingFiltersCode = ref(false)
</script>

<template>
<div class="magnetar-table magnetar-column magnetar-gap-md">
<section class="magnetar-row magnetar-gap-md">
<table>
<thead>
<tr>
<td>{{ muiLabel('magnetar table info counts total') }}</td>
<td>{{ muiLabel('magnetar table info counts filter') }}</td>
<td>{{ muiLabel('magnetar table info counts showing') }}</td>
</tr>
</thead>
<tbody>
<tr>
<td>{{ collection.count }}</td>
<td>{{ collectionInstance.count }}</td>
<td>{{ rows.length }}</td>
</tr>
</tbody>
</table>
<section
v-if="filters.length || hasSomeFilterOrOrderby"
class="magnetar-column magnetar-gap-sm"
>
<div class="magnetar-row magnetar-gap-sm">
<h6>
{{
showingFiltersCode
? muiLabel('magnetar table active filters')
: muiLabel('magnetar table filters')
}}
</h6>
<input v-model="showingFiltersCode" type="checkbox" id="showingFiltersCode" />
<label for="showingFiltersCode">{{ muiLabel('magnetar table show filters code') }}</label>
<div
v-if="hasSomeFilterOrOrderby || !initialStateActive"
class="magnetar-ml-auto magnetar-row magnetar-gap-sm"
>
<button v-if="!initialStateActive" @click="() => resetState()">
{{ muiLabel('magnetar table fetch-state reset') }}
</button>
<button v-if="hasSomeFilterOrOrderby" @click="() => clearState()">
{{ muiLabel('magnetar table clear filters button') }}
</button>
</div>
</div>

<LoadingSpinner v-if="fetchState === 'fetching'" class="magnetar-fetch-state-loading" />
</section>
<div v-if="!showingFiltersCode" class="magnetar-row magnetar-gap-sm">
<TableFilter
v-for="(filter, filterIndex) in filters"
:filter="filter"
:filterState="filtersState.get(filterIndex)"
:collection="collection"
:parseLabel="parseLabel"
@setFilter="(payload) => setFilter(filterIndex, payload)"
/>
</div>

<section class="magnetar-row magnetar-gap-md">
<TableFilter
v-for="(filter, filterIndex) in filters"
:filter="filter"
:filterState="filtersState.get(filterIndex)"
:collection="collection"
:parseLabel="parseLabel"
@setFilter="(payload) => setFilter(filterIndex, payload)"
/>
<div v-if="hasSomeFilterOrOrderby" class="magnetar-column magnetar-gap-sm">
<h6>{{ muiLabel('magnetar table active filters') }}</h6>
<div class="magnetar-row magnetar-gap-sm magnetar-active-filters">
<div v-for="whereOrQuery in currentFilters" :key="JSON.stringify(filters)">
{{
isArray(whereOrQuery)
? `.where(${whereOrQuery.map((chunk) => JSON.stringify(chunk)).join(', ')})`
: `.query(${JSON.stringify(whereOrQuery)})`
}}
</div>
<div v-for="_orderBy in currentOrderBy" :key="JSON.stringify(_orderBy)">
<!-- TODO: @click="() => orderByState.delete(_orderBy[0])" -->
.orderBy({{ _orderBy.map((o) => JSON.stringify(o)).join(', ') }})
</div>
<div v-if="showingFiltersCode" class="magnetar-row magnetar-gap-sm magnetar-active-filters">
<div v-if="!hasSomeFilterOrOrderby">{{ muiLabel('magnetar table no active filters') }}</div>
<div v-for="info in currentFilters" :key="JSON.stringify(filters)">
{{
isArray(info.result)
? `.where(${info.result.map((chunk) => JSON.stringify(chunk)).join(', ')})`
: `.query(${JSON.stringify(info.result)})`
}}
<button @click="() => setFilter(info.filterIndex, null)">✕</button>
</div>
<div v-for="_orderBy in currentOrderBy" :key="JSON.stringify(_orderBy)">
.orderBy({{ _orderBy.map((o) => JSON.stringify(o)).join(', ') }})
<button @click="() => setOrderBy(_orderBy[0], null)">✕</button>
</div>
</div>
</section>
Expand All @@ -230,16 +236,14 @@ async function setOrderBy(
:text="fetchState.error"
/>

<section
v-if="hasSomeFilterOrOrderby || !initialStateActive"
class="magnetar-row magnetar-gap-sm"
>
<button v-if="!initialStateActive" @click="() => resetState()">
{{ muiLabel('magnetar table fetch-state reset') }}
</button>
<button v-if="hasSomeFilterOrOrderby" @click="() => clearState()">
{{ muiLabel('magnetar table clear filters button') }}
</button>
<section class="magnetar-column magnetar-gap-sm magnetar-items-end">
<h6>{{ muiLabel('magnetar table record counts') }}</h6>
<div class="magnetar-row magnetar-gap-md">
<LoadingSpinner v-if="fetchState === 'fetching'" />
{{ collection.count }} {{ muiLabel('magnetar table info counts total') }} /
{{ collectionInstance.count }} {{ muiLabel('magnetar table info counts filtered') }} /
{{ rows.length }} {{ muiLabel('magnetar table info counts showing') }}
</div>
</section>

<table>
Expand All @@ -256,6 +260,15 @@ async function setOrderBy(
</tr>
</thead>
<tbody>
<slot v-if="!rows.length && fetchState !== 'fetching'" name="empty">
<tr>
<td :colspan="columns.length">
<div class="magnetar-row magnetar-justify-center" style="min-height: 100px">
{{ muiLabel('magnetar table no-results') }}
</div>
</td>
</tr>
</slot>
<tr v-for="row in rows" :key="row.id">
<td
v-for="(column, columnIndex) in columns"
Expand All @@ -266,13 +279,6 @@ async function setOrderBy(
</slot>
</td>
</tr>
<slot v-if="!rows.length" name="empty">
<tr>
<td :colspan="columns.length">
<div style="min-height: 100px">{{ muiLabel('magnetar table no-results') }}</div>
</td>
</tr>
</slot>
</tbody>
</table>

Expand Down Expand Up @@ -301,6 +307,12 @@ async function setOrderBy(
gap: 0.5rem
.magnetar-gap-md
gap: 1rem
.magnetar-items-end
align-items: flex-end
.magnetar-justify-center
justify-content: center
.magnetar-ml-auto
margin-left: auto
.magnetar-table
h6
margin: 0
Expand Down
12 changes: 5 additions & 7 deletions packages/ui/src/components/TableFilter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,6 @@ const emit = defineEmits<{
(e: 'setFilter', payload: FilterState | null): void
}>()
/** Used for being able to type guard in the DOM */
const filterAndState = computed<[filter: MUIFilter<any>, state?: FilterState]>(() => {
return [props.filter, props.filterState]
})
onMounted(() => {
// filters with options will fetch the "record count" for each where filter in the option
for (const option of props.filter.options || []) {
Expand Down Expand Up @@ -110,11 +105,14 @@ const filterAttrs = computed<{

<template>
<fieldset class="magnetar-table-filter" :class="filter.class" :style="filter.style">
<legend>{{ filterAttrs.label }}</legend>
<legend>
{{ filterAttrs.label }}
<button v-if="filterState" @click="() => emit('setFilter', null)">✕</button>
</legend>

<template v-if="filter.type === 'checkboxes' && usesFilterStateOr(filter, filterState)">
<!-- CHECKBOXES -->
<div v-for="option in filterAndState[0].options" class="magnetar-inline-block">
<div v-for="option in filter.options" class="magnetar-inline-block">
<input
:id="JSON.stringify(option.where)"
type="checkbox"
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/components/TableTh.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const props = defineProps<{
}>()
const emit = defineEmits<{
(e: 'setOrderBy', payload: [OPaths<any>, 'asc' | 'desc' | undefined]): void
(e: 'setOrderBy', payload: [OPaths<any>, 'asc' | 'desc' | null]): void
}>()
const direction = computed<'asc' | 'desc' | 'sortable' | 'unsortable'>(() => {
Expand Down Expand Up @@ -37,7 +37,7 @@ function onClick(e: MouseEvent) {
return
}
if (direction.value === 'asc') {
emit('setOrderBy', [fieldPath, undefined])
emit('setOrderBy', [fieldPath, null])
return
}
}
Expand Down
1 change: 0 additions & 1 deletion packages/ui/src/components/TestTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ const filters: MUIFilter<Item>[] = [
table
display: block
border-collapse: collapse
margin: 1rem 0
overflow-x: auto
tr
border-top: 1px solid #dfe2e5
Expand Down
1 change: 0 additions & 1 deletion packages/ui/src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ a:hover {
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
Expand Down
14 changes: 11 additions & 3 deletions packages/ui/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,24 +252,32 @@ export function usesFilterStateSingle(
export type OrderByState = Map<OPaths<any>, 'asc' | 'desc'>

export type MUILabel =
| 'magnetar table record counts'
| 'magnetar table fetch-state error default'
| 'magnetar table info counts total'
| 'magnetar table info counts filter'
| 'magnetar table info counts filtered'
| 'magnetar table info counts showing'
| 'magnetar table fetch-state reset'
| 'magnetar table filters'
| 'magnetar table active filters'
| 'magnetar table show filters code'
| 'magnetar table clear filters button'
| 'magnetar table no active filters'
| 'magnetar table no-results'
| 'magnetar table fetch-more button'
| 'magnetar table fetch-more button end'

export const muiLabelDic = {
export const muiLabelDic: Record<MUILabel, string> = {
'magnetar table record counts': 'Found Record Counts',
'magnetar table fetch-state error default': 'An error occured, check the console',
'magnetar table info counts total': 'total',
'magnetar table info counts filter': 'filter',
'magnetar table info counts filtered': 'filtered',
'magnetar table info counts showing': 'showing',
'magnetar table fetch-state reset': 'Reset to Defaults',
'magnetar table filters': 'Filters',
'magnetar table show filters code': 'Show Code',
'magnetar table active filters': 'Active Filters',
'magnetar table no active filters': 'No Filters Active',
'magnetar table clear filters button': 'Clear All',
'magnetar table no-results': 'No results found',
'magnetar table fetch-more button': 'Fetch More',
Expand Down
51 changes: 33 additions & 18 deletions packages/ui/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,48 +105,62 @@ function combineWhereClausesWherePossible(whereClauses: WhereClause[]): WhereCla
}, [])
}

export function filterStateToClauses(state: FiltersState): (WhereClause | { or: WhereClause[] })[] {
return [...state.entries()].reduce<(WhereClause | { or: WhereClause[] })[]>((result, entry) => {
export function filterStateToClauses(
state: FiltersState
): { filterIndex: number; result: WhereClause | { or: WhereClause[] } }[] {
return [...state.entries()].reduce<
{ filterIndex: number; result: WhereClause | { or: WhereClause[] } }[]
>((results, entry) => {
const [filterIndex, filters] = entry

if (isArray(filters)) {
result.push(filters)
return result
results.push({ filterIndex, result: filters })
return results
}

// TODO: not yet implemented
// if ('and' in filters) {
// const set = filters.and
// if (set.size === 0) return result
// if (set.size === 0) return results
// if (set.size === 1) {
// result.push([...set][0])
// return result
// const result = ([...set][0])
// results.push({ filterIndex, result })
// return results
// }
// const combinedWheres = combineWhereClausesWherePossible([...set])
// result.push(...combinedWheres)
// return result
// if (combinedWheres.length === 1) {
// const result = (combinedWheres[0])
// results.push({ filterIndex, result })
// return results
// }
// const result = ({ and: combinedWheres })
// results.push({ filterIndex, result })
// return results
// }

if ('or' in filters) {
const set = filters.or
if (set.size === 0) return result
if (set.size === 0) return results
if (set.size === 1) {
result.push([...set][0])
return result
const result = [...set][0]
results.push({ filterIndex, result })
return results
}
/**
* Perhaps we can combine all of these into 1?
*/
const combinedWheres = combineWhereClausesWherePossible([...set])
if (combinedWheres.length === 1) {
result.push(combinedWheres[0])
return result
const result = combinedWheres[0]
results.push({ filterIndex, result })
return results
}
result.push({ or: combinedWheres })
return result
const result = { or: combinedWheres }
results.push({ filterIndex, result })
return results
}

return result
return results
}, [])
}

Expand All @@ -161,7 +175,8 @@ export function calcCollection(
filtersState: FiltersState,
orderByState: OrderByState
): CollectionInstance<any> {
for (const whereOrQuery of filterStateToClauses(filtersState)) {
const clauses = filterStateToClauses(filtersState).map(({ result }) => result)
for (const whereOrQuery of clauses) {
if (isArray(whereOrQuery)) {
collection = collection.where(...whereOrQuery)
} else {
Expand Down

0 comments on commit 183faf8

Please sign in to comment.