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
|
@@ -1,145 +1,216 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<Section
|
|
2
|
+
<Section
|
|
3
|
+
title="Horizontal Tabs"
|
|
4
|
+
subtitle="A flexible tabbed control component with support for multiple tab panes"
|
|
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>
|
|
6
16
|
</template>
|
|
7
17
|
|
|
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
|
-
|
|
18
|
+
<Tabs
|
|
19
|
+
:tabs="basicTabs"
|
|
20
|
+
:padding="horizontalTabsPadding"
|
|
21
|
+
@tab-change="onTabChange"
|
|
22
|
+
>
|
|
23
|
+
<template #tab-0>
|
|
24
|
+
<div class="tab-content">
|
|
25
|
+
<h4>Overview</h4>
|
|
26
|
+
<p>
|
|
27
|
+
This is the Overview tab. It contains general information about the current topic.
|
|
28
|
+
You can place any content here, including forms, tables, images, or other components.
|
|
29
|
+
</p>
|
|
30
|
+
<ul>
|
|
31
|
+
<li>First item in the overview</li>
|
|
32
|
+
<li>Second item with more details</li>
|
|
33
|
+
<li>Third item explaining features</li>
|
|
34
|
+
</ul>
|
|
35
|
+
</div>
|
|
36
|
+
</template>
|
|
37
|
+
<template #tab-1>
|
|
38
|
+
<div class="tab-content">
|
|
39
|
+
<h4>Details</h4>
|
|
40
|
+
<p>
|
|
41
|
+
The Details tab provides in-depth information. This section can be used to display
|
|
42
|
+
comprehensive data, specifications, or detailed descriptions.
|
|
43
|
+
</p>
|
|
44
|
+
<dl>
|
|
45
|
+
<dt>Property 1</dt>
|
|
46
|
+
<dd>Value 1</dd>
|
|
47
|
+
|
|
48
|
+
<dt>Property 2</dt>
|
|
49
|
+
<dd>Value 2</dd>
|
|
50
|
+
|
|
51
|
+
<dt>Property 3</dt>
|
|
52
|
+
<dd>Value 3</dd>
|
|
53
|
+
|
|
54
|
+
<dt>Property 4</dt>
|
|
55
|
+
<dd>Value 4</dd>
|
|
56
|
+
</dl>
|
|
57
|
+
</div>
|
|
58
|
+
</template>
|
|
59
|
+
<template #tab-2>
|
|
60
|
+
<div class="tab-content">
|
|
61
|
+
<h4>Settings</h4>
|
|
62
|
+
<p>Configure your preferences and settings in this tab.</p>
|
|
63
|
+
<FormLayout @submit.prevent="saveSettings">
|
|
64
|
+
<FormField label="Setting 1" for="tabs-setting1">
|
|
65
|
+
<input
|
|
66
|
+
id="tabs-setting1"
|
|
67
|
+
v-model="settings.setting1"
|
|
68
|
+
type="text"
|
|
69
|
+
placeholder="Enter value"
|
|
70
|
+
/>
|
|
71
|
+
</FormField>
|
|
72
|
+
|
|
73
|
+
<FormField label="Setting 2" for="tabs-setting2">
|
|
74
|
+
<select id="tabs-setting2" v-model="settings.setting2">
|
|
75
|
+
<option value="option1">Option 1</option>
|
|
76
|
+
<option value="option2">Option 2</option>
|
|
77
|
+
<option value="option3">Option 3</option>
|
|
78
|
+
</select>
|
|
79
|
+
</FormField>
|
|
80
|
+
|
|
81
|
+
<FormField label="Enable feature" for="tabs-setting-enabled">
|
|
82
|
+
<input
|
|
83
|
+
id="tabs-setting-enabled"
|
|
84
|
+
v-model="settings.enabled"
|
|
85
|
+
type="checkbox"
|
|
86
|
+
/>
|
|
87
|
+
</FormField>
|
|
88
|
+
|
|
89
|
+
<template #actions>
|
|
90
|
+
<button type="submit" class="good">Save Settings</button>
|
|
91
|
+
</template>
|
|
92
|
+
</FormLayout>
|
|
93
|
+
</div>
|
|
94
|
+
</template>
|
|
95
|
+
</Tabs>
|
|
96
|
+
</Section>
|
|
76
97
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
98
|
+
<Section
|
|
99
|
+
title="Vertical Tabs"
|
|
100
|
+
subtitle="Side tab strip with arrow-key navigation up and down"
|
|
101
|
+
:padding="verticalTabsSectionPadding"
|
|
102
|
+
>
|
|
103
|
+
<template #toolbar>
|
|
104
|
+
<label class="tabs-padding-toggle">
|
|
105
|
+
<input v-model="verticalTabsSectionPadding" type="checkbox" />
|
|
106
|
+
Section padding
|
|
107
|
+
</label>
|
|
108
|
+
<label class="tabs-padding-toggle">
|
|
109
|
+
<input v-model="verticalTabsPadding" type="checkbox" />
|
|
110
|
+
Tab padding
|
|
111
|
+
</label>
|
|
112
|
+
</template>
|
|
90
113
|
|
|
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
|
-
|
|
114
|
+
<Tabs
|
|
115
|
+
:tabs="verticalTabs"
|
|
116
|
+
orientation="vertical"
|
|
117
|
+
:padding="verticalTabsPadding"
|
|
118
|
+
@tab-change="onTabChange"
|
|
119
|
+
>
|
|
120
|
+
<template #tab-general>
|
|
121
|
+
<div class="tab-content">
|
|
122
|
+
<h4>General</h4>
|
|
123
|
+
<p>
|
|
124
|
+
Vertical tabs place the tab list beside the panel. Use them when labels are
|
|
125
|
+
longer or when the layout mirrors a settings sidebar.
|
|
126
|
+
</p>
|
|
127
|
+
</div>
|
|
128
|
+
</template>
|
|
129
|
+
<template #tab-appearance>
|
|
130
|
+
<div class="tab-content">
|
|
131
|
+
<h4>Appearance</h4>
|
|
132
|
+
<p>Theme and display options would live in this pane.</p>
|
|
133
|
+
<FormLayout>
|
|
134
|
+
<FormField label="Theme" for="vertical-tab-theme">
|
|
135
|
+
<select id="vertical-tab-theme">
|
|
136
|
+
<option>System</option>
|
|
137
|
+
<option>Light</option>
|
|
138
|
+
<option>Dark</option>
|
|
139
|
+
</select>
|
|
140
|
+
</FormField>
|
|
141
|
+
</FormLayout>
|
|
142
|
+
</div>
|
|
143
|
+
</template>
|
|
144
|
+
<template #tab-notifications>
|
|
145
|
+
<div class="tab-content">
|
|
146
|
+
<h4>Notifications</h4>
|
|
147
|
+
<p>Configure alerts and delivery preferences here.</p>
|
|
148
|
+
<ul>
|
|
149
|
+
<li>Email digests</li>
|
|
150
|
+
<li>Push notifications</li>
|
|
151
|
+
<li>In-app badges</li>
|
|
152
|
+
</ul>
|
|
153
|
+
</div>
|
|
154
|
+
</template>
|
|
155
|
+
<template #tab-advanced>
|
|
156
|
+
<div class="tab-content">
|
|
157
|
+
<h4>Advanced</h4>
|
|
158
|
+
<p>Less common options grouped at the bottom of the strip.</p>
|
|
159
|
+
</div>
|
|
160
|
+
</template>
|
|
161
|
+
</Tabs>
|
|
162
|
+
</Section>
|
|
163
|
+
|
|
164
|
+
<Section
|
|
165
|
+
title="Dynamic Tabs"
|
|
166
|
+
subtitle="Add and remove tab panes programmatically"
|
|
167
|
+
>
|
|
168
|
+
<template #toolbar>
|
|
169
|
+
<button @click="addRandomTab" class="good">Add Tab</button>
|
|
170
|
+
<button @click="removeLastTab" class="bad" :disabled="tabs.length <= 1">Remove Tab</button>
|
|
171
|
+
</template>
|
|
172
|
+
|
|
173
|
+
<Tabs :tabs="tabs" :default-tab="0" @tab-change="onDynamicTabChange">
|
|
174
|
+
<template v-for="(tab, index) in tabs" :key="tab.id" #[`tab-${index}`]>
|
|
175
|
+
<div class="tab-content">
|
|
176
|
+
<h4>{{ tab.label }}</h4>
|
|
177
|
+
<p>{{ tab.content }}</p>
|
|
178
|
+
<div class="tab-info">
|
|
179
|
+
<p><strong>Tab ID:</strong> {{ tab.id }}</p>
|
|
180
|
+
<p><strong>Created:</strong> {{ tab.created }}</p>
|
|
116
181
|
</div>
|
|
117
|
-
</
|
|
118
|
-
</
|
|
119
|
-
</
|
|
182
|
+
</div>
|
|
183
|
+
</template>
|
|
184
|
+
</Tabs>
|
|
120
185
|
</Section>
|
|
121
186
|
</template>
|
|
122
187
|
|
|
123
188
|
<script setup>
|
|
124
189
|
import { ref, reactive } from 'vue';
|
|
190
|
+
import { HomeIcon, Settings01Icon, UserIcon, ViewIcon } from '@hugeicons/core-free-icons';
|
|
125
191
|
import Section from '../components/Section.vue';
|
|
126
192
|
import Tabs from '../components/Tabs.vue';
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
// Example: import { HomeIcon, UserIcon, SettingsIcon } from '@hugeicons/core-free-icons';
|
|
193
|
+
import FormLayout from '../components/FormLayout.vue';
|
|
194
|
+
import FormField from '../components/FormField.vue';
|
|
130
195
|
|
|
131
196
|
const basicTabs = ref([
|
|
132
|
-
{ id: 0, label: 'Overview' },
|
|
133
|
-
{ id: 1, label: 'Details' },
|
|
134
|
-
{ id: 2, label: 'Settings' }
|
|
197
|
+
{ id: 0, label: 'Overview', icon: HomeIcon },
|
|
198
|
+
{ id: 1, label: 'Details', icon: ViewIcon },
|
|
199
|
+
{ id: 2, label: 'Settings', icon: Settings01Icon },
|
|
135
200
|
]);
|
|
136
201
|
|
|
137
|
-
const
|
|
138
|
-
{ id: '
|
|
139
|
-
{ id: '
|
|
140
|
-
{ id: '
|
|
202
|
+
const verticalTabs = ref([
|
|
203
|
+
{ id: 'general', label: 'General', icon: HomeIcon },
|
|
204
|
+
{ id: 'appearance', label: 'Appearance', icon: Settings01Icon },
|
|
205
|
+
{ id: 'notifications', label: 'Notifications', icon: UserIcon },
|
|
206
|
+
{ id: 'advanced', label: 'Advanced', icon: Settings01Icon },
|
|
141
207
|
]);
|
|
142
208
|
|
|
209
|
+
const horizontalTabsSectionPadding = ref(true);
|
|
210
|
+
const horizontalTabsPadding = ref(false);
|
|
211
|
+
const verticalTabsSectionPadding = ref(true);
|
|
212
|
+
const verticalTabsPadding = ref(false);
|
|
213
|
+
|
|
143
214
|
const tabs = ref([
|
|
144
215
|
{
|
|
145
216
|
id: 0,
|
|
@@ -196,142 +267,16 @@ function saveSettings() {
|
|
|
196
267
|
</script>
|
|
197
268
|
|
|
198
269
|
<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;
|
|
270
|
+
.tabs-padding-toggle {
|
|
271
|
+
display: inline-flex;
|
|
272
|
+
align-items: center;
|
|
273
|
+
gap: 0.4em;
|
|
274
|
+
margin-right: 0.75em;
|
|
279
275
|
}
|
|
280
276
|
|
|
281
277
|
.tab-info {
|
|
282
|
-
margin-top: 1rem;
|
|
283
|
-
padding: 1rem;
|
|
284
|
-
background-color: var(--hover-background-color, #f5f5f5);
|
|
285
|
-
border-radius: 4px;
|
|
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
278
|
padding: 1rem;
|
|
297
|
-
background-color: var(--
|
|
298
|
-
border-radius:
|
|
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
|
-
}
|
|
279
|
+
background-color: var(--standout-bg-color);
|
|
280
|
+
border-radius: 0.4em;
|
|
336
281
|
}
|
|
337
282
|
</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>
|