diff --git a/packages/ui/src/components/MagnetarTable.vue b/packages/ui/src/components/MagnetarTable.vue index f8bbe764..a06f67fb 100644 --- a/packages/ui/src/components/MagnetarTable.vue +++ b/packages/ui/src/components/MagnetarTable.vue @@ -38,7 +38,7 @@ 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(filtersToInitialState(props.filters)) const orderByState = ref(columnsToInitialOrderByState(props.columns)) @@ -46,18 +46,31 @@ const orderByState = ref(columnsToInitialOrderByState(props.column 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) @@ -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) @@ -82,6 +95,7 @@ async function fetchMore() { } onMounted(() => { + clearAllRecords() props.collection.fetchCount() fetchMore() }) @@ -92,6 +106,7 @@ const rows = computed(() => { }) async function setFilter(where: WhereClauseTuple, to: boolean): Promise { + clearAllRecords() filterState.value.set(where, to) const op: WhereFilterOp = where[1] @@ -119,6 +134,7 @@ async function setOrderBy( fieldPath: OPaths, direction: 'asc' | 'desc' | undefined ): Promise { + 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 @@ -148,13 +164,6 @@ async function setOrderBy( - -
-
⚠️ {{ fetchState.error }}
- -
@@ -178,9 +187,17 @@ async function setOrderBy( .orderBy({{ _orderBy.map((o) => JSON.stringify(o)).join(', ') }}) - +
+ {{ fetchState.error }} +
+
+ + +
@@ -210,15 +227,19 @@ async function setOrderBy(
-

{{ muiLabel('magnetar table no-results') }}

+

{{ muiLabel('magnetar table no-results') }}

-
@@ -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 @@ -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 diff --git a/packages/ui/src/components/TableTd.vue b/packages/ui/src/components/TableTd.vue index 8f9cb064..7367995f 100644 --- a/packages/ui/src/components/TableTd.vue +++ b/packages/ui/src/components/TableTd.vue @@ -27,14 +27,18 @@ const buttonsLoading = ref(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 } @@ -43,11 +47,12 @@ const buttonStates = computed<{ label: string; disabled: boolean | undefined }[] async function handleClick(index: number): Promise { 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) } diff --git a/packages/ui/src/components/TestTable.vue b/packages/ui/src/components/TestTable.vue index 9323ef48..b9313380 100644 --- a/packages/ui/src/components/TestTable.vue +++ b/packages/ui/src/components/TestTable.vue @@ -12,12 +12,17 @@ window.itemsModuleT = itemsModuleT const columns: MUIColumn[] = [ { - 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?', diff --git a/packages/ui/src/types.ts b/packages/ui/src/types.ts index fd014d6e..5bb8df9a 100644 --- a/packages/ui/src/types.ts +++ b/packages/ui/src/types.ts @@ -6,9 +6,9 @@ export type OPaths = OPathsWithOptional export type MUIParseLabel = (label: LabelType) => string export type MUIButton, Label extends string = string> = { - label: Label | ((info: { data: T }) => Label) - handler: (info: { data: T }) => void | Promise - disabled?: (info: { data: T }) => boolean | undefined + label: Label | ((info: { value: any; data: T }) => Label) + handler: (info: { value: any; data: T }) => void | Promise + disabled?: (info: { value: any; data: T }) => boolean | undefined } export type MUIColumn, Label extends string = string> = { @@ -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!', } diff --git a/packages/ui/src/utils.ts b/packages/ui/src/utils.ts index b7fe676c..18e337c9 100644 --- a/packages/ui/src/utils.ts +++ b/packages/ui/src/utils.ts @@ -34,14 +34,20 @@ export function columnsToInitialOrderByState(columns: MUIColumn[]): OrderBy } /** Clears JavaScript reference pointers */ -function carbonCopyWhere(where: WhereClauseTuple): WhereClauseTuple { - return where.map((w) => (isArray(w) ? [...w] : w)) as any +export function carbonCopyMap>(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[] { - const whereClauses = [...state.entries()] + const whereClauses = [...carbonCopyMap(state).entries()] .filter(([clause, state]) => !!state) - .map(([clause]) => carbonCopyWhere(clause)) + .map(([clause]) => clause) return whereClauses.reduce[]>((result, whereArr) => { const [fieldPath, op, value] = whereArr