@conduction/nextcloud-vue 2.56.0-beta.35 → 2.56.0-beta.36
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/CnDashboardGrid/CnDashboardGrid.vue.js +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js +114 -0
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js +2 -2
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue.js +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js +95 -0
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js +24 -2
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue.js +1 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js +2 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue3.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +120 -65
- package/dist/esm/utils/gridStack.js +40 -1
- package/dist/esm/utils/gridStack.js.map +1 -1
- package/dist/esm/utils/humaniseKey.js +41 -0
- package/dist/esm/utils/humaniseKey.js.map +1 -0
- package/dist/esm/utils/notificationRuleLabel.js +6 -23
- package/dist/esm/utils/notificationRuleLabel.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +546 -256
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +120 -65
- package/package.json +1 -1
- package/src/components/CnDashboardGrid/CnDashboardGrid.vue +116 -2
- package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +123 -3
- package/src/components/CnWeekStripWidget/CnWeekStripWidget.vue +2 -1
- package/src/css/kpi-card.css +13 -0
- package/src/css/look-board.css +19 -0
- package/src/index.js +2 -0
- package/src/utils/gridStack.js +39 -0
- package/src/utils/humaniseKey.js +38 -0
- package/src/utils/notificationRuleLabel.js +5 -23
|
@@ -781,6 +781,25 @@
|
|
|
781
781
|
.cn-look-board .cn-dashboard-page:not(.cn-look-nextcloud *) .cn-record-list__chevron {
|
|
782
782
|
display: none;
|
|
783
783
|
}
|
|
784
|
+
|
|
785
|
+
/* ---- Week strip (DqMijnWerk "Termijnen deze week") ---------------------- */
|
|
786
|
+
/* The days wrap inside the card instead of scrolling sideways: a row holds
|
|
787
|
+
as many days of at least 120px as the card fits, and the rest go to the
|
|
788
|
+
next row (dossiq at 1100 wide held 680px of days in a 494px card and
|
|
789
|
+
scrolled; now three days and two). 120px keeps a case title's words whole;
|
|
790
|
+
five 88px days fitted one row but broke "Bezwaar" mid-word. A card wide
|
|
791
|
+
enough for the week draws it as before, one row of equal columns. The
|
|
792
|
+
day's head wraps its "today" badge under the label when the day is
|
|
793
|
+
narrow.
|
|
794
|
+
@spec openspec/changes/board-week-strip-fits/specs/week-strip-board-look/spec.md#requirement-a-board-week-strip-fits-its-card */
|
|
795
|
+
.cn-look-board .cn-week-strip:not(.cn-look-nextcloud *) .cn-week-strip__days {
|
|
796
|
+
grid-template-columns: repeat(auto-fit, minmax(min(100%, 120px), 1fr));
|
|
797
|
+
min-width: 0;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
.cn-look-board .cn-week-strip:not(.cn-look-nextcloud *) .cn-week-strip__head {
|
|
801
|
+
flex-wrap: wrap;
|
|
802
|
+
}
|
|
784
803
|
/*
|
|
785
804
|
* The board dialog (screens-dialog-parity, canon section 5).
|
|
786
805
|
*
|
|
@@ -3949,6 +3968,19 @@ a.cn-kpi-card--stacked:focus-visible {
|
|
|
3949
3968
|
color: var(--color-main-text);
|
|
3950
3969
|
}
|
|
3951
3970
|
|
|
3971
|
+
/* A board tile whose label wraps grows into its cell instead of clipping its
|
|
3972
|
+
own last line. dossiq's "Afgehandeld deze maand" at 1100 wide held 129px of
|
|
3973
|
+
content in a 114px tile and lost its caption's bottom. The tile still fills
|
|
3974
|
+
a cell it fits (`min-height: 100%`), so every tile that fits keeps its
|
|
3975
|
+
size; one that does not takes the height of its content, and the card-fit
|
|
3976
|
+
cell's `safe center` keeps its top in view.
|
|
3977
|
+
@spec openspec/changes/board-stat-tile-grows/specs/stat-tile-board-look/spec.md#requirement-a-board-stat-tile-shows-all-its-lines */
|
|
3978
|
+
.cn-kpi-card--board.cn-kpi-card--stacked {
|
|
3979
|
+
flex: 0 0 auto;
|
|
3980
|
+
height: auto;
|
|
3981
|
+
min-height: 100%;
|
|
3982
|
+
}
|
|
3983
|
+
|
|
3952
3984
|
.cn-kpi-card--board.cn-kpi-card--stacked .cn-kpi-card__caption-line,
|
|
3953
3985
|
.cn-kpi-card--board.cn-kpi-card--stacked .cn-stats-block__count-label {
|
|
3954
3986
|
font-size: 13px;
|
|
@@ -22309,6 +22341,19 @@ a.cn-kpi-card--stacked:focus-visible {
|
|
|
22309
22341
|
color: var(--color-main-text);
|
|
22310
22342
|
}
|
|
22311
22343
|
|
|
22344
|
+
/* A board tile whose label wraps grows into its cell instead of clipping its
|
|
22345
|
+
own last line. dossiq's "Afgehandeld deze maand" at 1100 wide held 129px of
|
|
22346
|
+
content in a 114px tile and lost its caption's bottom. The tile still fills
|
|
22347
|
+
a cell it fits (`min-height: 100%`), so every tile that fits keeps its
|
|
22348
|
+
size; one that does not takes the height of its content, and the card-fit
|
|
22349
|
+
cell's `safe center` keeps its top in view.
|
|
22350
|
+
@spec openspec/changes/board-stat-tile-grows/specs/stat-tile-board-look/spec.md#requirement-a-board-stat-tile-shows-all-its-lines */
|
|
22351
|
+
.cn-kpi-card--board.cn-kpi-card--stacked {
|
|
22352
|
+
flex: 0 0 auto;
|
|
22353
|
+
height: auto;
|
|
22354
|
+
min-height: 100%;
|
|
22355
|
+
}
|
|
22356
|
+
|
|
22312
22357
|
.cn-kpi-card--board.cn-kpi-card--stacked .cn-kpi-card__caption-line,
|
|
22313
22358
|
.cn-kpi-card--board.cn-kpi-card--stacked .cn-stats-block__count-label {
|
|
22314
22359
|
font-size: 13px;
|
|
@@ -23136,7 +23181,7 @@ a.cn-kpi-card--board * {
|
|
|
23136
23181
|
}
|
|
23137
23182
|
|
|
23138
23183
|
|
|
23139
|
-
.cn-object-data-widget__image[data-v-
|
|
23184
|
+
.cn-object-data-widget__image[data-v-324e1a00] {
|
|
23140
23185
|
max-width: 100%;
|
|
23141
23186
|
max-height: 160px;
|
|
23142
23187
|
border-radius: 8px;
|
|
@@ -23144,33 +23189,33 @@ a.cn-kpi-card--board * {
|
|
|
23144
23189
|
}
|
|
23145
23190
|
|
|
23146
23191
|
/* Shimmering placeholder while a relation's display name resolves. */
|
|
23147
|
-
.cn-object-data-widget__skeleton[data-v-
|
|
23192
|
+
.cn-object-data-widget__skeleton[data-v-324e1a00] {
|
|
23148
23193
|
display: inline-block;
|
|
23149
23194
|
width: 90px;
|
|
23150
23195
|
height: 1em;
|
|
23151
23196
|
border-radius: 4px;
|
|
23152
23197
|
background: linear-gradient(90deg, var(--color-background-dark) 25%, var(--color-background-hover) 50%, var(--color-background-dark) 75%);
|
|
23153
23198
|
background-size: 200% 100%;
|
|
23154
|
-
animation: cn-odw-shimmer-
|
|
23199
|
+
animation: cn-odw-shimmer-324e1a00 1.2s ease-in-out infinite;
|
|
23155
23200
|
}
|
|
23156
|
-
@keyframes cn-odw-shimmer-
|
|
23201
|
+
@keyframes cn-odw-shimmer-324e1a00 {
|
|
23157
23202
|
from { background-position: 200% 0;
|
|
23158
23203
|
}
|
|
23159
23204
|
to { background-position: -200% 0;
|
|
23160
23205
|
}
|
|
23161
23206
|
}
|
|
23162
23207
|
@media (prefers-reduced-motion: reduce) {
|
|
23163
|
-
.cn-object-data-widget__skeleton[data-v-
|
|
23208
|
+
.cn-object-data-widget__skeleton[data-v-324e1a00] { animation: none;
|
|
23164
23209
|
}
|
|
23165
23210
|
}
|
|
23166
|
-
.cn-object-data-widget__grid[data-v-
|
|
23211
|
+
.cn-object-data-widget__grid[data-v-324e1a00] {
|
|
23167
23212
|
display: grid;
|
|
23168
23213
|
gap: calc(2 * var(--default-grid-baseline, 4px)) calc(4 * var(--default-grid-baseline, 4px));
|
|
23169
23214
|
}
|
|
23170
|
-
.cn-object-data-widget__toggle[data-v-
|
|
23215
|
+
.cn-object-data-widget__toggle[data-v-324e1a00] {
|
|
23171
23216
|
margin-top: 4px;
|
|
23172
23217
|
}
|
|
23173
|
-
.cn-object-data-widget__cell[data-v-
|
|
23218
|
+
.cn-object-data-widget__cell[data-v-324e1a00] {
|
|
23174
23219
|
display: flex;
|
|
23175
23220
|
flex-direction: column;
|
|
23176
23221
|
gap: 2px;
|
|
@@ -23178,12 +23223,12 @@ to { background-position: -200% 0;
|
|
|
23178
23223
|
border-bottom: 1px solid var(--color-border-dark);
|
|
23179
23224
|
min-width: 0;
|
|
23180
23225
|
}
|
|
23181
|
-
.cn-object-data-widget__label[data-v-
|
|
23226
|
+
.cn-object-data-widget__label[data-v-324e1a00] {
|
|
23182
23227
|
font-size: 0.85em;
|
|
23183
23228
|
color: var(--color-text-maxcontrast);
|
|
23184
23229
|
font-weight: 500;
|
|
23185
23230
|
}
|
|
23186
|
-
.cn-object-data-widget__value[data-v-
|
|
23231
|
+
.cn-object-data-widget__value[data-v-324e1a00] {
|
|
23187
23232
|
font-size: 1em;
|
|
23188
23233
|
color: var(--color-main-text);
|
|
23189
23234
|
overflow-wrap: anywhere;
|
|
@@ -23197,24 +23242,24 @@ to { background-position: -200% 0;
|
|
|
23197
23242
|
which drew a horizontal scrollbar under every data widget in a tab (dossiq's
|
|
23198
23243
|
case page, Data tab). The end padding drops by the same 4px so the text and
|
|
23199
23244
|
the edit icon sit where they sat. */
|
|
23200
|
-
.cn-object-data-widget__value--editable[data-v-
|
|
23245
|
+
.cn-object-data-widget__value--editable[data-v-324e1a00] {
|
|
23201
23246
|
cursor: pointer;
|
|
23202
23247
|
border-radius: var(--border-radius);
|
|
23203
23248
|
padding: 4px 20px 4px 4px;
|
|
23204
23249
|
margin: -4px 0 -4px -4px;
|
|
23205
23250
|
}
|
|
23206
|
-
.cn-object-data-widget__value--editable[data-v-
|
|
23251
|
+
.cn-object-data-widget__value--editable[data-v-324e1a00]:hover {
|
|
23207
23252
|
background: var(--color-background-dark);
|
|
23208
23253
|
}
|
|
23209
|
-
.cn-object-data-widget__value--editable[data-v-
|
|
23254
|
+
.cn-object-data-widget__value--editable[data-v-324e1a00]:focus-visible {
|
|
23210
23255
|
outline: 2px solid var(--color-primary-element);
|
|
23211
23256
|
outline-offset: 2px;
|
|
23212
23257
|
}
|
|
23213
|
-
.cn-object-data-widget__value--empty[data-v-
|
|
23258
|
+
.cn-object-data-widget__value--empty[data-v-324e1a00] {
|
|
23214
23259
|
color: var(--color-text-maxcontrast);
|
|
23215
23260
|
font-style: italic;
|
|
23216
23261
|
}
|
|
23217
|
-
.cn-object-data-widget__edit-icon[data-v-
|
|
23262
|
+
.cn-object-data-widget__edit-icon[data-v-324e1a00] {
|
|
23218
23263
|
position: absolute;
|
|
23219
23264
|
right: 4px;
|
|
23220
23265
|
top: 50%;
|
|
@@ -23223,21 +23268,21 @@ to { background-position: -200% 0;
|
|
|
23223
23268
|
opacity: 0;
|
|
23224
23269
|
transition: opacity 0.15s ease;
|
|
23225
23270
|
}
|
|
23226
|
-
.cn-object-data-widget__value--editable:hover .cn-object-data-widget__edit-icon[data-v-
|
|
23227
|
-
.cn-object-data-widget__value--editable:focus-visible .cn-object-data-widget__edit-icon[data-v-
|
|
23271
|
+
.cn-object-data-widget__value--editable:hover .cn-object-data-widget__edit-icon[data-v-324e1a00],
|
|
23272
|
+
.cn-object-data-widget__value--editable:focus-visible .cn-object-data-widget__edit-icon[data-v-324e1a00] {
|
|
23228
23273
|
opacity: 1;
|
|
23229
23274
|
}
|
|
23230
|
-
.cn-object-data-widget__editor[data-v-
|
|
23275
|
+
.cn-object-data-widget__editor[data-v-324e1a00] {
|
|
23231
23276
|
display: flex;
|
|
23232
23277
|
flex-direction: column;
|
|
23233
23278
|
gap: 4px;
|
|
23234
23279
|
}
|
|
23235
|
-
.cn-object-data-widget__editor-actions[data-v-
|
|
23280
|
+
.cn-object-data-widget__editor-actions[data-v-324e1a00] {
|
|
23236
23281
|
display: flex;
|
|
23237
23282
|
gap: 2px;
|
|
23238
23283
|
justify-content: flex-end;
|
|
23239
23284
|
}
|
|
23240
|
-
.cn-object-data-widget__textarea[data-v-
|
|
23285
|
+
.cn-object-data-widget__textarea[data-v-324e1a00] {
|
|
23241
23286
|
width: 100%;
|
|
23242
23287
|
min-height: 80px;
|
|
23243
23288
|
padding: 8px;
|
|
@@ -23249,11 +23294,11 @@ to { background-position: -200% 0;
|
|
|
23249
23294
|
font-size: inherit;
|
|
23250
23295
|
resize: vertical;
|
|
23251
23296
|
}
|
|
23252
|
-
.cn-object-data-widget__textarea[data-v-
|
|
23297
|
+
.cn-object-data-widget__textarea[data-v-324e1a00]:focus {
|
|
23253
23298
|
border-color: var(--color-primary-element);
|
|
23254
23299
|
outline: none;
|
|
23255
23300
|
}
|
|
23256
|
-
.cn-object-data-widget__empty[data-v-
|
|
23301
|
+
.cn-object-data-widget__empty[data-v-324e1a00] {
|
|
23257
23302
|
color: var(--color-text-maxcontrast);
|
|
23258
23303
|
font-style: italic;
|
|
23259
23304
|
padding: calc(2 * var(--default-grid-baseline, 4px));
|
|
@@ -23261,16 +23306,16 @@ to { background-position: -200% 0;
|
|
|
23261
23306
|
|
|
23262
23307
|
/* Compact inline table for array-of-objects values. Dense + unstyled-simple;
|
|
23263
23308
|
fits the card and participates in the whole-row overflow handling. */
|
|
23264
|
-
.cn-object-data-widget__mini-table-wrap[data-v-
|
|
23309
|
+
.cn-object-data-widget__mini-table-wrap[data-v-324e1a00] {
|
|
23265
23310
|
max-width: 100%;
|
|
23266
23311
|
}
|
|
23267
|
-
.cn-object-data-widget__mini-table[data-v-
|
|
23312
|
+
.cn-object-data-widget__mini-table[data-v-324e1a00] {
|
|
23268
23313
|
width: 100%;
|
|
23269
23314
|
border-collapse: collapse;
|
|
23270
23315
|
font-size: 0.9em;
|
|
23271
23316
|
}
|
|
23272
|
-
.cn-object-data-widget__mini-table th[data-v-
|
|
23273
|
-
.cn-object-data-widget__mini-table td[data-v-
|
|
23317
|
+
.cn-object-data-widget__mini-table th[data-v-324e1a00],
|
|
23318
|
+
.cn-object-data-widget__mini-table td[data-v-324e1a00] {
|
|
23274
23319
|
text-align: start;
|
|
23275
23320
|
padding: 2px 8px 2px 0;
|
|
23276
23321
|
border-bottom: 1px solid var(--color-border);
|
|
@@ -23281,23 +23326,23 @@ to { background-position: -200% 0;
|
|
|
23281
23326
|
cell, and the card 8px wider than its cell (pipelinq's ticket page). */
|
|
23282
23327
|
white-space: normal;
|
|
23283
23328
|
}
|
|
23284
|
-
.cn-object-data-widget__mini-table th[data-v-
|
|
23329
|
+
.cn-object-data-widget__mini-table th[data-v-324e1a00] {
|
|
23285
23330
|
color: var(--color-text-maxcontrast);
|
|
23286
23331
|
font-weight: 500;
|
|
23287
23332
|
}
|
|
23288
|
-
.cn-object-data-widget__more[data-v-
|
|
23333
|
+
.cn-object-data-widget__more[data-v-324e1a00] {
|
|
23289
23334
|
margin-top: 2px;
|
|
23290
23335
|
font-size: 0.85em;
|
|
23291
23336
|
color: var(--color-text-maxcontrast);
|
|
23292
23337
|
}
|
|
23293
23338
|
|
|
23294
23339
|
/* Comma-less chip row for array-of-scalars values. */
|
|
23295
|
-
.cn-object-data-widget__chips[data-v-
|
|
23340
|
+
.cn-object-data-widget__chips[data-v-324e1a00] {
|
|
23296
23341
|
display: flex;
|
|
23297
23342
|
flex-wrap: wrap;
|
|
23298
23343
|
gap: 4px;
|
|
23299
23344
|
}
|
|
23300
|
-
.cn-object-data-widget__chip[data-v-
|
|
23345
|
+
.cn-object-data-widget__chip[data-v-324e1a00] {
|
|
23301
23346
|
padding: 1px 8px;
|
|
23302
23347
|
border-radius: 12px;
|
|
23303
23348
|
background: var(--color-background-dark);
|
|
@@ -23305,27 +23350,37 @@ to { background-position: -200% 0;
|
|
|
23305
23350
|
}
|
|
23306
23351
|
|
|
23307
23352
|
/* Key: value definition list for a single plain-object value. */
|
|
23308
|
-
.cn-object-data-widget__deflist[data-v-
|
|
23353
|
+
.cn-object-data-widget__deflist[data-v-324e1a00] {
|
|
23309
23354
|
display: grid;
|
|
23310
23355
|
grid-template-columns: auto 1fr;
|
|
23311
23356
|
gap: 2px 8px;
|
|
23312
23357
|
margin: 0;
|
|
23313
23358
|
}
|
|
23314
|
-
.cn-object-data-widget__deflist dt[data-v-
|
|
23359
|
+
.cn-object-data-widget__deflist dt[data-v-324e1a00] {
|
|
23315
23360
|
color: var(--color-text-maxcontrast);
|
|
23316
23361
|
font-size: 0.9em;
|
|
23317
23362
|
}
|
|
23318
|
-
.cn-object-data-widget__deflist dd[data-v-
|
|
23363
|
+
.cn-object-data-widget__deflist dd[data-v-324e1a00] {
|
|
23319
23364
|
margin: 0;
|
|
23320
23365
|
overflow-wrap: anywhere;
|
|
23321
23366
|
}
|
|
23322
23367
|
|
|
23368
|
+
/* A long description keeps its own paragraphs. Set on an inline span with no
|
|
23369
|
+
surrounding template whitespace, so only the value's own line breaks show. */
|
|
23370
|
+
.cn-object-data-widget__deflist-text[data-v-324e1a00] {
|
|
23371
|
+
white-space: pre-wrap;
|
|
23372
|
+
}
|
|
23373
|
+
.cn-object-data-widget__deflist--nested[data-v-324e1a00] {
|
|
23374
|
+
padding-inline-start: 8px;
|
|
23375
|
+
border-inline-start: 2px solid var(--color-border);
|
|
23376
|
+
}
|
|
23377
|
+
|
|
23323
23378
|
/* Responsive: collapse to single column on narrow widths */
|
|
23324
23379
|
@media (max-width: 600px) {
|
|
23325
|
-
.cn-object-data-widget__grid[data-v-
|
|
23380
|
+
.cn-object-data-widget__grid[data-v-324e1a00] {
|
|
23326
23381
|
grid-template-columns: 1fr !important;
|
|
23327
23382
|
}
|
|
23328
|
-
.cn-object-data-widget__cell[data-v-
|
|
23383
|
+
.cn-object-data-widget__cell[data-v-324e1a00] {
|
|
23329
23384
|
grid-column: span 1 !important;
|
|
23330
23385
|
grid-row: span 1 !important;
|
|
23331
23386
|
}
|
|
@@ -25216,18 +25271,18 @@ button.cn-stepper__step[data-v-776581d4] {
|
|
|
25216
25271
|
}
|
|
25217
25272
|
|
|
25218
25273
|
|
|
25219
|
-
.cn-week-strip[data-v-
|
|
25274
|
+
.cn-week-strip[data-v-27a717bd] {
|
|
25220
25275
|
width: 100%;
|
|
25221
25276
|
}
|
|
25222
25277
|
|
|
25223
25278
|
/* `content.inset: true`: the board's inset inside the card (zuiddrecht-pixel-
|
|
25224
25279
|
gaps-3). A dashboard renders its widgets flush, so without it the strip ran
|
|
25225
25280
|
from card edge to card edge. Theme hook: --cn-widget-board-inset. */
|
|
25226
|
-
.cn-week-strip--inset[data-v-
|
|
25281
|
+
.cn-week-strip--inset[data-v-27a717bd] {
|
|
25227
25282
|
box-sizing: border-box;
|
|
25228
25283
|
padding: var(--cn-widget-board-inset, 16px 24px 22px);
|
|
25229
25284
|
}
|
|
25230
|
-
.cn-week-strip__state[data-v-
|
|
25285
|
+
.cn-week-strip__state[data-v-27a717bd] {
|
|
25231
25286
|
display: flex;
|
|
25232
25287
|
align-items: center;
|
|
25233
25288
|
gap: 8px;
|
|
@@ -25235,15 +25290,15 @@ button.cn-stepper__step[data-v-776581d4] {
|
|
|
25235
25290
|
padding: 12px 4px;
|
|
25236
25291
|
color: var(--color-text-maxcontrast);
|
|
25237
25292
|
}
|
|
25238
|
-
.cn-week-strip__scroll[data-v-
|
|
25293
|
+
.cn-week-strip__scroll[data-v-27a717bd] {
|
|
25239
25294
|
overflow-x: auto;
|
|
25240
25295
|
border-radius: var(--border-radius-large, 8px);
|
|
25241
25296
|
}
|
|
25242
|
-
.cn-week-strip__scroll[data-v-
|
|
25297
|
+
.cn-week-strip__scroll[data-v-27a717bd]:focus-visible {
|
|
25243
25298
|
outline: 2px solid var(--color-primary-element);
|
|
25244
25299
|
outline-offset: 2px;
|
|
25245
25300
|
}
|
|
25246
|
-
.cn-week-strip__days[data-v-
|
|
25301
|
+
.cn-week-strip__days[data-v-27a717bd] {
|
|
25247
25302
|
display: grid;
|
|
25248
25303
|
grid-template-columns: repeat(var(--cn-week-strip-columns, 5), minmax(128px, 1fr));
|
|
25249
25304
|
gap: 10px;
|
|
@@ -25253,7 +25308,7 @@ button.cn-stepper__step[data-v-776581d4] {
|
|
|
25253
25308
|
padding: 0;
|
|
25254
25309
|
list-style: none;
|
|
25255
25310
|
}
|
|
25256
|
-
.cn-week-strip__day[data-v-
|
|
25311
|
+
.cn-week-strip__day[data-v-27a717bd] {
|
|
25257
25312
|
display: flex;
|
|
25258
25313
|
flex-direction: column;
|
|
25259
25314
|
gap: 8px;
|
|
@@ -25263,11 +25318,11 @@ button.cn-stepper__step[data-v-776581d4] {
|
|
|
25263
25318
|
border-radius: var(--border-radius-large, 8px);
|
|
25264
25319
|
background: var(--color-background-hover);
|
|
25265
25320
|
}
|
|
25266
|
-
.cn-week-strip__day--today[data-v-
|
|
25321
|
+
.cn-week-strip__day--today[data-v-27a717bd] {
|
|
25267
25322
|
border-color: var(--color-primary-element);
|
|
25268
25323
|
background: var(--color-primary-element-light);
|
|
25269
25324
|
}
|
|
25270
|
-
.cn-week-strip__head[data-v-
|
|
25325
|
+
.cn-week-strip__head[data-v-27a717bd] {
|
|
25271
25326
|
display: flex;
|
|
25272
25327
|
align-items: center;
|
|
25273
25328
|
justify-content: space-between;
|
|
@@ -25276,7 +25331,7 @@ button.cn-stepper__step[data-v-776581d4] {
|
|
|
25276
25331
|
font-weight: 600;
|
|
25277
25332
|
color: var(--color-main-text);
|
|
25278
25333
|
}
|
|
25279
|
-
.cn-week-strip__today[data-v-
|
|
25334
|
+
.cn-week-strip__today[data-v-27a717bd] {
|
|
25280
25335
|
padding: 1px 8px;
|
|
25281
25336
|
border-radius: var(--border-radius-pill, 10px);
|
|
25282
25337
|
background: var(--color-primary-element);
|
|
@@ -25284,7 +25339,7 @@ button.cn-stepper__step[data-v-776581d4] {
|
|
|
25284
25339
|
font-size: 0.85em;
|
|
25285
25340
|
font-weight: 700;
|
|
25286
25341
|
}
|
|
25287
|
-
.cn-week-strip__items[data-v-
|
|
25342
|
+
.cn-week-strip__items[data-v-27a717bd] {
|
|
25288
25343
|
display: flex;
|
|
25289
25344
|
flex-direction: column;
|
|
25290
25345
|
gap: 6px;
|
|
@@ -25292,7 +25347,7 @@ button.cn-stepper__step[data-v-776581d4] {
|
|
|
25292
25347
|
padding: 0;
|
|
25293
25348
|
list-style: none;
|
|
25294
25349
|
}
|
|
25295
|
-
.cn-week-strip__item[data-v-
|
|
25350
|
+
.cn-week-strip__item[data-v-27a717bd] {
|
|
25296
25351
|
display: flex;
|
|
25297
25352
|
flex-direction: column;
|
|
25298
25353
|
gap: 4px;
|
|
@@ -25303,41 +25358,41 @@ button.cn-stepper__step[data-v-776581d4] {
|
|
|
25303
25358
|
color: var(--color-main-text);
|
|
25304
25359
|
text-decoration: none;
|
|
25305
25360
|
}
|
|
25306
|
-
.cn-week-strip__item--link[data-v-
|
|
25361
|
+
.cn-week-strip__item--link[data-v-27a717bd]:hover {
|
|
25307
25362
|
border-color: var(--color-primary-element);
|
|
25308
25363
|
}
|
|
25309
|
-
.cn-week-strip__item--link[data-v-
|
|
25364
|
+
.cn-week-strip__item--link[data-v-27a717bd]:focus-visible {
|
|
25310
25365
|
outline: 2px solid var(--color-primary-element);
|
|
25311
25366
|
outline-offset: 2px;
|
|
25312
25367
|
}
|
|
25313
25368
|
|
|
25314
25369
|
/* The edge is drawn inside the box, so a late item is as wide as the others. */
|
|
25315
|
-
.cn-week-strip__item--late[data-v-
|
|
25370
|
+
.cn-week-strip__item--late[data-v-27a717bd] {
|
|
25316
25371
|
border-color: var(--color-element-error, var(--color-error));
|
|
25317
25372
|
box-shadow: inset 3px 0 0 0 var(--color-element-error, var(--color-error));
|
|
25318
25373
|
}
|
|
25319
|
-
.cn-week-strip__title[data-v-
|
|
25374
|
+
.cn-week-strip__title[data-v-27a717bd] {
|
|
25320
25375
|
font-size: 0.95em;
|
|
25321
25376
|
line-height: 1.3;
|
|
25322
25377
|
overflow-wrap: anywhere;
|
|
25323
25378
|
}
|
|
25324
|
-
.cn-week-strip__meta[data-v-
|
|
25379
|
+
.cn-week-strip__meta[data-v-27a717bd] {
|
|
25325
25380
|
font-size: 0.85em;
|
|
25326
25381
|
color: var(--color-text-maxcontrast);
|
|
25327
25382
|
overflow-wrap: anywhere;
|
|
25328
25383
|
}
|
|
25329
|
-
.cn-week-strip__late[data-v-
|
|
25384
|
+
.cn-week-strip__late[data-v-27a717bd] {
|
|
25330
25385
|
font-size: 0.8em;
|
|
25331
25386
|
font-weight: 600;
|
|
25332
25387
|
color: var(--color-text-error, var(--color-error-text));
|
|
25333
25388
|
}
|
|
25334
|
-
.cn-week-strip__empty[data-v-
|
|
25389
|
+
.cn-week-strip__empty[data-v-27a717bd] {
|
|
25335
25390
|
font-size: 0.9em;
|
|
25336
25391
|
color: var(--color-text-maxcontrast);
|
|
25337
25392
|
}
|
|
25338
25393
|
|
|
25339
25394
|
/* "+N more" under a capped day: a quiet text control, 24px high to click. */
|
|
25340
|
-
.cn-week-strip__more[data-v-
|
|
25395
|
+
.cn-week-strip__more[data-v-27a717bd] {
|
|
25341
25396
|
align-self: flex-start;
|
|
25342
25397
|
min-height: 24px;
|
|
25343
25398
|
margin: 0;
|
|
@@ -25352,10 +25407,10 @@ button.cn-stepper__step[data-v-776581d4] {
|
|
|
25352
25407
|
text-decoration: none;
|
|
25353
25408
|
cursor: pointer;
|
|
25354
25409
|
}
|
|
25355
|
-
.cn-week-strip__more[data-v-
|
|
25410
|
+
.cn-week-strip__more[data-v-27a717bd]:hover {
|
|
25356
25411
|
text-decoration: underline;
|
|
25357
25412
|
}
|
|
25358
|
-
.cn-week-strip__more[data-v-
|
|
25413
|
+
.cn-week-strip__more[data-v-27a717bd]:focus-visible {
|
|
25359
25414
|
outline: 2px solid var(--color-primary-element);
|
|
25360
25415
|
outline-offset: 2px;
|
|
25361
25416
|
}
|
|
@@ -37533,17 +37588,17 @@ button.cn-object-calendar__overflow[data-v-2735e5ef] {
|
|
|
37533
37588
|
}
|
|
37534
37589
|
|
|
37535
37590
|
|
|
37536
|
-
.cn-dashboard-grid[data-v-
|
|
37591
|
+
.cn-dashboard-grid[data-v-a91998fc] {
|
|
37537
37592
|
width: 100%;
|
|
37538
37593
|
min-height: 200px;
|
|
37539
37594
|
}
|
|
37540
|
-
.grid-stack[data-v-
|
|
37595
|
+
.grid-stack[data-v-a91998fc] {
|
|
37541
37596
|
background: transparent;
|
|
37542
37597
|
}
|
|
37543
37598
|
|
|
37544
37599
|
/* Screen-reader-only text: the keyboard key map and the live region. Kept in
|
|
37545
37600
|
the layout (not `display: none`) so assistive tech still reads it. */
|
|
37546
|
-
.cn-dashboard-grid__sr-only[data-v-
|
|
37601
|
+
.cn-dashboard-grid__sr-only[data-v-a91998fc] {
|
|
37547
37602
|
position: absolute;
|
|
37548
37603
|
width: 1px;
|
|
37549
37604
|
height: 1px;
|
|
@@ -37558,7 +37613,7 @@ button.cn-object-calendar__overflow[data-v-2735e5ef] {
|
|
|
37558
37613
|
/* Grid items become tab stops in edit mode — give them a visible focus ring
|
|
37559
37614
|
that satisfies WCAG 2.1 SC 2.4.7 without relying on the widget's own
|
|
37560
37615
|
styling. `outline-offset` keeps the ring outside the rounded card. */
|
|
37561
|
-
.grid-stack-item[data-v-
|
|
37616
|
+
.grid-stack-item[data-v-a91998fc]:focus-visible {
|
|
37562
37617
|
outline: 2px solid var(--color-primary-element, #0082c9);
|
|
37563
37618
|
outline-offset: 2px;
|
|
37564
37619
|
border-radius: var(--border-radius-container-large, 16px);
|
|
@@ -37570,19 +37625,19 @@ button.cn-object-calendar__overflow[data-v-2735e5ef] {
|
|
|
37570
37625
|
cell never shrank: "Waiting for me" stayed a tall card around one row. In
|
|
37571
37626
|
such a cell the child takes its own height. A cell without the key keeps
|
|
37572
37627
|
the filling child. */
|
|
37573
|
-
.cn-dashboard-grid__item--fit[data-v-
|
|
37628
|
+
.cn-dashboard-grid__item--fit[data-v-a91998fc] > .grid-stack-item-content > * {
|
|
37574
37629
|
height: auto;
|
|
37575
37630
|
}
|
|
37576
|
-
[data-v-
|
|
37631
|
+
[data-v-a91998fc] .grid-stack-item-content {
|
|
37577
37632
|
background: var(--color-main-background);
|
|
37578
37633
|
border: none;
|
|
37579
37634
|
box-shadow: none;
|
|
37580
37635
|
overflow: hidden;
|
|
37581
37636
|
}
|
|
37582
|
-
[data-v-
|
|
37637
|
+
[data-v-a91998fc] .grid-stack-item-content:has(.cn-widget-wrapper--borderless) {
|
|
37583
37638
|
background: transparent;
|
|
37584
37639
|
}
|
|
37585
|
-
[data-v-
|
|
37640
|
+
[data-v-a91998fc] .grid-stack-placeholder > .placeholder-content {
|
|
37586
37641
|
background: var(--color-primary-element-light);
|
|
37587
37642
|
border: 2px dashed var(--color-primary-element);
|
|
37588
37643
|
border-radius: var(--border-radius-large);
|
|
@@ -55,5 +55,44 @@ function readGridGeometry(items) {
|
|
|
55
55
|
return map
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Where the items under a growing cell go so they keep their order: every item
|
|
60
|
+
* the growth would overlap moves down by the growth, and so does every item
|
|
61
|
+
* that move would overlap in turn. Items beside the cell stay put.
|
|
62
|
+
*
|
|
63
|
+
* GridStack resolves the same collision itself, but not in order: when
|
|
64
|
+
* pipelinq's size-to-content banner grew from two rows to three, the
|
|
65
|
+
* full-width card under the stat tiles landed below the lists.
|
|
66
|
+
*
|
|
67
|
+
* @spec openspec/changes/dashboard-growth-keeps-order/specs/dashboard-grid/spec.md#requirement-a-growing-cell-keeps-the-order-of-the-cards-under-it
|
|
68
|
+
* @param {Array<{x: number, y: number, w: number, h: number}>} nodes The grid's items (the growing one among them).
|
|
69
|
+
* @param {{x: number, y: number, w: number, h: number}} node The growing item.
|
|
70
|
+
* @param {number} h Its new height, in grid rows.
|
|
71
|
+
* @return {Array<{node: object, y: number}>} The items to move and their new row, bottom-most first.
|
|
72
|
+
*/
|
|
73
|
+
function planGrowth(nodes, node, h) {
|
|
74
|
+
const delta = h - node.h;
|
|
75
|
+
if (!(delta > 0)) {
|
|
76
|
+
return []
|
|
77
|
+
}
|
|
78
|
+
const moved = new Map();
|
|
79
|
+
const overlaps = (a, b) => a.x < b.x + b.w && b.x < a.x + a.w && a.y < b.y + b.h && b.y < a.y + a.h;
|
|
80
|
+
const queue = [{ x: node.x, y: node.y, w: node.w, h }];
|
|
81
|
+
while (queue.length > 0) {
|
|
82
|
+
const rect = queue.shift();
|
|
83
|
+
for (const other of nodes) {
|
|
84
|
+
if (other === node || moved.has(other) || !overlaps(rect, other)) {
|
|
85
|
+
continue
|
|
86
|
+
}
|
|
87
|
+
const next = { x: other.x, y: other.y + delta, w: other.w, h: other.h };
|
|
88
|
+
moved.set(other, next.y);
|
|
89
|
+
queue.push(next);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return [...moved.entries()]
|
|
93
|
+
.map(([item, y]) => ({ node: item, y }))
|
|
94
|
+
.sort((a, b) => b.y - a.y)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export { initGridStack, planGrowth, readGridGeometry };
|
|
59
98
|
//# sourceMappingURL=gridStack.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gridStack.js","sources":["../../../src/utils/gridStack.js"],"sourcesContent":["/**\n * Thin GridStack bootstrap shared by the drag/resize grids (CnDashboardGrid and\n * CnWidgetGrid's editable body slot). Keeps the GridStack option shape in one\n * place so the two engines stay consistent.\n *\n * @module utils/gridStack\n */\n\nimport { GridStack } from 'gridstack'\n// `gridstack` is a peerDependency, not bundled — see the matching comment in\n// CnDashboardGrid.vue. The consumer must install `gridstack` themselves and\n// import its stylesheet (`gridstack/dist/gridstack.min.css`) from that same\n// copy; nc-vue ships no GridStack CSS of its own so the JS driving the grid\n// and the CSS sizing it can never come from two different versions.\n\n/**\n * Initialise a GridStack instance on a `.grid-stack` element.\n *\n * @param {HTMLElement} el The `.grid-stack` container element.\n * @param {object} opts Options.\n * @param {number} opts.columns Column count.\n * @param {boolean} opts.editable Whether drag + resize are enabled.\n * @param {number} [opts.cellHeight] Cell height in px (default 80).\n * @param {number} [opts.margin] Grid margin in px (default 12).\n * @return {import('gridstack').GridStack} The GridStack instance.\n */\nexport function initGridStack(el, opts) {\n\tconst { columns, editable, cellHeight = 80, margin = 12 } = opts\n\treturn GridStack.init({\n\t\tcolumn: columns,\n\t\tcellHeight,\n\t\tmargin,\n\t\tfloat: true,\n\t\tanimate: true,\n\t\tdisableDrag: !editable,\n\t\tdisableResize: !editable,\n\t\tremovable: false,\n\t}, el)\n}\n\n/**\n * Read the geometry GridStack reports for a changed set of items into a plain\n * map keyed by node id.\n *\n * @param {Array<{id: (string|number), x: number, y: number, w: number, h: number}>} items\n * The items GridStack passes to its `change` handler.\n * @return {Map<string, {gridX: number, gridY: number, gridWidth: number, gridHeight: number}>}\n */\nexport function readGridGeometry(items) {\n\tconst map = new Map()\n\tfor (const it of items || []) {\n\t\tmap.set(String(it.id), { gridX: it.x, gridY: it.y, gridWidth: it.w, gridHeight: it.h })\n\t}\n\treturn map\n}\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,aAAa,CAAC,EAAE,EAAE,IAAI,EAAE;AACxC,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,GAAG,EAAE,EAAE,MAAM,GAAG,EAAE,EAAE,GAAG,KAAI;AACjE,CAAC,OAAO,SAAS,CAAC,IAAI,CAAC;AACvB,EAAE,MAAM,EAAE,OAAO;AACjB,EAAE,UAAU;AACZ,EAAE,MAAM;AACR,EAAE,KAAK,EAAE,IAAI;AACb,EAAE,OAAO,EAAE,IAAI;AACf,EAAE,WAAW,EAAE,CAAC,QAAQ;AACxB,EAAE,aAAa,EAAE,CAAC,QAAQ;AAC1B,EAAE,SAAS,EAAE,KAAK;AAClB,EAAE,EAAE,EAAE,CAAC;AACP,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,gBAAgB,CAAC,KAAK,EAAE;AACxC,CAAC,MAAM,GAAG,GAAG,IAAI,GAAG,GAAE;AACtB,CAAC,KAAK,MAAM,EAAE,IAAI,KAAK,IAAI,EAAE,EAAE;AAC/B,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC,EAAE,EAAC;AACzF,EAAE;AACF,CAAC,OAAO,GAAG;AACX;;;;"}
|
|
1
|
+
{"version":3,"file":"gridStack.js","sources":["../../../src/utils/gridStack.js"],"sourcesContent":["/**\n * Thin GridStack bootstrap shared by the drag/resize grids (CnDashboardGrid and\n * CnWidgetGrid's editable body slot). Keeps the GridStack option shape in one\n * place so the two engines stay consistent.\n *\n * @module utils/gridStack\n */\n\nimport { GridStack } from 'gridstack'\n// `gridstack` is a peerDependency, not bundled — see the matching comment in\n// CnDashboardGrid.vue. The consumer must install `gridstack` themselves and\n// import its stylesheet (`gridstack/dist/gridstack.min.css`) from that same\n// copy; nc-vue ships no GridStack CSS of its own so the JS driving the grid\n// and the CSS sizing it can never come from two different versions.\n\n/**\n * Initialise a GridStack instance on a `.grid-stack` element.\n *\n * @param {HTMLElement} el The `.grid-stack` container element.\n * @param {object} opts Options.\n * @param {number} opts.columns Column count.\n * @param {boolean} opts.editable Whether drag + resize are enabled.\n * @param {number} [opts.cellHeight] Cell height in px (default 80).\n * @param {number} [opts.margin] Grid margin in px (default 12).\n * @return {import('gridstack').GridStack} The GridStack instance.\n */\nexport function initGridStack(el, opts) {\n\tconst { columns, editable, cellHeight = 80, margin = 12 } = opts\n\treturn GridStack.init({\n\t\tcolumn: columns,\n\t\tcellHeight,\n\t\tmargin,\n\t\tfloat: true,\n\t\tanimate: true,\n\t\tdisableDrag: !editable,\n\t\tdisableResize: !editable,\n\t\tremovable: false,\n\t}, el)\n}\n\n/**\n * Read the geometry GridStack reports for a changed set of items into a plain\n * map keyed by node id.\n *\n * @param {Array<{id: (string|number), x: number, y: number, w: number, h: number}>} items\n * The items GridStack passes to its `change` handler.\n * @return {Map<string, {gridX: number, gridY: number, gridWidth: number, gridHeight: number}>}\n */\nexport function readGridGeometry(items) {\n\tconst map = new Map()\n\tfor (const it of items || []) {\n\t\tmap.set(String(it.id), { gridX: it.x, gridY: it.y, gridWidth: it.w, gridHeight: it.h })\n\t}\n\treturn map\n}\n\n/**\n * Where the items under a growing cell go so they keep their order: every item\n * the growth would overlap moves down by the growth, and so does every item\n * that move would overlap in turn. Items beside the cell stay put.\n *\n * GridStack resolves the same collision itself, but not in order: when\n * pipelinq's size-to-content banner grew from two rows to three, the\n * full-width card under the stat tiles landed below the lists.\n *\n * @spec openspec/changes/dashboard-growth-keeps-order/specs/dashboard-grid/spec.md#requirement-a-growing-cell-keeps-the-order-of-the-cards-under-it\n * @param {Array<{x: number, y: number, w: number, h: number}>} nodes The grid's items (the growing one among them).\n * @param {{x: number, y: number, w: number, h: number}} node The growing item.\n * @param {number} h Its new height, in grid rows.\n * @return {Array<{node: object, y: number}>} The items to move and their new row, bottom-most first.\n */\nexport function planGrowth(nodes, node, h) {\n\tconst delta = h - node.h\n\tif (!(delta > 0)) {\n\t\treturn []\n\t}\n\tconst moved = new Map()\n\tconst overlaps = (a, b) => a.x < b.x + b.w && b.x < a.x + a.w && a.y < b.y + b.h && b.y < a.y + a.h\n\tconst queue = [{ x: node.x, y: node.y, w: node.w, h }]\n\twhile (queue.length > 0) {\n\t\tconst rect = queue.shift()\n\t\tfor (const other of nodes) {\n\t\t\tif (other === node || moved.has(other) || !overlaps(rect, other)) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst next = { x: other.x, y: other.y + delta, w: other.w, h: other.h }\n\t\t\tmoved.set(other, next.y)\n\t\t\tqueue.push(next)\n\t\t}\n\t}\n\treturn [...moved.entries()]\n\t\t.map(([item, y]) => ({ node: item, y }))\n\t\t.sort((a, b) => b.y - a.y)\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl>
|
|
3
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
4
|
+
*
|
|
5
|
+
* Make an identifier-shaped key readable for a person. Pure: no Vue.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Split camelCase, snake_case and kebab-case into words, keep acronyms, and
|
|
10
|
+
* capitalise only the first word.
|
|
11
|
+
*
|
|
12
|
+
* `caseAssigned` → "Case assigned", `object_created` → "Object created",
|
|
13
|
+
* `newBRPRecord` → "New BRP record".
|
|
14
|
+
*
|
|
15
|
+
* @param {string} key The key.
|
|
16
|
+
* @return {string} A readable sentence-case label, or the key when it has no words.
|
|
17
|
+
* @spec openspec/changes/data-widget-nested-objects/specs/data-widget-nested-objects/spec.md#requirement-a-nested-object-reads-as-a-list-per-key
|
|
18
|
+
*/
|
|
19
|
+
function humaniseKey(key) {
|
|
20
|
+
const words = String(key || '')
|
|
21
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
22
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
|
|
23
|
+
.split(/[\s_\-.]+/)
|
|
24
|
+
.filter(Boolean);
|
|
25
|
+
if (words.length === 0) {
|
|
26
|
+
return String(key || '')
|
|
27
|
+
}
|
|
28
|
+
return words
|
|
29
|
+
.map((word, index) => {
|
|
30
|
+
const isAcronym = word.length > 1 && word === word.toUpperCase() && /[A-Z]/.test(word);
|
|
31
|
+
if (isAcronym) {
|
|
32
|
+
return word
|
|
33
|
+
}
|
|
34
|
+
const lower = word.toLowerCase();
|
|
35
|
+
return index === 0 ? lower.charAt(0).toUpperCase() + lower.slice(1) : lower
|
|
36
|
+
})
|
|
37
|
+
.join(' ')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export { humaniseKey };
|
|
41
|
+
//# sourceMappingURL=humaniseKey.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"humaniseKey.js","sources":["../../../src/utils/humaniseKey.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl>\n * SPDX-License-Identifier: EUPL-1.2\n *\n * Make an identifier-shaped key readable for a person. Pure: no Vue.\n */\n\n/**\n * Split camelCase, snake_case and kebab-case into words, keep acronyms, and\n * capitalise only the first word.\n *\n * `caseAssigned` → \"Case assigned\", `object_created` → \"Object created\",\n * `newBRPRecord` → \"New BRP record\".\n *\n * @param {string} key The key.\n * @return {string} A readable sentence-case label, or the key when it has no words.\n * @spec openspec/changes/data-widget-nested-objects/specs/data-widget-nested-objects/spec.md#requirement-a-nested-object-reads-as-a-list-per-key\n */\nexport function humaniseKey(key) {\n\tconst words = String(key || '')\n\t\t.replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n\t\t.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')\n\t\t.split(/[\\s_\\-.]+/)\n\t\t.filter(Boolean)\n\tif (words.length === 0) {\n\t\treturn String(key || '')\n\t}\n\treturn words\n\t\t.map((word, index) => {\n\t\t\tconst isAcronym = word.length > 1 && word === word.toUpperCase() && /[A-Z]/.test(word)\n\t\t\tif (isAcronym) {\n\t\t\t\treturn word\n\t\t\t}\n\t\t\tconst lower = word.toLowerCase()\n\t\t\treturn index === 0 ? lower.charAt(0).toUpperCase() + lower.slice(1) : lower\n\t\t})\n\t\t.join(' ')\n}\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,WAAW,CAAC,GAAG,EAAE;AACjC,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;AAChC,GAAG,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC;AACzC,GAAG,OAAO,CAAC,uBAAuB,EAAE,OAAO,CAAC;AAC5C,GAAG,KAAK,CAAC,WAAW,CAAC;AACrB,GAAG,MAAM,CAAC,OAAO,EAAC;AAClB,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE;AACzB,EAAE,OAAO,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;AAC1B,EAAE;AACF,CAAC,OAAO,KAAK;AACb,GAAG,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,KAAK;AACxB,GAAG,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,KAAK,IAAI,CAAC,WAAW,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,EAAC;AACzF,GAAG,IAAI,SAAS,EAAE;AAClB,IAAI,OAAO,IAAI;AACf,IAAI;AACJ,GAAG,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,GAAE;AACnC,GAAG,OAAO,KAAK,KAAK,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK;AAC9E,GAAG,CAAC;AACJ,GAAG,IAAI,CAAC,GAAG,CAAC;AACZ;;;;"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { humaniseKey } from './humaniseKey.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* SPDX-FileCopyrightText: 2026 Conduction B.V. <info@conduction.nl>
|
|
3
5
|
* SPDX-License-Identifier: EUPL-1.2
|
|
@@ -28,6 +30,7 @@
|
|
|
28
30
|
* @spec openspec/changes/notification-rule-labels-and-runtime-version/specs/notification-preferences/spec.md
|
|
29
31
|
*/
|
|
30
32
|
|
|
33
|
+
|
|
31
34
|
/**
|
|
32
35
|
* Pick the string for the current language out of a string or per-locale map.
|
|
33
36
|
*
|
|
@@ -54,34 +57,14 @@ function resolveLocalised(value, language = 'en') {
|
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
/**
|
|
57
|
-
* Make a rule key readable
|
|
58
|
-
* words, keep acronyms, and capitalise only the first word.
|
|
59
|
-
*
|
|
60
|
-
* `caseAssigned` → "Case assigned", `object_created` → "Object created",
|
|
61
|
-
* `newBRPRecord` → "New BRP record".
|
|
60
|
+
* Make a rule key readable (`caseAssigned` → "Case assigned"); see `humaniseKey`.
|
|
62
61
|
*
|
|
63
62
|
* @param {string} key The rule key.
|
|
64
63
|
* @return {string} A readable sentence-case label, or the key when it has no words.
|
|
64
|
+
* @spec openspec/changes/notification-rule-labels-and-runtime-version/specs/notification-preferences/spec.md#requirement-a-notification-rule-reads-as-a-label
|
|
65
65
|
*/
|
|
66
66
|
function humaniseRuleKey(key) {
|
|
67
|
-
|
|
68
|
-
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
69
|
-
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
|
|
70
|
-
.split(/[\s_\-.]+/)
|
|
71
|
-
.filter(Boolean);
|
|
72
|
-
if (words.length === 0) {
|
|
73
|
-
return String(key || '')
|
|
74
|
-
}
|
|
75
|
-
return words
|
|
76
|
-
.map((word, index) => {
|
|
77
|
-
const isAcronym = word.length > 1 && word === word.toUpperCase() && /[A-Z]/.test(word);
|
|
78
|
-
if (isAcronym) {
|
|
79
|
-
return word
|
|
80
|
-
}
|
|
81
|
-
const lower = word.toLowerCase();
|
|
82
|
-
return index === 0 ? lower.charAt(0).toUpperCase() + lower.slice(1) : lower
|
|
83
|
-
})
|
|
84
|
-
.join(' ')
|
|
67
|
+
return humaniseKey(key)
|
|
85
68
|
}
|
|
86
69
|
|
|
87
70
|
/**
|