srcdev-nuxt-components 9.1.55 → 9.1.56

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 (100) hide show
  1. package/.claude/settings.json +29 -2
  2. package/.claude/skills/components/alert-content-inner.md +60 -0
  3. package/.claude/skills/components/display-dialog.md +80 -0
  4. package/.claude/skills/components/display-prompt.md +163 -0
  5. package/.claude/skills/components/display-toast.md +235 -0
  6. package/.claude/skills/index.md +7 -3
  7. package/.claude/skills/theming-colour-ramps.md +328 -0
  8. package/.claude/skills/theming-override-default.md +115 -186
  9. package/.claude/skills/theming-partial-override.md +88 -75
  10. package/README.md +166 -0
  11. package/app/app.config.ts +44 -0
  12. package/app/assets/styles/setup/01.config/_head.css +3 -3
  13. package/app/assets/styles/setup/02.colours/_amber.css +12 -10
  14. package/app/assets/styles/setup/02.colours/_blue.css +10 -9
  15. package/app/assets/styles/setup/02.colours/_green.css +12 -11
  16. package/app/assets/styles/setup/02.colours/_orange.css +12 -10
  17. package/app/assets/styles/setup/02.colours/_red.css +10 -9
  18. package/app/assets/styles/setup/02.colours/_slate.css +12 -11
  19. package/app/assets/styles/setup/02.colours/_sunset.css +12 -10
  20. package/app/assets/styles/setup/02.colours/_theme-params.css +18 -0
  21. package/app/assets/styles/setup/02.colours/index.css +1 -0
  22. package/app/assets/styles/setup/03.theming/_default.css +87 -0
  23. package/app/assets/styles/setup/03.theming/_error.css +14 -0
  24. package/app/assets/styles/setup/03.theming/_success.css +4 -0
  25. package/app/assets/styles/setup/03.theming/_theme-slots.css +16 -0
  26. package/app/assets/styles/setup/03.theming/_warning.css +13 -0
  27. package/app/assets/styles/setup/03.theming/index.css +6 -4
  28. package/app/assets/styles/setup/03.theming/theme-ramp.css +19 -0
  29. package/app/assets/styles/setup/04.elements/forms/00.element-defaults.css +0 -7
  30. package/app/assets/styles/setup/04.elements/forms/06.button-geometry.css +1 -1
  31. package/app/assets/styles/setup/index.css +1 -1
  32. package/app/components/01.atoms/display-dialog/DisplayDialog.vue +74 -24
  33. package/app/components/01.atoms/display-dialog/tests/DisplayDialog.spec.ts +148 -14
  34. package/app/components/01.atoms/display-dialog/tests/__snapshots__/DisplayDialog.spec.ts.snap +3 -3
  35. package/app/components/01.atoms/prompt/DisplayPrompt.vue +113 -0
  36. package/app/components/01.atoms/prompt/stories/DisplayPrompt.stories.ts +112 -0
  37. package/app/components/01.atoms/prompt/tests/DisplayPrompt.spec.ts +205 -0
  38. package/app/components/01.atoms/toast/DisplayToast.vue +327 -0
  39. package/app/components/01.atoms/toast/DisplayToastProvider.vue +362 -0
  40. package/app/components/01.atoms/toast/stories/DisplayToast.stories.ts +195 -0
  41. package/app/components/01.atoms/toast/stories/DisplayToastProvider.stories.ts +142 -0
  42. package/app/components/01.atoms/toast/tests/DisplayToast.spec.ts +266 -0
  43. package/app/components/01.atoms/toast/tests/DisplayToastProvider.spec.ts +256 -0
  44. package/app/components/02.molecules/action-menu/ActionMenu.vue +8 -1
  45. package/app/components/02.molecules/action-menu/ActionMenuItemCore.vue +11 -3
  46. package/app/components/02.molecules/alert-content/AlertContent.vue +67 -0
  47. package/app/components/02.molecules/alert-content/AlertContentInner.vue +153 -0
  48. package/app/components/02.molecules/alert-content/stories/AlertContent.stories.ts +131 -0
  49. package/app/components/02.molecules/alert-content/tests/AlertContent.spec.ts +135 -0
  50. package/app/components/02.molecules/alert-content/tests/AlertContentInner.spec.ts +135 -0
  51. package/app/components/02.molecules/alert-masked-content/AlertMaskedContent.vue +78 -0
  52. package/app/components/02.molecules/alert-masked-content/stories/AlertMaskedContent.stories.ts +132 -0
  53. package/app/components/02.molecules/alert-masked-content/tests/AlertMaskedContent.spec.ts +149 -0
  54. package/app/components/05.forms/input-button/InputButtonCore.vue +35 -39
  55. package/app/components/05.forms/input-checkbox-radio/InputCheckboxRadioButton.vue +8 -8
  56. package/app/components/05.forms/input-checkbox-radio/InputCheckboxRadioCore.vue +5 -5
  57. package/app/components/05.forms/input-copy/InputCopyCore.vue +7 -7
  58. package/app/components/05.forms/input-number/InputNumberCore.vue +5 -5
  59. package/app/components/05.forms/input-select/InputSelectCore.vue +6 -6
  60. package/app/components/05.forms/input-text/InputTextCore.vue +6 -6
  61. package/app/components/05.forms/input-text/variants/InputTextAsNumberWithLabel.vue +2 -2
  62. package/app/components/05.forms/input-textarea/InputTextareaCore.vue +5 -5
  63. package/app/components/05.forms/pending-effect/PendingEffect.vue +60 -60
  64. package/app/components/05.forms/toggle-switch/ToggleSwitchCore.vue +10 -10
  65. package/app/components/05.forms/triple-toggle-switch/TripleToggleSwitchCore.vue +4 -4
  66. package/app/components/display-theme-switch/DisplayThemeSwitch.vue +4 -4
  67. package/app/components/display-tooltip/DisplayTooltip.vue +4 -4
  68. package/app/composables/useBodyLock.ts +21 -0
  69. package/app/composables/useDialogControls.ts +11 -11
  70. package/app/composables/useToastQueue.ts +39 -0
  71. package/app/layouts/default.vue +1 -1
  72. package/app/pages/ui/display-dialog.vue +275 -127
  73. package/app/pages/ui/display-prompt.vue +255 -52
  74. package/app/pages/ui/display-toast.vue +278 -299
  75. package/app/pages/ui/mask-element.vue +25 -3
  76. package/app/pages/ui/settings.vue +9 -35
  77. package/app/types/app-config.d.ts +41 -0
  78. package/app/types/components/display-prompt.d.ts +11 -0
  79. package/app/types/components/display-toast.d.ts +11 -9
  80. package/app/types/components/index.ts +2 -0
  81. package/app/types/components/semantic-theme.d.ts +1 -0
  82. package/nuxt.config.ts +16 -0
  83. package/package.json +6 -4
  84. package/app/assets/styles/setup/03.theming/default/_dark.css +0 -156
  85. package/app/assets/styles/setup/03.theming/default/_light.css +0 -166
  86. package/app/assets/styles/setup/03.theming/default/index.css +0 -2
  87. package/app/assets/styles/setup/03.theming/error/_dark.css +0 -56
  88. package/app/assets/styles/setup/03.theming/error/_light.css +0 -74
  89. package/app/assets/styles/setup/03.theming/error/index.css +0 -2
  90. package/app/assets/styles/setup/03.theming/success/_dark.css +0 -50
  91. package/app/assets/styles/setup/03.theming/success/_light.css +0 -65
  92. package/app/assets/styles/setup/03.theming/success/index.css +0 -2
  93. package/app/assets/styles/setup/03.theming/warning/_dark.css +0 -50
  94. package/app/assets/styles/setup/03.theming/warning/_light.css +0 -64
  95. package/app/assets/styles/setup/03.theming/warning/index.css +0 -2
  96. package/app/components/display-prompt/DisplayPromptCore.vue +0 -228
  97. package/app/components/display-prompt/variants/DisplayPromptError.vue +0 -53
  98. package/app/components/display-toast/DisplayToast.vue +0 -447
  99. package/app/components/display-toast/molecules/DefaultToastContent.vue +0 -163
  100. package/app/components/display-toast/stories/DisplayToast.stories.ts +0 -380
@@ -2,157 +2,158 @@
2
2
  <div>
3
3
  <NuxtLayout name="default">
4
4
  <template #layout-content>
5
- <PageRow tag="div" variant="popout" :style-class-passthrough="['mbe-20']">
6
- <h1 class="page-heading-2">Dialog with scrollable content</h1>
7
- <p><button type="button" @click.prevent="openDialog('sample1')">Show Dialog</button></p>
5
+ <PageRow tag="div" variant="content" :style-class-passthrough="['mbe-20']">
6
+ <h1 class="page-heading-2">Display Dialog</h1>
8
7
 
9
- <DisplayDialog
10
- v-if="dialogsConfig['sample1']"
11
- v-model="dialogsConfig['sample1']"
12
- variant="dialog"
13
- :style-class-passthrough="['your-modifier-class']"
14
- :allow-content-scroll="true"
15
- data-dialog-id="sample1"
16
- >
17
- <template #dialogTitle>
18
- <p class="text-normal wght-700 m-0">Dialog with scrollable content</p>
19
- </template>
8
+ <div v-if="isDev" class="qa-panel">
9
+ <details class="qa-panel__details">
10
+ <summary class="qa-panel__summary">
11
+ <span class="qa-panel__title">QA — DisplayDialog</span>
12
+ <code class="qa-panel__status">
13
+ {{ qaVariant }} · {{ qaTheme ?? "no theme" }}
14
+ </code>
15
+ </summary>
16
+ <div class="qa-panel__body">
17
+ <div class="qa-panel__group">
18
+ <span class="qa-panel__label">Variant</span>
19
+ <div class="qa-panel__chips">
20
+ <button
21
+ v-for="opt in variants"
22
+ :key="opt"
23
+ class="qa-panel__chip"
24
+ :class="{ 'is-active': qaVariant === opt }"
25
+ @click="qaVariant = opt"
26
+ >
27
+ {{ opt }}
28
+ </button>
29
+ </div>
30
+ </div>
20
31
 
21
- <template #dialogContent>
22
- <div class="pt-12 pb-12">
23
- <p class="text-normal">
24
- Lorem ipsum odor amet, consectetuer adipiscing elit. Scelerisque tincidunt vestibulum litora torquent
25
- aliquam a. Sem litora tellus mattis nisi vehicula sodales arcu egestas. Consectetur ultrices maximus
26
- pulvinar habitant imperdiet habitasse feugiat. Varius vehicula potenti aliquam mollis efficitur at mus
27
- ridiculus. Nec dis elit euismod posuere etiam fermentum, nisi ante. Pharetra ligula laoreet commodo et
28
- mollis, semper malesuada urna. Id porttitor hendrerit scelerisque justo ridiculus sagittis. Adipiscing
29
- pellentesque eget metus orci; potenti natoque habitasse. Adipiscing leo parturient dis nullam
30
- condimentum id.
31
- </p>
32
- <p class="text-normal">
33
- Justo molestie felis tellus tellus taciti? Ullamcorper viverra quis felis donec aliquam torquent
34
- imperdiet. Curabitur vitae quis malesuada imperdiet hendrerit felis quam dictum. Aptent curabitur
35
- lacus dapibus; nunc enim vestibulum finibus. Lorem commodo tortor blandit nisl curabitur interdum
36
- ultricies magnis. Dui vehicula ex; congue sagittis commodo tincidunt. Varius sociosqu finibus facilisi
37
- per, vel quisque. Ornare fames cubilia diam maximus habitant nulla nisl. Sollicitudin cubilia inceptos
38
- taciti hac tristique mauris hendrerit vestibulum.
39
- </p>
40
- <p class="text-normal">
41
- Lobortis efficitur enim litora dictum montes. Sagittis eget etiam curae suspendisse cubilia. Ante
42
- aliquam orci mus ultricies nostra. Ad augue quam egestas ipsum sem lacus tempus. Magnis enim posuere
43
- eros montes fermentum velit. Montes lacinia nisl facilisi volutpat vehicula porttitor auctor
44
- condimentum venenatis. Semper varius nascetur imperdiet laoreet vivamus montes praesent purus nostra.
45
- Magna nascetur augue curae proin nascetur sagittis urna adipiscing.
46
- </p>
47
- <p class="text-normal">
48
- Venenatis adipiscing integer eget donec ridiculus risus. Nulla quis sollicitudin sem nam bibendum
49
- ligula. Curabitur malesuada platea egestas venenatis in torquent. Eget eros euismod vehicula consequat
50
- at, efficitur ullamcorper. Donec nibh molestie semper nulla vehicula. Vulputate porta euismod cras;
51
- inceptos vel erat. Vehicula urna mattis suscipit iaculis nostra eget. Eleifend vivamus nullam porta
52
- urna nulla dictum praesent aenean. Etiam ipsum placerat bibendum nullam rhoncus egestas. Mollis
53
- pretium nullam nullam donec eget imperdiet.
54
- </p>
55
- <p class="text-normal">
56
- Curae maximus nam tortor porta sodales at, feugiat iaculis integer. Lacus habitasse odio fames natoque
57
- neque varius nostra. Imperdiet tellus libero ornare lobortis maximus rutrum parturient litora viverra.
58
- Praesent proin sem nulla mauris justo mi morbi. Neque diam sodales nam nascetur commodo risus. Ornare
59
- at sit, mattis erat aenean ipsum. Dictum integer ipsum suspendisse sagittis nisl sollicitudin.
60
- </p>
32
+ <div class="qa-panel__group">
33
+ <span class="qa-panel__label">Theme</span>
34
+ <div class="qa-panel__chips">
35
+ <button
36
+ v-for="opt in themes"
37
+ :key="String(opt)"
38
+ class="qa-panel__chip"
39
+ :class="{ 'is-active': qaTheme === opt }"
40
+ @click="qaTheme = opt"
41
+ >
42
+ {{ opt ?? "none" }}
43
+ </button>
44
+ </div>
45
+ </div>
61
46
  </div>
62
- </template>
63
- <template #actionButtonLeft>
64
- <button type="submit" @click.prevent="closeDialog('sample1', 'cancel')">Cancel</button>
65
- </template>
66
- <template #actionButtonRight>
67
- <button type="submit" @click.prevent="closeDialog('sample1', 'confirm')">Confirm</button>
68
- </template>
69
- </DisplayDialog>
70
- </PageRow>
47
+ </details>
48
+ </div>
71
49
 
72
- <PageRow tag="div" variant="popout" :style-class-passthrough="['mbe-20']">
73
- <h2 class="page-heading-2">Confirm Dialog</h2>
74
- <p><button type="button" @click.prevent="openDialog('logout')">Show Confirm Dialog</button></p>
50
+ <p>
51
+ <button type="button" @click.prevent="openDialog('demo')">Open Dialog</button>
52
+ </p>
75
53
 
76
54
  <DisplayDialog
77
- v-if="dialogsConfig['logout']"
78
- v-model="dialogsConfig['logout']"
79
- variant="confirm"
80
- :style-class-passthrough="['your-modifier-class']"
81
- data-dialog-id="logout"
55
+ v-if="dialogsConfig['demo']"
56
+ v-model="dialogsConfig['demo']"
57
+ :variant="qaVariant"
58
+ :theme="qaTheme"
59
+ :allow-content-scroll="qaVariant === 'dialog'"
60
+ data-dialog-id="demo"
82
61
  >
83
62
  <template #dialogTitle>
84
- <p class="text-normal wght-700 m-0">Confirm logout?</p>
63
+ <p class="text-normal wght-700 m-0">{{ dialogTitle }}</p>
85
64
  </template>
86
65
 
87
66
  <template #dialogContent>
88
- <div class="m-0">
89
- <p class="text-normal">Are you sure you wish to log out?</p>
67
+ <div class="pt-12 pb-12">
68
+ <p class="text-normal">{{ dialogBody }}</p>
69
+ <template v-if="qaVariant === 'dialog'">
70
+ <p class="text-normal">
71
+ Justo molestie felis tellus tellus taciti? Ullamcorper viverra quis felis donec aliquam torquent
72
+ imperdiet. Curabitur vitae quis malesuada imperdiet hendrerit felis quam dictum. Aptent curabitur
73
+ lacus dapibus; nunc enim vestibulum finibus. Lorem commodo tortor blandit nisl curabitur interdum
74
+ ultricies magnis.
75
+ </p>
76
+ <p class="text-normal">
77
+ Lobortis efficitur enim litora dictum montes. Sagittis eget etiam curae suspendisse cubilia. Ante
78
+ aliquam orci mus ultricies nostra. Ad augue quam egestas ipsum sem lacus tempus. Magnis enim posuere
79
+ eros montes fermentum velit.
80
+ </p>
81
+ <p class="text-normal">
82
+ Venenatis adipiscing integer eget donec ridiculus risus. Nulla quis sollicitudin sem nam bibendum
83
+ ligula. Curabitur malesuada platea egestas venenatis in torquent. Eget eros euismod vehicula consequat
84
+ at, efficitur ullamcorper.
85
+ </p>
86
+ </template>
90
87
  </div>
91
88
  </template>
89
+
92
90
  <template #actionButtonLeft>
93
- <button type="submit" @click.prevent="closeDialog('logout', 'cancel')">Cancel</button>
91
+ <button type="button" @click.prevent="closeDialog('demo', 'cancel')">Cancel</button>
94
92
  </template>
95
93
  <template #actionButtonRight>
96
- <button type="submit" @click.prevent="closeDialog('logout', 'confirm')">Confirm</button>
94
+ <button type="button" @click.prevent="closeDialog('demo', 'confirm')">{{ confirmLabel }}</button>
97
95
  </template>
98
96
  </DisplayDialog>
99
97
  </PageRow>
100
98
 
101
- <PageRow tag="div" variant="popout" :style-class-passthrough="['mbe-20']">
102
- <h2 class="page-heading-2">Alert Dialog</h2>
103
- <p class="text-normal">Cannot be dismissed by clicking outside or pressing Escape — requires explicit action.</p>
104
- <p><button type="button" @click.prevent="openDialog('deleteAccount')">Show Alert Dialog</button></p>
99
+ <PageRow tag="div" variant="content" :style-class-passthrough="['mbe-20']">
100
+ <h2 class="page-heading-2">Nested dialog pattern</h2>
101
+ <p class="text-normal">
102
+ A dialog managing its own internal confirm sub-dialog via a local
103
+ <code>useDialogControls</code> instance. Tick "Simulate unsaved changes" before
104
+ clicking Cancel to trigger the nested confirm.
105
+ </p>
106
+ <p>
107
+ <button type="button" @click.prevent="openNestedDialog('editProfile')">Open nested dialog demo</button>
108
+ </p>
105
109
 
106
110
  <DisplayDialog
107
- v-if="dialogsConfig['deleteAccount']"
108
- v-model="dialogsConfig['deleteAccount']"
109
- variant="alert"
110
- :style-class-passthrough="['your-modifier-class']"
111
- data-dialog-id="deleteAccount"
111
+ v-if="nestedDialogsConfig['editProfile']"
112
+ v-model="nestedDialogsConfig['editProfile']"
113
+ variant="dialog"
114
+ data-dialog-id="nested-edit"
112
115
  >
113
116
  <template #dialogTitle>
114
- <p class="text-normal wght-700 m-0">Delete account?</p>
117
+ <p class="text-normal wght-700 m-0">Edit profile</p>
115
118
  </template>
116
-
117
119
  <template #dialogContent>
118
- <div class="m-0">
119
- <p class="text-normal">This action is permanent and cannot be undone. All your data will be removed.</p>
120
+ <div class="pt-12 pb-12 nested-demo-content">
121
+ <p class="text-normal">Make some changes below, then click Cancel to trigger the nested confirm dialog.</p>
122
+ <label class="nested-demo-label">
123
+ <input v-model="nestedHasChanges" type="checkbox" />
124
+ Simulate unsaved changes
125
+ </label>
120
126
  </div>
121
127
  </template>
122
128
  <template #actionButtonLeft>
123
- <button type="button" @click.prevent="closeDialog('deleteAccount', 'cancel')">Cancel</button>
129
+ <button type="button" @click="handleNestedCancel">Cancel</button>
124
130
  </template>
125
131
  <template #actionButtonRight>
126
- <button type="button" @click.prevent="closeDialog('deleteAccount', 'confirm')">Delete account</button>
132
+ <button type="button" @click="closeNestedDialog('editProfile', 'confirm')">Save</button>
127
133
  </template>
128
134
  </DisplayDialog>
129
- </PageRow>
130
-
131
- <PageRow tag="div" variant="popout" :style-class-passthrough="['mbe-20']">
132
- <h2 class="page-heading-2">Fullscreen Dialog</h2>
133
- <p><button type="button" @click.prevent="openDialog('fullscreen')">Show Fullscreen Dialog</button></p>
134
135
 
135
136
  <DisplayDialog
136
- v-if="dialogsConfig['fullscreen']"
137
- v-model="dialogsConfig['fullscreen']"
138
- variant="fullscreen"
139
- :style-class-passthrough="['your-modifier-class']"
140
- data-dialog-id="fullscreen"
137
+ v-if="nestedDialogsConfig['confirmDiscard']"
138
+ v-model="nestedDialogsConfig['confirmDiscard']"
139
+ variant="confirm"
140
+ theme="warning"
141
+ :lock-viewport="false"
142
+ data-dialog-id="nested-confirm"
141
143
  >
142
144
  <template #dialogTitle>
143
- <p class="text-normal wght-700 m-0">Fullscreen dialog</p>
145
+ <p class="text-normal wght-700 m-0">Discard changes?</p>
144
146
  </template>
145
-
146
147
  <template #dialogContent>
147
- <div class="pt-12 pb-12">
148
- <p class="text-normal">This dialog fills the entire viewport. Useful for immersive flows such as onboarding, image editors, or multi-step wizards.</p>
148
+ <div class="m-0">
149
+ <p class="text-normal">Your unsaved changes will be lost.</p>
149
150
  </div>
150
151
  </template>
151
152
  <template #actionButtonLeft>
152
- <button type="button" @click.prevent="closeDialog('fullscreen', 'cancel')">Cancel</button>
153
+ <button type="button" @click="closeNestedDialog('confirmDiscard', 'cancel')">Keep editing</button>
153
154
  </template>
154
155
  <template #actionButtonRight>
155
- <button type="button" @click.prevent="closeDialog('fullscreen', 'confirm')">Done</button>
156
+ <button type="button" @click="closeNestedDialog('confirmDiscard', 'confirm')">Discard</button>
156
157
  </template>
157
158
  </DisplayDialog>
158
159
  </PageRow>
@@ -162,35 +163,182 @@
162
163
  </template>
163
164
 
164
165
  <script setup lang="ts">
165
- definePageMeta({
166
- layout: false,
167
- });
166
+ import type { SemanticTheme } from "~/types/components";
167
+
168
+ definePageMeta({ layout: false });
168
169
 
169
170
  useHead({
170
171
  title: "UI Dialogs",
171
172
  meta: [{ name: "description", content: "Examples of Dialogs" }],
172
- bodyAttrs: {
173
- class: "home",
174
- },
173
+ bodyAttrs: { class: "displayDialog-page" },
174
+ });
175
+
176
+ const isDev = import.meta.dev;
177
+
178
+ const variants = ["dialog", "modal", "confirm", "alert", "fullscreen"] as const;
179
+ type DialogVariant = (typeof variants)[number];
180
+
181
+ const themes = [undefined, "info", "success", "warning", "error"] as const;
182
+
183
+ const qaVariant = ref<DialogVariant>("confirm");
184
+ const qaTheme = ref<SemanticTheme | undefined>(undefined);
185
+
186
+ const dialogTitle = computed(() => {
187
+ if (qaTheme.value === "error") return "Delete account?";
188
+ if (qaTheme.value === "warning") return "Confirm action";
189
+ if (qaVariant.value === "dialog") return "Dialog with scrollable content";
190
+ if (qaVariant.value === "confirm") return "Confirm logout?";
191
+ if (qaVariant.value === "alert") return "Alert — action required";
192
+ if (qaVariant.value === "fullscreen") return "Fullscreen dialog";
193
+ return "Dialog";
194
+ });
195
+
196
+ const dialogBody = computed(() => {
197
+ if (qaTheme.value === "error")
198
+ return "This action is permanent and cannot be undone. All your data will be removed.";
199
+ if (qaVariant.value === "alert")
200
+ return "Cannot be dismissed by clicking outside or pressing Escape — an explicit action is required.";
201
+ if (qaVariant.value === "fullscreen")
202
+ return "This dialog fills the entire viewport. Useful for immersive flows such as onboarding, image editors, or multi-step wizards.";
203
+ if (qaVariant.value === "dialog")
204
+ return "Lorem ipsum odor amet, consectetuer adipiscing elit. Scelerisque tincidunt vestibulum litora torquent aliquam a. Sem litora tellus mattis nisi vehicula sodales arcu egestas.";
205
+ return "Are you sure you want to continue? This will apply the selected action.";
206
+ });
207
+
208
+ const confirmLabel = computed(() => {
209
+ if (qaTheme.value === "error") return "Delete account";
210
+ if (qaVariant.value === "confirm") return "Log out";
211
+ return "Confirm";
175
212
  });
176
213
 
177
214
  const { dialogsConfig, openDialog, closeDialog } = useDialogControls({
178
- sample1: {
179
- onConfirm: () => console.log("Sample1 dialog confirmed"),
180
- onCancel: () => console.log("Sample1 dialog cancelled"),
181
- },
182
- logout: {
183
- onConfirm: () => console.log("User logged out"),
184
- onCancel: () => console.log("Logout cancelled"),
185
- },
186
- deleteAccount: {
187
- onConfirm: () => console.log("Account deleted"),
188
- onCancel: () => console.log("Delete account cancelled"),
215
+ demo: {
216
+ onConfirm: () => {},
217
+ onCancel: () => {},
189
218
  },
190
- fullscreen: {
191
- onConfirm: () => console.log("Fullscreen dialog confirmed"),
192
- onCancel: () => console.log("Fullscreen dialog cancelled"),
219
+ });
220
+
221
+ const nestedHasChanges = ref(false);
222
+
223
+ const { dialogsConfig: nestedDialogsConfig, openDialog: openNestedDialog, closeDialog: closeNestedDialog } = useDialogControls({
224
+ editProfile: { onConfirm: () => {}, onCancel: () => {} },
225
+ confirmDiscard: {
226
+ onConfirm: () => { nestedDialogsConfig.editProfile = false; },
227
+ onCancel: () => {},
193
228
  },
194
229
  });
230
+
231
+ const handleNestedCancel = () => {
232
+ if (nestedHasChanges.value) {
233
+ openNestedDialog("confirmDiscard");
234
+ } else {
235
+ closeNestedDialog("editProfile", "cancel");
236
+ }
237
+ };
195
238
  </script>
196
239
 
240
+ <style lang="css">
241
+ .displayDialog-page {
242
+ .qa-panel {
243
+ background: oklch(15% 0 0);
244
+ color: white;
245
+ font-size: 1.3rem;
246
+ margin-block: 1.2rem;
247
+ }
248
+
249
+ .qa-panel__details {
250
+ padding: 1rem 2rem;
251
+ }
252
+
253
+ .qa-panel__summary {
254
+ cursor: pointer;
255
+ display: flex;
256
+ align-items: center;
257
+ gap: 1.6rem;
258
+ list-style: none;
259
+ user-select: none;
260
+
261
+ &::-webkit-details-marker {
262
+ display: none;
263
+ }
264
+ }
265
+
266
+ .qa-panel__title {
267
+ font-weight: 600;
268
+ font-size: 1.1rem;
269
+ text-transform: uppercase;
270
+ letter-spacing: 0.08em;
271
+ }
272
+
273
+ .qa-panel__status {
274
+ font-family: monospace;
275
+ font-size: 1.2rem;
276
+ background: oklch(0% 0 0 / 0.3);
277
+ padding: 0.2rem 0.8rem;
278
+ border-radius: 0.4rem;
279
+ user-select: text;
280
+ cursor: text;
281
+ }
282
+
283
+ .qa-panel__body {
284
+ display: flex;
285
+ flex-wrap: wrap;
286
+ gap: 2.4rem;
287
+ padding-block: 1.2rem 0.4rem;
288
+ }
289
+
290
+ .qa-panel__group {
291
+ display: flex;
292
+ flex-direction: column;
293
+ gap: 0.6rem;
294
+ }
295
+
296
+ .qa-panel__label {
297
+ font-size: 1.1rem;
298
+ text-transform: uppercase;
299
+ letter-spacing: 0.08em;
300
+ opacity: 0.55;
301
+ }
302
+
303
+ .qa-panel__chips {
304
+ display: flex;
305
+ flex-wrap: wrap;
306
+ gap: 0.4rem;
307
+ }
308
+
309
+ .nested-demo-content {
310
+ display: flex;
311
+ flex-direction: column;
312
+ gap: 1.6rem;
313
+ }
314
+
315
+ .nested-demo-label {
316
+ display: flex;
317
+ align-items: center;
318
+ gap: 0.8rem;
319
+ cursor: pointer;
320
+ font-size: 1.4rem;
321
+ }
322
+
323
+ .qa-panel__chip {
324
+ font-family: monospace;
325
+ font-size: 1.2rem;
326
+ color: white;
327
+ background: oklch(0% 0 0 / 0.25);
328
+ border: 1px solid oklch(100% 0 0 / 0.18);
329
+ padding: 0.3rem 1rem;
330
+ border-radius: 0.4rem;
331
+ cursor: pointer;
332
+ transition: background 0.15s;
333
+
334
+ &:hover {
335
+ background: oklch(0% 0 0 / 0.4);
336
+ }
337
+
338
+ &.is-active {
339
+ background: oklch(55% 0.18 240);
340
+ border-color: oklch(55% 0.18 240);
341
+ }
342
+ }
343
+ }
344
+ </style>