Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions src/store/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,32 +91,35 @@ export const useDataStore = defineStore('data', {
this.loading[stateId] = false
return columns
},

async loadColumnsFromBE({ view, tableId }) {
let allColumns = await this.getColumnsFromBE({ tableId, viewId: view?.id })
if (view) {
// Transform array to object for faster access
// Meta columns aren't real DB columns, so they never come back
// from the fetch above -- append any this view has settings for.
const columnSettingsMap = view.columnSettings?.reduce((acc, item) => {
acc[item.columnId] = item
return acc
}, {}) ?? {}

allColumns = allColumns.concat(MetaColumns.filter(col => columnSettingsMap[col.id]))
if (view.columnSettings) {
allColumns = allColumns.sort((a, b) => {
const orderA = columnSettingsMap[a.id]?.order ?? Number.MAX_SAFE_INTEGER
const orderB = columnSettingsMap[b.id]?.order ?? Number.MAX_SAFE_INTEGER
return orderA - orderB
})
}

// Real columns carry their own order via viewColumnInformation;
// meta columns fall back to columnSettingsMap since they were
// just concatenated above and never went through server-side
// enhancement.
allColumns = allColumns.sort((a, b) => {
const orderA = a.viewColumnInformation?.order ?? columnSettingsMap[a.id]?.order ?? Number.MAX_SAFE_INTEGER
const orderB = b.viewColumnInformation?.order ?? columnSettingsMap[b.id]?.order ?? Number.MAX_SAFE_INTEGER
return orderA - orderB
})
} else {
// no view: keep the backend-ordered result (ColumnService::findAllByTable already applies columnOrder)
}
const stateId = genStateKey(!!(view?.id), view?.id ?? tableId)
this.columns[stateId] = allColumns
return true
},

async loadPublicColumnsFromBE({ token }) {
const stateId = 'public-' + token
this.loading[stateId] = true
Expand Down
116 changes: 76 additions & 40 deletions src/views/ContentReferenceWidget.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,18 @@
<div v-if="rows && rows.length > 0" class="nc-table">
<NcTable
:rows="filteredRows"
:columns="richObject.columns"
:columns="columns"
:element-id="richObject.id"
:is-view="Boolean(richObject.type)"
:is-view="isView"
v-model:view-setting="localViewSetting"
v-bind="tablePermissions"
@edit-row="editRow"
@copy-row="copyRow"
@delete-row="deleteRow" />
</div>
<CreateRow
:columns="richObject.columns"
:is-view="Boolean(richObject.type)"
:columns="columns"
:is-view="isView"
:element-id="richObject.id"
:show-modal="showCopyRow"
:prefill-data="copyPrefillData"
Expand All @@ -38,11 +39,11 @@
v-if="rowToDelete !== null"
:rows-to-delete="[rowToDelete]"
:element-id="richObject.id"
:is-view="Boolean(richObject.type)"
:is-view="isView"
@cancel="rowToDelete = null" />
</div>
</template>

<script>
import NcTable from '../shared/components/ncTable/NcTable.vue'
import Options from '../shared/components/ncTable/sections/Options.vue'
Expand All @@ -54,19 +55,19 @@ import { useResizeObserver } from '@vueuse/core'
import { spawnDialog } from '@nextcloud/vue/functions/dialog'
import { useTablesStore } from '../store/store.js'
import { useDataStore } from '../store/data.js'

export default {

components: {
NcTable,
Options,
CreateRow,
DeleteRows,
NcLoadingIcon,
},

mixins: [permissionsMixin],

props: {
richObjectType: {
type: String,
Expand All @@ -81,20 +82,24 @@ export default {
default: true,
},
},

data() {
return {
searchExp: null,
localRows: [], // Keep as fallback only
localViewSetting: {},
showCopyRow: false,
copyPrefillData: null,
rowToDelete: null,
tablesStore: null,
dataStore: null,
}
},

computed: {
isView() {
return Boolean(this.richObject?.type)
},
tablePermissions() {
return {
canCreateRows: this.canCreateRowInElement(this.richObject),
Expand Down Expand Up @@ -124,7 +129,7 @@ export default {
}
},
getRows() {
return this.dataStore ? this.dataStore.getRows(false, this.richObject.id) : []
return this.dataStore ? this.dataStore.getRows(this.isView, this.richObject.id) : []
},
// Use computed property to get rows from store or richObject
rows() {
Expand All @@ -136,8 +141,19 @@ export default {
// Fallback to richObject rows or local rows
return this.richObject?.rows || this.localRows
},
getColumns() {
return this.dataStore ? this.dataStore.getColumns(this.isView, this.richObject.id) : []
},
// Prefer fresh store data over the (possibly stale) richObject snapshot
columns() {
const storeColumns = this.getColumns
if (storeColumns && storeColumns.length > 0) {
return storeColumns
}
return this.richObject?.columns || []
},
},

watch: {
richObject: {
deep: true,
Expand All @@ -161,22 +177,28 @@ export default {
},
},
},

async mounted() {
useResizeObserver(this.$el, (entries) => {
const entry = entries[0]
const { width } = entry.contentRect
// In Vue 3 $el can be a fragment/comment node (no style), so guard it.
this.$el?.style?.setProperty?.('--widget-content-width', `${width}px`)
})

this.tablesStore = useTablesStore()
this.dataStore = useDataStore()

await this.loadRows()
await Promise.all([this.loadRows(), this.loadColumns()])
},

methods: {
// { tableId } or { viewId } payload for loadRowsFromBE
elementIdPayload() {
return this.isView
? { viewId: this.richObject.id }
: { tableId: this.richObject.id }
},
search(searchString) {
this.searchExp = (searchString !== '')
? new RegExp(searchString.trim(), 'ig')
Expand All @@ -186,28 +208,24 @@ export default {
const { default: CreateRow } = await import('../modules/modals/CreateRow.vue')
spawnDialog(CreateRow, {
showModal: true,
columns: this.richObject.columns,
isView: Boolean(this.richObject.type),
columns: this.columns,
isView: this.isView,
elementId: this.richObject.id,
}, async () => {
// Reload rows from the backend to get the latest data
await this.dataStore.loadRowsFromBE({
tableId: this.richObject.id,
})
await this.dataStore.loadRowsFromBE(this.elementIdPayload())
})
},
async editRow(rowId) {
const { default: EditRow } = await import('../modules/modals/EditRow.vue')
spawnDialog(EditRow, {
showModal: true,
columns: this.richObject.columns,
columns: this.columns,
row: this.getRow(rowId),
isView: Boolean(this.richObject.type),
isView: this.isView,
element: this.richObject,
}, async () => {
await this.dataStore.loadRowsFromBE({
tableId: this.richObject.id,
})
await this.dataStore.loadRowsFromBE(this.elementIdPayload())
})
},
copyRow(rowId) {
Expand All @@ -222,41 +240,56 @@ export default {
},
async loadRows() {
if (!this.dataStore) return


// Paint from cached snapshot immediately, but it can be stale --
// always reconcile with the backend below.
if (this.richObject.rows) {
this.localRows = this.richObject.rows
this.dataStore.seedRows({
isView: Boolean(this.richObject.type),
isView: this.isView,
elementId: this.richObject.id,
rows: this.richObject.rows,
})
return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why remove this early return? for a table embed this now runs loadRowsFromBE, which hits /apps/tables/row/table/{id} and returns the full row set, whereas the reference only embedded about 100 rows

}

try {
await this.dataStore.loadRowsFromBE({
tableId: this.richObject.id,
})
await this.dataStore.loadRowsFromBE(this.elementIdPayload())
// No need to set local rows as the computed property will use store data
} catch (error) {
console.error('Error loading rows:', error)
}
},
async loadColumns() {
if (!this.dataStore) return
try {
if (this.isView) {
await this.dataStore.loadColumnsFromBE({ view: this.richObject })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the reference provider (ContentReferenceHelper) never puts columnSettings on the rich object. So inside loadColumnsFromBE the meta-column append doesn't seem to do anything?

} else {
await this.dataStore.loadColumnsFromBE({ tableId: this.richObject.id })
}
} catch (error) {
console.error('Error loading columns:', error)
}
},
},
}
</script>
<style lang="scss" scoped>

.tables-content-widget {
min-height: max(50vh, 200px);
height: 50vh;
height: auto;
max-height: calc(100dvh - 40px);
overflow: scroll;
overscroll-behavior: contain;
isolation: isolate;

& .header {
position: sticky;
top: 0;
inset-inline-start: 0;
z-index: 1;
z-index: 7;
background-color: var(--color-main-background);

:where(.options) {
position: sticky;
Expand Down Expand Up @@ -285,8 +318,11 @@ export default {
.nc-table {
min-width: var(--widget-content-width);

:where(.options.row) {
display: none;
:deep(.options.row) {
height: 0 !important;
overflow: hidden !important;
margin: 0 !important;
padding: 0 !important;
Comment on lines 279 to +325

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From your gif, i only see vertical scrolling. Did you test horizontal too?

}

:where(thead) {
Expand Down