-
+
@@ -139,6 +139,10 @@ export default {
type: Object,
default: null,
},
+ showTitle: {
+ type: Boolean,
+ default: true,
+ },
},
diff --git a/src/shared/components/ncIconPicker/NamedIcon.vue b/src/shared/components/ncIconPicker/NamedIcon.vue
new file mode 100644
index 0000000000..c893b8f118
--- /dev/null
+++ b/src/shared/components/ncIconPicker/NamedIcon.vue
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
diff --git a/src/shared/utils/gridColors.js b/src/shared/utils/gridColors.js
new file mode 100644
index 0000000000..9e54849ce3
--- /dev/null
+++ b/src/shared/utils/gridColors.js
@@ -0,0 +1,14 @@
+/**
+ * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+/**
+ * Only hex colors are accepted in widget content, the same rule the backend applies.
+ *
+ * @param {string|null|undefined} value a color as entered by the user
+ * @return {boolean}
+ */
+export function isHexColor(value) {
+ return typeof value === 'string' && /^#[0-9a-fA-F]{3,6}$/.test(value)
+}
diff --git a/src/store/store.js b/src/store/store.js
index ce22251a6c..1a3ee499fd 100644
--- a/src/store/store.js
+++ b/src/store/store.js
@@ -11,6 +11,9 @@ import displayError from '../shared/utils/displayError.js'
import { NODE_TYPE_TABLE, NODE_TYPE_VIEW } from '../shared/constants.ts'
import { getCurrentUser } from '@nextcloud/auth'
+// The one request for the widget types while it runs, so widgets that mount together share it
+let widgetTypesRequest = null
+
export const useTablesStore = defineStore('store', {
state: () => ({
loading: {
@@ -27,6 +30,7 @@ export const useTablesStore = defineStore('store', {
activeRowId: null,
activeElementIsView: false,
activeContextId: null,
+ widgetTypes: [],
appNavCollapsed: false,
}),
@@ -516,6 +520,20 @@ export const useTablesStore = defineStore('store', {
return true
},
+ /**
+ * Loads the widget types the first time a grid needs them.
+ */
+ async loadWidgetTypes() {
+ if (this.widgetTypes.length > 0) {
+ return
+ }
+ widgetTypesRequest ??= axios.get(generateOcsUrl('/apps/tables/api/2/widget-types'))
+ .then(res => { this.widgetTypes = res.data.ocs.data })
+ .catch(e => displayError(e, t('tables', 'Could not load widget types.')))
+ .finally(() => { widgetTypesRequest = null })
+ await widgetTypesRequest
+ },
+
async getContextsTablesAndViews() {
for (const context of this.contexts) {
for (const node of Object.values(context?.nodes)) {