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.
Files changed (51) hide show
  1. package/eslint.config.js +75 -0
  2. package/package.json +17 -4
  3. package/router.js +74 -0
  4. package/styles/dark-theme.css +20 -11
  5. package/styles/picocrank-extensions.css +123 -30
  6. package/supplemental-themes/catppuccin-latte-frappe/theme.css +10 -24
  7. package/supplemental-themes/dracula-alucard/theme.css +10 -24
  8. package/supplemental-themes/gruvbox-dark-light/theme.css +10 -24
  9. package/supplemental-themes/waffles/theme.css +2 -16
  10. package/vite.config.js +5 -0
  11. package/vue/components/Breadcrumbs.vue +192 -45
  12. package/vue/components/Calendar.vue +162 -145
  13. package/vue/components/DangerZone.vue +186 -0
  14. package/vue/components/FormField.spec.js +350 -0
  15. package/vue/components/FormField.vue +236 -32
  16. package/vue/components/FormLayout.spec.js +56 -0
  17. package/vue/components/Header.vue +22 -6
  18. package/vue/components/Login.vue +152 -41
  19. package/vue/components/NavigationGrid.vue +26 -180
  20. package/vue/components/NotificationBlock.vue +120 -0
  21. package/vue/components/NotificationPopup.vue +44 -41
  22. package/vue/components/Pagination.vue +5 -4
  23. package/vue/components/ReadOnlyTextArea.vue +1 -1
  24. package/vue/components/SidebarNavLink.vue +3 -3
  25. package/vue/components/Table.vue +107 -79
  26. package/vue/components/TableColumnOptionsPopover.vue +9 -0
  27. package/vue/components/Tabs.spec.js +101 -0
  28. package/vue/components/Tabs.vue +455 -81
  29. package/vue/components/TopBar.vue +2 -2
  30. package/vue/components/form-validation.spec.js +126 -0
  31. package/vue/composables/tableColumnPriorities.js +33 -0
  32. package/vue/composables/tableFilters.js +1 -1
  33. package/vue/data/navSectionHubs.js +196 -0
  34. package/vue/data/routeBreadcrumbs.js +90 -0
  35. package/vue/examples/Admin.vue +12 -12
  36. package/vue/examples/App.vue +27 -36
  37. package/vue/examples/CalendarExample.vue +32 -22
  38. package/vue/examples/ExampleHeaderThemeSwitcher.vue +1 -1
  39. package/vue/examples/FormExample.vue +76 -48
  40. package/vue/examples/Login.vue +5 -3
  41. package/vue/examples/LoginExample.vue +99 -15
  42. package/vue/examples/NavSectionHub.vue +36 -0
  43. package/vue/examples/PageStructure.vue +15 -12
  44. package/vue/examples/StatusExample.vue +248 -129
  45. package/vue/examples/TableExample.vue +72 -71
  46. package/vue/examples/TabsExample.vue +236 -257
  47. package/vue/examples/ThemePreview.vue +21 -23
  48. package/vue/examples/ViewItem.vue +32 -5
  49. package/vue/examples/Welcome.vue +3 -40
  50. package/vue/test/formTestUtils.js +279 -0
  51. package/vue/test/setup.js +5 -0
@@ -1,145 +1,250 @@
1
1
  <template>
2
- <Section title="Tabs Example" subtitle="A flexible tabbed control component with support for multiple tab panes">
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
- <button @click="addRandomTab" class="good">Add Tab</button>
5
- <button @click="removeLastTab" class="bad" :disabled="tabs.length <= 1">Remove Tab</button>
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
- <div class="tabs-example-container">
9
- <h3>Basic Tabs</h3>
10
- <Tabs :tabs="basicTabs" @tab-change="onTabChange">
11
- <template #tab-0>
12
- <div class="tab-content">
13
- <h4>Overview</h4>
14
- <p>
15
- This is the Overview tab. It contains general information about the current topic.
16
- You can place any content here, including forms, tables, images, or other components.
17
- </p>
18
- <ul>
19
- <li>First item in the overview</li>
20
- <li>Second item with more details</li>
21
- <li>Third item explaining features</li>
22
- </ul>
23
- </div>
24
- </template>
25
- <template #tab-1>
26
- <div class="tab-content">
27
- <h4>Details</h4>
28
- <p>
29
- The Details tab provides in-depth information. This section can be used to display
30
- comprehensive data, specifications, or detailed descriptions.
31
- </p>
32
- <div class="details-grid">
33
- <div class="detail-item">
34
- <strong>Property 1:</strong> Value 1
35
- </div>
36
- <div class="detail-item">
37
- <strong>Property 2:</strong> Value 2
38
- </div>
39
- <div class="detail-item">
40
- <strong>Property 3:</strong> Value 3
41
- </div>
42
- <div class="detail-item">
43
- <strong>Property 4:</strong> Value 4
44
- </div>
45
- </div>
46
- </div>
47
- </template>
48
- <template #tab-2>
49
- <div class="tab-content">
50
- <h4>Settings</h4>
51
- <p>Configure your preferences and settings in this tab.</p>
52
- <form class="settings-form">
53
- <div class="form-group">
54
- <label for="setting1">Setting 1:</label>
55
- <input id="setting1" type="text" v-model="settings.setting1" placeholder="Enter value" />
56
- </div>
57
- <div class="form-group">
58
- <label for="setting2">Setting 2:</label>
59
- <select id="setting2" v-model="settings.setting2">
60
- <option value="option1">Option 1</option>
61
- <option value="option2">Option 2</option>
62
- <option value="option3">Option 3</option>
63
- </select>
64
- </div>
65
- <div class="form-group">
66
- <label>
67
- <input type="checkbox" v-model="settings.enabled" />
68
- Enable feature
69
- </label>
70
- </div>
71
- <button type="button" class="good" @click="saveSettings">Save Settings</button>
72
- </form>
73
- </div>
74
- </template>
75
- </Tabs>
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
- <h3>Dynamic Tabs</h3>
78
- <Tabs :tabs="tabs" :default-tab="0" @tab-change="onDynamicTabChange">
79
- <template v-for="(tab, index) in tabs" :key="tab.id" #[`tab-${index}`]>
80
- <div class="tab-content">
81
- <h4>{{ tab.label }}</h4>
82
- <p>{{ tab.content }}</p>
83
- <div class="tab-info">
84
- <p><strong>Tab ID:</strong> {{ tab.id }}</p>
85
- <p><strong>Created:</strong> {{ tab.created }}</p>
86
- </div>
87
- </div>
88
- </template>
89
- </Tabs>
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
- <h3>Tabs with Icons</h3>
92
- <Tabs :tabs="iconTabs" @tab-change="onTabChange">
93
- <template #tab-home>
94
- <div class="tab-content">
95
- <h4>Home</h4>
96
- <p>Welcome to the home section. This tab demonstrates the use of icons in tab labels.</p>
97
- </div>
98
- </template>
99
- <template #tab-profile>
100
- <div class="tab-content">
101
- <h4>Profile</h4>
102
- <p>View and edit your profile information here.</p>
103
- <div class="profile-card">
104
- <div class="profile-avatar">JD</div>
105
- <div class="profile-info">
106
- <h5>John Doe</h5>
107
- <p>john.doe@example.com</p>
108
- </div>
109
- </div>
110
- </div>
111
- </template>
112
- <template #tab-settings>
113
- <div class="tab-content">
114
- <h4>Settings</h4>
115
- <p>Manage your application settings and preferences.</p>
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
- </template>
118
- </Tabs>
119
- </div>
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
- // Icons are optional - set to null if not available
128
- // You can import specific icons from @hugeicons/core-free-icons if available
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 basicTabs = ref([
132
- { id: 0, label: 'Overview' },
133
- { id: 1, label: 'Details' },
134
- { id: 2, label: 'Settings' }
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 iconTabs = ref([
138
- { id: 'home', label: 'Home', icon: null }, // Add icon from @hugeicons/core-free-icons if available
139
- { id: 'profile', label: 'Profile', icon: null }, // Add icon from @hugeicons/core-free-icons if available
140
- { id: 'settings', label: 'Settings', icon: null } // Add icon from @hugeicons/core-free-icons if available
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-example-container {
200
- display: flex;
201
- flex-direction: column;
202
- gap: 2rem;
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(--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
- 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
- <div class="notification show good" role="status">
44
- <strong>GOOD:</strong> Theme tokens are applying to karma backgrounds.
45
- </div>
46
- <div class="notification show bad" role="status">
47
- <strong>BAD:</strong> Check contrast on error surfaces.
48
- </div>
49
- <div class="notification show warning" role="status">
50
- <strong>WARNING:</strong> Accent and border colors should stay readable.
51
- </div>
52
- <p class="fg-success show inline-notification">
53
- <strong>SUCCESS:</strong> Inline notification with foreground karma.
54
- </p>
55
- <p class="fg-warning show inline-notification">
56
- <strong>WARNING:</strong> Another inline status line.
57
- </p>
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: 'fg-good',
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: 'fg-good',
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: 'fg-warning',
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: 'fg-note',
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>