@conduction/nextcloud-vue 2.83.0 → 2.85.0
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/CnAdminSettingsShell/CnAdminSettingsShell.vue.js +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js +131 -3
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +54 -18
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue.js +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js.map +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue3.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue.js +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +232 -4
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +95 -51
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
- package/dist/esm/components/CnBodySections/CnBodySections.vue.js +1 -1
- package/dist/esm/components/CnBodySections/CnBodySections.vue2.js +22 -1
- package/dist/esm/components/CnBodySections/CnBodySections.vue2.js.map +1 -1
- package/dist/esm/components/CnBodySections/CnBodySections.vue3.js.map +1 -1
- package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue.js +1 -1
- package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue2.js +47 -4
- package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue.js +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js +239 -3
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js +5 -5
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +50 -0
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +22 -12
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue.js +1 -1
- package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue2.js +1 -1
- package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnDeltaWidget/CnDeltaWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +226 -8
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +3 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +18 -5
- 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.vue.js +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +38 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js +45 -5
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue.js +1 -1
- package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue2.js +1 -1
- package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnGaugeWidget/CnGaugeWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +548 -77
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +191 -82
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/useSelfFetchList.js +11 -2
- package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js +29 -6
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue.js +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js +126 -2
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js +24 -2
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +106 -3
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue.js +1 -1
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue2.js +67 -5
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue2.js.map +1 -1
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue3.js +181 -124
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue3.js.map +1 -1
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue.js +1 -1
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue2.js +135 -2
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue3.js +95 -43
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue.js +1 -1
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js +5 -1
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue.js +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js +72 -5
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js +33 -8
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue.js +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +135 -11
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +6 -2
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue.js +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js +17 -2
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +39 -6
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js +1 -1
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js +140 -7
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue.js +1 -1
- package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue2.js +37 -9
- package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue2.js.map +1 -1
- package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js +6 -5
- package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js.map +1 -1
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue.js +1 -1
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js +16 -0
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js.map +1 -1
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js.map +1 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue.js +1 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js +2 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetCardGrid/CnWidgetCardGrid.vue2.js +53 -1
- package/dist/esm/components/CnWidgetCardGrid/CnWidgetCardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetCardGrid/CnWidgetCardGrid.vue3.js +3 -2
- package/dist/esm/components/CnWidgetCardGrid/CnWidgetCardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue.js +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +135 -0
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue.js +1 -1
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js +19 -0
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js +37 -19
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js.map +1 -1
- package/dist/esm/composables/useListView.js +21 -0
- package/dist/esm/composables/useListView.js.map +1 -1
- package/dist/esm/dialogs/CnTransitionInputDialog.vue.js +1 -1
- package/dist/esm/dialogs/CnTransitionInputDialog.vue2.js +68 -6
- package/dist/esm/dialogs/CnTransitionInputDialog.vue2.js.map +1 -1
- package/dist/esm/dialogs/CnTransitionInputDialog.vue3.js +133 -105
- package/dist/esm/dialogs/CnTransitionInputDialog.vue3.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +38 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +78 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +1030 -533
- package/dist/esm/store/useObjectStore.js +16 -2
- package/dist/esm/store/useObjectStore.js.map +1 -1
- package/dist/esm/utils/boardColumns.js +8 -1
- package/dist/esm/utils/boardColumns.js.map +1 -1
- package/dist/esm/utils/boardReferenceColumns.js +278 -0
- package/dist/esm/utils/boardReferenceColumns.js.map +1 -0
- package/dist/esm/utils/buildManifest.js +41 -4
- package/dist/esm/utils/buildManifest.js.map +1 -1
- package/dist/esm/utils/detailWidgetVisibility.js +149 -0
- package/dist/esm/utils/detailWidgetVisibility.js.map +1 -0
- package/dist/esm/utils/formatMetric.js +39 -3
- package/dist/esm/utils/formatMetric.js.map +1 -1
- package/dist/esm/utils/gridStack.js +40 -1
- package/dist/esm/utils/gridStack.js.map +1 -1
- package/dist/esm/utils/humaniseKey.js +41 -0
- package/dist/esm/utils/humaniseKey.js.map +1 -0
- package/dist/esm/utils/notificationRuleLabel.js +6 -23
- package/dist/esm/utils/notificationRuleLabel.js.map +1 -1
- package/dist/esm/utils/resolveManifestTokens.js +1 -0
- package/dist/esm/utils/resolveManifestTokens.js.map +1 -1
- package/dist/esm/utils/sentinelTokens.js +9 -0
- package/dist/esm/utils/sentinelTokens.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/widgetDispatch.js +8 -2
- package/dist/esm/utils/widgetDispatch.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +17159 -13422
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +1030 -533
- package/l10n/en.json +38 -1
- package/l10n/nl.json +78 -1
- package/package.json +1 -1
- package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +160 -8
- package/src/components/CnAuditTrailCard/CnAuditTrailCard.vue +1 -1
- package/src/components/CnBoardView/CnBoardView.vue +262 -6
- package/src/components/CnBodySections/CnBodySections.vue +22 -1
- package/src/components/CnCountdownWidget/CnCountdownWidget.vue +47 -4
- package/src/components/CnDashboardGrid/CnDashboardGrid.vue +244 -8
- package/src/components/CnDashboardPage/CnDashboardPage.vue +62 -2
- package/src/components/CnDeltaWidget/CnDeltaWidget.vue +1 -1
- package/src/components/CnDetailPage/CnDetailPage.md +3 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +226 -8
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +3 -1
- package/src/components/CnFactsWidget/CnFactsWidget.vue +18 -5
- package/src/components/CnFieldListWidget/CnFieldListWidget.vue +63 -1
- package/src/components/CnGaugeWidget/CnGaugeWidget.vue +1 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +586 -25
- package/src/components/CnIndexPage/useSelfFetchList.js +10 -1
- package/src/components/CnInteractionFormWidget/CnInteractionFormWidget.vue +29 -6
- package/src/components/CnObjectDataWidget/CnObjectDataWidget.md +1 -1
- package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +154 -5
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +106 -3
- package/src/components/CnRegisterMapping/CnRegisterMapping.md +5 -0
- package/src/components/CnRegisterMapping/CnRegisterMapping.vue +231 -96
- package/src/components/CnReportsPage/CnReportsPage.vue +179 -1
- package/src/components/CnSettingsPage/CnSettingsPage.vue +5 -1
- package/src/components/CnStagesWidget/CnStagesWidget.vue +103 -6
- package/src/components/CnStatWidget/CnStatWidget.vue +137 -12
- package/src/components/CnStatsBlockWidget/CnStatsBlockWidget.vue +34 -3
- package/src/components/CnTabsWidget/CnTabsWidget.vue +39 -6
- package/src/components/CnToggleTableWidget/CnToggleTableWidget.vue +140 -7
- package/src/components/CnVersionInfoCard/CnVersionInfoCard.md +2 -1
- package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +42 -13
- package/src/components/CnWalkthrough/CnWalkthrough.vue +16 -0
- package/src/components/CnWeekStripWidget/CnWeekStripWidget.vue +2 -1
- package/src/components/CnWidgetCardGrid/CnWidgetCardGrid.vue +55 -2
- package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +135 -0
- package/src/components/CnWidgetWrapper/CnWidgetWrapper.md +10 -0
- package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +44 -1
- package/src/composables/useListView.js +21 -0
- package/src/css/board.css +65 -4
- package/src/css/dialog.css +4 -2
- package/src/css/form-field.css +14 -0
- package/src/css/form-widget.css +11 -0
- package/src/css/index-page.css +49 -0
- package/src/css/kpi-card.css +13 -0
- package/src/css/look-board-reports.css +58 -0
- package/src/css/look-board.css +108 -7
- package/src/dialogs/CnTransitionInputDialog.vue +87 -7
- package/src/index.js +2 -0
- package/src/schemas/app-manifest-v2.schema.json +137 -6
- package/src/store/useObjectStore.js +16 -2
- package/src/utils/boardColumns.js +8 -1
- package/src/utils/boardReferenceColumns.js +274 -0
- package/src/utils/buildManifest.js +40 -3
- package/src/utils/detailWidgetVisibility.js +145 -0
- package/src/utils/formatMetric.js +38 -2
- package/src/utils/gridStack.js +39 -0
- package/src/utils/humaniseKey.js +38 -0
- package/src/utils/notificationRuleLabel.js +5 -23
- package/src/utils/resolveManifestTokens.js +1 -0
- package/src/utils/sentinelTokens.js +9 -0
- package/src/utils/validateManifestV2.compiled.js +3 -3
- package/src/utils/widgetDispatch.js +8 -2
|
@@ -28,21 +28,21 @@
|
|
|
28
28
|
|
|
29
29
|
<div class="grid-stack">
|
|
30
30
|
<div
|
|
31
|
-
v-for="
|
|
31
|
+
v-for="item in itemsInRowOrder"
|
|
32
32
|
:key="resolveItemKey(item)"
|
|
33
33
|
class="grid-stack-item"
|
|
34
34
|
:class="{ 'cn-dashboard-grid__item--fit': sizeToContentAttr(item) !== undefined }"
|
|
35
35
|
role="group"
|
|
36
|
-
:aria-label="resolveItemLabel(item,
|
|
36
|
+
:aria-label="resolveItemLabel(item, layout.indexOf(item))"
|
|
37
37
|
:aria-describedby="itemDescribedBy"
|
|
38
38
|
:tabindex="itemTabindex"
|
|
39
39
|
:gs-id="item.id"
|
|
40
40
|
:gs-x="item.gridX"
|
|
41
|
-
:gs-y="item.gridY"
|
|
41
|
+
:gs-y="scaledRow(item.gridY)"
|
|
42
42
|
:gs-w="item.gridWidth"
|
|
43
|
-
:gs-h="item.gridHeight"
|
|
43
|
+
:gs-h="scaledRow(item.gridHeight)"
|
|
44
44
|
:gs-min-w="minWidth"
|
|
45
|
-
:gs-min-h="
|
|
45
|
+
:gs-min-h="minRows(item)"
|
|
46
46
|
:gs-size-to-content="sizeToContentAttr(item)"
|
|
47
47
|
@keydown="onItemKeydown($event, item)">
|
|
48
48
|
<div class="grid-stack-item-content">
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
<script>
|
|
72
72
|
import { translate as t } from '@nextcloud/l10n'
|
|
73
73
|
import { GridStack } from 'gridstack'
|
|
74
|
+
import { planGrowth } from '../../utils/gridStack.js'
|
|
74
75
|
// `gridstack` is a peerDependency (see package.json), not bundled — this
|
|
75
76
|
// component's JS and the consumer's GridStack stylesheet MUST come from the
|
|
76
77
|
// exact same installed copy, because GridStack's JS writes layout state
|
|
@@ -99,6 +100,24 @@ let domIdCounter = 0
|
|
|
99
100
|
*/
|
|
100
101
|
const MENU_ANCHOR_OFFSET = 8
|
|
101
102
|
|
|
103
|
+
/**
|
|
104
|
+
* How many GridStack rows a layout row is drawn as on a grid with
|
|
105
|
+
* size-to-content cells (`wantsRowSplit`): 80px rows become 10px rows.
|
|
106
|
+
*/
|
|
107
|
+
const ROW_SPLIT = 8
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Whether a layout holds a size-to-content cell (`sizeToContent` true or a
|
|
111
|
+
* positive row maximum).
|
|
112
|
+
*
|
|
113
|
+
* @param {Array<object>} layout The layout items.
|
|
114
|
+
* @return {boolean} True when one does.
|
|
115
|
+
*/
|
|
116
|
+
function hasFitCell(layout) {
|
|
117
|
+
return Array.isArray(layout) && layout.some((item) => item
|
|
118
|
+
&& (item.sizeToContent === true || (Number.isFinite(item.sizeToContent) && item.sizeToContent > 0)))
|
|
119
|
+
}
|
|
120
|
+
|
|
102
121
|
/**
|
|
103
122
|
* CnDashboardGrid — Low-level grid layout engine powered by GridStack.
|
|
104
123
|
*
|
|
@@ -304,10 +323,62 @@ export default {
|
|
|
304
323
|
* on that.
|
|
305
324
|
*/
|
|
306
325
|
reflowed: false,
|
|
326
|
+
/**
|
|
327
|
+
* How many GridStack rows one layout row is drawn as: 1, or
|
|
328
|
+
* ROW_SPLIT while `wantsRowSplit` holds. Set before the first render,
|
|
329
|
+
* since GridStack reads the items' places from their attributes,
|
|
330
|
+
* and again when the grid is rebuilt.
|
|
331
|
+
*/
|
|
332
|
+
rowSplit: (!this.editable && !this.float && hasFitCell(this.layout)) ? ROW_SPLIT : 1,
|
|
307
333
|
}
|
|
308
334
|
},
|
|
309
335
|
|
|
310
336
|
computed: {
|
|
337
|
+
/**
|
|
338
|
+
* The layout's items top to bottom, then left to right: the order the
|
|
339
|
+
* items are in the page, so GridStack places them in that order.
|
|
340
|
+
*
|
|
341
|
+
* GridStack sizes a size-to-content cell while it adds the items, one
|
|
342
|
+
* at a time, and an item it adds onto a cell that has already grown
|
|
343
|
+
* moves itself below whatever it then collides with. In the layout's
|
|
344
|
+
* own order a card declared after the lists (pipelinq's full-width
|
|
345
|
+
* "client in focus" card) was added last, so when the banner above
|
|
346
|
+
* it grew it skipped past the stat tiles AND the lists and landed at
|
|
347
|
+
* the bottom. Added top to bottom, every card lands under the one
|
|
348
|
+
* above it. It is also the order a screen reader and the Tab key
|
|
349
|
+
* follow. An item without a row or column sorts after the items
|
|
350
|
+
* that have one, in the layout's order.
|
|
351
|
+
*
|
|
352
|
+
* @spec openspec/changes/dashboard-growth-keeps-order/specs/dashboard-grid/spec.md#requirement-a-growing-cell-keeps-the-order-of-the-cards-under-it
|
|
353
|
+
* @return {Array<object>} The items in row order.
|
|
354
|
+
*/
|
|
355
|
+
itemsInRowOrder() {
|
|
356
|
+
const items = Array.isArray(this.layout) ? [...this.layout] : []
|
|
357
|
+
const at = (value) => (Number.isFinite(value) ? value : Number.MAX_SAFE_INTEGER)
|
|
358
|
+
return items.sort((a, b) => (at(a.gridY) - at(b.gridY)) || (at(a.gridX) - at(b.gridX)))
|
|
359
|
+
},
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Whether the grid draws each layout row as ROW_SPLIT finer rows. A
|
|
363
|
+
* size-to-content cell can only be a whole number of rows tall, so on
|
|
364
|
+
* 80px rows it kept up to a row of empty space under its content and
|
|
365
|
+
* a packing grid (`float: false`) could not close it (pipelinq and
|
|
366
|
+
* dossiq Mijn werk, decidiq Mijn acties). Split into 10px rows the
|
|
367
|
+
* cell ends within 10px of its content and the widgets below move up.
|
|
368
|
+
*
|
|
369
|
+
* Only in view mode, only on a packing grid with a size-to-content
|
|
370
|
+
* cell: every other grid is built exactly as before. Item positions
|
|
371
|
+
* and sizes scale with the rows, so every widget keeps its pixel
|
|
372
|
+
* place and size until a cell shrinks; the layout itself, what edit
|
|
373
|
+
* mode shows and what `layout-change` reports, stays in layout rows.
|
|
374
|
+
*
|
|
375
|
+
* @spec openspec/changes/dashboard-fit-cells-close-up/specs/dashboard-grid/spec.md#requirement-a-size-to-content-cell-ends-at-its-content
|
|
376
|
+
* @return {boolean} True to split the rows.
|
|
377
|
+
*/
|
|
378
|
+
wantsRowSplit() {
|
|
379
|
+
return !this.editable && !this.float && hasFitCell(this.layout)
|
|
380
|
+
},
|
|
381
|
+
|
|
311
382
|
/**
|
|
312
383
|
* @return {boolean} Whether the keyboard lane is live — items are
|
|
313
384
|
* focusable and the arrow keys reposition. Requires the opt-in prop,
|
|
@@ -374,6 +445,12 @@ export default {
|
|
|
374
445
|
this.syncInteractivity()
|
|
375
446
|
},
|
|
376
447
|
|
|
448
|
+
wantsRowSplit(wants) {
|
|
449
|
+
if (this.grid && (wants ? ROW_SPLIT : 1) !== this.rowSplit) {
|
|
450
|
+
this.rebuildGrid()
|
|
451
|
+
}
|
|
452
|
+
},
|
|
453
|
+
|
|
377
454
|
layout: {
|
|
378
455
|
deep: true,
|
|
379
456
|
handler(newLayout) {
|
|
@@ -433,11 +510,75 @@ export default {
|
|
|
433
510
|
}
|
|
434
511
|
// A number is GridStack's "size to content, but never taller than N rows" (a maximum).
|
|
435
512
|
if (Number.isFinite(v) && v > 0) {
|
|
436
|
-
return String(v)
|
|
513
|
+
return String(v * this.rowSplit)
|
|
437
514
|
}
|
|
438
515
|
return undefined
|
|
439
516
|
},
|
|
440
517
|
|
|
518
|
+
/**
|
|
519
|
+
* A layout row coordinate or height in GridStack rows (`rowSplit`).
|
|
520
|
+
*
|
|
521
|
+
* @spec openspec/changes/dashboard-fit-cells-close-up/specs/dashboard-grid/spec.md#requirement-a-size-to-content-cell-ends-at-its-content
|
|
522
|
+
* @param {number|undefined} value The value in layout rows.
|
|
523
|
+
* @return {number|undefined} The value in grid rows, or undefined when none is set.
|
|
524
|
+
*/
|
|
525
|
+
scaledRow(value) {
|
|
526
|
+
return Number.isFinite(value) ? value * this.rowSplit : undefined
|
|
527
|
+
},
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* The smallest height an item may take, in grid rows: `minHeight`
|
|
531
|
+
* layout rows, except for a size-to-content cell on split rows, which
|
|
532
|
+
* may shrink to its content down to one grid row. The minimum is for
|
|
533
|
+
* resizing by hand, and that only happens on whole rows in edit mode.
|
|
534
|
+
*
|
|
535
|
+
* @spec openspec/changes/dashboard-fit-cells-close-up/specs/dashboard-grid/spec.md#requirement-a-size-to-content-cell-ends-at-its-content
|
|
536
|
+
* @param {object} item The layout item.
|
|
537
|
+
* @return {number} The minimum height in grid rows.
|
|
538
|
+
*/
|
|
539
|
+
minRows(item) {
|
|
540
|
+
if (this.rowSplit !== 1 && this.sizeToContentAttr(item) !== undefined) {
|
|
541
|
+
return 1
|
|
542
|
+
}
|
|
543
|
+
return this.minHeight * this.rowSplit
|
|
544
|
+
},
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Build the grid again with the row split `wantsRowSplit` asks for, as
|
|
548
|
+
* edit mode turns on or off: the items go back to their layout places in
|
|
549
|
+
* the new rows, and the size-to-content cells are measured again.
|
|
550
|
+
*
|
|
551
|
+
* @spec openspec/changes/dashboard-fit-cells-close-up/specs/dashboard-grid/spec.md#requirement-a-size-to-content-cell-ends-at-its-content
|
|
552
|
+
* @return {void}
|
|
553
|
+
*/
|
|
554
|
+
rebuildGrid() {
|
|
555
|
+
this.disconnectContentObserver()
|
|
556
|
+
this.grid.destroy(false)
|
|
557
|
+
this.grid = null
|
|
558
|
+
this.rowSplit = this.wantsRowSplit ? ROW_SPLIT : 1
|
|
559
|
+
this.$nextTick(() => {
|
|
560
|
+
if (!this.$refs.gridContainer) {
|
|
561
|
+
return
|
|
562
|
+
}
|
|
563
|
+
// GridStack wrote its own places onto the items; put the layout's back.
|
|
564
|
+
for (const item of this.layout) {
|
|
565
|
+
const el = this.$refs.gridContainer.querySelector(`[gs-id="${item.id}"]`)
|
|
566
|
+
if (!el) {
|
|
567
|
+
continue
|
|
568
|
+
}
|
|
569
|
+
for (const [name, value] of [['gs-y', this.scaledRow(item.gridY)], ['gs-h', this.scaledRow(item.gridHeight)], ['gs-min-h', this.minRows(item)], ['gs-size-to-content', this.sizeToContentAttr(item)]]) {
|
|
570
|
+
if (value === undefined) {
|
|
571
|
+
el.removeAttribute(name)
|
|
572
|
+
} else {
|
|
573
|
+
el.setAttribute(name, String(value))
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
this.initGrid()
|
|
578
|
+
this.observeContentSizes()
|
|
579
|
+
})
|
|
580
|
+
},
|
|
581
|
+
|
|
441
582
|
/**
|
|
442
583
|
* Build the accessible name for a grid item. A custom `itemLabel` wins
|
|
443
584
|
* outright; otherwise the first human-readable field on the item is used
|
|
@@ -492,6 +633,13 @@ export default {
|
|
|
492
633
|
)
|
|
493
634
|
},
|
|
494
635
|
|
|
636
|
+
/**
|
|
637
|
+
* Build the GridStack grid on the rendered items, install the growth
|
|
638
|
+
* wrapper (`keepOrderOnGrowth`) and listen for its changes.
|
|
639
|
+
*
|
|
640
|
+
* @spec openspec/changes/dashboard-growth-keeps-order/specs/dashboard-grid/spec.md#requirement-a-growing-cell-keeps-the-order-of-the-cards-under-it
|
|
641
|
+
* @return {void}
|
|
642
|
+
*/
|
|
495
643
|
initGrid() {
|
|
496
644
|
if (this.cellHeightCssVar && typeof document !== 'undefined' && document.documentElement) {
|
|
497
645
|
document.documentElement.style.setProperty(this.cellHeightCssVar, `${this.cellHeight}px`)
|
|
@@ -499,7 +647,7 @@ export default {
|
|
|
499
647
|
const el = this.$refs.gridContainer.querySelector('.grid-stack')
|
|
500
648
|
this.grid = GridStack.init({
|
|
501
649
|
column: this.columns,
|
|
502
|
-
cellHeight: this.cellHeight,
|
|
650
|
+
cellHeight: this.cellHeight / this.rowSplit,
|
|
503
651
|
margin: this.margin,
|
|
504
652
|
float: this.float,
|
|
505
653
|
animate: true,
|
|
@@ -508,6 +656,7 @@ export default {
|
|
|
508
656
|
removable: false,
|
|
509
657
|
...(this.columnOpts ? { columnOpts: this.columnOpts } : {}),
|
|
510
658
|
}, el)
|
|
659
|
+
this.keepOrderOnGrowth()
|
|
511
660
|
|
|
512
661
|
this.grid.on('change', (_event, items) => {
|
|
513
662
|
this.handleGridChange(items)
|
|
@@ -519,6 +668,87 @@ export default {
|
|
|
519
668
|
this.syncInteractivity()
|
|
520
669
|
},
|
|
521
670
|
|
|
671
|
+
/**
|
|
672
|
+
* Let a size-to-content cell that grows push the cards under it down in
|
|
673
|
+
* their order. GridStack resolves that collision itself, but not in
|
|
674
|
+
* order: when pipelinq's banner grew from two rows to three, the
|
|
675
|
+
* full-width card under the stat tiles landed below the lists, also
|
|
676
|
+
* with `float: false`. Every grow after the grid is built, whether
|
|
677
|
+
* GridStack measures it on resize or this grid's content observer asks
|
|
678
|
+
* for it, now first moves the cards the growth reaches down by the
|
|
679
|
+
* growth (`planGrowth`), bottom-most first, and only then grows the
|
|
680
|
+
* cell, so GridStack has no collision left to resolve. The wrapper is
|
|
681
|
+
* installed after `GridStack.init` has already measured the cells on
|
|
682
|
+
* load; the order on load comes from adding the items in row order
|
|
683
|
+
* (`itemsInRowOrder`). A cell that shrinks is left to GridStack as
|
|
684
|
+
* before.
|
|
685
|
+
*
|
|
686
|
+
* @spec openspec/changes/dashboard-growth-keeps-order/specs/dashboard-grid/spec.md#requirement-a-growing-cell-keeps-the-order-of-the-cards-under-it
|
|
687
|
+
* @return {void}
|
|
688
|
+
*/
|
|
689
|
+
keepOrderOnGrowth() {
|
|
690
|
+
const grid = this.grid
|
|
691
|
+
if (!grid || typeof grid.resizeToContent !== 'function' || typeof grid.update !== 'function') {
|
|
692
|
+
return
|
|
693
|
+
}
|
|
694
|
+
const resize = grid.resizeToContent.bind(grid)
|
|
695
|
+
grid.resizeToContent = (el) => {
|
|
696
|
+
const node = el && el.gridstackNode
|
|
697
|
+
const nodes = grid.engine && Array.isArray(grid.engine.nodes) ? grid.engine.nodes : null
|
|
698
|
+
const rows = node && nodes ? this.contentRows(el, node) : null
|
|
699
|
+
const plan = rows ? planGrowth(nodes, node, rows) : []
|
|
700
|
+
if (plan.length === 0) {
|
|
701
|
+
resize(el)
|
|
702
|
+
return
|
|
703
|
+
}
|
|
704
|
+
// Inside a batch the moves are not packed until the cell has grown.
|
|
705
|
+
const batching = Boolean(grid.engine.batchMode)
|
|
706
|
+
if (!batching) {
|
|
707
|
+
grid.batchUpdate()
|
|
708
|
+
}
|
|
709
|
+
for (const step of plan) {
|
|
710
|
+
grid.update(step.node.el, { y: step.y })
|
|
711
|
+
}
|
|
712
|
+
resize(el)
|
|
713
|
+
if (!batching) {
|
|
714
|
+
grid.batchUpdate(false)
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
},
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* The rows a size-to-content cell needs for its content, measured the
|
|
721
|
+
* way GridStack's `resizeToContent` measures it (the content's first
|
|
722
|
+
* child, plus the cell's own padding), within its size-to-content row
|
|
723
|
+
* maximum and its minimum and maximum height.
|
|
724
|
+
*
|
|
725
|
+
* @spec openspec/changes/dashboard-growth-keeps-order/specs/dashboard-grid/spec.md#requirement-a-growing-cell-keeps-the-order-of-the-cards-under-it
|
|
726
|
+
* @param {HTMLElement} el The cell.
|
|
727
|
+
* @param {object} node Its GridStack node.
|
|
728
|
+
* @return {number|null} The rows, or null when it cannot be measured.
|
|
729
|
+
*/
|
|
730
|
+
contentRows(el, node) {
|
|
731
|
+
const cell = this.grid.getCellHeight(true)
|
|
732
|
+
const item = el.querySelector('.grid-stack-item-content')
|
|
733
|
+
const child = item && item.firstElementChild
|
|
734
|
+
if (!el.clientHeight || !cell || !child || !Number.isFinite(node.h)) {
|
|
735
|
+
return null
|
|
736
|
+
}
|
|
737
|
+
const padding = el.clientHeight - item.clientHeight
|
|
738
|
+
const itemHeight = node.h * cell - padding
|
|
739
|
+
const wanted = child.getBoundingClientRect().height || itemHeight
|
|
740
|
+
let rows = Math.ceil((node.h * cell + wanted - itemHeight) / cell)
|
|
741
|
+
if (Number.isInteger(node.sizeToContent) && rows > node.sizeToContent) {
|
|
742
|
+
rows = node.sizeToContent
|
|
743
|
+
}
|
|
744
|
+
if (node.minH && rows < node.minH) {
|
|
745
|
+
rows = node.minH
|
|
746
|
+
} else if (node.maxH && rows > node.maxH) {
|
|
747
|
+
rows = node.maxH
|
|
748
|
+
}
|
|
749
|
+
return rows
|
|
750
|
+
},
|
|
751
|
+
|
|
522
752
|
/**
|
|
523
753
|
* Keep both editing lanes in step with whether an edit could actually be
|
|
524
754
|
* kept: the pointer's handles, and — through `reflowed` feeding
|
|
@@ -586,6 +816,12 @@ export default {
|
|
|
586
816
|
if (this.isReflowed()) {
|
|
587
817
|
return
|
|
588
818
|
}
|
|
819
|
+
// A grid on split rows is a view-mode grid: what changes in it is
|
|
820
|
+
// packing and size-to-content measuring, never a user's
|
|
821
|
+
// arrangement, and it is not reported as one.
|
|
822
|
+
if (this.rowSplit !== 1) {
|
|
823
|
+
return
|
|
824
|
+
}
|
|
589
825
|
if (!items || items.length === 0) {
|
|
590
826
|
return
|
|
591
827
|
}
|
|
@@ -986,7 +1222,7 @@ export default {
|
|
|
986
1222
|
// grid's own position back, which matches and does
|
|
987
1223
|
// nothing here.
|
|
988
1224
|
const x = item.gridX ?? node.x
|
|
989
|
-
const y = item.gridY
|
|
1225
|
+
const y = Number.isFinite(item.gridY) ? item.gridY * this.rowSplit : node.y
|
|
990
1226
|
if (x !== node.x || y !== node.y) {
|
|
991
1227
|
// The layout said where it goes; GridStack's `change`
|
|
992
1228
|
// for it is not a user's arrangement and is not
|
|
@@ -395,6 +395,7 @@
|
|
|
395
395
|
<CnStatsBlockWidget
|
|
396
396
|
v-bind="getStatsBlockProps(item)"
|
|
397
397
|
:title="getWidgetTitle(item)"
|
|
398
|
+
:eyebrow="getWidgetEyebrow(item)"
|
|
398
399
|
:data-source="getWidgetDataSource(item)" />
|
|
399
400
|
</CnWidgetWrapper>
|
|
400
401
|
<CnWidgetWrapper
|
|
@@ -420,7 +421,7 @@
|
|
|
420
421
|
:is="registryRenderer(item)"
|
|
421
422
|
:widgetId="item.widgetId"
|
|
422
423
|
:content="registryWidgetBindings(item)"
|
|
423
|
-
v-bind="registryWidgetBindings(item)"
|
|
424
|
+
v-bind="{ ...registryWidgetBindings(item), ...containerBindings(item) }"
|
|
424
425
|
@header="onWidgetHeader(item, $event)" />
|
|
425
426
|
</CnWidgetWrapper>
|
|
426
427
|
<CnWidgetWrapper
|
|
@@ -433,6 +434,7 @@
|
|
|
433
434
|
<CnWidgetWrapper
|
|
434
435
|
v-else
|
|
435
436
|
:title="getWidgetTitle(item)"
|
|
437
|
+
:eyebrow="getWidgetEyebrow(item)"
|
|
436
438
|
:showTitle="false"
|
|
437
439
|
:showRefresh="false">
|
|
438
440
|
<div class="cn-dashboard-page__unknown">
|
|
@@ -494,6 +496,7 @@
|
|
|
494
496
|
<CnWidgetWrapper
|
|
495
497
|
v-if="missingRequiredApp(item)"
|
|
496
498
|
:title="getWidgetTitle(item)"
|
|
499
|
+
:eyebrow="getWidgetEyebrow(item)"
|
|
497
500
|
:showTitle="widgetShowTitle(item)">
|
|
498
501
|
<!-- Compact on purpose: a two-row tile is about 150px, less
|
|
499
502
|
a title row. NcEmptyContent's 64px icon, 20px name,
|
|
@@ -532,6 +535,7 @@
|
|
|
532
535
|
<CnWidgetWrapper
|
|
533
536
|
:widgetId="item.widgetId"
|
|
534
537
|
:title="getWidgetTitle(item)"
|
|
538
|
+
:eyebrow="getWidgetEyebrow(item)"
|
|
535
539
|
:iconUrl="getWidgetIconUrl(item)"
|
|
536
540
|
:iconClass="getWidgetIconClass(item)"
|
|
537
541
|
:showTitle="widgetShowTitle(item)"
|
|
@@ -613,6 +617,7 @@
|
|
|
613
617
|
:widgetId="item.widgetId"
|
|
614
618
|
:class="{ 'cn-dashboard-page__chart-fit': isChartFitted(item) }"
|
|
615
619
|
:title="getWidgetTitle(item)"
|
|
620
|
+
:eyebrow="getWidgetEyebrow(item)"
|
|
616
621
|
:iconUrl="getWidgetIconUrl(item)"
|
|
617
622
|
:iconClass="getWidgetIconClass(item)"
|
|
618
623
|
:showTitle="widgetShowTitle(item)"
|
|
@@ -691,6 +696,7 @@
|
|
|
691
696
|
<CnWidgetWrapper
|
|
692
697
|
:widgetId="item.widgetId"
|
|
693
698
|
:title="getWidgetTitle(item)"
|
|
699
|
+
:eyebrow="getWidgetEyebrow(item)"
|
|
694
700
|
:iconUrl="getWidgetIconUrl(item)"
|
|
695
701
|
:iconClass="getWidgetIconClass(item)"
|
|
696
702
|
:showTitle="widgetShowTitle(item)"
|
|
@@ -718,6 +724,7 @@
|
|
|
718
724
|
<CnWidgetWrapper
|
|
719
725
|
:widgetId="item.widgetId"
|
|
720
726
|
:title="getWidgetTitle(item)"
|
|
727
|
+
:eyebrow="getWidgetEyebrow(item)"
|
|
721
728
|
:iconUrl="getWidgetIconUrl(item)"
|
|
722
729
|
:iconClass="getWidgetIconClass(item)"
|
|
723
730
|
:showTitle="widgetShowTitle(item)"
|
|
@@ -757,6 +764,7 @@
|
|
|
757
764
|
<CnWidgetWrapper
|
|
758
765
|
:widgetId="item.widgetId"
|
|
759
766
|
:title="getWidgetTitle(item)"
|
|
767
|
+
:eyebrow="getWidgetEyebrow(item)"
|
|
760
768
|
:iconUrl="getWidgetIconUrl(item)"
|
|
761
769
|
:iconClass="getWidgetIconClass(item)"
|
|
762
770
|
:showTitle="widgetShowTitle(item)"
|
|
@@ -782,6 +790,7 @@
|
|
|
782
790
|
<CnWidgetWrapper
|
|
783
791
|
:widgetId="item.widgetId"
|
|
784
792
|
:title="getWidgetTitle(item)"
|
|
793
|
+
:eyebrow="getWidgetEyebrow(item)"
|
|
785
794
|
:showTitle="widgetShowTitle(item)"
|
|
786
795
|
:showActions="widgetShowActions(item)"
|
|
787
796
|
:showRefresh="getWidgetShowRefresh(item)"
|
|
@@ -873,7 +882,7 @@
|
|
|
873
882
|
:is="registryRenderer(item)"
|
|
874
883
|
:widgetId="item.widgetId"
|
|
875
884
|
:content="registryWidgetBindings(item)"
|
|
876
|
-
v-bind="registryWidgetBindings(item)"
|
|
885
|
+
v-bind="{ ...registryWidgetBindings(item), ...containerBindings(item) }"
|
|
877
886
|
@header="onWidgetHeader(item, $event)" />
|
|
878
887
|
</CnWidgetWrapper>
|
|
879
888
|
</template>
|
|
@@ -882,6 +891,7 @@
|
|
|
882
891
|
<CnWidgetWrapper
|
|
883
892
|
v-else
|
|
884
893
|
:title="getWidgetTitle(item)"
|
|
894
|
+
:eyebrow="getWidgetEyebrow(item)"
|
|
885
895
|
:showTitle="widgetShowTitle(item)"
|
|
886
896
|
:showRefresh="false">
|
|
887
897
|
<div class="cn-dashboard-page__unknown">
|
|
@@ -2944,6 +2954,7 @@ export default {
|
|
|
2944
2954
|
return {
|
|
2945
2955
|
widgetId: item.widgetId,
|
|
2946
2956
|
title: this.getWidgetTitle(item),
|
|
2957
|
+
eyebrow: this.getWidgetEyebrow(item),
|
|
2947
2958
|
showTitle: this.widgetShowTitle(item),
|
|
2948
2959
|
showActions: this.widgetShowActions(item),
|
|
2949
2960
|
showRefresh: this.getWidgetShowRefresh(item),
|
|
@@ -3630,6 +3641,35 @@ export default {
|
|
|
3630
3641
|
return outcome ? { ...content, conditionOutcome: outcome } : content
|
|
3631
3642
|
},
|
|
3632
3643
|
|
|
3644
|
+
/**
|
|
3645
|
+
* What a container widget (registry `container`, such as `tabs`) needs
|
|
3646
|
+
* beside its content: the page's widget definitions, so its tabs find
|
|
3647
|
+
* the widgets they name, and the consumer registry for custom child
|
|
3648
|
+
* types. Empty for every other widget, so a leaf gets no extra
|
|
3649
|
+
* attributes on its root.
|
|
3650
|
+
*
|
|
3651
|
+
* @param {object} item Layout item.
|
|
3652
|
+
* @return {object} The extra bindings.
|
|
3653
|
+
* @spec openspec/changes/dashboard-tabs-children/specs/dashboard-page/spec.md#requirement-a-tabs-widget-on-a-dashboard-renders-the-widgets-its-tabs-name
|
|
3654
|
+
*/
|
|
3655
|
+
containerBindings(item) {
|
|
3656
|
+
const def = this.getWidgetDef(item.widgetId)
|
|
3657
|
+
if (!def || !def.type) {
|
|
3658
|
+
return {}
|
|
3659
|
+
}
|
|
3660
|
+
const entry = getWidgetTypeEntry(def.type) || getWidgetTypeEntry(canonicalWidgetType(def.type))
|
|
3661
|
+
if (!entry || entry.container !== true) {
|
|
3662
|
+
return {}
|
|
3663
|
+
}
|
|
3664
|
+
const own = Array.isArray(this.widgets) ? this.widgets.filter(Boolean) : []
|
|
3665
|
+
const fromView = this.activeView && Array.isArray(this.activeView.widgets) ? this.activeView.widgets.filter(Boolean) : []
|
|
3666
|
+
return {
|
|
3667
|
+
availableWidgets: [...own, ...fromView],
|
|
3668
|
+
cnRegistry: this.cnRegistry || {},
|
|
3669
|
+
surface: this.surface,
|
|
3670
|
+
}
|
|
3671
|
+
},
|
|
3672
|
+
|
|
3633
3673
|
/**
|
|
3634
3674
|
* A widget's content with the board's quiet collection footer: in the
|
|
3635
3675
|
* board look a dashboard that drops the widget Actions menu
|
|
@@ -4002,6 +4042,21 @@ export default {
|
|
|
4002
4042
|
this.showWidgetConfig = false
|
|
4003
4043
|
},
|
|
4004
4044
|
|
|
4045
|
+
/**
|
|
4046
|
+
* The line above a widget's title (`eyebrow` on the widget
|
|
4047
|
+
* definition: "Saldo per week" above "Prognose 13 weken"), through the
|
|
4048
|
+
* host translate, or '' for none.
|
|
4049
|
+
*
|
|
4050
|
+
* @param {object} item Layout item.
|
|
4051
|
+
* @return {string}
|
|
4052
|
+
* @spec openspec/changes/widget-title-eyebrow/specs/dashboard-page/spec.md#requirement-a-widget-card-can-carry-a-line-above-its-title
|
|
4053
|
+
*/
|
|
4054
|
+
getWidgetEyebrow(item) {
|
|
4055
|
+
const def = this.getWidgetDef(item.widgetId)
|
|
4056
|
+
const eyebrow = def && typeof def.eyebrow === 'string' ? def.eyebrow : ''
|
|
4057
|
+
return eyebrow ? this.effectiveTranslate(eyebrow) : ''
|
|
4058
|
+
},
|
|
4059
|
+
|
|
4005
4060
|
/**
|
|
4006
4061
|
* The widget's title as AUTHORED — the untranslated manifest source
|
|
4007
4062
|
* string — resolved by widget id rather than by item, so a descendant
|
|
@@ -4587,6 +4642,11 @@ export default {
|
|
|
4587
4642
|
if (columns !== undefined) {
|
|
4588
4643
|
out.columns = Number(columns)
|
|
4589
4644
|
}
|
|
4645
|
+
// `tiles: "box"`: each grid tile on a surface of its own.
|
|
4646
|
+
const tiles = content.tiles !== undefined ? content.tiles : props.tiles
|
|
4647
|
+
if (typeof tiles === 'string') {
|
|
4648
|
+
out.tiles = tiles
|
|
4649
|
+
}
|
|
4590
4650
|
const contentEntries = content.entries !== undefined ? content.entries : props.entries
|
|
4591
4651
|
if (contentEntries !== undefined) {
|
|
4592
4652
|
out.entries = contentEntries
|
|
@@ -511,7 +511,7 @@ export default {
|
|
|
511
511
|
* @return {string} The formatted string.
|
|
512
512
|
*/
|
|
513
513
|
formatNumber(value) {
|
|
514
|
-
return formatMetricValue(value, this.resolvedFormat, unwrapAppConfig(this.cnAppConfig))
|
|
514
|
+
return formatMetricValue(value, this.resolvedFormat, unwrapAppConfig(this.cnAppConfig), null, this.effectiveTranslate)
|
|
515
515
|
},
|
|
516
516
|
|
|
517
517
|
/**
|
|
@@ -446,6 +446,8 @@ When the schema-driven fetch answers 404 the page shows a not-found state whose
|
|
|
446
446
|
|
|
447
447
|
`headerCard` (default `false`) draws the header as a bordered card. `headerWidget` (default `''`) names a widget in `widgets` to render inside the header under the title, without a card of its own and out of the body grid: the stages bars of the board's case card.
|
|
448
448
|
|
|
449
|
+
`visibilityContext` (default `{}`) names records the page reads beside the loaded one, `{ <name>: { field, register, schema } }`. A grid widget's `visibleWhen` is read against the record, and against those records as `@context.<name>.<path>`, so a case page can show a widget for some case types only (`{ "field": "@context.caseType.category", "op": "in", "value": ["Sociaal domein"] }`). A widget without `visibleWhen` shows as before; a conditional one stays out of the grid until the record and the named records have loaded.
|
|
450
|
+
|
|
449
451
|
`showWidgetActions` (Boolean, default `true`): `false` (manifest `config.showWidgetActions: false`) drops the overflow Actions menu from the cards of the body grid and the side column, unless a widget definition sets `showActions: true`.
|
|
450
452
|
|
|
451
453
|
### Favourite and follow
|
|
@@ -458,4 +460,4 @@ When the schema-driven fetch answers 404 the page shows a not-found state whose
|
|
|
458
460
|
|
|
459
461
|
### The board look
|
|
460
462
|
|
|
461
|
-
`look` (String, default `''`): `nextcloud` or `board`; empty takes the app's look. `headerMeta` (String): the meta line on row 2 of the board header, a field template such as `via {channel}` (manifest `config.headerMeta`). `tabsLabel` (String): the accessible name of the tab list (manifest `config.tabsLabel`). `identifierField` (String): the field named in the last line of the History side card (manifest `config.identifierField`). See `docs/components/cn-detail-page.md`.
|
|
463
|
+
`look` (String, default `''`): `nextcloud` or `board`; empty takes the app's look. `headerMeta` (String): the meta line on row 2 of the board header, a field template such as `via {channel}` (manifest `config.headerMeta`). `tabsLabel` (String): the accessible name of the tab list (manifest `config.tabsLabel`). `identifierField` (String): the field named in the last line of the History side card (manifest `config.identifierField`). `titleField` (String): the record field the header names the record by, such as an invoice number (manifest `config.titleField`); an empty value falls back to the display name. See `docs/components/cn-detail-page.md`.
|