@conduction/nextcloud-vue 2.98.0 → 2.99.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 (84) hide show
  1. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js +12 -0
  2. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js.map +1 -1
  3. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js +11 -2
  4. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js.map +1 -1
  5. package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue.js +1 -1
  6. package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue2.js.map +1 -1
  7. package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue3.js +1 -1
  8. package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue3.js.map +1 -1
  9. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  10. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +2 -1
  11. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  12. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue.js +1 -1
  13. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js +26 -7
  14. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js.map +1 -1
  15. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js +78 -17
  16. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js.map +1 -1
  17. package/dist/esm/components/CnSettingsField/CnSettingsField.vue.js +9 -0
  18. package/dist/esm/components/CnSettingsField/CnSettingsField.vue.js.map +1 -0
  19. package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js +265 -0
  20. package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js.map +1 -0
  21. package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js +191 -0
  22. package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js.map +1 -0
  23. package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue.js +9 -0
  24. package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue.js.map +1 -0
  25. package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue2.js +508 -0
  26. package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue2.js.map +1 -0
  27. package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue3.js +99 -0
  28. package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue3.js.map +1 -0
  29. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue.js +1 -1
  30. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js +18 -70
  31. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js.map +1 -1
  32. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue3.js +6 -44
  33. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue3.js.map +1 -1
  34. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  35. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  36. package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue.js +9 -0
  37. package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue.js.map +1 -0
  38. package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue2.js +472 -0
  39. package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue2.js.map +1 -0
  40. package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue3.js +231 -0
  41. package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue3.js.map +1 -0
  42. package/dist/esm/components/CnWorkingCalendarWidget/index.js +27 -0
  43. package/dist/esm/components/CnWorkingCalendarWidget/index.js.map +1 -0
  44. package/dist/esm/composables/useFlowStore.js +2 -1
  45. package/dist/esm/composables/useFlowStore.js.map +1 -1
  46. package/dist/esm/index.js +3 -0
  47. package/dist/esm/index.js.map +1 -1
  48. package/dist/esm/l10n/en.json.js +95 -1
  49. package/dist/esm/l10n/en.json.js.map +1 -1
  50. package/dist/esm/l10n/nl.json.js +109 -1
  51. package/dist/esm/l10n/nl.json.js.map +1 -1
  52. package/dist/esm/library-style.js +1 -1
  53. package/dist/esm/nextcloud-vue.css +660 -81
  54. package/dist/esm/utils/libraryWidgetKeys.js +1 -0
  55. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  56. package/dist/esm/utils/workingCalendarMonth.js +85 -0
  57. package/dist/esm/utils/workingCalendarMonth.js.map +1 -0
  58. package/dist/nextcloud-vue.cjs.js +9355 -7303
  59. package/dist/nextcloud-vue.cjs.js.map +2 -2
  60. package/dist/nextcloud-vue.css +660 -81
  61. package/l10n/en.json +95 -1
  62. package/l10n/nl.json +109 -1
  63. package/package.json +1 -1
  64. package/src/components/CnActionsMenu/CnActionsMenu.vue +14 -1
  65. package/src/components/CnBreadcrumbs/CnBreadcrumbs.vue +1 -0
  66. package/src/components/CnDetailPage/CnDetailPage.vue +1 -0
  67. package/src/components/CnOrderedListField/CnOrderedListField.vue +81 -9
  68. package/src/components/CnSettingsField/CnSettingsField.vue +449 -0
  69. package/src/components/CnSettingsField/index.js +4 -0
  70. package/src/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue +613 -0
  71. package/src/components/CnSettingsFieldsSection/index.js +4 -0
  72. package/src/components/CnSettingsPage/CnSettingsPage.vue +6 -86
  73. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  74. package/src/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue +725 -0
  75. package/src/components/CnWorkingCalendarWidget/index.js +26 -0
  76. package/src/components/index.js +3 -0
  77. package/src/composables/useFlowStore.js +2 -1
  78. package/src/css/badge.css +32 -16
  79. package/src/css/look-board-detail.css +80 -8
  80. package/src/css/look-board-index.css +52 -8
  81. package/src/css/look-board.css +219 -1
  82. package/src/index.js +3 -0
  83. package/src/utils/libraryWidgetKeys.js +1 -0
  84. package/src/utils/workingCalendarMonth.js +82 -0
@@ -0,0 +1,231 @@
1
+ import { resolveComponent, openBlock, createElementBlock, toDisplayString, createVNode, Fragment, createElementVNode, withCtx, createBlock, createTextVNode, createCommentVNode, renderList, normalizeClass, withModifiers, withDirectives, vModelText } from 'vue';
2
+
3
+ const _hoisted_1 = {
4
+ class: "cn-working-calendar",
5
+ "data-testid": "cn-working-calendar"
6
+ };
7
+ const _hoisted_2 = {
8
+ key: 0,
9
+ class: "cn-working-calendar__error",
10
+ role: "alert",
11
+ "data-testid": "cn-working-calendar-error"
12
+ };
13
+ const _hoisted_3 = {
14
+ key: 1,
15
+ class: "cn-working-calendar__loading"
16
+ };
17
+ const _hoisted_4 = { class: "cn-working-calendar__head" };
18
+ const _hoisted_5 = ["id"];
19
+ const _hoisted_6 = {
20
+ key: 0,
21
+ class: "cn-working-calendar__note",
22
+ "data-testid": "cn-working-calendar-note"
23
+ };
24
+ const _hoisted_7 = {
25
+ class: "cn-working-calendar__live",
26
+ "aria-live": "polite"
27
+ };
28
+ const _hoisted_8 = ["aria-labelledby"];
29
+ const _hoisted_9 = {
30
+ role: "row",
31
+ class: "cn-working-calendar__row"
32
+ };
33
+ const _hoisted_10 = ["aria-label"];
34
+ const _hoisted_11 = ["tabindex", "aria-label", "aria-current", "title", "data-date", "onClick", "onFocus"];
35
+ const _hoisted_12 = { class: "cn-working-calendar__field" };
36
+ const _hoisted_13 = { class: "cn-working-calendar__field" };
37
+ const _hoisted_14 = { class: "cn-working-calendar__form-actions" };
38
+ const _hoisted_15 = {
39
+ key: 0,
40
+ class: "cn-working-calendar__error",
41
+ role: "alert",
42
+ "data-testid": "cn-working-calendar-save-error"
43
+ };
44
+ const _hoisted_16 = {
45
+ key: 3,
46
+ class: "cn-working-calendar__note",
47
+ "data-testid": "cn-working-calendar-empty"
48
+ };
49
+
50
+ function render(_ctx, _cache, $props, $setup, $data, $options) {
51
+ const _component_NcLoadingIcon = resolveComponent("NcLoadingIcon");
52
+ const _component_ChevronLeft = resolveComponent("ChevronLeft");
53
+ const _component_NcButton = resolveComponent("NcButton");
54
+ const _component_ChevronRight = resolveComponent("ChevronRight");
55
+
56
+ return (openBlock(), createElementBlock("div", _hoisted_1, [
57
+ ($data.error)
58
+ ? (openBlock(), createElementBlock("p", _hoisted_2, toDisplayString($data.error), 1 /* TEXT */))
59
+ : ($data.loading && !$data.calendar)
60
+ ? (openBlock(), createElementBlock("div", _hoisted_3, [
61
+ createVNode(_component_NcLoadingIcon, {
62
+ size: 20,
63
+ name: $options.loadingLabel
64
+ }, null, 8 /* PROPS */, ["name"])
65
+ ]))
66
+ : ($data.calendar)
67
+ ? (openBlock(), createElementBlock(Fragment, { key: 2 }, [
68
+ createElementVNode("div", _hoisted_4, [
69
+ createVNode(_component_NcButton, {
70
+ variant: "tertiary",
71
+ "aria-label": $options.previousMonthLabel,
72
+ "data-testid": "cn-working-calendar-previous",
73
+ onClick: _cache[0] || (_cache[0] = $event => ($options.shiftMonth(-1)))
74
+ }, {
75
+ icon: withCtx(() => [
76
+ createVNode(_component_ChevronLeft, { size: 20 })
77
+ ]),
78
+ _: 1 /* STABLE */
79
+ }, 8 /* PROPS */, ["aria-label"]),
80
+ createElementVNode("strong", {
81
+ id: `${$data.uid}-title`,
82
+ class: "cn-working-calendar__title",
83
+ "data-testid": "cn-working-calendar-title"
84
+ }, toDisplayString($options.monthTitle), 9 /* TEXT, PROPS */, _hoisted_5),
85
+ createVNode(_component_NcButton, {
86
+ variant: "tertiary",
87
+ "aria-label": $options.nextMonthLabel,
88
+ "data-testid": "cn-working-calendar-next",
89
+ onClick: _cache[1] || (_cache[1] = $event => ($options.shiftMonth(1)))
90
+ }, {
91
+ icon: withCtx(() => [
92
+ createVNode(_component_ChevronRight, { size: 20 })
93
+ ]),
94
+ _: 1 /* STABLE */
95
+ }, 8 /* PROPS */, ["aria-label"]),
96
+ ($options.editable)
97
+ ? (openBlock(), createBlock(_component_NcButton, {
98
+ key: 0,
99
+ class: "cn-working-calendar__add",
100
+ variant: "secondary",
101
+ "data-testid": "cn-working-calendar-add",
102
+ onClick: _cache[2] || (_cache[2] = $event => ($options.openAdd()))
103
+ }, {
104
+ default: withCtx(() => [
105
+ createTextVNode(toDisplayString($options.addLabel), 1 /* TEXT */)
106
+ ]),
107
+ _: 1 /* STABLE */
108
+ }))
109
+ : createCommentVNode("v-if", true)
110
+ ]),
111
+ ($options.previewNote)
112
+ ? (openBlock(), createElementBlock("p", _hoisted_6, toDisplayString($options.previewNote), 1 /* TEXT */))
113
+ : createCommentVNode("v-if", true),
114
+ createElementVNode("span", _hoisted_7, toDisplayString($data.liveText), 1 /* TEXT */),
115
+ createElementVNode("div", {
116
+ role: "grid",
117
+ class: "cn-working-calendar__grid",
118
+ "aria-labelledby": `${$data.uid}-title`,
119
+ "data-testid": "cn-working-calendar-grid",
120
+ onKeydown: _cache[3] || (_cache[3] = (...args) => ($options.onKeydown && $options.onKeydown(...args)))
121
+ }, [
122
+ createElementVNode("div", _hoisted_9, [
123
+ (openBlock(true), createElementBlock(Fragment, null, renderList($options.weekdayNames, (name) => {
124
+ return (openBlock(), createElementBlock("span", {
125
+ key: name.long,
126
+ role: "columnheader",
127
+ class: "cn-working-calendar__weekday",
128
+ "aria-label": name.long
129
+ }, toDisplayString(name.short), 9 /* TEXT, PROPS */, _hoisted_10))
130
+ }), 128 /* KEYED_FRAGMENT */))
131
+ ]),
132
+ (openBlock(true), createElementBlock(Fragment, null, renderList($options.weeks, (week, w) => {
133
+ return (openBlock(), createElementBlock("div", {
134
+ key: w,
135
+ role: "row",
136
+ class: "cn-working-calendar__row"
137
+ }, [
138
+ (openBlock(true), createElementBlock(Fragment, null, renderList(week, (slot, d) => {
139
+ return (openBlock(), createElementBlock(Fragment, null, [
140
+ (!slot)
141
+ ? (openBlock(), createElementBlock("span", {
142
+ key: `empty-${w}-${d}`,
143
+ role: "gridcell",
144
+ class: "cn-working-calendar__blank"
145
+ }))
146
+ : (openBlock(), createElementBlock("span", {
147
+ key: slot.date,
148
+ role: "gridcell",
149
+ class: normalizeClass(["cn-working-calendar__day", {
150
+ 'cn-working-calendar__day--off': !slot.working && !slot.closure,
151
+ 'cn-working-calendar__day--closed': slot.closure,
152
+ 'cn-working-calendar__day--today': slot.today,
153
+ }]),
154
+ tabindex: slot.date === $data.focusDate ? 0 : -1,
155
+ "aria-label": $options.dayLabel(slot),
156
+ "aria-current": slot.today ? 'date' : null,
157
+ title: slot.closure ? $options.closureName(slot.closure) : null,
158
+ "data-date": slot.date,
159
+ "data-testid": "cn-working-calendar-day",
160
+ onClick: $event => ($options.onDayClick(slot)),
161
+ onFocus: $event => ($data.focusDate = slot.date)
162
+ }, toDisplayString(slot.day), 43 /* TEXT, CLASS, PROPS, NEED_HYDRATION */, _hoisted_11))
163
+ ], 64 /* STABLE_FRAGMENT */))
164
+ }), 256 /* UNKEYED_FRAGMENT */))
165
+ ]))
166
+ }), 128 /* KEYED_FRAGMENT */))
167
+ ], 40 /* PROPS, NEED_HYDRATION */, _hoisted_8),
168
+ ($data.adding)
169
+ ? (openBlock(), createElementBlock("form", {
170
+ key: 1,
171
+ class: "cn-working-calendar__form",
172
+ "data-testid": "cn-working-calendar-form",
173
+ onSubmit: _cache[7] || (_cache[7] = withModifiers((...args) => ($options.saveDay && $options.saveDay(...args)), ["prevent"]))
174
+ }, [
175
+ createElementVNode("label", _hoisted_12, [
176
+ createTextVNode(toDisplayString($options.dateLabel) + " ", 1 /* TEXT */),
177
+ withDirectives(createElementVNode("input", {
178
+ ref: "dateInput",
179
+ "onUpdate:modelValue": _cache[4] || (_cache[4] = $event => (($data.newDate) = $event)),
180
+ type: "date",
181
+ required: "",
182
+ "data-testid": "cn-working-calendar-date"
183
+ }, null, 512 /* NEED_PATCH */), [
184
+ [vModelText, $data.newDate]
185
+ ])
186
+ ]),
187
+ createElementVNode("label", _hoisted_13, [
188
+ createTextVNode(toDisplayString($options.nameLabel) + " ", 1 /* TEXT */),
189
+ withDirectives(createElementVNode("input", {
190
+ "onUpdate:modelValue": _cache[5] || (_cache[5] = $event => (($data.newName) = $event)),
191
+ type: "text",
192
+ "data-testid": "cn-working-calendar-name"
193
+ }, null, 512 /* NEED_PATCH */), [
194
+ [vModelText, $data.newName]
195
+ ])
196
+ ]),
197
+ createElementVNode("div", _hoisted_14, [
198
+ createVNode(_component_NcButton, {
199
+ variant: "tertiary",
200
+ "data-testid": "cn-working-calendar-cancel",
201
+ onClick: _cache[6] || (_cache[6] = $event => ($data.adding = false))
202
+ }, {
203
+ default: withCtx(() => [
204
+ createTextVNode(toDisplayString($options.cancelLabel), 1 /* TEXT */)
205
+ ]),
206
+ _: 1 /* STABLE */
207
+ }),
208
+ createVNode(_component_NcButton, {
209
+ variant: "primary",
210
+ type: "submit",
211
+ disabled: $data.saving || !$data.newDate,
212
+ "data-testid": "cn-working-calendar-save"
213
+ }, {
214
+ default: withCtx(() => [
215
+ createTextVNode(toDisplayString($options.saveLabel), 1 /* TEXT */)
216
+ ]),
217
+ _: 1 /* STABLE */
218
+ }, 8 /* PROPS */, ["disabled"])
219
+ ]),
220
+ ($data.saveError)
221
+ ? (openBlock(), createElementBlock("p", _hoisted_15, toDisplayString($data.saveError), 1 /* TEXT */))
222
+ : createCommentVNode("v-if", true)
223
+ ], 32 /* NEED_HYDRATION */))
224
+ : createCommentVNode("v-if", true)
225
+ ], 64 /* STABLE_FRAGMENT */))
226
+ : (openBlock(), createElementBlock("p", _hoisted_16, toDisplayString($options.emptyLabel), 1 /* TEXT */))
227
+ ]))
228
+ }
229
+
230
+ export { render };
231
+ //# sourceMappingURL=CnWorkingCalendarWidget.vue3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnWorkingCalendarWidget.vue3.js","sources":["../../../../src/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue?vue&type=template&id=03ee459c&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-working-calendar\" data-testid=\"cn-working-calendar\">\n\t\t<p v-if=\"error\"\n\t\t\tclass=\"cn-working-calendar__error\"\n\t\t\trole=\"alert\"\n\t\t\tdata-testid=\"cn-working-calendar-error\">\n\t\t\t{{ error }}\n\t\t</p>\n\t\t<div v-else-if=\"loading && !calendar\" class=\"cn-working-calendar__loading\">\n\t\t\t<NcLoadingIcon :size=\"20\" :name=\"loadingLabel\" />\n\t\t</div>\n\t\t<template v-else-if=\"calendar\">\n\t\t\t<div class=\"cn-working-calendar__head\">\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t:aria-label=\"previousMonthLabel\"\n\t\t\t\t\tdata-testid=\"cn-working-calendar-previous\"\n\t\t\t\t\t@click=\"shiftMonth(-1)\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<ChevronLeft :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t</NcButton>\n\t\t\t\t<strong :id=\"`${uid}-title`\" class=\"cn-working-calendar__title\" data-testid=\"cn-working-calendar-title\">{{ monthTitle }}</strong>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t:aria-label=\"nextMonthLabel\"\n\t\t\t\t\tdata-testid=\"cn-working-calendar-next\"\n\t\t\t\t\t@click=\"shiftMonth(1)\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<ChevronRight :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t</NcButton>\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"editable\"\n\t\t\t\t\tclass=\"cn-working-calendar__add\"\n\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\tdata-testid=\"cn-working-calendar-add\"\n\t\t\t\t\t@click=\"openAdd()\">\n\t\t\t\t\t{{ addLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t\t<p v-if=\"previewNote\" class=\"cn-working-calendar__note\" data-testid=\"cn-working-calendar-note\">\n\t\t\t\t{{ previewNote }}\n\t\t\t</p>\n\t\t\t<span class=\"cn-working-calendar__live\" aria-live=\"polite\">{{ liveText }}</span>\n\n\t\t\t<div\n\t\t\t\trole=\"grid\"\n\t\t\t\tclass=\"cn-working-calendar__grid\"\n\t\t\t\t:aria-labelledby=\"`${uid}-title`\"\n\t\t\t\tdata-testid=\"cn-working-calendar-grid\"\n\t\t\t\t@keydown=\"onKeydown\">\n\t\t\t\t<div role=\"row\" class=\"cn-working-calendar__row\">\n\t\t\t\t\t<span\n\t\t\t\t\t\tv-for=\"name in weekdayNames\"\n\t\t\t\t\t\t:key=\"name.long\"\n\t\t\t\t\t\trole=\"columnheader\"\n\t\t\t\t\t\tclass=\"cn-working-calendar__weekday\"\n\t\t\t\t\t\t:aria-label=\"name.long\">{{ name.short }}</span>\n\t\t\t\t</div>\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"(week, w) in weeks\"\n\t\t\t\t\t:key=\"w\"\n\t\t\t\t\trole=\"row\"\n\t\t\t\t\tclass=\"cn-working-calendar__row\">\n\t\t\t\t\t<template v-for=\"(slot, d) in week\">\n\t\t\t\t\t\t<span v-if=\"!slot\"\n\t\t\t\t\t\t\t:key=\"`empty-${w}-${d}`\"\n\t\t\t\t\t\t\trole=\"gridcell\"\n\t\t\t\t\t\t\tclass=\"cn-working-calendar__blank\" />\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tv-else\n\t\t\t\t\t\t\t:key=\"slot.date\"\n\t\t\t\t\t\t\trole=\"gridcell\"\n\t\t\t\t\t\t\tclass=\"cn-working-calendar__day\"\n\t\t\t\t\t\t\t:class=\"{\n\t\t\t\t\t\t\t\t'cn-working-calendar__day--off': !slot.working && !slot.closure,\n\t\t\t\t\t\t\t\t'cn-working-calendar__day--closed': slot.closure,\n\t\t\t\t\t\t\t\t'cn-working-calendar__day--today': slot.today,\n\t\t\t\t\t\t\t}\"\n\t\t\t\t\t\t\t:tabindex=\"slot.date === focusDate ? 0 : -1\"\n\t\t\t\t\t\t\t:aria-label=\"dayLabel(slot)\"\n\t\t\t\t\t\t\t:aria-current=\"slot.today ? 'date' : null\"\n\t\t\t\t\t\t\t:title=\"slot.closure ? closureName(slot.closure) : null\"\n\t\t\t\t\t\t\t:data-date=\"slot.date\"\n\t\t\t\t\t\t\tdata-testid=\"cn-working-calendar-day\"\n\t\t\t\t\t\t\t@click=\"onDayClick(slot)\"\n\t\t\t\t\t\t\t@focus=\"focusDate = slot.date\">{{ slot.day }}</span>\n\t\t\t\t\t</template>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<form\n\t\t\t\tv-if=\"adding\"\n\t\t\t\tclass=\"cn-working-calendar__form\"\n\t\t\t\tdata-testid=\"cn-working-calendar-form\"\n\t\t\t\t@submit.prevent=\"saveDay\">\n\t\t\t\t<label class=\"cn-working-calendar__field\">\n\t\t\t\t\t{{ dateLabel }}\n\t\t\t\t\t<input\n\t\t\t\t\t\tref=\"dateInput\"\n\t\t\t\t\t\tv-model=\"newDate\"\n\t\t\t\t\t\ttype=\"date\"\n\t\t\t\t\t\trequired\n\t\t\t\t\t\tdata-testid=\"cn-working-calendar-date\">\n\t\t\t\t</label>\n\t\t\t\t<label class=\"cn-working-calendar__field\">\n\t\t\t\t\t{{ nameLabel }}\n\t\t\t\t\t<input\n\t\t\t\t\t\tv-model=\"newName\"\n\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\tdata-testid=\"cn-working-calendar-name\">\n\t\t\t\t</label>\n\t\t\t\t<div class=\"cn-working-calendar__form-actions\">\n\t\t\t\t\t<NcButton variant=\"tertiary\" data-testid=\"cn-working-calendar-cancel\" @click=\"adding = false\">\n\t\t\t\t\t\t{{ cancelLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t:disabled=\"saving || !newDate\"\n\t\t\t\t\t\tdata-testid=\"cn-working-calendar-save\">\n\t\t\t\t\t\t{{ saveLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t\t<p v-if=\"saveError\"\n\t\t\t\t\tclass=\"cn-working-calendar__error\"\n\t\t\t\t\trole=\"alert\"\n\t\t\t\t\tdata-testid=\"cn-working-calendar-save-error\">\n\t\t\t\t\t{{ saveError }}\n\t\t\t\t</p>\n\t\t\t</form>\n\t\t</template>\n\t\t<p v-else class=\"cn-working-calendar__note\" data-testid=\"cn-working-calendar-empty\">\n\t\t\t{{ emptyLabel }}\n\t\t</p>\n\t</div>\n</template>\n\n<script>\nimport { getCurrentUser } from '@nextcloud/auth'\nimport axios from '@nextcloud/axios'\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcLoadingIcon } from '@nextcloud/vue'\nimport ChevronLeft from 'vue-material-design-icons/ChevronLeft.vue'\nimport ChevronRight from 'vue-material-design-icons/ChevronRight.vue'\nimport { fetchReferenceObjects } from '../../utils/boardReferenceColumns.js'\nimport { hasUnresolvedTokens, resolveFilterTokens } from '../../utils/resolveFilterTokens.js'\nimport { userLocale } from '../../utils/userLocale.js'\nimport { buildMonth, isoDate, normaliseExceptions } from '../../utils/workingCalendarMonth.js'\n\nconst OBJECT_API = '/apps/openregister/api/objects'\nconst PREVIEW_URL = '/apps/openregister/api/flow-timers/calendars/preview'\n\nlet instanceCounter = 0\n\n/**\n * The id of an object, wherever OpenRegister carries it.\n *\n * @param {object|null} object The object.\n * @return {string} The id, or ''.\n */\nfunction idOf(object) {\n\treturn String(object?.id ?? object?.['@self']?.id ?? object?.uuid ?? '')\n}\n\n/**\n * Today as `YYYY-MM-DD`, in local time.\n *\n * @return {string} The date.\n */\nfunction todayIso() {\n\tconst now = new Date()\n\treturn isoDate(now.getFullYear(), now.getMonth(), now.getDate())\n}\n\n/**\n * CnWorkingCalendarWidget — the `working-calendar` widget: one month of a\n * working calendar kept as an OpenRegister object (board\n * DqInstellingenAlgemeen, \"Werkdagen en feestdagen\").\n *\n * The grid runs Monday to Sunday. Weekdays the calendar does not work are\n * grey; the dates it is closed on are red, with their name on hover and in\n * the day's label. The closures are not computed here: the widget asks\n * OpenRegister's calendar preview (`/api/flow-timers/calendars/preview`) for\n * the year shown, which expands the calendar's rules and its exceptions.\n *\n * \"Add day\" appends `{ date, name }` to the object's `exceptions` with one\n * update through the object API, under the reader's own rights; it shows for\n * administrators, and the server decides. The preview is administrators-only\n * too; for anyone else the widget shows the stored exceptions and says the\n * holidays could not be read.\n *\n * ```json\n * {\n * \"type\": \"working-calendar\",\n * \"content\": {\n * \"calendar\": { \"register\": \"flow-timers\", \"schema\": \"working-calendar\", \"filter\": { \"slug\": \"default\" } }\n * }\n * }\n * ```\n *\n * Keyboard: the days form a grid with one tab stop; the arrow keys move a\n * day or a week, Home and End to the start and end of the week, Page Up and\n * Page Down a month. Enter on a day opens Add day for that date.\n */\nexport default {\n\tname: 'CnWorkingCalendarWidget',\n\n\tcomponents: { ChevronLeft, ChevronRight, NcButton, NcLoadingIcon },\n\n\tinject: {\n\t\t/** The page's workspace values, for `@workspace.<key>` tokens in the filter. */\n\t\tcnWorkspaceContext: { default: null },\n\t\t/** The host's translate, for the manifest's texts. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget config: `{ calendar: { register, schema, id? | filter? },\n\t\t * previewUrl?, editable?, emptyText? }`.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The bound record's id, for `@objectId` in the filter. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The bound record, for `@object.<field>` in the filter. */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\tdata() {\n\t\tinstanceCounter += 1\n\t\tconst now = new Date()\n\t\treturn {\n\t\t\tuid: `cn-working-calendar-${instanceCounter}`,\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\tcalendar: null,\n\t\t\tpreviews: {},\n\t\t\tpreviewFailed: false,\n\t\t\tyear: now.getFullYear(),\n\t\t\tmonth: now.getMonth(),\n\t\t\tfocusDate: todayIso(),\n\t\t\tadding: false,\n\t\t\tnewDate: '',\n\t\t\tnewName: '',\n\t\t\tsaving: false,\n\t\t\tsaveError: '',\n\t\t\tliveText: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {{register: string, schema: string, id: string, filter: (object|null)}|null} The calendar source, tokens resolved, or null while unresolved. */\n\t\tsource() {\n\t\t\tconst c = this.content?.calendar\n\t\t\tif (!c || !c.register || !c.schema) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst unwrap = (v) => (v && typeof v === 'object' && 'value' in v ? v.value : v)\n\t\t\tconst ctx = { objectId: this.objectId || null, object: this.objectData, workspace: unwrap(this.cnWorkspaceContext) || {} }\n\t\t\tconst filter = c.filter && typeof c.filter === 'object' ? resolveFilterTokens(c.filter, ctx) : null\n\t\t\tif (filter && hasUnresolvedTokens(filter)) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn { register: String(c.register), schema: String(c.schema), id: c.id ? String(c.id) : '', filter }\n\t\t},\n\n\t\t/** @return {string} A key of the source, to read it again when it changes. */\n\t\tsourceKey() {\n\t\t\treturn this.source ? JSON.stringify(this.source) : ''\n\t\t},\n\n\t\t/** @return {boolean} Whether Add day shows: administrators, unless the manifest turns it off. */\n\t\teditable() {\n\t\t\treturn this.content?.editable !== false && Boolean(getCurrentUser()?.isAdmin)\n\t\t},\n\n\t\t/** @return {number[]} The ISO weekdays that work. */\n\t\tworkingWeekdays() {\n\t\t\tconst days = this.calendar?.workingWeekdays\n\t\t\treturn Array.isArray(days) ? days.map((d) => Number(d)) : [1, 2, 3, 4, 5]\n\t\t},\n\n\t\t/** @return {Array<{date: string, name: string}>} The stored exceptions. */\n\t\texceptions() {\n\t\t\treturn normaliseExceptions(this.calendar?.exceptions)\n\t\t},\n\n\t\t/**\n\t\t * The closed dates of the year shown: the preview's, each marked as an\n\t\t * exception when the calendar lists it, else as a holiday; without a\n\t\t * preview, the stored exceptions.\n\t\t *\n\t\t * @return {Map<string, {name: string, kind: string}>}\n\t\t * @spec openspec/changes/working-calendar-widget/specs/working-calendar-widget/spec.md#requirement-a-working-calendar-shows-a-month\n\t\t */\n\t\tclosures() {\n\t\t\tconst own = new Set(this.exceptions.map((e) => e.date))\n\t\t\tconst map = new Map()\n\t\t\tconst preview = this.previews[this.year]\n\t\t\tfor (const entry of Array.isArray(preview) ? preview : []) {\n\t\t\t\tif (entry && typeof entry.date === 'string') {\n\t\t\t\t\tmap.set(entry.date, { name: String(entry.name || ''), kind: own.has(entry.date) ? 'exception' : 'holiday' })\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const e of this.exceptions) {\n\t\t\t\tif (!map.has(e.date)) {\n\t\t\t\t\tmap.set(e.date, { name: e.name, kind: 'exception' })\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn map\n\t\t},\n\n\t\t/** @return {Array<Array<object|null>>} The weeks of the month shown. */\n\t\tweeks() {\n\t\t\treturn buildMonth(this.year, this.month, { workingWeekdays: this.workingWeekdays, closures: this.closures, today: todayIso() })\n\t\t},\n\n\t\t/** @return {Array<{short: string, long: string}>} Monday to Sunday in the user's language. */\n\t\tweekdayNames() {\n\t\t\tconst short = new Intl.DateTimeFormat(userLocale(), { weekday: 'short' })\n\t\t\tconst long = new Intl.DateTimeFormat(userLocale(), { weekday: 'long' })\n\t\t\treturn Array.from({ length: 7 }, (_v, i) => {\n\t\t\t\tconst day = new Date(Date.UTC(2024, 0, 1 + i, 12))\n\t\t\t\treturn { short: short.format(day).replace(/\\.$/, ''), long: long.format(day) }\n\t\t\t})\n\t\t},\n\n\t\t/** @return {string} \"December 2026\" in the user's language. */\n\t\tmonthTitle() {\n\t\t\tconst text = new Intl.DateTimeFormat(userLocale(), { month: 'long', year: 'numeric' }).format(new Date(this.year, this.month, 15))\n\t\t\treturn text.charAt(0).toUpperCase() + text.slice(1)\n\t\t},\n\n\t\t/** @return {string} Why holidays are missing, or ''. */\n\t\tpreviewNote() {\n\t\t\treturn this.previewFailed ? t('nextcloud-vue', 'Holidays could not be read. Only the days added by hand are shown.') : ''\n\t\t},\n\n\t\tloadingLabel: () => t('nextcloud-vue', 'Loading …'),\n\t\tpreviousMonthLabel: () => t('nextcloud-vue', 'Previous month'),\n\t\tnextMonthLabel: () => t('nextcloud-vue', 'Next month'),\n\t\taddLabel: () => t('nextcloud-vue', 'Add day'),\n\t\tdateLabel: () => t('nextcloud-vue', 'Date'),\n\t\tnameLabel: () => t('nextcloud-vue', 'Name'),\n\t\tcancelLabel: () => t('nextcloud-vue', 'Cancel'),\n\t\tsaveLabel: () => t('nextcloud-vue', 'Save'),\n\n\t\t/** @return {string} The empty-state text. */\n\t\temptyLabel() {\n\t\t\treturn this.content?.emptyText ? this.translate(this.content.emptyText) : t('nextcloud-vue', 'No working calendar found.')\n\t\t},\n\t},\n\n\twatch: {\n\t\tsourceKey: {\n\t\t\thandler() {\n\t\t\t\tthis.load()\n\t\t\t},\n\n\t\t\timmediate: true,\n\t\t},\n\n\t\tyear() {\n\t\t\tthis.loadPreview()\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * The host's translate, or the text unchanged.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The translation.\n\t\t */\n\t\ttranslate(text) {\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text\n\t\t},\n\n\t\t/** @return {string} The calendar object's API URL. */\n\t\tobjectUrl() {\n\t\t\tconst s = this.source\n\t\t\tconst id = idOf(this.calendar) || s.id\n\t\t\treturn generateUrl(`${OBJECT_API}/${encodeURIComponent(s.register)}/${encodeURIComponent(s.schema)}/${encodeURIComponent(id)}`)\n\t\t},\n\n\t\t/**\n\t\t * Read the calendar object, then the closures of the year shown.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tconst key = this.sourceKey\n\t\t\tif (key === '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tconst s = this.source\n\t\t\t\tlet calendar = null\n\t\t\t\tif (s.id) {\n\t\t\t\t\tconst response = await axios.get(generateUrl(`${OBJECT_API}/${encodeURIComponent(s.register)}/${encodeURIComponent(s.schema)}/${encodeURIComponent(s.id)}`))\n\t\t\t\t\tcalendar = response?.data || null\n\t\t\t\t} else {\n\t\t\t\t\tconst found = await fetchReferenceObjects({ register: s.register, schema: s.schema, filter: s.filter || {}, limit: 1 })\n\t\t\t\t\tcalendar = found[0] || null\n\t\t\t\t}\n\t\t\t\tif (key !== this.sourceKey) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tthis.calendar = calendar\n\t\t\t\tthis.previews = {}\n\t\t\t\tif (calendar) {\n\t\t\t\t\tawait this.loadPreview()\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tthis.error = t('nextcloud-vue', 'The working calendar could not be loaded.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Ask the calendar preview for the closed dates of the year shown,\n\t\t * once per year.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/working-calendar-widget/specs/working-calendar-widget/spec.md#requirement-a-working-calendar-shows-a-month\n\t\t */\n\t\tasync loadPreview() {\n\t\t\tconst year = this.year\n\t\t\tif (!this.calendar || this.previews[year]) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst url = generateUrl(this.content?.previewUrl || PREVIEW_URL)\n\t\t\t\tconst response = await axios.post(url, { calendar: this.calendar, year })\n\t\t\t\tconst dates = Array.isArray(response?.data?.dates) ? response.data.dates : []\n\t\t\t\tthis.previews = { ...this.previews, [year]: dates }\n\t\t\t\tthis.previewFailed = false\n\t\t\t} catch {\n\t\t\t\tthis.previewFailed = true\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Show another month, keeping focus on the same day where it exists.\n\t\t *\n\t\t * @param {number} delta Months to move.\n\t\t * @return {void}\n\t\t */\n\t\tshiftMonth(delta) {\n\t\t\tconst date = new Date(this.year, this.month + delta, 1)\n\t\t\tthis.year = date.getFullYear()\n\t\t\tthis.month = date.getMonth()\n\t\t\tconst day = Math.min(Number(this.focusDate.slice(8, 10)) || 1, new Date(this.year, this.month + 1, 0).getDate())\n\t\t\tthis.focusDate = isoDate(this.year, this.month, day)\n\t\t\tthis.liveText = this.monthTitle\n\t\t},\n\n\t\t/**\n\t\t * A day's accessible label: the full date, whether it works, and the\n\t\t * closure's name.\n\t\t *\n\t\t * @param {object} slot The day.\n\t\t * @return {string} The label.\n\t\t */\n\t\tdayLabel(slot) {\n\t\t\tconst [y, m, d] = slot.date.split('-').map(Number)\n\t\t\tconst date = new Intl.DateTimeFormat(userLocale(), { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' }).format(new Date(y, m - 1, d))\n\t\t\tif (slot.closure) {\n\t\t\t\tconst name = this.closureName(slot.closure)\n\t\t\t\treturn name\n\t\t\t\t\t? t('nextcloud-vue', '{date}, closed: {name}', { date, name })\n\t\t\t\t\t: t('nextcloud-vue', '{date}, closed', { date })\n\t\t\t}\n\t\t\treturn slot.working ? t('nextcloud-vue', '{date}, working day', { date }) : t('nextcloud-vue', '{date}, not a working day', { date })\n\t\t},\n\n\t\t/**\n\t\t * A closure's name, with a fallback for an unnamed added day.\n\t\t *\n\t\t * @param {{name: string, kind: string}} closure The closure.\n\t\t * @return {string} The name.\n\t\t */\n\t\tclosureName(closure) {\n\t\t\tif (closure.name) {\n\t\t\t\treturn this.translate(closure.name)\n\t\t\t}\n\t\t\treturn closure.kind === 'exception' ? t('nextcloud-vue', 'Day added by hand') : ''\n\t\t},\n\n\t\t/**\n\t\t * Open Add day, with a date filled in.\n\t\t *\n\t\t * @param {string} [date] The date.\n\t\t * @return {void}\n\t\t */\n\t\topenAdd(date) {\n\t\t\tthis.newDate = date || this.focusDate || ''\n\t\t\tthis.newName = ''\n\t\t\tthis.saveError = ''\n\t\t\tthis.adding = true\n\t\t\tthis.$nextTick(() => this.$refs.dateInput?.focus?.())\n\t\t},\n\n\t\t/**\n\t\t * A click on a day: open Add day for it when the reader may edit.\n\t\t *\n\t\t * @param {object} slot The day.\n\t\t * @return {void}\n\t\t */\n\t\tonDayClick(slot) {\n\t\t\tthis.focusDate = slot.date\n\t\t\tif (this.editable) {\n\t\t\t\tthis.openAdd(slot.date)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Append the new day to the calendar's exceptions with one update,\n\t\t * under the reader's rights, then read the closures again.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/working-calendar-widget/specs/working-calendar-widget/spec.md#requirement-a-day-can-be-added-to-the-calendar\n\t\t */\n\t\tasync saveDay() {\n\t\t\tif (!/^\\d{4}-\\d{2}-\\d{2}$/.test(this.newDate) || !this.calendar) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.saving = true\n\t\t\tthis.saveError = ''\n\t\t\ttry {\n\t\t\t\tconst stored = Array.isArray(this.calendar.exceptions) ? this.calendar.exceptions : []\n\t\t\t\tconst name = this.newName.trim()\n\t\t\t\tconst exceptions = [...stored.filter((e) => (typeof e === 'string' ? e : e?.date) !== this.newDate), name ? { date: this.newDate, name } : { date: this.newDate }]\n\t\t\t\tconst response = await axios.patch(this.objectUrl(), { exceptions })\n\t\t\t\tthis.calendar = { ...this.calendar, ...(response?.data && typeof response.data === 'object' ? response.data : {}), exceptions }\n\t\t\t\tconst year = Number(this.newDate.slice(0, 4))\n\t\t\t\tconst { [year]: _stale, ...rest } = this.previews\n\t\t\t\tthis.previews = rest\n\t\t\t\tconst [y, m] = this.newDate.split('-').map(Number)\n\t\t\t\tthis.year = y\n\t\t\t\tthis.month = m - 1\n\t\t\t\tthis.focusDate = this.newDate\n\t\t\t\tawait this.loadPreview()\n\t\t\t\tthis.adding = false\n\t\t\t\tthis.liveText = t('nextcloud-vue', 'Day added.')\n\t\t\t} catch (e) {\n\t\t\t\tthis.saveError = e?.response?.status === 403\n\t\t\t\t\t? t('nextcloud-vue', 'Only an administrator can change the working calendar.')\n\t\t\t\t\t: t('nextcloud-vue', 'The day could not be added. Try again.')\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Arrow keys, Home / End and Page Up / Down move focus across the days.\n\t\t *\n\t\t * @param {KeyboardEvent} event The key event.\n\t\t * @return {void}\n\t\t */\n\t\tonKeydown(event) {\n\t\t\tconst moves = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -7, ArrowDown: 7 }\n\t\t\tconst [y, m, d] = (this.focusDate || todayIso()).split('-').map(Number)\n\t\t\tlet target\n\t\t\tif (event.key in moves) {\n\t\t\t\ttarget = new Date(y, m - 1, d + moves[event.key])\n\t\t\t} else if (event.key === 'Home' || event.key === 'End') {\n\t\t\t\tconst weekday = (new Date(y, m - 1, d).getDay() + 6) % 7\n\t\t\t\ttarget = new Date(y, m - 1, d + (event.key === 'Home' ? -weekday : 6 - weekday))\n\t\t\t} else if (event.key === 'PageUp' || event.key === 'PageDown') {\n\t\t\t\ttarget = new Date(y, m - 1 + (event.key === 'PageUp' ? -1 : 1), 1)\n\t\t\t\ttarget.setDate(Math.min(d, new Date(target.getFullYear(), target.getMonth() + 1, 0).getDate()))\n\t\t\t} else if ((event.key === 'Enter' || event.key === ' ') && this.editable) {\n\t\t\t\tevent.preventDefault()\n\t\t\t\tthis.openAdd(this.focusDate)\n\t\t\t\treturn\n\t\t\t} else {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tevent.preventDefault()\n\t\t\tthis.focusDate = isoDate(target.getFullYear(), target.getMonth(), target.getDate())\n\t\t\tif (target.getFullYear() !== this.year || target.getMonth() !== this.month) {\n\t\t\t\tthis.year = target.getFullYear()\n\t\t\t\tthis.month = target.getMonth()\n\t\t\t\tthis.liveText = this.monthTitle\n\t\t\t}\n\t\t\tthis.$nextTick(() => this.$el.querySelector?.(`[data-date=\"${this.focusDate}\"]`)?.focus?.())\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-working-calendar {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n}\n\n.cn-working-calendar__head {\n\talign-items: center;\n\tdisplay: flex;\n\tgap: 4px;\n}\n\n.cn-working-calendar__title {\n\tfont-size: 15px;\n\tmin-width: 9em;\n\ttext-align: center;\n}\n\n.cn-working-calendar__add {\n\tmargin-inline-start: auto;\n}\n\n.cn-working-calendar__grid {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-working-calendar__row {\n\tdisplay: grid;\n\tgap: 4px;\n\tgrid-template-columns: repeat(7, minmax(0, 1fr));\n}\n\n.cn-working-calendar__weekday {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 12px;\n\ttext-align: center;\n}\n\n.cn-working-calendar__day {\n\talign-items: center;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: 6px;\n\tcursor: default;\n\tdisplay: flex;\n\tfont-size: 13px;\n\theight: 34px;\n\tjustify-content: center;\n}\n\n.cn-working-calendar__day:focus-visible {\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 1px;\n}\n\n.cn-working-calendar__day--off {\n\tbackground: var(--color-background-dark);\n}\n\n.cn-working-calendar__day--closed {\n\tbackground: var(--color-error-light, color-mix(in srgb, var(--color-error) 12%, var(--color-main-background)));\n\tborder-color: var(--color-error);\n\tcolor: var(--color-error-text, var(--color-main-text));\n\tfont-weight: 600;\n}\n\n.cn-working-calendar__day--today {\n\tbox-shadow: inset 0 0 0 2px var(--color-primary-element);\n}\n\n.cn-working-calendar__live {\n\tclip-path: inset(50%);\n\theight: 1px;\n\toverflow: hidden;\n\tposition: absolute;\n\twhite-space: nowrap;\n\twidth: 1px;\n}\n\n.cn-working-calendar__note {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 0;\n}\n\n.cn-working-calendar__form {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 8px 12px;\n}\n\n.cn-working-calendar__field {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-working-calendar__form-actions {\n\talign-items: flex-end;\n\tdisplay: flex;\n\tgap: 8px;\n}\n\n.cn-working-calendar__error {\n\tcolor: var(--color-error-text, var(--color-error));\n\tmargin: 0;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_toDisplayString","_createVNode","_Fragment","_createElementVNode","_createBlock","_renderList","_createTextVNode"],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import __sfc_main from './CnWorkingCalendarWidget.vue.js';
2
+ import { registerDashboardWidget } from '../CnWidgetGrid/dashboardWidgetRegistry.js';
3
+
4
+ /**
5
+ * SPDX-FileCopyrightText: 2026 Conduction B.V.
6
+ * SPDX-License-Identifier: EUPL-1.2
7
+ *
8
+ * CnWorkingCalendarWidget — self-registers the `working-calendar` widget type
9
+ * (one month of a working calendar kept as an OpenRegister object, with its
10
+ * closed dates and Add day) into the shared dashboardWidgetRegistry at module
11
+ * load. Renderer only: the calendar object is named in the manifest.
12
+ */
13
+
14
+
15
+ registerDashboardWidget('working-calendar', {
16
+ renderer: __sfc_main,
17
+ form: null,
18
+ defaultContent: {
19
+ calendar: { register: 'flow-timers', schema: 'working-calendar', filter: {} },
20
+ },
21
+ displayName: 'Working calendar',
22
+ icon: 'CalendarMonthOutline',
23
+ surfaces: ['app-dashboard', 'detail-page'],
24
+ });
25
+
26
+ export { __sfc_main as CnWorkingCalendarWidget, __sfc_main as default };
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../../../src/components/CnWorkingCalendarWidget/index.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * CnWorkingCalendarWidget — self-registers the `working-calendar` widget type\n * (one month of a working calendar kept as an OpenRegister object, with its\n * closed dates and Add day) into the shared dashboardWidgetRegistry at module\n * load. Renderer only: the calendar object is named in the manifest.\n */\n\nimport CnWorkingCalendarWidget from './CnWorkingCalendarWidget.vue'\nimport { registerDashboardWidget } from '../CnWidgetGrid/dashboardWidgetRegistry.js'\n\nregisterDashboardWidget('working-calendar', {\n\trenderer: CnWorkingCalendarWidget,\n\tform: null,\n\tdefaultContent: {\n\t\tcalendar: { register: 'flow-timers', schema: 'working-calendar', filter: {} },\n\t},\n\tdisplayName: 'Working calendar',\n\ticon: 'CalendarMonthOutline',\n\tsurfaces: ['app-dashboard', 'detail-page'],\n})\n\nexport { CnWorkingCalendarWidget }\nexport default CnWorkingCalendarWidget\n"],"names":["CnWorkingCalendarWidget"],"mappings":";;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACA,uBAAuB,CAAC,kBAAkB,EAAE;AAC5C,CAAC,QAAQ,EAAEA,UAAuB;AAClC,CAAC,IAAI,EAAE,IAAI;AACX,CAAC,cAAc,EAAE;AACjB,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,EAAE,EAAE;AAC/E,EAAE;AACF,CAAC,WAAW,EAAE,kBAAkB;AAChC,CAAC,IAAI,EAAE,sBAAsB;AAC7B,CAAC,QAAQ,EAAE,CAAC,eAAe,EAAE,aAAa,CAAC;AAC3C,CAAC;;;;"}
@@ -1,4 +1,5 @@
1
1
  import axios__default from '@nextcloud/axios';
2
+ import { translate } from '@nextcloud/l10n';
2
3
  import { generateUrl } from '@nextcloud/router';
3
4
  import { defineStore } from 'pinia';
4
5
  import { layoutFlowNodes, needsFullLayout, placeLooseNodes } from './flowGraphLayout.js';
@@ -969,7 +970,7 @@ const useFlowStore = defineStore('cnFlow', {
969
970
  // A new flow is runnable on demand until its author picks a real
970
971
  // trigger, so the flow-level trigger and the seeded start node
971
972
  // say the same thing.
972
- this.flow = { ...emptyFlow(), name: 'New flow', app: app || 'openregister', trigger: 'manual' };
973
+ this.flow = { ...emptyFlow(), name: translate('nextcloud-vue', 'New flow'), app: app || 'openregister', trigger: 'manual' };
973
974
  this.seedStartNode();
974
975
  this.dirty = false;
975
976
  return