@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.
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js +12 -0
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js +11 -2
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js.map +1 -1
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue.js +1 -1
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue2.js.map +1 -1
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue3.js +1 -1
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +2 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue.js +1 -1
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js +26 -7
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js.map +1 -1
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js +78 -17
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js.map +1 -1
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue.js +9 -0
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue.js.map +1 -0
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js +265 -0
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js.map +1 -0
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js +191 -0
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js.map +1 -0
- package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue.js +9 -0
- package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue.js.map +1 -0
- package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue2.js +508 -0
- package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue2.js.map +1 -0
- package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue3.js +99 -0
- package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue3.js.map +1 -0
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue.js +1 -1
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js +18 -70
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue3.js +6 -44
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue.js +9 -0
- package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue.js.map +1 -0
- package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue2.js +472 -0
- package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue3.js +231 -0
- package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnWorkingCalendarWidget/index.js +27 -0
- package/dist/esm/components/CnWorkingCalendarWidget/index.js.map +1 -0
- package/dist/esm/composables/useFlowStore.js +2 -1
- package/dist/esm/composables/useFlowStore.js.map +1 -1
- package/dist/esm/index.js +3 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +95 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +109 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +660 -81
- package/dist/esm/utils/libraryWidgetKeys.js +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/workingCalendarMonth.js +85 -0
- package/dist/esm/utils/workingCalendarMonth.js.map +1 -0
- package/dist/nextcloud-vue.cjs.js +9355 -7303
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +660 -81
- package/l10n/en.json +95 -1
- package/l10n/nl.json +109 -1
- package/package.json +1 -1
- package/src/components/CnActionsMenu/CnActionsMenu.vue +14 -1
- package/src/components/CnBreadcrumbs/CnBreadcrumbs.vue +1 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +1 -0
- package/src/components/CnOrderedListField/CnOrderedListField.vue +81 -9
- package/src/components/CnSettingsField/CnSettingsField.vue +449 -0
- package/src/components/CnSettingsField/index.js +4 -0
- package/src/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue +613 -0
- package/src/components/CnSettingsFieldsSection/index.js +4 -0
- package/src/components/CnSettingsPage/CnSettingsPage.vue +6 -86
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
- package/src/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue +725 -0
- package/src/components/CnWorkingCalendarWidget/index.js +26 -0
- package/src/components/index.js +3 -0
- package/src/composables/useFlowStore.js +2 -1
- package/src/css/badge.css +32 -16
- package/src/css/look-board-detail.css +80 -8
- package/src/css/look-board-index.css +52 -8
- package/src/css/look-board.css +219 -1
- package/src/index.js +3 -0
- package/src/utils/libraryWidgetKeys.js +1 -0
- package/src/utils/workingCalendarMonth.js +82 -0
package/src/css/look-board.css
CHANGED
|
@@ -141,6 +141,50 @@
|
|
|
141
141
|
color: var(--color-primary-element-text);
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
+
/* ---- Header action buttons (CnActionButtons) ----------------------------- */
|
|
145
|
+
/* The board draws a page's secondary actions as neutral 40px buttons: the
|
|
146
|
+
main background, a grey border, the main text at 600 (DqZaak "Bericht",
|
|
147
|
+
"Document", "Contact vastleggen", "Bewerken"). Nextcloud's secondary button
|
|
148
|
+
is a primary tint. The label hands its ink to --nldesign-color-on-surface,
|
|
149
|
+
which a theme that forces the body ink onto every span (thematiq) reads. */
|
|
150
|
+
.cn-look-board .cn-action-buttons .button-vue--secondary:not(.cn-look-nextcloud *),
|
|
151
|
+
.cn-look-board .cn-action-buttons .button-vue--vue-secondary:not(.cn-look-nextcloud *) {
|
|
152
|
+
--nldesign-color-on-surface: var(--color-main-text);
|
|
153
|
+
height: var(--cn-board-control-height);
|
|
154
|
+
min-height: var(--cn-board-control-height);
|
|
155
|
+
padding: 0 14px;
|
|
156
|
+
border: 1px solid var(--color-border-dark);
|
|
157
|
+
border-radius: var(--cn-board-control-radius);
|
|
158
|
+
background: var(--color-main-background);
|
|
159
|
+
color: var(--color-main-text);
|
|
160
|
+
font-size: 14px;
|
|
161
|
+
font-weight: 600;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/* Buttons 10px apart, each icon 16px with 8px to its label (DqZaak's header,
|
|
165
|
+
DqZaken's "Downloaden"); NcButton draws a 20px icon in a 30px slot. */
|
|
166
|
+
.cn-look-board .cn-action-buttons:not(.cn-look-nextcloud *) {
|
|
167
|
+
gap: 10px;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.cn-look-board .cn-action-buttons .button-vue .button-vue__icon:not(.cn-look-nextcloud *) {
|
|
171
|
+
width: 16px;
|
|
172
|
+
min-width: 16px;
|
|
173
|
+
height: 16px;
|
|
174
|
+
min-height: 16px;
|
|
175
|
+
margin-inline-end: 8px;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.cn-look-board .cn-action-buttons .button-vue .button-vue__icon:not(.cn-look-nextcloud *) svg {
|
|
179
|
+
width: 16px;
|
|
180
|
+
height: 16px;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.cn-look-board .cn-action-buttons .button-vue--secondary:not(.cn-look-nextcloud *):hover,
|
|
184
|
+
.cn-look-board .cn-action-buttons .button-vue--vue-secondary:not(.cn-look-nextcloud *):hover {
|
|
185
|
+
background: var(--color-background-hover);
|
|
186
|
+
}
|
|
187
|
+
|
|
144
188
|
/* ---- The buildiq square ------------------------------------------------ */
|
|
145
189
|
.cn-look-board .cn-buildiq-edit .cn-buildiq-edit__actions .button-vue {
|
|
146
190
|
box-sizing: border-box;
|
|
@@ -153,6 +197,34 @@
|
|
|
153
197
|
border-radius: var(--cn-board-control-radius);
|
|
154
198
|
}
|
|
155
199
|
|
|
200
|
+
/* ---- The Actions menu trigger (CnActionsMenu) ---------------------------- */
|
|
201
|
+
/* A menu asked for as `secondary` ("Meer" in a detail header, "Acties" at the
|
|
202
|
+
end of a tab strip) is the board's neutral control: the main background, a
|
|
203
|
+
grey border, the main text at 600 (DqZaak), not Nextcloud's primary tint.
|
|
204
|
+
NcActions marks a secondary menu `action-item--secondary` on its root and
|
|
205
|
+
keeps a class given to it off, so the rule reads that marker. The label
|
|
206
|
+
hands its ink to --nldesign-color-on-surface for a theme that forces the
|
|
207
|
+
body ink onto every span (thematiq). */
|
|
208
|
+
.cn-look-board .action-item.action-item--secondary .action-item__menutoggle.button-vue--secondary:not(.cn-look-nextcloud *) {
|
|
209
|
+
--nldesign-color-on-surface: var(--color-main-text);
|
|
210
|
+
border: 1px solid var(--color-border-dark);
|
|
211
|
+
border-radius: var(--cn-board-control-radius);
|
|
212
|
+
background: var(--color-main-background);
|
|
213
|
+
color: var(--color-main-text);
|
|
214
|
+
font-size: 14px;
|
|
215
|
+
font-weight: 600;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.cn-look-board .action-item.action-item--secondary .action-item__menutoggle.button-vue--secondary:not(.cn-look-nextcloud *):hover {
|
|
219
|
+
background: var(--color-background-hover);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/* A trailing chevron ("Meer ⌄") follows the label, 6px after it. */
|
|
223
|
+
.cn-look-board .action-item.action-item--secondary .action-item__menutoggle:has(.chevron-down-icon) .button-vue__wrapper:not(.cn-look-nextcloud *) {
|
|
224
|
+
flex-direction: row-reverse;
|
|
225
|
+
gap: 6px;
|
|
226
|
+
}
|
|
227
|
+
|
|
156
228
|
/* ---- The navigation ---------------------------------------------------- */
|
|
157
229
|
.cn-look-board .app-navigation[data-testid="cn-nav"] {
|
|
158
230
|
--app-navigation-padding: 0px;
|
|
@@ -374,6 +446,71 @@
|
|
|
374
446
|
font-weight: 600;
|
|
375
447
|
}
|
|
376
448
|
|
|
449
|
+
/* ---- Breadcrumbs (CnBreadcrumbs) ----------------------------------------- */
|
|
450
|
+
/* The board draws a trail as a line of 14px text, 8px apart: the crumbs that
|
|
451
|
+
lead somewhere as underlined links in the primary colour, the current one
|
|
452
|
+
as plain muted text (DqZaak "Alle zaken / 2026-0082"). NcBreadcrumbs draws
|
|
453
|
+
each crumb as a 34px tertiary button. The doubled classes outrank its
|
|
454
|
+
scoped rules; the ink goes through --nldesign-color-on-surface as well, for
|
|
455
|
+
a theme that forces the body ink onto every link and span (thematiq). */
|
|
456
|
+
.cn-look-board .cn-breadcrumbs.cn-breadcrumbs:not(.cn-look-nextcloud *) {
|
|
457
|
+
min-height: 0;
|
|
458
|
+
height: auto;
|
|
459
|
+
color: var(--color-text-maxcontrast);
|
|
460
|
+
font-size: 14px;
|
|
461
|
+
line-height: normal;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.cn-look-board .cn-breadcrumbs .breadcrumb__crumbs:not(.cn-look-nextcloud *) {
|
|
465
|
+
gap: 8px;
|
|
466
|
+
min-height: 0;
|
|
467
|
+
margin: 0;
|
|
468
|
+
padding: 0;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.cn-look-board .cn-breadcrumbs .vue-crumb.vue-crumb:not(.cn-look-nextcloud *) {
|
|
472
|
+
gap: 8px;
|
|
473
|
+
min-width: 0;
|
|
474
|
+
min-height: 0;
|
|
475
|
+
height: auto;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
.cn-look-board .cn-breadcrumbs .vue-crumb .button-vue.button-vue:not(.cn-look-nextcloud *) {
|
|
479
|
+
--nldesign-color-on-surface: var(--color-primary-element);
|
|
480
|
+
min-width: 0;
|
|
481
|
+
min-height: 0;
|
|
482
|
+
height: auto;
|
|
483
|
+
padding: 0;
|
|
484
|
+
border: 0;
|
|
485
|
+
background: transparent;
|
|
486
|
+
color: var(--color-primary-element);
|
|
487
|
+
font-size: 14px;
|
|
488
|
+
font-weight: 400;
|
|
489
|
+
line-height: inherit;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
.cn-look-board .cn-breadcrumbs .vue-crumb .button-vue.button-vue:not(.cn-look-nextcloud *) .button-vue__text {
|
|
493
|
+
padding: 0;
|
|
494
|
+
font-weight: inherit;
|
|
495
|
+
line-height: inherit;
|
|
496
|
+
text-decoration: underline;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.cn-look-board .cn-breadcrumbs .vue-crumb .button-vue.button-vue[aria-current='page']:not(.cn-look-nextcloud *) {
|
|
500
|
+
--nldesign-color-on-surface: var(--color-text-maxcontrast);
|
|
501
|
+
color: var(--color-text-maxcontrast);
|
|
502
|
+
cursor: default;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.cn-look-board .cn-breadcrumbs .vue-crumb .button-vue.button-vue[aria-current='page']:not(.cn-look-nextcloud *) .button-vue__text {
|
|
506
|
+
text-decoration: none;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
.cn-look-board .cn-breadcrumbs .vue-crumb__separator:not(.cn-look-nextcloud *) {
|
|
510
|
+
margin: 0;
|
|
511
|
+
color: var(--color-text-maxcontrast);
|
|
512
|
+
}
|
|
513
|
+
|
|
377
514
|
/* ---- Settings: header, cards in a grid, version card ------------------- */
|
|
378
515
|
.cn-look-board .cn-admin-settings-shell__header:not(.cn-look-nextcloud *),
|
|
379
516
|
.cn-look-board .cn-settings-page__header:not(.cn-look-nextcloud *) {
|
|
@@ -792,7 +929,26 @@
|
|
|
792
929
|
ground ("Verzoek", "Nieuw"); the default look keeps the brighter
|
|
793
930
|
primary-element text. The other tones already match the board. */
|
|
794
931
|
.cn-look-board .cn-status-badge--primary:not(.cn-status-badge--solid):not(.cn-look-nextcloud *) {
|
|
795
|
-
|
|
932
|
+
--cn-status-badge-ink: var(--color-primary-element-light-text);
|
|
933
|
+
color: var(--cn-status-badge-ink);
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/* Every board pill is 13px at 600 on a 3px 10px pad with a 12px corner
|
|
937
|
+
(DqZaak's header pills, DqZaken's status column). */
|
|
938
|
+
.cn-look-board .cn-status-badge:not(.cn-status-badge--small):not(.cn-look-nextcloud *) {
|
|
939
|
+
padding: 3px 10px;
|
|
940
|
+
border-radius: 12px;
|
|
941
|
+
font-size: 13px;
|
|
942
|
+
font-weight: 600;
|
|
943
|
+
line-height: normal;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
/* A pill paints its own pairing of ink and fill. A theme that forces the body
|
|
947
|
+
ink onto every span (thematiq's text rule, `!important` at id level) reads
|
|
948
|
+
that ink from --nldesign-color-on-surface when the surface sets it, so the
|
|
949
|
+
pill hands it its own ink: "In behandeling" stays purple on purple. */
|
|
950
|
+
.cn-look-board .cn-status-badge:not(.cn-look-nextcloud *) {
|
|
951
|
+
--nldesign-color-on-surface: var(--cn-status-badge-ink);
|
|
796
952
|
}
|
|
797
953
|
|
|
798
954
|
/* ---- Date and age cells (screens-cell-date-parity) ---------------------- */
|
|
@@ -802,6 +958,68 @@
|
|
|
802
958
|
font-weight: 600;
|
|
803
959
|
}
|
|
804
960
|
|
|
961
|
+
/* ---- Label and value pairs (CnDetailGrid) -------------------------------- */
|
|
962
|
+
/* The board draws pairs as plain text, not as grey cards with an accent bar:
|
|
963
|
+
the grid as a 13px muted label over a 15px value at weight 500 with a
|
|
964
|
+
hairline under each pair (DqZaak "Zaakgegevens"), the horizontal layout as
|
|
965
|
+
two columns of 14px text, the label column as wide as its longest label
|
|
966
|
+
(the side cards). Both at the board's line height, 1.2, not Nextcloud's 1.5:
|
|
967
|
+
a pair is 47px tall on the board. The muted label also hands its ink to
|
|
968
|
+
--nldesign-color-on-surface, which a theme that forces the body ink onto
|
|
969
|
+
every div (thematiq) reads. */
|
|
970
|
+
.cn-look-board .cn-detail-grid.cn-detail-grid--grid:not(.cn-look-nextcloud *) {
|
|
971
|
+
gap: 12px 24px;
|
|
972
|
+
font-size: 15px;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
.cn-look-board .cn-detail-grid.cn-detail-grid--horizontal:not(.cn-look-nextcloud *) {
|
|
976
|
+
display: grid;
|
|
977
|
+
grid-template-columns: max-content 1fr;
|
|
978
|
+
gap: 8px 16px;
|
|
979
|
+
font-size: 14px;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
.cn-look-board .cn-detail-grid .cn-detail-grid__item:not(.cn-look-nextcloud *) {
|
|
983
|
+
gap: 3px;
|
|
984
|
+
padding: 0;
|
|
985
|
+
border-inline-start: none;
|
|
986
|
+
border-radius: 0;
|
|
987
|
+
background: none;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
.cn-look-board .cn-detail-grid.cn-detail-grid--grid .cn-detail-grid__item:not(.cn-look-nextcloud *) {
|
|
991
|
+
padding-bottom: 10px;
|
|
992
|
+
border-bottom: 1px solid var(--cn-board-hairline);
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
.cn-look-board .cn-detail-grid.cn-detail-grid--horizontal .cn-detail-grid__item:not(.cn-look-nextcloud *) {
|
|
996
|
+
display: contents;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
.cn-look-board .cn-detail-grid .cn-detail-grid__label:not(.cn-look-nextcloud *) {
|
|
1000
|
+
--nldesign-color-on-surface: var(--color-text-maxcontrast);
|
|
1001
|
+
min-width: 0;
|
|
1002
|
+
color: var(--color-text-maxcontrast);
|
|
1003
|
+
font-size: 13px;
|
|
1004
|
+
font-weight: 400;
|
|
1005
|
+
line-height: 1.2;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
.cn-look-board .cn-detail-grid.cn-detail-grid--horizontal .cn-detail-grid__label:not(.cn-look-nextcloud *) {
|
|
1009
|
+
font-size: inherit;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
.cn-look-board .cn-detail-grid .cn-detail-grid__value:not(.cn-look-nextcloud *) {
|
|
1013
|
+
margin: 0;
|
|
1014
|
+
font-size: inherit;
|
|
1015
|
+
font-weight: 500;
|
|
1016
|
+
line-height: 1.2;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
.cn-look-board .cn-detail-grid.cn-detail-grid--horizontal .cn-detail-grid__value:not(.cn-look-nextcloud *) {
|
|
1020
|
+
font-weight: 400;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
805
1023
|
/* ---- Dashboard card titles --------------------------------------------- */
|
|
806
1024
|
/* A card's title on a dashboard (and on the widgets under a reports page) is
|
|
807
1025
|
the board's card h2: 17px weight 700 (boards PqMijnWerk, DcRapportages and
|
package/src/index.js
CHANGED
|
@@ -280,6 +280,8 @@ export {
|
|
|
280
280
|
CnSearchPage,
|
|
281
281
|
CnSectionsWidget,
|
|
282
282
|
CnSettingsCard,
|
|
283
|
+
CnSettingsField,
|
|
284
|
+
CnSettingsFieldsSection,
|
|
283
285
|
CnSettingsFormWidget,
|
|
284
286
|
CnSettingsPage,
|
|
285
287
|
CnSettingsSection,
|
|
@@ -344,6 +346,7 @@ export {
|
|
|
344
346
|
CnWidgetVisibilityRulesModal,
|
|
345
347
|
CnWidgetWrapper,
|
|
346
348
|
CnWizardDialog,
|
|
349
|
+
CnWorkingCalendarWidget,
|
|
347
350
|
CnWorkspaceFilterWidget,
|
|
348
351
|
DEFAULT_DATE_RANGE_PRESETS,
|
|
349
352
|
defaultPageTypes,
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
3
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
4
|
+
*
|
|
5
|
+
* workingCalendarMonth: the weeks of one month as a working calendar draws
|
|
6
|
+
* them, Monday first, each day marked working or not and with the closures
|
|
7
|
+
* that fall on it.
|
|
8
|
+
*
|
|
9
|
+
* Pure: the closures come in from the caller (OpenRegister's calendar preview
|
|
10
|
+
* computes them), nothing here knows a holiday.
|
|
11
|
+
*
|
|
12
|
+
* @module utils/workingCalendarMonth
|
|
13
|
+
* @spec openspec/changes/working-calendar-widget/specs/working-calendar-widget/spec.md#requirement-a-working-calendar-shows-a-month
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A date as `YYYY-MM-DD`.
|
|
18
|
+
*
|
|
19
|
+
* @param {number} year The year.
|
|
20
|
+
* @param {number} month The month, 0 to 11.
|
|
21
|
+
* @param {number} day The day of the month.
|
|
22
|
+
* @return {string} The date.
|
|
23
|
+
*/
|
|
24
|
+
export function isoDate(year, month, day) {
|
|
25
|
+
return `${String(year).padStart(4, '0')}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The stored exceptions of a calendar as `{ date, name }`: entries are a
|
|
30
|
+
* `YYYY-MM-DD` string or an object with `date` and an optional `name`.
|
|
31
|
+
*
|
|
32
|
+
* @param {unknown} exceptions The stored list.
|
|
33
|
+
* @return {Array<{date: string, name: string}>} The exceptions.
|
|
34
|
+
*/
|
|
35
|
+
export function normaliseExceptions(exceptions) {
|
|
36
|
+
return (Array.isArray(exceptions) ? exceptions : []).map((entry) => {
|
|
37
|
+
if (typeof entry === 'string') {
|
|
38
|
+
return { date: entry, name: '' }
|
|
39
|
+
}
|
|
40
|
+
if (entry && typeof entry === 'object' && typeof entry.date === 'string') {
|
|
41
|
+
return { date: entry.date, name: typeof entry.name === 'string' ? entry.name : '' }
|
|
42
|
+
}
|
|
43
|
+
return null
|
|
44
|
+
}).filter((entry) => entry && /^\d{4}-\d{2}-\d{2}$/.test(entry.date))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The weeks of a month, Monday first. A slot outside the month is null.
|
|
49
|
+
*
|
|
50
|
+
* @param {number} year The year.
|
|
51
|
+
* @param {number} month The month, 0 to 11.
|
|
52
|
+
* @param {object} options The calendar.
|
|
53
|
+
* @param {number[]} [options.workingWeekdays] ISO weekdays that work (1 = Monday); Monday to Friday by default.
|
|
54
|
+
* @param {Map<string, {name: string, kind: string}>} [options.closures] Closed dates, by `YYYY-MM-DD`.
|
|
55
|
+
* @param {string} [options.today] Today as `YYYY-MM-DD`.
|
|
56
|
+
* @return {Array<Array<null|{date: string, day: number, weekday: number, working: boolean, closure: (null|{name: string, kind: string}), today: boolean}>>} The weeks.
|
|
57
|
+
*/
|
|
58
|
+
export function buildMonth(year, month, { workingWeekdays = [1, 2, 3, 4, 5], closures = new Map(), today = '' } = {}) {
|
|
59
|
+
const days = new Date(Date.UTC(year, month + 1, 0)).getUTCDate()
|
|
60
|
+
const firstWeekday = ((new Date(Date.UTC(year, month, 1)).getUTCDay() + 6) % 7) + 1
|
|
61
|
+
const slots = Array.from({ length: firstWeekday - 1 }, () => null)
|
|
62
|
+
for (let day = 1; day <= days; day += 1) {
|
|
63
|
+
const date = isoDate(year, month, day)
|
|
64
|
+
const weekday = ((firstWeekday - 1 + day - 1) % 7) + 1
|
|
65
|
+
slots.push({
|
|
66
|
+
date,
|
|
67
|
+
day,
|
|
68
|
+
weekday,
|
|
69
|
+
working: workingWeekdays.includes(weekday) && !closures.has(date),
|
|
70
|
+
closure: closures.get(date) || null,
|
|
71
|
+
today: date === today,
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
while (slots.length % 7 !== 0) {
|
|
75
|
+
slots.push(null)
|
|
76
|
+
}
|
|
77
|
+
const weeks = []
|
|
78
|
+
for (let i = 0; i < slots.length; i += 7) {
|
|
79
|
+
weeks.push(slots.slice(i, i + 7))
|
|
80
|
+
}
|
|
81
|
+
return weeks
|
|
82
|
+
}
|