@conduction/nextcloud-vue 2.64.0 → 2.65.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/CnAppNav/CnAppNav.vue2.js +99 -3
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js +23 -7
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue2.js +13 -0
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue2.js.map +1 -1
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue3.js +2 -1
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +26 -2
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +6 -3
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +64 -5
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +66 -28
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js +22 -5
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js +1 -1
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +22 -3
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +10 -3
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +164 -0
- package/dist/esm/store/useObjectStore.js +118 -33
- package/dist/esm/store/useObjectStore.js.map +1 -1
- package/dist/esm/utils/actionsDispatcher.js +14 -0
- package/dist/esm/utils/actionsDispatcher.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +488 -96
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +164 -0
- package/package.json +1 -1
- package/src/components/CnAppNav/CnAppNav.vue +148 -7
- package/src/components/CnBreadcrumbs/CnBreadcrumbs.vue +14 -0
- package/src/components/CnDashboardPage/CnDashboardPage.vue +29 -2
- package/src/components/CnDetailPage/CnDetailPage.md +5 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +114 -6
- package/src/components/CnHeaderWidget/CnHeaderWidget.vue +34 -6
- package/src/components/CnStagesWidget/CnStagesWidget.vue +9 -0
- package/src/components/CnStatWidget/CnStatWidget.vue +33 -5
- package/src/css/detail-page.css +22 -0
- package/src/css/kpi-card.css +47 -0
- package/src/schemas/app-manifest-v2.schema.json +23 -2
- package/src/store/useObjectStore.js +118 -33
- package/src/utils/actionsDispatcher.js +14 -0
- package/src/utils/validateManifestV2.compiled.js +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: package/dist/nextcloud-vue.cjs.js.map
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 35252015 bytes
|
|
4
|
+
sha256: fc6a53315288d3796fbfb1aabafee9aa7c5145a0eadbfa2e240269fa544b9764
|
package/dist/nextcloud-vue.css
CHANGED
|
@@ -1283,6 +1283,53 @@
|
|
|
1283
1283
|
font-size: var(--cn-kpi-value-size-stacked, 2rem);
|
|
1284
1284
|
}
|
|
1285
1285
|
|
|
1286
|
+
/* Stacked: the board's tile (`content.layout: 'stacked'`). A plain muted
|
|
1287
|
+
label, the number at 34px/700 in the text colour, and the caption on a line
|
|
1288
|
+
of its own; CnStatWidget draws no icon circle in this layout. The padding
|
|
1289
|
+
adds up with a dashboard card-fit cell's 8px 14px to the board's 18px 20px.
|
|
1290
|
+
Every value is a token so a theme can move it. */
|
|
1291
|
+
.cn-kpi-card--stacked {
|
|
1292
|
+
flex-direction: column;
|
|
1293
|
+
align-items: stretch;
|
|
1294
|
+
justify-content: center;
|
|
1295
|
+
gap: 0;
|
|
1296
|
+
padding: var(--cn-kpi-stacked-padding, 10px 6px);
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
.cn-kpi-card--stacked .cn-kpi-card__body {
|
|
1300
|
+
gap: var(--cn-kpi-stacked-gap, 6px);
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
.cn-kpi-card--stacked .cn-kpi-card__title {
|
|
1304
|
+
font-size: var(--cn-kpi-stacked-title-size, 14px);
|
|
1305
|
+
font-weight: var(--cn-kpi-stacked-title-weight, 400);
|
|
1306
|
+
color: var(--color-text-maxcontrast);
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
.cn-kpi-card--stacked .cn-kpi-card__value {
|
|
1310
|
+
font-size: var(--cn-kpi-stacked-value-size, 34px);
|
|
1311
|
+
font-weight: var(--cn-kpi-value-weight, 700);
|
|
1312
|
+
line-height: 1;
|
|
1313
|
+
letter-spacing: -0.01em;
|
|
1314
|
+
color: var(--cn-kpi-stacked-value-color, var(--color-main-text));
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
.cn-kpi-card--stacked .cn-kpi-card__caption-line {
|
|
1318
|
+
flex: 0 0 auto;
|
|
1319
|
+
max-width: 100%;
|
|
1320
|
+
font-size: var(--cn-kpi-label-size, 13px);
|
|
1321
|
+
font-weight: 500;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
/* A linked stacked tile reads as a tile, not as underlined link text. The
|
|
1325
|
+
NL Design theme underlines every `a` with `!important`, so the opt-out
|
|
1326
|
+
needs it too; it only applies to this opt-in layout. */
|
|
1327
|
+
a.cn-kpi-card--stacked,
|
|
1328
|
+
a.cn-kpi-card--stacked:hover,
|
|
1329
|
+
a.cn-kpi-card--stacked:focus-visible {
|
|
1330
|
+
text-decoration: none !important;
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1286
1333
|
/* Filled: the card draws its own grey box, for a KPI that is NOT already
|
|
1287
1334
|
inside a wrapper that draws one. Opt-in via `content.flat: false`. */
|
|
1288
1335
|
.cn-kpi-card--filled {
|
|
@@ -2790,6 +2837,28 @@
|
|
|
2790
2837
|
min-width: 0;
|
|
2791
2838
|
}
|
|
2792
2839
|
|
|
2840
|
+
/* A widget in the header (`config.headerWidget`) takes a row of its own under
|
|
2841
|
+
the title and the actions. */
|
|
2842
|
+
.cn-detail-page__header--with-widget {
|
|
2843
|
+
flex-wrap: wrap;
|
|
2844
|
+
row-gap: var(--cn-detail-header-row-gap, 22px);
|
|
2845
|
+
}
|
|
2846
|
+
|
|
2847
|
+
.cn-detail-page__header-widget {
|
|
2848
|
+
flex: 1 0 100%;
|
|
2849
|
+
min-width: 0;
|
|
2850
|
+
}
|
|
2851
|
+
|
|
2852
|
+
/* The header as a card (`config.headerCard`): the board's case header, a
|
|
2853
|
+
bordered box on the surface colour. The 56px inset that clears the
|
|
2854
|
+
navigation toggle is not needed inside a card that starts below it. */
|
|
2855
|
+
.cn-detail-page__header--card {
|
|
2856
|
+
padding: var(--cn-detail-header-card-padding, 22px 24px);
|
|
2857
|
+
border: 1px solid var(--color-border);
|
|
2858
|
+
border-radius: var(--cn-detail-header-card-radius, 12px);
|
|
2859
|
+
background: var(--color-main-background);
|
|
2860
|
+
}
|
|
2861
|
+
|
|
2793
2862
|
.cn-detail-page__header-left {
|
|
2794
2863
|
display: flex;
|
|
2795
2864
|
align-items: center;
|
|
@@ -16875,7 +16944,17 @@ a.cn-xwiki-card__title[data-v-54e85fe3]:hover {
|
|
|
16875
16944
|
text-overflow: ellipsis;
|
|
16876
16945
|
white-space: nowrap;
|
|
16877
16946
|
}
|
|
16947
|
+
|
|
16948
|
+
/* A clickable label is a <button>, and Nextcloud's core stylesheet gives
|
|
16949
|
+
every `button:not(.button-vue)` a min-height of the clickable area (34 to
|
|
16950
|
+
44px) with the text centred in it. That pushed each reachable stage's label
|
|
16951
|
+
about 12px below the current one, which is a <span>. Zeroed here so all
|
|
16952
|
+
labels sit 8px under their bar, as the board draws them. The visually
|
|
16953
|
+
hidden "(current step)" is absolutely positioned and takes no space. */
|
|
16878
16954
|
button.cn-stages-widget__bar-label[data-v-358fde0f] {
|
|
16955
|
+
min-height: 0;
|
|
16956
|
+
height: auto;
|
|
16957
|
+
line-height: inherit;
|
|
16879
16958
|
cursor: pointer;
|
|
16880
16959
|
}
|
|
16881
16960
|
button.cn-stages-widget__bar-label[aria-disabled="true"][data-v-358fde0f] {
|
|
@@ -17310,6 +17389,53 @@ button.cn-stages-widget__bar-label[data-v-358fde0f]:focus-visible {
|
|
|
17310
17389
|
font-size: var(--cn-kpi-value-size-stacked, 2rem);
|
|
17311
17390
|
}
|
|
17312
17391
|
|
|
17392
|
+
/* Stacked: the board's tile (`content.layout: 'stacked'`). A plain muted
|
|
17393
|
+
label, the number at 34px/700 in the text colour, and the caption on a line
|
|
17394
|
+
of its own; CnStatWidget draws no icon circle in this layout. The padding
|
|
17395
|
+
adds up with a dashboard card-fit cell's 8px 14px to the board's 18px 20px.
|
|
17396
|
+
Every value is a token so a theme can move it. */
|
|
17397
|
+
.cn-kpi-card--stacked {
|
|
17398
|
+
flex-direction: column;
|
|
17399
|
+
align-items: stretch;
|
|
17400
|
+
justify-content: center;
|
|
17401
|
+
gap: 0;
|
|
17402
|
+
padding: var(--cn-kpi-stacked-padding, 10px 6px);
|
|
17403
|
+
}
|
|
17404
|
+
|
|
17405
|
+
.cn-kpi-card--stacked .cn-kpi-card__body {
|
|
17406
|
+
gap: var(--cn-kpi-stacked-gap, 6px);
|
|
17407
|
+
}
|
|
17408
|
+
|
|
17409
|
+
.cn-kpi-card--stacked .cn-kpi-card__title {
|
|
17410
|
+
font-size: var(--cn-kpi-stacked-title-size, 14px);
|
|
17411
|
+
font-weight: var(--cn-kpi-stacked-title-weight, 400);
|
|
17412
|
+
color: var(--color-text-maxcontrast);
|
|
17413
|
+
}
|
|
17414
|
+
|
|
17415
|
+
.cn-kpi-card--stacked .cn-kpi-card__value {
|
|
17416
|
+
font-size: var(--cn-kpi-stacked-value-size, 34px);
|
|
17417
|
+
font-weight: var(--cn-kpi-value-weight, 700);
|
|
17418
|
+
line-height: 1;
|
|
17419
|
+
letter-spacing: -0.01em;
|
|
17420
|
+
color: var(--cn-kpi-stacked-value-color, var(--color-main-text));
|
|
17421
|
+
}
|
|
17422
|
+
|
|
17423
|
+
.cn-kpi-card--stacked .cn-kpi-card__caption-line {
|
|
17424
|
+
flex: 0 0 auto;
|
|
17425
|
+
max-width: 100%;
|
|
17426
|
+
font-size: var(--cn-kpi-label-size, 13px);
|
|
17427
|
+
font-weight: 500;
|
|
17428
|
+
}
|
|
17429
|
+
|
|
17430
|
+
/* A linked stacked tile reads as a tile, not as underlined link text. The
|
|
17431
|
+
NL Design theme underlines every `a` with `!important`, so the opt-out
|
|
17432
|
+
needs it too; it only applies to this opt-in layout. */
|
|
17433
|
+
a.cn-kpi-card--stacked,
|
|
17434
|
+
a.cn-kpi-card--stacked:hover,
|
|
17435
|
+
a.cn-kpi-card--stacked:focus-visible {
|
|
17436
|
+
text-decoration: none !important;
|
|
17437
|
+
}
|
|
17438
|
+
|
|
17313
17439
|
/* Filled: the card draws its own grey box, for a KPI that is NOT already
|
|
17314
17440
|
inside a wrapper that draws one. Opt-in via `content.flat: false`. */
|
|
17315
17441
|
.cn-kpi-card--filled {
|
|
@@ -18262,6 +18388,16 @@ to { background-position: -200% 0;
|
|
|
18262
18388
|
flex: 0 0 auto;
|
|
18263
18389
|
padding: 16px;
|
|
18264
18390
|
}
|
|
18391
|
+
|
|
18392
|
+
/* On the page ground (`content.ground`): no card, so no inset around the
|
|
18393
|
+
view switch, and the heading at the board's 32px. */
|
|
18394
|
+
.cn-header-widget--ground .cn-header-widget__views[data-v-1b97430f] {
|
|
18395
|
+
padding: 0;
|
|
18396
|
+
}
|
|
18397
|
+
.cn-header-widget--ground .cn-header-widget__title[data-v-1b97430f] {
|
|
18398
|
+
font-size: var(--cn-header-ground-title-size, 32px);
|
|
18399
|
+
letter-spacing: -0.01em;
|
|
18400
|
+
}
|
|
18265
18401
|
.cn-header-widget__date[data-v-1b97430f] {
|
|
18266
18402
|
font-size: 14px;
|
|
18267
18403
|
line-height: 1.4;
|
|
@@ -33247,6 +33383,21 @@ a.cn-integration-card__title[data-v-f7e9bbfa]:hover {
|
|
|
33247
33383
|
padding: calc(var(--default-grid-baseline, 4px) * 2);
|
|
33248
33384
|
}
|
|
33249
33385
|
|
|
33386
|
+
/*
|
|
33387
|
+
* The primary action never shrinks out of sight. NcAppNavigation renders it in
|
|
33388
|
+
* `.app-navigation__body`, a flex child with `overflow-y: scroll` (so its
|
|
33389
|
+
* minimum height is 0) beside a list of `height: 100%` and the footer. With a
|
|
33390
|
+
* card and footer entries below the list, the column overflows and the body
|
|
33391
|
+
* gives up its share of the shrink: "New case" showed as a 24px sliver of a
|
|
33392
|
+
* 42px button. All three forms carry `.app-navigation-new` (the solid and
|
|
33393
|
+
* link wrappers mirror NcAppNavigationNew's class), and the rule is scoped to
|
|
33394
|
+
* this component's navigation, so a navigation without a primary action lays
|
|
33395
|
+
* out exactly as before.
|
|
33396
|
+
*/
|
|
33397
|
+
.app-navigation[data-testid="cn-nav"] .app-navigation__body:has(> .app-navigation-new) {
|
|
33398
|
+
flex-shrink: 0;
|
|
33399
|
+
}
|
|
33400
|
+
|
|
33250
33401
|
/*
|
|
33251
33402
|
* Footer-section items (section: "footer") render in NcAppNavigation's
|
|
33252
33403
|
* #footer slot, outside the scroll container, so they stay visible above
|
|
@@ -33270,6 +33421,19 @@ a.cn-integration-card__title[data-v-f7e9bbfa]:hover {
|
|
|
33270
33421
|
overflow: visible !important;
|
|
33271
33422
|
}
|
|
33272
33423
|
|
|
33424
|
+
/*
|
|
33425
|
+
* A declared footer that names `settings` first (`nav.footer`) puts the
|
|
33426
|
+
* foldout above the footer list. The two are siblings in NcAppNavigation's
|
|
33427
|
+
* flex column, so `order` moves them without changing either's markup; the
|
|
33428
|
+
* card above keeps order 0 and stays first.
|
|
33429
|
+
*/
|
|
33430
|
+
.cn-app-nav__settings--first {
|
|
33431
|
+
order: 1;
|
|
33432
|
+
}
|
|
33433
|
+
.cn-app-nav__footer-list--after-settings {
|
|
33434
|
+
order: 2;
|
|
33435
|
+
}
|
|
33436
|
+
|
|
33273
33437
|
/*
|
|
33274
33438
|
* Align the settings foldout's items with the main/footer list items,
|
|
33275
33439
|
* whose icons sit at a 16px inset. The foldout's items live in a bare
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@conduction/nextcloud-vue",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.65.0",
|
|
4
4
|
"description": "Shared Vue component library for Conduction Nextcloud apps — complements @nextcloud/vue with higher-level components, OpenRegister integration, and NL Design System support",
|
|
5
5
|
"homepage": "https://github.com/ConductionNL/nextcloud-vue#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -246,7 +246,7 @@
|
|
|
246
246
|
</NcAppNavigationItem>
|
|
247
247
|
</template>
|
|
248
248
|
</template>
|
|
249
|
-
<template v-if="footerItems.length > 0 || showSettingsFoldout || resolvedCard ||
|
|
249
|
+
<template v-if="footerItems.length > 0 || showSettingsFoldout || resolvedCard || showsHelp" #footer>
|
|
250
250
|
<!--
|
|
251
251
|
@slot card
|
|
252
252
|
@description Replace the card above the footer entries
|
|
@@ -282,11 +282,15 @@
|
|
|
282
282
|
hover highlight are scoped to itself, so they survive being
|
|
283
283
|
slotted. The main list's inset comes from NcAppNavigation's
|
|
284
284
|
scope, which slot content does not carry. -->
|
|
285
|
-
<NcAppNavigationList
|
|
285
|
+
<NcAppNavigationList
|
|
286
|
+
v-if="footerItems.length > 0 || showsHelp"
|
|
287
|
+
class="cn-app-nav__footer-list"
|
|
288
|
+
:class="{ 'cn-app-nav__footer-list--after-settings': settingsFirst }">
|
|
286
289
|
<!-- The help entry (`nav.help`): a link to the app's own help
|
|
287
|
-
with a help icon, above the footer entries
|
|
290
|
+
with a help icon, above the footer entries (or after them,
|
|
291
|
+
when a declared `nav.footer` names it last). -->
|
|
288
292
|
<NcAppNavigationItem
|
|
289
|
-
v-if="
|
|
293
|
+
v-if="showsHelp && !helpAfterItems"
|
|
290
294
|
:name="resolvedHelp.label"
|
|
291
295
|
:to="resolvedHelp.to"
|
|
292
296
|
:href="resolvedHelp.href"
|
|
@@ -318,6 +322,17 @@
|
|
|
318
322
|
:active="isActive(item)" />
|
|
319
323
|
</template>
|
|
320
324
|
</NcAppNavigationItem>
|
|
325
|
+
<NcAppNavigationItem
|
|
326
|
+
v-if="showsHelp && helpAfterItems"
|
|
327
|
+
:name="resolvedHelp.label"
|
|
328
|
+
:to="resolvedHelp.to"
|
|
329
|
+
:href="resolvedHelp.href"
|
|
330
|
+
:target="resolvedHelp.href ? '_blank' : undefined"
|
|
331
|
+
data-testid="cn-nav-help">
|
|
332
|
+
<template #icon>
|
|
333
|
+
<HelpCircleOutline :size="20" />
|
|
334
|
+
</template>
|
|
335
|
+
</NcAppNavigationItem>
|
|
321
336
|
</NcAppNavigationList>
|
|
322
337
|
<!-- Settings foldout (section: "settings" items). NC-native
|
|
323
338
|
gear-icon button that slides open a panel; the first entry
|
|
@@ -326,6 +341,7 @@
|
|
|
326
341
|
<NcAppNavigationSettings
|
|
327
342
|
v-if="showSettingsFoldout"
|
|
328
343
|
:name="settingsFoldoutLabel"
|
|
344
|
+
:class="{ 'cn-app-nav__settings--first': settingsFirst }"
|
|
329
345
|
data-testid="cn-nav-settings">
|
|
330
346
|
<ul class="cn-app-nav__settings-list">
|
|
331
347
|
<NcAppNavigationItem
|
|
@@ -958,17 +974,113 @@ export default {
|
|
|
958
974
|
* Documentation, Features & Roadmap, About.
|
|
959
975
|
*/
|
|
960
976
|
footerItems() {
|
|
961
|
-
|
|
977
|
+
const footer = this.visibleItems.filter((item) => item.section === 'footer')
|
|
978
|
+
const declared = this.declaredFooter
|
|
979
|
+
if (declared === null) {
|
|
980
|
+
return footer
|
|
981
|
+
}
|
|
982
|
+
// A declared footer (`nav.footer`) keeps only the entries it names,
|
|
983
|
+
// in the order it names them.
|
|
984
|
+
return declared
|
|
985
|
+
.map((id) => footer.find((item) => item.id === id))
|
|
986
|
+
.filter(Boolean)
|
|
987
|
+
},
|
|
988
|
+
|
|
989
|
+
/**
|
|
990
|
+
* The footer the manifest declares (`nav.footer`): an ordered list of
|
|
991
|
+
* menu entry ids plus the reserved ids `help` (the `nav.help` entry)
|
|
992
|
+
* and `settings` (the settings foldout). Null when the manifest
|
|
993
|
+
* declares none, which keeps today's footer: the help entry, then
|
|
994
|
+
* every `section: "footer"` entry, then the foldout.
|
|
995
|
+
*
|
|
996
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-2/specs/zuiddrecht-pixel-gaps-2/spec.md#requirement-the-navigation-footer-can-be-declared
|
|
997
|
+
* @return {Array<string>|null}
|
|
998
|
+
*/
|
|
999
|
+
declaredFooter() {
|
|
1000
|
+
const declared = this.effectiveManifest?.nav?.footer
|
|
1001
|
+
if (!Array.isArray(declared)) {
|
|
1002
|
+
return null
|
|
1003
|
+
}
|
|
1004
|
+
return declared.filter((id) => typeof id === 'string' && id !== '')
|
|
1005
|
+
},
|
|
1006
|
+
|
|
1007
|
+
/**
|
|
1008
|
+
* Footer-section entries a declared footer leaves out. They move into
|
|
1009
|
+
* the settings foldout rather than disappearing, so a page such as
|
|
1010
|
+
* Store or Reports stays reachable. Empty without `nav.footer`.
|
|
1011
|
+
*
|
|
1012
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-2/specs/zuiddrecht-pixel-gaps-2/spec.md#requirement-the-navigation-footer-can-be-declared
|
|
1013
|
+
* @return {Array<object>}
|
|
1014
|
+
*/
|
|
1015
|
+
undeclaredFooterItems() {
|
|
1016
|
+
const declared = this.declaredFooter
|
|
1017
|
+
if (declared === null) {
|
|
1018
|
+
return []
|
|
1019
|
+
}
|
|
1020
|
+
return this.visibleItems.filter((item) => item.section === 'footer' && !declared.includes(item.id))
|
|
1021
|
+
},
|
|
1022
|
+
|
|
1023
|
+
/**
|
|
1024
|
+
* Whether the help entry renders: whenever it resolves, unless a
|
|
1025
|
+
* declared footer leaves `help` out.
|
|
1026
|
+
*
|
|
1027
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-2/specs/zuiddrecht-pixel-gaps-2/spec.md#requirement-the-navigation-footer-can-be-declared
|
|
1028
|
+
* @return {boolean}
|
|
1029
|
+
*/
|
|
1030
|
+
showsHelp() {
|
|
1031
|
+
if (!this.resolvedHelp) {
|
|
1032
|
+
return false
|
|
1033
|
+
}
|
|
1034
|
+
return this.declaredFooter === null || this.declaredFooter.includes('help')
|
|
1035
|
+
},
|
|
1036
|
+
|
|
1037
|
+
/**
|
|
1038
|
+
* Whether the help entry goes AFTER the footer entries. Only a declared
|
|
1039
|
+
* footer that names an entry before `help` puts it there; by default
|
|
1040
|
+
* it leads the list.
|
|
1041
|
+
*
|
|
1042
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-2/specs/zuiddrecht-pixel-gaps-2/spec.md#requirement-the-navigation-footer-can-be-declared
|
|
1043
|
+
* @return {boolean}
|
|
1044
|
+
*/
|
|
1045
|
+
helpAfterItems() {
|
|
1046
|
+
const declared = this.declaredFooter
|
|
1047
|
+
if (declared === null) {
|
|
1048
|
+
return false
|
|
1049
|
+
}
|
|
1050
|
+
const help = declared.indexOf('help')
|
|
1051
|
+
return this.footerItems.some((item) => declared.indexOf(item.id) < help)
|
|
1052
|
+
},
|
|
1053
|
+
|
|
1054
|
+
/**
|
|
1055
|
+
* Whether the settings foldout goes ABOVE the footer list: only when a
|
|
1056
|
+
* declared footer names `settings` before every other entry
|
|
1057
|
+
* (`["settings", "help"]`, the board's "Instellingen" then "Hulp en
|
|
1058
|
+
* uitleg"). Done with CSS `order` on the two siblings, so the markup
|
|
1059
|
+
* of both stays as it is.
|
|
1060
|
+
*
|
|
1061
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-2/specs/zuiddrecht-pixel-gaps-2/spec.md#requirement-the-navigation-footer-can-be-declared
|
|
1062
|
+
* @return {boolean}
|
|
1063
|
+
*/
|
|
1064
|
+
settingsFirst() {
|
|
1065
|
+
const declared = this.declaredFooter
|
|
1066
|
+
return declared !== null && declared.indexOf('settings') === 0
|
|
962
1067
|
},
|
|
963
1068
|
|
|
964
1069
|
/**
|
|
965
1070
|
* Items that render INSIDE the NcAppNavigationSettings foldout
|
|
966
1071
|
* (section: "settings"). The foldout is the NC-native gear-icon
|
|
967
1072
|
* button that slides a panel open; these entries are app-level
|
|
968
|
-
* configuration pages (Forms, Pipelines, Automations, …).
|
|
1073
|
+
* configuration pages (Forms, Pipelines, Automations, …). A declared
|
|
1074
|
+
* footer (`nav.footer`) adds the footer entries it leaves out.
|
|
1075
|
+
*
|
|
1076
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-2/specs/zuiddrecht-pixel-gaps-2/spec.md#requirement-the-navigation-footer-can-be-declared
|
|
1077
|
+
* @return {Array<object>}
|
|
969
1078
|
*/
|
|
970
1079
|
settingsItems() {
|
|
971
|
-
return
|
|
1080
|
+
return [
|
|
1081
|
+
...this.visibleItems.filter((item) => item.section === 'settings'),
|
|
1082
|
+
...this.undeclaredFooterItems,
|
|
1083
|
+
]
|
|
972
1084
|
},
|
|
973
1085
|
|
|
974
1086
|
/**
|
|
@@ -2294,6 +2406,21 @@ export default {
|
|
|
2294
2406
|
padding: calc(var(--default-grid-baseline, 4px) * 2);
|
|
2295
2407
|
}
|
|
2296
2408
|
|
|
2409
|
+
/*
|
|
2410
|
+
* The primary action never shrinks out of sight. NcAppNavigation renders it in
|
|
2411
|
+
* `.app-navigation__body`, a flex child with `overflow-y: scroll` (so its
|
|
2412
|
+
* minimum height is 0) beside a list of `height: 100%` and the footer. With a
|
|
2413
|
+
* card and footer entries below the list, the column overflows and the body
|
|
2414
|
+
* gives up its share of the shrink: "New case" showed as a 24px sliver of a
|
|
2415
|
+
* 42px button. All three forms carry `.app-navigation-new` (the solid and
|
|
2416
|
+
* link wrappers mirror NcAppNavigationNew's class), and the rule is scoped to
|
|
2417
|
+
* this component's navigation, so a navigation without a primary action lays
|
|
2418
|
+
* out exactly as before.
|
|
2419
|
+
*/
|
|
2420
|
+
.app-navigation[data-testid="cn-nav"] .app-navigation__body:has(> .app-navigation-new) {
|
|
2421
|
+
flex-shrink: 0;
|
|
2422
|
+
}
|
|
2423
|
+
|
|
2297
2424
|
/*
|
|
2298
2425
|
* Footer-section items (section: "footer") render in NcAppNavigation's
|
|
2299
2426
|
* #footer slot, outside the scroll container, so they stay visible above
|
|
@@ -2317,6 +2444,20 @@ export default {
|
|
|
2317
2444
|
overflow: visible !important;
|
|
2318
2445
|
}
|
|
2319
2446
|
|
|
2447
|
+
/*
|
|
2448
|
+
* A declared footer that names `settings` first (`nav.footer`) puts the
|
|
2449
|
+
* foldout above the footer list. The two are siblings in NcAppNavigation's
|
|
2450
|
+
* flex column, so `order` moves them without changing either's markup; the
|
|
2451
|
+
* card above keeps order 0 and stays first.
|
|
2452
|
+
*/
|
|
2453
|
+
.cn-app-nav__settings--first {
|
|
2454
|
+
order: 1;
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
.cn-app-nav__footer-list--after-settings {
|
|
2458
|
+
order: 2;
|
|
2459
|
+
}
|
|
2460
|
+
|
|
2320
2461
|
/*
|
|
2321
2462
|
* Align the settings foldout's items with the main/footer list items,
|
|
2322
2463
|
* whose icons sit at a 16px inset. The foldout's items live in a bare
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
<NcBreadcrumbs
|
|
19
19
|
v-if="crumbs.length > 0"
|
|
20
20
|
:aria-label="ariaLabel"
|
|
21
|
+
:rootIcon="rootText ? '' : undefined"
|
|
21
22
|
data-testid="cn-breadcrumbs">
|
|
22
23
|
<!-- `name` falls back to '' because NcBreadcrumb declares it a REQUIRED
|
|
23
24
|
String and warns per render on the label-less icon-only crumb. The
|
|
@@ -97,6 +98,19 @@ export default {
|
|
|
97
98
|
type: String,
|
|
98
99
|
default: () => t('nextcloud-vue', 'Breadcrumbs'),
|
|
99
100
|
},
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Draw the FIRST crumb's label as text. NcBreadcrumbs gives the root
|
|
104
|
+
* crumb a home icon and prints its `label` only as the accessible
|
|
105
|
+
* name, so "All cases" read as a house. Off by default, which keeps
|
|
106
|
+
* the home icon. A first crumb with its own `icon` keeps that icon.
|
|
107
|
+
*
|
|
108
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-2/specs/zuiddrecht-pixel-gaps-2/spec.md#requirement-a-declared-breadcrumb-label-shows-as-text
|
|
109
|
+
*/
|
|
110
|
+
rootText: {
|
|
111
|
+
type: Boolean,
|
|
112
|
+
default: false,
|
|
113
|
+
},
|
|
100
114
|
},
|
|
101
115
|
|
|
102
116
|
methods: {
|
|
@@ -404,6 +404,7 @@
|
|
|
404
404
|
:iconUrl="getWidgetIconUrl(item)"
|
|
405
405
|
:iconClass="getWidgetIconClass(item)"
|
|
406
406
|
:showTitle="widgetShowTitle(item)"
|
|
407
|
+
:showActions="widgetShowActions(item)"
|
|
407
408
|
:borderless="widgetBorderless(item)"
|
|
408
409
|
:flush="item.flush !== false"
|
|
409
410
|
:buttons="getWidgetButtons(item)"
|
|
@@ -508,6 +509,7 @@
|
|
|
508
509
|
:iconUrl="getWidgetIconUrl(item)"
|
|
509
510
|
:iconClass="getWidgetIconClass(item)"
|
|
510
511
|
:showTitle="widgetShowTitle(item)"
|
|
512
|
+
:showActions="widgetShowActions(item)"
|
|
511
513
|
:borderless="widgetBorderless(item)"
|
|
512
514
|
:flush="item.flush !== false"
|
|
513
515
|
:buttons="getWidgetButtons(item)"
|
|
@@ -546,6 +548,7 @@
|
|
|
546
548
|
:iconUrl="getWidgetIconUrl(item)"
|
|
547
549
|
:iconClass="getWidgetIconClass(item)"
|
|
548
550
|
:showTitle="widgetShowTitle(item)"
|
|
551
|
+
:showActions="widgetShowActions(item)"
|
|
549
552
|
:buttons="getWidgetButtons(item)"
|
|
550
553
|
:headerLink="getWidgetHeaderLink(item)"
|
|
551
554
|
:styleConfig="item.styleConfig || {}"
|
|
@@ -946,6 +949,21 @@ export default {
|
|
|
946
949
|
default: true,
|
|
947
950
|
},
|
|
948
951
|
|
|
952
|
+
/**
|
|
953
|
+
* Whether a widget draws its overflow Actions menu when neither its
|
|
954
|
+
* placement nor its definition says (`showActions`). `false` (manifest
|
|
955
|
+
* `config.showWidgetActions: false`) drops the menu from every such
|
|
956
|
+
* widget, for a dashboard whose widgets carry a header link and
|
|
957
|
+
* nothing else. A widget that sets `showActions: true` keeps its menu.
|
|
958
|
+
* Card widgets have no menu either way.
|
|
959
|
+
*
|
|
960
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-2/specs/zuiddrecht-pixel-gaps-2/spec.md#requirement-a-dashboard-can-drop-the-widget-actions-menu
|
|
961
|
+
*/
|
|
962
|
+
showWidgetActions: {
|
|
963
|
+
type: Boolean,
|
|
964
|
+
default: true,
|
|
965
|
+
},
|
|
966
|
+
|
|
949
967
|
/**
|
|
950
968
|
* Widget definitions array. Each widget defines metadata for rendering.
|
|
951
969
|
*
|
|
@@ -3380,11 +3398,20 @@ export default {
|
|
|
3380
3398
|
* exactly that reason; there is now one rule for every family, so a
|
|
3381
3399
|
* custom slot and a registered widget cannot disagree about it.
|
|
3382
3400
|
*
|
|
3401
|
+
* A greeting header that declares `content.ground: true` sits on the
|
|
3402
|
+
* page ground, so it asks for no card either: one key on the widget
|
|
3403
|
+
* instead of a second one on its placement.
|
|
3404
|
+
*
|
|
3405
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-2/specs/zuiddrecht-pixel-gaps-2/spec.md#requirement-a-greeting-can-sit-on-the-page-ground
|
|
3383
3406
|
* @param {object} item the layout placement.
|
|
3384
3407
|
* @return {boolean}
|
|
3385
3408
|
*/
|
|
3386
3409
|
widgetBorderless(item) {
|
|
3387
|
-
|
|
3410
|
+
if (item.borderless === true) {
|
|
3411
|
+
return true
|
|
3412
|
+
}
|
|
3413
|
+
const def = this.getWidgetDef(item.widgetId)
|
|
3414
|
+
return Boolean(def && def.type === 'header' && this.getWidgetContent(item).ground === true)
|
|
3388
3415
|
},
|
|
3389
3416
|
|
|
3390
3417
|
/**
|
|
@@ -3401,7 +3428,7 @@ export default {
|
|
|
3401
3428
|
const def = this.getWidgetDef(item.widgetId)
|
|
3402
3429
|
const value = item.showActions !== undefined ? item.showActions : def?.showActions
|
|
3403
3430
|
if (value === undefined || value === null) {
|
|
3404
|
-
return !this.isCardWidget(item)
|
|
3431
|
+
return this.showWidgetActions && !this.isCardWidget(item)
|
|
3405
3432
|
}
|
|
3406
3433
|
return value !== false
|
|
3407
3434
|
},
|
|
@@ -433,4 +433,8 @@ When the schema-driven fetch answers 404 the page shows a not-found state whose
|
|
|
433
433
|
|
|
434
434
|
### Type eyebrow and breadcrumb
|
|
435
435
|
|
|
436
|
-
`showTypeEyebrow` (default `true`) can drop the type label above the record name. `breadcrumb` (`{ label, route?, params?, href? }`) draws a breadcrumb line above the header: that crumb, then the record's name as the current crumb.
|
|
436
|
+
`showTypeEyebrow` (default `true`) can drop the type label above the record name. `breadcrumb` (`{ label, route?, params?, href?, icon? }`) draws a breadcrumb line above the header: that crumb, then the record's name as the current crumb. The label shows as text; `icon` draws an icon instead.
|
|
437
|
+
|
|
438
|
+
### Header card and header widget
|
|
439
|
+
|
|
440
|
+
`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.
|