picocrank 1.25.1 → 1.26.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/eslint.config.js +68 -0
- package/package.json +13 -3
- package/router.js +74 -0
- package/styles/dark-theme.css +21 -9
- package/styles/picocrank-extensions.css +131 -2
- package/supplemental-themes/catppuccin-latte-frappe/theme.css +12 -22
- package/supplemental-themes/dracula-alucard/theme.css +12 -22
- package/supplemental-themes/gruvbox-dark-light/theme.css +12 -22
- package/supplemental-themes/waffles/theme.css +2 -12
- package/vue/components/Breadcrumbs.vue +192 -45
- package/vue/components/Calendar.vue +93 -180
- package/vue/components/DangerZone.vue +186 -0
- package/vue/components/Header.vue +22 -6
- package/vue/components/Login.vue +6 -7
- package/vue/components/NavigationGrid.vue +26 -204
- package/vue/components/NotificationBlock.vue +120 -0
- package/vue/components/NotificationPopup.vue +44 -41
- package/vue/components/QuickSearch.vue +21 -47
- package/vue/components/RadioGroup.vue +4 -4
- package/vue/components/ReadOnlyTextArea.vue +1 -1
- package/vue/components/SidebarNavLink.vue +3 -3
- package/vue/components/Table.vue +109 -81
- package/vue/components/TableColumnFilterPopover.vue +7 -17
- package/vue/components/TableColumnOptionsPopover.vue +22 -27
- package/vue/components/Tabs.vue +366 -82
- package/vue/components/TopBar.vue +2 -2
- package/vue/composables/tableColumnPriorities.js +33 -0
- package/vue/composables/tableFilters.js +1 -1
- package/vue/data/navSectionHubs.js +196 -0
- package/vue/data/routeBreadcrumbs.js +90 -0
- package/vue/examples/Admin.vue +12 -12
- package/vue/examples/App.vue +27 -36
- package/vue/examples/CalendarExample.vue +32 -22
- package/vue/examples/ExampleHeaderThemeSwitcher.vue +1 -1
- package/vue/examples/Login.vue +0 -3
- package/vue/examples/LoginExample.vue +2 -14
- package/vue/examples/NavSectionHub.vue +36 -0
- package/vue/examples/PageStructure.vue +6 -3
- package/vue/examples/StatusExample.vue +248 -129
- package/vue/examples/TableExample.vue +68 -67
- package/vue/examples/TabsExample.vue +200 -255
- package/vue/examples/ThemePreview.vue +21 -23
- package/vue/examples/ViewItem.vue +32 -5
- package/vue/examples/Welcome.vue +3 -112
|
@@ -1,9 +1,34 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
<div class="notification-popup" role="status">
|
|
3
|
+
<NotificationBlock
|
|
4
|
+
:type="notificationType"
|
|
5
|
+
:label="popup.label || ''"
|
|
6
|
+
>
|
|
7
|
+
<span class="notification-popup-content">
|
|
8
|
+
<span class="notification-popup-message">{{ popup.message }}</span>
|
|
9
|
+
|
|
10
|
+
<RouterLink
|
|
11
|
+
v-if="popup.linkTo && !isExternalLink"
|
|
12
|
+
:to="popup.linkTo"
|
|
13
|
+
class="notification-popup-link"
|
|
14
|
+
@click="emit('dismiss', popup.id)"
|
|
15
|
+
>
|
|
16
|
+
{{ popup.linkLabel || 'View' }}
|
|
17
|
+
</RouterLink>
|
|
18
|
+
|
|
19
|
+
<a
|
|
20
|
+
v-else-if="popup.linkTo && isExternalLink"
|
|
21
|
+
:href="popup.linkTo"
|
|
22
|
+
class="notification-popup-link"
|
|
23
|
+
target="_blank"
|
|
24
|
+
rel="noopener noreferrer"
|
|
25
|
+
@click="emit('dismiss', popup.id)"
|
|
26
|
+
>
|
|
27
|
+
{{ popup.linkLabel || 'View' }}
|
|
28
|
+
</a>
|
|
29
|
+
</span>
|
|
30
|
+
</NotificationBlock>
|
|
31
|
+
|
|
7
32
|
<button
|
|
8
33
|
type="button"
|
|
9
34
|
class="notification-popup-dismiss"
|
|
@@ -12,33 +37,6 @@
|
|
|
12
37
|
>
|
|
13
38
|
<HugeiconsIcon :icon="Cancel01Icon" width="1em" height="1em" />
|
|
14
39
|
</button>
|
|
15
|
-
|
|
16
|
-
<div class="notification-popup-body">
|
|
17
|
-
<p class="notification-popup-message">
|
|
18
|
-
<strong v-if="popup.label">{{ popup.label }}:</strong>
|
|
19
|
-
{{ popup.message }}
|
|
20
|
-
</p>
|
|
21
|
-
|
|
22
|
-
<RouterLink
|
|
23
|
-
v-if="popup.linkTo && !isExternalLink"
|
|
24
|
-
:to="popup.linkTo"
|
|
25
|
-
class="notification-popup-link"
|
|
26
|
-
@click="emit('dismiss', popup.id)"
|
|
27
|
-
>
|
|
28
|
-
{{ popup.linkLabel || 'View' }}
|
|
29
|
-
</RouterLink>
|
|
30
|
-
|
|
31
|
-
<a
|
|
32
|
-
v-else-if="popup.linkTo && isExternalLink"
|
|
33
|
-
:href="popup.linkTo"
|
|
34
|
-
class="notification-popup-link"
|
|
35
|
-
target="_blank"
|
|
36
|
-
rel="noopener noreferrer"
|
|
37
|
-
@click="emit('dismiss', popup.id)"
|
|
38
|
-
>
|
|
39
|
-
{{ popup.linkLabel || 'View' }}
|
|
40
|
-
</a>
|
|
41
|
-
</div>
|
|
42
40
|
</div>
|
|
43
41
|
</template>
|
|
44
42
|
|
|
@@ -47,6 +45,7 @@ import { computed } from 'vue'
|
|
|
47
45
|
import { RouterLink } from 'vue-router'
|
|
48
46
|
import { HugeiconsIcon } from '@hugeicons/vue'
|
|
49
47
|
import { Cancel01Icon } from '@hugeicons/core-free-icons'
|
|
48
|
+
import NotificationBlock from './NotificationBlock.vue'
|
|
50
49
|
|
|
51
50
|
const props = defineProps({
|
|
52
51
|
popup: {
|
|
@@ -57,6 +56,8 @@ const props = defineProps({
|
|
|
57
56
|
|
|
58
57
|
const emit = defineEmits(['dismiss'])
|
|
59
58
|
|
|
59
|
+
const notificationType = computed(() => props.popup.class || 'info')
|
|
60
|
+
|
|
60
61
|
const isExternalLink = computed(() =>
|
|
61
62
|
typeof props.popup.linkTo === 'string' && /^https?:\/\//.test(props.popup.linkTo)
|
|
62
63
|
)
|
|
@@ -67,30 +68,32 @@ const isExternalLink = computed(() =>
|
|
|
67
68
|
position: relative;
|
|
68
69
|
width: min(22rem, calc(100vw - 2rem));
|
|
69
70
|
margin: 0;
|
|
70
|
-
|
|
71
|
-
cursor: default;
|
|
72
|
-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
71
|
+
box-shadow: 0 4px 12px color-mix(in srgb, var(--shadow-color) 60%, transparent);
|
|
73
72
|
}
|
|
74
73
|
|
|
75
|
-
.notification-popup
|
|
74
|
+
.notification-popup :deep(.notification) {
|
|
76
75
|
margin: 0;
|
|
77
76
|
}
|
|
78
77
|
|
|
79
|
-
.notification-popup-
|
|
78
|
+
.notification-popup-content {
|
|
80
79
|
display: flex;
|
|
81
80
|
flex-direction: column;
|
|
82
81
|
gap: 0.5rem;
|
|
83
82
|
}
|
|
84
83
|
|
|
84
|
+
.notification-popup-message {
|
|
85
|
+
display: block;
|
|
86
|
+
}
|
|
87
|
+
|
|
85
88
|
.notification-popup-link {
|
|
86
|
-
color:
|
|
89
|
+
color: var(--link-color);
|
|
87
90
|
font-weight: bold;
|
|
88
91
|
text-decoration: underline;
|
|
89
92
|
align-self: flex-start;
|
|
90
93
|
}
|
|
91
94
|
|
|
92
95
|
.notification-popup-link:hover {
|
|
93
|
-
|
|
96
|
+
text-decoration-thickness: 2px;
|
|
94
97
|
}
|
|
95
98
|
|
|
96
99
|
.notification-popup-dismiss {
|
|
@@ -106,13 +109,13 @@ const isExternalLink = computed(() =>
|
|
|
106
109
|
border: 0;
|
|
107
110
|
border-radius: 0.35rem;
|
|
108
111
|
background: transparent;
|
|
109
|
-
color:
|
|
112
|
+
color: var(--text-color);
|
|
110
113
|
cursor: pointer;
|
|
111
114
|
opacity: 0.75;
|
|
112
115
|
}
|
|
113
116
|
|
|
114
117
|
.notification-popup-dismiss:hover {
|
|
115
118
|
opacity: 1;
|
|
116
|
-
background:
|
|
119
|
+
background: var(--hover-background-color);
|
|
117
120
|
}
|
|
118
121
|
</style>
|
|
@@ -547,14 +547,14 @@ defineExpose({
|
|
|
547
547
|
width: min(32rem, calc(100vw - 2rem));
|
|
548
548
|
max-width: 32rem;
|
|
549
549
|
padding: 0;
|
|
550
|
-
border: 1px solid var(--border
|
|
551
|
-
background: var(--
|
|
550
|
+
border: 1px solid var(--quick-search-border);
|
|
551
|
+
background: var(--quick-search-dialog-bg);
|
|
552
552
|
/* Dialog is rendered under <header>, so do not inherit header's white text */
|
|
553
|
-
color: var(--
|
|
553
|
+
color: var(--quick-search-dialog-fg);
|
|
554
554
|
}
|
|
555
555
|
|
|
556
556
|
.quick-search-dialog::backdrop {
|
|
557
|
-
background-color:
|
|
557
|
+
background-color: var(--quick-search-backdrop);
|
|
558
558
|
}
|
|
559
559
|
|
|
560
560
|
.dialog-body {
|
|
@@ -573,23 +573,22 @@ defineExpose({
|
|
|
573
573
|
.search-input {
|
|
574
574
|
width: 100%;
|
|
575
575
|
padding: 0.75em 2.25em 0.75em 0.75em;
|
|
576
|
-
border: 1px solid var(--border
|
|
576
|
+
border: 1px solid var(--quick-search-border);
|
|
577
577
|
border-radius: 0.4em;
|
|
578
578
|
font-size: 1rem;
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
color: var(--text-color, #334155);
|
|
579
|
+
background-color: var(--quick-search-input-bg);
|
|
580
|
+
color: var(--quick-search-input-fg);
|
|
582
581
|
}
|
|
583
582
|
|
|
584
|
-
.search-input:focus {
|
|
585
|
-
|
|
586
|
-
|
|
583
|
+
.search-input:focus-visible {
|
|
584
|
+
outline: 2px solid var(--quick-search-focus-outline);
|
|
585
|
+
outline-offset: 2px;
|
|
587
586
|
}
|
|
588
587
|
|
|
589
588
|
.search-icon {
|
|
590
589
|
position: absolute;
|
|
591
590
|
right: 0.75rem;
|
|
592
|
-
color:
|
|
591
|
+
color: var(--quick-search-muted-fg);
|
|
593
592
|
pointer-events: none;
|
|
594
593
|
display: inline-flex;
|
|
595
594
|
}
|
|
@@ -606,12 +605,13 @@ defineExpose({
|
|
|
606
605
|
|
|
607
606
|
.search-status {
|
|
608
607
|
margin: 0;
|
|
608
|
+
color: var(--quick-search-muted-fg);
|
|
609
609
|
}
|
|
610
610
|
|
|
611
611
|
.search-results {
|
|
612
612
|
max-height: min(20rem, 50vh);
|
|
613
613
|
overflow-y: auto;
|
|
614
|
-
border: 1px solid var(--border
|
|
614
|
+
border: 1px solid var(--quick-search-border);
|
|
615
615
|
border-radius: 0.4em;
|
|
616
616
|
}
|
|
617
617
|
|
|
@@ -620,7 +620,7 @@ defineExpose({
|
|
|
620
620
|
align-items: center;
|
|
621
621
|
padding: 0.75rem 1rem;
|
|
622
622
|
cursor: pointer;
|
|
623
|
-
border-bottom: 1px solid var(--border
|
|
623
|
+
border-bottom: 1px solid var(--quick-search-border);
|
|
624
624
|
}
|
|
625
625
|
|
|
626
626
|
.search-result-item:last-child {
|
|
@@ -629,8 +629,8 @@ defineExpose({
|
|
|
629
629
|
|
|
630
630
|
.search-result-item:hover,
|
|
631
631
|
.search-result-item.active {
|
|
632
|
-
background-color: var(--hover-
|
|
633
|
-
color: var(--hover-
|
|
632
|
+
background-color: var(--quick-search-hover-bg);
|
|
633
|
+
color: var(--quick-search-hover-fg);
|
|
634
634
|
}
|
|
635
635
|
|
|
636
636
|
.result-content {
|
|
@@ -645,20 +645,20 @@ defineExpose({
|
|
|
645
645
|
|
|
646
646
|
.result-description {
|
|
647
647
|
font-size: 0.875rem;
|
|
648
|
-
color:
|
|
648
|
+
color: var(--quick-search-muted-fg);
|
|
649
649
|
margin-bottom: 0.25rem;
|
|
650
650
|
}
|
|
651
651
|
|
|
652
652
|
.result-category {
|
|
653
653
|
font-size: 0.75rem;
|
|
654
|
-
color:
|
|
654
|
+
color: var(--quick-search-subtle-fg);
|
|
655
655
|
text-transform: uppercase;
|
|
656
656
|
letter-spacing: 0.05em;
|
|
657
657
|
}
|
|
658
658
|
|
|
659
659
|
.result-icon {
|
|
660
660
|
margin-right: 0.75rem;
|
|
661
|
-
color:
|
|
661
|
+
color: var(--quick-search-muted-fg);
|
|
662
662
|
flex-shrink: 0;
|
|
663
663
|
display: inline-flex;
|
|
664
664
|
}
|
|
@@ -668,7 +668,7 @@ defineExpose({
|
|
|
668
668
|
flex-direction: column;
|
|
669
669
|
align-items: center;
|
|
670
670
|
padding: 1.5rem 1rem;
|
|
671
|
-
color:
|
|
671
|
+
color: var(--quick-search-muted-fg);
|
|
672
672
|
text-align: center;
|
|
673
673
|
}
|
|
674
674
|
|
|
@@ -682,35 +682,9 @@ defineExpose({
|
|
|
682
682
|
}
|
|
683
683
|
|
|
684
684
|
mark {
|
|
685
|
-
background-color: var(--
|
|
685
|
+
background-color: var(--quick-search-highlight-bg);
|
|
686
686
|
color: inherit;
|
|
687
687
|
padding: 0;
|
|
688
688
|
border-radius: 2px;
|
|
689
689
|
}
|
|
690
|
-
|
|
691
|
-
html[data-theme="dark"] {
|
|
692
|
-
.quick-search-dialog {
|
|
693
|
-
color: #f9fafb;
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
.search-input {
|
|
697
|
-
background-color: #1f2937;
|
|
698
|
-
border-color: #374151;
|
|
699
|
-
color: #f9fafb;
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
.search-icon,
|
|
703
|
-
.result-description,
|
|
704
|
-
.result-category,
|
|
705
|
-
.result-icon,
|
|
706
|
-
.no-results-content,
|
|
707
|
-
.search-status {
|
|
708
|
-
color: #9ca3af;
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
.search-input:focus {
|
|
712
|
-
border-color: #60a5fa;
|
|
713
|
-
box-shadow: 0 0 0 3px rgba(96, 165, 250, 0.1);
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
690
|
</style>
|
|
@@ -147,7 +147,7 @@ function select(option) {
|
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
.radio-group label:has(input:focus-visible) {
|
|
150
|
-
outline: 2px solid
|
|
150
|
+
outline: 2px solid var(--radio-group-focus-outline);
|
|
151
151
|
outline-offset: 2px;
|
|
152
152
|
z-index: 2;
|
|
153
153
|
}
|
|
@@ -155,8 +155,8 @@ function select(option) {
|
|
|
155
155
|
/* Beat Femtocrank's gray .radio-group focus/hover when the option is selected. */
|
|
156
156
|
.radio-group label:has(input:checked):hover,
|
|
157
157
|
.radio-group label:has(input:checked:focus-visible) {
|
|
158
|
-
background-color:
|
|
159
|
-
color:
|
|
158
|
+
background-color: var(--radio-group-checked-hover-bg);
|
|
159
|
+
color: var(--radio-group-checked-hover-fg);
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
.radio-group.disabled {
|
|
@@ -185,7 +185,7 @@ function select(option) {
|
|
|
185
185
|
|
|
186
186
|
.radio-group label.disabled:has(input:focus-visible),
|
|
187
187
|
.radio-group.disabled label:has(input:focus-visible) {
|
|
188
|
-
outline: 2px solid
|
|
188
|
+
outline: 2px solid var(--radio-group-focus-outline);
|
|
189
189
|
outline-offset: 2px;
|
|
190
190
|
}
|
|
191
191
|
</style>
|
|
@@ -121,7 +121,7 @@ a.active {
|
|
|
121
121
|
width: 0.5rem;
|
|
122
122
|
height: 0.5rem;
|
|
123
123
|
border-radius: 50%;
|
|
124
|
-
background: var(--
|
|
124
|
+
background: var(--karma-bad-tint);
|
|
125
125
|
flex-shrink: 0;
|
|
126
126
|
}
|
|
127
127
|
|
|
@@ -129,8 +129,8 @@ a.active {
|
|
|
129
129
|
min-width: 1.25rem;
|
|
130
130
|
padding: 0.1rem 0.4rem;
|
|
131
131
|
border-radius: 999px;
|
|
132
|
-
background: var(--
|
|
133
|
-
color:
|
|
132
|
+
background: var(--karma-bad-tint);
|
|
133
|
+
color: var(--text-color);
|
|
134
134
|
font-size: 0.75em;
|
|
135
135
|
font-weight: 600;
|
|
136
136
|
line-height: 1.2;
|
package/vue/components/Table.vue
CHANGED
|
@@ -37,27 +37,24 @@
|
|
|
37
37
|
v-if="showColumnOptionsButton"
|
|
38
38
|
class="actions table-column-options-header"
|
|
39
39
|
>
|
|
40
|
-
<
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
</button>
|
|
59
|
-
</span>
|
|
60
|
-
</div>
|
|
40
|
+
<span class="actions-menu-trigger">
|
|
41
|
+
<button
|
|
42
|
+
ref="columnOptionsButtonRef"
|
|
43
|
+
type="button"
|
|
44
|
+
aria-haspopup="dialog"
|
|
45
|
+
aria-label="Column options"
|
|
46
|
+
:aria-expanded="columnOptionsOpen ? 'true' : 'false'"
|
|
47
|
+
@click.stop="openColumnOptions"
|
|
48
|
+
>
|
|
49
|
+
<HugeiconsIcon
|
|
50
|
+
:icon="LayoutGridIcon"
|
|
51
|
+
width="0.95em"
|
|
52
|
+
height="0.95em"
|
|
53
|
+
:strokeWidth="2"
|
|
54
|
+
aria-hidden="true"
|
|
55
|
+
/>
|
|
56
|
+
</button>
|
|
57
|
+
</span>
|
|
61
58
|
</th>
|
|
62
59
|
</tr>
|
|
63
60
|
</thead>
|
|
@@ -71,7 +68,12 @@
|
|
|
71
68
|
<td
|
|
72
69
|
v-for="(header, cellIndex) in visibleHeaders"
|
|
73
70
|
:key="header.key || cellIndex"
|
|
74
|
-
:
|
|
71
|
+
:colspan="bodyColumnColspan(cellIndex) || undefined"
|
|
72
|
+
:class="[
|
|
73
|
+
cellClasses(header),
|
|
74
|
+
header.class,
|
|
75
|
+
isLastColumnWithOptions(cellIndex) ? 'table-last-column-with-options' : null,
|
|
76
|
+
]"
|
|
75
77
|
>
|
|
76
78
|
<slot
|
|
77
79
|
v-if="slots[`cell-${header.key}`]"
|
|
@@ -89,11 +91,6 @@
|
|
|
89
91
|
{{ row[header.key] }}
|
|
90
92
|
</span>
|
|
91
93
|
</td>
|
|
92
|
-
<td
|
|
93
|
-
v-if="showColumnOptionsButton"
|
|
94
|
-
class="actions table-column-options-cell"
|
|
95
|
-
aria-hidden="true"
|
|
96
|
-
/>
|
|
97
94
|
</tr>
|
|
98
95
|
</tbody>
|
|
99
96
|
</table>
|
|
@@ -135,6 +132,7 @@
|
|
|
135
132
|
:column-keys="resolvedColumnOrder"
|
|
136
133
|
:visible-keys="visibleColumnKeys"
|
|
137
134
|
:column-priorities="resolvedColumnPriorities"
|
|
135
|
+
:default-column-priorities="defaultColumnPriorities"
|
|
138
136
|
:filters="activeFilters"
|
|
139
137
|
:rows="props.data"
|
|
140
138
|
:table-id="props.tableId"
|
|
@@ -161,11 +159,9 @@ import TableColumnFilterPopover from './TableColumnFilterPopover.vue'
|
|
|
161
159
|
import TableColumnOptionsPopover from './TableColumnOptionsPopover.vue'
|
|
162
160
|
import { useLongPress } from '../composables/useLongPress.js'
|
|
163
161
|
import {
|
|
164
|
-
applyFilters,
|
|
165
162
|
buildSelectOptions,
|
|
166
163
|
cloneColumnFilterEntries,
|
|
167
164
|
cloneFilters,
|
|
168
|
-
countActiveFilters,
|
|
169
165
|
isColumnFiltered,
|
|
170
166
|
isFilterEntryActive,
|
|
171
167
|
normalizeColumnFilterEntries,
|
|
@@ -186,6 +182,10 @@ import {
|
|
|
186
182
|
sanitizePresetState,
|
|
187
183
|
layoutStatesEqual,
|
|
188
184
|
} from '../composables/tableColumnPresets.js'
|
|
185
|
+
import {
|
|
186
|
+
defaultColumnPriorityForIndex,
|
|
187
|
+
isValidColPriority,
|
|
188
|
+
} from '../composables/tableColumnPriorities.js'
|
|
189
189
|
|
|
190
190
|
const DEFAULT_PAGE_SIZE = 10
|
|
191
191
|
|
|
@@ -231,6 +231,10 @@ const props = defineProps({
|
|
|
231
231
|
type: Boolean,
|
|
232
232
|
default: false,
|
|
233
233
|
},
|
|
234
|
+
responsiveColumns: {
|
|
235
|
+
type: Boolean,
|
|
236
|
+
default: true,
|
|
237
|
+
},
|
|
234
238
|
stickyCols: {
|
|
235
239
|
type: Number,
|
|
236
240
|
default: null,
|
|
@@ -286,6 +290,8 @@ const emit = defineEmits([
|
|
|
286
290
|
'column-order-change',
|
|
287
291
|
'update:columnPriorities',
|
|
288
292
|
'column-priorities-change',
|
|
293
|
+
'update:layoutLabel',
|
|
294
|
+
'layout-label-change',
|
|
289
295
|
])
|
|
290
296
|
|
|
291
297
|
const attrs = useAttrs()
|
|
@@ -368,6 +374,18 @@ const showColumnOptionsButton = computed(() =>
|
|
|
368
374
|
props.columnOptions && manageableColumnKeys.value.length > 0,
|
|
369
375
|
)
|
|
370
376
|
|
|
377
|
+
function isLastVisibleColumn(index) {
|
|
378
|
+
return index === visibleHeaders.value.length - 1
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function isLastColumnWithOptions(index) {
|
|
382
|
+
return showColumnOptionsButton.value && isLastVisibleColumn(index)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function bodyColumnColspan(index) {
|
|
386
|
+
return isLastColumnWithOptions(index) ? 2 : null
|
|
387
|
+
}
|
|
388
|
+
|
|
371
389
|
function isHeaderHidden(header) {
|
|
372
390
|
if (header.hidden) {
|
|
373
391
|
return true
|
|
@@ -386,9 +404,13 @@ const visibleColumnKeys = computed(() =>
|
|
|
386
404
|
.map((header) => header.key),
|
|
387
405
|
)
|
|
388
406
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
407
|
+
const hasHorizontalScroll = computed(() =>
|
|
408
|
+
props.horizontalScroll || (props.stickyCols >= 1 && props.stickyCols <= 3),
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
const useResponsiveColumns = computed(() =>
|
|
412
|
+
props.responsiveColumns && !hasHorizontalScroll.value,
|
|
413
|
+
)
|
|
392
414
|
|
|
393
415
|
function resolvedColumnPriority(header) {
|
|
394
416
|
if (!header?.key) {
|
|
@@ -404,9 +426,35 @@ function resolvedColumnPriority(header) {
|
|
|
404
426
|
return isValidColPriority(override) ? override : null
|
|
405
427
|
}
|
|
406
428
|
|
|
407
|
-
|
|
429
|
+
if (!useResponsiveColumns.value) {
|
|
430
|
+
return null
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const index = visibleHeaders.value.findIndex((item) => item.key === header.key)
|
|
434
|
+
if (index < 0) {
|
|
435
|
+
return null
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
return defaultColumnPriorityForIndex(header, index, useResponsiveColumns.value)
|
|
408
439
|
}
|
|
409
440
|
|
|
441
|
+
const defaultColumnPriorities = computed(() => {
|
|
442
|
+
if (!useResponsiveColumns.value) {
|
|
443
|
+
return Object.fromEntries(
|
|
444
|
+
visibleHeaders.value
|
|
445
|
+
.filter((header) => isValidColPriority(header?.colPriority))
|
|
446
|
+
.map((header) => [header.key, header.colPriority]),
|
|
447
|
+
)
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return Object.fromEntries(
|
|
451
|
+
visibleHeaders.value.flatMap((header, index) => {
|
|
452
|
+
const priority = defaultColumnPriorityForIndex(header, index, true)
|
|
453
|
+
return isValidColPriority(priority) ? [[header.key, priority]] : []
|
|
454
|
+
}),
|
|
455
|
+
)
|
|
456
|
+
})
|
|
457
|
+
|
|
410
458
|
const resolvedColumnPriorities = computed(() =>
|
|
411
459
|
Object.fromEntries(
|
|
412
460
|
manageableColumnKeys.value.map((key) => {
|
|
@@ -420,10 +468,6 @@ const hasColPriorities = computed(() =>
|
|
|
420
468
|
orderedHeaders.value.some((header) => isValidColPriority(resolvedColumnPriority(header))),
|
|
421
469
|
)
|
|
422
470
|
|
|
423
|
-
const hasHorizontalScroll = computed(() =>
|
|
424
|
-
props.horizontalScroll || (props.stickyCols >= 1 && props.stickyCols <= 3),
|
|
425
|
-
)
|
|
426
|
-
|
|
427
471
|
const needsTableWrapper = computed(() => hasColPriorities.value || hasHorizontalScroll.value)
|
|
428
472
|
|
|
429
473
|
const tableWrapperClasses = computed(() => {
|
|
@@ -528,10 +572,6 @@ const queryParams = computed(() => {
|
|
|
528
572
|
return params
|
|
529
573
|
})
|
|
530
574
|
|
|
531
|
-
function isValidColPriority(priority) {
|
|
532
|
-
return Number.isInteger(priority) && priority >= 1 && priority <= 5
|
|
533
|
-
}
|
|
534
|
-
|
|
535
575
|
function colPriorityClass(priority) {
|
|
536
576
|
return isValidColPriority(priority) ? `col-priority-${priority}` : null
|
|
537
577
|
}
|
|
@@ -1105,6 +1145,11 @@ const activeLayoutLabel = computed(() => {
|
|
|
1105
1145
|
return 'Custom'
|
|
1106
1146
|
})
|
|
1107
1147
|
|
|
1148
|
+
function emitLayoutLabelChange(label) {
|
|
1149
|
+
emit('update:layoutLabel', label)
|
|
1150
|
+
emit('layout-label-change', label)
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1108
1153
|
function loadDeveloperDefaults() {
|
|
1109
1154
|
setFilters({})
|
|
1110
1155
|
sortBy.value = null
|
|
@@ -1313,6 +1358,10 @@ watch(
|
|
|
1313
1358
|
{ deep: true },
|
|
1314
1359
|
)
|
|
1315
1360
|
|
|
1361
|
+
watch(activeLayoutLabel, (label) => {
|
|
1362
|
+
emitLayoutLabelChange(label)
|
|
1363
|
+
}, { immediate: true })
|
|
1364
|
+
|
|
1316
1365
|
watch(sortedItems, () => {
|
|
1317
1366
|
if (!isRemote.value) {
|
|
1318
1367
|
page.value = 1
|
|
@@ -1356,6 +1405,11 @@ onBeforeUnmount(() => {
|
|
|
1356
1405
|
clearTimeout(fetchDebounceTimer.value)
|
|
1357
1406
|
}
|
|
1358
1407
|
})
|
|
1408
|
+
|
|
1409
|
+
defineExpose({
|
|
1410
|
+
layoutLabel: activeLayoutLabel,
|
|
1411
|
+
loadDeveloperDefaults,
|
|
1412
|
+
})
|
|
1359
1413
|
</script>
|
|
1360
1414
|
|
|
1361
1415
|
<style scoped>
|
|
@@ -1414,7 +1468,7 @@ tbody tr.row-clickable {
|
|
|
1414
1468
|
min-height: 6rem;
|
|
1415
1469
|
padding: 2rem 1rem;
|
|
1416
1470
|
text-align: center;
|
|
1417
|
-
color: var(--
|
|
1471
|
+
color: var(--table-muted-fg);
|
|
1418
1472
|
}
|
|
1419
1473
|
|
|
1420
1474
|
.table-empty-state.loading {
|
|
@@ -1436,48 +1490,26 @@ tbody tr.row-clickable {
|
|
|
1436
1490
|
gap: 0.75rem;
|
|
1437
1491
|
padding: 2rem 1rem;
|
|
1438
1492
|
text-align: center;
|
|
1439
|
-
color: var(--
|
|
1493
|
+
color: var(--table-muted-fg);
|
|
1440
1494
|
}
|
|
1441
1495
|
|
|
1442
1496
|
.table-filtered-empty-message {
|
|
1443
1497
|
margin: 0;
|
|
1444
1498
|
}
|
|
1445
1499
|
|
|
1446
|
-
th.table-column-options-header
|
|
1447
|
-
td.table-column-options-cell {
|
|
1500
|
+
th.table-column-options-header {
|
|
1448
1501
|
padding-right: 1rem;
|
|
1449
1502
|
text-align: right;
|
|
1450
1503
|
vertical-align: middle;
|
|
1451
|
-
}
|
|
1452
|
-
|
|
1453
|
-
th.table-column-options-header {
|
|
1454
1504
|
width: auto;
|
|
1455
1505
|
min-width: 2.5rem;
|
|
1456
1506
|
max-width: none;
|
|
1457
1507
|
white-space: nowrap;
|
|
1458
1508
|
}
|
|
1459
1509
|
|
|
1460
|
-
td.table-column-options
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
max-width: 2.5rem;
|
|
1464
|
-
}
|
|
1465
|
-
|
|
1466
|
-
.table-column-options-header-content {
|
|
1467
|
-
display: inline-flex;
|
|
1468
|
-
align-items: center;
|
|
1469
|
-
justify-content: flex-end;
|
|
1470
|
-
gap: 0.5rem;
|
|
1471
|
-
width: 100%;
|
|
1472
|
-
}
|
|
1473
|
-
|
|
1474
|
-
.table-layout-label {
|
|
1475
|
-
font-size: 0.85rem;
|
|
1476
|
-
color: var(--text-muted, #666);
|
|
1477
|
-
max-width: 10rem;
|
|
1478
|
-
overflow: hidden;
|
|
1479
|
-
text-overflow: ellipsis;
|
|
1480
|
-
white-space: nowrap;
|
|
1510
|
+
td.table-last-column-with-options {
|
|
1511
|
+
padding-right: 1rem;
|
|
1512
|
+
vertical-align: middle;
|
|
1481
1513
|
}
|
|
1482
1514
|
|
|
1483
1515
|
th.table-column-options-header .actions-menu-trigger {
|
|
@@ -1486,24 +1518,20 @@ th.table-column-options-header .actions-menu-trigger {
|
|
|
1486
1518
|
flex: 0 0 auto;
|
|
1487
1519
|
}
|
|
1488
1520
|
|
|
1489
|
-
.table-scroll
|
|
1490
|
-
|
|
1521
|
+
.table-scroll {
|
|
1522
|
+
min-width: 0;
|
|
1523
|
+
max-width: 100%;
|
|
1524
|
+
contain: inline-size;
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
.table-scroll th.table-column-options-header {
|
|
1491
1528
|
position: sticky;
|
|
1492
1529
|
right: 0;
|
|
1493
|
-
z-index:
|
|
1530
|
+
z-index: 2;
|
|
1494
1531
|
background-color: var(--table-sticky-bg);
|
|
1495
1532
|
box-shadow: -1px 0 0 var(--border-color);
|
|
1496
1533
|
}
|
|
1497
1534
|
|
|
1498
|
-
.table-scroll thead th.table-column-options-header {
|
|
1499
|
-
z-index: 2;
|
|
1500
|
-
}
|
|
1501
|
-
|
|
1502
|
-
.table-scroll table.row-hover tbody tr:hover td.table-column-options-cell,
|
|
1503
|
-
.table-scroll table.row-hover tbody tr:focus-within td.table-column-options-cell {
|
|
1504
|
-
background-color: var(--table-sticky-hover-bg);
|
|
1505
|
-
}
|
|
1506
|
-
|
|
1507
1535
|
th.actions .actions-menu-trigger button {
|
|
1508
1536
|
display: inline-flex;
|
|
1509
1537
|
align-items: center;
|