@conduction/nextcloud-vue 2.56.0-beta.31 → 2.56.0-beta.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +55 -4
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js +51 -15
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +13 -2
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +63 -52
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue.js +10 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue.js.map +1 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue2.js +252 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue3.js +140 -0
- package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnChipsFieldWidget/index.js +26 -0
- package/dist/esm/components/CnChipsFieldWidget/index.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue.js +10 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue2.js +354 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue3.js +172 -0
- package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnChoicePickerWidget/index.js +27 -0
- package/dist/esm/components/CnChoicePickerWidget/index.js.map +1 -0
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +25 -2
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +2 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +3 -2
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +29 -0
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +20 -3
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +7 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +77 -3
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js +282 -69
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +0 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +65 -2
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +6 -2
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue.js +10 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue2.js +174 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue3.js +75 -0
- package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnRadioCardsWidget/index.js +28 -0
- package/dist/esm/components/CnRadioCardsWidget/index.js.map +1 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +18 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +34 -25
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue.js +10 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue2.js +222 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue3.js +64 -0
- package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSavedViewsWidget/index.js +30 -0
- package/dist/esm/components/CnSavedViewsWidget/index.js.map +1 -0
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js.map +1 -1
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js.map +1 -1
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue.js +10 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue2.js +347 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue3.js +184 -0
- package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSettingsFormWidget/index.js +27 -0
- package/dist/esm/components/CnSettingsFormWidget/index.js.map +1 -0
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +87 -6
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue.js +10 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue2.js +159 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue3.js +60 -0
- package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSwitchRowsWidget/index.js +26 -0
- package/dist/esm/components/CnSwitchRowsWidget/index.js.map +1 -0
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +47 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +16 -14
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +80 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +75 -32
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js +10 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js.map +1 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js +269 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js +106 -0
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnToggleTableWidget/index.js +27 -0
- package/dist/esm/components/CnToggleTableWidget/index.js.map +1 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +7 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js +4 -2
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +1 -0
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +1 -0
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +843 -4
- package/dist/esm/utils/contentHeaderLink.js +51 -0
- package/dist/esm/utils/contentHeaderLink.js.map +1 -0
- package/dist/esm/utils/fetchFilterCounts.js +40 -5
- package/dist/esm/utils/fetchFilterCounts.js.map +1 -1
- package/dist/esm/utils/initials.js +32 -0
- package/dist/esm/utils/initials.js.map +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js +7 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/objectWrite.js +59 -0
- package/dist/esm/utils/objectWrite.js.map +1 -0
- package/dist/esm/utils/widgetDispatch.js +1 -1
- package/dist/esm/utils/widgetDispatch.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +10510 -6830
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +843 -4
- package/l10n/en.json +1 -0
- package/l10n/nl.json +1 -0
- package/package.json +1 -1
- package/src/components/CnBannerWidget/CnBannerWidget.vue +93 -13
- package/src/components/CnCellRenderer/CnCellRenderer.md +2 -0
- package/src/components/CnCellRenderer/CnCellRenderer.vue +43 -3
- package/src/components/CnChipsFieldWidget/CnChipsFieldWidget.vue +433 -0
- package/src/components/CnChipsFieldWidget/index.js +25 -0
- package/src/components/CnChoicePickerWidget/CnChoicePickerWidget.vue +556 -0
- package/src/components/CnChoicePickerWidget/index.js +26 -0
- package/src/components/CnDashboardPage/CnDashboardPage.vue +38 -2
- package/src/components/CnDataTable/CnDataTable.vue +2 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +45 -2
- package/src/components/CnFactsWidget/CnFactsWidget.vue +18 -3
- package/src/components/CnFieldListWidget/CnFieldListWidget.vue +265 -4
- package/src/components/CnIndexPage/CnIndexPage.vue +0 -1
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +69 -2
- package/src/components/CnRadioCardsWidget/CnRadioCardsWidget.vue +313 -0
- package/src/components/CnRadioCardsWidget/index.js +27 -0
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +43 -1
- package/src/components/CnSavedViewsWidget/CnSavedViewsWidget.vue +296 -0
- package/src/components/CnSavedViewsWidget/index.js +29 -0
- package/src/components/CnSegmentedControl/CnSegmentedControl.vue +13 -0
- package/src/components/CnSettingsFormWidget/CnSettingsFormWidget.vue +555 -0
- package/src/components/CnSettingsFormWidget/index.js +26 -0
- package/src/components/CnStatWidget/CnStatWidget.vue +87 -6
- package/src/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue +226 -0
- package/src/components/CnSwitchRowsWidget/index.js +25 -0
- package/src/components/CnTabsWidget/CnTabsWidget.vue +82 -3
- package/src/components/CnTasksWidget/CnTasksWidget.vue +175 -15
- package/src/components/CnToggleTableWidget/CnToggleTableWidget.vue +386 -0
- package/src/components/CnToggleTableWidget/index.js +26 -0
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +7 -0
- package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +28 -1
- package/src/components/index.js +7 -0
- package/src/css/look-board-detail.css +36 -2
- package/src/css/look-board-index.css +23 -0
- package/src/css/look-board.css +72 -0
- package/src/index.js +7 -0
- package/src/utils/contentHeaderLink.js +48 -0
- package/src/utils/fetchFilterCounts.js +39 -4
- package/src/utils/initials.js +29 -0
- package/src/utils/libraryWidgetKeys.js +7 -0
- package/src/utils/objectWrite.js +55 -0
- package/src/utils/widgetDispatch.js +1 -1
|
@@ -379,7 +379,7 @@
|
|
|
379
379
|
:showDocumentation="showDocumentation && menuShowsHelpLinks"
|
|
380
380
|
:actionsMenuLabel="boardMenuLabel"
|
|
381
381
|
:variant="isBoard ? 'secondary' : undefined"
|
|
382
|
-
:hasPrimaryItems="headerMenuGroups.length > 0 || showMarkUnread"
|
|
382
|
+
:hasPrimaryItems="headerMenuGroups.length > 0 || showMarkUnread || showOpenSidebar"
|
|
383
383
|
:documentationUrl="documentationUrl"
|
|
384
384
|
:docsAnchor="resolvedPageId"
|
|
385
385
|
:documentationLabel="documentationLabel || undefined"
|
|
@@ -390,7 +390,7 @@
|
|
|
390
390
|
testidBase="cn-detail-page"
|
|
391
391
|
@refresh="onHeaderRefresh"
|
|
392
392
|
@requestFeature="onHeaderRequestFeature">
|
|
393
|
-
<template v-if="headerMenuGroups.length || showMarkUnread" #primary-items>
|
|
393
|
+
<template v-if="headerMenuGroups.length || showMarkUnread || showOpenSidebar" #primary-items>
|
|
394
394
|
<!-- One block per group. A page that groups nothing has
|
|
395
395
|
a single captionless group, and renders exactly the
|
|
396
396
|
flat list it always did. -->
|
|
@@ -449,6 +449,20 @@
|
|
|
449
449
|
</template>
|
|
450
450
|
{{ markUnreadLabel }}
|
|
451
451
|
</NcActionButton>
|
|
452
|
+
<!-- Board look: the sidebar's own open toggle is hidden (it sat
|
|
453
|
+
over the header's last button, where the board draws none), so
|
|
454
|
+
the menu opens it. -->
|
|
455
|
+
<NcActionButton
|
|
456
|
+
v-if="showOpenSidebar"
|
|
457
|
+
key="open-sidebar"
|
|
458
|
+
data-testid="cn-detail-page-open-sidebar"
|
|
459
|
+
:closeAfterClick="true"
|
|
460
|
+
@click="openSidebar">
|
|
461
|
+
<template #icon>
|
|
462
|
+
<DockRight :size="20" />
|
|
463
|
+
</template>
|
|
464
|
+
{{ openSidebarLabel }}
|
|
465
|
+
</NcActionButton>
|
|
452
466
|
<!-- Divides the page's own actions from the help links
|
|
453
467
|
below. With the help links switched off there is
|
|
454
468
|
nothing below to divide from. -->
|
|
@@ -1191,6 +1205,7 @@ import Check from 'vue-material-design-icons/Check.vue'
|
|
|
1191
1205
|
import ChevronLeft from 'vue-material-design-icons/ChevronLeft.vue'
|
|
1192
1206
|
import ChevronRight from 'vue-material-design-icons/ChevronRight.vue'
|
|
1193
1207
|
import Cog from 'vue-material-design-icons/Cog.vue'
|
|
1208
|
+
import DockRight from 'vue-material-design-icons/DockRight.vue'
|
|
1194
1209
|
import EyeOffOutline from 'vue-material-design-icons/EyeOffOutline.vue'
|
|
1195
1210
|
import FileQuestionOutline from 'vue-material-design-icons/FileQuestionOutline.vue'
|
|
1196
1211
|
import InformationOutline from 'vue-material-design-icons/InformationOutline.vue'
|
|
@@ -1440,6 +1455,7 @@ export default {
|
|
|
1440
1455
|
CnSummaryAggregates,
|
|
1441
1456
|
CnDetailHeaderChips,
|
|
1442
1457
|
CnFollowToggle,
|
|
1458
|
+
DockRight,
|
|
1443
1459
|
EyeOffOutline,
|
|
1444
1460
|
CnRelatedCollections,
|
|
1445
1461
|
CnBodySections,
|
|
@@ -3909,6 +3925,22 @@ export default {
|
|
|
3909
3925
|
&& this.register !== '' && this.schema !== '' && this.objectId !== ''
|
|
3910
3926
|
},
|
|
3911
3927
|
|
|
3928
|
+
/**
|
|
3929
|
+
* Whether the header menu offers "Open sidebar": the board look, where the
|
|
3930
|
+
* sidebar's own toggle is hidden, on a page whose sidebar is active and
|
|
3931
|
+
* closed.
|
|
3932
|
+
*
|
|
3933
|
+
* @return {boolean}
|
|
3934
|
+
*/
|
|
3935
|
+
showOpenSidebar() {
|
|
3936
|
+
return this.isBoard && this.hasExternalSidebar && this.sidebarActive && this.objectSidebarState.open !== true
|
|
3937
|
+
},
|
|
3938
|
+
|
|
3939
|
+
/** @return {string} Label of the Open sidebar entry. */
|
|
3940
|
+
openSidebarLabel() {
|
|
3941
|
+
return t('nextcloud-vue', 'Open sidebar')
|
|
3942
|
+
},
|
|
3943
|
+
|
|
3912
3944
|
/** @return {string} Label of the Mark as unread entry. */
|
|
3913
3945
|
markUnreadLabel() {
|
|
3914
3946
|
return t('nextcloud-vue', 'Mark as unread')
|
|
@@ -4619,6 +4651,17 @@ export default {
|
|
|
4619
4651
|
},
|
|
4620
4652
|
|
|
4621
4653
|
methods: {
|
|
4654
|
+
/**
|
|
4655
|
+
* Open the object sidebar through the shared channel.
|
|
4656
|
+
*
|
|
4657
|
+
* @return {void}
|
|
4658
|
+
*/
|
|
4659
|
+
openSidebar() {
|
|
4660
|
+
if (this.objectSidebarState) {
|
|
4661
|
+
this.objectSidebarState.open = true
|
|
4662
|
+
}
|
|
4663
|
+
},
|
|
4664
|
+
|
|
4622
4665
|
/**
|
|
4623
4666
|
* Start or finish arranging the chosen view. Finishing stores the
|
|
4624
4667
|
* arrangement of every view the user moved, once.
|
|
@@ -169,7 +169,13 @@ function sharedRefLabels() {
|
|
|
169
169
|
return refLabels
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
/**
|
|
172
|
+
/**
|
|
173
|
+
* Key of a resolved reference in the widget's `labels` data.
|
|
174
|
+
*
|
|
175
|
+
* @param {{register: string, schema: string, labelField?: string}} resolve The entry's `resolve` config.
|
|
176
|
+
* @param {string} id The referenced object's id.
|
|
177
|
+
* @return {string} The key.
|
|
178
|
+
*/
|
|
173
179
|
function refKey(resolve, id) {
|
|
174
180
|
return `${resolve.register}/${resolve.schema}/${resolve.labelField || ''}/${id}`
|
|
175
181
|
}
|
|
@@ -1012,12 +1018,21 @@ export default {
|
|
|
1012
1018
|
display: contents;
|
|
1013
1019
|
}
|
|
1014
1020
|
|
|
1015
|
-
|
|
1021
|
+
/* Nextcloud core styles `dt` and `dd` (text-align end, 12px padding), which
|
|
1022
|
+
pushed the label to the right of its column and added 12px around both. The
|
|
1023
|
+
class is doubled so these resets outrank that rule, and a dossiq-style
|
|
1024
|
+
`:deep()` override in an app is not needed. */
|
|
1025
|
+
.cn-facts-widget__label.cn-facts-widget__label {
|
|
1026
|
+
margin: 0;
|
|
1027
|
+
padding: 0;
|
|
1028
|
+
text-align: start;
|
|
1016
1029
|
color: var(--color-text-maxcontrast);
|
|
1017
1030
|
}
|
|
1018
1031
|
|
|
1019
|
-
.cn-facts-widget__value {
|
|
1032
|
+
.cn-facts-widget__value.cn-facts-widget__value {
|
|
1020
1033
|
margin: 0;
|
|
1034
|
+
padding: 0;
|
|
1035
|
+
text-align: start;
|
|
1021
1036
|
font-weight: 500;
|
|
1022
1037
|
min-width: 0;
|
|
1023
1038
|
overflow-wrap: anywhere;
|
|
@@ -5,9 +5,123 @@
|
|
|
5
5
|
|
|
6
6
|
<template>
|
|
7
7
|
<div class="cn-field-list-widget" data-testid="cn-field-list-widget">
|
|
8
|
+
<div v-if="canAdd && addTrigger && addPlacement === 'header'" class="cn-field-list-widget__toolbar">
|
|
9
|
+
<NcButton
|
|
10
|
+
variant="secondary"
|
|
11
|
+
:disabled="busy !== ''"
|
|
12
|
+
data-testid="cn-field-list-open-add"
|
|
13
|
+
@click="adding = !adding">
|
|
14
|
+
+ {{ addLabel }}
|
|
15
|
+
</NcButton>
|
|
16
|
+
</div>
|
|
17
|
+
<form
|
|
18
|
+
v-if="canAdd && addTrigger && addPlacement === 'header' && adding"
|
|
19
|
+
class="cn-field-list-widget__add"
|
|
20
|
+
data-testid="cn-field-list-add"
|
|
21
|
+
@submit.prevent="add">
|
|
22
|
+
<div
|
|
23
|
+
v-for="field in addFields"
|
|
24
|
+
:key="field.key"
|
|
25
|
+
class="cn-field-list-widget__field"
|
|
26
|
+
:data-testid="`cn-field-list-input-${field.key}`">
|
|
27
|
+
<NcSelect
|
|
28
|
+
v-if="field.type === 'select'"
|
|
29
|
+
v-model="draft[field.key]"
|
|
30
|
+
:inputLabel="field.label"
|
|
31
|
+
:options="field.options"
|
|
32
|
+
:reduce="(option) => option.value"
|
|
33
|
+
:clearable="false"
|
|
34
|
+
label="label" />
|
|
35
|
+
<NcTextField
|
|
36
|
+
v-else
|
|
37
|
+
v-model="draft[field.key]"
|
|
38
|
+
:label="field.label"
|
|
39
|
+
:error="Boolean(errors[field.key])"
|
|
40
|
+
:helperText="errors[field.key] || ''" />
|
|
41
|
+
</div>
|
|
42
|
+
<NcButton type="submit"
|
|
43
|
+
variant="primary"
|
|
44
|
+
:disabled="busy !== ''"
|
|
45
|
+
data-testid="cn-field-list-submit">
|
|
46
|
+
{{ addLabel }}
|
|
47
|
+
</NcButton>
|
|
48
|
+
</form>
|
|
8
49
|
<p v-if="rows.length === 0" class="cn-field-list-widget__empty" data-testid="cn-field-list-empty">
|
|
9
50
|
{{ emptyLabel }}
|
|
10
51
|
</p>
|
|
52
|
+
<div v-else-if="isTable" class="cn-field-list-widget__table-wrap">
|
|
53
|
+
<table class="cn-field-list-widget__table" data-testid="cn-field-list-table">
|
|
54
|
+
<thead>
|
|
55
|
+
<tr>
|
|
56
|
+
<th
|
|
57
|
+
v-for="column in tableColumns"
|
|
58
|
+
:key="column.key"
|
|
59
|
+
scope="col"
|
|
60
|
+
class="cn-field-list-widget__th">
|
|
61
|
+
{{ column.header }}
|
|
62
|
+
</th>
|
|
63
|
+
<th v-if="hasMenu" scope="col" class="cn-field-list-widget__th cn-field-list-widget__th--menu">
|
|
64
|
+
<span class="cn-field-list-widget__sr">{{ tr('Actions') }}</span>
|
|
65
|
+
</th>
|
|
66
|
+
</tr>
|
|
67
|
+
</thead>
|
|
68
|
+
<tbody>
|
|
69
|
+
<tr
|
|
70
|
+
v-for="row in rows"
|
|
71
|
+
:key="row.key"
|
|
72
|
+
class="cn-field-list-widget__tr"
|
|
73
|
+
:data-testid="`cn-field-list-row-${row.key}`">
|
|
74
|
+
<td
|
|
75
|
+
v-for="(column, index) in tableColumns"
|
|
76
|
+
:key="column.key"
|
|
77
|
+
class="cn-field-list-widget__td">
|
|
78
|
+
<CnStatusBadge
|
|
79
|
+
v-if="column.pill && row.cells[index].pill"
|
|
80
|
+
:label="row.cells[index].pill.label"
|
|
81
|
+
:variant="row.cells[index].pill.variant"
|
|
82
|
+
size="small" />
|
|
83
|
+
<strong v-else-if="column.strong">{{ row.cells[index].text }}</strong>
|
|
84
|
+
<template v-else>
|
|
85
|
+
{{ row.cells[index].text }}
|
|
86
|
+
</template>
|
|
87
|
+
</td>
|
|
88
|
+
<td v-if="hasMenu" class="cn-field-list-widget__td cn-field-list-widget__td--menu">
|
|
89
|
+
<NcActions
|
|
90
|
+
v-if="menuItems(row).length > 0"
|
|
91
|
+
:forceMenu="true"
|
|
92
|
+
:disabled="busy !== ''"
|
|
93
|
+
:ariaLabel="`${tr('More actions')}: ${row.title}`"
|
|
94
|
+
:data-testid="`cn-field-list-menu-${row.key}`">
|
|
95
|
+
<NcActionButton
|
|
96
|
+
v-for="item in menuItems(row)"
|
|
97
|
+
:key="item.id"
|
|
98
|
+
:data-testid="`cn-field-list-menu-${item.id}-${row.key}`"
|
|
99
|
+
@click="item.run">
|
|
100
|
+
{{ item.label }}
|
|
101
|
+
</NcActionButton>
|
|
102
|
+
</NcActions>
|
|
103
|
+
</td>
|
|
104
|
+
</tr>
|
|
105
|
+
</tbody>
|
|
106
|
+
</table>
|
|
107
|
+
<div
|
|
108
|
+
v-for="row in bandRows"
|
|
109
|
+
:key="row.key"
|
|
110
|
+
class="cn-field-list-widget__band"
|
|
111
|
+
role="note"
|
|
112
|
+
:data-testid="`cn-field-list-band-${row.key}`">
|
|
113
|
+
<span class="cn-field-list-widget__band-text">{{ row.detail }}</span>
|
|
114
|
+
<NcButton
|
|
115
|
+
v-for="action in row.bandActions"
|
|
116
|
+
:key="action.id"
|
|
117
|
+
variant="secondary"
|
|
118
|
+
:disabled="busy !== ''"
|
|
119
|
+
:data-testid="`cn-field-list-band-action-${action.id}-${row.key}`"
|
|
120
|
+
@click="runAction(action, row)">
|
|
121
|
+
{{ action.label }}
|
|
122
|
+
</NcButton>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
11
125
|
<ul v-else class="cn-field-list-widget__rows">
|
|
12
126
|
<li
|
|
13
127
|
v-for="row in rows"
|
|
@@ -55,8 +169,17 @@
|
|
|
55
169
|
{{ hint }}
|
|
56
170
|
</p>
|
|
57
171
|
|
|
172
|
+
<div v-if="canAdd && addTrigger && addPlacement !== 'header' && !adding" class="cn-field-list-widget__toolbar">
|
|
173
|
+
<NcButton
|
|
174
|
+
variant="secondary"
|
|
175
|
+
:disabled="busy !== ''"
|
|
176
|
+
data-testid="cn-field-list-open-add"
|
|
177
|
+
@click="adding = true">
|
|
178
|
+
+ {{ addLabel }}
|
|
179
|
+
</NcButton>
|
|
180
|
+
</div>
|
|
58
181
|
<form
|
|
59
|
-
v-if="canAdd"
|
|
182
|
+
v-if="canAdd && !(addTrigger && addPlacement === 'header') && (!addTrigger || adding)"
|
|
60
183
|
class="cn-field-list-widget__add"
|
|
61
184
|
data-testid="cn-field-list-add"
|
|
62
185
|
@submit.prevent="add">
|
|
@@ -95,7 +218,7 @@
|
|
|
95
218
|
</template>
|
|
96
219
|
|
|
97
220
|
<script>
|
|
98
|
-
import { NcButton, NcNoteCard, NcSelect, NcTextField } from '@nextcloud/vue'
|
|
221
|
+
import { NcActionButton, NcActions, NcButton, NcNoteCard, NcSelect, NcTextField } from '@nextcloud/vue'
|
|
99
222
|
import CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'
|
|
100
223
|
import { isBadgeVariant } from '../../utils/badgeVariants.js'
|
|
101
224
|
import { formatBoardDate } from '../../utils/boardDate.js'
|
|
@@ -125,7 +248,7 @@ import { serverErrorMessage } from '../../utils/serverErrorMessage.js'
|
|
|
125
248
|
export default {
|
|
126
249
|
name: 'CnFieldListWidget',
|
|
127
250
|
|
|
128
|
-
components: { CnStatusBadge, NcButton, NcNoteCard, NcSelect, NcTextField },
|
|
251
|
+
components: { CnStatusBadge, NcActionButton, NcActions, NcButton, NcNoteCard, NcSelect, NcTextField },
|
|
129
252
|
|
|
130
253
|
inject: {
|
|
131
254
|
cnTranslate: { default: () => (key) => key },
|
|
@@ -140,8 +263,10 @@ export default {
|
|
|
140
263
|
* - `row` — `{ title, titleFallback?, avatar?, meta?: [paths|{ path, map?, format? }], pill?: { path, map?, variantMap? }, aside?: { path, format?, prefix?, fallback? }, detail?: { template, when? } }`.
|
|
141
264
|
* - `add` — `{ label?, fields: [{ key, label, type?: 'text'|'select', options?, required?, pattern?, patternMessage? }], unique?, defaults? }`.
|
|
142
265
|
* - `remove` — `{ label? }`; omitted = no remove.
|
|
143
|
-
* - `rowActions` — `[{ id, label, when?, request: { method?, url, payload? } }]`; tokens `@object.<field>`, `@objectId`, `@row.<field>`.
|
|
266
|
+
* - `rowActions` — `[{ id, label, when?, placement?: 'band', request: { method?, url, payload? } }]`; tokens `@object.<field>`, `@objectId`, `@row.<field>`.
|
|
144
267
|
* - `rowSource` — `{ url, path?, key }`: live per-row state merged by key.
|
|
268
|
+
* - `variant: 'table'` + `columns` — `[{ header, path, map?, format?, strong?, pill?, variantMap? }]`: rows as a table; a per-row "..." menu holds the row actions and remove; `detail` lines become a warning band under the table, with the row actions that have `placement: 'band'`.
|
|
269
|
+
* - `add.trigger: 'button'`, `add.placement: 'header' | 'footer'` — the add form opens from a button.
|
|
145
270
|
* - `readOnly`, `hint`, `emptyText`.
|
|
146
271
|
*
|
|
147
272
|
* @type {object}
|
|
@@ -185,6 +310,8 @@ export default {
|
|
|
185
310
|
/** Live per-row state from `rowSource`, by key. */
|
|
186
311
|
live: {},
|
|
187
312
|
draft: {},
|
|
313
|
+
/** Whether the add form is open (only with `add.trigger: 'button'`). */
|
|
314
|
+
adding: false,
|
|
188
315
|
errors: {},
|
|
189
316
|
busy: '',
|
|
190
317
|
notice: '',
|
|
@@ -232,6 +359,7 @@ export default {
|
|
|
232
359
|
aside: spec.aside ? this.asideOf(entry, spec.aside) : '',
|
|
233
360
|
detail: spec.detail && this.matches(entry, spec.detail.when) ? this.fill(spec.detail.template, entry) : '',
|
|
234
361
|
actions: this.actionsFor(entry),
|
|
362
|
+
cells: this.isTable ? this.cellsOf(entry) : [],
|
|
235
363
|
}
|
|
236
364
|
})
|
|
237
365
|
},
|
|
@@ -269,6 +397,44 @@ export default {
|
|
|
269
397
|
}))
|
|
270
398
|
},
|
|
271
399
|
|
|
400
|
+
/** Whether the rows are drawn as a table (`variant: 'table'`). */
|
|
401
|
+
isTable() {
|
|
402
|
+
return this.content.variant === 'table' && this.tableColumns.length > 0
|
|
403
|
+
},
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* The table columns, headers translated.
|
|
407
|
+
*
|
|
408
|
+
* @return {Array<object>}
|
|
409
|
+
*/
|
|
410
|
+
tableColumns() {
|
|
411
|
+
return (Array.isArray(this.content.columns) ? this.content.columns : [])
|
|
412
|
+
.filter((column) => column && column.path)
|
|
413
|
+
.map((column, index) => ({ ...column, key: `${column.path}-${index}`, header: this.tr(column.header || '') }))
|
|
414
|
+
},
|
|
415
|
+
|
|
416
|
+
/** Whether each row has a "more actions" menu (row actions not placed in the band, and remove). */
|
|
417
|
+
hasMenu() {
|
|
418
|
+
return this.isTable && !this.readOnly && (this.canRemove || this.rows.some((row) => this.menuItems(row).length > 0))
|
|
419
|
+
},
|
|
420
|
+
|
|
421
|
+
/** Rows with a detail line: in the table variant it is a warning band under the table. */
|
|
422
|
+
bandRows() {
|
|
423
|
+
return this.rows
|
|
424
|
+
.filter((row) => row.detail !== '')
|
|
425
|
+
.map((row) => ({ ...row, bandActions: row.actions.filter((action) => action.placement === 'band') }))
|
|
426
|
+
},
|
|
427
|
+
|
|
428
|
+
/** Whether the add form opens from a button (`add.trigger: 'button'`). */
|
|
429
|
+
addTrigger() {
|
|
430
|
+
return Boolean(this.content.add && this.content.add.trigger === 'button')
|
|
431
|
+
},
|
|
432
|
+
|
|
433
|
+
/** Where the add button sits: `header` (above the rows) or `footer` (default). */
|
|
434
|
+
addPlacement() {
|
|
435
|
+
return this.content.add && this.content.add.placement === 'header' ? 'header' : 'footer'
|
|
436
|
+
},
|
|
437
|
+
|
|
272
438
|
addLabel() {
|
|
273
439
|
return this.tr((this.content.add && this.content.add.label) || 'Add')
|
|
274
440
|
},
|
|
@@ -315,6 +481,36 @@ export default {
|
|
|
315
481
|
return typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text
|
|
316
482
|
},
|
|
317
483
|
|
|
484
|
+
/**
|
|
485
|
+
* The cells of one table row, one per column.
|
|
486
|
+
*
|
|
487
|
+
* @param {object} entry The entry.
|
|
488
|
+
* @return {Array<{text: string, pill: object|null}>}
|
|
489
|
+
*/
|
|
490
|
+
cellsOf(entry) {
|
|
491
|
+
return this.tableColumns.map((column) => ({
|
|
492
|
+
text: this.text(entry, column),
|
|
493
|
+
pill: column.pill ? this.pillOf(entry, column) : null,
|
|
494
|
+
}))
|
|
495
|
+
},
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* The items of a row's menu in the table variant: the row actions not
|
|
499
|
+
* placed in the band, then remove.
|
|
500
|
+
*
|
|
501
|
+
* @param {object} row The row.
|
|
502
|
+
* @return {Array<{id: string, label: string, run: Function}>}
|
|
503
|
+
*/
|
|
504
|
+
menuItems(row) {
|
|
505
|
+
const items = row.actions
|
|
506
|
+
.filter((action) => action.placement !== 'band')
|
|
507
|
+
.map((action) => ({ id: action.id, label: action.label, run: () => this.runAction(action, row) }))
|
|
508
|
+
if (this.canRemove) {
|
|
509
|
+
items.push({ id: 'remove', label: this.removeLabel, run: () => this.remove(row) })
|
|
510
|
+
}
|
|
511
|
+
return items
|
|
512
|
+
},
|
|
513
|
+
|
|
318
514
|
/**
|
|
319
515
|
* The identity of an entry.
|
|
320
516
|
*
|
|
@@ -599,6 +795,7 @@ export default {
|
|
|
599
795
|
}, '*')
|
|
600
796
|
if (saved) {
|
|
601
797
|
this.resetDraft()
|
|
798
|
+
this.adding = false
|
|
602
799
|
this.loadLive()
|
|
603
800
|
}
|
|
604
801
|
},
|
|
@@ -744,4 +941,68 @@ export default {
|
|
|
744
941
|
.cn-field-list-widget__add > button {
|
|
745
942
|
flex: 0 0 auto;
|
|
746
943
|
}
|
|
944
|
+
|
|
945
|
+
.cn-field-list-widget__toolbar {
|
|
946
|
+
display: flex;
|
|
947
|
+
justify-content: flex-end;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
.cn-field-list-widget__table-wrap {
|
|
951
|
+
display: flex;
|
|
952
|
+
flex-direction: column;
|
|
953
|
+
gap: 12px;
|
|
954
|
+
overflow-x: auto;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
.cn-field-list-widget__table {
|
|
958
|
+
width: 100%;
|
|
959
|
+
border-collapse: collapse;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
.cn-field-list-widget__th {
|
|
963
|
+
padding: 8px 12px;
|
|
964
|
+
text-align: start;
|
|
965
|
+
font-size: 13px;
|
|
966
|
+
font-weight: 600;
|
|
967
|
+
white-space: nowrap;
|
|
968
|
+
color: var(--color-text-maxcontrast);
|
|
969
|
+
background: var(--color-background-hover);
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
.cn-field-list-widget__th--menu,
|
|
973
|
+
.cn-field-list-widget__td--menu {
|
|
974
|
+
width: 1%;
|
|
975
|
+
text-align: end;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
.cn-field-list-widget__tr {
|
|
979
|
+
border-top: 1px solid var(--cn-board-hairline, var(--color-border));
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
.cn-field-list-widget__td {
|
|
983
|
+
padding: 10px 12px;
|
|
984
|
+
vertical-align: middle;
|
|
985
|
+
overflow-wrap: anywhere;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
.cn-field-list-widget__sr {
|
|
989
|
+
position: absolute;
|
|
990
|
+
width: 1px;
|
|
991
|
+
height: 1px;
|
|
992
|
+
overflow: hidden;
|
|
993
|
+
clip-path: inset(50%);
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
.cn-field-list-widget__band {
|
|
997
|
+
display: flex;
|
|
998
|
+
flex-wrap: wrap;
|
|
999
|
+
align-items: center;
|
|
1000
|
+
justify-content: space-between;
|
|
1001
|
+
gap: 8px 12px;
|
|
1002
|
+
padding: 12px 14px;
|
|
1003
|
+
border: 1px solid var(--color-warning);
|
|
1004
|
+
border-radius: 10px;
|
|
1005
|
+
background: color-mix(in srgb, var(--color-warning) 14%, var(--color-main-background));
|
|
1006
|
+
font-size: 14px;
|
|
1007
|
+
}
|
|
747
1008
|
</style>
|
|
@@ -1483,7 +1483,6 @@ export default {
|
|
|
1483
1483
|
default: true,
|
|
1484
1484
|
},
|
|
1485
1485
|
|
|
1486
|
-
// eslint-disable-next-line vue/no-unused-properties
|
|
1487
1486
|
/**
|
|
1488
1487
|
* Draw this page in the `board` look (or `nextcloud`) whatever the app
|
|
1489
1488
|
* does. Empty follows the `cnLook` CnAppRoot provides. Manifest key
|
|
@@ -151,6 +151,8 @@
|
|
|
151
151
|
:sortKey="localSort.field || null"
|
|
152
152
|
:sortOrder="localSort.dir || 'asc'"
|
|
153
153
|
borderless
|
|
154
|
+
:fitWidth="fitWidthActive"
|
|
155
|
+
:fixedLayout="fixedLayoutActive"
|
|
154
156
|
:hideHeader="content.hideHeader === true"
|
|
155
157
|
@rowClick="onRowClick"
|
|
156
158
|
@rowAuxClick="onRowAuxClick"
|
|
@@ -173,6 +175,8 @@
|
|
|
173
175
|
:sortKey="localSort.field || null"
|
|
174
176
|
:sortOrder="localSort.dir || 'asc'"
|
|
175
177
|
borderless
|
|
178
|
+
:fitWidth="fitWidthActive"
|
|
179
|
+
:fixedLayout="fixedLayoutActive"
|
|
176
180
|
:hideHeader="content.hideHeader === true"
|
|
177
181
|
@rowClick="onRowClick"
|
|
178
182
|
@rowAuxClick="onRowAuxClick"
|
|
@@ -448,6 +452,15 @@ export default {
|
|
|
448
452
|
page: 1,
|
|
449
453
|
/** Rows that fit the host cell; null = unconstrained (dashboards). */
|
|
450
454
|
fitRows: null,
|
|
455
|
+
/**
|
|
456
|
+
* How far the table has been squeezed to the widget's width, so the
|
|
457
|
+
* card never scrolls sideways: 0 = its own auto layout, 1 = one column
|
|
458
|
+
* takes what is left and is cut with an ellipsis while the rest keep
|
|
459
|
+
* their content on one line, 2 = a fixed layout, every column an equal
|
|
460
|
+
* share with its text wrapped. Each step is taken only while the table
|
|
461
|
+
* still scrolls sideways. `content.fitWidth` forces stage 1 on or off.
|
|
462
|
+
*/
|
|
463
|
+
fitStage: 0,
|
|
451
464
|
/** Whether the create dialog is open. */
|
|
452
465
|
showCreate: false,
|
|
453
466
|
/**
|
|
@@ -480,6 +493,27 @@ export default {
|
|
|
480
493
|
},
|
|
481
494
|
|
|
482
495
|
computed: {
|
|
496
|
+
/**
|
|
497
|
+
* Whether the table fits the widget's width (stage 1): `content.fitWidth`
|
|
498
|
+
* when it says, else once the columns were measured not to fit.
|
|
499
|
+
*
|
|
500
|
+
* @return {boolean}
|
|
501
|
+
*/
|
|
502
|
+
fitWidthActive() {
|
|
503
|
+
const forced = this.content?.fitWidth
|
|
504
|
+
return typeof forced === 'boolean' ? forced : this.fitStage === 1
|
|
505
|
+
},
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Whether the table takes a fixed layout, the last resort when even the
|
|
509
|
+
* fitted table is wider than the widget (not with `content.fitWidth` set).
|
|
510
|
+
*
|
|
511
|
+
* @return {boolean}
|
|
512
|
+
*/
|
|
513
|
+
fixedLayoutActive() {
|
|
514
|
+
return typeof this.content?.fitWidth !== 'boolean' && this.fitStage >= 2
|
|
515
|
+
},
|
|
516
|
+
|
|
483
517
|
/**
|
|
484
518
|
* The unwrapped detail-page object context for token resolution, or null
|
|
485
519
|
* on surfaces (dashboards) that don't provide one.
|
|
@@ -619,7 +653,7 @@ export default {
|
|
|
619
653
|
// `secondary` (a second, muted line under the cell) rides
|
|
620
654
|
// along too: the table draws it, and dropping it here took the
|
|
621
655
|
// list rows' second line away (zuiddrecht-pixel-gaps-3).
|
|
622
|
-
for (const k of ['format', 'widget', 'widgetProps', 'formatter', 'align', 'width', 'type', 'enum', 'enumLabels', 'sortable', 'secondary', 'colorMap']) {
|
|
656
|
+
for (const k of ['format', 'widget', 'widgetProps', 'formatter', 'align', 'width', 'type', 'enum', 'enumLabels', 'sortable', 'secondary', 'colorMap', 'grow']) {
|
|
623
657
|
if (c[k] !== undefined) {
|
|
624
658
|
out[k] = c[k]
|
|
625
659
|
}
|
|
@@ -1165,6 +1199,12 @@ export default {
|
|
|
1165
1199
|
mounted() {
|
|
1166
1200
|
this.fetchRows()
|
|
1167
1201
|
this.loadHeadingsIfNeeded()
|
|
1202
|
+
// A table wider than the widget fits its columns to the width instead of
|
|
1203
|
+
// scrolling sideways (no inner scrollbars).
|
|
1204
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
1205
|
+
this._widthObserver = new ResizeObserver(() => this.measureWidthFit())
|
|
1206
|
+
this._widthObserver.observe(this.$el)
|
|
1207
|
+
}
|
|
1168
1208
|
// Re-read on a page-level refresh. This list fetches its own rows from
|
|
1169
1209
|
// OpenRegister and subscribed to nothing, so after a write elsewhere on
|
|
1170
1210
|
// the page it went on rendering the result set it fetched on mount —
|
|
@@ -1203,6 +1243,9 @@ export default {
|
|
|
1203
1243
|
if (this._fitObserver) {
|
|
1204
1244
|
this._fitObserver.disconnect()
|
|
1205
1245
|
}
|
|
1246
|
+
if (this._widthObserver) {
|
|
1247
|
+
this._widthObserver.disconnect()
|
|
1248
|
+
}
|
|
1206
1249
|
},
|
|
1207
1250
|
|
|
1208
1251
|
methods: {
|
|
@@ -1277,7 +1320,10 @@ export default {
|
|
|
1277
1320
|
const res = await axios.get(url, { params })
|
|
1278
1321
|
this.rows = (res && res.data && res.data.results) || []
|
|
1279
1322
|
this.total = (res && res.data && typeof res.data.total === 'number') ? res.data.total : this.rows.length
|
|
1280
|
-
this.$nextTick(() =>
|
|
1323
|
+
this.$nextTick(() => {
|
|
1324
|
+
this.measureFit()
|
|
1325
|
+
this.measureWidthFit()
|
|
1326
|
+
})
|
|
1281
1327
|
} catch (e) {
|
|
1282
1328
|
// Keep the raw message OUT of the template — log it for
|
|
1283
1329
|
// debugging, surface only the quiet `loadErrorLabel` line.
|
|
@@ -1291,6 +1337,27 @@ export default {
|
|
|
1291
1337
|
}
|
|
1292
1338
|
},
|
|
1293
1339
|
|
|
1340
|
+
/**
|
|
1341
|
+
* Squeeze the table to the widget's width while it scrolls sideways: its
|
|
1342
|
+
* `.cn-data-table__scroll` wrapper is wider than it is visible. First the
|
|
1343
|
+
* fit (stage 1), then, if that is still too wide, a fixed layout
|
|
1344
|
+
* (stage 2). It never goes back (a squeezed table does not overflow, so
|
|
1345
|
+
* there is nothing to measure to relax it). No-op when `content.fitWidth`
|
|
1346
|
+
* is set.
|
|
1347
|
+
*
|
|
1348
|
+
* @return {void}
|
|
1349
|
+
*/
|
|
1350
|
+
measureWidthFit() {
|
|
1351
|
+
if (this.fitStage >= 2 || typeof this.content?.fitWidth === 'boolean') {
|
|
1352
|
+
return
|
|
1353
|
+
}
|
|
1354
|
+
const scroller = this.$el && this.$el.querySelector ? this.$el.querySelector('.cn-data-table__scroll') : null
|
|
1355
|
+
if (scroller && scroller.scrollWidth > scroller.clientWidth + 1) {
|
|
1356
|
+
this.fitStage += 1
|
|
1357
|
+
this.$nextTick(() => this.measureWidthFit())
|
|
1358
|
+
}
|
|
1359
|
+
},
|
|
1360
|
+
|
|
1294
1361
|
/**
|
|
1295
1362
|
* Fit the visible row count to the host grid cell (ADR-062 — the cell
|
|
1296
1363
|
* is the budget). Measures the cell's remaining height below the table
|