srcdev-nuxt-components 6.1.39 → 6.1.41

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.
@@ -3,6 +3,8 @@
3
3
  class="display-prompt-core"
4
4
  :class="[{ closed: !compopnentOpen }]"
5
5
  :data-test-id="`display-prompt-core-${theme}`"
6
+ ref="promptElementRef"
7
+ tabindex="0"
6
8
  >
7
9
  <div class="display-prompt-wrapper" :data-theme="theme" :class="[elementClasses]" data-test-id="display-prompt">
8
10
  <div class="display-prompt-inner">
@@ -11,7 +13,7 @@
11
13
  <Icon :name="displayPromptIcons[theme] ?? 'akar-icons:circle-alert'" class="icon" :color="iconColor" />
12
14
  </slot>
13
15
  </div>
14
- <div class="display-prompt-content">
16
+ <div class="display-prompt-content" :aria-live="useAutoFocus ? 'polite' : undefined">
15
17
  <p class="title" data-test-id="display-prompt-title">
16
18
  <slot name="title"></slot>
17
19
  </p>
@@ -65,10 +67,15 @@ const props = defineProps({
65
67
  secondary: "akar-icons:info",
66
68
  }),
67
69
  },
70
+ useAutoFocus: {
71
+ type: Boolean,
72
+ default: false,
73
+ },
68
74
  })
69
75
 
70
76
  const slots = useSlots()
71
- const parentComponentState = defineModel<boolean>("parentComponentState", { default: false })
77
+ const promptElementRef = useTemplateRef<HTMLElement>("promptElementRef")
78
+ const parentComponentState = defineModel<boolean>({ default: false })
72
79
  const compopnentOpen = ref(true)
73
80
  const { elementClasses } = useStyleClassPassthrough(props.styleClassPassthrough)
74
81
 
@@ -80,6 +87,12 @@ const updateComponentState = () => {
80
87
 
81
88
  compopnentOpen.value = false
82
89
  }
90
+
91
+ onMounted(async () => {
92
+ if (props.useAutoFocus && promptElementRef.value) {
93
+ promptElementRef.value.focus()
94
+ }
95
+ })
83
96
  </script>
84
97
 
85
98
  <style lang="css">
@@ -2,7 +2,7 @@
2
2
  <Teleport to="body">
3
3
  <div
4
4
  v-if="privateDisplayToast"
5
- ref="toastElement"
5
+ ref="toastElementRef"
6
6
  class="display-toast"
7
7
  :class="[
8
8
  elementClasses,
@@ -28,7 +28,7 @@
28
28
  </slot>
29
29
  </div>
30
30
  <div class="toast-message" :id="'toast-message-' + toastId">{{ toastDisplayText }}</div>
31
- <div class="toast-action">
31
+ <div v-if="!autoDismiss" class="toast-action">
32
32
  <button @click.prevent="setDismissToast()">
33
33
  <Icon name="material-symbols:close" class="icon" />
34
34
  <span class="sr-only">Close</span>
@@ -176,7 +176,7 @@ const positionClasses = computed(() => {
176
176
  * Accessibility setup
177
177
  */
178
178
  const toastId = useId()
179
- const toastElement = ref<HTMLElement>()
179
+ const toastElementRef = useTemplateRef<HTMLElement>("toastElementRef")
180
180
 
181
181
  // Determine appropriate ARIA attributes based on theme
182
182
  const toastRole = computed(() => {
@@ -232,7 +232,7 @@ watch(
232
232
  await nextTick()
233
233
  // Wait for animation to start before focusing
234
234
  setTimeout(() => {
235
- toastElement.value?.focus()
235
+ toastElementRef.value?.focus()
236
236
  }, 100)
237
237
  }
238
238
 
@@ -240,33 +240,48 @@ watch(
240
240
  await useSleep(duration.value)
241
241
  setDismissToast()
242
242
  }
243
+ } else if (!newValue && previousValue) {
244
+ // If external model is set to false, dismiss the toast
245
+ setDismissToast()
243
246
  }
244
247
  }
245
248
  )
249
+
250
+ onBeforeRouteLeave(() => {
251
+ setDismissToast()
252
+ })
246
253
  </script>
247
254
 
248
255
  <style lang="css">
249
256
  @keyframes show {
250
257
  to {
251
258
  opacity: 1;
252
- /* visibility: visible; */
253
259
  transform: translateY(0);
254
260
  }
255
261
  }
256
262
 
257
- @keyframes hide {
263
+ @keyframes hideTop {
258
264
  0% {
259
265
  opacity: 1;
260
- /* visibility: visible; */
261
266
  transform: translateY(0);
262
267
  }
263
268
  100% {
264
269
  opacity: 0;
265
- /* visibility: hidden; */
266
270
  transform: translateY(-30px);
267
271
  }
268
272
  }
269
273
 
274
+ @keyframes hideBottom {
275
+ 0% {
276
+ opacity: 1;
277
+ transform: translateY(0);
278
+ }
279
+ 100% {
280
+ opacity: 0;
281
+ transform: translateY(30px);
282
+ }
283
+ }
284
+
270
285
  @keyframes progress {
271
286
  to {
272
287
  transform: scaleX(1);
@@ -274,12 +289,16 @@ watch(
274
289
  }
275
290
 
276
291
  .display-toast {
292
+ --_toast-gutter: 12px;
293
+ @media (width >= 600px) {
294
+ --_toast-gutter: 24px;
295
+ }
296
+
277
297
  display: block;
278
298
  overflow: hidden;
279
299
  position: fixed;
280
300
  margin: 0;
281
301
  opacity: 0;
282
- /* visibility: hidden; */
283
302
 
284
303
  z-index: 100;
285
304
 
@@ -299,52 +318,67 @@ watch(
299
318
  }
300
319
 
301
320
  @supports not (animation-timing-function: linear(0, 1)) {
302
- animation: show v-bind(revealDurationMs) linear forwards;
321
+ animation: show calc(v-bind(revealDurationMs) / 2) linear forwards;
303
322
  }
304
323
  }
305
324
 
306
325
  &.hide {
307
326
  @supports (animation-timing-function: linear(0, 1)) {
308
- animation: hide v-bind(revealDurationMs) var(--spring-easing) forwards;
327
+ animation: hideTop v-bind(revealDurationMs) var(--spring-easing) forwards;
309
328
  }
310
329
 
311
330
  @supports not (animation-timing-function: linear(0, 1)) {
312
- animation: hide v-bind(revealDurationMs) linear forwards;
331
+ animation: hideTop calc(v-bind(revealDurationMs) / 2) linear forwards;
313
332
  }
314
- }
315
333
 
316
- &.full-width {
317
- left: 24px;
318
- right: 24px;
334
+ &.bottom {
335
+ @supports (animation-timing-function: linear(0, 1)) {
336
+ animation: hideBottom v-bind(revealDurationMs) var(--spring-easing) forwards;
337
+ }
338
+
339
+ @supports not (animation-timing-function: linear(0, 1)) {
340
+ animation: hideBottom calc(v-bind(revealDurationMs) / 2) linear forwards;
341
+ }
342
+ }
319
343
  }
320
344
 
321
- &:not(.full-width) {
345
+ /*
346
+ * Default is centre for smaller screens
347
+ */
348
+ inset-inline: var(--_toast-gutter);
349
+ margin-inline: auto;
350
+
351
+ @media (width >= 600px) {
322
352
  &.left {
323
- left: 24px;
353
+ inset-inline-start: var(--_toast-gutter);
354
+ inset-inline-end: unset;
324
355
  }
325
356
 
326
357
  &.right {
327
- right: 24px;
358
+ inset-inline-end: var(--_toast-gutter);
359
+ inset-inline-start: unset;
328
360
  }
329
361
 
330
362
  &.center {
331
- inset-inline: 0;
332
- margin-inline: auto;
333
- width: max-content;
363
+ &:not(.full-width) {
364
+ inset-inline: 0;
365
+ margin-inline: auto;
366
+ width: max-content;
367
+ }
334
368
  }
335
369
  }
336
370
 
337
371
  &.top {
338
- top: 24px;
372
+ inset-block-start: var(--_toast-gutter);
339
373
  transform: translateY(-30px);
340
374
  }
341
375
  &.bottom {
342
- bottom: 24px;
376
+ inset-block-end: var(--_toast-gutter);
343
377
  transform: translateY(30px);
344
378
  }
345
379
 
346
380
  /*
347
- * Styles for the display toast component
381
+ * Styles for the display toast component if slot is empty
348
382
  */
349
383
  &.has-theme {
350
384
  padding-inline-start: 6px;
@@ -440,9 +474,8 @@ watch(
440
474
 
441
475
  .display-toast-progress {
442
476
  position: absolute;
443
- right: 8px;
444
- bottom: 4px;
445
- width: calc(100% - 16px);
477
+ inset-block-end: 4px;
478
+ inset-inline: 15px 8px;
446
479
  height: 3px;
447
480
  transform: scaleX(0);
448
481
  transform-origin: right;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "srcdev-nuxt-components",
3
3
  "type": "module",
4
- "version": "6.1.39",
4
+ "version": "6.1.41",
5
5
  "main": "nuxt.config.ts",
6
6
  "scripts": {
7
7
  "clean": "rm -rf .nuxt && rm -rf .output && rm -rf .playground/.nuxt && rm -rf .playground/.output",