picocrank 1.25.2 → 1.27.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 +75 -0
- package/package.json +17 -4
- package/router.js +74 -0
- package/styles/dark-theme.css +20 -11
- package/styles/picocrank-extensions.css +123 -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/vite.config.js +5 -0
- package/vue/components/Breadcrumbs.vue +192 -45
- package/vue/components/Calendar.vue +162 -145
- package/vue/components/DangerZone.vue +186 -0
- package/vue/components/FormField.spec.js +350 -0
- package/vue/components/FormField.vue +236 -32
- package/vue/components/FormLayout.spec.js +56 -0
- package/vue/components/Header.vue +22 -6
- package/vue/components/Login.vue +152 -41
- package/vue/components/NavigationGrid.vue +26 -180
- package/vue/components/NotificationBlock.vue +120 -0
- package/vue/components/NotificationPopup.vue +44 -41
- package/vue/components/Pagination.vue +5 -4
- 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.spec.js +101 -0
- package/vue/components/Tabs.vue +455 -81
- package/vue/components/TopBar.vue +2 -2
- package/vue/components/form-validation.spec.js +126 -0
- 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/FormExample.vue +76 -48
- package/vue/examples/Login.vue +5 -3
- package/vue/examples/LoginExample.vue +99 -15
- package/vue/examples/NavSectionHub.vue +36 -0
- package/vue/examples/PageStructure.vue +15 -12
- package/vue/examples/StatusExample.vue +248 -129
- package/vue/examples/TableExample.vue +72 -71
- package/vue/examples/TabsExample.vue +236 -257
- package/vue/examples/ThemePreview.vue +21 -23
- package/vue/examples/ViewItem.vue +32 -5
- package/vue/examples/Welcome.vue +3 -40
- package/vue/test/formTestUtils.js +279 -0
- package/vue/test/setup.js +5 -0
|
@@ -1,145 +1,250 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<Section
|
|
2
|
+
<Section
|
|
3
|
+
title="Horizontal Tabs"
|
|
4
|
+
subtitle="A flexible tabbed control component with support for multiple tab panes and disabled tabs"
|
|
5
|
+
:padding="horizontalTabsSectionPadding"
|
|
6
|
+
>
|
|
3
7
|
<template #toolbar>
|
|
4
|
-
<
|
|
5
|
-
|
|
8
|
+
<label class="tabs-padding-toggle">
|
|
9
|
+
<input v-model="horizontalTabsSectionPadding" type="checkbox" />
|
|
10
|
+
Section padding
|
|
11
|
+
</label>
|
|
12
|
+
<label class="tabs-padding-toggle">
|
|
13
|
+
<input v-model="horizontalTabsPadding" type="checkbox" />
|
|
14
|
+
Tab padding
|
|
15
|
+
</label>
|
|
16
|
+
<label class="tabs-padding-toggle">
|
|
17
|
+
<input v-model="detailsTabEnabled" type="checkbox" />
|
|
18
|
+
Enable Details tab
|
|
19
|
+
</label>
|
|
6
20
|
</template>
|
|
7
21
|
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
<
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
</
|
|
70
|
-
</
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
22
|
+
<Tabs
|
|
23
|
+
:tabs="basicTabs"
|
|
24
|
+
:padding="horizontalTabsPadding"
|
|
25
|
+
@tab-change="onTabChange"
|
|
26
|
+
>
|
|
27
|
+
<template #tab-0>
|
|
28
|
+
<div class="tab-content">
|
|
29
|
+
<h4>Overview</h4>
|
|
30
|
+
<p>
|
|
31
|
+
This is the Overview tab. It contains general information about the current topic.
|
|
32
|
+
You can place any content here, including forms, tables, images, or other components.
|
|
33
|
+
The <strong>Details</strong> tab starts disabled — use the toolbar checkbox to
|
|
34
|
+
enable it when elevated permissions are granted.
|
|
35
|
+
</p>
|
|
36
|
+
<ul>
|
|
37
|
+
<li>First item in the overview</li>
|
|
38
|
+
<li>Second item with more details</li>
|
|
39
|
+
<li>Third item explaining features</li>
|
|
40
|
+
</ul>
|
|
41
|
+
</div>
|
|
42
|
+
</template>
|
|
43
|
+
<template #tab-1>
|
|
44
|
+
<div class="tab-content">
|
|
45
|
+
<h4>Details</h4>
|
|
46
|
+
<p>
|
|
47
|
+
The Details tab provides in-depth information. This section can be used to display
|
|
48
|
+
comprehensive data, specifications, or detailed descriptions.
|
|
49
|
+
</p>
|
|
50
|
+
<dl>
|
|
51
|
+
<dt>Property 1</dt>
|
|
52
|
+
<dd>Value 1</dd>
|
|
53
|
+
|
|
54
|
+
<dt>Property 2</dt>
|
|
55
|
+
<dd>Value 2</dd>
|
|
56
|
+
|
|
57
|
+
<dt>Property 3</dt>
|
|
58
|
+
<dd>Value 3</dd>
|
|
59
|
+
|
|
60
|
+
<dt>Property 4</dt>
|
|
61
|
+
<dd>Value 4</dd>
|
|
62
|
+
</dl>
|
|
63
|
+
</div>
|
|
64
|
+
</template>
|
|
65
|
+
<template #tab-2>
|
|
66
|
+
<div class="tab-content">
|
|
67
|
+
<h4>Settings</h4>
|
|
68
|
+
<p>Configure your preferences and settings in this tab.</p>
|
|
69
|
+
<FormLayout @submit.prevent="saveSettings">
|
|
70
|
+
<FormField label="Setting 1" for="tabs-setting1">
|
|
71
|
+
<input
|
|
72
|
+
id="tabs-setting1"
|
|
73
|
+
v-model="settings.setting1"
|
|
74
|
+
type="text"
|
|
75
|
+
placeholder="Enter value"
|
|
76
|
+
/>
|
|
77
|
+
</FormField>
|
|
78
|
+
|
|
79
|
+
<FormField label="Setting 2" for="tabs-setting2">
|
|
80
|
+
<select id="tabs-setting2" v-model="settings.setting2">
|
|
81
|
+
<option value="option1">Option 1</option>
|
|
82
|
+
<option value="option2">Option 2</option>
|
|
83
|
+
<option value="option3">Option 3</option>
|
|
84
|
+
</select>
|
|
85
|
+
</FormField>
|
|
86
|
+
|
|
87
|
+
<FormField label="Enable feature" for="tabs-setting-enabled">
|
|
88
|
+
<input
|
|
89
|
+
id="tabs-setting-enabled"
|
|
90
|
+
v-model="settings.enabled"
|
|
91
|
+
type="checkbox"
|
|
92
|
+
/>
|
|
93
|
+
</FormField>
|
|
94
|
+
|
|
95
|
+
<template #actions>
|
|
96
|
+
<button type="submit" class="good">Save Settings</button>
|
|
97
|
+
</template>
|
|
98
|
+
</FormLayout>
|
|
99
|
+
</div>
|
|
100
|
+
</template>
|
|
101
|
+
</Tabs>
|
|
102
|
+
</Section>
|
|
76
103
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
104
|
+
<Section
|
|
105
|
+
title="Vertical Tabs"
|
|
106
|
+
subtitle="Side tab strip with arrow-key navigation up and down; disabled tabs are skipped"
|
|
107
|
+
:padding="verticalTabsSectionPadding"
|
|
108
|
+
>
|
|
109
|
+
<template #toolbar>
|
|
110
|
+
<label class="tabs-padding-toggle">
|
|
111
|
+
<input v-model="verticalTabsSectionPadding" type="checkbox" />
|
|
112
|
+
Section padding
|
|
113
|
+
</label>
|
|
114
|
+
<label class="tabs-padding-toggle">
|
|
115
|
+
<input v-model="verticalTabsPadding" type="checkbox" />
|
|
116
|
+
Tab padding
|
|
117
|
+
</label>
|
|
118
|
+
<label class="tabs-padding-toggle">
|
|
119
|
+
<input v-model="advancedTabEnabled" type="checkbox" />
|
|
120
|
+
Enable Advanced tab
|
|
121
|
+
</label>
|
|
122
|
+
</template>
|
|
90
123
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
<
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
<
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
124
|
+
<Tabs
|
|
125
|
+
:tabs="verticalTabs"
|
|
126
|
+
orientation="vertical"
|
|
127
|
+
:padding="verticalTabsPadding"
|
|
128
|
+
@tab-change="onTabChange"
|
|
129
|
+
>
|
|
130
|
+
<template #tab-general>
|
|
131
|
+
<div class="tab-content">
|
|
132
|
+
<h4>General</h4>
|
|
133
|
+
<p>
|
|
134
|
+
Vertical tabs place the tab list beside the panel. Use them when labels are
|
|
135
|
+
longer or when the layout mirrors a settings sidebar.
|
|
136
|
+
<strong>Advanced</strong> starts disabled — use the toolbar checkbox to enable it.
|
|
137
|
+
</p>
|
|
138
|
+
</div>
|
|
139
|
+
</template>
|
|
140
|
+
<template #tab-appearance>
|
|
141
|
+
<div class="tab-content">
|
|
142
|
+
<h4>Appearance</h4>
|
|
143
|
+
<p>Theme and display options would live in this pane.</p>
|
|
144
|
+
<FormLayout>
|
|
145
|
+
<FormField label="Theme" for="vertical-tab-theme">
|
|
146
|
+
<select id="vertical-tab-theme">
|
|
147
|
+
<option>System</option>
|
|
148
|
+
<option>Light</option>
|
|
149
|
+
<option>Dark</option>
|
|
150
|
+
</select>
|
|
151
|
+
</FormField>
|
|
152
|
+
</FormLayout>
|
|
153
|
+
</div>
|
|
154
|
+
</template>
|
|
155
|
+
<template #tab-notifications>
|
|
156
|
+
<div class="tab-content">
|
|
157
|
+
<h4>Notifications</h4>
|
|
158
|
+
<p>Configure alerts and delivery preferences here.</p>
|
|
159
|
+
<ul>
|
|
160
|
+
<li>Email digests</li>
|
|
161
|
+
<li>Push notifications</li>
|
|
162
|
+
<li>In-app badges</li>
|
|
163
|
+
</ul>
|
|
164
|
+
</div>
|
|
165
|
+
</template>
|
|
166
|
+
<template #tab-advanced>
|
|
167
|
+
<div class="tab-content">
|
|
168
|
+
<h4>Advanced</h4>
|
|
169
|
+
<p>Less common options grouped at the bottom of the strip.</p>
|
|
170
|
+
</div>
|
|
171
|
+
</template>
|
|
172
|
+
</Tabs>
|
|
173
|
+
</Section>
|
|
174
|
+
|
|
175
|
+
<Section
|
|
176
|
+
title="Dynamic Tabs"
|
|
177
|
+
subtitle="Add and remove tab panes programmatically"
|
|
178
|
+
>
|
|
179
|
+
<template #toolbar>
|
|
180
|
+
<button @click="addRandomTab" class="good">Add Tab</button>
|
|
181
|
+
<button @click="removeLastTab" class="bad" :disabled="tabs.length <= 1">Remove Tab</button>
|
|
182
|
+
</template>
|
|
183
|
+
|
|
184
|
+
<Tabs :tabs="tabs" :default-tab="0" @tab-change="onDynamicTabChange">
|
|
185
|
+
<template v-for="(tab, index) in tabs" :key="tab.id" #[`tab-${index}`]>
|
|
186
|
+
<div class="tab-content">
|
|
187
|
+
<h4>{{ tab.label }}</h4>
|
|
188
|
+
<p>{{ tab.content }}</p>
|
|
189
|
+
<div class="tab-info">
|
|
190
|
+
<p><strong>Tab ID:</strong> {{ tab.id }}</p>
|
|
191
|
+
<p><strong>Created:</strong> {{ tab.created }}</p>
|
|
116
192
|
</div>
|
|
117
|
-
</
|
|
118
|
-
</
|
|
119
|
-
</
|
|
193
|
+
</div>
|
|
194
|
+
</template>
|
|
195
|
+
</Tabs>
|
|
120
196
|
</Section>
|
|
121
197
|
</template>
|
|
122
198
|
|
|
123
199
|
<script setup>
|
|
124
|
-
import { ref, reactive } from 'vue';
|
|
200
|
+
import { computed, ref, reactive } from 'vue';
|
|
201
|
+
import { HomeIcon, Settings01Icon, UserIcon, ViewIcon } from '@hugeicons/core-free-icons';
|
|
125
202
|
import Section from '../components/Section.vue';
|
|
126
203
|
import Tabs from '../components/Tabs.vue';
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
// Example: import { HomeIcon, UserIcon, SettingsIcon } from '@hugeicons/core-free-icons';
|
|
204
|
+
import FormLayout from '../components/FormLayout.vue';
|
|
205
|
+
import FormField from '../components/FormField.vue';
|
|
130
206
|
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
207
|
+
const detailsTabEnabled = ref(false);
|
|
208
|
+
const advancedTabEnabled = ref(false);
|
|
209
|
+
|
|
210
|
+
const basicTabs = computed(() => [
|
|
211
|
+
{ id: 0, label: 'Overview', icon: HomeIcon },
|
|
212
|
+
{
|
|
213
|
+
id: 1,
|
|
214
|
+
label: 'Details',
|
|
215
|
+
icon: ViewIcon,
|
|
216
|
+
...(detailsTabEnabled.value
|
|
217
|
+
? {}
|
|
218
|
+
: {
|
|
219
|
+
disabled: true,
|
|
220
|
+
disabledTitle: 'Details requires elevated permissions',
|
|
221
|
+
}),
|
|
222
|
+
},
|
|
223
|
+
{ id: 2, label: 'Settings', icon: Settings01Icon },
|
|
135
224
|
]);
|
|
136
225
|
|
|
137
|
-
const
|
|
138
|
-
{ id: '
|
|
139
|
-
{ id: '
|
|
140
|
-
{ id: '
|
|
226
|
+
const verticalTabs = computed(() => [
|
|
227
|
+
{ id: 'general', label: 'General', icon: HomeIcon },
|
|
228
|
+
{ id: 'appearance', label: 'Appearance', icon: Settings01Icon },
|
|
229
|
+
{ id: 'notifications', label: 'Notifications', icon: UserIcon },
|
|
230
|
+
{
|
|
231
|
+
id: 'advanced',
|
|
232
|
+
label: 'Advanced',
|
|
233
|
+
icon: Settings01Icon,
|
|
234
|
+
...(advancedTabEnabled.value
|
|
235
|
+
? {}
|
|
236
|
+
: {
|
|
237
|
+
disabled: true,
|
|
238
|
+
disabledTitle: 'Advanced settings are not available for this account',
|
|
239
|
+
}),
|
|
240
|
+
},
|
|
141
241
|
]);
|
|
142
242
|
|
|
243
|
+
const horizontalTabsSectionPadding = ref(true);
|
|
244
|
+
const horizontalTabsPadding = ref(false);
|
|
245
|
+
const verticalTabsSectionPadding = ref(true);
|
|
246
|
+
const verticalTabsPadding = ref(false);
|
|
247
|
+
|
|
143
248
|
const tabs = ref([
|
|
144
249
|
{
|
|
145
250
|
id: 0,
|
|
@@ -196,142 +301,16 @@ function saveSettings() {
|
|
|
196
301
|
</script>
|
|
197
302
|
|
|
198
303
|
<style scoped>
|
|
199
|
-
.tabs-
|
|
200
|
-
display: flex;
|
|
201
|
-
|
|
202
|
-
gap:
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
.tabs-example-container h3 {
|
|
206
|
-
margin: 0 0 1rem 0;
|
|
207
|
-
font-size: 1.3em;
|
|
208
|
-
font-weight: 600;
|
|
209
|
-
border-bottom: 1px solid var(--border-color, #e1e5e9);
|
|
210
|
-
padding-bottom: 0.5rem;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
.tab-content {
|
|
214
|
-
display: flex;
|
|
215
|
-
flex-direction: column;
|
|
216
|
-
gap: 1rem;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
.tab-content h4 {
|
|
220
|
-
margin: 0 0 0.5rem 0;
|
|
221
|
-
font-size: 1.2em;
|
|
222
|
-
font-weight: 600;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
.tab-content p {
|
|
226
|
-
margin: 0;
|
|
227
|
-
line-height: 1.6;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
.tab-content ul {
|
|
231
|
-
margin: 0.5rem 0;
|
|
232
|
-
padding-left: 1.5rem;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
.tab-content li {
|
|
236
|
-
margin: 0.5rem 0;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
.details-grid {
|
|
240
|
-
display: grid;
|
|
241
|
-
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
242
|
-
gap: 1rem;
|
|
243
|
-
margin-top: 1rem;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
.detail-item {
|
|
247
|
-
padding: 0.75rem;
|
|
248
|
-
background-color: var(--hover-background-color, #f5f5f5);
|
|
249
|
-
border-radius: 4px;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
.settings-form {
|
|
253
|
-
display: flex;
|
|
254
|
-
flex-direction: column;
|
|
255
|
-
gap: 1rem;
|
|
256
|
-
margin-top: 1rem;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
.form-group {
|
|
260
|
-
display: flex;
|
|
261
|
-
flex-direction: column;
|
|
262
|
-
gap: 0.5rem;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
.form-group label {
|
|
266
|
-
font-weight: 500;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
.form-group input[type="text"],
|
|
270
|
-
.form-group select {
|
|
271
|
-
padding: 0.5rem;
|
|
272
|
-
border: 1px solid var(--border-color, #ccc);
|
|
273
|
-
border-radius: 4px;
|
|
274
|
-
font-size: 1em;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
.form-group input[type="checkbox"] {
|
|
278
|
-
margin-right: 0.5rem;
|
|
304
|
+
.tabs-padding-toggle {
|
|
305
|
+
display: inline-flex;
|
|
306
|
+
align-items: center;
|
|
307
|
+
gap: 0.4em;
|
|
308
|
+
margin-right: 0.75em;
|
|
279
309
|
}
|
|
280
310
|
|
|
281
311
|
.tab-info {
|
|
282
|
-
margin-top: 1rem;
|
|
283
312
|
padding: 1rem;
|
|
284
|
-
background-color: var(--
|
|
285
|
-
border-radius:
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
.tab-info p {
|
|
289
|
-
margin: 0.5rem 0;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
.profile-card {
|
|
293
|
-
display: flex;
|
|
294
|
-
align-items: center;
|
|
295
|
-
gap: 1rem;
|
|
296
|
-
padding: 1rem;
|
|
297
|
-
background-color: var(--hover-background-color, #f5f5f5);
|
|
298
|
-
border-radius: 8px;
|
|
299
|
-
margin-top: 1rem;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
.profile-avatar {
|
|
303
|
-
width: 60px;
|
|
304
|
-
height: 60px;
|
|
305
|
-
border-radius: 50%;
|
|
306
|
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
307
|
-
display: flex;
|
|
308
|
-
align-items: center;
|
|
309
|
-
justify-content: center;
|
|
310
|
-
color: white;
|
|
311
|
-
font-size: 1.5em;
|
|
312
|
-
font-weight: bold;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
.profile-info h5 {
|
|
316
|
-
margin: 0 0 0.25rem 0;
|
|
317
|
-
font-size: 1.1em;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
.profile-info p {
|
|
321
|
-
margin: 0;
|
|
322
|
-
color: var(--text-muted, #666);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
html[data-theme="dark"] {
|
|
326
|
-
.detail-item,
|
|
327
|
-
.tab-info,
|
|
328
|
-
.profile-card {
|
|
329
|
-
background-color: var(--hover-background-color, #333);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
.detail-item strong,
|
|
333
|
-
.tab-info strong {
|
|
334
|
-
color: var(--text-color, #fff);
|
|
335
|
-
}
|
|
313
|
+
background-color: var(--standout-bg-color);
|
|
314
|
+
border-radius: 0.4em;
|
|
336
315
|
}
|
|
337
316
|
</style>
|
|
@@ -40,21 +40,26 @@
|
|
|
40
40
|
</Section>
|
|
41
41
|
|
|
42
42
|
<Section title="Notifications" subtitle="Block-level karma status">
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
<
|
|
56
|
-
<
|
|
57
|
-
|
|
43
|
+
<NotificationBlock
|
|
44
|
+
type="success"
|
|
45
|
+
message="Theme tokens are applying to karma backgrounds."
|
|
46
|
+
/>
|
|
47
|
+
<NotificationBlock
|
|
48
|
+
type="critical"
|
|
49
|
+
message="Check contrast on error surfaces."
|
|
50
|
+
/>
|
|
51
|
+
<NotificationBlock
|
|
52
|
+
type="warning"
|
|
53
|
+
message="Accent and border colors should stay readable."
|
|
54
|
+
/>
|
|
55
|
+
<span class="annotation good">
|
|
56
|
+
<span class="annotation-key">success</span>
|
|
57
|
+
<span class="annotation-val">Inline annotation with karma tint.</span>
|
|
58
|
+
</span>
|
|
59
|
+
<span class="annotation warning">
|
|
60
|
+
<span class="annotation-key">warning</span>
|
|
61
|
+
<span class="annotation-val">Another inline status chip.</span>
|
|
62
|
+
</span>
|
|
58
63
|
</Section>
|
|
59
64
|
|
|
60
65
|
<Section title="Form controls" subtitle="Inputs inherit theme variables">
|
|
@@ -132,6 +137,7 @@ import { inject } from 'vue'
|
|
|
132
137
|
import Section from '../components/Section.vue'
|
|
133
138
|
import FormLayout from '../components/FormLayout.vue'
|
|
134
139
|
import FormField from '../components/FormField.vue'
|
|
140
|
+
import NotificationBlock from '../components/NotificationBlock.vue'
|
|
135
141
|
import { HEADER_THEME_SWITCHER_SELECT_ID } from '../components/ThemeSwitcher.vue'
|
|
136
142
|
|
|
137
143
|
const themeSwitcherEnabled = inject('themeSwitcherEnabled')
|
|
@@ -168,12 +174,4 @@ function openThemeSwitcher() {
|
|
|
168
174
|
padding: 0.5rem 0.75rem;
|
|
169
175
|
border-bottom: 1px solid var(--border-color, #d7d7d7);
|
|
170
176
|
}
|
|
171
|
-
|
|
172
|
-
.notification {
|
|
173
|
-
margin-bottom: 0.5rem;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
.inline-notification {
|
|
177
|
-
margin: 0.5rem 0;
|
|
178
|
-
}
|
|
179
177
|
</style>
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
<h2 :title="item.subtitle">{{ item.title }}</h2>
|
|
6
6
|
</div>
|
|
7
7
|
<div role="toolbar">
|
|
8
|
-
<router-link :to="{ name: 'TableExample' }" class="button neutral">Back to table</router-link>
|
|
9
8
|
<button type="button" class="good">Edit</button>
|
|
10
9
|
</div>
|
|
11
10
|
</div>
|
|
@@ -83,10 +82,23 @@
|
|
|
83
82
|
</ul>
|
|
84
83
|
</div>
|
|
85
84
|
</section>
|
|
85
|
+
|
|
86
|
+
<DangerZone
|
|
87
|
+
title="Danger zone"
|
|
88
|
+
subtitle="Destructive actions for this record"
|
|
89
|
+
description="Show destructive actions"
|
|
90
|
+
>
|
|
91
|
+
<div role="toolbar" class="danger-zone-actions">
|
|
92
|
+
<button type="button" class="bad" @click="deleteItem">
|
|
93
|
+
Delete {{ item.name }}
|
|
94
|
+
</button>
|
|
95
|
+
</div>
|
|
96
|
+
</DangerZone>
|
|
86
97
|
</template>
|
|
87
98
|
|
|
88
99
|
<script setup>
|
|
89
100
|
import { computed } from 'vue'
|
|
101
|
+
import DangerZone from '../components/DangerZone.vue'
|
|
90
102
|
|
|
91
103
|
const props = defineProps({
|
|
92
104
|
id: {
|
|
@@ -102,7 +114,7 @@ const itemsById = {
|
|
|
102
114
|
summary: 'Read-only detail view for a single record, using Femtocrank definition lists and section subheaders.',
|
|
103
115
|
name: 'Acme deployment',
|
|
104
116
|
status: 'Active',
|
|
105
|
-
statusClass: '
|
|
117
|
+
statusClass: 'good',
|
|
106
118
|
role: 'Release',
|
|
107
119
|
location: 'us-east-1',
|
|
108
120
|
email: 'releases@example.com',
|
|
@@ -124,7 +136,7 @@ const itemsById = {
|
|
|
124
136
|
summary: 'Example person record linked from the table example.',
|
|
125
137
|
name: 'Alice',
|
|
126
138
|
status: 'Active',
|
|
127
|
-
statusClass: '
|
|
139
|
+
statusClass: 'good',
|
|
128
140
|
role: 'Senior analyst',
|
|
129
141
|
location: 'New York',
|
|
130
142
|
email: 'alice@example.com',
|
|
@@ -145,7 +157,7 @@ const itemsById = {
|
|
|
145
157
|
summary: 'Example person record linked from the table example.',
|
|
146
158
|
name: 'Bob',
|
|
147
159
|
status: 'Away',
|
|
148
|
-
statusClass: '
|
|
160
|
+
statusClass: 'warning',
|
|
149
161
|
role: 'Support specialist',
|
|
150
162
|
location: 'Los Angeles',
|
|
151
163
|
email: 'bob@example.com',
|
|
@@ -175,7 +187,7 @@ const item = computed(() => {
|
|
|
175
187
|
summary: 'Generic detail view for records opened from the table or navigation.',
|
|
176
188
|
name: label,
|
|
177
189
|
status: 'Unknown',
|
|
178
|
-
statusClass: '
|
|
190
|
+
statusClass: 'note',
|
|
179
191
|
role: '—',
|
|
180
192
|
location: '—',
|
|
181
193
|
email: `${key.toLowerCase()}@example.com`,
|
|
@@ -188,4 +200,19 @@ const item = computed(() => {
|
|
|
188
200
|
activity: [{ when: '—', text: 'No activity recorded for this item.' }],
|
|
189
201
|
}
|
|
190
202
|
})
|
|
203
|
+
|
|
204
|
+
function deleteItem() {
|
|
205
|
+
const name = item.value.name
|
|
206
|
+
if (window.confirm(`Delete “${name}”? This action cannot be undone.`)) {
|
|
207
|
+
window.alert(`“${name}” would be deleted. (Demo only — no request was sent.)`)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
191
210
|
</script>
|
|
211
|
+
|
|
212
|
+
<style scoped>
|
|
213
|
+
.danger-zone-actions {
|
|
214
|
+
display: flex;
|
|
215
|
+
flex-wrap: wrap;
|
|
216
|
+
gap: 0.5rem;
|
|
217
|
+
}
|
|
218
|
+
</style>
|