🖼️ Add persistent per-view layout modes (table / tiles / gallery)#2407
🖼️ Add persistent per-view layout modes (table / tiles / gallery)#2407Rello wants to merge 5 commits into
Conversation
|
@jancborchardt as new layouts will be relevant for your opinion |
jancborchardt
left a comment
There was a problem hiding this comment.
@marcoambrosini one for you, check it out. :)
@Rello do you have reference screenshots from Sharepoint as you mention?
List and Gallery view currently also look very similar (with Gallery in need of some design improvement).
|
@marcoambrosini can you please have a look now? the PR was now implemented and tested on the branch and I added the actual screenshots from the working app |
This comment was marked as resolved.
This comment was marked as resolved.
enjeck
left a comment
There was a problem hiding this comment.
Just confirming that the state when there's no image is designer-approved? Here, for a tile view, I don't see why say "No image" instead of just text-based tiling like in https://media.geeksforgeeks.org/wp-content/uploads/20231218102144/Screenshot-2023-12-18-102058.png, for example.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
@enjeck what do you mean by this? I am not sure if I understand. what would you show instead of the "no image"? |
Signed-off-by: Rello <github@scherello.de> Signed-off-by: Rello <Rello@users.noreply.github.com> AI-assistant: Codex (GPT-5)
31001dd to
253b902
Compare
jancborchardt
left a comment
There was a problem hiding this comment.
@Rello As @enjeck says, it is indeed weird to show "No image", especially as it underlays the text.
Instead, it would be better to show nothing as @enjeck proposed and just focus on the text.
FYI @marcoambrosini
…extcloud-tables Signed-off-by: Rello <Rello@users.noreply.github.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a0456bd268
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| currentLayout() { | ||
| return ['tiles', 'gallery'].includes(this.localViewSetting?.layout) ? this.localViewSetting.layout : 'table' |
There was a problem hiding this comment.
Honor saved card layouts in context resources
When a saved view is rendered inside an application/context, src/pages/Context.vue passes the shared empty viewSetting into CustomView rather than the MainWrapper-initialized settings, so this computed property falls back to table and tiles/gallery views display as normal tables in that context. The layout should be derived from the view itself or initialized for every caller, not only the main view route.
Useful? React with 👍 / 👎.
Signed-off-by: Rello <github@scherello.de>
…t-modes-in-nextcloud-tables' into codex/implement-persistent-layout-modes-in-nextcloud-tables
Hello @enjeck , @jancborchardt it was corrected
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8588c7515a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Signed-off-by: Rello <github@scherello.de>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7666b30f5d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| computed: { | ||
| ...mapState(useTablesStore, ['appNavCollapsed']), | ||
| currentLayout() { | ||
| return ['tiles', 'gallery'].includes(this.localViewSetting?.layout) ? this.localViewSetting.layout : 'table' |
There was a problem hiding this comment.
Hydrate public shares with the saved card layout
When a saved tiles/gallery view is opened through a public link, PublicMainWrapper/PublicElement do not pass any viewSetting, and the public node data currently does not include layout or viewSettings; because the renderer only reads localViewSetting.layout here, public shares always fall back to table even for views that were saved as cards. Please initialize the public view setting from the persisted view data (or let this component consume the persisted element settings) so shared links render the selected layout.
Useful? React with 👍 / 👎.
| <div class="container"> | ||
| <table class="tables-list__table"> | ||
| <div class="container" :class="{ 'container--cards': currentLayout !== 'table' }"> | ||
| <table v-if="currentLayout === 'table'" class="tables-list__table"> |
There was a problem hiding this comment.
Keep the actions slot available in card layouts
For tiles or gallery, this v-if removes the whole table header, which is the only place that renders the named actions slot passed by View.vue, DataTable.vue, and PublicElement.vue. Those slots contain controls such as edit view, create column, import/export, share, and integration, so opening a card-layout view makes the main action menu disappear; render the actions somewhere outside the table header for card layouts.
Useful? React with 👍 / 👎.
|
@enjeck can you give it another look when possible? thank you |
| <div v-if="totalPages > 1" class="pagination-footer" :class="{'large-width': !appNavCollapsed || isMobile}"> | ||
| <div class="pagination-items"> | ||
| <NcButton type="tertiary" :disabled="totalPages === 1 || pageNumber <= 1" :aria-label="t('tables', 'Go to first page')" @click="pageNumber = 1"> | ||
| <template #icon> | ||
| <PageFirstIcon :size="20" /> | ||
| </template> | ||
| </NcButton> | ||
| <NcButton type="tertiary" :disabled="totalPages === 1 || pageNumber <= 1" :aria-label="t('tables', 'Go to previous page')" @click="pageNumber--"> | ||
| <template #icon> | ||
| <ChevronLeftIcon :size="20" /> | ||
| </template> | ||
| </NcButton> | ||
| <div class="page-number"> | ||
| <NcSelect | ||
| v-model="pageNumber" | ||
| :options="allPageNumbersArray" | ||
| :clearable="false" | ||
| :aria-label-combobox="t('tables', 'Page number')"> | ||
| <template #selected-option-container="{ option }"> | ||
| <span class="selected-page"> | ||
| {{ option.label }} of {{ totalPages }} | ||
| </span> | ||
| </template> | ||
| </NcSelect> | ||
| </div> | ||
| <NcButton type="tertiary" :disabled="totalPages === 1 || pageNumber >= totalPages" :aria-label="t('tables', 'Go to next page')" @click="pageNumber++"> | ||
| <template #icon> | ||
| <ChevronRightIcon :size="20" /> | ||
| </template> | ||
| </NcButton> | ||
| <NcButton type="tertiary" :disabled="totalPages === 1 || pageNumber >= totalPages" :aria-label="t('tables', 'Go to last page')" @click="pageNumber = totalPages"> | ||
| <template #icon> | ||
| <PageLastIcon :size="20" /> | ||
| </template> | ||
| </NcButton> | ||
| </div> |
There was a problem hiding this comment.
can we reuse PaginationBlock.vue ?
| updated() { | ||
| if (this.pageNumber > this.totalPages || this.totalPages === 1) { | ||
| this.pageNumber = this.totalPages | ||
| } | ||
| }, |
There was a problem hiding this comment.
Mutating reactive state in updated() could cause re-render loops, and the totalPages === 1 branch fires on every update. PaginationBlock handles this with a watcher
There was a problem hiding this comment.
ideally renamed to later version, since new migration files have been added since this was started
| * Ensures that card view settings reference columns that are part of the view. | ||
| * @throws InvalidArgumentException | ||
| */ | ||
| protected function assertCardSourceColumnsAreValid(View $view): void { |
There was a problem hiding this comment.
assertCardSourceColumnsAreValid() blocks unrelated view updates.
ViewService::update() calls assertCardSourceColumnsAreValid() on every update. That method re-validates the view's stored cardBackgroundSource and cardTitleSource against the view's column set even when the current request does not touch viewSettings at all.
The result is that once a card source points at a column, any later update that removes that column from the view by changing columnSettings gets rejected even though the request never mentions the card settings. The caller is then forced to also rewrite viewSettings in the same request just to make an unrelated column change succeed.
To reproduce:
# 1. View with two columns; cardTitleSource = column 8
PUT /index.php/apps/tables/api/1/views/3
{"data":{"columnSettings":[{"columnId":7,"order":0},{"columnId":8,"order":1}],
"layout":"gallery",
"viewSettings":{"cardBackgroundSource":7,"cardTitleSource":8}}}
→ 200 OK
# 2. Remove column 8 from the view, nothing else changed
PUT /index.php/apps/tables/api/1/views/3
{"data":{"columnSettings":[{"columnId":7,"order":0}]}}
→ 400 {"message":"Invalid cardTitleSource column ID: 8"}
# 3. Identical column change, but also repoint card sources to column 7
PUT /index.php/apps/tables/api/1/views/3
{"data":{"columnSettings":[{"columnId":7,"order":0}],
"viewSettings":{"cardBackgroundSource":7,"cardTitleSource":7}}}
→ 200 OK
The second and third requests send the same column edit, so the only thing that changes the outcome is whether viewSettings gets rewritten

Motivation
Tileis only showing the picture plus its title from the 2nd column. Use case: Index pagesGalleryview is showing all additional columns from the current view. Use case: Product catalogue or inventorytable,tilesandgalleryand have that preference stored with the view.table).Implementation
Source data
Tile View
Gallery View
Settings modal
Sharepoint reference