picocrank 1.25.0 → 1.25.2
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/package.json +2 -2
- package/public/themes/README.md +15 -0
- package/styles/dark-theme.css +44 -200
- package/styles/picocrank-extensions.css +97 -0
- package/styles.css +1 -0
- package/supplemental-themes/catppuccin-latte-frappe/theme.css +86 -147
- package/supplemental-themes/dracula-alucard/theme.css +86 -147
- package/supplemental-themes/gruvbox-dark-light/theme.css +86 -147
- package/supplemental-themes/waffles/theme.css +71 -143
- package/vue/components/Breadcrumbs.vue +3 -3
- package/vue/components/Calendar.vue +48 -155
- package/vue/components/Header.vue +3 -3
- package/vue/components/Login.vue +6 -7
- package/vue/components/NavigationGrid.vue +23 -47
- package/vue/components/QuickSearch.vue +21 -47
- package/vue/components/RadioGroup.vue +4 -4
- package/vue/components/Sidebar.vue +4 -10
- package/vue/components/SidebarNavLink.vue +6 -15
- package/vue/components/Table.vue +7 -7
- package/vue/components/TableColumnFilterPopover.vue +7 -17
- package/vue/components/TableColumnOptionsPopover.vue +13 -27
- package/vue/components/Tabs.vue +8 -29
- package/vue/components/ThemeSwitcher.vue +44 -5
- package/vue/components/TopBar.vue +2 -2
- package/vue/examples/App.vue +13 -0
- package/vue/examples/ExampleHeaderThemeSwitcher.vue +78 -0
- package/vue/examples/PageStructure.vue +17 -0
- package/vue/examples/ThemePreview.vue +41 -20
- package/vue/examples/Welcome.vue +0 -72
package/vue/components/Tabs.vue
CHANGED
|
@@ -116,7 +116,7 @@ defineExpose({
|
|
|
116
116
|
|
|
117
117
|
.tabs-header {
|
|
118
118
|
display: flex;
|
|
119
|
-
border-bottom: 2px solid var(--border
|
|
119
|
+
border-bottom: 2px solid var(--tab-border);
|
|
120
120
|
gap: 0.25rem;
|
|
121
121
|
overflow-x: auto;
|
|
122
122
|
scrollbar-width: thin;
|
|
@@ -131,7 +131,7 @@ defineExpose({
|
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
.tabs-header::-webkit-scrollbar-thumb {
|
|
134
|
-
background: var(--border
|
|
134
|
+
background: var(--tab-border);
|
|
135
135
|
border-radius: 2px;
|
|
136
136
|
}
|
|
137
137
|
|
|
@@ -146,25 +146,25 @@ defineExpose({
|
|
|
146
146
|
border-radius: 0;
|
|
147
147
|
cursor: pointer;
|
|
148
148
|
font-size: 1em;
|
|
149
|
-
color: var(--
|
|
149
|
+
color: var(--tab-fg);
|
|
150
150
|
transition: all 0.2s ease;
|
|
151
151
|
white-space: nowrap;
|
|
152
152
|
position: relative;
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
.tab-button:hover {
|
|
156
|
-
color: var(--
|
|
157
|
-
background-color: var(--hover-
|
|
156
|
+
color: var(--tab-hover-fg);
|
|
157
|
+
background-color: var(--tab-hover-bg);
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
.tab-button.active {
|
|
161
|
-
color: var(--
|
|
162
|
-
border-bottom-color: var(--
|
|
161
|
+
color: var(--tab-active-fg);
|
|
162
|
+
border-bottom-color: var(--tab-active-fg);
|
|
163
163
|
font-weight: bold;
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
.tab-button:focus {
|
|
167
|
-
outline: 2px solid var(--
|
|
167
|
+
outline: 2px solid var(--tab-focus-outline-color);
|
|
168
168
|
outline-offset: -2px;
|
|
169
169
|
border-radius: 0;
|
|
170
170
|
}
|
|
@@ -196,27 +196,6 @@ defineExpose({
|
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
/* Dark theme support */
|
|
200
|
-
html[data-theme="dark"] {
|
|
201
|
-
.tabs-header {
|
|
202
|
-
border-bottom-color: var(--border-color, #444);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
.tab-button {
|
|
206
|
-
color: var(--text-muted, #999);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
.tab-button:hover {
|
|
210
|
-
color: var(--text-color, #fff);
|
|
211
|
-
background-color: var(--hover-background-color, #333);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
.tab-button.active {
|
|
215
|
-
color: var(--primary-color, #60a5fa);
|
|
216
|
-
border-bottom-color: var(--primary-color, #60a5fa);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
199
|
/* Responsive */
|
|
221
200
|
@media (max-width: 768px) {
|
|
222
201
|
.tabs-header {
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<label class="theme-switcher">
|
|
2
|
+
<label :class="['theme-switcher', { 'theme-switcher--compact': compact }]">
|
|
3
3
|
<span v-if="label" class="theme-switcher-label">{{ label }}</span>
|
|
4
|
-
<select
|
|
4
|
+
<select
|
|
5
|
+
ref="selectRef"
|
|
6
|
+
:id="selectId || undefined"
|
|
7
|
+
:value="themePreference"
|
|
8
|
+
:aria-label="compact ? (label || 'Active theme') : undefined"
|
|
9
|
+
@change="onThemeChange"
|
|
10
|
+
>
|
|
5
11
|
<option value="">{{ defaultOptionLabel }}</option>
|
|
6
12
|
<option
|
|
7
13
|
v-for="name in availableThemes"
|
|
@@ -12,7 +18,7 @@
|
|
|
12
18
|
</option>
|
|
13
19
|
</select>
|
|
14
20
|
</label>
|
|
15
|
-
<p v-if="showEmptyHint && availableThemes.length === 0" class="subtle theme-switcher-hint">
|
|
21
|
+
<p v-if="showEmptyHint && !compact && availableThemes.length === 0" class="subtle theme-switcher-hint">
|
|
16
22
|
<slot name="empty">
|
|
17
23
|
No drop-in themes found. Add <code>public/themes/<name>/theme.css</code>
|
|
18
24
|
or set <code>include-supplemental-themes</code> to list bundled supplemental themes.
|
|
@@ -20,8 +26,12 @@
|
|
|
20
26
|
</p>
|
|
21
27
|
</template>
|
|
22
28
|
|
|
29
|
+
<script>
|
|
30
|
+
export const HEADER_THEME_SWITCHER_SELECT_ID = 'picocrank-header-theme-switcher'
|
|
31
|
+
</script>
|
|
32
|
+
|
|
23
33
|
<script setup>
|
|
24
|
-
import { onMounted, watch } from 'vue'
|
|
34
|
+
import { onMounted, ref, watch } from 'vue'
|
|
25
35
|
import { useCustomTheme } from '../composables/useCustomTheme.js'
|
|
26
36
|
|
|
27
37
|
const props = defineProps({
|
|
@@ -31,7 +41,7 @@ const props = defineProps({
|
|
|
31
41
|
},
|
|
32
42
|
defaultOptionLabel: {
|
|
33
43
|
type: String,
|
|
34
|
-
default: 'Default (Femtocrank)',
|
|
44
|
+
default: 'Default (Femtocrank only)',
|
|
35
45
|
},
|
|
36
46
|
includeSupplementalThemes: {
|
|
37
47
|
type: Boolean,
|
|
@@ -53,6 +63,14 @@ const props = defineProps({
|
|
|
53
63
|
type: Boolean,
|
|
54
64
|
default: false,
|
|
55
65
|
},
|
|
66
|
+
compact: {
|
|
67
|
+
type: Boolean,
|
|
68
|
+
default: false,
|
|
69
|
+
},
|
|
70
|
+
selectId: {
|
|
71
|
+
type: String,
|
|
72
|
+
default: undefined,
|
|
73
|
+
},
|
|
56
74
|
})
|
|
57
75
|
|
|
58
76
|
const emit = defineEmits(['change'])
|
|
@@ -66,6 +84,17 @@ const {
|
|
|
66
84
|
configure,
|
|
67
85
|
} = useCustomTheme()
|
|
68
86
|
|
|
87
|
+
const selectRef = ref(null)
|
|
88
|
+
|
|
89
|
+
function focusSelect() {
|
|
90
|
+
selectRef.value?.focus()
|
|
91
|
+
selectRef.value?.scrollIntoView({ block: 'nearest', inline: 'nearest' })
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
defineExpose({
|
|
95
|
+
focus: focusSelect,
|
|
96
|
+
})
|
|
97
|
+
|
|
69
98
|
function syncOptions() {
|
|
70
99
|
configure({
|
|
71
100
|
includeSupplementalThemes: props.includeSupplementalThemes,
|
|
@@ -113,6 +142,16 @@ onMounted(() => {
|
|
|
113
142
|
min-width: 14rem;
|
|
114
143
|
}
|
|
115
144
|
|
|
145
|
+
.theme-switcher--compact {
|
|
146
|
+
flex-wrap: nowrap;
|
|
147
|
+
gap: 0;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.theme-switcher--compact select {
|
|
151
|
+
min-width: 10rem;
|
|
152
|
+
max-width: 13.5rem;
|
|
153
|
+
}
|
|
154
|
+
|
|
116
155
|
.theme-switcher-hint {
|
|
117
156
|
margin: 0.75rem 0 0;
|
|
118
157
|
}
|
|
@@ -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-text-color, inherit);
|
|
100
|
+
color: var(--header-fg-color, var(--header-text-color, inherit));
|
|
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-text-color, #f8f9fa);
|
|
127
|
+
color: var(--header-fg-color, var(--header-text-color, #f8f9fa));
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
.separator {
|
package/vue/examples/App.vue
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
<Navigation ref="navigation">
|
|
3
3
|
<Header username = "Guest" @toggleSidebar="toggleSidebar" @logoClick="goToIndex" @userClick="goToUserDetails" title = "PicoCrank" :logoUrl="logoUrl" :sidebarEnabled="sidebarEnabled" :navigation="navigation" :topBarEnabled="topBarEnabled" :showBranding="brandingEnabled" :breadcrumbs="breadcrumbsEnabled" :themeToggleEnabled="themeToggleEnabled">
|
|
4
4
|
<template #toolbar>
|
|
5
|
+
<ExampleHeaderThemeSwitcher
|
|
6
|
+
v-if="themeSwitcherEnabled"
|
|
7
|
+
:include-supplemental-themes="includeSupplementalThemes"
|
|
8
|
+
/>
|
|
5
9
|
<QuickSearch
|
|
6
10
|
v-if="quickSearchEnabled"
|
|
7
11
|
ref="quickSearchRef"
|
|
@@ -37,6 +41,7 @@
|
|
|
37
41
|
import { Pin02Icon, UserIcon } from '@hugeicons/core-free-icons'
|
|
38
42
|
|
|
39
43
|
import QuickSearch from './../components/QuickSearch.vue'
|
|
44
|
+
import ExampleHeaderThemeSwitcher from './ExampleHeaderThemeSwitcher.vue'
|
|
40
45
|
import Navigation from './../components/Navigation.vue'
|
|
41
46
|
import NotificationPopups from './../components/NotificationPopups.vue'
|
|
42
47
|
import '../../styles.css'
|
|
@@ -54,6 +59,8 @@
|
|
|
54
59
|
const quickSearchEnabled = ref(true);
|
|
55
60
|
const dummySearchFetchEnabled = ref(false);
|
|
56
61
|
const themeToggleEnabled = ref(true);
|
|
62
|
+
const themeSwitcherEnabled = ref(true);
|
|
63
|
+
const includeSupplementalThemes = ref(true);
|
|
57
64
|
const topBarEnabled = ref(false);
|
|
58
65
|
const breadcrumbsEnabled = ref(false);
|
|
59
66
|
|
|
@@ -93,6 +100,10 @@
|
|
|
93
100
|
themeToggleEnabled.value = !themeToggleEnabled.value;
|
|
94
101
|
}
|
|
95
102
|
|
|
103
|
+
function toggleThemeSwitcherEnabled() {
|
|
104
|
+
themeSwitcherEnabled.value = !themeSwitcherEnabled.value;
|
|
105
|
+
}
|
|
106
|
+
|
|
96
107
|
function toggleTopBarEnabled() {
|
|
97
108
|
topBarEnabled.value = !topBarEnabled.value;
|
|
98
109
|
}
|
|
@@ -161,6 +172,8 @@
|
|
|
161
172
|
provide('toggleDummySearchFetchEnabled', toggleDummySearchFetchEnabled);
|
|
162
173
|
provide('themeToggleEnabled', themeToggleEnabled);
|
|
163
174
|
provide('toggleThemeToggleEnabled', toggleThemeToggleEnabled);
|
|
175
|
+
provide('themeSwitcherEnabled', themeSwitcherEnabled);
|
|
176
|
+
provide('toggleThemeSwitcherEnabled', toggleThemeSwitcherEnabled);
|
|
164
177
|
provide('topBarEnabled', topBarEnabled);
|
|
165
178
|
provide('toggleTopBarEnabled', toggleTopBarEnabled);
|
|
166
179
|
provide('breadcrumbsEnabled', breadcrumbsEnabled);
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ThemeSwitcher
|
|
3
|
+
v-if="enabled"
|
|
4
|
+
compact
|
|
5
|
+
label=""
|
|
6
|
+
:select-id="HEADER_THEME_SWITCHER_SELECT_ID"
|
|
7
|
+
default-option-label="Default (Femtocrank only)"
|
|
8
|
+
:include-supplemental-themes="includeSupplementalThemes"
|
|
9
|
+
/>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script setup>
|
|
13
|
+
import ThemeSwitcher, { HEADER_THEME_SWITCHER_SELECT_ID } from '../components/ThemeSwitcher.vue'
|
|
14
|
+
|
|
15
|
+
defineProps({
|
|
16
|
+
enabled: {
|
|
17
|
+
type: Boolean,
|
|
18
|
+
default: true,
|
|
19
|
+
},
|
|
20
|
+
includeSupplementalThemes: {
|
|
21
|
+
type: Boolean,
|
|
22
|
+
default: true,
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<!-- Examples app only: match Header toolbar chrome (see Header search-trigger styles). -->
|
|
28
|
+
<style>
|
|
29
|
+
/*
|
|
30
|
+
* Header renders theme toggle before the toolbar slot; use flex order to swap
|
|
31
|
+
* the drop-in theme selector ahead of the light/dark button without changing Header.
|
|
32
|
+
*/
|
|
33
|
+
header .header-actions .theme-switcher--compact {
|
|
34
|
+
order: 1;
|
|
35
|
+
align-self: stretch;
|
|
36
|
+
display: inline-flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
margin: 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
header .header-actions .theme-toggle {
|
|
42
|
+
order: 2;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
header .header-actions .quick-search {
|
|
46
|
+
order: 3;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
header .header-actions .user-info {
|
|
50
|
+
order: 4;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
header .header-actions .theme-switcher--compact select {
|
|
54
|
+
align-self: stretch;
|
|
55
|
+
height: 100%;
|
|
56
|
+
border: 0;
|
|
57
|
+
border-radius: 0;
|
|
58
|
+
background-color: transparent;
|
|
59
|
+
color: var(--header-fg-color, var(--header-text-color, #fff));
|
|
60
|
+
padding-top: 0;
|
|
61
|
+
padding-bottom: 0;
|
|
62
|
+
padding-left: 0.6em;
|
|
63
|
+
padding-right: 0.6em;
|
|
64
|
+
font-size: small;
|
|
65
|
+
font-family: inherit;
|
|
66
|
+
cursor: pointer;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
header .header-actions .theme-switcher--compact select:hover {
|
|
70
|
+
background-color: var(--header-hover-background-color);
|
|
71
|
+
color: var(--header-hover-text-color);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
header .header-actions .theme-switcher--compact select:focus-visible {
|
|
75
|
+
outline: 2px solid var(--header-hover-text-color, #fff);
|
|
76
|
+
outline-offset: -2px;
|
|
77
|
+
}
|
|
78
|
+
</style>
|
|
@@ -62,6 +62,17 @@
|
|
|
62
62
|
/>
|
|
63
63
|
</FormField>
|
|
64
64
|
|
|
65
|
+
<FormField label="Theme switcher" fake>
|
|
66
|
+
<RadioGroup
|
|
67
|
+
name="ps-theme-switcher"
|
|
68
|
+
variant="boolean"
|
|
69
|
+
aria-label="Theme switcher"
|
|
70
|
+
:model-value="themeSwitcherEnabled"
|
|
71
|
+
:options="showHideOptions"
|
|
72
|
+
@update:model-value="setThemeSwitcherEnabled"
|
|
73
|
+
/>
|
|
74
|
+
</FormField>
|
|
75
|
+
|
|
65
76
|
<FormField label="Top bar" fake>
|
|
66
77
|
<div>
|
|
67
78
|
<RadioGroup
|
|
@@ -170,6 +181,8 @@ const dummySearchFetchEnabled = inject('dummySearchFetchEnabled')
|
|
|
170
181
|
const toggleDummySearchFetchEnabled = inject('toggleDummySearchFetchEnabled')
|
|
171
182
|
const themeToggleEnabled = inject('themeToggleEnabled')
|
|
172
183
|
const toggleThemeToggleEnabled = inject('toggleThemeToggleEnabled')
|
|
184
|
+
const themeSwitcherEnabled = inject('themeSwitcherEnabled')
|
|
185
|
+
const toggleThemeSwitcherEnabled = inject('toggleThemeSwitcherEnabled')
|
|
173
186
|
const topBarEnabled = inject('topBarEnabled')
|
|
174
187
|
const toggleTopBarEnabled = inject('toggleTopBarEnabled')
|
|
175
188
|
const breadcrumbsEnabled = inject('breadcrumbsEnabled')
|
|
@@ -200,6 +213,10 @@ function setThemeToggleEnabled(want) {
|
|
|
200
213
|
setFlag(themeToggleEnabled, toggleThemeToggleEnabled, want)
|
|
201
214
|
}
|
|
202
215
|
|
|
216
|
+
function setThemeSwitcherEnabled(want) {
|
|
217
|
+
setFlag(themeSwitcherEnabled, toggleThemeSwitcherEnabled, want)
|
|
218
|
+
}
|
|
219
|
+
|
|
203
220
|
function setTopBarEnabled(want) {
|
|
204
221
|
setFlag(topBarEnabled, toggleTopBarEnabled, want)
|
|
205
222
|
}
|
|
@@ -8,24 +8,24 @@
|
|
|
8
8
|
optionally <code>supplemental-themes/</code> (Catppuccin, Dracula / Alucard, Gruvbox, waffles). Drop a folder
|
|
9
9
|
in and restart the dev server (or rebuild); PicoCrank discovers themes from
|
|
10
10
|
<code>index.json</code> and applies them with <code>useCustomTheme()</code> or
|
|
11
|
-
<code>ThemeSwitcher</code
|
|
11
|
+
<code>ThemeSwitcher</code> in the examples app header toolbar. Supplemental themes are bundled by default but only
|
|
12
12
|
listed when <code>include-supplemental-themes</code> is enabled.
|
|
13
13
|
</p>
|
|
14
14
|
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
:include-supplemental-themes="true"
|
|
18
|
-
@change="onThemeChange"
|
|
19
|
-
/>
|
|
20
|
-
|
|
21
|
-
<p v-if="availableThemes.length === 0" class="subtle">
|
|
22
|
-
No drop-in themes found. Add <code>public/themes/<name>/theme.css</code>
|
|
23
|
-
or enable supplemental themes on <code>ThemeSwitcher</code>.
|
|
24
|
-
</p>
|
|
25
|
-
<p v-else class="subtle">
|
|
15
|
+
<p class="subtle">
|
|
16
|
+
Use the theme dropdown in the header (next to the light/dark toggle) to switch drop-in themes.
|
|
26
17
|
Preference is stored in localStorage and applies across the whole examples site.
|
|
27
|
-
Light/dark mode
|
|
18
|
+
Light/dark mode switches Femtocrank defaults, or the dual-variant supplemental themes when active.
|
|
28
19
|
</p>
|
|
20
|
+
|
|
21
|
+
<button
|
|
22
|
+
type="button"
|
|
23
|
+
class="neutral open-theme-switcher-button"
|
|
24
|
+
:disabled="!themeSwitcherEnabled"
|
|
25
|
+
@click="openThemeSwitcher"
|
|
26
|
+
>
|
|
27
|
+
Open theme switcher
|
|
28
|
+
</button>
|
|
29
29
|
</Section>
|
|
30
30
|
|
|
31
31
|
<Section title="Buttons" subtitle="Default and karma variants">
|
|
@@ -106,28 +106,49 @@
|
|
|
106
106
|
<td><code>--karma-*</code></td>
|
|
107
107
|
<td>Status backgrounds and accents</td>
|
|
108
108
|
</tr>
|
|
109
|
+
<tr>
|
|
110
|
+
<td><code>--link-color</code></td>
|
|
111
|
+
<td>Links, active tabs, sort/filter column headers</td>
|
|
112
|
+
</tr>
|
|
113
|
+
<tr>
|
|
114
|
+
<td><code>--muted-text-color</code></td>
|
|
115
|
+
<td>Secondary text and inactive tab labels</td>
|
|
116
|
+
</tr>
|
|
117
|
+
<tr>
|
|
118
|
+
<td><code>--header-fg-color</code></td>
|
|
119
|
+
<td>Header chrome text and icons</td>
|
|
120
|
+
</tr>
|
|
121
|
+
<tr>
|
|
122
|
+
<td><code>--input-bg-color</code> / <code>--input-fg-color</code></td>
|
|
123
|
+
<td>Form fields</td>
|
|
124
|
+
</tr>
|
|
109
125
|
</tbody>
|
|
110
126
|
</table>
|
|
111
127
|
</Section>
|
|
112
128
|
</template>
|
|
113
129
|
|
|
114
130
|
<script setup>
|
|
131
|
+
import { inject } from 'vue'
|
|
115
132
|
import Section from '../components/Section.vue'
|
|
116
|
-
import ThemeSwitcher from '../components/ThemeSwitcher.vue'
|
|
117
133
|
import FormLayout from '../components/FormLayout.vue'
|
|
118
134
|
import FormField from '../components/FormField.vue'
|
|
119
|
-
import {
|
|
135
|
+
import { HEADER_THEME_SWITCHER_SELECT_ID } from '../components/ThemeSwitcher.vue'
|
|
120
136
|
|
|
121
|
-
const
|
|
137
|
+
const themeSwitcherEnabled = inject('themeSwitcherEnabled')
|
|
122
138
|
|
|
123
|
-
function
|
|
124
|
-
|
|
139
|
+
function openThemeSwitcher() {
|
|
140
|
+
const select = document.getElementById(HEADER_THEME_SWITCHER_SELECT_ID)
|
|
141
|
+
if (!(select instanceof HTMLSelectElement)) {
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
select.focus()
|
|
145
|
+
select.scrollIntoView({ block: 'nearest', inline: 'nearest' })
|
|
125
146
|
}
|
|
126
147
|
</script>
|
|
127
148
|
|
|
128
149
|
<style scoped>
|
|
129
|
-
.theme-switcher-
|
|
130
|
-
margin:
|
|
150
|
+
.open-theme-switcher-button {
|
|
151
|
+
margin-top: 0.75rem;
|
|
131
152
|
}
|
|
132
153
|
|
|
133
154
|
.preview-row {
|
package/vue/examples/Welcome.vue
CHANGED
|
@@ -47,24 +47,6 @@
|
|
|
47
47
|
</Navigation>
|
|
48
48
|
</Section>
|
|
49
49
|
|
|
50
|
-
<Section
|
|
51
|
-
title="Popular examples"
|
|
52
|
-
subtitle="A few good places to begin"
|
|
53
|
-
>
|
|
54
|
-
<div class="grid-boxed welcome-popular-grid">
|
|
55
|
-
<router-link
|
|
56
|
-
v-for="example in popularExamples"
|
|
57
|
-
:key="example.name"
|
|
58
|
-
:to="{ name: example.name }"
|
|
59
|
-
class="stat-display welcome-popular-card"
|
|
60
|
-
>
|
|
61
|
-
<span class="subtle">{{ example.category }}</span>
|
|
62
|
-
<span class="stat">{{ example.label }}</span>
|
|
63
|
-
<span class="welcome-popular-description">{{ example.description }}</span>
|
|
64
|
-
</router-link>
|
|
65
|
-
</div>
|
|
66
|
-
</Section>
|
|
67
|
-
|
|
68
50
|
<Section
|
|
69
51
|
title="Try it"
|
|
70
52
|
subtitle="Small interactions you can trigger right now"
|
|
@@ -109,33 +91,6 @@ import logoUrl from '/logo.png'
|
|
|
109
91
|
const { show: showPopup } = useNotificationPopups()
|
|
110
92
|
const welcomeNavigation = ref(null)
|
|
111
93
|
|
|
112
|
-
const popularExamples = [
|
|
113
|
-
{
|
|
114
|
-
name: 'ThemePreview',
|
|
115
|
-
category: 'Layouts',
|
|
116
|
-
label: 'Theme Switcher',
|
|
117
|
-
description: 'Switch drop-in themes live',
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
name: 'TableExample',
|
|
121
|
-
category: 'Data display',
|
|
122
|
-
label: 'Table',
|
|
123
|
-
description: 'Sorting, pagination, and column filters',
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
name: 'CalendarExample',
|
|
127
|
-
category: 'Data display',
|
|
128
|
-
label: 'Calendar',
|
|
129
|
-
description: 'Events, drag-and-drop, and date ranges',
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
name: 'ButtonsExample',
|
|
133
|
-
category: 'Forms & input',
|
|
134
|
-
label: 'Buttons',
|
|
135
|
-
description: 'Variants, toolbars, and form actions',
|
|
136
|
-
},
|
|
137
|
-
]
|
|
138
|
-
|
|
139
94
|
function showWelcomeToast() {
|
|
140
95
|
showPopup({
|
|
141
96
|
label: 'WELCOME',
|
|
@@ -212,31 +167,4 @@ onMounted(() => {
|
|
|
212
167
|
gap: 0.5rem;
|
|
213
168
|
margin-bottom: 0.75rem;
|
|
214
169
|
}
|
|
215
|
-
|
|
216
|
-
.welcome-popular-grid {
|
|
217
|
-
grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
.welcome-popular-card {
|
|
221
|
-
display: flex;
|
|
222
|
-
flex-direction: column;
|
|
223
|
-
gap: 0.35rem;
|
|
224
|
-
text-decoration: none;
|
|
225
|
-
color: inherit;
|
|
226
|
-
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
.welcome-popular-card:hover {
|
|
230
|
-
transform: translateY(-2px);
|
|
231
|
-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
.welcome-popular-description {
|
|
235
|
-
font-size: 0.85rem;
|
|
236
|
-
color: #666;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
html[data-theme="dark"] .welcome-popular-description {
|
|
240
|
-
color: #bbb;
|
|
241
|
-
}
|
|
242
170
|
</style>
|