@conduction/nextcloud-vue 2.89.1 → 2.90.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/CnAdminSettingsShell/CnAdminSettingsShell.vue.js +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js +13 -0
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +3 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue.js +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js +27 -10
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +18 -0
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +2 -2
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +71 -71
- package/dist/esm/utils/declaredProps.js +106 -0
- package/dist/esm/utils/declaredProps.js.map +1 -0
- package/dist/esm/utils/gridStack.js +7 -3
- package/dist/esm/utils/gridStack.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +184 -30
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +71 -71
- package/package.json +1 -1
- package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +14 -1
- package/src/components/CnDashboardGrid/CnDashboardGrid.vue +27 -10
- package/src/components/CnDashboardPage/CnDashboardPage.vue +20 -2
- package/src/utils/declaredProps.js +115 -0
- package/src/utils/gridStack.js +7 -3
|
@@ -34796,28 +34796,28 @@ to {
|
|
|
34796
34796
|
}
|
|
34797
34797
|
|
|
34798
34798
|
|
|
34799
|
-
.cn-admin-settings-shell[data-v-
|
|
34799
|
+
.cn-admin-settings-shell[data-v-038f200a] {
|
|
34800
34800
|
max-width: 900px;
|
|
34801
34801
|
}
|
|
34802
34802
|
|
|
34803
34803
|
/* The board look lifts the width and lays the header and cards on a grid (look-board.css). */
|
|
34804
|
-
.cn-support-info[data-v-
|
|
34804
|
+
.cn-support-info[data-v-038f200a] {
|
|
34805
34805
|
margin-top: 16px;
|
|
34806
34806
|
padding-top: 16px;
|
|
34807
34807
|
border-top: 1px solid var(--color-border);
|
|
34808
34808
|
}
|
|
34809
|
-
.cn-support-info h4[data-v-
|
|
34809
|
+
.cn-support-info h4[data-v-038f200a] {
|
|
34810
34810
|
margin: 0 0 8px 0;
|
|
34811
34811
|
font-size: 14px;
|
|
34812
34812
|
font-weight: 600;
|
|
34813
34813
|
color: var(--color-main-text);
|
|
34814
34814
|
}
|
|
34815
|
-
.cn-support-info p[data-v-
|
|
34815
|
+
.cn-support-info p[data-v-038f200a] {
|
|
34816
34816
|
margin: 4px 0;
|
|
34817
34817
|
color: var(--color-text-maxcontrast);
|
|
34818
34818
|
font-size: 14px;
|
|
34819
34819
|
}
|
|
34820
|
-
.cn-support-info a[data-v-
|
|
34820
|
+
.cn-support-info a[data-v-038f200a] {
|
|
34821
34821
|
color: var(--color-primary-element);
|
|
34822
34822
|
}
|
|
34823
34823
|
|
|
@@ -38141,17 +38141,17 @@ button.cn-object-calendar__overflow[data-v-e1c69ee2] {
|
|
|
38141
38141
|
}
|
|
38142
38142
|
|
|
38143
38143
|
|
|
38144
|
-
.cn-dashboard-grid[data-v-
|
|
38144
|
+
.cn-dashboard-grid[data-v-a76b6fc6] {
|
|
38145
38145
|
width: 100%;
|
|
38146
38146
|
min-height: 200px;
|
|
38147
38147
|
}
|
|
38148
|
-
.grid-stack[data-v-
|
|
38148
|
+
.grid-stack[data-v-a76b6fc6] {
|
|
38149
38149
|
background: transparent;
|
|
38150
38150
|
}
|
|
38151
38151
|
|
|
38152
38152
|
/* Screen-reader-only text: the keyboard key map and the live region. Kept in
|
|
38153
38153
|
the layout (not `display: none`) so assistive tech still reads it. */
|
|
38154
|
-
.cn-dashboard-grid__sr-only[data-v-
|
|
38154
|
+
.cn-dashboard-grid__sr-only[data-v-a76b6fc6] {
|
|
38155
38155
|
position: absolute;
|
|
38156
38156
|
width: 1px;
|
|
38157
38157
|
height: 1px;
|
|
@@ -38166,7 +38166,7 @@ button.cn-object-calendar__overflow[data-v-e1c69ee2] {
|
|
|
38166
38166
|
/* Grid items become tab stops in edit mode — give them a visible focus ring
|
|
38167
38167
|
that satisfies WCAG 2.1 SC 2.4.7 without relying on the widget's own
|
|
38168
38168
|
styling. `outline-offset` keeps the ring outside the rounded card. */
|
|
38169
|
-
.grid-stack-item[data-v-
|
|
38169
|
+
.grid-stack-item[data-v-a76b6fc6]:focus-visible {
|
|
38170
38170
|
outline: 2px solid var(--color-primary-element, #0082c9);
|
|
38171
38171
|
outline-offset: 2px;
|
|
38172
38172
|
border-radius: var(--border-radius-container-large, 16px);
|
|
@@ -38178,19 +38178,19 @@ button.cn-object-calendar__overflow[data-v-e1c69ee2] {
|
|
|
38178
38178
|
cell never shrank: "Waiting for me" stayed a tall card around one row. In
|
|
38179
38179
|
such a cell the child takes its own height. A cell without the key keeps
|
|
38180
38180
|
the filling child. */
|
|
38181
|
-
.cn-dashboard-grid__item--fit[data-v-
|
|
38181
|
+
.cn-dashboard-grid__item--fit[data-v-a76b6fc6] > .grid-stack-item-content > * {
|
|
38182
38182
|
height: auto;
|
|
38183
38183
|
}
|
|
38184
|
-
[data-v-
|
|
38184
|
+
[data-v-a76b6fc6] .grid-stack-item-content {
|
|
38185
38185
|
background: var(--color-main-background);
|
|
38186
38186
|
border: none;
|
|
38187
38187
|
box-shadow: none;
|
|
38188
38188
|
overflow: hidden;
|
|
38189
38189
|
}
|
|
38190
|
-
[data-v-
|
|
38190
|
+
[data-v-a76b6fc6] .grid-stack-item-content:has(.cn-widget-wrapper--borderless) {
|
|
38191
38191
|
background: transparent;
|
|
38192
38192
|
}
|
|
38193
|
-
[data-v-
|
|
38193
|
+
[data-v-a76b6fc6] .grid-stack-placeholder > .placeholder-content {
|
|
38194
38194
|
background: var(--color-primary-element-light);
|
|
38195
38195
|
border: 2px dashed var(--color-primary-element);
|
|
38196
38196
|
border-radius: var(--border-radius-large);
|
|
@@ -38458,7 +38458,7 @@ button.cn-object-calendar__overflow[data-v-e1c69ee2] {
|
|
|
38458
38458
|
}
|
|
38459
38459
|
|
|
38460
38460
|
|
|
38461
|
-
.cn-dashboard-page[data-v-
|
|
38461
|
+
.cn-dashboard-page[data-v-42bc14a4] {
|
|
38462
38462
|
padding: 20px;
|
|
38463
38463
|
max-width: 1800px;
|
|
38464
38464
|
/* Centre the leftover space once the cap engages, so a wide screen shows
|
|
@@ -38472,7 +38472,7 @@ button.cn-object-calendar__overflow[data-v-e1c69ee2] {
|
|
|
38472
38472
|
overflow-y: auto;
|
|
38473
38473
|
box-sizing: border-box;
|
|
38474
38474
|
}
|
|
38475
|
-
.cn-dashboard-page__header[data-v-
|
|
38475
|
+
.cn-dashboard-page__header[data-v-42bc14a4] {
|
|
38476
38476
|
display: flex;
|
|
38477
38477
|
justify-content: space-between;
|
|
38478
38478
|
align-items: flex-start;
|
|
@@ -38484,21 +38484,21 @@ button.cn-object-calendar__overflow[data-v-e1c69ee2] {
|
|
|
38484
38484
|
room. Only the HEADER shifts right — body widgets keep full width. */
|
|
38485
38485
|
padding-inline-start: 56px;
|
|
38486
38486
|
}
|
|
38487
|
-
.cn-dashboard-page__header-left[data-v-
|
|
38487
|
+
.cn-dashboard-page__header-left[data-v-42bc14a4] {
|
|
38488
38488
|
min-width: 0;
|
|
38489
38489
|
}
|
|
38490
|
-
.cn-dashboard-page__title-row[data-v-
|
|
38490
|
+
.cn-dashboard-page__title-row[data-v-42bc14a4] {
|
|
38491
38491
|
display: flex;
|
|
38492
38492
|
align-items: center;
|
|
38493
38493
|
flex-wrap: wrap;
|
|
38494
38494
|
gap: 12px;
|
|
38495
38495
|
}
|
|
38496
|
-
.cn-dashboard-page__title[data-v-
|
|
38496
|
+
.cn-dashboard-page__title[data-v-42bc14a4] {
|
|
38497
38497
|
margin: 0;
|
|
38498
38498
|
font-size: 20px;
|
|
38499
38499
|
font-weight: 700;
|
|
38500
38500
|
}
|
|
38501
|
-
.cn-dashboard-page__description[data-v-
|
|
38501
|
+
.cn-dashboard-page__description[data-v-42bc14a4] {
|
|
38502
38502
|
margin: 4px 0 0;
|
|
38503
38503
|
font-size: 14px;
|
|
38504
38504
|
color: var(--color-text-maxcontrast);
|
|
@@ -38506,19 +38506,19 @@ button.cn-object-calendar__overflow[data-v-e1c69ee2] {
|
|
|
38506
38506
|
|
|
38507
38507
|
/* Board look (screens-dashboard-parity): h1 28/700, a 15px grey subtitle 6px
|
|
38508
38508
|
under it, and 40px buttons with a 10px gap. */
|
|
38509
|
-
.cn-dashboard-page--board .cn-dashboard-page__title[data-v-
|
|
38509
|
+
.cn-dashboard-page--board .cn-dashboard-page__title[data-v-42bc14a4] {
|
|
38510
38510
|
font-size: 28px;
|
|
38511
38511
|
font-weight: 700;
|
|
38512
38512
|
line-height: 1.2;
|
|
38513
38513
|
}
|
|
38514
|
-
.cn-dashboard-page--board .cn-dashboard-page__description[data-v-
|
|
38514
|
+
.cn-dashboard-page--board .cn-dashboard-page__description[data-v-42bc14a4] {
|
|
38515
38515
|
margin: 6px 0 0;
|
|
38516
38516
|
font-size: 15px;
|
|
38517
38517
|
}
|
|
38518
|
-
.cn-dashboard-page--board .cn-dashboard-page__header-actions[data-v-
|
|
38518
|
+
.cn-dashboard-page--board .cn-dashboard-page__header-actions[data-v-42bc14a4] {
|
|
38519
38519
|
gap: 10px;
|
|
38520
38520
|
}
|
|
38521
|
-
.cn-dashboard-page--board .cn-dashboard-page__header-actions[data-v-
|
|
38521
|
+
.cn-dashboard-page--board .cn-dashboard-page__header-actions[data-v-42bc14a4] .button-vue {
|
|
38522
38522
|
min-height: var(--cn-board-control-height, 40px);
|
|
38523
38523
|
height: var(--cn-board-control-height, 40px);
|
|
38524
38524
|
}
|
|
@@ -38526,17 +38526,17 @@ button.cn-object-calendar__overflow[data-v-e1c69ee2] {
|
|
|
38526
38526
|
/* The switch row under the header (screens-dashboard-greeting-header): the
|
|
38527
38527
|
view switch left, the `viewLinks` pills right (DqMijnWerk: pills 34px high,
|
|
38528
38528
|
radius 17, 13px/600, a 14px icon, primary light on primary deep). */
|
|
38529
|
-
.cn-dashboard-page__switch-row[data-v-
|
|
38529
|
+
.cn-dashboard-page__switch-row[data-v-42bc14a4] {
|
|
38530
38530
|
display: flex;
|
|
38531
38531
|
flex-wrap: wrap;
|
|
38532
38532
|
align-items: center;
|
|
38533
38533
|
gap: 10px;
|
|
38534
38534
|
margin-bottom: var(--cn-board-section-gap, 20px);
|
|
38535
38535
|
}
|
|
38536
|
-
.cn-dashboard-page__switch-row-spacer[data-v-
|
|
38536
|
+
.cn-dashboard-page__switch-row-spacer[data-v-42bc14a4] {
|
|
38537
38537
|
flex: 1 1 auto;
|
|
38538
38538
|
}
|
|
38539
|
-
.cn-dashboard-page__view-link[data-v-
|
|
38539
|
+
.cn-dashboard-page__view-link[data-v-42bc14a4] {
|
|
38540
38540
|
display: inline-flex;
|
|
38541
38541
|
align-items: center;
|
|
38542
38542
|
gap: 6px;
|
|
@@ -38550,36 +38550,36 @@ button.cn-object-calendar__overflow[data-v-e1c69ee2] {
|
|
|
38550
38550
|
white-space: nowrap;
|
|
38551
38551
|
text-decoration: none !important;
|
|
38552
38552
|
}
|
|
38553
|
-
.cn-dashboard-page__view-link[data-v-
|
|
38553
|
+
.cn-dashboard-page__view-link[data-v-42bc14a4]:hover {
|
|
38554
38554
|
background: var(--color-primary-element-light-hover, var(--color-primary-element-light));
|
|
38555
38555
|
}
|
|
38556
|
-
.cn-dashboard-page__view-link[data-v-
|
|
38556
|
+
.cn-dashboard-page__view-link[data-v-42bc14a4]:focus-visible {
|
|
38557
38557
|
outline: 2px solid var(--color-main-text);
|
|
38558
38558
|
outline-offset: 2px;
|
|
38559
38559
|
}
|
|
38560
|
-
.cn-dashboard-page__view-link-icon[data-v-
|
|
38560
|
+
.cn-dashboard-page__view-link-icon[data-v-42bc14a4] {
|
|
38561
38561
|
display: inline-flex;
|
|
38562
38562
|
}
|
|
38563
38563
|
|
|
38564
38564
|
/* The period group, in a row of its own between the header and the KPI row. */
|
|
38565
|
-
.cn-dashboard-page__date-range--segmented[data-v-
|
|
38565
|
+
.cn-dashboard-page__date-range--segmented[data-v-42bc14a4] {
|
|
38566
38566
|
margin-bottom: 16px;
|
|
38567
38567
|
}
|
|
38568
|
-
.cn-dashboard-page__date-segmented[data-v-
|
|
38568
|
+
.cn-dashboard-page__date-segmented[data-v-42bc14a4] {
|
|
38569
38569
|
display: flex;
|
|
38570
38570
|
align-items: center;
|
|
38571
38571
|
gap: 8px;
|
|
38572
38572
|
}
|
|
38573
38573
|
|
|
38574
38574
|
/* The KPI row above the grid: tiles are cells of the auto-fit grid. */
|
|
38575
|
-
.cn-dashboard-page__kpi-row[data-v-
|
|
38575
|
+
.cn-dashboard-page__kpi-row[data-v-42bc14a4] {
|
|
38576
38576
|
margin-bottom: var(--cn-board-section-gap, 16px);
|
|
38577
38577
|
}
|
|
38578
|
-
.cn-dashboard-page__kpi-cell[data-v-
|
|
38578
|
+
.cn-dashboard-page__kpi-cell[data-v-42bc14a4] {
|
|
38579
38579
|
display: flex;
|
|
38580
38580
|
min-width: 0;
|
|
38581
38581
|
}
|
|
38582
|
-
.cn-dashboard-page__kpi-cell[data-v-
|
|
38582
|
+
.cn-dashboard-page__kpi-cell[data-v-42bc14a4] > * {
|
|
38583
38583
|
flex: 1 1 auto;
|
|
38584
38584
|
min-width: 0;
|
|
38585
38585
|
}
|
|
@@ -38592,7 +38592,7 @@ button.cn-object-calendar__overflow[data-v-e1c69ee2] {
|
|
|
38592
38592
|
scoped chip rules then silently don't match and the chip collapses into
|
|
38593
38593
|
NcActions' ~30px icon slot (text wraps to "Last / 30 / days"). The
|
|
38594
38594
|
data-testid is stable across chunks, so those rules match regardless. */
|
|
38595
|
-
.cn-dashboard-page__header-actions[data-v-
|
|
38595
|
+
.cn-dashboard-page__header-actions[data-v-42bc14a4] {
|
|
38596
38596
|
display: flex;
|
|
38597
38597
|
align-items: center;
|
|
38598
38598
|
gap: 8px;
|
|
@@ -38608,19 +38608,19 @@ button.cn-object-calendar__overflow[data-v-e1c69ee2] {
|
|
|
38608
38608
|
|
|
38609
38609
|
/* Date-range header band. Kept compact so it doesn't open a tall gap
|
|
38610
38610
|
above the grid; the pills variant is a single tidy toggle row. */
|
|
38611
|
-
.cn-dashboard-page__date-range[data-v-
|
|
38611
|
+
.cn-dashboard-page__date-range[data-v-42bc14a4] {
|
|
38612
38612
|
margin-bottom: 12px;
|
|
38613
38613
|
}
|
|
38614
|
-
.cn-dashboard-page__date-range--pills[data-v-
|
|
38614
|
+
.cn-dashboard-page__date-range--pills[data-v-42bc14a4] {
|
|
38615
38615
|
margin-bottom: 8px;
|
|
38616
38616
|
}
|
|
38617
|
-
.cn-dashboard-page__date-pills[data-v-
|
|
38617
|
+
.cn-dashboard-page__date-pills[data-v-42bc14a4] {
|
|
38618
38618
|
display: flex;
|
|
38619
38619
|
align-items: center;
|
|
38620
38620
|
flex-wrap: wrap;
|
|
38621
38621
|
gap: 6px;
|
|
38622
38622
|
}
|
|
38623
|
-
.cn-dashboard-page__date-pill[data-v-
|
|
38623
|
+
.cn-dashboard-page__date-pill[data-v-42bc14a4] {
|
|
38624
38624
|
display: inline-flex;
|
|
38625
38625
|
align-items: center;
|
|
38626
38626
|
padding: 5px 14px;
|
|
@@ -38635,77 +38635,77 @@ button.cn-object-calendar__overflow[data-v-e1c69ee2] {
|
|
|
38635
38635
|
cursor: pointer;
|
|
38636
38636
|
transition: background-color 100ms ease, border-color 100ms ease, color 100ms ease;
|
|
38637
38637
|
}
|
|
38638
|
-
.cn-dashboard-page__date-pill[data-v-
|
|
38638
|
+
.cn-dashboard-page__date-pill[data-v-42bc14a4]:hover {
|
|
38639
38639
|
background: var(--color-background-hover);
|
|
38640
38640
|
}
|
|
38641
|
-
.cn-dashboard-page__date-pill[data-v-
|
|
38641
|
+
.cn-dashboard-page__date-pill[data-v-42bc14a4]:focus-visible {
|
|
38642
38642
|
outline: 2px solid var(--color-primary-element);
|
|
38643
38643
|
outline-offset: 2px;
|
|
38644
38644
|
}
|
|
38645
|
-
.cn-dashboard-page__date-pill--active[data-v-
|
|
38645
|
+
.cn-dashboard-page__date-pill--active[data-v-42bc14a4] {
|
|
38646
38646
|
background: var(--color-primary-element);
|
|
38647
38647
|
border-color: var(--color-primary-element);
|
|
38648
38648
|
color: var(--color-primary-element-text);
|
|
38649
38649
|
}
|
|
38650
|
-
.cn-dashboard-page__date-pill--active[data-v-
|
|
38650
|
+
.cn-dashboard-page__date-pill--active[data-v-42bc14a4]:hover {
|
|
38651
38651
|
background: var(--color-primary-element-hover, var(--color-primary-element));
|
|
38652
38652
|
}
|
|
38653
38653
|
|
|
38654
38654
|
/* The custom-range pill is de-emphasised — dashed outline, muted text —
|
|
38655
38655
|
so it reads as an escape hatch rather than a primary option. */
|
|
38656
|
-
.cn-dashboard-page__date-pill--custom[data-v-
|
|
38656
|
+
.cn-dashboard-page__date-pill--custom[data-v-42bc14a4] {
|
|
38657
38657
|
border-style: dashed;
|
|
38658
38658
|
color: var(--color-text-maxcontrast);
|
|
38659
38659
|
}
|
|
38660
|
-
.cn-dashboard-page__date-pill-custom[data-v-
|
|
38660
|
+
.cn-dashboard-page__date-pill-custom[data-v-42bc14a4] .action-item__menutoggle {
|
|
38661
38661
|
min-width: 0;
|
|
38662
38662
|
min-height: 0;
|
|
38663
38663
|
padding: 0;
|
|
38664
38664
|
background: transparent;
|
|
38665
38665
|
border: none;
|
|
38666
38666
|
}
|
|
38667
|
-
.cn-dashboard-page__date-pill-custom[data-v-
|
|
38668
|
-
.cn-dashboard-page__date-pill-custom[data-v-
|
|
38669
|
-
.cn-dashboard-page__date-pill-custom[data-v-
|
|
38667
|
+
.cn-dashboard-page__date-pill-custom[data-v-42bc14a4] .button-vue,
|
|
38668
|
+
.cn-dashboard-page__date-pill-custom[data-v-42bc14a4] .button-vue__wrapper,
|
|
38669
|
+
.cn-dashboard-page__date-pill-custom[data-v-42bc14a4] .button-vue__icon {
|
|
38670
38670
|
width: auto !important;
|
|
38671
38671
|
min-width: 0 !important;
|
|
38672
38672
|
overflow: visible !important;
|
|
38673
38673
|
}
|
|
38674
|
-
.cn-dashboard-page__empty[data-v-
|
|
38674
|
+
.cn-dashboard-page__empty[data-v-42bc14a4] {
|
|
38675
38675
|
padding: 60px 20px;
|
|
38676
38676
|
}
|
|
38677
38677
|
|
|
38678
38678
|
/* The page's view switch on a page without a header row: at the end of its
|
|
38679
38679
|
own row, above the grids, where the header would have put it. */
|
|
38680
|
-
.cn-dashboard-page__view-switch[data-v-
|
|
38680
|
+
.cn-dashboard-page__view-switch[data-v-42bc14a4] {
|
|
38681
38681
|
display: flex;
|
|
38682
38682
|
justify-content: flex-end;
|
|
38683
38683
|
margin-bottom: 12px;
|
|
38684
38684
|
}
|
|
38685
38685
|
|
|
38686
38686
|
/* A view the switch shows with nothing to draw. */
|
|
38687
|
-
.cn-page-view-region--empty[data-v-
|
|
38687
|
+
.cn-page-view-region--empty[data-v-42bc14a4] {
|
|
38688
38688
|
padding: 40px 20px;
|
|
38689
38689
|
}
|
|
38690
|
-
.cn-dashboard-page__page-filters[data-v-
|
|
38690
|
+
.cn-dashboard-page__page-filters[data-v-42bc14a4] {
|
|
38691
38691
|
display: flex;
|
|
38692
38692
|
flex-wrap: wrap;
|
|
38693
38693
|
gap: 16px;
|
|
38694
38694
|
align-items: flex-end;
|
|
38695
38695
|
margin-bottom: 12px;
|
|
38696
38696
|
}
|
|
38697
|
-
.cn-dashboard-page__page-filter[data-v-
|
|
38697
|
+
.cn-dashboard-page__page-filter[data-v-42bc14a4] {
|
|
38698
38698
|
display: flex;
|
|
38699
38699
|
flex-direction: column;
|
|
38700
38700
|
gap: 4px;
|
|
38701
38701
|
min-width: 200px;
|
|
38702
38702
|
}
|
|
38703
|
-
.cn-dashboard-page__page-filter-label[data-v-
|
|
38703
|
+
.cn-dashboard-page__page-filter-label[data-v-42bc14a4] {
|
|
38704
38704
|
font-size: 0.85em;
|
|
38705
38705
|
font-weight: 600;
|
|
38706
38706
|
color: var(--color-text-maxcontrast);
|
|
38707
38707
|
}
|
|
38708
|
-
.cn-dashboard-page__widget-edit[data-v-
|
|
38708
|
+
.cn-dashboard-page__widget-edit[data-v-42bc14a4] {
|
|
38709
38709
|
position: absolute;
|
|
38710
38710
|
top: 4px;
|
|
38711
38711
|
inset-inline-end: 4px;
|
|
@@ -38724,8 +38724,8 @@ button.cn-object-calendar__overflow[data-v-e1c69ee2] {
|
|
|
38724
38724
|
and centre the card. */
|
|
38725
38725
|
/* `safe` centre so an overflowing tile does not spill out of the top, where
|
|
38726
38726
|
flex overflow cannot be scrolled back to. Same as detail-page.css. */
|
|
38727
|
-
.cn-dashboard-page__card-fit[data-v-
|
|
38728
|
-
.cn-dashboard-page__card-fit.cn-widget-wrapper--flush[data-v-
|
|
38727
|
+
.cn-dashboard-page__card-fit[data-v-42bc14a4] .cn-widget-wrapper__content,
|
|
38728
|
+
.cn-dashboard-page__card-fit.cn-widget-wrapper--flush[data-v-42bc14a4] .cn-widget-wrapper__content {
|
|
38729
38729
|
overflow: hidden;
|
|
38730
38730
|
display: flex;
|
|
38731
38731
|
flex-direction: column;
|
|
@@ -38741,7 +38741,7 @@ button.cn-object-calendar__overflow[data-v-e1c69ee2] {
|
|
|
38741
38741
|
shows up as a scrollbar the user has to drag to see the whole graph. Same
|
|
38742
38742
|
shape as the card-fit rule above, minus the padding: chart tiles render
|
|
38743
38743
|
`flush`, and apexcharts already draws its own margins. */
|
|
38744
|
-
.cn-dashboard-page__chart-fit[data-v-
|
|
38744
|
+
.cn-dashboard-page__chart-fit[data-v-42bc14a4] .cn-widget-wrapper__content {
|
|
38745
38745
|
overflow: hidden;
|
|
38746
38746
|
display: flex;
|
|
38747
38747
|
flex-direction: column;
|
|
@@ -38751,13 +38751,13 @@ button.cn-object-calendar__overflow[data-v-e1c69ee2] {
|
|
|
38751
38751
|
/* Card content (stat / gauge / delta) must fit the tile width: let the
|
|
38752
38752
|
widget shrink and its value/label truncate instead of pushing past the
|
|
38753
38753
|
tile edge (the horizontal clip on long currency values). */
|
|
38754
|
-
.cn-dashboard-page__card-fit[data-v-
|
|
38755
|
-
.cn-dashboard-page__card-fit[data-v-
|
|
38756
|
-
.cn-dashboard-page__card-fit[data-v-
|
|
38754
|
+
.cn-dashboard-page__card-fit[data-v-42bc14a4] .cn-stat-widget,
|
|
38755
|
+
.cn-dashboard-page__card-fit[data-v-42bc14a4] .cn-gauge-widget,
|
|
38756
|
+
.cn-dashboard-page__card-fit[data-v-42bc14a4] .cn-delta-widget {
|
|
38757
38757
|
min-width: 0;
|
|
38758
38758
|
max-width: 100%;
|
|
38759
38759
|
}
|
|
38760
|
-
.cn-dashboard-page__unknown[data-v-
|
|
38760
|
+
.cn-dashboard-page__unknown[data-v-42bc14a4] {
|
|
38761
38761
|
display: flex;
|
|
38762
38762
|
align-items: center;
|
|
38763
38763
|
justify-content: center;
|
|
@@ -38768,29 +38768,29 @@ button.cn-object-calendar__overflow[data-v-e1c69ee2] {
|
|
|
38768
38768
|
}
|
|
38769
38769
|
|
|
38770
38770
|
/* widget-ref content items */
|
|
38771
|
-
.cn-dashboard-page__content[data-v-
|
|
38771
|
+
.cn-dashboard-page__content[data-v-42bc14a4] {
|
|
38772
38772
|
display: flex;
|
|
38773
38773
|
flex-direction: column;
|
|
38774
38774
|
gap: 16px;
|
|
38775
38775
|
}
|
|
38776
|
-
.cn-dashboard-page__content-item[data-v-
|
|
38776
|
+
.cn-dashboard-page__content-item[data-v-42bc14a4] {
|
|
38777
38777
|
width: 100%;
|
|
38778
38778
|
}
|
|
38779
38779
|
/* On a phone (screens-dashboard-phone) the touch targets of a board dashboard
|
|
38780
38780
|
are 44px: the widget Actions menu and the KPI tiles. */
|
|
38781
38781
|
@media (max-width: 640px) {
|
|
38782
|
-
.cn-dashboard-page--board[data-v-
|
|
38783
|
-
.cn-dashboard-page--board[data-v-
|
|
38782
|
+
.cn-dashboard-page--board[data-v-42bc14a4] .cn-widget-wrapper__actions .action-item__menutoggle,
|
|
38783
|
+
.cn-dashboard-page--board[data-v-42bc14a4] .cn-widget-wrapper__actions .button-vue {
|
|
38784
38784
|
min-height: 44px;
|
|
38785
38785
|
}
|
|
38786
|
-
.cn-dashboard-page--board .cn-dashboard-page__kpi-cell[data-v-
|
|
38786
|
+
.cn-dashboard-page--board .cn-dashboard-page__kpi-cell[data-v-42bc14a4] {
|
|
38787
38787
|
min-height: 44px;
|
|
38788
38788
|
}
|
|
38789
38789
|
}
|
|
38790
38790
|
|
|
38791
38791
|
/* What a registry widget reports for its card header (`header` event): the
|
|
38792
38792
|
note beside the link ("3 in total") and one action, as on a detail page. */
|
|
38793
|
-
.cn-dashboard-page__widget-count[data-v-
|
|
38793
|
+
.cn-dashboard-page__widget-count[data-v-42bc14a4] {
|
|
38794
38794
|
align-self: center;
|
|
38795
38795
|
padding: 1px 8px;
|
|
38796
38796
|
border-radius: 10px;
|
|
@@ -38799,13 +38799,13 @@ button.cn-object-calendar__overflow[data-v-e1c69ee2] {
|
|
|
38799
38799
|
font-size: 13px;
|
|
38800
38800
|
font-weight: 600;
|
|
38801
38801
|
}
|
|
38802
|
-
.cn-dashboard-page__widget-note[data-v-
|
|
38802
|
+
.cn-dashboard-page__widget-note[data-v-42bc14a4] {
|
|
38803
38803
|
align-self: center;
|
|
38804
38804
|
color: var(--color-text-maxcontrast);
|
|
38805
38805
|
font-size: 14px;
|
|
38806
38806
|
white-space: nowrap;
|
|
38807
38807
|
}
|
|
38808
|
-
.cn-dashboard-page__widget-action[data-v-
|
|
38808
|
+
.cn-dashboard-page__widget-action[data-v-42bc14a4] {
|
|
38809
38809
|
display: inline-flex;
|
|
38810
38810
|
align-items: center;
|
|
38811
38811
|
align-self: center;
|
|
@@ -38821,7 +38821,7 @@ button.cn-object-calendar__overflow[data-v-e1c69ee2] {
|
|
|
38821
38821
|
white-space: nowrap;
|
|
38822
38822
|
cursor: pointer;
|
|
38823
38823
|
}
|
|
38824
|
-
.cn-dashboard-page__widget-action[data-v-
|
|
38824
|
+
.cn-dashboard-page__widget-action[data-v-42bc14a4]:focus-visible {
|
|
38825
38825
|
outline: 2px solid var(--color-primary-element);
|
|
38826
38826
|
outline-offset: 2px;
|
|
38827
38827
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { camelize } from 'vue';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
5
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
const warned = new Set();
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Add the prop names one component options object declares, following its
|
|
13
|
+
* `extends` and `mixins`.
|
|
14
|
+
*
|
|
15
|
+
* @param {object} options A component options object.
|
|
16
|
+
* @param {Set<string>} names The set to add camelCase prop names to.
|
|
17
|
+
* @param {number} depth Recursion guard.
|
|
18
|
+
*/
|
|
19
|
+
function collect(options, names, depth) {
|
|
20
|
+
if (!options || typeof options !== 'object' || depth > 8) {
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
const props = options.props;
|
|
24
|
+
if (Array.isArray(props)) {
|
|
25
|
+
props.forEach((name) => names.add(camelize(String(name))));
|
|
26
|
+
} else if (props && typeof props === 'object') {
|
|
27
|
+
Object.keys(props).forEach((name) => names.add(camelize(name)));
|
|
28
|
+
}
|
|
29
|
+
collect(options.extends, names, depth + 1);
|
|
30
|
+
if (Array.isArray(options.mixins)) {
|
|
31
|
+
options.mixins.forEach((mixin) => collect(mixin, names, depth + 1));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The prop names a component declares, or `null` when it takes every
|
|
37
|
+
* attribute itself and so must keep receiving all of them:
|
|
38
|
+
* - `inheritAttrs: false`: the component forwards `$attrs` on purpose;
|
|
39
|
+
* - a functional component without `props`: its attrs are its props;
|
|
40
|
+
* - an async component that has not resolved yet.
|
|
41
|
+
*
|
|
42
|
+
* @spec openspec/changes/dashboard-declared-widget-props/specs/dashboard-page/spec.md#requirement-a-dashboard-widget-receives-only-the-content-keys-it-declares
|
|
43
|
+
* @param {object|((...args: unknown[]) => unknown)|null} component A component definition.
|
|
44
|
+
* @return {Set<string>|null} camelCase prop names, or null for "all".
|
|
45
|
+
*/
|
|
46
|
+
function declaredPropNames(component) {
|
|
47
|
+
if (!component) {
|
|
48
|
+
return null
|
|
49
|
+
}
|
|
50
|
+
if (typeof component === 'function') {
|
|
51
|
+
if (!component.props) {
|
|
52
|
+
return null
|
|
53
|
+
}
|
|
54
|
+
const names = new Set();
|
|
55
|
+
collect({ props: component.props }, names, 0);
|
|
56
|
+
return names
|
|
57
|
+
}
|
|
58
|
+
if (typeof component !== 'object') {
|
|
59
|
+
return null
|
|
60
|
+
}
|
|
61
|
+
if (component.__asyncLoader) {
|
|
62
|
+
return component.__asyncResolved ? declaredPropNames(component.__asyncResolved) : null
|
|
63
|
+
}
|
|
64
|
+
const chain = [component, component.extends, ...(component.mixins || [])];
|
|
65
|
+
if (chain.some((options) => options && options.inheritAttrs === false)) {
|
|
66
|
+
return null
|
|
67
|
+
}
|
|
68
|
+
const names = new Set();
|
|
69
|
+
collect(component, names, 0);
|
|
70
|
+
return names
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Keep the entries of `bindings` whose key `component` declares as a prop.
|
|
75
|
+
* A component for which {@link declaredPropNames} returns null gets every
|
|
76
|
+
* entry. A dropped key is warned about once outside production when the
|
|
77
|
+
* component has no `content` prop either, so the key reaches nothing.
|
|
78
|
+
*
|
|
79
|
+
* @spec openspec/changes/dashboard-declared-widget-props/specs/dashboard-page/spec.md#requirement-a-dashboard-widget-receives-only-the-content-keys-it-declares
|
|
80
|
+
* @param {object|((...args: unknown[]) => unknown)|null} component The component that will receive the bindings.
|
|
81
|
+
* @param {object} bindings The candidate bindings.
|
|
82
|
+
* @param {string} label Names the component in the warning (for example the widget type).
|
|
83
|
+
* @return {object} The bindings the component declares.
|
|
84
|
+
*/
|
|
85
|
+
function pickDeclaredProps(component, bindings, label) {
|
|
86
|
+
const declared = declaredPropNames(component);
|
|
87
|
+
if (!declared || !bindings || typeof bindings !== 'object') {
|
|
88
|
+
return bindings
|
|
89
|
+
}
|
|
90
|
+
const out = {};
|
|
91
|
+
for (const [key, value] of Object.entries(bindings)) {
|
|
92
|
+
if (declared.has(camelize(key))) {
|
|
93
|
+
out[key] = value;
|
|
94
|
+
continue
|
|
95
|
+
}
|
|
96
|
+
if (process.env.NODE_ENV !== 'production' && !declared.has('content') && !warned.has(`${label}\u0000${key}`)) {
|
|
97
|
+
warned.add(`${label}\u0000${key}`);
|
|
98
|
+
// eslint-disable-next-line no-console
|
|
99
|
+
console.warn(`[nextcloud-vue] content key "${key}" is not passed to widget "${label}": it declares neither that prop nor \`content\`.`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return out
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export { declaredPropNames, pickDeclaredProps };
|
|
106
|
+
//# sourceMappingURL=declaredProps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"declaredProps.js","sources":["../../../src/utils/declaredProps.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n */\n\n/*\n * `process.env.NODE_ENV` is substituted by every consuming app's bundler at\n * build time; see NcSelectTags.js.\n */\n/* global process */\n\nimport { camelize } from 'vue'\n\nconst warned = new Set()\n\n/**\n * Add the prop names one component options object declares, following its\n * `extends` and `mixins`.\n *\n * @param {object} options A component options object.\n * @param {Set<string>} names The set to add camelCase prop names to.\n * @param {number} depth Recursion guard.\n */\nfunction collect(options, names, depth) {\n\tif (!options || typeof options !== 'object' || depth > 8) {\n\t\treturn\n\t}\n\tconst props = options.props\n\tif (Array.isArray(props)) {\n\t\tprops.forEach((name) => names.add(camelize(String(name))))\n\t} else if (props && typeof props === 'object') {\n\t\tObject.keys(props).forEach((name) => names.add(camelize(name)))\n\t}\n\tcollect(options.extends, names, depth + 1)\n\tif (Array.isArray(options.mixins)) {\n\t\toptions.mixins.forEach((mixin) => collect(mixin, names, depth + 1))\n\t}\n}\n\n/**\n * The prop names a component declares, or `null` when it takes every\n * attribute itself and so must keep receiving all of them:\n * - `inheritAttrs: false`: the component forwards `$attrs` on purpose;\n * - a functional component without `props`: its attrs are its props;\n * - an async component that has not resolved yet.\n *\n * @spec openspec/changes/dashboard-declared-widget-props/specs/dashboard-page/spec.md#requirement-a-dashboard-widget-receives-only-the-content-keys-it-declares\n * @param {object|((...args: unknown[]) => unknown)|null} component A component definition.\n * @return {Set<string>|null} camelCase prop names, or null for \"all\".\n */\nexport function declaredPropNames(component) {\n\tif (!component) {\n\t\treturn null\n\t}\n\tif (typeof component === 'function') {\n\t\tif (!component.props) {\n\t\t\treturn null\n\t\t}\n\t\tconst names = new Set()\n\t\tcollect({ props: component.props }, names, 0)\n\t\treturn names\n\t}\n\tif (typeof component !== 'object') {\n\t\treturn null\n\t}\n\tif (component.__asyncLoader) {\n\t\treturn component.__asyncResolved ? declaredPropNames(component.__asyncResolved) : null\n\t}\n\tconst chain = [component, component.extends, ...(component.mixins || [])]\n\tif (chain.some((options) => options && options.inheritAttrs === false)) {\n\t\treturn null\n\t}\n\tconst names = new Set()\n\tcollect(component, names, 0)\n\treturn names\n}\n\n/**\n * Keep the entries of `bindings` whose key `component` declares as a prop.\n * A component for which {@link declaredPropNames} returns null gets every\n * entry. A dropped key is warned about once outside production when the\n * component has no `content` prop either, so the key reaches nothing.\n *\n * @spec openspec/changes/dashboard-declared-widget-props/specs/dashboard-page/spec.md#requirement-a-dashboard-widget-receives-only-the-content-keys-it-declares\n * @param {object|((...args: unknown[]) => unknown)|null} component The component that will receive the bindings.\n * @param {object} bindings The candidate bindings.\n * @param {string} label Names the component in the warning (for example the widget type).\n * @return {object} The bindings the component declares.\n */\nexport function pickDeclaredProps(component, bindings, label) {\n\tconst declared = declaredPropNames(component)\n\tif (!declared || !bindings || typeof bindings !== 'object') {\n\t\treturn bindings\n\t}\n\tconst out = {}\n\tfor (const [key, value] of Object.entries(bindings)) {\n\t\tif (declared.has(camelize(key))) {\n\t\t\tout[key] = value\n\t\t\tcontinue\n\t\t}\n\t\tif (process.env.NODE_ENV !== 'production' && !declared.has('content') && !warned.has(`${label}\\u0000${key}`)) {\n\t\t\twarned.add(`${label}\\u0000${key}`)\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.warn(`[nextcloud-vue] content key \"${key}\" is not passed to widget \"${label}\": it declares neither that prop nor \\`content\\`.`)\n\t\t}\n\t}\n\treturn out\n}\n\n/**\n * Forget which keys were warned about. For tests.\n */\nexport function resetDeclaredPropWarnings() {\n\twarned.clear()\n}\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AAQA;AACA,MAAM,MAAM,GAAG,IAAI,GAAG,GAAE;AACxB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE;AACxC,CAAC,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,KAAK,GAAG,CAAC,EAAE;AAC3D,EAAE,MAAM;AACR,EAAE;AACF,CAAC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAK;AAC5B,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;AAC3B,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAC;AAC5D,EAAE,MAAM,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;AAChD,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAC;AACjE,EAAE;AACF,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG,CAAC,EAAC;AAC3C,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;AACpC,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,KAAK,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,EAAC;AACrE,EAAE;AACF,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,iBAAiB,CAAC,SAAS,EAAE;AAC7C,CAAC,IAAI,CAAC,SAAS,EAAE;AACjB,EAAE,OAAO,IAAI;AACb,EAAE;AACF,CAAC,IAAI,OAAO,SAAS,KAAK,UAAU,EAAE;AACtC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE;AACxB,GAAG,OAAO,IAAI;AACd,GAAG;AACH,EAAE,MAAM,KAAK,GAAG,IAAI,GAAG,GAAE;AACzB,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAC;AAC/C,EAAE,OAAO,KAAK;AACd,EAAE;AACF,CAAC,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE;AACpC,EAAE,OAAO,IAAI;AACb,EAAE;AACF,CAAC,IAAI,SAAS,CAAC,aAAa,EAAE;AAC9B,EAAE,OAAO,SAAS,CAAC,eAAe,GAAG,iBAAiB,CAAC,SAAS,CAAC,eAAe,CAAC,GAAG,IAAI;AACxF,EAAE;AACF,CAAC,MAAM,KAAK,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,OAAO,EAAE,IAAI,SAAS,CAAC,MAAM,IAAI,EAAE,CAAC,EAAC;AAC1E,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,KAAK,OAAO,IAAI,OAAO,CAAC,YAAY,KAAK,KAAK,CAAC,EAAE;AACzE,EAAE,OAAO,IAAI;AACb,EAAE;AACF,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,GAAE;AACxB,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,EAAE,CAAC,EAAC;AAC7B,CAAC,OAAO,KAAK;AACb,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,iBAAiB,CAAC,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE;AAC9D,CAAC,MAAM,QAAQ,GAAG,iBAAiB,CAAC,SAAS,EAAC;AAC9C,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE;AAC7D,EAAE,OAAO,QAAQ;AACjB,EAAE;AACF,CAAC,MAAM,GAAG,GAAG,GAAE;AACf,CAAC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE;AACtD,EAAE,IAAI,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE;AACnC,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,MAAK;AACnB,GAAG,QAAQ;AACX,GAAG;AACH,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE;AAChH,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,EAAC;AACrC;AACA,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,6BAA6B,EAAE,GAAG,CAAC,2BAA2B,EAAE,KAAK,CAAC,iDAAiD,CAAC,EAAC;AAC1I,GAAG;AACH,EAAE;AACF,CAAC,OAAO,GAAG;AACX;;;;"}
|
|
@@ -65,8 +65,11 @@ function readGridGeometry(items) {
|
|
|
65
65
|
* full-width card under the stat tiles landed below the lists.
|
|
66
66
|
*
|
|
67
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
|
-
*
|
|
69
|
-
*
|
|
68
|
+
* The growing item is never among the items it moves, also when `nodes`
|
|
69
|
+
* holds another reference to it (a proxy of the same node, matched by `el`).
|
|
70
|
+
*
|
|
71
|
+
* @param {Array<{x: number, y: number, w: number, h: number, el: (HTMLElement|undefined)}>} nodes The grid's items (the growing one among them).
|
|
72
|
+
* @param {{x: number, y: number, w: number, h: number, el: (HTMLElement|undefined)}} node The growing item.
|
|
70
73
|
* @param {number} h Its new height, in grid rows.
|
|
71
74
|
* @return {Array<{node: object, y: number}>} The items to move and their new row, bottom-most first.
|
|
72
75
|
*/
|
|
@@ -81,7 +84,8 @@ function planGrowth(nodes, node, h) {
|
|
|
81
84
|
while (queue.length > 0) {
|
|
82
85
|
const rect = queue.shift();
|
|
83
86
|
for (const other of nodes) {
|
|
84
|
-
|
|
87
|
+
const self = other === node || (node.el && other.el === node.el);
|
|
88
|
+
if (self || moved.has(other) || !overlaps(rect, other)) {
|
|
85
89
|
continue
|
|
86
90
|
}
|
|
87
91
|
const next = { x: other.x, y: other.y + delta, w: other.w, h: other.h };
|
|
@@ -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\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\
|
|
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 * The growing item is never among the items it moves, also when `nodes`\n * holds another reference to it (a proxy of the same node, matched by `el`).\n *\n * @param {Array<{x: number, y: number, w: number, h: number, el: (HTMLElement|undefined)}>} nodes The grid's items (the growing one among them).\n * @param {{x: number, y: number, w: number, h: number, el: (HTMLElement|undefined)}} 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\tconst self = other === node || (node.el && other.el === node.el)\n\t\t\tif (self || 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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"}
|