@conduction/nextcloud-vue 2.60.0 → 2.61.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.
Files changed (56) hide show
  1. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +41 -1
  2. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
  3. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
  4. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +9 -2
  5. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
  6. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
  7. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +10 -2
  8. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  9. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  10. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  11. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +6 -5
  12. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  13. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js +3 -1
  14. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js.map +1 -1
  15. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js.map +1 -1
  16. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue.js +10 -0
  17. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue.js.map +1 -0
  18. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue2.js +316 -0
  19. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue2.js.map +1 -0
  20. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue3.js +105 -0
  21. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue3.js.map +1 -0
  22. package/dist/esm/components/CnPageRenderer/pageTypes.js +1 -0
  23. package/dist/esm/components/CnPageRenderer/pageTypes.js.map +1 -1
  24. package/dist/esm/index.js +6 -0
  25. package/dist/esm/index.js.map +1 -1
  26. package/dist/esm/l10n/en.json.js +1 -0
  27. package/dist/esm/l10n/en.json.js.map +1 -1
  28. package/dist/esm/l10n/nl.json.js +1 -0
  29. package/dist/esm/l10n/nl.json.js.map +1 -1
  30. package/dist/esm/nextcloud-vue.css +92 -2
  31. package/dist/esm/utils/headers.js +28 -2
  32. package/dist/esm/utils/headers.js.map +1 -1
  33. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  34. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  35. package/dist/nextcloud-vue.cjs.js +8741 -8229
  36. package/dist/nextcloud-vue.cjs.js.map +2 -2
  37. package/dist/nextcloud-vue.css +92 -2
  38. package/l10n/en.json +1 -0
  39. package/l10n/nl.json +1 -0
  40. package/package.json +1 -1
  41. package/scripts/.jsdoc-baselines.json +1 -0
  42. package/src/components/CnAppNav/CnAppNav.vue +41 -1
  43. package/src/components/CnBannerWidget/CnBannerWidget.vue +9 -2
  44. package/src/components/CnDashboardPage/CnDashboardPage.vue +10 -2
  45. package/src/components/CnDetailPage/CnDetailPage.vue +4 -1
  46. package/src/components/CnHeaderWidget/CnHeaderWidget.vue +3 -1
  47. package/src/components/CnLinkCardsPage/CnLinkCardsPage.vue +484 -0
  48. package/src/components/CnLinkCardsPage/index.js +4 -0
  49. package/src/components/CnPageRenderer/pageTypes.js +1 -0
  50. package/src/components/index.js +1 -0
  51. package/src/css/detail-page.css +10 -2
  52. package/src/index.js +7 -0
  53. package/src/schemas/app-manifest-v2.schema.json +44 -2
  54. package/src/types/manifest.d.ts +1 -1
  55. package/src/utils/headers.js +28 -2
  56. 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: 34515119 bytes
4
- sha256: 5f3fcfa87667eab7bfc0a4c99e86f6e46f2b136b344fa184d53d9e04aff4db7e
3
+ size: 34609538 bytes
4
+ sha256: c869939a6bed082d5118c299d33615f1b775c22e374698978bf2cec085595abb
@@ -2660,9 +2660,17 @@
2660
2660
  font-weight: 700;
2661
2661
  line-height: 1.3;
2662
2662
  color: var(--color-main-text);
2663
- white-space: nowrap;
2663
+ /* Two lines, then an ellipsis. One line cut a long case title short on a
2664
+ page with room to spare, and the title is the one thing that says which
2665
+ record this is. The element carries the full text as its `title`, so a
2666
+ title cut after two lines is still reachable. `overflow-wrap` breaks a
2667
+ word longer than the line, which nowrap used to hide. */
2668
+ display: -webkit-box;
2669
+ -webkit-box-orient: vertical;
2670
+ -webkit-line-clamp: 2;
2671
+ line-clamp: 2;
2664
2672
  overflow: hidden;
2665
- text-overflow: ellipsis;
2673
+ overflow-wrap: anywhere;
2666
2674
  }
2667
2675
 
2668
2676
  .cn-detail-page__subtitle {
@@ -32005,6 +32013,88 @@ a.cn-integration-card__title[data-v-f7e9bbfa]:hover {
32005
32013
  }
32006
32014
 
32007
32015
 
32016
+ .cn-link-cards-page[data-v-2f9a546b] {
32017
+ display: flex;
32018
+ flex-direction: column;
32019
+ gap: calc(6 * var(--default-grid-baseline));
32020
+ padding: calc(5 * var(--default-grid-baseline));
32021
+ }
32022
+ .cn-link-cards-page__title[data-v-2f9a546b] {
32023
+ margin: 0 0 var(--default-grid-baseline);
32024
+ }
32025
+ .cn-link-cards-page__description[data-v-2f9a546b],
32026
+ .cn-link-cards-page__empty[data-v-2f9a546b] {
32027
+ color: var(--color-text-maxcontrast);
32028
+ margin: 0;
32029
+ max-width: 70ch;
32030
+ }
32031
+ .cn-link-cards-page__group[data-v-2f9a546b] {
32032
+ display: flex;
32033
+ flex-direction: column;
32034
+ gap: calc(3 * var(--default-grid-baseline));
32035
+ }
32036
+ .cn-link-cards-page__caption[data-v-2f9a546b] {
32037
+ color: var(--color-text-maxcontrast);
32038
+ font-size: 0.85em;
32039
+ font-weight: 600;
32040
+ letter-spacing: 0.06em;
32041
+ margin: 0;
32042
+ text-transform: uppercase;
32043
+ }
32044
+ .cn-link-cards-page__grid[data-v-2f9a546b] {
32045
+ display: grid;
32046
+ gap: calc(3 * var(--default-grid-baseline));
32047
+ grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
32048
+ list-style: none;
32049
+ margin: 0;
32050
+ padding: 0;
32051
+ }
32052
+ .cn-link-cards-page__cell[data-v-2f9a546b] {
32053
+ display: flex;
32054
+ }
32055
+ .cn-link-cards-page__card[data-v-2f9a546b] {
32056
+ align-items: flex-start;
32057
+ background: var(--color-main-background);
32058
+ border: 1px solid var(--color-border-dark);
32059
+ border-radius: var(--border-radius-large);
32060
+ color: var(--color-main-text);
32061
+ display: flex;
32062
+ flex: 1;
32063
+ gap: calc(3 * var(--default-grid-baseline));
32064
+ min-width: 0;
32065
+ padding: calc(4 * var(--default-grid-baseline));
32066
+ text-decoration: none;
32067
+ }
32068
+ .cn-link-cards-page__card[data-v-2f9a546b]:hover {
32069
+ background: var(--color-background-hover);
32070
+ border-color: var(--color-primary-element);
32071
+ }
32072
+ .cn-link-cards-page__card[data-v-2f9a546b]:focus-visible {
32073
+ border-color: var(--color-primary-element);
32074
+ outline: 2px solid var(--color-primary-element);
32075
+ outline-offset: 2px;
32076
+ }
32077
+ .cn-link-cards-page__icon[data-v-2f9a546b] {
32078
+ color: var(--color-primary-element);
32079
+ flex: none;
32080
+ }
32081
+ .cn-link-cards-page__body[data-v-2f9a546b] {
32082
+ display: flex;
32083
+ flex-direction: column;
32084
+ gap: var(--default-grid-baseline);
32085
+ min-width: 0;
32086
+ }
32087
+ .cn-link-cards-page__label[data-v-2f9a546b] {
32088
+ font-weight: 600;
32089
+ overflow-wrap: anywhere;
32090
+ }
32091
+ .cn-link-cards-page__card-description[data-v-2f9a546b] {
32092
+ color: var(--color-text-maxcontrast);
32093
+ font-size: 0.9em;
32094
+ line-height: 1.4;
32095
+ }
32096
+
32097
+
32008
32098
  /* NcDialog supplies the padding and the heading (via `name`); this div keeps only
32009
32099
  the body's own column layout. */
32010
32100
  .cn-page-config[data-v-9e9a9cf8] {
package/l10n/en.json CHANGED
@@ -294,6 +294,7 @@
294
294
  "JSON pointer": "JSON pointer",
295
295
  "Last": "Last",
296
296
  "Load more": "Load more",
297
+ "Nothing to open here.": "Nothing to open here.",
297
298
  "To do": "To do",
298
299
  "Next step": "Next step",
299
300
  "Review": "Review",
package/l10n/nl.json CHANGED
@@ -294,6 +294,7 @@
294
294
  "JSON pointer": "JSON-aanwijzer",
295
295
  "Last": "Laatste",
296
296
  "Load more": "Meer laden",
297
+ "Nothing to open here.": "Hier is niets te openen.",
297
298
  "To do": "Nog doen",
298
299
  "Next step": "Volgende stap",
299
300
  "Review": "Beoordelen",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conduction/nextcloud-vue",
3
- "version": "2.60.0",
3
+ "version": "2.61.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": {
@@ -130,6 +130,7 @@
130
130
  "CnLifecycleActions": 1,
131
131
  "CnLinkButtonWidget": 1,
132
132
  "CnLinkButtonWidgetForm": 1,
133
+ "CnLinkCardsPage": 1,
133
134
  "CnLinksWidget": 1,
134
135
  "CnLinksWidgetForm": 1,
135
136
  "CnLockedBanner": 1,
@@ -1184,11 +1184,51 @@ export default {
1184
1184
  return this.effectiveTranslate(item.label)
1185
1185
  },
1186
1186
 
1187
+ /**
1188
+ * Whether a menu entry is the page the reader is on.
1189
+ *
1190
+ * Two entries may share a route and differ only in `query` ("My
1191
+ * work" and "Queue" on one list). The route name alone lit both. An
1192
+ * entry with a `query` is active only when the address carries that
1193
+ * query; an entry without one steps back when a sibling's query
1194
+ * matches, so exactly one of them is marked.
1195
+ *
1196
+ * @param {object} item Menu entry.
1197
+ * @return {boolean} True when the entry is the current page.
1198
+ * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-menu-entries-that-differ-in-query
1199
+ */
1187
1200
  isActive(item) {
1188
1201
  if (item.href || !item.route) {
1189
1202
  return false
1190
1203
  }
1191
- return item.route === this.activeRouteName
1204
+ if (item.route !== this.activeRouteName) {
1205
+ return false
1206
+ }
1207
+ if (item.query && typeof item.query === 'object') {
1208
+ return this.queryMatches(item)
1209
+ }
1210
+ const siblings = []
1211
+ for (const entry of this.visibleItems) {
1212
+ siblings.push(entry, ...this.visibleChildren(entry))
1213
+ }
1214
+ return !siblings.some((entry) => entry !== item
1215
+ && entry.route === item.route
1216
+ && entry.query && typeof entry.query === 'object'
1217
+ && this.queryMatches(entry))
1218
+ },
1219
+
1220
+ /**
1221
+ * Whether the address carries every key of an entry's `query`.
1222
+ * Values are compared as strings, since an address has no numbers.
1223
+ *
1224
+ * @param {object} item Menu entry with a `query`.
1225
+ * @return {boolean} True when every declared key matches.
1226
+ * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-menu-entries-that-differ-in-query
1227
+ */
1228
+ queryMatches(item) {
1229
+ const current = this.$route?.query ?? {}
1230
+ const keys = Object.keys(item.query)
1231
+ return keys.length > 0 && keys.every((key) => String(current[key] ?? '') === String(item.query[key]))
1192
1232
  },
1193
1233
 
1194
1234
  /**
@@ -359,7 +359,9 @@ export default {
359
359
  return this.fillValue(reason)
360
360
  }
361
361
  const title = this.title || (this.content && this.content.title) || ''
362
- return title ? this.displayText : ''
362
+ // A card that sets `text` to the same words as its `title` says
363
+ // them once.
364
+ return title && this.displayText !== this.displayTitle ? this.displayText : ''
363
365
  },
364
366
 
365
367
  /**
@@ -512,7 +514,12 @@ export default {
512
514
  const value = await readVisibleWhenValue(cond)
513
515
  this.conditionValue = value
514
516
  this.conditionMet = compareVisibleWhen(value, cond.op || 'eq', cond.value)
515
- } catch {
517
+ } catch (error) {
518
+ // Still hidden: a banner that cannot tell whether it applies
519
+ // should not claim attention. But not silently, because a
520
+ // failed count and "nothing to report" look the same on screen.
521
+ // eslint-disable-next-line no-console
522
+ console.warn('[CnBannerWidget] visibleWhen could not be evaluated, banner hidden:', error?.message || error)
516
523
  this.conditionMet = false
517
524
  this.conditionValue = null
518
525
  }
@@ -2748,16 +2748,24 @@ export default {
2748
2748
  * the registry branch forwards to CnBannerWidget), or in the legacy
2749
2749
  * manifest `props` blob (the fallback getStatsBlockProps and
2750
2750
  * getChartProps already honour for their own widget types). `text`
2751
- * is only meaningful for banners.
2751
+ * is only meaningful for banners: it is what the banner would show,
2752
+ * which for an attention card is its `title` when it has no `text`.
2752
2753
  *
2753
2754
  * @param {object} def Widget definition.
2755
+ * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-an-attention-card-with-a-title-keeps-its-cell
2754
2756
  * @return {{ text: string, visibleWhen: (object|null) }}
2755
2757
  */
2756
2758
  widgetDisplayConfig(def) {
2757
2759
  const content = (def && typeof def.content === 'object' && def.content) || {}
2758
2760
  const props = (def && typeof def.props === 'object' && def.props) || {}
2761
+ // An attention card (`layout: "attention"`) is headed by `title`,
2762
+ // and its title only FALLS BACK to `text`. Reading `text` alone
2763
+ // called a card with a title and a reason empty, and gave its cell
2764
+ // up before its `visibleWhen` was even looked at.
2765
+ const layout = content.layout || props.layout || ''
2766
+ const title = layout === 'attention' ? (content.title || props.title || '') : ''
2759
2767
  return {
2760
- text: content.text || props.text || '',
2768
+ text: content.text || props.text || title || '',
2761
2769
  visibleWhen: (def && def.visibleWhen) || content.visibleWhen || props.visibleWhen || null,
2762
2770
  }
2763
2771
  },
@@ -103,7 +103,10 @@
103
103
  data-testid="cn-detail-page-type-eyebrow">
104
104
  {{ typeEyebrow }}
105
105
  </p>
106
- <h2 v-if="displayTitle" class="cn-detail-page__title">
106
+ <h2
107
+ v-if="displayTitle"
108
+ class="cn-detail-page__title"
109
+ :title="displayTitle">
107
110
  {{ displayTitle }}
108
111
  </h2>
109
112
  <!--
@@ -496,7 +496,9 @@ export default {
496
496
  'flex-direction': 'column',
497
497
  'align-items': this.flexAlignFromTextAlign,
498
498
  'justify-content': VERTICAL_ALIGN_FLEX[this.verticalAlign],
499
- padding: this.isPlain ? '0' : '16px 24px',
499
+ // Plain drops the coloured background, not the card padding:
500
+ // at 0 the greeting sat flush against the card's left edge.
501
+ padding: this.isPlain ? '16px' : '16px 24px',
500
502
  'box-sizing': 'border-box',
501
503
  gap: '8px',
502
504
  'text-align': this.textAlign,