@conduction/nextcloud-vue 2.121.0 → 2.123.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/CnAppRoot/CnAppRoot.vue2.js +1 -0
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnAttentionFlagWidget/CnAttentionFlagWidget.vue.js +1 -1
- package/dist/esm/components/CnAttentionFlagWidget/CnAttentionFlagWidget.vue2.js +18 -4
- package/dist/esm/components/CnAttentionFlagWidget/CnAttentionFlagWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnAttentionFlagWidget/CnAttentionFlagWidget.vue3.js +63 -32
- package/dist/esm/components/CnAttentionFlagWidget/CnAttentionFlagWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +35 -0
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +11 -5
- 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 +13 -0
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +37 -5
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +40 -0
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +71 -65
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFlowDetail/CnFlowDetail.vue.js +1 -1
- package/dist/esm/components/CnFlowDetail/CnFlowDetail.vue2.js +20 -0
- package/dist/esm/components/CnFlowDetail/CnFlowDetail.vue2.js.map +1 -1
- package/dist/esm/components/CnFlowDetail/CnFlowDetail.vue3.js +2 -1
- package/dist/esm/components/CnFlowDetail/CnFlowDetail.vue3.js.map +1 -1
- package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue.js +1 -1
- package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue2.js +2 -0
- package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue2.js.map +1 -1
- package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue3.js +49 -44
- package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue3.js.map +1 -1
- package/dist/esm/components/CnFlowsPage/CnFlowEditorPage.vue2.js +10 -0
- package/dist/esm/components/CnFlowsPage/CnFlowEditorPage.vue2.js.map +1 -1
- package/dist/esm/components/CnFlowsPage/CnFlowEditorPage.vue3.js +2 -1
- package/dist/esm/components/CnFlowsPage/CnFlowEditorPage.vue3.js.map +1 -1
- package/dist/esm/components/CnGraphCanvas/CnGraphCanvas.vue2.js +31 -0
- package/dist/esm/components/CnGraphCanvas/CnGraphCanvas.vue2.js.map +1 -1
- package/dist/esm/components/CnGraphCanvas/CnGraphCanvas.vue3.js +4 -3
- package/dist/esm/components/CnGraphCanvas/CnGraphCanvas.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +34 -2
- 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/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js +1 -0
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue.js +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +2 -0
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +2 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue.js +1 -1
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue2.js +31 -1
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue3.js +14 -4
- package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue3.js.map +1 -1
- package/dist/esm/composables/flowGraphLayout.js +36 -7
- package/dist/esm/composables/flowGraphLayout.js.map +1 -1
- package/dist/esm/composables/useFlowStore.js +7 -3
- package/dist/esm/composables/useFlowStore.js.map +1 -1
- package/dist/esm/l10n/en.json.js +3 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +3 -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 +670 -316
- package/dist/esm/utils/manifestTranslate.js +12 -3
- package/dist/esm/utils/manifestTranslate.js.map +1 -1
- package/dist/esm/utils/routeFilters.js +3 -3
- package/dist/esm/utils/routeFilters.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +1218 -847
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +670 -316
- package/l10n/en.json +3 -1
- package/l10n/nl.json +3 -1
- package/package.json +1 -1
- package/src/components/CnAppRoot/CnAppRoot.vue +1 -0
- package/src/components/CnAttentionFlagWidget/CnAttentionFlagWidget.vue +50 -5
- package/src/components/CnChartWidget/CnChartWidget.vue +10 -8
- package/src/components/CnDetailPage/CnDetailPage.md +10 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +42 -1
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +39 -0
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +95 -37
- package/src/components/CnFlowDetail/CnFlowDetail.vue +21 -0
- package/src/components/CnFlowsPage/CnFlowBoard.vue +35 -18
- package/src/components/CnFlowsPage/CnFlowEditorPage.vue +11 -0
- package/src/components/CnGraphCanvas/CnGraphCanvas.vue +34 -2
- package/src/components/CnIndexPage/CnIndexPage.vue +34 -2
- package/src/components/CnIndexSidebar/CnIndexSidebar.vue +1 -0
- package/src/components/CnObjectSidebar/CnFilesTab.vue +3 -0
- package/src/components/CnSectionsWidget/CnSectionsWidget.vue +53 -2
- package/src/composables/flowGraphLayout.js +35 -6
- package/src/composables/useFlowStore.js +7 -3
- package/src/css/detail-page.css +6 -0
- package/src/css/look-board-detail.css +53 -6
- package/src/css/look-board-index.css +151 -12
- package/src/css/look-board.css +116 -4
- package/src/utils/manifestTranslate.js +12 -3
- package/src/utils/routeFilters.js +3 -3
|
@@ -101,10 +101,10 @@
|
|
|
101
101
|
</NcNoteCard>
|
|
102
102
|
|
|
103
103
|
<div class="cn-flow-board__body">
|
|
104
|
-
<aside v-if="showPalette"
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
<aside v-if="showPalette"
|
|
105
|
+
class="cn-flow-board__card cn-flow-board__palette"
|
|
106
|
+
:aria-label="t('nextcloud-vue', 'Steps')"
|
|
107
|
+
data-testid="flow-board-palette">
|
|
108
108
|
<section v-for="group in paletteGroups"
|
|
109
109
|
:key="group.app"
|
|
110
110
|
class="cn-flow-board__palette-group">
|
|
@@ -132,6 +132,8 @@
|
|
|
132
132
|
<CnFlowDetail :id="id"
|
|
133
133
|
:app="app"
|
|
134
134
|
:run="run"
|
|
135
|
+
:layoutDirection="flowDirection === 'down' ? 'down' : 'right'"
|
|
136
|
+
:fitMaxZoom="flowDirection === 'down' ? 1 : 0"
|
|
135
137
|
:toolbarActions="false"
|
|
136
138
|
:addStep="!showPalette"
|
|
137
139
|
@save="$emit('save')"
|
|
@@ -177,12 +179,14 @@
|
|
|
177
179
|
:emptyText="runTab === 'step' && !store.selectedNodeId ? t('nextcloud-vue', 'Select a step on the canvas to see what it did.') : ''" />
|
|
178
180
|
</template>
|
|
179
181
|
<template v-else-if="store.selectedNode">
|
|
180
|
-
<p class="cn-flow-board__eyebrow">
|
|
181
|
-
{{ stepTypeLabel }}
|
|
182
|
-
</p>
|
|
183
182
|
<h3 class="cn-flow-board__card-title" data-testid="flow-board-step-name">
|
|
184
183
|
{{ stepName }}
|
|
185
184
|
</h3>
|
|
185
|
+
<!-- The step type as a pill under the name (DqFlow "Beslistabel van OpenRegister"). -->
|
|
186
|
+
<CnStatusBadge :label="stepTypeLabel"
|
|
187
|
+
variant="purple"
|
|
188
|
+
class="cn-flow-board__step-type"
|
|
189
|
+
data-testid="flow-board-step-type" />
|
|
186
190
|
<dl v-if="stepSettings.length > 0" class="cn-flow-board__settings">
|
|
187
191
|
<div v-for="setting in stepSettings"
|
|
188
192
|
:key="setting.key"
|
|
@@ -285,6 +289,8 @@ export default {
|
|
|
285
289
|
palette: { type: String, default: 'picker' },
|
|
286
290
|
/** Free text under the path in the palette card. */
|
|
287
291
|
paletteFootnote: { type: String, default: '' },
|
|
292
|
+
/** `down` runs the steps top to bottom and opens them at their own size (DqFlow); default `right`, left to right and fitted. */
|
|
293
|
+
flowDirection: { type: String, default: 'right' },
|
|
288
294
|
/** An info notice under the path. */
|
|
289
295
|
notice: { type: String, default: '' },
|
|
290
296
|
/** The pill beside the title ("Case type flow"). */
|
|
@@ -769,8 +775,9 @@ export default {
|
|
|
769
775
|
min-height: 520px;
|
|
770
776
|
}
|
|
771
777
|
|
|
778
|
+
/* Palette, canvas and inspector at 220 / rest / 280 (DqFlow). */
|
|
772
779
|
.cn-flow-board--with-palette .cn-flow-board__body {
|
|
773
|
-
grid-template-columns:
|
|
780
|
+
grid-template-columns: 220px minmax(0, 1fr) 280px;
|
|
774
781
|
}
|
|
775
782
|
|
|
776
783
|
.cn-flow-board__card {
|
|
@@ -784,14 +791,22 @@ export default {
|
|
|
784
791
|
background: var(--color-main-background);
|
|
785
792
|
}
|
|
786
793
|
|
|
794
|
+
/* The canvas is the white card with the dot grid; the palette beside it sits
|
|
795
|
+
on the page ground (DqFlow). */
|
|
787
796
|
.cn-flow-board__canvas {
|
|
788
797
|
padding: 0;
|
|
789
798
|
overflow: hidden;
|
|
790
|
-
background-color: var(--color-background
|
|
791
|
-
background-image: radial-gradient(var(--color-border
|
|
799
|
+
background-color: var(--color-main-background);
|
|
800
|
+
background-image: radial-gradient(var(--color-border) 1px, transparent 1px);
|
|
792
801
|
background-size: 16px 16px;
|
|
793
802
|
}
|
|
794
803
|
|
|
804
|
+
.cn-flow-board__palette {
|
|
805
|
+
gap: 14px;
|
|
806
|
+
padding: 14px;
|
|
807
|
+
background: transparent;
|
|
808
|
+
}
|
|
809
|
+
|
|
795
810
|
.cn-flow-board__canvas > :first-child {
|
|
796
811
|
flex: 1 1 auto;
|
|
797
812
|
min-height: 0;
|
|
@@ -809,10 +824,8 @@ export default {
|
|
|
809
824
|
font-weight: 600;
|
|
810
825
|
}
|
|
811
826
|
|
|
812
|
-
.cn-flow-
|
|
813
|
-
|
|
814
|
-
color: var(--color-text-maxcontrast);
|
|
815
|
-
font-size: 13px;
|
|
827
|
+
.cn-flow-board__step-type {
|
|
828
|
+
align-self: flex-start;
|
|
816
829
|
}
|
|
817
830
|
|
|
818
831
|
.cn-flow-board__validity,
|
|
@@ -841,20 +854,23 @@ export default {
|
|
|
841
854
|
.cn-flow-board__palette-group {
|
|
842
855
|
display: flex;
|
|
843
856
|
flex-direction: column;
|
|
844
|
-
gap:
|
|
857
|
+
gap: 6px;
|
|
845
858
|
}
|
|
846
859
|
|
|
860
|
+
/* The providing app as a small caps label, no title row above the groups. */
|
|
847
861
|
.cn-flow-board__palette-app {
|
|
848
862
|
margin: 0;
|
|
849
863
|
color: var(--color-text-maxcontrast);
|
|
850
|
-
font-size:
|
|
864
|
+
font-size: 12px;
|
|
851
865
|
font-weight: 600;
|
|
866
|
+
letter-spacing: 0.06em;
|
|
867
|
+
text-transform: uppercase;
|
|
852
868
|
}
|
|
853
869
|
|
|
854
870
|
.cn-flow-board__palette-list {
|
|
855
871
|
display: flex;
|
|
856
872
|
flex-direction: column;
|
|
857
|
-
gap:
|
|
873
|
+
gap: 6px;
|
|
858
874
|
margin: 0;
|
|
859
875
|
padding: 0;
|
|
860
876
|
list-style: none;
|
|
@@ -866,11 +882,12 @@ export default {
|
|
|
866
882
|
--nldesign-component-button-font-weight: 400;
|
|
867
883
|
width: 100%;
|
|
868
884
|
min-height: 34px;
|
|
869
|
-
padding:
|
|
885
|
+
padding: 8px 10px;
|
|
870
886
|
border: 1px solid var(--color-border);
|
|
871
887
|
border-radius: 8px;
|
|
872
888
|
background: var(--color-main-background);
|
|
873
889
|
color: var(--color-main-text);
|
|
890
|
+
font-size: 14px;
|
|
874
891
|
font-weight: 400;
|
|
875
892
|
text-align: start;
|
|
876
893
|
cursor: pointer;
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
:listLabel="listLabel ? cnTranslate(listLabel) : ''"
|
|
10
10
|
:palette="palette"
|
|
11
11
|
:paletteFootnote="paletteFootnote ? cnTranslate(paletteFootnote) : ''"
|
|
12
|
+
:flowDirection="flowDirection"
|
|
12
13
|
:notice="notice ? cnTranslate(notice) : ''"
|
|
13
14
|
:pill="pill ? cnTranslate(pill) : ''"
|
|
14
15
|
:testSubject="testSubject ? { ...testSubject, label: testSubject.label ? cnTranslate(testSubject.label) : '' } : null"
|
|
@@ -123,6 +124,16 @@ export default {
|
|
|
123
124
|
default: '',
|
|
124
125
|
},
|
|
125
126
|
|
|
127
|
+
/**
|
|
128
|
+
* Board look: `down` lays the steps out top to bottom and opens them
|
|
129
|
+
* at their own size instead of zoomed to fit; `right` (default) lays
|
|
130
|
+
* them left to right. Manifest key `config.flowDirection`.
|
|
131
|
+
*/
|
|
132
|
+
flowDirection: {
|
|
133
|
+
type: String,
|
|
134
|
+
default: 'right',
|
|
135
|
+
},
|
|
136
|
+
|
|
126
137
|
/**
|
|
127
138
|
* Board look: the records a test run can take as its subject,
|
|
128
139
|
* `{ register, schema, labelField?, label? }`. Manifest key
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
:edges="edges"
|
|
10
10
|
:minZoom="minZoom"
|
|
11
11
|
:maxZoom="maxZoom"
|
|
12
|
-
:fitViewOnInit="fitView"
|
|
12
|
+
:fitViewOnInit="fitView && !fitMaxZoom"
|
|
13
13
|
:snapToGrid="snapToGrid"
|
|
14
14
|
:snapGrid="snapGrid"
|
|
15
15
|
:nodesDraggable="interactive"
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
@connect="onConnect"
|
|
22
22
|
@nodeClick="onNodeClick"
|
|
23
23
|
@edgeClick="onEdgeClick"
|
|
24
|
-
@paneClick="onPaneClick"
|
|
24
|
+
@paneClick="onPaneClick"
|
|
25
|
+
@nodesInitialized="onNodesInitialized">
|
|
25
26
|
<!-- Per-type node components, Vue Flow's convention. A host that
|
|
26
27
|
registers `type: 'trigger'` gets `#node-trigger`; anything
|
|
27
28
|
unregistered falls back to CnFlowNode, which carries the
|
|
@@ -206,6 +207,15 @@ export default {
|
|
|
206
207
|
default: true,
|
|
207
208
|
},
|
|
208
209
|
|
|
210
|
+
/**
|
|
211
|
+
* The most that first frame may zoom in. 0 lets it go to `maxZoom`;
|
|
212
|
+
* 1 keeps a graph that fits at its own size.
|
|
213
|
+
*/
|
|
214
|
+
fitMaxZoom: {
|
|
215
|
+
type: Number,
|
|
216
|
+
default: 0,
|
|
217
|
+
},
|
|
218
|
+
|
|
209
219
|
/** Snap dragged nodes to the grid. */
|
|
210
220
|
snapToGrid: {
|
|
211
221
|
type: Boolean,
|
|
@@ -293,6 +303,13 @@ export default {
|
|
|
293
303
|
return { project, zoomIn, zoomOut, zoomTo, fitViewNow, viewport }
|
|
294
304
|
},
|
|
295
305
|
|
|
306
|
+
data() {
|
|
307
|
+
return {
|
|
308
|
+
// Whether the capped first frame (`fitMaxZoom`) has been taken.
|
|
309
|
+
framedOnce: false,
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
|
|
296
313
|
computed: {
|
|
297
314
|
/**
|
|
298
315
|
* @return {boolean} Whether the canvas accepts interaction at all.
|
|
@@ -315,6 +332,21 @@ export default {
|
|
|
315
332
|
},
|
|
316
333
|
|
|
317
334
|
methods: {
|
|
335
|
+
/**
|
|
336
|
+
* Frame the graph once its nodes have a size, no closer than
|
|
337
|
+
* `fitMaxZoom`. Only the first time: a step added later must not move
|
|
338
|
+
* the view the author is working in.
|
|
339
|
+
*
|
|
340
|
+
* @return {void}
|
|
341
|
+
*/
|
|
342
|
+
onNodesInitialized() {
|
|
343
|
+
if (!this.fitView || !this.fitMaxZoom || this.framedOnce) {
|
|
344
|
+
return
|
|
345
|
+
}
|
|
346
|
+
this.framedOnce = true
|
|
347
|
+
this.fitViewNow({ maxZoom: this.fitMaxZoom, padding: 0.1 })
|
|
348
|
+
},
|
|
349
|
+
|
|
318
350
|
/**
|
|
319
351
|
* Forward Vue Flow's node changes.
|
|
320
352
|
*
|
|
@@ -4218,6 +4218,8 @@ export default {
|
|
|
4218
4218
|
appliedSavedViewId: '',
|
|
4219
4219
|
// The sort option the applied view's `overrides.sortOption` selected, so leaving the view can undo it.
|
|
4220
4220
|
viewSortOption: '',
|
|
4221
|
+
// The applied view's filters as one comparable key, so the address keeps naming the view until they change.
|
|
4222
|
+
appliedViewFiltersKey: '',
|
|
4221
4223
|
/**
|
|
4222
4224
|
* The non-`_` query keys this page owns, i.e. may clear on the next
|
|
4223
4225
|
* persist. Seeded with what it adopted from the query on load, and
|
|
@@ -9957,14 +9959,23 @@ export default {
|
|
|
9957
9959
|
* (same resulting path/query) is swallowed.
|
|
9958
9960
|
*
|
|
9959
9961
|
* @param {{filters?: object, search?: string, sortKey?: ?string, sortOrder?: string, sortKeys?: Array<{key: string, order: string}>}} state Current view state.
|
|
9960
|
-
*
|
|
9962
|
+
* Without `savedView` the address keeps the applied view it names while
|
|
9963
|
+
* the filters are still the view's: a sort, a search or the view's own
|
|
9964
|
+
* sort option shows the same view's records.
|
|
9965
|
+
*
|
|
9966
|
+
* @param {{savedView?: string}} [options] The saved view the address keeps naming, or `''` for none.
|
|
9961
9967
|
* @return {void}
|
|
9968
|
+
* @spec openspec/changes/saved-view-kept-in-route/specs/saved-view-kept-in-route/spec.md#requirement-the-address-keeps-naming-the-applied-view
|
|
9962
9969
|
*/
|
|
9963
|
-
persistViewStateToRoute(state, { savedView
|
|
9970
|
+
persistViewStateToRoute(state, { savedView } = {}) {
|
|
9964
9971
|
if (!this.$router || !this.$route) {
|
|
9965
9972
|
return
|
|
9966
9973
|
}
|
|
9967
9974
|
const query = { ...this.$route.query }
|
|
9975
|
+
if (savedView === undefined) {
|
|
9976
|
+
const named = this.appliedSavedViewId !== '' && query.savedView === this.appliedSavedViewId
|
|
9977
|
+
savedView = named && this.viewFiltersKey(state.filters) === this.appliedViewFiltersKey ? this.appliedSavedViewId : ''
|
|
9978
|
+
}
|
|
9968
9979
|
// The menu entry of a saved view is active while the address names
|
|
9969
9980
|
// it; any other change to the filters is no longer that view.
|
|
9970
9981
|
if (savedView !== '') {
|
|
@@ -10005,6 +10016,26 @@ export default {
|
|
|
10005
10016
|
this.$router.replace({ query }).catch(() => {})
|
|
10006
10017
|
},
|
|
10007
10018
|
|
|
10019
|
+
/**
|
|
10020
|
+
* Filters as one comparable key: empty values dropped, every value a
|
|
10021
|
+
* sorted list of strings, the keys sorted.
|
|
10022
|
+
*
|
|
10023
|
+
* @param {object} filters The filters.
|
|
10024
|
+
* @return {string}
|
|
10025
|
+
* @spec openspec/changes/saved-view-kept-in-route/specs/saved-view-kept-in-route/spec.md#requirement-the-address-keeps-naming-the-applied-view
|
|
10026
|
+
*/
|
|
10027
|
+
viewFiltersKey(filters) {
|
|
10028
|
+
const out = {}
|
|
10029
|
+
for (const key of Object.keys(filters || {}).sort()) {
|
|
10030
|
+
const value = filters[key]
|
|
10031
|
+
const values = (Array.isArray(value) ? value : [value]).filter((v) => v !== undefined && v !== null && v !== '').map(String).sort()
|
|
10032
|
+
if (values.length > 0) {
|
|
10033
|
+
out[key] = values
|
|
10034
|
+
}
|
|
10035
|
+
}
|
|
10036
|
+
return JSON.stringify(out)
|
|
10037
|
+
},
|
|
10038
|
+
|
|
10008
10039
|
/**
|
|
10009
10040
|
* "Clear all" from the sidebar: reset search, every active filter, the
|
|
10010
10041
|
* sort and the folder-sidebar selection in one fetch + one route
|
|
@@ -11755,6 +11786,7 @@ export default {
|
|
|
11755
11786
|
const ctx = typeof this.selfFetchTokenCtx === 'function' ? this.selfFetchTokenCtx() : {}
|
|
11756
11787
|
state.filters = resolveFilterTokens(state.filters, ctx)
|
|
11757
11788
|
}
|
|
11789
|
+
this.appliedViewFiltersKey = this.viewFiltersKey(state.filters)
|
|
11758
11790
|
// A view that carries columns wins while it is applied; one without leaves the person's own layout.
|
|
11759
11791
|
const viewColumns = view && view.query && Array.isArray(view.query.columns) ? view.query.columns.map(String) : []
|
|
11760
11792
|
this.appliedViewColumns = viewColumns.length > 0 ? viewColumns : null
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
:selectionLabelOne="selectionLabelOne"
|
|
33
33
|
:showClearSelection="showClearSelection"
|
|
34
34
|
:sideCards="sideCards"
|
|
35
|
+
:sideCardsPlacement="sideCardsPlacement"
|
|
35
36
|
:groupBy="groupBy"
|
|
36
37
|
:facets="facets"
|
|
37
38
|
@changed="onBrowserChanged" />
|
|
@@ -422,6 +423,8 @@ export default {
|
|
|
422
423
|
* @type {Array<{id: string, title: string, fields?: Array<object>, actions?: Array<object>}>}
|
|
423
424
|
*/
|
|
424
425
|
sideCards: { type: Array, default: () => [] },
|
|
426
|
+
/** Forwarded to CnFilesBrowser: `page-side` puts the side cards in the detail page's side column. */
|
|
427
|
+
sideCardsPlacement: { type: String, default: 'beside' },
|
|
425
428
|
/** Forwarded to CnFilesBrowser: the key of a declared column to group the files on. */
|
|
426
429
|
groupBy: { type: String, default: '' },
|
|
427
430
|
/**
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
:class="{
|
|
10
10
|
'cn-sections-widget--cards': cards,
|
|
11
11
|
'cn-sections-widget--two-column': twoColumn,
|
|
12
|
+
'cn-sections-widget--grid': gridColumns > 1,
|
|
12
13
|
'cn-sections-widget--plain': plain,
|
|
13
14
|
}"
|
|
14
15
|
data-testid="cn-sections-widget">
|
|
@@ -16,7 +17,8 @@
|
|
|
16
17
|
v-for="column in columns"
|
|
17
18
|
:key="column.id"
|
|
18
19
|
class="cn-sections-widget__column"
|
|
19
|
-
:class="`cn-sections-widget__column--${column.id}`"
|
|
20
|
+
:class="`cn-sections-widget__column--${column.id}`"
|
|
21
|
+
:style="gridColumns > 1 ? { '--cn-sections-columns': gridColumns } : null">
|
|
20
22
|
<section
|
|
21
23
|
v-for="section in column.sections"
|
|
22
24
|
:key="section.key"
|
|
@@ -59,6 +61,9 @@
|
|
|
59
61
|
</div>
|
|
60
62
|
</section>
|
|
61
63
|
</div>
|
|
64
|
+
<p v-if="footnote" class="cn-sections-widget__footnote" data-testid="cn-sections-widget-footnote">
|
|
65
|
+
{{ footnote }}
|
|
66
|
+
</p>
|
|
62
67
|
</div>
|
|
63
68
|
</template>
|
|
64
69
|
|
|
@@ -123,7 +128,11 @@ export default {
|
|
|
123
128
|
* - `dividers` — draw a rule between sections (default true). `false` stacks them 8px apart with no rule,
|
|
124
129
|
* for a row of strips that each say one thing (a record's banners).
|
|
125
130
|
*
|
|
126
|
-
*
|
|
131
|
+
* - `columns` — lay the sections out side by side in this many columns (2 to 4), one column on a narrow
|
|
132
|
+
* screen. Ignored when a section asks for the side column.
|
|
133
|
+
* - `footnote` — a line of muted text under the sections.
|
|
134
|
+
*
|
|
135
|
+
* @type {{sections?: Array<object>, cards?: boolean, hideEmpty?: boolean, dividers?: boolean, columns?: number, footnote?: string}}
|
|
127
136
|
*/
|
|
128
137
|
content: {
|
|
129
138
|
type: Object,
|
|
@@ -285,6 +294,32 @@ export default {
|
|
|
285
294
|
return this.sections.some((section) => section.column === 'side')
|
|
286
295
|
},
|
|
287
296
|
|
|
297
|
+
/**
|
|
298
|
+
* How many columns the sections are laid out in side by side: the
|
|
299
|
+
* configured `columns`, capped at four; 1 (stacked) without it or
|
|
300
|
+
* when a section asks for the side column.
|
|
301
|
+
*
|
|
302
|
+
* @return {number}
|
|
303
|
+
* @spec openspec/changes/sections-card-grid/specs/sections-card-grid/spec.md#requirement-a-sections-widget-can-lay-its-cards-out-in-columns
|
|
304
|
+
*/
|
|
305
|
+
gridColumns() {
|
|
306
|
+
const n = Math.floor(Number(this.content?.columns))
|
|
307
|
+
if (!Number.isFinite(n) || n < 2 || this.twoColumn) {
|
|
308
|
+
return 1
|
|
309
|
+
}
|
|
310
|
+
return Math.min(n, 4)
|
|
311
|
+
},
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* The translated footnote under the sections, or ''.
|
|
315
|
+
*
|
|
316
|
+
* @return {string}
|
|
317
|
+
* @spec openspec/changes/sections-card-grid/specs/sections-card-grid/spec.md#requirement-a-sections-widget-can-close-with-a-footnote
|
|
318
|
+
*/
|
|
319
|
+
footnote() {
|
|
320
|
+
return this.tr(this.content?.footnote || '')
|
|
321
|
+
},
|
|
322
|
+
|
|
288
323
|
/**
|
|
289
324
|
* The columns to draw, each with its sections in order.
|
|
290
325
|
*
|
|
@@ -451,6 +486,22 @@ export default {
|
|
|
451
486
|
}
|
|
452
487
|
}
|
|
453
488
|
|
|
489
|
+
/* Sections side by side (`columns`): at most that many to a row, each at
|
|
490
|
+
least 240px wide, so a narrow cell wraps them onto more rows. */
|
|
491
|
+
.cn-sections-widget.cn-sections-widget--grid .cn-sections-widget__column {
|
|
492
|
+
--cn-sections-gap: 14px;
|
|
493
|
+
display: grid;
|
|
494
|
+
grid-template-columns: repeat(auto-fit, minmax(min(100%, max(240px, calc((100% - (var(--cn-sections-columns, 2) - 1) * var(--cn-sections-gap)) / var(--cn-sections-columns, 2)))), 1fr));
|
|
495
|
+
gap: var(--cn-sections-gap);
|
|
496
|
+
align-items: start;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.cn-sections-widget__footnote {
|
|
500
|
+
margin: calc(var(--default-grid-baseline, 4px) * 3) 0 0;
|
|
501
|
+
color: var(--color-text-maxcontrast);
|
|
502
|
+
font-size: 13px;
|
|
503
|
+
}
|
|
504
|
+
|
|
454
505
|
/* A host page may space every <section>; these sections are spaced by the column gap only. */
|
|
455
506
|
.cn-sections-widget__section {
|
|
456
507
|
margin: 0;
|
|
@@ -79,6 +79,34 @@ export const FLOW_LAYOUT_MARGIN = 60
|
|
|
79
79
|
*/
|
|
80
80
|
export const FLOW_LAYOUT_TOP = 96
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Vertical distance between layers when the run reads top to bottom
|
|
84
|
+
* (`direction: 'down'`), in canvas pixels: a step card (~62px) plus room for
|
|
85
|
+
* the line and its arrowhead, as DqFlow draws its steps 100 to 110px apart.
|
|
86
|
+
*
|
|
87
|
+
* @type {number}
|
|
88
|
+
*/
|
|
89
|
+
export const FLOW_LAYOUT_LAYER_HEIGHT_DOWN = 110
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Where a node sits, from its layer (run order) and its lane (branch).
|
|
93
|
+
*
|
|
94
|
+
* `right` (default): layers are columns left to right, lanes are rows.
|
|
95
|
+
* `down`: layers are rows top to bottom, lanes are columns.
|
|
96
|
+
*
|
|
97
|
+
* @param {number} layer Zero-based layer.
|
|
98
|
+
* @param {number} lane Zero-based lane.
|
|
99
|
+
* @param {string} direction `right` or `down`.
|
|
100
|
+
* @param {number} top The y of the first layer or row.
|
|
101
|
+
* @return {{x: number, y: number}} The point.
|
|
102
|
+
*/
|
|
103
|
+
function cellPoint(layer, lane, direction, top) {
|
|
104
|
+
if (direction === 'down') {
|
|
105
|
+
return { x: FLOW_LAYOUT_MARGIN + (lane * FLOW_LAYOUT_COLUMN_WIDTH), y: top + (layer * FLOW_LAYOUT_LAYER_HEIGHT_DOWN) }
|
|
106
|
+
}
|
|
107
|
+
return { x: FLOW_LAYOUT_MARGIN + (layer * FLOW_LAYOUT_COLUMN_WIDTH), y: top + (lane * FLOW_LAYOUT_ROW_HEIGHT) }
|
|
108
|
+
}
|
|
109
|
+
|
|
82
110
|
/**
|
|
83
111
|
* The position a node carries, in either of the two spellings in circulation.
|
|
84
112
|
*
|
|
@@ -324,9 +352,11 @@ function orderRows(nodes, lines, columns) {
|
|
|
324
352
|
* @param {Array<object>} nodes The flow's nodes.
|
|
325
353
|
* @param {Array<{source: string, target: string}>} lines The expanded edges.
|
|
326
354
|
* @param {Array<string>} startIds The ids a run enters through.
|
|
355
|
+
* @param {object} [options] `{ direction }`: `right` (default) runs left to
|
|
356
|
+
* right, `down` runs top to bottom with branches side by side.
|
|
327
357
|
* @return {Array<object>} New node objects carrying computed positions.
|
|
328
358
|
*/
|
|
329
|
-
export function layoutFlowNodes(nodes, lines, startIds) {
|
|
359
|
+
export function layoutFlowNodes(nodes, lines, startIds, options = {}) {
|
|
330
360
|
const list = Array.isArray(nodes) ? nodes : []
|
|
331
361
|
if (!list.length) {
|
|
332
362
|
return []
|
|
@@ -336,8 +366,7 @@ export function layoutFlowNodes(nodes, lines, startIds) {
|
|
|
336
366
|
const rows = orderRows(list, lines, columns)
|
|
337
367
|
|
|
338
368
|
return list.map((node) => {
|
|
339
|
-
const x
|
|
340
|
-
const y = FLOW_LAYOUT_TOP + (rows.get(node.id) * FLOW_LAYOUT_ROW_HEIGHT)
|
|
369
|
+
const { x, y } = cellPoint(columns.get(node.id), rows.get(node.id), options.direction, FLOW_LAYOUT_TOP)
|
|
341
370
|
return { ...node, x, y, position: { x, y } }
|
|
342
371
|
})
|
|
343
372
|
}
|
|
@@ -359,9 +388,10 @@ export function layoutFlowNodes(nodes, lines, startIds) {
|
|
|
359
388
|
* @param {Array<object>} nodes The flow's nodes, some positioned and some not.
|
|
360
389
|
* @param {Array<{source: string, target: string}>} lines The expanded edges.
|
|
361
390
|
* @param {Array<string>} startIds The ids a run enters through.
|
|
391
|
+
* @param {object} [options] `{ direction }`, as {@link layoutFlowNodes}.
|
|
362
392
|
* @return {Array<object>} The same array shape, loose nodes placed beneath.
|
|
363
393
|
*/
|
|
364
|
-
export function placeLooseNodes(nodes, lines, startIds) {
|
|
394
|
+
export function placeLooseNodes(nodes, lines, startIds, options = {}) {
|
|
365
395
|
const list = Array.isArray(nodes) ? nodes : []
|
|
366
396
|
const loose = list.filter((node) => readNodePoint(node) === null)
|
|
367
397
|
if (!loose.length) {
|
|
@@ -395,8 +425,7 @@ export function placeLooseNodes(nodes, lines, startIds) {
|
|
|
395
425
|
const row = rowsTaken.get(column) || 0
|
|
396
426
|
rowsTaken.set(column, row + 1)
|
|
397
427
|
|
|
398
|
-
const x
|
|
399
|
-
const y = top + (row * FLOW_LAYOUT_ROW_HEIGHT)
|
|
428
|
+
const { x, y } = cellPoint(column, row, options.direction, top)
|
|
400
429
|
return { ...node, x, y, position: { x, y } }
|
|
401
430
|
})
|
|
402
431
|
}
|
|
@@ -101,6 +101,10 @@ export const useFlowStore = defineStore('cnFlow', {
|
|
|
101
101
|
flow: emptyFlow(),
|
|
102
102
|
flows: [],
|
|
103
103
|
|
|
104
|
+
// How the automatic layout runs: `right` (left to right) or `down`
|
|
105
|
+
// (top to bottom). Set by the editor that hosts the canvas.
|
|
106
|
+
layoutDirection: 'right',
|
|
107
|
+
|
|
104
108
|
// Snapshots of `flow`, oldest first, for Ctrl+Z.
|
|
105
109
|
//
|
|
106
110
|
// A canvas is direct manipulation: a mis-drag or a mis-aimed Delete
|
|
@@ -1659,7 +1663,7 @@ export const useFlowStore = defineStore('cnFlow', {
|
|
|
1659
1663
|
return
|
|
1660
1664
|
}
|
|
1661
1665
|
|
|
1662
|
-
this.flow.nodes = layoutFlowNodes(this.nodes, this.canvasEdges, this.startNodeIds)
|
|
1666
|
+
this.flow.nodes = layoutFlowNodes(this.nodes, this.canvasEdges, this.startNodeIds, { direction: this.layoutDirection })
|
|
1663
1667
|
this.dirty = true
|
|
1664
1668
|
},
|
|
1665
1669
|
|
|
@@ -1693,12 +1697,12 @@ export const useFlowStore = defineStore('cnFlow', {
|
|
|
1693
1697
|
}
|
|
1694
1698
|
|
|
1695
1699
|
if (needsFullLayout(nodes)) {
|
|
1696
|
-
this.flow.nodes = layoutFlowNodes(nodes, this.canvasEdges, this.startNodeIds)
|
|
1700
|
+
this.flow.nodes = layoutFlowNodes(nodes, this.canvasEdges, this.startNodeIds, { direction: this.layoutDirection })
|
|
1697
1701
|
return true
|
|
1698
1702
|
}
|
|
1699
1703
|
|
|
1700
1704
|
if (nodes.some((node) => !this.hasPosition(node))) {
|
|
1701
|
-
this.flow.nodes = placeLooseNodes(nodes, this.canvasEdges, this.startNodeIds)
|
|
1705
|
+
this.flow.nodes = placeLooseNodes(nodes, this.canvasEdges, this.startNodeIds, { direction: this.layoutDirection })
|
|
1702
1706
|
return true
|
|
1703
1707
|
}
|
|
1704
1708
|
|
package/src/css/detail-page.css
CHANGED
|
@@ -470,6 +470,12 @@
|
|
|
470
470
|
gap: calc(5 * var(--default-grid-baseline));
|
|
471
471
|
}
|
|
472
472
|
|
|
473
|
+
/* A body widget's own cards (`cnDetailSideColumn`) stand in the column's
|
|
474
|
+
flow, at its gap, and the slot takes no room while it is empty. */
|
|
475
|
+
.cn-detail-page__side-takeover {
|
|
476
|
+
display: contents;
|
|
477
|
+
}
|
|
478
|
+
|
|
473
479
|
/* The record as a place (case-page-and-list-as-a-place) */
|
|
474
480
|
|
|
475
481
|
/* A skip link that reaches the page's declared primary action. Off screen
|
|
@@ -652,9 +652,10 @@
|
|
|
652
652
|
display: flex;
|
|
653
653
|
}
|
|
654
654
|
|
|
655
|
-
/* The notice at the top of the side column: radius 12,
|
|
656
|
-
|
|
657
|
-
|
|
655
|
+
/* The notice at the top of the side column: one tinted block, radius 12,
|
|
656
|
+
padding 14px 16px. Only the outer layer paints; the card wrapper, the
|
|
657
|
+
banner and its note card inside it stay flat, so the notice is not drawn
|
|
658
|
+
as a box in a box. */
|
|
658
659
|
.cn-look-board .cn-detail-page__side-item--notice > * {
|
|
659
660
|
background: var(--cn-board-notice-bg);
|
|
660
661
|
border: none;
|
|
@@ -663,6 +664,23 @@
|
|
|
663
664
|
padding: 14px 16px;
|
|
664
665
|
}
|
|
665
666
|
|
|
667
|
+
.cn-look-board .cn-detail-page__side-item--notice > * .cn-widget-wrapper,
|
|
668
|
+
.cn-look-board .cn-detail-page__side-item--notice > * .cn-widget-wrapper__content,
|
|
669
|
+
.cn-look-board .cn-detail-page__side-item--notice .cn-banner-widget,
|
|
670
|
+
.cn-look-board .cn-detail-page__side-item--notice .cn-banner-widget__card {
|
|
671
|
+
background: transparent;
|
|
672
|
+
border: none;
|
|
673
|
+
border-radius: 0;
|
|
674
|
+
margin: 0;
|
|
675
|
+
padding: 0;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/* A notice whose condition does not hold renders no banner; its slot then
|
|
679
|
+
takes no room instead of standing as an empty tinted block. */
|
|
680
|
+
.cn-look-board .cn-detail-page__side-item--notice:not(:has(.cn-banner-widget)) {
|
|
681
|
+
display: none;
|
|
682
|
+
}
|
|
683
|
+
|
|
666
684
|
/* The History card ends with the record's identifier. */
|
|
667
685
|
.cn-look-board .cn-detail-page__side-item--with-identifier .cn-widget-wrapper {
|
|
668
686
|
border-bottom: none;
|
|
@@ -692,14 +710,16 @@
|
|
|
692
710
|
}
|
|
693
711
|
|
|
694
712
|
/* The kicker keeps its tone under a theme that colours every h3 with
|
|
695
|
-
`!important` (thematiq's heading colour); only `!important` beats it.
|
|
713
|
+
`!important` (thematiq's heading colour); only `!important` beats it. The
|
|
714
|
+
boards set it in sentence case ("Wat nu? Stap 2: in behandeling", DqZaak,
|
|
715
|
+
DqSociaalDomein), not as the plain look's spaced capitals. */
|
|
696
716
|
.cn-look-board .cn-next-step-card--board .cn-next-step-card__title.cn-next-step-card__title {
|
|
697
717
|
color: var(--color-primary-element-light-text) !important;
|
|
698
718
|
font-size: 13px !important;
|
|
699
719
|
font-weight: 700;
|
|
700
|
-
letter-spacing: 0
|
|
720
|
+
letter-spacing: 0;
|
|
701
721
|
line-height: normal;
|
|
702
|
-
text-transform:
|
|
722
|
+
text-transform: none;
|
|
703
723
|
}
|
|
704
724
|
|
|
705
725
|
.cn-look-board .cn-next-step-card--board .cn-next-step-card__list {
|
|
@@ -974,3 +994,30 @@
|
|
|
974
994
|
.cn-look-board .cn-tasks-widget .cn-tasks-widget__cell-actions .button-vue .button-vue__text.button-vue__text {
|
|
975
995
|
padding-inline: 0;
|
|
976
996
|
}
|
|
997
|
+
|
|
998
|
+
/* ---- Grouped cards (CnGroupedCardsWidget, DqZaaktype "Fasen en taken") --- */
|
|
999
|
+
/* Groups 14px apart, each padded 14px with a 10px radius and a bold title; a
|
|
1000
|
+
card in a group is padded 9px 12px with the 8px control radius, its title
|
|
1001
|
+
15px. The doubled class outranks the widget's scoped rules.
|
|
1002
|
+
@spec openspec/changes/board-grouped-cards/specs/board-grouped-cards/spec.md#requirement-board-grouped-cards-take-the-boards-group-and-card-shape */
|
|
1003
|
+
.cn-look-board .cn-grouped-cards__grid.cn-grouped-cards__grid {
|
|
1004
|
+
gap: 14px;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
.cn-look-board .cn-grouped-cards__group.cn-grouped-cards__group {
|
|
1008
|
+
padding: 14px;
|
|
1009
|
+
border-radius: 10px;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
.cn-look-board .cn-grouped-cards__group-title.cn-grouped-cards__group-title {
|
|
1013
|
+
font-weight: 700;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
.cn-look-board .cn-grouped-cards__item.cn-grouped-cards__item {
|
|
1017
|
+
padding: 9px 12px;
|
|
1018
|
+
border-radius: var(--cn-board-control-radius, 8px);
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
.cn-look-board .cn-grouped-cards__item-title.cn-grouped-cards__item-title {
|
|
1022
|
+
font-size: 15px;
|
|
1023
|
+
}
|