Skip to content

Commit

Permalink
fix: make the way state reset happens more straight-forward
Browse files Browse the repository at this point in the history
  • Loading branch information
mesqueeb committed Jul 19, 2023
1 parent f3ec3d7 commit 30cad44
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 43 deletions.
74 changes: 47 additions & 27 deletions packages/ui/src/components/MagnetarTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,26 +38,39 @@ function muiLabel(label: MUILabel): string {
}
// const emit = defineEmits<{}>()
const fetchState = ref<'ok' | 'fetching' | { error: string }>('ok')
const fetchState = ref<'ok' | 'end' | 'fetching' | { error: string }>('ok')
const filterState = ref<FilterState>(filtersToInitialState(props.filters))
const orderByState = ref<OrderByState>(columnsToInitialOrderByState(props.columns))
const currentFilters = computed(() => filterStateToClauses(filterState.value))
const currentOrderBy = computed(() => orderByStateToClauses(orderByState.value))
function resetState(resetFetchState?: boolean): void {
filterState.value = filtersToInitialState(props.filters)
orderByState.value = columnsToInitialOrderByState(props.columns)
collectionInstance.value = calcCollection(props.collection, filterState.value, orderByState.value)
if (resetFetchState) fetchState.value = 'ok'
}
/** This instance is not computed so that we can delay setting it after fetching the relevant records */
const collectionInstance = ref(
calcCollection(props.collection, filterState.value, orderByState.value)
)
function clearAllRecords(): void {
props.collection.data.clear()
collectionInstance.value.fetched.cursor = undefined
}
function resetState(): void {
filterState.value = filtersToInitialState(props.filters)
orderByState.value = columnsToInitialOrderByState(props.columns)
clearAllRecords()
fetchMore()
}
function clearState(): void {
filterState.value = new Map()
orderByState.value = new Map()
clearAllRecords()
fetchMore()
}
/** never throws */
async function fetchMore() {
fetchState.value = 'fetching'
const collection = calcCollection(props.collection, filterState.value, orderByState.value)
Expand All @@ -68,8 +81,8 @@ async function fetchMore() {
.startAfter(collection.fetched.cursor)
.fetch({ force: true })
await collection.fetchCount()
if (collection.fetched.reachedEnd) console.log(`that's all!`)
fetchState.value = 'ok'
fetchState.value = collection.fetched.reachedEnd ? 'end' : 'ok'
// set new state
collectionInstance.value = collection
} catch (error: unknown) {
console.error(`fetchMore error:`, error)
Expand All @@ -82,6 +95,7 @@ async function fetchMore() {
}
onMounted(() => {
clearAllRecords()
props.collection.fetchCount()
fetchMore()
})
Expand All @@ -92,6 +106,7 @@ const rows = computed(() => {
})
async function setFilter(where: WhereClauseTuple<any, any, any>, to: boolean): Promise<void> {
clearAllRecords()
filterState.value.set(where, to)
const op: WhereFilterOp = where[1]
Expand Down Expand Up @@ -119,6 +134,7 @@ async function setOrderBy(
fieldPath: OPaths<any>,
direction: 'asc' | 'desc' | undefined
): 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
Expand Down Expand Up @@ -148,13 +164,6 @@ async function setOrderBy(
</table>

<LoadingSpinner v-if="fetchState === 'fetching'" class="magnetar-fetch-state-loading" />

<div v-if="isPlainObject(fetchState)" class="magnetar-fetch-state-error">
<div><span>⚠️</span> {{ fetchState.error }}</div>
<button @click="() => resetState(true)">
{{ muiLabel('magnetar table fetch-state reset') }}
</button>
</div>
</section>

<section class="magnetar-table-filters">
Expand All @@ -178,9 +187,17 @@ async function setOrderBy(
.orderBy({{ _orderBy.map((o) => JSON.stringify(o)).join(', ') }})
</div>
</div>
<button @click="() => resetState()">
{{ muiLabel('magnetar table clear filters button') }}
</button>
<div v-if="isPlainObject(fetchState)" class="magnetar-fetch-state-error">
{{ fetchState.error }}
</div>
<div>
<button @click="() => resetState()">
{{ muiLabel('magnetar table fetch-state reset') }}
</button>
<button @click="() => clearState()">
{{ muiLabel('magnetar table clear filters button') }}
</button>
</div>
</div>
</section>

Expand Down Expand Up @@ -210,15 +227,19 @@ async function setOrderBy(
</tr>
<slot v-if="!rows.length" name="empty">
<div>
<p class="text-center">{{ muiLabel('magnetar table no-results') }}</p>
<p>{{ muiLabel('magnetar table no-results') }}</p>
</div>
</slot>
</tbody>
</table>

<section class="magnetar-table-controls">
<button @click="() => fetchMore()">
{{ muiLabel('magnetar table fetch-more button') }}
<button :disabled="fetchState === 'end'" @click="() => fetchMore()">
{{
fetchState === 'end'
? muiLabel('magnetar table fetch-more button end')
: muiLabel('magnetar table fetch-more button')
}}
</button>
</section>
</div>
Expand All @@ -238,11 +259,10 @@ async function setOrderBy(
h6
margin: 0
.magnetar-fetch-state-error
display: flex
flex-direction: column
gap: 0.5rem
white-space: pre-line
word-break: break-word
text-align: left
color: var(--c-error, indianred)
.magnetar-current-state
display: flex
flex-direction: column
Expand All @@ -255,7 +275,7 @@ async function setOrderBy(
flex-wrap: wrap
gap: .5rem
align-items: center
> *
> div
background: var(--c-primary-extra-light, whitesmoke)
border-radius: 0.25rem
padding: 0.25rem 0.5rem
Expand Down
11 changes: 8 additions & 3 deletions packages/ui/src/components/TableTd.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,18 @@ const buttonsLoading = ref<boolean[]>(props.column.buttons?.map(() => false) ||
const buttonStates = computed<{ label: string; disabled: boolean | undefined }[]>(() => {
const { column, row, parseLabel } = props
const rawValue = cellValueRaw.value
return (column.buttons || []).map((button, index) => {
const text = isFunction(button.label) ? button.label({ data: row }) : button.label
const text = isFunction(button.label)
? button.label({ data: row, value: rawValue })
: button.label
const label = parseLabel ? parseLabel(text) : text
const disabled = buttonsLoading.value[index]
? true
: isFunction(button.disabled)
? button.disabled({ data: row })
? button.disabled({ data: row, value: rawValue })
: button.disabled
return { label, disabled }
Expand All @@ -43,11 +47,12 @@ const buttonStates = computed<{ label: string; disabled: boolean | undefined }[]
async function handleClick(index: number): Promise<void> {
const { row, column } = props
const rawValue = cellValueRaw.value
const button = column.buttons?.[index]
if (!button) return
buttonsLoading.value[index] = true
try {
await button.handler?.({ data: row })
await button.handler?.({ data: row, value: rawValue })
} catch (error: unknown) {
console.error(error)
}
Expand Down
15 changes: 10 additions & 5 deletions packages/ui/src/components/TestTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,17 @@ window.itemsModuleT = itemsModuleT
const columns: MUIColumn<Item>[] = [
{
button: {
label: 'Edit',
handler: ({ data }) => alert(`edit ${data.id} (you have to implement this yourself)`),
},
buttons: [
{
label: 'Edit',
handler: ({ data }) => alert(`edit ${data.id} (you have to implement this yourself)`),
},
],
},
{
fieldPath: 'id',
buttons: [{ label: 'Copy', handler: ({ value }) => alert(`copied to clipboard ${value}`) }],
},
{ fieldPath: 'id' },
{ label: 'Title', fieldPath: 'title', sortable: true },
{
label: 'Is it done?',
Expand Down
10 changes: 6 additions & 4 deletions packages/ui/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ export type OPaths<T> = OPathsWithOptional<T>
export type MUIParseLabel<LabelType = any> = (label: LabelType) => string

export type MUIButton<T extends Record<string, any>, Label extends string = string> = {
label: Label | ((info: { data: T }) => Label)
handler: (info: { data: T }) => void | Promise<void>
disabled?: (info: { data: T }) => boolean | undefined
label: Label | ((info: { value: any; data: T }) => Label)
handler: (info: { value: any; data: T }) => void | Promise<void>
disabled?: (info: { value: any; data: T }) => boolean | undefined
}

export type MUIColumn<T extends Record<string, any>, Label extends string = string> = {
Expand Down Expand Up @@ -105,15 +105,17 @@ export type MUILabel =
| 'magnetar table clear filters button'
| 'magnetar table no-results'
| 'magnetar table fetch-more button'
| 'magnetar table fetch-more button end'

export const muiLabelDic = {
'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 showing': 'showing',
'magnetar table fetch-state reset': 'reset to defaults',
'magnetar table fetch-state reset': 'Reset to Defaults',
'magnetar table active filters': 'Active Filters',
'magnetar table clear filters button': 'Clear All',
'magnetar table no-results': 'No results found',
'magnetar table fetch-more button': 'Fetch More',
'magnetar table fetch-more button end': 'Fetched Everything!',
}
14 changes: 10 additions & 4 deletions packages/ui/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,20 @@ export function columnsToInitialOrderByState(columns: MUIColumn<any>[]): OrderBy
}

/** Clears JavaScript reference pointers */
function carbonCopyWhere(where: WhereClauseTuple<any, any, any>): WhereClauseTuple<any, any, any> {
return where.map((w) => (isArray(w) ? [...w] : w)) as any
export function carbonCopyMap<T extends Map<any, any>>(map: T): T {
let newMap = new Map() as T
for (let [key, value] of map) {
const _key = isArray(key) ? [...key] : key
const _value = isArray(value) ? [...value] : value
newMap.set(_key, _value)
}
return newMap
}

export function filterStateToClauses(state: FilterState): WhereClauseTuple<any, any, any>[] {
const whereClauses = [...state.entries()]
const whereClauses = [...carbonCopyMap(state).entries()]
.filter(([clause, state]) => !!state)
.map(([clause]) => carbonCopyWhere(clause))
.map(([clause]) => clause)

return whereClauses.reduce<WhereClauseTuple<any, any, any>[]>((result, whereArr) => {
const [fieldPath, op, value] = whereArr
Expand Down

0 comments on commit 30cad44

Please sign in to comment.