picocrank 1.25.2 → 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 +20 -11
- package/styles/picocrank-extensions.css +77 -30
- package/supplemental-themes/catppuccin-latte-frappe/theme.css +10 -24
- package/supplemental-themes/dracula-alucard/theme.css +10 -24
- package/supplemental-themes/gruvbox-dark-light/theme.css +10 -24
- package/supplemental-themes/waffles/theme.css +2 -16
- package/vue/components/Breadcrumbs.vue +192 -45
- package/vue/components/Calendar.vue +45 -25
- package/vue/components/DangerZone.vue +186 -0
- package/vue/components/Header.vue +22 -6
- package/vue/components/NavigationGrid.vue +26 -180
- package/vue/components/NotificationBlock.vue +120 -0
- package/vue/components/NotificationPopup.vue +44 -41
- package/vue/components/ReadOnlyTextArea.vue +1 -1
- package/vue/components/SidebarNavLink.vue +3 -3
- package/vue/components/Table.vue +107 -79
- package/vue/components/TableColumnOptionsPopover.vue +9 -0
- package/vue/components/Tabs.vue +363 -79
- 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 -40
package/vue/components/Tabs.vue
CHANGED
|
@@ -1,112 +1,290 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
<div
|
|
3
|
+
class="tabs-container"
|
|
4
|
+
:class="{
|
|
5
|
+
'tabs-vertical': orientation === 'vertical',
|
|
6
|
+
'tabs-flush': !padding,
|
|
7
|
+
}"
|
|
8
|
+
>
|
|
9
|
+
<div class="tabs-header">
|
|
10
|
+
<div
|
|
11
|
+
class="tabs-header-scroll"
|
|
12
|
+
role="tablist"
|
|
13
|
+
:aria-orientation="orientation"
|
|
14
|
+
:aria-labelledby="tablistLabelledBy || undefined"
|
|
15
|
+
@keydown="onTabListKeydown"
|
|
13
16
|
>
|
|
14
|
-
<
|
|
15
|
-
v-
|
|
16
|
-
:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
<button
|
|
18
|
+
v-for="(tab, index) in tabs"
|
|
19
|
+
:key="getTabKey(tab, index)"
|
|
20
|
+
:ref="(el) => setTabRef(getTabKey(tab, index), el)"
|
|
21
|
+
:class="['tab-button', { active: isTabSelected(tab, index) }]"
|
|
22
|
+
:aria-selected="isTabSelected(tab, index) ? 'true' : 'false'"
|
|
23
|
+
:aria-controls="`tab-panel-${getTabKey(tab, index)}`"
|
|
24
|
+
:id="`tab-${getTabKey(tab, index)}`"
|
|
25
|
+
:tabindex="isTabFocused(tab, index) ? 0 : -1"
|
|
26
|
+
role="tab"
|
|
27
|
+
type="button"
|
|
28
|
+
@click="onTabClick(getTabKey(tab, index))"
|
|
29
|
+
>
|
|
30
|
+
<HugeiconsIcon
|
|
31
|
+
v-if="tab.icon"
|
|
32
|
+
:icon="tab.icon"
|
|
33
|
+
width="1em"
|
|
34
|
+
height="1em"
|
|
35
|
+
aria-hidden="true"
|
|
36
|
+
/>
|
|
37
|
+
<span>{{ tab.label }}</span>
|
|
38
|
+
<slot :name="`tab-${getTabKey(tab, index)}-badge`" />
|
|
39
|
+
</button>
|
|
40
|
+
</div>
|
|
23
41
|
</div>
|
|
24
42
|
<div class="tabs-content">
|
|
25
43
|
<div
|
|
26
44
|
v-for="(tab, index) in tabs"
|
|
27
|
-
:key="tab
|
|
28
|
-
:id="`tab-panel-${tab
|
|
29
|
-
:class="['tab-panel', { active:
|
|
45
|
+
:key="`panel-${getTabKey(tab, index)}`"
|
|
46
|
+
:id="`tab-panel-${getTabKey(tab, index)}`"
|
|
47
|
+
:class="['tab-panel', { active: isTabSelected(tab, index) }]"
|
|
30
48
|
role="tabpanel"
|
|
31
|
-
:aria-labelledby="`tab-${tab
|
|
32
|
-
|
|
49
|
+
:aria-labelledby="`tab-${getTabKey(tab, index)}`"
|
|
50
|
+
:hidden="!isTabSelected(tab, index)"
|
|
51
|
+
:tabindex="isTabSelected(tab, index) ? 0 : -1"
|
|
33
52
|
>
|
|
34
|
-
<slot :name="`tab-${tab
|
|
53
|
+
<slot :name="`tab-${getTabKey(tab, index)}`" :tab="tab" />
|
|
35
54
|
</div>
|
|
36
55
|
</div>
|
|
37
56
|
</div>
|
|
38
57
|
</template>
|
|
39
58
|
|
|
40
59
|
<script setup>
|
|
41
|
-
import {
|
|
42
|
-
import { HugeiconsIcon } from '@hugeicons/vue'
|
|
60
|
+
import { nextTick, onMounted, ref, watch } from 'vue'
|
|
61
|
+
import { HugeiconsIcon } from '@hugeicons/vue'
|
|
43
62
|
|
|
44
63
|
const props = defineProps({
|
|
45
64
|
tabs: {
|
|
46
65
|
type: Array,
|
|
47
66
|
required: true,
|
|
48
67
|
validator: (tabs) => {
|
|
49
|
-
return tabs.every(tab =>
|
|
50
|
-
typeof tab === 'object'
|
|
51
|
-
(tab.id !== undefined || true)
|
|
52
|
-
typeof tab.label === 'string'
|
|
53
|
-
)
|
|
54
|
-
}
|
|
68
|
+
return tabs.every(tab =>
|
|
69
|
+
typeof tab === 'object'
|
|
70
|
+
&& (tab.id !== undefined || true)
|
|
71
|
+
&& typeof tab.label === 'string',
|
|
72
|
+
)
|
|
73
|
+
},
|
|
55
74
|
},
|
|
56
75
|
defaultTab: {
|
|
57
76
|
type: [String, Number],
|
|
58
|
-
default: null
|
|
59
|
-
}
|
|
60
|
-
|
|
77
|
+
default: null,
|
|
78
|
+
},
|
|
79
|
+
/** APG activation: `auto` selects on focus; `manual` requires Space/Enter. */
|
|
80
|
+
activation: {
|
|
81
|
+
type: String,
|
|
82
|
+
default: 'auto',
|
|
83
|
+
validator: (value) => ['auto', 'manual'].includes(value),
|
|
84
|
+
},
|
|
85
|
+
/** Optional id of an element that labels the tablist. */
|
|
86
|
+
tablistLabelledBy: {
|
|
87
|
+
type: String,
|
|
88
|
+
default: '',
|
|
89
|
+
},
|
|
90
|
+
/** Tab strip layout: `horizontal` (default) or `vertical`. */
|
|
91
|
+
orientation: {
|
|
92
|
+
type: String,
|
|
93
|
+
default: 'horizontal',
|
|
94
|
+
validator: (value) => ['horizontal', 'vertical'].includes(value),
|
|
95
|
+
},
|
|
96
|
+
/** When true, panels use full inset padding; default false keeps dividers flush. */
|
|
97
|
+
padding: {
|
|
98
|
+
type: Boolean,
|
|
99
|
+
default: false,
|
|
100
|
+
},
|
|
101
|
+
})
|
|
61
102
|
|
|
62
|
-
const emit = defineEmits(['tab-change'])
|
|
103
|
+
const emit = defineEmits(['tab-change'])
|
|
104
|
+
|
|
105
|
+
const tabRefs = new Map()
|
|
63
106
|
|
|
64
|
-
// Initialize activeTabId with first tab by default
|
|
65
107
|
function getInitialTabId() {
|
|
66
108
|
if (props.defaultTab !== null) {
|
|
67
|
-
return props.defaultTab
|
|
109
|
+
return props.defaultTab
|
|
68
110
|
}
|
|
69
111
|
if (props.tabs.length > 0) {
|
|
70
|
-
return props.tabs[0]
|
|
112
|
+
return getTabKey(props.tabs[0], 0)
|
|
71
113
|
}
|
|
72
|
-
return null
|
|
114
|
+
return null
|
|
73
115
|
}
|
|
74
116
|
|
|
75
|
-
const activeTabId = ref(getInitialTabId())
|
|
117
|
+
const activeTabId = ref(getInitialTabId())
|
|
118
|
+
const focusedTabId = ref(getInitialTabId())
|
|
119
|
+
|
|
120
|
+
function getTabKey(tab, index) {
|
|
121
|
+
return tab.id ?? index
|
|
122
|
+
}
|
|
76
123
|
|
|
77
|
-
function
|
|
78
|
-
if (
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
emit('tab-change', activeTab, tabId);
|
|
124
|
+
function setTabRef(tabKey, el) {
|
|
125
|
+
if (el) {
|
|
126
|
+
tabRefs.set(tabKey, el)
|
|
127
|
+
return
|
|
82
128
|
}
|
|
129
|
+
tabRefs.delete(tabKey)
|
|
83
130
|
}
|
|
84
131
|
|
|
85
|
-
function
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
132
|
+
function tabIndexForKey(tabKey) {
|
|
133
|
+
return props.tabs.findIndex((tab, index) => getTabKey(tab, index) === tabKey)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function isTabSelected(tab, index) {
|
|
137
|
+
return activeTabId.value === getTabKey(tab, index)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function isTabFocused(tab, index) {
|
|
141
|
+
return focusedTabId.value === getTabKey(tab, index)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function focusTab(tabKey) {
|
|
145
|
+
tabRefs.get(tabKey)?.focus()
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function selectTab(tabKey, { emitChange = true } = {}) {
|
|
149
|
+
if (activeTabId.value !== tabKey) {
|
|
150
|
+
activeTabId.value = tabKey
|
|
151
|
+
if (emitChange) {
|
|
152
|
+
const index = tabIndexForKey(tabKey)
|
|
153
|
+
const activeTab = index >= 0 ? props.tabs[index] : null
|
|
154
|
+
emit('tab-change', activeTab, tabKey)
|
|
155
|
+
}
|
|
89
156
|
}
|
|
157
|
+
focusedTabId.value = tabKey
|
|
90
158
|
}
|
|
91
159
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
160
|
+
function onTabClick(tabKey) {
|
|
161
|
+
selectTab(tabKey)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function moveFocusToIndex(index, { activate = props.activation === 'auto' } = {}) {
|
|
165
|
+
if (props.tabs.length === 0) {
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const normalized = (index + props.tabs.length) % props.tabs.length
|
|
170
|
+
const tabKey = getTabKey(props.tabs[normalized], normalized)
|
|
171
|
+
focusedTabId.value = tabKey
|
|
172
|
+
|
|
173
|
+
if (activate) {
|
|
174
|
+
selectTab(tabKey)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
nextTick(() => focusTab(tabKey))
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function tabKeyFromElement(tabEl) {
|
|
181
|
+
const suffix = tabEl.id.slice('tab-'.length)
|
|
182
|
+
const index = props.tabs.findIndex((tab, i) => String(getTabKey(tab, i)) === suffix)
|
|
183
|
+
if (index >= 0) {
|
|
184
|
+
return getTabKey(props.tabs[index], index)
|
|
185
|
+
}
|
|
186
|
+
return suffix
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function onTabListKeydown(event) {
|
|
190
|
+
const tabEl = event.target?.closest?.('[role="tab"]')
|
|
191
|
+
if (!tabEl || !tabEl.id?.startsWith('tab-')) {
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const tabKey = tabKeyFromElement(tabEl)
|
|
196
|
+
const index = tabIndexForKey(tabKey)
|
|
197
|
+
if (index < 0) {
|
|
198
|
+
return
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const isVertical = props.orientation === 'vertical'
|
|
202
|
+
|
|
203
|
+
switch (event.key) {
|
|
204
|
+
case 'ArrowRight':
|
|
205
|
+
if (!isVertical) {
|
|
206
|
+
event.preventDefault()
|
|
207
|
+
moveFocusToIndex(index + 1)
|
|
208
|
+
}
|
|
209
|
+
break
|
|
210
|
+
case 'ArrowLeft':
|
|
211
|
+
if (!isVertical) {
|
|
212
|
+
event.preventDefault()
|
|
213
|
+
moveFocusToIndex(index - 1)
|
|
214
|
+
}
|
|
215
|
+
break
|
|
216
|
+
case 'ArrowDown':
|
|
217
|
+
if (isVertical) {
|
|
218
|
+
event.preventDefault()
|
|
219
|
+
moveFocusToIndex(index + 1)
|
|
220
|
+
}
|
|
221
|
+
break
|
|
222
|
+
case 'ArrowUp':
|
|
223
|
+
if (isVertical) {
|
|
224
|
+
event.preventDefault()
|
|
225
|
+
moveFocusToIndex(index - 1)
|
|
226
|
+
}
|
|
227
|
+
break
|
|
228
|
+
case 'Home':
|
|
229
|
+
event.preventDefault()
|
|
230
|
+
moveFocusToIndex(0)
|
|
231
|
+
break
|
|
232
|
+
case 'End':
|
|
233
|
+
event.preventDefault()
|
|
234
|
+
moveFocusToIndex(props.tabs.length - 1)
|
|
235
|
+
break
|
|
236
|
+
case ' ':
|
|
237
|
+
case 'Enter':
|
|
238
|
+
if (props.activation === 'manual') {
|
|
239
|
+
event.preventDefault()
|
|
240
|
+
selectTab(tabKey)
|
|
241
|
+
}
|
|
242
|
+
break
|
|
243
|
+
default:
|
|
244
|
+
break
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function setActiveTab(tabKey) {
|
|
249
|
+
const index = tabIndexForKey(tabKey)
|
|
250
|
+
if (index >= 0) {
|
|
251
|
+
selectTab(tabKey)
|
|
252
|
+
nextTick(() => focusTab(tabKey))
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function syncTabIdsAfterListChange() {
|
|
257
|
+
if (props.tabs.length === 0) {
|
|
258
|
+
activeTabId.value = null
|
|
259
|
+
focusedTabId.value = null
|
|
260
|
+
return
|
|
96
261
|
}
|
|
97
|
-
});
|
|
98
262
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
activeTabId.value =
|
|
263
|
+
const activeStillExists = tabIndexForKey(activeTabId.value) >= 0
|
|
264
|
+
if (!activeStillExists) {
|
|
265
|
+
const firstKey = getTabKey(props.tabs[0], 0)
|
|
266
|
+
activeTabId.value = firstKey
|
|
267
|
+
focusedTabId.value = firstKey
|
|
268
|
+
return
|
|
103
269
|
}
|
|
104
|
-
|
|
270
|
+
|
|
271
|
+
if (tabIndexForKey(focusedTabId.value) < 0) {
|
|
272
|
+
focusedTabId.value = activeTabId.value
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
onMounted(() => {
|
|
277
|
+
syncTabIdsAfterListChange()
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
watch(() => props.tabs, () => {
|
|
281
|
+
syncTabIdsAfterListChange()
|
|
282
|
+
}, { deep: true })
|
|
105
283
|
|
|
106
284
|
defineExpose({
|
|
107
285
|
setActiveTab,
|
|
108
|
-
activeTabId
|
|
109
|
-
})
|
|
286
|
+
activeTabId,
|
|
287
|
+
})
|
|
110
288
|
</script>
|
|
111
289
|
|
|
112
290
|
<style scoped>
|
|
@@ -114,27 +292,55 @@ defineExpose({
|
|
|
114
292
|
width: 100%;
|
|
115
293
|
}
|
|
116
294
|
|
|
295
|
+
.tabs-container.tabs-vertical {
|
|
296
|
+
display: flex;
|
|
297
|
+
align-items: stretch;
|
|
298
|
+
gap: 0;
|
|
299
|
+
}
|
|
300
|
+
|
|
117
301
|
.tabs-header {
|
|
302
|
+
border-bottom: 1px solid var(--tab-border);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.tabs-vertical .tabs-header {
|
|
306
|
+
flex-shrink: 0;
|
|
307
|
+
border-bottom: none;
|
|
308
|
+
border-right: 1px solid var(--tab-border);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.tabs-header-scroll {
|
|
118
312
|
display: flex;
|
|
119
|
-
border-bottom: 2px solid var(--tab-border);
|
|
120
313
|
gap: 0.25rem;
|
|
121
314
|
overflow-x: auto;
|
|
122
315
|
scrollbar-width: thin;
|
|
123
316
|
}
|
|
124
317
|
|
|
125
|
-
.tabs-header::-webkit-scrollbar {
|
|
318
|
+
.tabs-header-scroll::-webkit-scrollbar {
|
|
126
319
|
height: 4px;
|
|
127
320
|
}
|
|
128
321
|
|
|
129
|
-
.tabs-header::-webkit-scrollbar-track {
|
|
322
|
+
.tabs-header-scroll::-webkit-scrollbar-track {
|
|
130
323
|
background: transparent;
|
|
131
324
|
}
|
|
132
325
|
|
|
133
|
-
.tabs-header::-webkit-scrollbar-thumb {
|
|
326
|
+
.tabs-header-scroll::-webkit-scrollbar-thumb {
|
|
134
327
|
background: var(--tab-border);
|
|
135
328
|
border-radius: 2px;
|
|
136
329
|
}
|
|
137
330
|
|
|
331
|
+
.tabs-vertical .tabs-header-scroll {
|
|
332
|
+
flex-direction: column;
|
|
333
|
+
overflow-x: hidden;
|
|
334
|
+
overflow-y: auto;
|
|
335
|
+
min-width: 10rem;
|
|
336
|
+
max-height: 24rem;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.tabs-vertical .tabs-header-scroll::-webkit-scrollbar {
|
|
340
|
+
width: 4px;
|
|
341
|
+
height: auto;
|
|
342
|
+
}
|
|
343
|
+
|
|
138
344
|
.tab-button {
|
|
139
345
|
display: flex;
|
|
140
346
|
align-items: center;
|
|
@@ -142,33 +348,89 @@ defineExpose({
|
|
|
142
348
|
padding: 0.75rem 1.25rem;
|
|
143
349
|
background: transparent;
|
|
144
350
|
border: none;
|
|
145
|
-
border-bottom:
|
|
146
|
-
border-radius: 0;
|
|
351
|
+
border-bottom: 1px solid transparent;
|
|
352
|
+
border-radius: 0.4em 0.4em 0 0;
|
|
147
353
|
cursor: pointer;
|
|
148
354
|
font-size: 1em;
|
|
149
355
|
color: var(--tab-fg);
|
|
150
|
-
transition: all 0.2s ease;
|
|
151
356
|
white-space: nowrap;
|
|
152
357
|
position: relative;
|
|
153
358
|
}
|
|
154
359
|
|
|
155
|
-
.tab-button:hover {
|
|
360
|
+
.tab-button:hover:not(.active) {
|
|
156
361
|
color: var(--tab-hover-fg);
|
|
157
362
|
background-color: var(--tab-hover-bg);
|
|
158
363
|
}
|
|
159
364
|
|
|
160
365
|
.tab-button.active {
|
|
366
|
+
background-color: var(--tab-active-bg);
|
|
161
367
|
color: var(--tab-active-fg);
|
|
162
|
-
border-bottom-color:
|
|
163
|
-
font-weight: bold;
|
|
368
|
+
border-bottom-color: transparent;
|
|
164
369
|
}
|
|
165
370
|
|
|
166
|
-
.tab-button
|
|
167
|
-
|
|
168
|
-
|
|
371
|
+
.tabs-vertical .tab-button {
|
|
372
|
+
width: 100%;
|
|
373
|
+
justify-content: flex-start;
|
|
374
|
+
border-bottom: none;
|
|
375
|
+
border-right: 1px solid transparent;
|
|
376
|
+
border-radius: 0.4em 0 0 0.4em;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.tabs-vertical .tab-button.active {
|
|
380
|
+
border-right-color: transparent;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.tabs-vertical .tabs-content {
|
|
384
|
+
flex: 1;
|
|
385
|
+
min-width: 0;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.tabs-flush .tab-panel {
|
|
389
|
+
padding: 0;
|
|
390
|
+
padding-top: 1.5rem;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.tabs-vertical.tabs-flush .tab-panel {
|
|
394
|
+
padding-top: 0;
|
|
395
|
+
padding-left: 0.75rem;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.tabs-vertical.tabs-flush .tabs-header {
|
|
399
|
+
display: flex;
|
|
400
|
+
flex-direction: column;
|
|
401
|
+
align-self: stretch;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.tabs-vertical.tabs-flush .tabs-header-scroll {
|
|
405
|
+
flex: 1;
|
|
406
|
+
max-height: none;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.tabs-vertical.tabs-flush .tab-button {
|
|
169
410
|
border-radius: 0;
|
|
170
411
|
}
|
|
171
412
|
|
|
413
|
+
.tab-button.active:hover,
|
|
414
|
+
.tab-button.active:focus-visible {
|
|
415
|
+
background-color: var(--tab-active-hover-bg);
|
|
416
|
+
color: var(--tab-active-fg);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.tab-button:focus {
|
|
420
|
+
outline: none;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.tab-button:focus-visible {
|
|
424
|
+
outline: none;
|
|
425
|
+
box-shadow: inset 0 0 0 2px var(--focus-outline-color);
|
|
426
|
+
z-index: 1;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.tab-button:hover:not(.active):focus-visible {
|
|
430
|
+
color: var(--tab-hover-fg);
|
|
431
|
+
background-color: var(--tab-hover-bg);
|
|
432
|
+
}
|
|
433
|
+
|
|
172
434
|
.tabs-content {
|
|
173
435
|
width: 100%;
|
|
174
436
|
min-height: 200px;
|
|
@@ -177,11 +439,23 @@ defineExpose({
|
|
|
177
439
|
.tab-panel {
|
|
178
440
|
padding-block: 1.5rem;
|
|
179
441
|
padding-inline: 0.75rem;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.tab-panel:focus {
|
|
445
|
+
outline: none;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.tab-panel:focus-visible {
|
|
449
|
+
outline: 2px solid var(--focus-outline-color);
|
|
450
|
+
outline-offset: 2px;
|
|
451
|
+
border-radius: 0.4em;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.tab-panel[hidden] {
|
|
180
455
|
display: none;
|
|
181
456
|
}
|
|
182
457
|
|
|
183
458
|
.tab-panel.active {
|
|
184
|
-
display: block;
|
|
185
459
|
animation: fadeIn 0.2s ease-in;
|
|
186
460
|
}
|
|
187
461
|
|
|
@@ -198,7 +472,7 @@ defineExpose({
|
|
|
198
472
|
|
|
199
473
|
/* Responsive */
|
|
200
474
|
@media (max-width: 768px) {
|
|
201
|
-
.tabs-header {
|
|
475
|
+
.tabs-header-scroll {
|
|
202
476
|
gap: 0;
|
|
203
477
|
}
|
|
204
478
|
|
|
@@ -211,5 +485,15 @@ defineExpose({
|
|
|
211
485
|
padding-block: 1rem;
|
|
212
486
|
padding-inline: 0.5rem;
|
|
213
487
|
}
|
|
488
|
+
|
|
489
|
+
.tabs-flush .tab-panel {
|
|
490
|
+
padding: 0;
|
|
491
|
+
padding-top: 1rem;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.tabs-vertical.tabs-flush .tab-panel {
|
|
495
|
+
padding-top: 0;
|
|
496
|
+
padding-left: 0.5rem;
|
|
497
|
+
}
|
|
214
498
|
}
|
|
215
499
|
</style>
|
|
@@ -97,7 +97,7 @@ function handleLinkClick(callback = null) {
|
|
|
97
97
|
padding: 0.5em 0.75em;
|
|
98
98
|
border-radius: 0.25em;
|
|
99
99
|
text-decoration: none;
|
|
100
|
-
color: var(--header-fg-color
|
|
100
|
+
color: var(--header-fg-color);
|
|
101
101
|
transition: background-color 0.15s ease;
|
|
102
102
|
white-space: nowrap;
|
|
103
103
|
border: 0;
|
|
@@ -124,7 +124,7 @@ function handleLinkClick(callback = null) {
|
|
|
124
124
|
|
|
125
125
|
html[data-theme="dark"] {
|
|
126
126
|
.topbar-link {
|
|
127
|
-
color: var(--header-fg-color
|
|
127
|
+
color: var(--header-fg-color);
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
.separator {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export function isValidColPriority(priority) {
|
|
2
|
+
return Number.isInteger(priority) && priority >= 1 && priority <= 5
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
/** Default responsive priority by visible column index (0-based). First column has no auto priority. */
|
|
6
|
+
export function autoColumnPriorityForIndex(index) {
|
|
7
|
+
if (index <= 0) {
|
|
8
|
+
return null
|
|
9
|
+
}
|
|
10
|
+
if (index === 1) {
|
|
11
|
+
return 5
|
|
12
|
+
}
|
|
13
|
+
if (index === 2) {
|
|
14
|
+
return 4
|
|
15
|
+
}
|
|
16
|
+
if (index === 3) {
|
|
17
|
+
return 3
|
|
18
|
+
}
|
|
19
|
+
if (index === 4) {
|
|
20
|
+
return 2
|
|
21
|
+
}
|
|
22
|
+
return 1
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function defaultColumnPriorityForIndex(header, index, responsiveColumns = true) {
|
|
26
|
+
if (isValidColPriority(header?.colPriority)) {
|
|
27
|
+
return header.colPriority
|
|
28
|
+
}
|
|
29
|
+
if (!responsiveColumns) {
|
|
30
|
+
return null
|
|
31
|
+
}
|
|
32
|
+
return autoColumnPriorityForIndex(index)
|
|
33
|
+
}
|