@conduction/nextcloud-vue 2.76.0 → 2.78.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/CnAiCompanion/CnAiCompanion.vue2.js +23 -0
- package/dist/esm/components/CnAiCompanion/CnAiCompanion.vue2.js.map +1 -1
- package/dist/esm/components/CnAiCompanion/CnAiCompanion.vue3.js.map +1 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +18 -3
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js +49 -3
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +2 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +79 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +229 -203
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue.js +9 -0
- package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue.js.map +1 -0
- package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue2.js +50 -0
- package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue2.js.map +1 -0
- package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue3.js +13 -0
- package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue3.js.map +1 -0
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +37 -0
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js +2 -2
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +29 -4
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +48 -6
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +90 -51
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDateRangePicker/CnDateRangePicker.vue.js +1 -1
- package/dist/esm/components/CnDateRangePicker/CnDateRangePicker.vue2.js +45 -2
- package/dist/esm/components/CnDateRangePicker/CnDateRangePicker.vue2.js.map +1 -1
- package/dist/esm/components/CnDateRangePicker/CnDateRangePicker.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +16 -28
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +12 -24
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnFavouriteToggle/CnFavouriteToggle.vue2.js +7 -2
- package/dist/esm/components/CnFavouriteToggle/CnFavouriteToggle.vue2.js.map +1 -1
- package/dist/esm/components/CnFavouriteToggle/CnFavouriteToggle.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +114 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +115 -62
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue2.js +89 -7
- package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue2.js.map +1 -1
- package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue3.js +62 -30
- package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue3.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +7 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnFormPage/CnFormPage.vue2.js +4 -1
- package/dist/esm/components/CnFormPage/CnFormPage.vue2.js.map +1 -1
- package/dist/esm/components/CnFormPage/CnFormPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +104 -13
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +13 -9
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js +199 -7
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js +155 -119
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +6 -0
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +4 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +36 -0
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +35 -23
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStatusBadge/CnStatusBadge.vue2.js +6 -2
- package/dist/esm/components/CnStatusBadge/CnStatusBadge.vue2.js.map +1 -1
- package/dist/esm/components/CnStatusBadge/CnStatusBadge.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +13 -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/composables/useFormDraft.js +11 -1
- package/dist/esm/composables/useFormDraft.js.map +1 -1
- package/dist/esm/composables/useListView.js +4 -0
- package/dist/esm/composables/useListView.js.map +1 -1
- package/dist/esm/composables/useObjectPresence.js +15 -4
- package/dist/esm/composables/useObjectPresence.js.map +1 -1
- package/dist/esm/index.js +2 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +32 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +32 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +500 -75
- package/dist/esm/store/plugins/interactions.js +20 -10
- package/dist/esm/store/plugins/interactions.js.map +1 -1
- package/dist/esm/store/useObjectStore.js +17 -0
- package/dist/esm/store/useObjectStore.js.map +1 -1
- package/dist/esm/utils/badgeVariants.js +32 -0
- package/dist/esm/utils/badgeVariants.js.map +1 -0
- package/dist/esm/utils/builtInFormatters.js +15 -7
- package/dist/esm/utils/builtInFormatters.js.map +1 -1
- package/dist/esm/utils/groupAutocomplete.js +63 -1
- package/dist/esm/utils/groupAutocomplete.js.map +1 -1
- package/dist/esm/utils/lensAvailability.js +90 -0
- package/dist/esm/utils/lensAvailability.js.map +1 -0
- package/dist/esm/utils/personalLenses.js +13 -6
- package/dist/esm/utils/personalLenses.js.map +1 -1
- package/dist/esm/utils/recordInteractions.js +31 -13
- package/dist/esm/utils/recordInteractions.js.map +1 -1
- package/dist/esm/utils/resolveFilterTokens.js +46 -3
- package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
- package/dist/esm/utils/schema.js +1 -1
- package/dist/esm/utils/schema.js.map +1 -1
- package/dist/esm/utils/sentinelTokens.js +4 -4
- package/dist/esm/utils/sentinelTokens.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/esm/utils/widgetVisibility.js +46 -1
- package/dist/esm/utils/widgetVisibility.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +4389 -2890
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +500 -75
- package/l10n/en.json +32 -1
- package/l10n/nl.json +32 -1
- package/package.json +1 -1
- package/src/components/CnAiCompanion/CnAiCompanion.vue +23 -0
- package/src/components/CnAppNav/CnAppNav.vue +44 -5
- package/src/components/CnBoardView/CnBoardView.vue +2 -1
- package/src/components/CnCellRenderer/CnCellRenderer.md +1 -0
- package/src/components/CnCellRenderer/CnCellRenderer.vue +106 -9
- package/src/components/CnCellRenderer/CnGroupNameCell.vue +61 -0
- package/src/components/CnChartWidget/CnChartWidget.vue +39 -2
- package/src/components/CnDashboardPage/CnDashboardPage.vue +29 -4
- package/src/components/CnDataTable/CnDataTable.md +9 -0
- package/src/components/CnDataTable/CnDataTable.vue +87 -22
- package/src/components/CnDateRangePicker/CnDateRangePicker.vue +44 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +10 -27
- package/src/components/CnFavouriteToggle/CnFavouriteToggle.vue +7 -2
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +204 -5
- package/src/components/CnFollowToggle/CnFollowToggle.vue +118 -13
- package/src/components/CnFormDialog/CnFormDialog.vue +7 -1
- package/src/components/CnFormPage/CnFormPage.vue +4 -1
- package/src/components/CnIndexPage/CnIndexPage.md +36 -0
- package/src/components/CnIndexPage/CnIndexPage.vue +112 -19
- package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +226 -2
- package/src/components/CnObjectSidebar/CnFilesTab.vue +9 -0
- package/src/components/CnStatWidget/CnStatWidget.vue +52 -2
- package/src/components/CnStatusBadge/CnStatusBadge.vue +6 -2
- package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +13 -0
- package/src/composables/__tests__/useObjectPresence.spec.js +24 -0
- package/src/composables/useFormDraft.js +11 -1
- package/src/composables/useListView.js +4 -0
- package/src/composables/useObjectPresence.js +15 -4
- package/src/css/badge.css +31 -0
- package/src/css/kpi-card.css +51 -0
- package/src/css/look-board-detail.css +40 -2
- package/src/css/look-board-index.css +36 -0
- package/src/css/look-board.css +8 -0
- package/src/css/table.css +82 -17
- package/src/index.js +2 -1
- package/src/schemas/app-manifest-v2.schema.json +15 -11
- package/src/store/plugins/interactions.js +20 -10
- package/src/store/useObjectStore.js +17 -0
- package/src/utils/badgeVariants.js +29 -0
- package/src/utils/builtInFormatters.js +15 -7
- package/src/utils/groupAutocomplete.js +75 -0
- package/src/utils/index.js +1 -1
- package/src/utils/lensAvailability.js +86 -0
- package/src/utils/personalLenses.js +13 -6
- package/src/utils/recordInteractions.js +31 -13
- package/src/utils/resolveFilterTokens.js +45 -2
- package/src/utils/schema.js +1 -1
- package/src/utils/sentinelTokens.js +4 -4
- package/src/utils/validateManifestV2.compiled.js +2 -2
- package/src/utils/widgetVisibility.js +45 -0
|
@@ -883,7 +883,7 @@ import CnBodySections from '../CnBodySections/CnBodySections.vue'
|
|
|
883
883
|
import CnBuildiqEditButton from '../CnBuildiqEditButton/CnBuildiqEditButton.vue'
|
|
884
884
|
import CnChartWidget from '../CnChartWidget/CnChartWidget.vue'
|
|
885
885
|
import CnDashboardGrid from '../CnDashboardGrid/CnDashboardGrid.vue'
|
|
886
|
-
import CnDateRangePicker, { DEFAULT_DATE_RANGE_PRESETS, resolvePresetWindow } from '../CnDateRangePicker/CnDateRangePicker.vue'
|
|
886
|
+
import CnDateRangePicker, { DEFAULT_DATE_RANGE_PRESETS, resolvePresetWindow, translatePresetLabel } from '../CnDateRangePicker/CnDateRangePicker.vue'
|
|
887
887
|
import CnEmptyContent from '../CnEmptyContent/CnEmptyContent.vue'
|
|
888
888
|
import CnKpiGrid from '../CnKpiGrid/CnKpiGrid.vue'
|
|
889
889
|
import CnSegmentedControl from '../CnSegmentedControl/CnSegmentedControl.vue'
|
|
@@ -2009,9 +2009,12 @@ export default {
|
|
|
2009
2009
|
* doesn't supply one.
|
|
2010
2010
|
*/
|
|
2011
2011
|
effectivePresets() {
|
|
2012
|
-
|
|
2012
|
+
const presets = Array.isArray(this.dateRange?.presets) && this.dateRange.presets.length > 0
|
|
2013
2013
|
? this.dateRange.presets
|
|
2014
|
-
: DEFAULT_DATE_RANGE_PRESETS
|
|
2014
|
+
: DEFAULT_DATE_RANGE_PRESETS
|
|
2015
|
+
// Labels in the user's language (screens-dashboard-i18n): the app's
|
|
2016
|
+
// catalogue first, then the library's own for the default labels.
|
|
2017
|
+
return presets.map((p) => ({ ...p, label: translatePresetLabel(p.label, this.effectiveTranslate) }))
|
|
2015
2018
|
},
|
|
2016
2019
|
|
|
2017
2020
|
/**
|
|
@@ -3354,7 +3357,7 @@ export default {
|
|
|
3354
3357
|
* @return {object}
|
|
3355
3358
|
*/
|
|
3356
3359
|
registryWidgetBindings(item) {
|
|
3357
|
-
const content = this.getWidgetContent(item)
|
|
3360
|
+
const content = this.collectionContent(this.getWidgetContent(item))
|
|
3358
3361
|
const def = this.getWidgetDef(item.widgetId)
|
|
3359
3362
|
if (!this.isBannerDef(def)) {
|
|
3360
3363
|
return content
|
|
@@ -3363,6 +3366,28 @@ export default {
|
|
|
3363
3366
|
return outcome ? { ...content, conditionOutcome: outcome } : content
|
|
3364
3367
|
},
|
|
3365
3368
|
|
|
3369
|
+
/**
|
|
3370
|
+
* A widget's content with the board's quiet collection footer: in the
|
|
3371
|
+
* board look a dashboard that drops the widget Actions menu
|
|
3372
|
+
* (`showWidgetActions: false`) also drops the Add footer of its list
|
|
3373
|
+
* widgets, whose Add is the menu's create entry at the bottom of the
|
|
3374
|
+
* card. A widget that says `content.allowCreate` keeps its own value.
|
|
3375
|
+
* Otherwise the content is returned as is.
|
|
3376
|
+
*
|
|
3377
|
+
* @spec openspec/changes/screens-dashboard-legacy-widgets/specs/dashboard-page/spec.md#requirement-a-board-dashboard-without-widget-actions-drops-the-add-footer
|
|
3378
|
+
* @param {object} content The widget's stored content.
|
|
3379
|
+
* @return {object}
|
|
3380
|
+
*/
|
|
3381
|
+
collectionContent(content) {
|
|
3382
|
+
if (!this.isBoardLook || this.showWidgetActions !== false) {
|
|
3383
|
+
return content
|
|
3384
|
+
}
|
|
3385
|
+
if (content && Object.hasOwn(content, 'allowCreate')) {
|
|
3386
|
+
return content
|
|
3387
|
+
}
|
|
3388
|
+
return { ...content, allowCreate: false }
|
|
3389
|
+
},
|
|
3390
|
+
|
|
3366
3391
|
/**
|
|
3367
3392
|
* Re-compact a filtered layout upward, display-only. GridStack runs
|
|
3368
3393
|
* `float: true`, so removing an item does not close its row — this
|
|
@@ -205,6 +205,7 @@ export default {
|
|
|
205
205
|
| `filterable` | Boolean | `false` | Show a filter button in every header whose column can filter: enum, boolean, text, number, date and reference columns backed by a schema property. A column opts out with `filterable: false`. Applying a filter emits `column-filter`; the host owns the state and passes it back as `activeFilters`. `CnIndexPage` turns it on by default. |
|
|
206
206
|
| `activeFilters` | Object | `{}` | The active filter map, `{ paramKey: values[] }`: the same map the facet sidebar writes, so a header filter and a sidebar filter on one field show the same state. |
|
|
207
207
|
| `filterRegister` | String | `''` | Register slug a reference column's filter searches in when the column names none. |
|
|
208
|
+
| `lensReasonTexts` | Object \| null | `null` | App wording for an unavailable personal lens in self-fetch mode, keyed `<lens>.<reason>` or `<reason>`. Without it the library's text is used (see below). |
|
|
208
209
|
|
|
209
210
|
## Slots
|
|
210
211
|
|
|
@@ -233,6 +234,14 @@ features are folded in here as opt-in props (bare-table usage is unchanged):
|
|
|
233
234
|
- `fetchParams` — extra query params for the self-fetch (a resolved filter map,
|
|
234
235
|
`_order[field]` ordering, `_limit`); changing it re-triggers the fetch. Used by
|
|
235
236
|
`CnWidgetObjectTable`'s declarative `source`.
|
|
237
|
+
- When the self-fetch asked for a personal lens (a "Recently opened" tile with
|
|
238
|
+
`source.filter._recent: true`) and the response reports it unavailable
|
|
239
|
+
(`@self.lenses.recent.available: false`, openregister#4514), the empty row
|
|
240
|
+
says why instead of `emptyText`: "This server does not keep track of what you
|
|
241
|
+
open." (`audit-trail-disabled`), "Log in to see what you opened recently."
|
|
242
|
+
(`anonymous`) or "Your recent items are not available right now."
|
|
243
|
+
(`read-history-unavailable`). `lensReasonTexts` replaces the wording; no
|
|
244
|
+
report, an available lens or an unknown reason keeps `emptyText`.
|
|
236
245
|
- `rowClickRoute` — a function mapping a row to a vue-router route. Each such row renders a real `<a href>` in its first cell, stretched over the row, so hovering shows the URL, a plain or alt click routes in place, a ctrl/cmd/shift or middle click opens a new tab, and the row is reachable with Tab. The link is named by the first cell's text ("Open row" when that is empty or not plain text). There is no link on a `selectable` table without `rowClickToView`, where a row click selects. `CnWidgetObjectTable` rows inherit it. Trade-offs: cell text cannot be selected by dragging on a linked row, and a right-click on it shows the browser's link menu only when no host listens to `row-context-menu`.
|
|
237
246
|
- `hideHeader` — drop the column-label row for a compact list widget.
|
|
238
247
|
- `#footer` slot — supply a custom footer link (e.g. "+ New" or an always-shown
|
|
@@ -130,25 +130,42 @@
|
|
|
130
130
|
pinClass(leadingCount + colIndex),
|
|
131
131
|
]"
|
|
132
132
|
:style="{ ...(col.width ? { width: col.width } : {}), ...pinStyle(leadingCount + colIndex) }"
|
|
133
|
-
:tabindex="col.sortable ? 0 : null"
|
|
134
133
|
:aria-sort="ariaSortFor(col)"
|
|
135
134
|
:data-filtered="isColumnFiltered(col) ? 'true' : null"
|
|
136
135
|
:title="translateLabel(col.description) || null"
|
|
137
136
|
@click="col.sortable ? onHeaderClick(col.key, $event) : null"
|
|
138
137
|
@keydown.enter="col.sortable ? onHeaderKeydown(col.key, $event) : null">
|
|
139
|
-
|
|
138
|
+
<!-- The sort control is a real button named by the column
|
|
139
|
+
label; the header's `aria-sort` carries the state. Its
|
|
140
|
+
click and Enter bubble to the header's own handlers, so
|
|
141
|
+
a click anywhere in the header still sorts. -->
|
|
142
|
+
<button
|
|
143
|
+
v-if="col.sortable"
|
|
144
|
+
type="button"
|
|
145
|
+
class="cn-table-header__sort"
|
|
146
|
+
data-testid="cn-table-header-sort">
|
|
147
|
+
<span :class="col.description ? 'cn-table-header--described' : ''">{{ translateLabel(col.label) }}</span>
|
|
148
|
+
<span
|
|
149
|
+
v-if="sortKeyIndex(col.key) !== -1"
|
|
150
|
+
class="cn-table-header__chevron cn-table-sort-indicator"
|
|
151
|
+
:data-sort-direction="effectiveSortKeys[sortKeyIndex(col.key)].order === 'asc' ? 'asc' : 'desc'">
|
|
152
|
+
<ChevronUp v-if="effectiveSortKeys[sortKeyIndex(col.key)].order === 'asc'" :size="14" />
|
|
153
|
+
<ChevronDown v-else :size="14" />
|
|
154
|
+
</span>
|
|
155
|
+
<span
|
|
156
|
+
v-else
|
|
157
|
+
class="cn-table-header__chevron cn-table-header__chevron--idle">
|
|
158
|
+
<ChevronDown :size="14" />
|
|
159
|
+
</span>
|
|
160
|
+
<span
|
|
161
|
+
v-if="sortBadgeFor(col.key) !== null"
|
|
162
|
+
class="cn-table-sort-badge">
|
|
163
|
+
{{ sortBadgeFor(col.key) }}
|
|
164
|
+
</span>
|
|
165
|
+
</button>
|
|
166
|
+
<span v-else :class="col.description ? 'cn-table-header--described' : ''">
|
|
140
167
|
{{ translateLabel(col.label) }}
|
|
141
168
|
</span>
|
|
142
|
-
<span
|
|
143
|
-
v-if="col.sortable && sortKeyIndex(col.key) !== -1"
|
|
144
|
-
class="cn-table-sort-indicator">
|
|
145
|
-
{{ effectiveSortKeys[sortKeyIndex(col.key)].order === 'asc' ? '▲' : '▼' }}
|
|
146
|
-
</span>
|
|
147
|
-
<span
|
|
148
|
-
v-if="col.sortable && sortBadgeFor(col.key) !== null"
|
|
149
|
-
class="cn-table-sort-badge">
|
|
150
|
-
{{ sortBadgeFor(col.key) }}
|
|
151
|
-
</span>
|
|
152
169
|
<!-- Header filter: a real button, so it is reachable with Tab
|
|
153
170
|
and its click and Enter never also sort the column. -->
|
|
154
171
|
<button
|
|
@@ -163,8 +180,11 @@
|
|
|
163
180
|
data-testid="cn-table-header-filter"
|
|
164
181
|
@click.stop="toggleColumnFilter(col, $event)"
|
|
165
182
|
@keydown.enter.stop>
|
|
166
|
-
<
|
|
167
|
-
<
|
|
183
|
+
<FilterOutline :size="14" />
|
|
184
|
+
<span
|
|
185
|
+
v-if="isColumnFiltered(col)"
|
|
186
|
+
class="cn-table-header__filter-dot"
|
|
187
|
+
data-testid="cn-table-header-filter-dot" />
|
|
168
188
|
</button>
|
|
169
189
|
</th>
|
|
170
190
|
|
|
@@ -182,9 +202,12 @@
|
|
|
182
202
|
<!-- Empty state -->
|
|
183
203
|
<tr v-if="effectiveRows.length === 0" class="cn-table-empty" data-testid="cn-object-list-empty">
|
|
184
204
|
<td :colspan="totalColumns">
|
|
185
|
-
<!-- @slot Empty-state content shown when there are no rows (defaults to `emptyText
|
|
205
|
+
<!-- @slot Empty-state content shown when there are no rows (defaults to `emptyText`, or why an unavailable personal lens is empty). -->
|
|
186
206
|
<slot name="empty">
|
|
187
|
-
{{
|
|
207
|
+
<span v-if="lensReasonText" class="cn-table-empty__lens-reason" data-testid="cn-data-table-lens-reason">{{ lensReasonText }}</span>
|
|
208
|
+
<template v-else>
|
|
209
|
+
{{ translateLabel(emptyText) }}
|
|
210
|
+
</template>
|
|
188
211
|
</slot>
|
|
189
212
|
</td>
|
|
190
213
|
</tr>
|
|
@@ -370,12 +393,14 @@ import axios from '@nextcloud/axios'
|
|
|
370
393
|
import { translatePlural as n, translate as t } from '@nextcloud/l10n'
|
|
371
394
|
import { generateUrl } from '@nextcloud/router'
|
|
372
395
|
import { NcCheckboxRadioSwitch, NcLoadingIcon } from '@nextcloud/vue'
|
|
373
|
-
import
|
|
396
|
+
import ChevronDown from 'vue-material-design-icons/ChevronDown.vue'
|
|
397
|
+
import ChevronUp from 'vue-material-design-icons/ChevronUp.vue'
|
|
374
398
|
import FilterOutline from 'vue-material-design-icons/FilterOutline.vue'
|
|
375
399
|
import CnColumnFilterPopover from './CnColumnFilterPopover.vue'
|
|
376
400
|
import { useClickDragGuard } from '../../composables/useClickDragGuard.js'
|
|
377
401
|
import { normalizeLook } from '../../composables/useLook.js'
|
|
378
402
|
import { clearedColumnFilterParams, columnFilterDef, columnFilterParams, columnFilterState, isColumnFilterActive, isColumnSortable } from '../../utils/columnFilters.js'
|
|
403
|
+
import { lensUnavailableText, readLensReports } from '../../utils/lensAvailability.js'
|
|
379
404
|
import { followLinkClick, openRowTarget, resolveHref } from '../../utils/linkNavigation.js'
|
|
380
405
|
import { nextSortState } from '../../utils/multiColumnSort.js'
|
|
381
406
|
import { isNewTabClick, isNewTabHandled, isRowMiddleClick, markNewTabHandled, preventMiddleClickAutoscroll } from '../../utils/rowAuxClick.js'
|
|
@@ -485,7 +510,8 @@ export default {
|
|
|
485
510
|
CnIcon,
|
|
486
511
|
CnLockIndicator,
|
|
487
512
|
CnUnreadMarker,
|
|
488
|
-
|
|
513
|
+
ChevronDown,
|
|
514
|
+
ChevronUp,
|
|
489
515
|
FilterOutline,
|
|
490
516
|
},
|
|
491
517
|
|
|
@@ -666,6 +692,20 @@ export default {
|
|
|
666
692
|
default: () => t('nextcloud-vue', 'No items found'),
|
|
667
693
|
},
|
|
668
694
|
|
|
695
|
+
/**
|
|
696
|
+
* App wording for an unavailable personal lens in self-fetch mode,
|
|
697
|
+
* keyed `<lens>.<reason>` (`recent.audit-trail-disabled`) or
|
|
698
|
+
* `<reason>`. Without it the library's object-neutral text is shown
|
|
699
|
+
* when the response reports `@self.lenses.<lens>.available: false`.
|
|
700
|
+
*
|
|
701
|
+
* @type {{[key: string]: string}|null}
|
|
702
|
+
* @spec openspec/changes/lens-says-why-it-is-empty/specs/personal-lens-availability/spec.md#requirement-an-unavailable-lens-explains-its-empty-page
|
|
703
|
+
*/
|
|
704
|
+
lensReasonTexts: {
|
|
705
|
+
type: Object,
|
|
706
|
+
default: null,
|
|
707
|
+
},
|
|
708
|
+
|
|
669
709
|
/** Function returning CSS class(es) for a row: (row) => string|object */
|
|
670
710
|
rowClass: {
|
|
671
711
|
type: Function,
|
|
@@ -942,6 +982,8 @@ export default {
|
|
|
942
982
|
aggregateRequestId: 0,
|
|
943
983
|
/** Rows fetched in self-fetch mode (register + schemaId). */
|
|
944
984
|
fetchedRows: [],
|
|
985
|
+
/** Personal-lens reports (`@self.lenses`) of the latest self-fetch response. */
|
|
986
|
+
fetchedLenses: {},
|
|
945
987
|
/** True while a self-fetch request is in flight. */
|
|
946
988
|
selfFetchLoading: false,
|
|
947
989
|
}
|
|
@@ -966,6 +1008,22 @@ export default {
|
|
|
966
1008
|
return t('nextcloud-vue', 'Actions')
|
|
967
1009
|
},
|
|
968
1010
|
|
|
1011
|
+
/**
|
|
1012
|
+
* Why the self-fetched list is empty when a personal lens could not
|
|
1013
|
+
* answer (openregister#4514), or `''` to keep `emptyText`. Only the
|
|
1014
|
+
* table's own fetch reports: external `rows` carry no report.
|
|
1015
|
+
*
|
|
1016
|
+
* @return {string}
|
|
1017
|
+
* @spec openspec/changes/lens-says-why-it-is-empty/specs/personal-lens-availability/spec.md#requirement-an-unavailable-lens-explains-its-empty-page
|
|
1018
|
+
*/
|
|
1019
|
+
lensReasonText() {
|
|
1020
|
+
if (this.rows && this.rows.length > 0) {
|
|
1021
|
+
return ''
|
|
1022
|
+
}
|
|
1023
|
+
const text = lensUnavailableText(this.fetchedLenses, this.lensReasonTexts)
|
|
1024
|
+
return text ? this.translateLabel(text) : ''
|
|
1025
|
+
},
|
|
1026
|
+
|
|
969
1027
|
/** @return {number} Leading cells before the data columns (selection, icon). */
|
|
970
1028
|
leadingCount() {
|
|
971
1029
|
return (this.selectable ? 1 : 0) + (this.rowIcon ? 1 : 0)
|
|
@@ -1495,8 +1553,10 @@ export default {
|
|
|
1495
1553
|
...(this.fetchParams ? { params: this.fetchParams } : {}),
|
|
1496
1554
|
})
|
|
1497
1555
|
this.fetchedRows = (data && data.results) || (Array.isArray(data) ? data : [])
|
|
1556
|
+
this.fetchedLenses = readLensReports(data)
|
|
1498
1557
|
} catch {
|
|
1499
1558
|
this.fetchedRows = []
|
|
1559
|
+
this.fetchedLenses = {}
|
|
1500
1560
|
} finally {
|
|
1501
1561
|
this.selfFetchLoading = false
|
|
1502
1562
|
}
|
|
@@ -1586,19 +1646,24 @@ export default {
|
|
|
1586
1646
|
* handed a `:schema` for at all) still get type-aware rendering.
|
|
1587
1647
|
* `enumLabels` rides with `enum` rather than getting its own `if`
|
|
1588
1648
|
* branch: a column can't usefully declare labels without also
|
|
1589
|
-
* declaring the codes they label.
|
|
1649
|
+
* declaring the codes they label. A column `colorMap` (raw value to
|
|
1650
|
+
* badge variant) colours the enum pill from the manifest and wins over
|
|
1651
|
+
* the schema property's own `colorMap` / `x-color-map`.
|
|
1590
1652
|
*
|
|
1591
1653
|
* @param {object} col Column definition.
|
|
1592
1654
|
* @return {object} Property definition for CnCellRenderer.
|
|
1655
|
+
* @spec openspec/changes/screens-cell-pill-parity/specs/cell-pill-tones/spec.md#requirement-a-manifest-column-colours-its-enum-pills
|
|
1593
1656
|
*/
|
|
1594
1657
|
columnProperty(col) {
|
|
1595
1658
|
const base = this.getSchemaProperty(col.key)
|
|
1596
|
-
|
|
1659
|
+
const colorMap = col && col.colorMap && typeof col.colorMap === 'object' && !Array.isArray(col.colorMap) ? col.colorMap : null
|
|
1660
|
+
if (col && (col.format || col.type || col.enum || colorMap)) {
|
|
1597
1661
|
return {
|
|
1598
1662
|
...base,
|
|
1599
1663
|
...(col.type ? { type: col.type } : {}),
|
|
1600
1664
|
...(col.format ? { format: col.format } : {}),
|
|
1601
1665
|
...(col.enum ? { enum: col.enum, enumLabels: col.enumLabels || base.enumLabels } : {}),
|
|
1666
|
+
...(colorMap ? { colorMap } : {}),
|
|
1602
1667
|
}
|
|
1603
1668
|
}
|
|
1604
1669
|
return base
|
|
@@ -1975,8 +2040,8 @@ export default {
|
|
|
1975
2040
|
filterButtonLabel(col) {
|
|
1976
2041
|
const column = this.translateLabel(col.label)
|
|
1977
2042
|
return this.isColumnFiltered(col)
|
|
1978
|
-
? t('nextcloud-vue', 'Filter {column}, active', { column })
|
|
1979
|
-
: t('nextcloud-vue', 'Filter {column}', { column })
|
|
2043
|
+
? t('nextcloud-vue', 'Filter by {column}, active', { column })
|
|
2044
|
+
: t('nextcloud-vue', 'Filter by {column}', { column })
|
|
1980
2045
|
},
|
|
1981
2046
|
|
|
1982
2047
|
/**
|
|
@@ -69,6 +69,44 @@ export const DEFAULT_DATE_RANGE_PRESETS = Object.freeze([
|
|
|
69
69
|
{ id: 'custom', label: 'Custom range', days: null },
|
|
70
70
|
])
|
|
71
71
|
|
|
72
|
+
/**
|
|
73
|
+
* The library's own translations of the default preset labels. Literal
|
|
74
|
+
* `t()` calls, so the strings are extracted into the `nextcloud-vue`
|
|
75
|
+
* catalogue.
|
|
76
|
+
*
|
|
77
|
+
* @type {{[label: string]: function(): string}}
|
|
78
|
+
*/
|
|
79
|
+
const LIBRARY_PRESET_LABELS = Object.freeze({
|
|
80
|
+
'Last 8 hours': () => t('nextcloud-vue', 'Last 8 hours'),
|
|
81
|
+
'Last 24 hours': () => t('nextcloud-vue', 'Last 24 hours'),
|
|
82
|
+
Today: () => t('nextcloud-vue', 'Today'),
|
|
83
|
+
'Last 7 days': () => t('nextcloud-vue', 'Last 7 days'),
|
|
84
|
+
'Last 30 days': () => t('nextcloud-vue', 'Last 30 days'),
|
|
85
|
+
'Last 90 days': () => t('nextcloud-vue', 'Last 90 days'),
|
|
86
|
+
'Custom range': () => t('nextcloud-vue', 'Custom range'),
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* A preset label in the user's language: the host translation when the app's
|
|
91
|
+
* catalogue has the key, else the library's own translation of a default
|
|
92
|
+
* label ("Last 30 days"), else the label as written.
|
|
93
|
+
*
|
|
94
|
+
* @spec openspec/changes/screens-dashboard-i18n/specs/dashboard-page/spec.md#requirement-date-range-preset-labels-are-translated
|
|
95
|
+
* @param {string} label The preset label (an i18n key).
|
|
96
|
+
* @param {function(string): string} [translate] The host translate function.
|
|
97
|
+
* @return {string} The translated label.
|
|
98
|
+
*/
|
|
99
|
+
export function translatePresetLabel(label, translate) {
|
|
100
|
+
if (typeof label !== 'string' || label === '') {
|
|
101
|
+
return ''
|
|
102
|
+
}
|
|
103
|
+
const own = typeof translate === 'function' ? translate(label) : label
|
|
104
|
+
if (typeof own === 'string' && own !== '' && own !== label) {
|
|
105
|
+
return own
|
|
106
|
+
}
|
|
107
|
+
return Object.hasOwn(LIBRARY_PRESET_LABELS, label) ? LIBRARY_PRESET_LABELS[label]() : label
|
|
108
|
+
}
|
|
109
|
+
|
|
72
110
|
/**
|
|
73
111
|
* Resolve a `period` preset into its calendar-aligned `{ from, to }`
|
|
74
112
|
* window — the CURRENT week / month / quarter / year to date, not a
|
|
@@ -196,6 +234,11 @@ export default {
|
|
|
196
234
|
NcSelect,
|
|
197
235
|
},
|
|
198
236
|
|
|
237
|
+
inject: {
|
|
238
|
+
/** Host translate function provided by CnAppRoot, for app-authored preset labels. */
|
|
239
|
+
cnTranslate: { default: () => (key) => key },
|
|
240
|
+
},
|
|
241
|
+
|
|
199
242
|
props: {
|
|
200
243
|
/**
|
|
201
244
|
* Current range value. `from` / `to` are ISO-8601 UTC strings;
|
|
@@ -327,7 +370,7 @@ export default {
|
|
|
327
370
|
},
|
|
328
371
|
|
|
329
372
|
presetOptions() {
|
|
330
|
-
return this.presets.map((p) => ({ id: p.id, label: p.label, days: p.days }))
|
|
373
|
+
return this.presets.map((p) => ({ id: p.id, label: translatePresetLabel(p.label, this.cnTranslate), days: p.days }))
|
|
331
374
|
},
|
|
332
375
|
|
|
333
376
|
selectedPresetOption() {
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
data-testid="cn-detail-page-type-eyebrow">
|
|
117
117
|
{{ typeEyebrow }}
|
|
118
118
|
</p>
|
|
119
|
-
<div v-if="displayTitle ||
|
|
119
|
+
<div v-if="displayTitle || showFollowToggle" class="cn-detail-page__title-row">
|
|
120
120
|
<component
|
|
121
121
|
:is="isBoard ? 'h1' : 'h2'"
|
|
122
122
|
v-if="displayTitle"
|
|
@@ -124,24 +124,19 @@
|
|
|
124
124
|
:title="displayTitle">
|
|
125
125
|
{{ displayTitle }}
|
|
126
126
|
</component>
|
|
127
|
-
<!--
|
|
128
|
-
|
|
127
|
+
<!-- The one Follow control beside the title (manifest `config.follow`):
|
|
128
|
+
shown when the object carries `@self.watching`. There is no star:
|
|
129
|
+
a favourite is a follow with notifications off. -->
|
|
129
130
|
<span
|
|
130
|
-
v-if="
|
|
131
|
+
v-if="showFollowToggle"
|
|
131
132
|
class="cn-detail-page__interactions"
|
|
132
133
|
data-testid="cn-detail-page-interactions">
|
|
133
|
-
<CnFavouriteToggle
|
|
134
|
-
v-if="showFavouriteToggle"
|
|
135
|
-
:register="register"
|
|
136
|
-
:schema="schema"
|
|
137
|
-
:objectId="String(objectId)"
|
|
138
|
-
:favourite="resolvedSelf.favourite === true" />
|
|
139
134
|
<CnFollowToggle
|
|
140
|
-
v-if="showFollowToggle"
|
|
141
135
|
:register="register"
|
|
142
136
|
:schema="schema"
|
|
143
137
|
:objectId="String(objectId)"
|
|
144
138
|
:watching="resolvedSelf.watching === true"
|
|
139
|
+
:notify="typeof resolvedSelf.watchNotify === 'boolean' ? resolvedSelf.watchNotify : null"
|
|
145
140
|
:watcherCount="typeof resolvedSelf.watcherCount === 'number' ? resolvedSelf.watcherCount : null"
|
|
146
141
|
:canManage="!!(resolvedSelf.can && resolvedSelf.can.manage === true)"
|
|
147
142
|
:notifies="followNotifies" />
|
|
@@ -1211,7 +1206,6 @@ import CnBodySections from '../CnBodySections/CnBodySections.vue'
|
|
|
1211
1206
|
import CnBuildiqEditButton from '../CnBuildiqEditButton/CnBuildiqEditButton.vue'
|
|
1212
1207
|
import CnDashboardGrid from '../CnDashboardGrid/CnDashboardGrid.vue'
|
|
1213
1208
|
import CnDetailWidgetHost from '../CnDetailWidgetHost/CnDetailWidgetHost.vue'
|
|
1214
|
-
import CnFavouriteToggle from '../CnFavouriteToggle/CnFavouriteToggle.vue'
|
|
1215
1209
|
import CnFollowToggle from '../CnFollowToggle/CnFollowToggle.vue'
|
|
1216
1210
|
import CnFormDialog from '../CnFormDialog/CnFormDialog.vue'
|
|
1217
1211
|
import CnLifecycleActions from '../CnLifecycleActions/CnLifecycleActions.vue'
|
|
@@ -1413,7 +1407,6 @@ export default {
|
|
|
1413
1407
|
CnStatusBadge,
|
|
1414
1408
|
CnSummaryAggregates,
|
|
1415
1409
|
CnDetailHeaderChips,
|
|
1416
|
-
CnFavouriteToggle,
|
|
1417
1410
|
CnFollowToggle,
|
|
1418
1411
|
EyeOffOutline,
|
|
1419
1412
|
CnRelatedCollections,
|
|
@@ -1884,9 +1877,11 @@ export default {
|
|
|
1884
1877
|
},
|
|
1885
1878
|
|
|
1886
1879
|
/**
|
|
1887
|
-
*
|
|
1888
|
-
*
|
|
1880
|
+
* Deprecated and ignored: the star is gone, a favourite is a follow with
|
|
1881
|
+
* notifications off (OpenRegister `merge-follow-and-favourites`). Kept so a
|
|
1882
|
+
* manifest that still sets `config.favourite` stays valid; use `follow`.
|
|
1889
1883
|
*
|
|
1884
|
+
* @deprecated Use `follow`.
|
|
1890
1885
|
* @type {boolean|null}
|
|
1891
1886
|
*/
|
|
1892
1887
|
favourite: {
|
|
@@ -3867,18 +3862,6 @@ export default {
|
|
|
3867
3862
|
&& this.register !== '' && this.schema !== '' && this.objectId !== ''
|
|
3868
3863
|
},
|
|
3869
3864
|
|
|
3870
|
-
/**
|
|
3871
|
-
* Whether the star renders: the object carries `@self.favourite` and the page did not turn it off.
|
|
3872
|
-
*
|
|
3873
|
-
* @return {boolean}
|
|
3874
|
-
* @spec openspec/changes/record-favourite-and-follow/tasks.md#task-3
|
|
3875
|
-
*/
|
|
3876
|
-
showFavouriteToggle() {
|
|
3877
|
-
return this.favourite !== false
|
|
3878
|
-
&& typeof this.resolvedSelf.favourite === 'boolean'
|
|
3879
|
-
&& this.register !== '' && this.schema !== '' && this.objectId !== ''
|
|
3880
|
-
},
|
|
3881
|
-
|
|
3882
3865
|
/**
|
|
3883
3866
|
* Whether the Follow toggle renders: the object carries `@self.watching` and the page did not turn it off.
|
|
3884
3867
|
*
|
|
@@ -31,12 +31,17 @@ import { setFavourite } from '../../utils/recordInteractions.js'
|
|
|
31
31
|
/**
|
|
32
32
|
* CnFavouriteToggle — a star bound to a record's `@self.favourite` marker.
|
|
33
33
|
*
|
|
34
|
+
* @deprecated Following and favourites are one feature since OpenRegister's
|
|
35
|
+
* `merge-follow-and-favourites`: a favourite is a follow with notifications
|
|
36
|
+
* off. Use `CnFollowToggle`. Neither `CnDetailPage` nor `CnIndexPage` renders
|
|
37
|
+
* this star any more; it stays exported for one minor line so an app that
|
|
38
|
+
* placed it itself keeps working (the server answers it as a quiet follow).
|
|
39
|
+
*
|
|
34
40
|
* A click flips the star at once and sends `PUT` (star) or `DELETE` (unstar)
|
|
35
41
|
* `/apps/openregister/api/objects/{register}/{schema}/{id}/favourite`; on
|
|
36
42
|
* failure it flips back and shows the server's message. The server's answer is
|
|
37
43
|
* written into the stored object's `@self`, so a list showing the same record
|
|
38
|
-
* agrees.
|
|
39
|
-
* per row for `showFavouriteColumn`.
|
|
44
|
+
* agrees.
|
|
40
45
|
*
|
|
41
46
|
* Example:
|
|
42
47
|
* ```vue
|