tide-design-system 2.4.5 → 2.4.6

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 (45) hide show
  1. package/dist/css/reset.css +1 -1
  2. package/dist/css/utilities-responsive.css +0 -546
  3. package/dist/style.css +1 -1
  4. package/dist/tide-design-system.cjs +2 -2
  5. package/dist/tide-design-system.esm.d.ts +19 -6
  6. package/dist/tide-design-system.esm.js +1201 -1170
  7. package/dist/utilities/storybook.ts +6 -2
  8. package/dist/utilities/validation.ts +1 -1
  9. package/index.ts +4 -4
  10. package/package.json +1 -1
  11. package/src/assets/css/reset.css +1 -1
  12. package/src/assets/css/utilities-responsive.css +0 -546
  13. package/src/components/TideButton.vue +14 -4
  14. package/src/components/TideButtonIcon.vue +12 -2
  15. package/src/components/TideButtonPagination.vue +11 -16
  16. package/src/components/TideButtonSegmented.vue +1 -0
  17. package/src/components/TideCard.vue +11 -2
  18. package/src/components/TideCarousel.vue +9 -4
  19. package/src/components/TideChipAction.vue +1 -0
  20. package/src/components/TideChipFilter.vue +1 -0
  21. package/src/components/TideChipInput.vue +1 -0
  22. package/src/components/TideIcon.vue +1 -1
  23. package/src/components/TideImage.vue +9 -9
  24. package/src/components/TideInputText.vue +2 -0
  25. package/src/components/TideInputTextDeprecated.vue +2 -0
  26. package/src/components/TideInputTextarea.vue +2 -2
  27. package/src/components/TideLink.vue +2 -1
  28. package/src/components/TideModal.vue +91 -85
  29. package/src/components/TideSwitch.vue +1 -0
  30. package/src/stories/TideButtonPagination.stories.ts +6 -6
  31. package/src/stories/TideCarousel.stories.ts +0 -1
  32. package/src/stories/TideInputCheckbox.stories.ts +58 -23
  33. package/src/stories/TideInputRadio.stories.ts +39 -30
  34. package/src/stories/TideInputSelect.stories.ts +51 -27
  35. package/src/stories/TideInputText.stories.ts +83 -23
  36. package/src/stories/TideInputTextarea.stories.ts +66 -17
  37. package/src/stories/TideLink.stories.ts +1 -14
  38. package/src/stories/TidePagination.stories.ts +2 -2
  39. package/src/stories/TidePopover.stories.ts +1 -1
  40. package/src/types/Badge.ts +4 -0
  41. package/src/types/Element.ts +2 -2
  42. package/src/types/Storybook.ts +4 -6
  43. package/src/types/Type.ts +6 -0
  44. package/src/utilities/storybook.ts +6 -2
  45. package/src/utilities/validation.ts +1 -1
@@ -1,4 +1,6 @@
1
1
  <script lang="ts" setup>
2
+ import { computed } from 'vue';
3
+
2
4
  import TideIcon from '@/components/TideIcon.vue';
3
5
  import { ELEMENT } from '@/types/Element';
4
6
  import { PRIORITY } from '@/types/Priority';
@@ -10,6 +12,7 @@
10
12
  import type { Icon } from '@/types/Icon';
11
13
  import type { Priority } from '@/types/Priority';
12
14
  import type { SizeButton } from '@/types/Size';
15
+ import type { ButtonType } from '@/types/Type';
13
16
 
14
17
  type Props = {
15
18
  disabled?: boolean;
@@ -21,6 +24,7 @@
21
24
  label: string;
22
25
  priority?: Priority;
23
26
  size?: SizeButton;
27
+ type?: ButtonType;
24
28
  };
25
29
 
26
30
  const props = withDefaults(defineProps<Props>(), {
@@ -33,7 +37,12 @@
33
37
  label: undefined,
34
38
  priority: PRIORITY.PRIMARY,
35
39
  size: SIZE_BUTTON.LARGE,
40
+ type: undefined,
36
41
  });
42
+
43
+ const typeAttribute = computed<string | undefined>(() =>
44
+ props.element === ELEMENT.BUTTON ? props.type ?? ELEMENT.BUTTON : undefined
45
+ );
37
46
  </script>
38
47
 
39
48
  <template>
@@ -43,18 +52,19 @@
43
52
  props.priority && `tide-button-${props.priority}`,
44
53
  props.element === ELEMENT.LINK ? [CSS.DISPLAY.INLINE_FLEX] : [CSS.DISPLAY.FLEX],
45
54
  props.element === ELEMENT.LINK ? CSS.UNDERLINE.REST.OFF : '',
46
- size === SIZE_BUTTON.SMALL && [CSS.FONT.ROLE.BUTTON_2, CSS.PADDING.X.ONE, CSS.PADDING.Y.HALF],
47
- size === SIZE_BUTTON.LARGE && [CSS.FONT.ROLE.BUTTON_1, CSS.PADDING.X.TWO, CSS.PADDING.Y.ONE],
55
+ size === SIZE_BUTTON.SMALL
56
+ ? [CSS.FONT.ROLE.BUTTON_2, CSS.PADDING.X.ONE, CSS.PADDING.Y.HALF, CSS.BORDER.RADIUS.QUARTER]
57
+ : [CSS.FONT.ROLE.BUTTON_1, CSS.PADDING.X.TWO, CSS.PADDING.Y.ONE, CSS.BORDER.RADIUS.HALF],
48
58
  CSS.AXIS1.CENTER,
49
59
  CSS.AXIS2.CENTER,
50
60
  CSS.GAP.HALF,
51
- CSS.BORDER.RADIUS.HALF,
52
61
  CSS.FONT.ROLE.LABEL_1_SEMIBOLD,
53
62
  ]"
54
63
  :disabled="props.element === ELEMENT.BUTTON && props.disabled"
55
64
  :href="props.element === ELEMENT.LINK && props.href ? props.href : undefined"
56
65
  :target="props.element === ELEMENT.LINK && props.isNewTab ? TARGET.BLANK : TARGET.SELF"
57
- :is="props.element === ELEMENT.LINK ? ELEMENT.LINK : ELEMENT.BUTTON"
66
+ :type="typeAttribute"
67
+ :is="element"
58
68
  >
59
69
  <TideIcon
60
70
  :icon="props.iconLeading"
@@ -1,4 +1,6 @@
1
1
  <script lang="ts" setup>
2
+ import { computed } from 'vue';
3
+
2
4
  import TideIcon from '@/components/TideIcon.vue';
3
5
  import { ELEMENT } from '@/types/Element';
4
6
  import { PRIORITY } from '@/types/Priority';
@@ -9,6 +11,7 @@
9
11
  import type { Element } from '@/types/Element';
10
12
  import type { Icon } from '@/types/Icon';
11
13
  import type { Priority } from '@/types/Priority';
14
+ import type { ButtonType } from '@/types/Type';
12
15
 
13
16
  type Props = {
14
17
  disabled?: boolean;
@@ -17,6 +20,7 @@
17
20
  icon: Icon;
18
21
  isNewTab?: boolean;
19
22
  priority?: Priority;
23
+ type?: ButtonType;
20
24
  };
21
25
 
22
26
  const props = withDefaults(defineProps<Props>(), {
@@ -26,14 +30,19 @@
26
30
  icon: undefined,
27
31
  isNewTab: false,
28
32
  priority: PRIORITY.PRIMARY,
33
+ type: undefined,
29
34
  });
35
+
36
+ const typeAttribute = computed<string | undefined>(() =>
37
+ props.element === ELEMENT.BUTTON ? props.type ?? ELEMENT.BUTTON : undefined
38
+ );
30
39
  </script>
31
40
 
32
41
  <template>
33
42
  <component
34
43
  :aria-label="props.icon"
35
44
  :class="[
36
- props.element === ELEMENT.LINK ? 'tide-link-as-button-icon' : 'tide-button-icon',
45
+ 'tide-button-icon',
37
46
  props.priority && `tide-button-${props.priority}`,
38
47
  [CSS.DISPLAY.FLEX, CSS.AXIS1.CENTER, CSS.AXIS2.CENTER, CSS.BORDER.RADIUS.FULL, CSS.PADDING.FULL.HALF],
39
48
  props.element === ELEMENT.LINK ? [CSS.UNDERLINE.REST.OFF] : '',
@@ -41,7 +50,8 @@
41
50
  :disabled="props.element === ELEMENT.BUTTON && props.disabled"
42
51
  :href="props.element === ELEMENT.LINK && props.href ? props.href : undefined"
43
52
  :target="props.element === ELEMENT.LINK && props.isNewTab ? TARGET.BLANK : TARGET.SELF"
44
- :is="props.element === ELEMENT.LINK ? 'a' : 'button'"
53
+ :type="typeAttribute"
54
+ :is="element"
45
55
  >
46
56
  <TideIcon
47
57
  :icon="props.icon"
@@ -1,19 +1,19 @@
1
1
  <script lang="ts" setup>
2
- import { ELEMENT_TEXT_AS_ICON } from '@/types/Element';
2
+ import { ELEMENT_BROAD } from '@/types/Element';
3
3
  import { CSS } from '@/types/Styles';
4
4
 
5
- import type { ElementTextAsIcon } from '@/types/Element';
5
+ import type { ElementBroad } from '@/types/Element';
6
6
 
7
7
  type Props = {
8
8
  disabled?: boolean;
9
- element?: ElementTextAsIcon;
9
+ element?: ElementBroad;
10
10
  href?: string;
11
11
  label: string | number;
12
12
  };
13
13
 
14
14
  const props = withDefaults(defineProps<Props>(), {
15
15
  disabled: false,
16
- element: ELEMENT_TEXT_AS_ICON.BUTTON,
16
+ element: ELEMENT_BROAD.BUTTON,
17
17
  href: undefined,
18
18
  label: undefined,
19
19
  });
@@ -22,21 +22,16 @@
22
22
  <template>
23
23
  <component
24
24
  :class="[
25
- props.element === ELEMENT_TEXT_AS_ICON.LINK ? 'tide-link-as-button-icon' : 'tide-button-icon',
25
+ 'tide-button-icon',
26
26
  [CSS.DISPLAY.INLINE_BLOCK, CSS.BORDER.RADIUS.FULL, CSS.PADDING.FULL.HALF],
27
- props.element === ELEMENT_TEXT_AS_ICON.LINK ? [CSS.UNDERLINE.REST.OFF] : '',
28
- props.element === ELEMENT_TEXT_AS_ICON.DIV ? [CSS.CURSOR.POINTER] : '',
27
+ props.element === ELEMENT_BROAD.LINK ? [CSS.UNDERLINE.REST.OFF] : '',
28
+ props.element === ELEMENT_BROAD.DIV ? [CSS.CURSOR.POINTER] : '',
29
29
  [CSS.FONT.ROLE.HEADLINE_3],
30
30
  ]"
31
- :disabled="props.element === ELEMENT_TEXT_AS_ICON.BUTTON && props.disabled"
32
- :href="props.element === ELEMENT_TEXT_AS_ICON.LINK && props.href ? props.href : undefined"
33
- :is="
34
- props.element === ELEMENT_TEXT_AS_ICON.LINK
35
- ? 'a'
36
- : props.element === ELEMENT_TEXT_AS_ICON.BUTTON
37
- ? 'button'
38
- : 'div'
39
- "
31
+ :disabled="props.element === ELEMENT_BROAD.BUTTON && props.disabled"
32
+ :href="props.element === ELEMENT_BROAD.LINK && props.href ? props.href : undefined"
33
+ :type="element === ELEMENT_BROAD.BUTTON ? ELEMENT_BROAD.BUTTON : undefined"
34
+ :is="element"
40
35
  >
41
36
  <span :class="['label', CSS.DISPLAY.FLEX, CSS.AXIS1.CENTER, CSS.AXIS2.CENTER]">
42
37
  {{ props.label }}
@@ -49,6 +49,7 @@
49
49
  :data-track="tab.dataTrack || undefined"
50
50
  :key="tab.label"
51
51
  @click="handleClick(index)"
52
+ type="button"
52
53
  v-for="(tab, index) in props.tabs"
53
54
  >
54
55
  <span :class="[CSS.FONT.ROLE.LABEL_2_SEMIBOLD]">
@@ -1,10 +1,14 @@
1
1
  <script lang="ts" setup>
2
+ import { computed } from 'vue';
3
+
2
4
  import TideIcon from '@/components/TideIcon.vue';
3
5
  import { TYPE_CARD } from '@/types/Card';
6
+ import { ELEMENT_BROAD } from '@/types/Element';
4
7
  import { SIZE } from '@/types/Size';
5
8
  import { CSS } from '@/types/Styles';
6
9
 
7
10
  import type { CardType } from '@/types/Card';
11
+ import type { ElementBroad } from '@/types/Element';
8
12
  import type { Icon } from '@/types/Icon';
9
13
 
10
14
  type Props = {
@@ -16,13 +20,17 @@
16
20
  href?: string;
17
21
  };
18
22
 
19
- withDefaults(defineProps<Props>(), {
23
+ const props = withDefaults(defineProps<Props>(), {
20
24
  description: undefined,
21
25
  href: undefined,
22
26
  icon: undefined,
23
27
  selected: undefined,
24
28
  type: TYPE_CARD.INFORMATIONAL,
25
29
  });
30
+
31
+ const rootElement = computed<ElementBroad>(() =>
32
+ props.href ? ELEMENT_BROAD.LINK : props.type === TYPE_CARD.INFORMATIONAL ? ELEMENT_BROAD.DIV : ELEMENT_BROAD.DIV
33
+ );
26
34
  </script>
27
35
 
28
36
  <template>
@@ -42,7 +50,8 @@
42
50
  type === TYPE_CARD.SELECTABLE && selected && 'selected',
43
51
  ]"
44
52
  :href="href"
45
- :is="href ? 'a' : type === TYPE_CARD.INFORMATIONAL ? 'div' : 'button'"
53
+ :type="rootElement === ELEMENT_BROAD.BUTTON ? ELEMENT_BROAD.BUTTON : undefined"
54
+ :is="rootElement"
46
55
  >
47
56
  <TideIcon
48
57
  :class="[CSS.FLEX.GROW.OFF, CSS.FLEX.SHRINK.OFF]"
@@ -12,7 +12,6 @@
12
12
  isHeadline1?: boolean;
13
13
  isHideawayButtons?: boolean;
14
14
  isScrollByPage?: boolean;
15
- isTouchscreen?: boolean;
16
15
  maxDots?: number;
17
16
  subtitle?: string;
18
17
  title?: string;
@@ -24,7 +23,6 @@
24
23
  isHeadline1: false,
25
24
  isHideawayButtons: true,
26
25
  isScrollByPage: true,
27
- isTouchscreen: undefined,
28
26
  maxDots: 5,
29
27
  subtitle: undefined,
30
28
  title: undefined,
@@ -83,6 +81,7 @@
83
81
 
84
82
  contentWidth.value = containerRef.value.scrollWidth;
85
83
  frameWidth.value = containerRef.value.clientWidth;
84
+ showButtons.value = contentWidth.value > frameWidth.value;
86
85
  };
87
86
 
88
87
  const observeSlides = () => {
@@ -237,14 +236,14 @@
237
236
  v-if="props.title"
238
237
  >
239
238
  <div
240
- :class="[CSS.WHITESPACE_WRAP.OFF, isHeadline1 ? CSS.FONT.ROLE.HEADLINE_1 : CSS.FONT.ROLE.HEADLINE_2]"
239
+ :class="[isHeadline1 ? CSS.FONT.ROLE.HEADLINE_1 : CSS.FONT.ROLE.HEADLINE_2]"
241
240
  v-if="props.title"
242
241
  >
243
242
  {{ props.title }}
244
243
  </div>
245
244
 
246
245
  <div
247
- :class="[CSS.WHITESPACE_WRAP.OFF, CSS.FONT.ROLE.LABEL_2, CSS.FONT.COLOR.SURFACE.VARIANT]"
246
+ :class="[CSS.FONT.ROLE.LABEL_2, CSS.FONT.COLOR.SURFACE.VARIANT]"
248
247
  v-if="props.subtitle"
249
248
  >
250
249
  {{ props.subtitle }}
@@ -393,4 +392,10 @@
393
392
  transform: scale(1);
394
393
  opacity: 1;
395
394
  }
395
+
396
+ @media (pointer: coarse) {
397
+ .tide-carousel .tide-carousel-button-overlay {
398
+ display: none;
399
+ }
400
+ }
396
401
  </style>
@@ -39,6 +39,7 @@
39
39
  ]"
40
40
  :href="props.href"
41
41
  :target="props.isNewTab ? TARGET.BLANK : TARGET.SELF"
42
+ :type="element === ELEMENT.BUTTON ? ELEMENT.BUTTON : undefined"
42
43
  :is="element"
43
44
  >
44
45
  <TideIcon :icon="ICON.SEARCH" />
@@ -26,6 +26,7 @@
26
26
  CSS.FONT.ROLE.LABEL_2,
27
27
  props.isActive ? 'active' : '',
28
28
  ]"
29
+ type="button"
29
30
  >
30
31
  <slot />
31
32
  <div :class="[CSS.DISPLAY.FLEX, CSS.AXIS1.CENTER, CSS.AXIS2.CENTER, CSS.GAP.HALF]">
@@ -25,6 +25,7 @@
25
25
  CSS.PADDING.Y.HALF,
26
26
  CSS.FONT.ROLE.LABEL_2,
27
27
  ]"
28
+ type="button"
28
29
  >
29
30
  <span>{{ props.label }}</span>
30
31
 
@@ -43,7 +43,7 @@
43
43
  </script>
44
44
 
45
45
  <template>
46
- <div :class="['tide-icon', props.size]">
46
+ <div :class="['tide-icon', props.size, CSS.FLEX.SHRINK.OFF]">
47
47
  <Component
48
48
  :class="[CSS.DISPLAY.BLOCK]"
49
49
  :data-icon="icon"
@@ -61,14 +61,6 @@
61
61
  <picture
62
62
  :class="['tide-image', CSS.DISPLAY.BLOCK]"
63
63
  ref="picture"
64
- :style="
65
- width || height
66
- ? {
67
- width: width || undefined,
68
- height: height || undefined,
69
- }
70
- : undefined
71
- "
72
64
  >
73
65
  <source
74
66
  :key="source.srcset"
@@ -79,11 +71,19 @@
79
71
 
80
72
  <img
81
73
  :alt="alt"
82
- :class="['tide-image-img', CSS.WIDTH.FULL, CSS.HEIGHT.FULL, CSS.OBJECT.CENTER, objectFitClassName]"
74
+ :class="[
75
+ 'tide-image-img',
76
+ !props.width && CSS.WIDTH.FULL,
77
+ !props.height && CSS.HEIGHT.FULL,
78
+ CSS.OBJECT.CENTER,
79
+ objectFitClassName,
80
+ ]"
83
81
  :fetchpriority="isLazy ? undefined : 'high'"
82
+ :height="props.height"
84
83
  :loading="props.isLazy ? 'lazy' : 'eager'"
85
84
  ref="img"
86
85
  :src="src ?? imageDefault"
86
+ :width="props.width"
87
87
  @error="setImageFromDefault"
88
88
  />
89
89
  </picture>
@@ -253,6 +253,7 @@
253
253
 
254
254
  <button
255
255
  @click="handleClear"
256
+ type="button"
256
257
  v-if="shouldShowClearButton"
257
258
  >
258
259
  <TideIcon
@@ -264,6 +265,7 @@
264
265
 
265
266
  <button
266
267
  @click="showPassword = !showPassword"
268
+ type="button"
267
269
  v-if="shouldShowPasswordButton"
268
270
  >
269
271
  <TideIcon
@@ -234,6 +234,7 @@
234
234
 
235
235
  <button
236
236
  @click="handleClear"
237
+ type="button"
237
238
  v-if="hasClear && type !== TEXT_INPUT_TYPE.PASSWORD"
238
239
  >
239
240
  <TideIcon
@@ -245,6 +246,7 @@
245
246
 
246
247
  <button
247
248
  @click="showPassword = !showPassword"
249
+ type="button"
248
250
  v-if="type === TEXT_INPUT_TYPE.PASSWORD"
249
251
  >
250
252
  <TideIcon
@@ -119,7 +119,6 @@
119
119
  CSS.GAP.HALF,
120
120
  CSS.POSITION.RELATIVE,
121
121
  CSS.BORDER.RADIUS.HALF,
122
- CSS.PADDING.LEFT.ONE,
123
122
  CSS.PADDING.TOP.HALF,
124
123
  CSS.OVERFLOW.XY.HIDDEN,
125
124
  CSS.BG.SURFACE.DEFAULT,
@@ -132,6 +131,7 @@
132
131
  <div
133
132
  :class="[
134
133
  'tide-input-textarea-label',
134
+ CSS.PADDING.X.ONE,
135
135
  hasMinilabel ? ['minilabel', CSS.FONT.ROLE.LABEL_3] : CSS.FONT.ROLE.LABEL_1,
136
136
  !showError && CSS.FONT.COLOR.SURFACE.VARIANT,
137
137
  CSS.POINTER_EVENTS.OFF,
@@ -142,7 +142,7 @@
142
142
  </div>
143
143
 
144
144
  <textarea
145
- :class="[CSS.DISPLAY.BLOCK, CSS.WIDTH.FULL, CSS.FONT.ROLE.BODY_1]"
145
+ :class="[CSS.DISPLAY.BLOCK, CSS.FONT.ROLE.BODY_1, CSS.PADDING.X.ONE, CSS.WIDTH.FULL]"
146
146
  :maxlength="maxlength"
147
147
  :minlength="minlength"
148
148
  :name="name"
@@ -38,7 +38,8 @@
38
38
  ]"
39
39
  :href="props.href"
40
40
  :target="props.isNewTab ? TARGET.BLANK : TARGET.SELF"
41
- :is="props.element === ELEMENT.LINK ? ELEMENT.LINK : ELEMENT.BUTTON"
41
+ :type="element === ELEMENT.BUTTON ? ELEMENT.BUTTON : undefined"
42
+ :is="element"
42
43
  >
43
44
  <TideIcon
44
45
  :class="[CSS.DISPLAY.INLINE_BLOCK, CSS.ALIGN.Y.MIDDLE, CSS.MARGIN.RIGHT.QUARTER]"
@@ -1,12 +1,12 @@
1
1
  <script lang="ts" setup>
2
- import { nextTick, onMounted, ref, watch } from 'vue';
2
+ import { nextTick, onBeforeMount, onMounted, ref, watch } from 'vue';
3
3
 
4
4
  import TideButtonIcon from '@/components/TideButtonIcon.vue';
5
5
  import { BREAKPOINT } from '@/types/Breakpoint';
6
6
  import { ICON } from '@/types/Icon';
7
7
  import { PRIORITY } from '@/types/Priority';
8
8
  import { CSS } from '@/types/Styles';
9
- import { setScrollLock } from '@/utilities/viewport';
9
+ import { TOP_LAYER_ID, initFauxTopLayer, setScrollLock } from '@/utilities/viewport';
10
10
 
11
11
  import type { Ref } from 'vue';
12
12
 
@@ -73,6 +73,10 @@
73
73
  }
74
74
  );
75
75
 
76
+ onBeforeMount(() => {
77
+ initFauxTopLayer();
78
+ });
79
+
76
80
  onMounted(() => {
77
81
  if (props.isOpen) {
78
82
  triggerNativeDialogOpen();
@@ -81,97 +85,99 @@
81
85
  </script>
82
86
 
83
87
  <template>
84
- <dialog
85
- :class="['tide-modal', CSS.BG.INITIAL, CSS.HEIGHT.FULL, CSS.WIDTH.FULL, CSS.OVERFLOW.XY.HIDDEN]"
86
- ref="modalDialog"
87
- :style="{ '--modal-width': props.width }"
88
- @click.self="handleBackdropClick"
89
- @close.prevent
90
- @keydown.escape="handleEscapeKeydown"
91
- >
92
- <div
93
- :class="[
94
- 'tide-modal-body',
95
- CSS.BG.SURFACE.DEFAULT,
96
- CSS.BORDER.RADIUS.ONE,
97
- CSS.DISPLAY.FLEX,
98
- CSS.FLEX.DIRECTION.COLUMN,
99
- CSS.OVERFLOW.XY.HIDDEN,
100
- CSS.POSITION.ABSOLUTE,
101
- CSS.POSITIONING.BOTTOM,
102
- CSS.SHADOW.TOP,
103
- CSS.WIDTH.FULL,
104
- CSS.WIDTH.MAX_FULL,
105
- CSS.withBreakpoint([CSS.SHADOW.BOTTOM], BREAKPOINT.SM),
106
- ]"
88
+ <Teleport :to="`#${TOP_LAYER_ID}`">
89
+ <dialog
90
+ :class="['tide-modal', CSS.BG.INITIAL, CSS.HEIGHT.FULL, CSS.WIDTH.FULL, CSS.OVERFLOW.XY.HIDDEN]"
91
+ ref="modalDialog"
92
+ :style="{ '--modal-width': props.width }"
93
+ @click.self="handleBackdropClick"
94
+ @close.prevent
95
+ @keydown.escape="handleEscapeKeydown"
107
96
  >
108
- <header
97
+ <div
109
98
  :class="[
110
- 'tide-modal-header',
111
- CSS.AXIS2.CENTER,
112
- CSS.BORDER.BOTTOM.ONE,
113
- CSS.BORDER.COLOR.LOW,
99
+ 'tide-modal-body',
100
+ CSS.BG.SURFACE.DEFAULT,
101
+ CSS.BORDER.RADIUS.ONE,
114
102
  CSS.DISPLAY.FLEX,
115
- CSS.GAP.HALF,
116
- CSS.ISOLATION.ISOLATE,
117
- CSS.PADDING.Y.ONE,
118
- CSS.POSITION.RELATIVE,
119
- CSS.Z_INDEX.ONE,
103
+ CSS.FLEX.DIRECTION.COLUMN,
104
+ CSS.OVERFLOW.XY.HIDDEN,
105
+ CSS.POSITION.ABSOLUTE,
106
+ CSS.POSITIONING.BOTTOM,
107
+ CSS.SHADOW.TOP,
108
+ CSS.WIDTH.FULL,
109
+ CSS.WIDTH.MAX_FULL,
110
+ CSS.withBreakpoint([CSS.SHADOW.BOTTOM], BREAKPOINT.SM),
120
111
  ]"
121
112
  >
122
- <TideButtonIcon
123
- :icon="ICON.CHEVRON_LEFT"
124
- :priority="PRIORITY.QUATERNARY"
125
- @click="emit('back')"
126
- title="Back"
127
- v-if="isBackButton"
128
- />
113
+ <header
114
+ :class="[
115
+ 'tide-modal-header',
116
+ CSS.AXIS2.CENTER,
117
+ CSS.BORDER.BOTTOM.ONE,
118
+ CSS.BORDER.COLOR.LOW,
119
+ CSS.DISPLAY.FLEX,
120
+ CSS.GAP.HALF,
121
+ CSS.ISOLATION.ISOLATE,
122
+ CSS.PADDING.Y.ONE,
123
+ CSS.POSITION.RELATIVE,
124
+ CSS.Z_INDEX.ONE,
125
+ ]"
126
+ >
127
+ <TideButtonIcon
128
+ :icon="ICON.CHEVRON_LEFT"
129
+ :priority="PRIORITY.QUATERNARY"
130
+ @click="emit('back')"
131
+ title="Back"
132
+ v-if="isBackButton"
133
+ />
134
+
135
+ <div
136
+ :class="[CSS.FONT.ROLE.HEADLINE_2]"
137
+ v-text="title"
138
+ />
139
+
140
+ <TideButtonIcon
141
+ :class="[CSS.FLEX.GROW.OFF, CSS.FLEX.SHRINK.OFF, CSS.MARGIN.LEFT.AUTO]"
142
+ :icon="ICON.CLOSE"
143
+ :priority="PRIORITY.QUATERNARY"
144
+ @click="emit('close')"
145
+ v-if="isDismissible"
146
+ />
147
+ </header>
129
148
 
130
149
  <div
131
- :class="[CSS.FONT.ROLE.HEADLINE_2]"
132
- v-text="title"
133
- />
134
-
135
- <TideButtonIcon
136
- :class="[CSS.FLEX.GROW.OFF, CSS.FLEX.SHRINK.OFF, CSS.MARGIN.LEFT.AUTO]"
137
- :icon="ICON.CLOSE"
138
- :priority="PRIORITY.QUATERNARY"
139
- @click="emit('close')"
140
- v-if="isDismissible"
141
- />
142
- </header>
143
-
144
- <div
145
- :class="[
146
- 'tide-modal-content',
147
- CSS.DISPLAY.GRID,
148
- CSS.ISOLATION.ISOLATE,
149
- CSS.OVERFLOW.X.HIDDEN,
150
- CSS.OVERFLOW.Y.AUTO,
151
- CSS.PADDING.Y.TWO,
152
- CSS.WIDTH.FULL,
153
- ]"
154
- ref="modalContent"
155
- >
156
- <slot />
150
+ :class="[
151
+ 'tide-modal-content',
152
+ CSS.DISPLAY.GRID,
153
+ CSS.ISOLATION.ISOLATE,
154
+ CSS.OVERFLOW.X.HIDDEN,
155
+ CSS.OVERFLOW.Y.AUTO,
156
+ CSS.PADDING.Y.TWO,
157
+ CSS.WIDTH.FULL,
158
+ ]"
159
+ ref="modalContent"
160
+ >
161
+ <slot />
162
+ </div>
163
+
164
+ <footer
165
+ :class="[
166
+ 'tide-modal-footer',
167
+ CSS.AXIS1.END,
168
+ CSS.DISPLAY.FLEX,
169
+ CSS.GAP.TWO,
170
+ CSS.ISOLATION.ISOLATE,
171
+ CSS.PADDING.Y.ONE,
172
+ CSS.SHADOW.TOP,
173
+ ]"
174
+ v-if="$slots.footer"
175
+ >
176
+ <slot name="footer" />
177
+ </footer>
157
178
  </div>
158
-
159
- <footer
160
- :class="[
161
- 'tide-modal-footer',
162
- CSS.AXIS1.END,
163
- CSS.DISPLAY.FLEX,
164
- CSS.GAP.TWO,
165
- CSS.ISOLATION.ISOLATE,
166
- CSS.PADDING.Y.ONE,
167
- CSS.SHADOW.TOP,
168
- ]"
169
- v-if="$slots.footer"
170
- >
171
- <slot name="footer" />
172
- </footer>
173
- </div>
174
- </dialog>
179
+ </dialog>
180
+ </Teleport>
175
181
  </template>
176
182
 
177
183
  <style scoped>
@@ -39,6 +39,7 @@
39
39
  :disabled="props.disabled"
40
40
  @click="handleClick"
41
41
  aria-label="Switch"
42
+ type="button"
42
43
  >
43
44
  <div
44
45
  :class="[