@conduction/nextcloud-vue 2.149.0 → 2.150.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/CnChartWidget/CnChartWidget.vue.js +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +12 -0
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +84 -76
- package/dist/esm/utils/fillTemplate.js +19 -2
- package/dist/esm/utils/fillTemplate.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +32 -3
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +84 -76
- package/package.json +1 -1
- package/src/components/CnChartWidget/CnChartWidget.md +1 -1
- package/src/components/CnChartWidget/CnChartWidget.vue +12 -0
- package/src/css/board.css +2 -0
- package/src/css/look-board-index.css +5 -1
- package/src/utils/fillTemplate.js +19 -2
|
@@ -4527,6 +4527,7 @@ body:has(.cn-look-board):not(:has(.cn-look-nextcloud)) .modal-mask {
|
|
|
4527
4527
|
|
|
4528
4528
|
.cn-board-view.cn-board-view--board .cn-board-view__card-sub {
|
|
4529
4529
|
font-size: 13px;
|
|
4530
|
+
line-height: normal;
|
|
4530
4531
|
color: var(--color-text-maxcontrast);
|
|
4531
4532
|
}
|
|
4532
4533
|
|
|
@@ -4546,6 +4547,7 @@ body:has(.cn-look-board):not(:has(.cn-look-nextcloud)) .modal-mask {
|
|
|
4546
4547
|
|
|
4547
4548
|
.cn-board-view.cn-board-view--board .cn-board-view__card-footer {
|
|
4548
4549
|
display: flex;
|
|
4550
|
+
line-height: normal;
|
|
4549
4551
|
align-items: center;
|
|
4550
4552
|
justify-content: space-between;
|
|
4551
4553
|
gap: 8px;
|
|
@@ -6291,17 +6293,19 @@ body:has(.cn-look-board):not(:has(.cn-look-nextcloud)) .modal-mask {
|
|
|
6291
6293
|
margin: 0;
|
|
6292
6294
|
font-size: 17px;
|
|
6293
6295
|
font-weight: 700;
|
|
6294
|
-
line-height:
|
|
6296
|
+
line-height: normal;
|
|
6295
6297
|
text-decoration: none;
|
|
6296
6298
|
white-space: normal;
|
|
6297
6299
|
}
|
|
6298
6300
|
|
|
6299
6301
|
.cn-look-board .cn-object-card--board .cn-object-card__record-meta {
|
|
6300
6302
|
--nldesign-component-paragraph-font-size: 14px;
|
|
6303
|
+
--nldesign-component-paragraph-line-height: normal;
|
|
6301
6304
|
--nldesign-component-paragraph-color: var(--color-text-maxcontrast);
|
|
6302
6305
|
margin: 0;
|
|
6303
6306
|
color: var(--color-text-maxcontrast);
|
|
6304
6307
|
font-size: 14px;
|
|
6308
|
+
line-height: normal;
|
|
6305
6309
|
}
|
|
6306
6310
|
|
|
6307
6311
|
/* The meta line, note and description are paragraphs: each hands the theme,
|
|
@@ -6309,11 +6313,13 @@ body:has(.cn-look-board):not(:has(.cn-look-nextcloud)) .modal-mask {
|
|
|
6309
6313
|
.cn-look-board .cn-object-card--board .cn-object-card__record-note {
|
|
6310
6314
|
--nldesign-component-paragraph-font-size: 13px;
|
|
6311
6315
|
--nldesign-component-paragraph-font-weight: 600;
|
|
6316
|
+
--nldesign-component-paragraph-line-height: normal;
|
|
6312
6317
|
--nldesign-component-paragraph-color: var(--cn-board-text-soft);
|
|
6313
6318
|
margin: 0;
|
|
6314
6319
|
color: var(--cn-board-text-soft);
|
|
6315
6320
|
font-size: 13px;
|
|
6316
6321
|
font-weight: 600;
|
|
6322
|
+
line-height: normal;
|
|
6317
6323
|
}
|
|
6318
6324
|
|
|
6319
6325
|
.cn-look-board .cn-object-card--board .cn-object-card__record-note--purple {
|
|
@@ -16996,7 +17002,7 @@ li[data-cn-admin-tag] > .action-link::after {
|
|
|
16996
17002
|
}
|
|
16997
17003
|
|
|
16998
17004
|
|
|
16999
|
-
.cn-chart-widget[data-v-
|
|
17005
|
+
.cn-chart-widget[data-v-37644514] {
|
|
17000
17006
|
width: 100%;
|
|
17001
17007
|
/* A chart FITS its container — it is never a scroll region. apexcharts
|
|
17002
17008
|
sizes its SVG from the element it is mounted in, but it also enforces
|
|
@@ -17018,13 +17024,13 @@ li[data-cn-admin-tag] > .action-link::after {
|
|
|
17018
17024
|
takes what is left, so the chart is sized by the box rather than sizing it.
|
|
17019
17025
|
`min-height: 0` drops the 100px floor — on a fixed-height surface a floor is
|
|
17020
17026
|
the thing that produces the overflow it was meant to prevent. */
|
|
17021
|
-
.cn-chart-widget--fit[data-v-
|
|
17027
|
+
.cn-chart-widget--fit[data-v-37644514] {
|
|
17022
17028
|
height: 100%;
|
|
17023
17029
|
min-height: 0;
|
|
17024
17030
|
display: flex;
|
|
17025
17031
|
flex-direction: column;
|
|
17026
17032
|
}
|
|
17027
|
-
.cn-chart-widget__canvas[data-v-
|
|
17033
|
+
.cn-chart-widget__canvas[data-v-37644514] {
|
|
17028
17034
|
min-width: 0;
|
|
17029
17035
|
max-width: 100%;
|
|
17030
17036
|
/* Never a scroll region (see the root). `overflow-x: hidden` alone made
|
|
@@ -17037,14 +17043,14 @@ li[data-cn-admin-tag] > .action-link::after {
|
|
|
17037
17043
|
/* apexcharts writes an inline `width` onto its own wrapper from its measured
|
|
17038
17044
|
minimum, which beats the container when the container is narrower. Cap the
|
|
17039
17045
|
painted box so the inline width cannot push past the widget's border. */
|
|
17040
|
-
.cn-chart-widget[data-v-
|
|
17041
|
-
.cn-chart-widget[data-v-
|
|
17046
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-canvas,
|
|
17047
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-canvas svg {
|
|
17042
17048
|
max-width: 100%;
|
|
17043
17049
|
}
|
|
17044
17050
|
|
|
17045
17051
|
/* The SVG is a block: as an inline box it sits on a line and adds the line's
|
|
17046
17052
|
descender space under the chart. */
|
|
17047
|
-
.cn-chart-widget[data-v-
|
|
17053
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-canvas svg {
|
|
17048
17054
|
display: block;
|
|
17049
17055
|
}
|
|
17050
17056
|
|
|
@@ -17052,24 +17058,24 @@ li[data-cn-admin-tag] > .action-link::after {
|
|
|
17052
17058
|
would grow to its content, i.e. to the chart, and the percentage height would
|
|
17053
17059
|
resolve against a box the chart itself defined. `overflow: hidden` keeps a
|
|
17054
17060
|
sub-pixel rounding difference from reintroducing a scrollbar. */
|
|
17055
|
-
.cn-chart-widget--fit .cn-chart-widget__canvas[data-v-
|
|
17061
|
+
.cn-chart-widget--fit .cn-chart-widget__canvas[data-v-37644514] {
|
|
17056
17062
|
flex: 1 1 0;
|
|
17057
17063
|
min-height: 0;
|
|
17058
17064
|
overflow: hidden;
|
|
17059
17065
|
}
|
|
17060
|
-
.cn-chart-widget__fallback[data-v-
|
|
17066
|
+
.cn-chart-widget__fallback[data-v-37644514] {
|
|
17061
17067
|
display: flex;
|
|
17062
17068
|
align-items: center;
|
|
17063
17069
|
justify-content: center;
|
|
17064
17070
|
min-height: 150px;
|
|
17065
17071
|
color: var(--color-text-maxcontrast);
|
|
17066
17072
|
}
|
|
17067
|
-
.cn-chart-widget__html[data-v-
|
|
17073
|
+
.cn-chart-widget__html[data-v-37644514] {
|
|
17068
17074
|
display: flex;
|
|
17069
17075
|
flex-direction: column;
|
|
17070
17076
|
gap: 12px;
|
|
17071
17077
|
}
|
|
17072
|
-
.cn-chart-widget__bar-list[data-v-
|
|
17078
|
+
.cn-chart-widget__bar-list[data-v-37644514] {
|
|
17073
17079
|
display: flex;
|
|
17074
17080
|
flex-direction: column;
|
|
17075
17081
|
gap: 10px;
|
|
@@ -17077,7 +17083,7 @@ li[data-cn-admin-tag] > .action-link::after {
|
|
|
17077
17083
|
margin: 0;
|
|
17078
17084
|
padding: 0;
|
|
17079
17085
|
}
|
|
17080
|
-
.cn-chart-widget__bar-line[data-v-
|
|
17086
|
+
.cn-chart-widget__bar-line[data-v-37644514] {
|
|
17081
17087
|
align-items: center;
|
|
17082
17088
|
background: none;
|
|
17083
17089
|
border: 0;
|
|
@@ -17092,32 +17098,32 @@ li[data-cn-admin-tag] > .action-link::after {
|
|
|
17092
17098
|
text-align: start;
|
|
17093
17099
|
width: 100%;
|
|
17094
17100
|
}
|
|
17095
|
-
.cn-chart-widget__bar-line--drill[data-v-
|
|
17101
|
+
.cn-chart-widget__bar-line--drill[data-v-37644514] {
|
|
17096
17102
|
border-radius: var(--border-radius-small, 4px);
|
|
17097
17103
|
cursor: pointer;
|
|
17098
17104
|
}
|
|
17099
|
-
.cn-chart-widget__bar-line--drill:hover .cn-chart-widget__bar-label[data-v-
|
|
17100
|
-
.cn-chart-widget__bar-line--drill:focus-visible .cn-chart-widget__bar-label[data-v-
|
|
17105
|
+
.cn-chart-widget__bar-line--drill:hover .cn-chart-widget__bar-label[data-v-37644514],
|
|
17106
|
+
.cn-chart-widget__bar-line--drill:focus-visible .cn-chart-widget__bar-label[data-v-37644514] {
|
|
17101
17107
|
text-decoration: underline;
|
|
17102
17108
|
}
|
|
17103
|
-
.cn-chart-widget__bar-label[data-v-
|
|
17109
|
+
.cn-chart-widget__bar-label[data-v-37644514] {
|
|
17104
17110
|
overflow: hidden;
|
|
17105
17111
|
text-overflow: ellipsis;
|
|
17106
17112
|
white-space: nowrap;
|
|
17107
17113
|
}
|
|
17108
|
-
.cn-chart-widget__bar-track[data-v-
|
|
17114
|
+
.cn-chart-widget__bar-track[data-v-37644514] {
|
|
17109
17115
|
background: var(--color-background-dark);
|
|
17110
17116
|
border-radius: 4px;
|
|
17111
17117
|
display: block;
|
|
17112
17118
|
height: 14px;
|
|
17113
17119
|
overflow: hidden;
|
|
17114
17120
|
}
|
|
17115
|
-
.cn-chart-widget__bar-fill[data-v-
|
|
17121
|
+
.cn-chart-widget__bar-fill[data-v-37644514] {
|
|
17116
17122
|
background: var(--color-primary-element);
|
|
17117
17123
|
display: block;
|
|
17118
17124
|
height: 100%;
|
|
17119
17125
|
}
|
|
17120
|
-
.cn-chart-widget__bar-value[data-v-
|
|
17126
|
+
.cn-chart-widget__bar-value[data-v-37644514] {
|
|
17121
17127
|
font-weight: 600;
|
|
17122
17128
|
text-align: end;
|
|
17123
17129
|
white-space: nowrap;
|
|
@@ -17125,10 +17131,10 @@ li[data-cn-admin-tag] > .action-link::after {
|
|
|
17125
17131
|
|
|
17126
17132
|
/* `barValueAtEnd`: no track; the bar takes its share of the row and gives way
|
|
17127
17133
|
to the value right after its end, so the longest bar still fits. */
|
|
17128
|
-
.cn-chart-widget__bar-list--value-at-end .cn-chart-widget__bar-line[data-v-
|
|
17134
|
+
.cn-chart-widget__bar-list--value-at-end .cn-chart-widget__bar-line[data-v-37644514] {
|
|
17129
17135
|
grid-template-columns: minmax(80px, 130px) minmax(0, 1fr);
|
|
17130
17136
|
}
|
|
17131
|
-
.cn-chart-widget__bar-list--value-at-end .cn-chart-widget__bar-track[data-v-
|
|
17137
|
+
.cn-chart-widget__bar-list--value-at-end .cn-chart-widget__bar-track[data-v-37644514] {
|
|
17132
17138
|
align-items: center;
|
|
17133
17139
|
background: none;
|
|
17134
17140
|
display: flex;
|
|
@@ -17136,71 +17142,71 @@ li[data-cn-admin-tag] > .action-link::after {
|
|
|
17136
17142
|
height: auto;
|
|
17137
17143
|
overflow: visible;
|
|
17138
17144
|
}
|
|
17139
|
-
.cn-chart-widget__bar-list--value-at-end .cn-chart-widget__bar-fill[data-v-
|
|
17145
|
+
.cn-chart-widget__bar-list--value-at-end .cn-chart-widget__bar-fill[data-v-37644514] {
|
|
17140
17146
|
border-radius: 4px;
|
|
17141
17147
|
flex: 0 1 auto;
|
|
17142
17148
|
height: 18px;
|
|
17143
17149
|
min-width: 2px;
|
|
17144
17150
|
}
|
|
17145
|
-
.cn-chart-widget__bar-list--value-at-end .cn-chart-widget__bar-value[data-v-
|
|
17151
|
+
.cn-chart-widget__bar-list--value-at-end .cn-chart-widget__bar-value[data-v-37644514] {
|
|
17146
17152
|
flex: none;
|
|
17147
17153
|
text-align: start;
|
|
17148
17154
|
}
|
|
17149
|
-
.cn-chart-widget__footnote[data-v-
|
|
17155
|
+
.cn-chart-widget__footnote[data-v-37644514] {
|
|
17150
17156
|
color: var(--color-text-maxcontrast);
|
|
17151
17157
|
margin: 0;
|
|
17152
17158
|
}
|
|
17153
|
-
.cn-chart-widget__notice[data-v-
|
|
17159
|
+
.cn-chart-widget__notice[data-v-37644514] {
|
|
17154
17160
|
margin: 12px 0 0;
|
|
17155
17161
|
}
|
|
17156
|
-
.cn-chart-widget__notice-text[data-v-
|
|
17162
|
+
.cn-chart-widget__notice-text[data-v-37644514] {
|
|
17157
17163
|
margin: 0;
|
|
17158
17164
|
}
|
|
17159
17165
|
|
|
17160
17166
|
/* Under an ApexCharts plot the footnote is a sibling of the canvas, so it keeps
|
|
17161
17167
|
its own line height while the canvas takes the rest of a fitted column. */
|
|
17162
|
-
.cn-chart-widget > .cn-chart-widget__footnote[data-v-
|
|
17168
|
+
.cn-chart-widget > .cn-chart-widget__footnote[data-v-37644514] {
|
|
17163
17169
|
flex: 0 0 auto;
|
|
17164
17170
|
margin-top: 8px;
|
|
17165
17171
|
}
|
|
17166
|
-
.cn-chart-widget__matrix[data-v-
|
|
17172
|
+
.cn-chart-widget__matrix[data-v-37644514] {
|
|
17167
17173
|
display: grid;
|
|
17168
17174
|
gap: 4px;
|
|
17169
17175
|
}
|
|
17170
|
-
.cn-chart-widget__matrix-row[data-v-
|
|
17176
|
+
.cn-chart-widget__matrix-row[data-v-37644514] {
|
|
17171
17177
|
display: contents;
|
|
17172
17178
|
}
|
|
17173
17179
|
|
|
17174
17180
|
/* A column takes its share of the width (minmax(0, 1fr)), so a long phase
|
|
17175
17181
|
name breaks inside its own column instead of running into the next. */
|
|
17176
|
-
.cn-chart-widget__matrix-head[data-v-
|
|
17182
|
+
.cn-chart-widget__matrix-head[data-v-37644514] {
|
|
17177
17183
|
--nldesign-color-on-surface: var(--color-text-maxcontrast);
|
|
17178
17184
|
color: var(--color-text-maxcontrast);
|
|
17179
17185
|
padding: 6px;
|
|
17180
17186
|
hyphens: auto;
|
|
17181
17187
|
overflow-wrap: anywhere;
|
|
17182
17188
|
}
|
|
17183
|
-
.cn-chart-widget__matrix-label[data-v-
|
|
17189
|
+
.cn-chart-widget__matrix-label[data-v-37644514] {
|
|
17184
17190
|
padding: 10px 6px;
|
|
17185
17191
|
}
|
|
17186
|
-
.cn-chart-widget__matrix-cell[data-v-
|
|
17192
|
+
.cn-chart-widget__matrix-cell[data-v-37644514] {
|
|
17187
17193
|
border-radius: 6px;
|
|
17188
17194
|
padding: 10px;
|
|
17189
17195
|
text-align: center;
|
|
17190
17196
|
}
|
|
17191
|
-
.cn-chart-widget__matrix-cell--success[data-v-
|
|
17197
|
+
.cn-chart-widget__matrix-cell--success[data-v-37644514] {
|
|
17192
17198
|
background: var(--color-success-light, color-mix(in srgb, var(--color-success) 15%, var(--color-main-background)));
|
|
17193
17199
|
}
|
|
17194
|
-
.cn-chart-widget__matrix-cell--warning[data-v-
|
|
17200
|
+
.cn-chart-widget__matrix-cell--warning[data-v-37644514] {
|
|
17195
17201
|
background: var(--color-warning-light, color-mix(in srgb, var(--color-warning) 15%, var(--color-main-background)));
|
|
17196
17202
|
}
|
|
17197
|
-
.cn-chart-widget__matrix-cell--error[data-v-
|
|
17203
|
+
.cn-chart-widget__matrix-cell--error[data-v-37644514] {
|
|
17198
17204
|
background: var(--color-error-light, color-mix(in srgb, var(--color-error) 12%, var(--color-main-background)));
|
|
17199
17205
|
}
|
|
17200
|
-
.cn-chart-widget__matrix-cell--danger[data-v-
|
|
17206
|
+
.cn-chart-widget__matrix-cell--danger[data-v-37644514] {
|
|
17201
17207
|
background: color-mix(in srgb, var(--color-error) 25%, var(--color-main-background));
|
|
17202
17208
|
}
|
|
17203
|
-
.cn-chart-widget__matrix-cell--current[data-v-
|
|
17209
|
+
.cn-chart-widget__matrix-cell--current[data-v-37644514] {
|
|
17204
17210
|
font-weight: 700;
|
|
17205
17211
|
outline: 3px solid var(--color-primary-element);
|
|
17206
17212
|
outline-offset: -3px;
|
|
@@ -17212,26 +17218,26 @@ li[data-cn-admin-tag] > .action-link::after {
|
|
|
17212
17218
|
primary at 35% and at 70%, and level 1 inverted. Levels 1 to 3 take a dark
|
|
17213
17219
|
ink; the board's white on level 3 is about 3.1:1, and 70% is as deep as the
|
|
17214
17220
|
dark ink stays at 4.5:1 in Nextcloud's dark theme. */
|
|
17215
|
-
.cn-chart-widget__matrix-cell--1[data-v-
|
|
17221
|
+
.cn-chart-widget__matrix-cell--1[data-v-37644514] {
|
|
17216
17222
|
--nldesign-color-on-surface: var(--color-primary-element-light-text);
|
|
17217
17223
|
background: var(--color-primary-element-light);
|
|
17218
17224
|
color: var(--color-primary-element-light-text);
|
|
17219
17225
|
}
|
|
17220
|
-
.cn-chart-widget__matrix-cell--2[data-v-
|
|
17226
|
+
.cn-chart-widget__matrix-cell--2[data-v-37644514] {
|
|
17221
17227
|
background: color-mix(in srgb, var(--color-primary-element) 35%, var(--color-main-background));
|
|
17222
17228
|
color: var(--color-main-text);
|
|
17223
17229
|
}
|
|
17224
|
-
.cn-chart-widget__matrix-cell--3[data-v-
|
|
17230
|
+
.cn-chart-widget__matrix-cell--3[data-v-37644514] {
|
|
17225
17231
|
--nldesign-color-on-surface: var(--color-main-text);
|
|
17226
17232
|
background: color-mix(in srgb, var(--color-primary-element) 70%, var(--color-main-background));
|
|
17227
17233
|
color: var(--color-main-text);
|
|
17228
17234
|
}
|
|
17229
|
-
.cn-chart-widget__matrix-cell--4[data-v-
|
|
17235
|
+
.cn-chart-widget__matrix-cell--4[data-v-37644514] {
|
|
17230
17236
|
--nldesign-color-on-surface: var(--color-primary-element-light);
|
|
17231
17237
|
background: var(--color-primary-element-light-text);
|
|
17232
17238
|
color: var(--color-primary-element-light);
|
|
17233
17239
|
}
|
|
17234
|
-
.cn-chart-widget__empty[data-v-
|
|
17240
|
+
.cn-chart-widget__empty[data-v-37644514] {
|
|
17235
17241
|
display: flex;
|
|
17236
17242
|
align-items: center;
|
|
17237
17243
|
justify-content: center;
|
|
@@ -17246,25 +17252,25 @@ li[data-cn-admin-tag] > .action-link::after {
|
|
|
17246
17252
|
content-sized case; in fit mode they take the leftover space and centre in it
|
|
17247
17253
|
instead, since the floor would overflow a short tile. Declared after the base
|
|
17248
17254
|
rules so the cascade order matches specificity (no-descending-specificity). */
|
|
17249
|
-
.cn-chart-widget--fit .cn-chart-widget__empty[data-v-
|
|
17250
|
-
.cn-chart-widget--fit .cn-chart-widget__fallback[data-v-
|
|
17255
|
+
.cn-chart-widget--fit .cn-chart-widget__empty[data-v-37644514],
|
|
17256
|
+
.cn-chart-widget--fit .cn-chart-widget__fallback[data-v-37644514] {
|
|
17251
17257
|
flex: 1 1 0;
|
|
17252
17258
|
min-height: 0;
|
|
17253
17259
|
}
|
|
17254
|
-
.cn-chart-widget__error[data-v-
|
|
17260
|
+
.cn-chart-widget__error[data-v-37644514] {
|
|
17255
17261
|
font-size: 14px;
|
|
17256
17262
|
margin: 0;
|
|
17257
17263
|
}
|
|
17258
17264
|
|
|
17259
17265
|
/* In-widget view switcher pills (views[] prop) — compact segmented row,
|
|
17260
17266
|
mirroring the dashboard date-range pill styling. */
|
|
17261
|
-
.cn-chart-widget__views[data-v-
|
|
17267
|
+
.cn-chart-widget__views[data-v-37644514] {
|
|
17262
17268
|
display: flex;
|
|
17263
17269
|
gap: 4px;
|
|
17264
17270
|
margin-bottom: 6px;
|
|
17265
17271
|
justify-content: flex-end;
|
|
17266
17272
|
}
|
|
17267
|
-
.cn-chart-widget__view-pill[data-v-
|
|
17273
|
+
.cn-chart-widget__view-pill[data-v-37644514] {
|
|
17268
17274
|
padding: 2px 10px;
|
|
17269
17275
|
border: 1px solid var(--color-border);
|
|
17270
17276
|
border-radius: 999px;
|
|
@@ -17273,7 +17279,7 @@ li[data-cn-admin-tag] > .action-link::after {
|
|
|
17273
17279
|
font-size: 12px;
|
|
17274
17280
|
cursor: pointer;
|
|
17275
17281
|
}
|
|
17276
|
-
.cn-chart-widget__view-pill--active[data-v-
|
|
17282
|
+
.cn-chart-widget__view-pill--active[data-v-37644514] {
|
|
17277
17283
|
background: var(--color-primary-element-light, #aad2ed);
|
|
17278
17284
|
color: var(--color-main-text);
|
|
17279
17285
|
border-color: var(--color-primary-element, #0082c9);
|
|
@@ -17302,8 +17308,8 @@ li[data-cn-admin-tag] > .action-link::after {
|
|
|
17302
17308
|
each override below therefore carries one more class than the apexcharts rule
|
|
17303
17309
|
it replaces, and holds even without the scope attribute this block compiles
|
|
17304
17310
|
with. */
|
|
17305
|
-
.cn-chart-widget[data-v-
|
|
17306
|
-
.cn-chart-widget[data-v-
|
|
17311
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-tooltip.apexcharts-theme-light,
|
|
17312
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-tooltip.apexcharts-theme-dark {
|
|
17307
17313
|
background: var(--color-main-background);
|
|
17308
17314
|
border: 1px solid var(--color-border);
|
|
17309
17315
|
color: var(--color-main-text);
|
|
@@ -17312,8 +17318,8 @@ li[data-cn-admin-tag] > .action-link::after {
|
|
|
17312
17318
|
|
|
17313
17319
|
/* The title row is the date/category header ("Aug 26"). Its own background is
|
|
17314
17320
|
what made it unreadable rather than merely off-theme. */
|
|
17315
|
-
.cn-chart-widget[data-v-
|
|
17316
|
-
.cn-chart-widget[data-v-
|
|
17321
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-tooltip.apexcharts-theme-light .apexcharts-tooltip-title,
|
|
17322
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-tooltip.apexcharts-theme-dark .apexcharts-tooltip-title {
|
|
17317
17323
|
background: var(--color-background-hover);
|
|
17318
17324
|
border-bottom: 1px solid var(--color-border);
|
|
17319
17325
|
color: var(--color-main-text);
|
|
@@ -17322,8 +17328,8 @@ li[data-cn-admin-tag] > .action-link::after {
|
|
|
17322
17328
|
/* Crosshair axis tooltips. apexcharts puts light hex values on the BASE class
|
|
17323
17329
|
here and only overrides them for its dark theme, so the base rule has to be
|
|
17324
17330
|
replaced too — not just the theme variants. */
|
|
17325
|
-
.cn-chart-widget[data-v-
|
|
17326
|
-
.cn-chart-widget[data-v-
|
|
17331
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-xaxistooltip,
|
|
17332
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-yaxistooltip {
|
|
17327
17333
|
background: var(--color-main-background);
|
|
17328
17334
|
border: 1px solid var(--color-border);
|
|
17329
17335
|
color: var(--color-main-text);
|
|
@@ -17332,55 +17338,55 @@ li[data-cn-admin-tag] > .action-link::after {
|
|
|
17332
17338
|
/* Their pointer arrows are two stacked triangles: `::before` paints the border
|
|
17333
17339
|
edge, `::after` the fill. Both need the tokens the box above uses, or the
|
|
17334
17340
|
arrow keeps the old palette and reads as a stray light wedge. */
|
|
17335
|
-
.cn-chart-widget[data-v-
|
|
17336
|
-
.cn-chart-widget[data-v-
|
|
17341
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-xaxistooltip-bottom::after,
|
|
17342
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-xaxistooltip-top::after {
|
|
17337
17343
|
border-bottom-color: var(--color-main-background);
|
|
17338
17344
|
border-top-color: var(--color-main-background);
|
|
17339
17345
|
}
|
|
17340
|
-
.cn-chart-widget[data-v-
|
|
17341
|
-
.cn-chart-widget[data-v-
|
|
17346
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-xaxistooltip-bottom::before,
|
|
17347
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-xaxistooltip-top::before {
|
|
17342
17348
|
border-bottom-color: var(--color-border);
|
|
17343
17349
|
border-top-color: var(--color-border);
|
|
17344
17350
|
}
|
|
17345
|
-
.cn-chart-widget[data-v-
|
|
17346
|
-
.cn-chart-widget[data-v-
|
|
17351
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-yaxistooltip-left::after,
|
|
17352
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-yaxistooltip-right::after {
|
|
17347
17353
|
border-left-color: var(--color-main-background);
|
|
17348
17354
|
border-right-color: var(--color-main-background);
|
|
17349
17355
|
}
|
|
17350
|
-
.cn-chart-widget[data-v-
|
|
17351
|
-
.cn-chart-widget[data-v-
|
|
17356
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-yaxistooltip-left::before,
|
|
17357
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-yaxistooltip-right::before {
|
|
17352
17358
|
border-left-color: var(--color-border);
|
|
17353
17359
|
border-right-color: var(--color-border);
|
|
17354
17360
|
}
|
|
17355
17361
|
|
|
17356
17362
|
/* Toolbar menu — only reachable with `toolbar: true`, but the same defect: a
|
|
17357
17363
|
hardcoded `#fff` panel under inherited near-white text. */
|
|
17358
|
-
.cn-chart-widget[data-v-
|
|
17364
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-menu {
|
|
17359
17365
|
background: var(--color-main-background);
|
|
17360
17366
|
border: 1px solid var(--color-border);
|
|
17361
17367
|
color: var(--color-main-text);
|
|
17362
17368
|
}
|
|
17363
|
-
.cn-chart-widget[data-v-
|
|
17369
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-menu .apexcharts-menu-item:hover {
|
|
17364
17370
|
background: var(--color-background-hover);
|
|
17365
17371
|
}
|
|
17366
17372
|
|
|
17367
17373
|
/* Toolbar icons: `#6e8192` at rest and `#333` on hover, i.e. the hover state
|
|
17368
17374
|
vanishes against a dark background. One selector per icon class, matching
|
|
17369
17375
|
apexcharts' own list, to clear its (0,3,1) hover rules. */
|
|
17370
|
-
.cn-chart-widget[data-v-
|
|
17371
|
-
.cn-chart-widget[data-v-
|
|
17372
|
-
.cn-chart-widget[data-v-
|
|
17373
|
-
.cn-chart-widget[data-v-
|
|
17374
|
-
.cn-chart-widget[data-v-
|
|
17375
|
-
.cn-chart-widget[data-v-
|
|
17376
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-toolbar .apexcharts-menu-icon svg,
|
|
17377
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-toolbar .apexcharts-reset-icon svg,
|
|
17378
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-toolbar .apexcharts-selection-icon svg,
|
|
17379
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-toolbar .apexcharts-zoom-icon svg,
|
|
17380
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-toolbar .apexcharts-zoomin-icon svg,
|
|
17381
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-toolbar .apexcharts-zoomout-icon svg {
|
|
17376
17382
|
fill: var(--color-text-maxcontrast);
|
|
17377
17383
|
}
|
|
17378
|
-
.cn-chart-widget[data-v-
|
|
17379
|
-
.cn-chart-widget[data-v-
|
|
17380
|
-
.cn-chart-widget[data-v-
|
|
17381
|
-
.cn-chart-widget[data-v-
|
|
17382
|
-
.cn-chart-widget[data-v-
|
|
17383
|
-
.cn-chart-widget[data-v-
|
|
17384
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-toolbar .apexcharts-menu-icon:hover svg,
|
|
17385
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-toolbar .apexcharts-reset-icon:hover svg,
|
|
17386
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-toolbar .apexcharts-selection-icon:hover svg,
|
|
17387
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-toolbar .apexcharts-zoom-icon:hover svg,
|
|
17388
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-toolbar .apexcharts-zoomin-icon:hover svg,
|
|
17389
|
+
.cn-chart-widget[data-v-37644514] .apexcharts-toolbar .apexcharts-zoomout-icon:hover svg {
|
|
17384
17390
|
fill: var(--color-main-text);
|
|
17385
17391
|
}
|
|
17386
17392
|
|
|
@@ -48554,6 +48560,7 @@ to {
|
|
|
48554
48560
|
|
|
48555
48561
|
.cn-board-view.cn-board-view--board .cn-board-view__card-sub {
|
|
48556
48562
|
font-size: 13px;
|
|
48563
|
+
line-height: normal;
|
|
48557
48564
|
color: var(--color-text-maxcontrast);
|
|
48558
48565
|
}
|
|
48559
48566
|
|
|
@@ -48573,6 +48580,7 @@ to {
|
|
|
48573
48580
|
|
|
48574
48581
|
.cn-board-view.cn-board-view--board .cn-board-view__card-footer {
|
|
48575
48582
|
display: flex;
|
|
48583
|
+
line-height: normal;
|
|
48576
48584
|
align-items: center;
|
|
48577
48585
|
justify-content: space-between;
|
|
48578
48586
|
gap: 8px;
|
|
@@ -61,6 +61,22 @@ function formatLongDate(value, now = new Date(), withYear = false) {
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* How many entries a list holds; with a key, the entries of that list in each
|
|
66
|
+
* of its entries summed (`{sections|count:items}`: the questions of every
|
|
67
|
+
* section of a checklist).
|
|
68
|
+
*
|
|
69
|
+
* @param {Array} list The list.
|
|
70
|
+
* @param {string} key The key of each entry's own list, or '' to count the entries.
|
|
71
|
+
* @return {number} The count.
|
|
72
|
+
*/
|
|
73
|
+
function countEntries(list, key) {
|
|
74
|
+
if (key === '') {
|
|
75
|
+
return list.length
|
|
76
|
+
}
|
|
77
|
+
return list.reduce((sum, entry) => sum + (entry && Array.isArray(entry[key]) ? entry[key].length : 0), 0)
|
|
78
|
+
}
|
|
79
|
+
|
|
64
80
|
/**
|
|
65
81
|
* Format one token value.
|
|
66
82
|
*
|
|
@@ -77,7 +93,7 @@ function formatLongDate(value, now = new Date(), withYear = false) {
|
|
|
77
93
|
function formatValue(value, format, now, arg = '', object = null, config = null, formatters = null) {
|
|
78
94
|
// `{list|count}`: how many entries a list holds, nothing for an empty one.
|
|
79
95
|
if (format === 'count') {
|
|
80
|
-
const count = Array.isArray(value) ? value
|
|
96
|
+
const count = Array.isArray(value) ? countEntries(value, arg) : Number(value);
|
|
81
97
|
return Number.isFinite(count) && count > 0 ? String(count) : ''
|
|
82
98
|
}
|
|
83
99
|
if (value === null || value === undefined || value === '' || typeof value === 'object') {
|
|
@@ -161,7 +177,8 @@ function formatValue(value, format, now, arg = '', object = null, config = null,
|
|
|
161
177
|
* ("vandaag, 09.42"), `{path|ago}` says how long ago it was ("10 min
|
|
162
178
|
* geleden", "vanochtend", "vrijdag"), and `{path|days}` is the
|
|
163
179
|
* whole days since that date ("has waited {createdAt|days} days"),
|
|
164
|
-
* `{path|count}` is how many entries a list holds (nothing for an empty one
|
|
180
|
+
* `{path|count}` is how many entries a list holds (nothing for an empty one;
|
|
181
|
+
* `{path|count:key}` sums the `key` list of each entry),
|
|
165
182
|
* `{path|lower}` writes the value in lower case ("Step 2: {statusLabel|lower}"
|
|
166
183
|
* reads "Step 2: in progress" mid-sentence), and any other name is one of
|
|
167
184
|
* the app's formatters, passed as `context.formatters` (`{id|caseAddress}`).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fillTemplate.js","sources":["../../../src/utils/fillTemplate.js"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 Conduction B.V.\n// SPDX-License-Identifier: EUPL-1.2\n\nimport { translate as t } from '@nextcloud/l10n'\nimport { boardDateLocale, formatAgo, formatBoardDate, formatBoardWeekday, formatRelativeDate, formatTimeOfDay, isoWeek } from './boardDate.js'\nimport { parseDateValue } from './dateVariant.js'\nimport { formatMoney } from './formatMetric.js'\nimport { readPath } from './readPath.js'\nimport { userDisplayName } from './userDisplayName.js'\n\nconst TOKEN = /\\{([^{}\\s|]+)(?:\\|([A-Za-z]+)(?::([^{}\\s]+))?)?\\}/g\n\n/**\n * A moment in the board's short form with the time: \"4 okt, 10:52\", or\n * \"14 feb 2024, 09:00\" outside the current year.\n *\n * @param {unknown} value A Date, a timestamp or an ISO string.\n * @param {Date} [now] The reference moment.\n * @return {string} The text, or '' when the value is not a date.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record\n */\nexport function formatBoardDateTime(value, now = new Date()) {\n\tconst day = formatBoardDate(value, now)\n\tconst date = parseDateValue(value)\n\tif (day === '' || date === null) {\n\t\treturn ''\n\t}\n\tlet time\n\ttry {\n\t\ttime = new Intl.DateTimeFormat(boardDateLocale(), { hour: '2-digit', minute: '2-digit' }).format(date)\n\t} catch {\n\t\ttime = new Intl.DateTimeFormat(undefined, { hour: '2-digit', minute: '2-digit' }).format(date)\n\t}\n\treturn `${day}, ${time}`\n}\n\n/**\n * A date with the month written out: \"7 oktober\", or \"6 oktober 2026\"\n * outside the current year or when `withYear` asks for it.\n *\n * @param {unknown} value A Date, a timestamp or an ISO string.\n * @param {Date} now The reference moment.\n * @param {boolean} [withYear] Always write the year.\n * @return {string} The text, or '' when the value is not a date.\n * @spec openspec/changes/screens-header-meta-tokens/specs/detail-page-board-look/spec.md#requirement-the-header-meta-line-formats-users-long-dates-and-enum-labels\n */\nexport function formatLongDate(value, now = new Date(), withYear = false) {\n\tconst date = parseDateValue(value)\n\tif (date === null) {\n\t\treturn ''\n\t}\n\tconst options = { day: 'numeric', month: 'long' }\n\tif (withYear || date.getFullYear() !== now.getFullYear()) {\n\t\toptions.year = 'numeric'\n\t}\n\ttry {\n\t\treturn new Intl.DateTimeFormat(boardDateLocale(), options).format(date)\n\t} catch {\n\t\treturn new Intl.DateTimeFormat(undefined, options).format(date)\n\t}\n}\n\n/**\n * Format one token value.\n *\n * @param {unknown} value The raw value.\n * @param {string} format `date`, `datetime`, `weekday`, `longdate`, `time`, `user`, `currency`, `relative`, `relativetime`, `ago`, `week`, `days`, `lower`, `mailbox` or '' (as is).\n * @param {Date} now The reference moment.\n * @param {string} [arg] The format's argument (`currency:EUR`, or a row path holding the code).\n * @param {object|null} [object] The row, for a currency read from a field.\n * @param {object|null} [config] The app config map, for the reporting currency.\n * @param {object|null} [formatters] The app's named formatters (`cnFormatters`), for any other format name.\n * @return {string} The text, '' when nothing can be shown.\n * @spec openspec/changes/map-list-formatters/specs/map-list-formatters/spec.md#requirement-a-map-list-reads-display-names-through-the-apps-formatters\n */\nfunction formatValue(value, format, now, arg = '', object = null, config = null, formatters = null) {\n\t// `{list|count}`: how many entries a list holds, nothing for an empty one.\n\tif (format === 'count') {\n\t\tconst count = Array.isArray(value) ? value.length : Number(value)\n\t\treturn Number.isFinite(count) && count > 0 ? String(count) : ''\n\t}\n\tif (value === null || value === undefined || value === '' || typeof value === 'object') {\n\t\treturn ''\n\t}\n\tif (format !== '' && formatters && typeof formatters[format] === 'function') {\n\t\tconst shown = formatters[format](value, object)\n\t\treturn shown === null || shown === undefined ? '' : String(shown)\n\t}\n\tif (format === 'date') {\n\t\treturn formatBoardDate(value, now)\n\t}\n\tif (format === 'datetime') {\n\t\treturn formatBoardDateTime(value, now)\n\t}\n\tif (format === 'weekday') {\n\t\treturn formatBoardWeekday(value, now, { today: arg === 'today' })\n\t}\n\tif (format === 'week') {\n\t\tconst date = parseDateValue(value)\n\t\treturn date === null ? '' : t('nextcloud-vue', 'Week {week}', { week: isoWeek(date) })\n\t}\n\tif (format === 'longdate') {\n\t\treturn formatLongDate(value, now, arg === 'year')\n\t}\n\tif (format === 'user') {\n\t\treturn userDisplayName(value)\n\t}\n\tif (format === 'time') {\n\t\tconst date = parseDateValue(value)\n\t\treturn date === null ? '' : formatTimeOfDay(date)\n\t}\n\tif (format === 'currency') {\n\t\t// `{amount|currency:EUR}` names the code, `{amount|currency:price.code}`\n\t\t// reads it from the row, `{amount|currency}` is the app's reporting\n\t\t// currency (EUR without one).\n\t\tconst isCode = /^[A-Za-z]{3}$/.test(arg)\n\t\treturn formatMoney(value, { currency: isCode ? arg : undefined, currencyField: !isCode && arg ? arg : undefined }, object, config)\n\t}\n\tif (format === 'relative') {\n\t\treturn formatRelativeDate(value, { now })\n\t}\n\tif (format === 'relativetime') {\n\t\treturn formatRelativeDate(value, { now, withTime: true })\n\t}\n\tif (format === 'ago') {\n\t\treturn formatAgo(value, now)\n\t}\n\tif (format === 'mailbox') {\n\t\t// An address up to and with its @ (\"gemeente@\" from gemeente@zuiddrecht.nl).\n\t\tconst text = String(value)\n\t\tconst at = text.indexOf('@')\n\t\treturn at > 0 ? text.slice(0, at + 1) : text\n\t}\n\tif (format === 'lower') {\n\t\treturn String(value).toLocaleLowerCase()\n\t}\n\tif (format === 'days') {\n\t\t// Whole days from the date to now (0 for today; negative for a future date).\n\t\tconst date = parseDateValue(value)\n\t\treturn date === null ? '' : String(Math.floor((now.getTime() - date.getTime()) / 86400000))\n\t}\n\treturn String(value)\n}\n\n/**\n * Fill a row template such as `\"{identifier} · {caseTypeName}\"` or\n * `\"Termijn {deadline|date}\"` from an object.\n *\n * Every `{path}` token is a dot path into the object; `{path|date}` and\n * `{path|datetime}` format it as the boards write dates, `{path|weekday}` adds\n * the short weekday (\"wo 7 okt\"; `{path|weekday:today}` reads \"Vandaag, wo 7 okt\"\n * for today), `{path|longdate}` writes the month out (\"7 oktober\";\n * `{path|longdate:year}` always adds the year), `{path|time}` is the time\n * of day (\"08:45\"), `{path|user}` is a user id's display name (looked up\n * once, '' until it arrives), `{path|currency}`,\n * `{path|currency:EUR}` and `{path|currency:field}` write an amount in a\n * currency (a code, or the row field that holds it; without either, or when\n * the row field is empty, the app's reporting currency), `{path|relative}` and\n * `{path|relativetime}` say \"today\", \"yesterday\" and then the short date\n * (\"vandaag, 09.42\"), `{path|ago}` says how long ago it was (\"10 min\n * geleden\", \"vanochtend\", \"vrijdag\"), and `{path|days}` is the\n * whole days since that date (\"has waited {createdAt|days} days\"),\n * `{path|count}` is how many entries a list holds (nothing for an empty one),\n * `{path|lower}` writes the value in lower case (\"Step 2: {statusLabel|lower}\"\n * reads \"Step 2: in progress\" mid-sentence), and any other name is one of\n * the app's formatters, passed as `context.formatters` (`{id|caseAddress}`).\n * A token without a\n * value empties the WHOLE template, because \"Termijn \" is worse than nothing.\n * A template without tokens is returned as written (a constant label).\n *\n * @param {string} template The template, already translated.\n * @param {object|null} object The object to read.\n * @param {Date} [now] The reference moment for dates.\n * @param {{config?: object, formatters?: object}|null} [context] The app config map, for the reporting currency of a money token, and the app's named formatters.\n * @return {string} The filled text, or ''.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record\n */\nexport function fillTemplate(template, object, now = new Date(), context = null) {\n\tif (typeof template !== 'string' || template.trim() === '') {\n\t\treturn ''\n\t}\n\tlet missing = false\n\tconst text = template.replace(TOKEN, (_match, path, format, arg) => {\n\t\tconst value = object && typeof object === 'object' ? readPath(object, path) : undefined\n\t\tconst shown = formatValue(value, format || '', now, arg || '', object, context && context.config ? context.config : null, context && context.formatters ? context.formatters : null)\n\t\tif (shown === '') {\n\t\t\tmissing = true\n\t\t}\n\t\treturn shown\n\t})\n\treturn missing ? '' : text.trim()\n}\n\n/**\n * The first of several templates that fills: `[\"Klaar {closedAt|date}\",\n * \"Termijn {deadline|date}\"]` reads \"Klaar ...\" once the case is closed and\n * \"Termijn ...\" while it is open. A single string is one candidate.\n *\n * @param {string|string[]} templates The candidates, in order.\n * @param {object|null} object The object to read.\n * @param {(key: string) => string} [translate] Applied to each template before filling.\n * @param {Date} [now] The reference moment for dates.\n * @param {{config?: object}|null} [context] Passed on to `fillTemplate`.\n * @return {string} The first filled text, or ''.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record\n */\nexport function firstFilled(templates, object, translate = (key) => key, now = new Date(), context = null) {\n\tconst list = Array.isArray(templates) ? templates : [templates]\n\tfor (const template of list) {\n\t\tif (typeof template !== 'string') {\n\t\t\tcontinue\n\t\t}\n\t\tconst text = fillTemplate(translate(template), object, now, context)\n\t\tif (text !== '') {\n\t\t\treturn text\n\t\t}\n\t}\n\treturn ''\n}\n\n/**\n * Fill a template such as `\"{identifier} · {requester}\"` field by field\n * (a table column's `secondary` line, a detail page's meta line).\n *\n * A field without a value takes its separator with it: the text between two\n * fields belongs to the field after it, so an empty requester leaves\n * \"2026-0002\", not \"2026-0002 ·\", and an empty first field does not leave a\n * leading separator either. Text before the first field and after the last\n * one is kept as written. A template whose fields all have values fills\n * exactly as before.\n *\n * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-an-empty-field-takes-its-separator-with-it\n * @param {string} template The template, with `{field}` placeholders.\n * @param {(key: string) => string} valueOf The text of a field ('' when empty).\n * @return {string} The filled template.\n */\nexport function joinTemplateParts(template, valueOf) {\n\t// split() with a capture group alternates literal, field, literal, …\n\tconst parts = template.split(/\\{([^}]+)\\}/)\n\tconst lead = parts[0]\n\tconst tail = parts[parts.length - 1]\n\tlet body = ''\n\tfor (let i = 1; i < parts.length; i += 2) {\n\t\tconst value = valueOf(parts[i].trim())\n\t\tif (value === '') {\n\t\t\tcontinue\n\t\t}\n\t\t// The literal before this field: the separator, unless nothing has\n\t\t// been written yet (then there is nothing to separate from).\n\t\tconst separator = i === 1 ? '' : parts[i - 1]\n\t\tbody += (body === '' ? '' : separator) + value\n\t}\n\treturn lead + body + tail\n}\n"],"names":["t"],"mappings":";;;;;;;AAAA;AACA;AACA;AAOA;AACA,MAAM,KAAK,GAAG,qDAAoD;AAClE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,mBAAmB,CAAC,KAAK,EAAE,GAAG,GAAG,IAAI,IAAI,EAAE,EAAE;AAC7D,CAAC,MAAM,GAAG,GAAG,eAAe,CAAC,KAAK,EAAE,GAAG,EAAC;AACxC,CAAC,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,EAAC;AACnC,CAAC,IAAI,GAAG,KAAK,EAAE,IAAI,IAAI,KAAK,IAAI,EAAE;AAClC,EAAE,OAAO,EAAE;AACX,EAAE;AACF,CAAC,IAAI,KAAI;AACT,CAAC,IAAI;AACL,EAAE,IAAI,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,eAAe,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,EAAC;AACxG,EAAE,CAAC,MAAM;AACT,EAAE,IAAI,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,EAAC;AAChG,EAAE;AACF,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;AACzB,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,cAAc,CAAC,KAAK,EAAE,GAAG,GAAG,IAAI,IAAI,EAAE,EAAE,QAAQ,GAAG,KAAK,EAAE;AAC1E,CAAC,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,EAAC;AACnC,CAAC,IAAI,IAAI,KAAK,IAAI,EAAE;AACpB,EAAE,OAAO,EAAE;AACX,EAAE;AACF,CAAC,MAAM,OAAO,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAE;AAClD,CAAC,IAAI,QAAQ,IAAI,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,WAAW,EAAE,EAAE;AAC3D,EAAE,OAAO,CAAC,IAAI,GAAG,UAAS;AAC1B,EAAE;AACF,CAAC,IAAI;AACL,EAAE,OAAO,IAAI,IAAI,CAAC,cAAc,CAAC,eAAe,EAAE,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;AACzE,EAAE,CAAC,MAAM;AACT,EAAE,OAAO,IAAI,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;AACjE,EAAE;AACF,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,EAAE,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,UAAU,GAAG,IAAI,EAAE;AACpG;AACA,CAAC,IAAI,MAAM,KAAK,OAAO,EAAE;AACzB,EAAE,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,KAAK,EAAC;AACnE,EAAE,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE;AACjE,EAAE;AACF,CAAC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;AACzF,EAAE,OAAO,EAAE;AACX,EAAE;AACF,CAAC,IAAI,MAAM,KAAK,EAAE,IAAI,UAAU,IAAI,OAAO,UAAU,CAAC,MAAM,CAAC,KAAK,UAAU,EAAE;AAC9E,EAAE,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,MAAM,EAAC;AACjD,EAAE,OAAO,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,GAAG,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC;AACnE,EAAE;AACF,CAAC,IAAI,MAAM,KAAK,MAAM,EAAE;AACxB,EAAE,OAAO,eAAe,CAAC,KAAK,EAAE,GAAG,CAAC;AACpC,EAAE;AACF,CAAC,IAAI,MAAM,KAAK,UAAU,EAAE;AAC5B,EAAE,OAAO,mBAAmB,CAAC,KAAK,EAAE,GAAG,CAAC;AACxC,EAAE;AACF,CAAC,IAAI,MAAM,KAAK,SAAS,EAAE;AAC3B,EAAE,OAAO,kBAAkB,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,GAAG,KAAK,OAAO,EAAE,CAAC;AACnE,EAAE;AACF,CAAC,IAAI,MAAM,KAAK,MAAM,EAAE;AACxB,EAAE,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,EAAC;AACpC,EAAE,OAAO,IAAI,KAAK,IAAI,GAAG,EAAE,GAAGA,SAAC,CAAC,eAAe,EAAE,aAAa,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;AACxF,EAAE;AACF,CAAC,IAAI,MAAM,KAAK,UAAU,EAAE;AAC5B,EAAE,OAAO,cAAc,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,KAAK,MAAM,CAAC;AACnD,EAAE;AACF,CAAC,IAAI,MAAM,KAAK,MAAM,EAAE;AACxB,EAAE,OAAO,eAAe,CAAC,KAAK,CAAC;AAC/B,EAAE;AACF,CAAC,IAAI,MAAM,KAAK,MAAM,EAAE;AACxB,EAAE,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,EAAC;AACpC,EAAE,OAAO,IAAI,KAAK,IAAI,GAAG,EAAE,GAAG,eAAe,CAAC,IAAI,CAAC;AACnD,EAAE;AACF,CAAC,IAAI,MAAM,KAAK,UAAU,EAAE;AAC5B;AACA;AACA;AACA,EAAE,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,GAAG,EAAC;AAC1C,EAAE,OAAO,WAAW,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,GAAG,GAAG,SAAS,EAAE,aAAa,EAAE,CAAC,MAAM,IAAI,GAAG,GAAG,GAAG,GAAG,SAAS,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC;AACpI,EAAE;AACF,CAAC,IAAI,MAAM,KAAK,UAAU,EAAE;AAC5B,EAAE,OAAO,kBAAkB,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,CAAC;AAC3C,EAAE;AACF,CAAC,IAAI,MAAM,KAAK,cAAc,EAAE;AAChC,EAAE,OAAO,kBAAkB,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAC3D,EAAE;AACF,CAAC,IAAI,MAAM,KAAK,KAAK,EAAE;AACvB,EAAE,OAAO,SAAS,CAAC,KAAK,EAAE,GAAG,CAAC;AAC9B,EAAE;AACF,CAAC,IAAI,MAAM,KAAK,SAAS,EAAE;AAC3B;AACA,EAAE,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,EAAC;AAC5B,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAC;AAC9B,EAAE,OAAO,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,GAAG,IAAI;AAC9C,EAAE;AACF,CAAC,IAAI,MAAM,KAAK,OAAO,EAAE;AACzB,EAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,iBAAiB,EAAE;AAC1C,EAAE;AACF,CAAC,IAAI,MAAM,KAAK,MAAM,EAAE;AACxB;AACA,EAAE,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,EAAC;AACpC,EAAE,OAAO,IAAI,KAAK,IAAI,GAAG,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,QAAQ,CAAC,CAAC;AAC7F,EAAE;AACF,CAAC,OAAO,MAAM,CAAC,KAAK,CAAC;AACrB,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI,IAAI,EAAE,EAAE,OAAO,GAAG,IAAI,EAAE;AACjF,CAAC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;AAC7D,EAAE,OAAO,EAAE;AACX,EAAE;AACF,CAAC,IAAI,OAAO,GAAG,MAAK;AACpB,CAAC,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK;AACrE,EAAE,MAAM,KAAK,GAAG,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,GAAG,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,UAAS;AACzF,EAAE,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,EAAE,MAAM,IAAI,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,EAAE,MAAM,EAAE,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,EAAE,OAAO,IAAI,OAAO,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,EAAC;AACtL,EAAE,IAAI,KAAK,KAAK,EAAE,EAAE;AACpB,GAAG,OAAO,GAAG,KAAI;AACjB,GAAG;AACH,EAAE,OAAO,KAAK;AACd,EAAE,EAAC;AACH,CAAC,OAAO,OAAO,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE;AAClC,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,GAAG,CAAC,GAAG,KAAK,GAAG,EAAE,GAAG,GAAG,IAAI,IAAI,EAAE,EAAE,OAAO,GAAG,IAAI,EAAE;AAC3G,CAAC,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS,GAAG,CAAC,SAAS,EAAC;AAChE,CAAC,KAAK,MAAM,QAAQ,IAAI,IAAI,EAAE;AAC9B,EAAE,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE;AACpC,GAAG,QAAQ;AACX,GAAG;AACH,EAAE,MAAM,IAAI,GAAG,YAAY,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAC;AACtE,EAAE,IAAI,IAAI,KAAK,EAAE,EAAE;AACnB,GAAG,OAAO,IAAI;AACd,GAAG;AACH,EAAE;AACF,CAAC,OAAO,EAAE;AACV,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,iBAAiB,CAAC,QAAQ,EAAE,OAAO,EAAE;AACrD;AACA,CAAC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,aAAa,EAAC;AAC5C,CAAC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,EAAC;AACtB,CAAC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAC;AACrC,CAAC,IAAI,IAAI,GAAG,GAAE;AACd,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE;AAC3C,EAAE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAC;AACxC,EAAE,IAAI,KAAK,KAAK,EAAE,EAAE;AACpB,GAAG,QAAQ;AACX,GAAG;AACH;AACA;AACA,EAAE,MAAM,SAAS,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,EAAC;AAC/C,EAAE,IAAI,IAAI,CAAC,IAAI,KAAK,EAAE,GAAG,EAAE,GAAG,SAAS,IAAI,MAAK;AAChD,EAAE;AACF,CAAC,OAAO,IAAI,GAAG,IAAI,GAAG,IAAI;AAC1B;;;;"}
|
|
1
|
+
{"version":3,"file":"fillTemplate.js","sources":["../../../src/utils/fillTemplate.js"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 Conduction B.V.\n// SPDX-License-Identifier: EUPL-1.2\n\nimport { translate as t } from '@nextcloud/l10n'\nimport { boardDateLocale, formatAgo, formatBoardDate, formatBoardWeekday, formatRelativeDate, formatTimeOfDay, isoWeek } from './boardDate.js'\nimport { parseDateValue } from './dateVariant.js'\nimport { formatMoney } from './formatMetric.js'\nimport { readPath } from './readPath.js'\nimport { userDisplayName } from './userDisplayName.js'\n\nconst TOKEN = /\\{([^{}\\s|]+)(?:\\|([A-Za-z]+)(?::([^{}\\s]+))?)?\\}/g\n\n/**\n * A moment in the board's short form with the time: \"4 okt, 10:52\", or\n * \"14 feb 2024, 09:00\" outside the current year.\n *\n * @param {unknown} value A Date, a timestamp or an ISO string.\n * @param {Date} [now] The reference moment.\n * @return {string} The text, or '' when the value is not a date.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record\n */\nexport function formatBoardDateTime(value, now = new Date()) {\n\tconst day = formatBoardDate(value, now)\n\tconst date = parseDateValue(value)\n\tif (day === '' || date === null) {\n\t\treturn ''\n\t}\n\tlet time\n\ttry {\n\t\ttime = new Intl.DateTimeFormat(boardDateLocale(), { hour: '2-digit', minute: '2-digit' }).format(date)\n\t} catch {\n\t\ttime = new Intl.DateTimeFormat(undefined, { hour: '2-digit', minute: '2-digit' }).format(date)\n\t}\n\treturn `${day}, ${time}`\n}\n\n/**\n * A date with the month written out: \"7 oktober\", or \"6 oktober 2026\"\n * outside the current year or when `withYear` asks for it.\n *\n * @param {unknown} value A Date, a timestamp or an ISO string.\n * @param {Date} now The reference moment.\n * @param {boolean} [withYear] Always write the year.\n * @return {string} The text, or '' when the value is not a date.\n * @spec openspec/changes/screens-header-meta-tokens/specs/detail-page-board-look/spec.md#requirement-the-header-meta-line-formats-users-long-dates-and-enum-labels\n */\nexport function formatLongDate(value, now = new Date(), withYear = false) {\n\tconst date = parseDateValue(value)\n\tif (date === null) {\n\t\treturn ''\n\t}\n\tconst options = { day: 'numeric', month: 'long' }\n\tif (withYear || date.getFullYear() !== now.getFullYear()) {\n\t\toptions.year = 'numeric'\n\t}\n\ttry {\n\t\treturn new Intl.DateTimeFormat(boardDateLocale(), options).format(date)\n\t} catch {\n\t\treturn new Intl.DateTimeFormat(undefined, options).format(date)\n\t}\n}\n\n/**\n * How many entries a list holds; with a key, the entries of that list in each\n * of its entries summed (`{sections|count:items}`: the questions of every\n * section of a checklist).\n *\n * @param {Array} list The list.\n * @param {string} key The key of each entry's own list, or '' to count the entries.\n * @return {number} The count.\n */\nfunction countEntries(list, key) {\n\tif (key === '') {\n\t\treturn list.length\n\t}\n\treturn list.reduce((sum, entry) => sum + (entry && Array.isArray(entry[key]) ? entry[key].length : 0), 0)\n}\n\n/**\n * Format one token value.\n *\n * @param {unknown} value The raw value.\n * @param {string} format `date`, `datetime`, `weekday`, `longdate`, `time`, `user`, `currency`, `relative`, `relativetime`, `ago`, `week`, `days`, `lower`, `mailbox` or '' (as is).\n * @param {Date} now The reference moment.\n * @param {string} [arg] The format's argument (`currency:EUR`, or a row path holding the code).\n * @param {object|null} [object] The row, for a currency read from a field.\n * @param {object|null} [config] The app config map, for the reporting currency.\n * @param {object|null} [formatters] The app's named formatters (`cnFormatters`), for any other format name.\n * @return {string} The text, '' when nothing can be shown.\n * @spec openspec/changes/map-list-formatters/specs/map-list-formatters/spec.md#requirement-a-map-list-reads-display-names-through-the-apps-formatters\n */\nfunction formatValue(value, format, now, arg = '', object = null, config = null, formatters = null) {\n\t// `{list|count}`: how many entries a list holds, nothing for an empty one.\n\tif (format === 'count') {\n\t\tconst count = Array.isArray(value) ? countEntries(value, arg) : Number(value)\n\t\treturn Number.isFinite(count) && count > 0 ? String(count) : ''\n\t}\n\tif (value === null || value === undefined || value === '' || typeof value === 'object') {\n\t\treturn ''\n\t}\n\tif (format !== '' && formatters && typeof formatters[format] === 'function') {\n\t\tconst shown = formatters[format](value, object)\n\t\treturn shown === null || shown === undefined ? '' : String(shown)\n\t}\n\tif (format === 'date') {\n\t\treturn formatBoardDate(value, now)\n\t}\n\tif (format === 'datetime') {\n\t\treturn formatBoardDateTime(value, now)\n\t}\n\tif (format === 'weekday') {\n\t\treturn formatBoardWeekday(value, now, { today: arg === 'today' })\n\t}\n\tif (format === 'week') {\n\t\tconst date = parseDateValue(value)\n\t\treturn date === null ? '' : t('nextcloud-vue', 'Week {week}', { week: isoWeek(date) })\n\t}\n\tif (format === 'longdate') {\n\t\treturn formatLongDate(value, now, arg === 'year')\n\t}\n\tif (format === 'user') {\n\t\treturn userDisplayName(value)\n\t}\n\tif (format === 'time') {\n\t\tconst date = parseDateValue(value)\n\t\treturn date === null ? '' : formatTimeOfDay(date)\n\t}\n\tif (format === 'currency') {\n\t\t// `{amount|currency:EUR}` names the code, `{amount|currency:price.code}`\n\t\t// reads it from the row, `{amount|currency}` is the app's reporting\n\t\t// currency (EUR without one).\n\t\tconst isCode = /^[A-Za-z]{3}$/.test(arg)\n\t\treturn formatMoney(value, { currency: isCode ? arg : undefined, currencyField: !isCode && arg ? arg : undefined }, object, config)\n\t}\n\tif (format === 'relative') {\n\t\treturn formatRelativeDate(value, { now })\n\t}\n\tif (format === 'relativetime') {\n\t\treturn formatRelativeDate(value, { now, withTime: true })\n\t}\n\tif (format === 'ago') {\n\t\treturn formatAgo(value, now)\n\t}\n\tif (format === 'mailbox') {\n\t\t// An address up to and with its @ (\"gemeente@\" from gemeente@zuiddrecht.nl).\n\t\tconst text = String(value)\n\t\tconst at = text.indexOf('@')\n\t\treturn at > 0 ? text.slice(0, at + 1) : text\n\t}\n\tif (format === 'lower') {\n\t\treturn String(value).toLocaleLowerCase()\n\t}\n\tif (format === 'days') {\n\t\t// Whole days from the date to now (0 for today; negative for a future date).\n\t\tconst date = parseDateValue(value)\n\t\treturn date === null ? '' : String(Math.floor((now.getTime() - date.getTime()) / 86400000))\n\t}\n\treturn String(value)\n}\n\n/**\n * Fill a row template such as `\"{identifier} · {caseTypeName}\"` or\n * `\"Termijn {deadline|date}\"` from an object.\n *\n * Every `{path}` token is a dot path into the object; `{path|date}` and\n * `{path|datetime}` format it as the boards write dates, `{path|weekday}` adds\n * the short weekday (\"wo 7 okt\"; `{path|weekday:today}` reads \"Vandaag, wo 7 okt\"\n * for today), `{path|longdate}` writes the month out (\"7 oktober\";\n * `{path|longdate:year}` always adds the year), `{path|time}` is the time\n * of day (\"08:45\"), `{path|user}` is a user id's display name (looked up\n * once, '' until it arrives), `{path|currency}`,\n * `{path|currency:EUR}` and `{path|currency:field}` write an amount in a\n * currency (a code, or the row field that holds it; without either, or when\n * the row field is empty, the app's reporting currency), `{path|relative}` and\n * `{path|relativetime}` say \"today\", \"yesterday\" and then the short date\n * (\"vandaag, 09.42\"), `{path|ago}` says how long ago it was (\"10 min\n * geleden\", \"vanochtend\", \"vrijdag\"), and `{path|days}` is the\n * whole days since that date (\"has waited {createdAt|days} days\"),\n * `{path|count}` is how many entries a list holds (nothing for an empty one;\n * `{path|count:key}` sums the `key` list of each entry),\n * `{path|lower}` writes the value in lower case (\"Step 2: {statusLabel|lower}\"\n * reads \"Step 2: in progress\" mid-sentence), and any other name is one of\n * the app's formatters, passed as `context.formatters` (`{id|caseAddress}`).\n * A token without a\n * value empties the WHOLE template, because \"Termijn \" is worse than nothing.\n * A template without tokens is returned as written (a constant label).\n *\n * @param {string} template The template, already translated.\n * @param {object|null} object The object to read.\n * @param {Date} [now] The reference moment for dates.\n * @param {{config?: object, formatters?: object}|null} [context] The app config map, for the reporting currency of a money token, and the app's named formatters.\n * @return {string} The filled text, or ''.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record\n */\nexport function fillTemplate(template, object, now = new Date(), context = null) {\n\tif (typeof template !== 'string' || template.trim() === '') {\n\t\treturn ''\n\t}\n\tlet missing = false\n\tconst text = template.replace(TOKEN, (_match, path, format, arg) => {\n\t\tconst value = object && typeof object === 'object' ? readPath(object, path) : undefined\n\t\tconst shown = formatValue(value, format || '', now, arg || '', object, context && context.config ? context.config : null, context && context.formatters ? context.formatters : null)\n\t\tif (shown === '') {\n\t\t\tmissing = true\n\t\t}\n\t\treturn shown\n\t})\n\treturn missing ? '' : text.trim()\n}\n\n/**\n * The first of several templates that fills: `[\"Klaar {closedAt|date}\",\n * \"Termijn {deadline|date}\"]` reads \"Klaar ...\" once the case is closed and\n * \"Termijn ...\" while it is open. A single string is one candidate.\n *\n * @param {string|string[]} templates The candidates, in order.\n * @param {object|null} object The object to read.\n * @param {(key: string) => string} [translate] Applied to each template before filling.\n * @param {Date} [now] The reference moment for dates.\n * @param {{config?: object}|null} [context] Passed on to `fillTemplate`.\n * @return {string} The first filled text, or ''.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record\n */\nexport function firstFilled(templates, object, translate = (key) => key, now = new Date(), context = null) {\n\tconst list = Array.isArray(templates) ? templates : [templates]\n\tfor (const template of list) {\n\t\tif (typeof template !== 'string') {\n\t\t\tcontinue\n\t\t}\n\t\tconst text = fillTemplate(translate(template), object, now, context)\n\t\tif (text !== '') {\n\t\t\treturn text\n\t\t}\n\t}\n\treturn ''\n}\n\n/**\n * Fill a template such as `\"{identifier} · {requester}\"` field by field\n * (a table column's `secondary` line, a detail page's meta line).\n *\n * A field without a value takes its separator with it: the text between two\n * fields belongs to the field after it, so an empty requester leaves\n * \"2026-0002\", not \"2026-0002 ·\", and an empty first field does not leave a\n * leading separator either. Text before the first field and after the last\n * one is kept as written. A template whose fields all have values fills\n * exactly as before.\n *\n * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-an-empty-field-takes-its-separator-with-it\n * @param {string} template The template, with `{field}` placeholders.\n * @param {(key: string) => string} valueOf The text of a field ('' when empty).\n * @return {string} The filled template.\n */\nexport function joinTemplateParts(template, valueOf) {\n\t// split() with a capture group alternates literal, field, literal, …\n\tconst parts = template.split(/\\{([^}]+)\\}/)\n\tconst lead = parts[0]\n\tconst tail = parts[parts.length - 1]\n\tlet body = ''\n\tfor (let i = 1; i < parts.length; i += 2) {\n\t\tconst value = valueOf(parts[i].trim())\n\t\tif (value === '') {\n\t\t\tcontinue\n\t\t}\n\t\t// The literal before this field: the separator, unless nothing has\n\t\t// been written yet (then there is nothing to separate from).\n\t\tconst separator = i === 1 ? '' : parts[i - 1]\n\t\tbody += (body === '' ? '' : separator) + value\n\t}\n\treturn lead + body + tail\n}\n"],"names":["t"],"mappings":"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"}
|