tide-design-system 2.4.5 → 2.4.7

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 (56) hide show
  1. package/.storybook/main.ts +1 -0
  2. package/dist/css/reset.css +1 -1
  3. package/dist/css/utilities-responsive.css +0 -546
  4. package/dist/style.css +1 -1
  5. package/dist/tide-design-system.cjs +2 -2
  6. package/dist/tide-design-system.esm.d.ts +62 -6
  7. package/dist/tide-design-system.esm.js +1821 -1720
  8. package/dist/utilities/storybook.ts +6 -2
  9. package/dist/utilities/validation.ts +1 -1
  10. package/index.ts +8 -5
  11. package/package.json +1 -1
  12. package/src/assets/css/reset.css +1 -1
  13. package/src/assets/css/utilities-responsive.css +0 -546
  14. package/src/components/InternalBaseLink.vue +11 -0
  15. package/src/components/TideBreadCrumbs.vue +3 -2
  16. package/src/components/TideButton.vue +17 -4
  17. package/src/components/TideButtonIcon.vue +15 -2
  18. package/src/components/TideButtonPagination.vue +16 -16
  19. package/src/components/TideButtonSegmented.vue +1 -0
  20. package/src/components/TideCard.vue +12 -2
  21. package/src/components/TideCarousel.vue +10 -5
  22. package/src/components/TideChipAction.vue +7 -1
  23. package/src/components/TideChipFilter.vue +1 -0
  24. package/src/components/TideChipInput.vue +1 -0
  25. package/src/components/TideIcon.vue +8 -9
  26. package/src/components/TideImage.vue +9 -9
  27. package/src/components/TideInputText.vue +2 -0
  28. package/src/components/TideInputTextDeprecated.vue +2 -0
  29. package/src/components/TideInputTextarea.vue +2 -2
  30. package/src/components/TideLink.vue +7 -1
  31. package/src/components/TideMenuItem.vue +83 -0
  32. package/src/components/TideModal.vue +91 -85
  33. package/src/components/TideSeoLinks.vue +3 -2
  34. package/src/components/TideSheet.vue +5 -3
  35. package/src/components/TideSwitch.vue +1 -0
  36. package/src/composables/useTideConfig.ts +23 -0
  37. package/src/stories/TideButtonPagination.stories.ts +6 -6
  38. package/src/stories/TideCarousel.stories.ts +0 -1
  39. package/src/stories/TideInputCheckbox.stories.ts +58 -23
  40. package/src/stories/TideInputRadio.stories.ts +39 -30
  41. package/src/stories/TideInputSelect.stories.ts +51 -27
  42. package/src/stories/TideInputText.stories.ts +83 -23
  43. package/src/stories/TideInputTextarea.stories.ts +66 -17
  44. package/src/stories/TideLink.stories.ts +1 -14
  45. package/src/stories/TideMenuItem.stories.ts +117 -0
  46. package/src/stories/TidePagination.stories.ts +2 -2
  47. package/src/stories/TidePopover.stories.ts +1 -1
  48. package/src/types/Badge.ts +4 -0
  49. package/src/types/Element.ts +2 -2
  50. package/src/types/Formatted.ts +1 -0
  51. package/src/types/Storybook.ts +4 -6
  52. package/src/types/Type.ts +6 -0
  53. package/src/types/Validation.ts +1 -0
  54. package/src/utilities/storybook.ts +6 -2
  55. package/src/utilities/validation.ts +1 -1
  56. package/tests/InternalBaseLink.spec.ts +61 -0
@@ -0,0 +1,11 @@
1
+ <script setup lang="ts">
2
+ import { useTideConfig } from '@/composables/useTideConfig';
3
+
4
+ const { linkComponent } = useTideConfig();
5
+ </script>
6
+
7
+ <template>
8
+ <component :is="linkComponent">
9
+ <slot />
10
+ </component>
11
+ </template>
@@ -1,4 +1,5 @@
1
1
  <script lang="ts" setup>
2
+ import InternalBaseLink from '@/components/InternalBaseLink.vue';
2
3
  import TideIcon from '@/components/TideIcon.vue';
3
4
  import { ICON } from '@/types/Icon';
4
5
  import { SIZE } from '@/types/Size';
@@ -22,14 +23,14 @@
22
23
  v-for="(crumb, index) in props.breadCrumbs"
23
24
  >
24
25
  <li :class="[CSS.FONT.ROLE.BODY_2]">
25
- <a
26
+ <InternalBaseLink
26
27
  :class="[CSS.FONT.ROLE.LINK_2, CSS.FONT.COLOR.SURFACE.DEFAULT]"
27
28
  :data-track="crumb.dataTrack || undefined"
28
29
  :href="crumb.url"
29
30
  v-if="crumb.url"
30
31
  >
31
32
  {{ crumb.label }}
32
- </a>
33
+ </InternalBaseLink>
33
34
 
34
35
  <span
35
36
  :class="[CSS.FONT.ROLE.LABEL_2, CSS.FONT.COLOR.SURFACE.VARIANT]"
@@ -1,4 +1,7 @@
1
1
  <script lang="ts" setup>
2
+ import { computed } from 'vue';
3
+
4
+ import InternalBaseLink from '@/components/InternalBaseLink.vue';
2
5
  import TideIcon from '@/components/TideIcon.vue';
3
6
  import { ELEMENT } from '@/types/Element';
4
7
  import { PRIORITY } from '@/types/Priority';
@@ -10,6 +13,7 @@
10
13
  import type { Icon } from '@/types/Icon';
11
14
  import type { Priority } from '@/types/Priority';
12
15
  import type { SizeButton } from '@/types/Size';
16
+ import type { ButtonType } from '@/types/Type';
13
17
 
14
18
  type Props = {
15
19
  disabled?: boolean;
@@ -21,6 +25,7 @@
21
25
  label: string;
22
26
  priority?: Priority;
23
27
  size?: SizeButton;
28
+ type?: ButtonType;
24
29
  };
25
30
 
26
31
  const props = withDefaults(defineProps<Props>(), {
@@ -33,7 +38,14 @@
33
38
  label: undefined,
34
39
  priority: PRIORITY.PRIMARY,
35
40
  size: SIZE_BUTTON.LARGE,
41
+ type: undefined,
36
42
  });
43
+
44
+ const typeAttribute = computed<string | undefined>(() =>
45
+ props.element === ELEMENT.BUTTON ? props.type ?? ELEMENT.BUTTON : undefined
46
+ );
47
+
48
+ const rootElement = computed(() => (props.element === ELEMENT.LINK ? InternalBaseLink : props.element));
37
49
  </script>
38
50
 
39
51
  <template>
@@ -43,18 +55,19 @@
43
55
  props.priority && `tide-button-${props.priority}`,
44
56
  props.element === ELEMENT.LINK ? [CSS.DISPLAY.INLINE_FLEX] : [CSS.DISPLAY.FLEX],
45
57
  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],
58
+ size === SIZE_BUTTON.SMALL
59
+ ? [CSS.FONT.ROLE.BUTTON_2, CSS.PADDING.X.ONE, CSS.PADDING.Y.HALF, CSS.BORDER.RADIUS.QUARTER]
60
+ : [CSS.FONT.ROLE.BUTTON_1, CSS.PADDING.X.TWO, CSS.PADDING.Y.ONE, CSS.BORDER.RADIUS.HALF],
48
61
  CSS.AXIS1.CENTER,
49
62
  CSS.AXIS2.CENTER,
50
63
  CSS.GAP.HALF,
51
- CSS.BORDER.RADIUS.HALF,
52
64
  CSS.FONT.ROLE.LABEL_1_SEMIBOLD,
53
65
  ]"
54
66
  :disabled="props.element === ELEMENT.BUTTON && props.disabled"
55
67
  :href="props.element === ELEMENT.LINK && props.href ? props.href : undefined"
56
68
  :target="props.element === ELEMENT.LINK && props.isNewTab ? TARGET.BLANK : TARGET.SELF"
57
- :is="props.element === ELEMENT.LINK ? ELEMENT.LINK : ELEMENT.BUTTON"
69
+ :type="typeAttribute"
70
+ :is="rootElement"
58
71
  >
59
72
  <TideIcon
60
73
  :icon="props.iconLeading"
@@ -1,4 +1,7 @@
1
1
  <script lang="ts" setup>
2
+ import { computed } from 'vue';
3
+
4
+ import InternalBaseLink from '@/components/InternalBaseLink.vue';
2
5
  import TideIcon from '@/components/TideIcon.vue';
3
6
  import { ELEMENT } from '@/types/Element';
4
7
  import { PRIORITY } from '@/types/Priority';
@@ -9,6 +12,7 @@
9
12
  import type { Element } from '@/types/Element';
10
13
  import type { Icon } from '@/types/Icon';
11
14
  import type { Priority } from '@/types/Priority';
15
+ import type { ButtonType } from '@/types/Type';
12
16
 
13
17
  type Props = {
14
18
  disabled?: boolean;
@@ -17,6 +21,7 @@
17
21
  icon: Icon;
18
22
  isNewTab?: boolean;
19
23
  priority?: Priority;
24
+ type?: ButtonType;
20
25
  };
21
26
 
22
27
  const props = withDefaults(defineProps<Props>(), {
@@ -26,14 +31,21 @@
26
31
  icon: undefined,
27
32
  isNewTab: false,
28
33
  priority: PRIORITY.PRIMARY,
34
+ type: undefined,
29
35
  });
36
+
37
+ const typeAttribute = computed<string | undefined>(() =>
38
+ props.element === ELEMENT.BUTTON ? props.type ?? ELEMENT.BUTTON : undefined
39
+ );
40
+
41
+ const rootElement = computed(() => (props.element === ELEMENT.LINK ? InternalBaseLink : props.element));
30
42
  </script>
31
43
 
32
44
  <template>
33
45
  <component
34
46
  :aria-label="props.icon"
35
47
  :class="[
36
- props.element === ELEMENT.LINK ? 'tide-link-as-button-icon' : 'tide-button-icon',
48
+ 'tide-button-icon',
37
49
  props.priority && `tide-button-${props.priority}`,
38
50
  [CSS.DISPLAY.FLEX, CSS.AXIS1.CENTER, CSS.AXIS2.CENTER, CSS.BORDER.RADIUS.FULL, CSS.PADDING.FULL.HALF],
39
51
  props.element === ELEMENT.LINK ? [CSS.UNDERLINE.REST.OFF] : '',
@@ -41,7 +53,8 @@
41
53
  :disabled="props.element === ELEMENT.BUTTON && props.disabled"
42
54
  :href="props.element === ELEMENT.LINK && props.href ? props.href : undefined"
43
55
  :target="props.element === ELEMENT.LINK && props.isNewTab ? TARGET.BLANK : TARGET.SELF"
44
- :is="props.element === ELEMENT.LINK ? 'a' : 'button'"
56
+ :type="typeAttribute"
57
+ :is="rootElement"
45
58
  >
46
59
  <TideIcon
47
60
  :icon="props.icon"
@@ -1,42 +1,42 @@
1
1
  <script lang="ts" setup>
2
- import { ELEMENT_TEXT_AS_ICON } from '@/types/Element';
2
+ import { computed } from 'vue';
3
+
4
+ import InternalBaseLink from '@/components/InternalBaseLink.vue';
5
+ import { ELEMENT, ELEMENT_BROAD } from '@/types/Element';
3
6
  import { CSS } from '@/types/Styles';
4
7
 
5
- import type { ElementTextAsIcon } from '@/types/Element';
8
+ import type { ElementBroad } from '@/types/Element';
6
9
 
7
10
  type Props = {
8
11
  disabled?: boolean;
9
- element?: ElementTextAsIcon;
12
+ element?: ElementBroad;
10
13
  href?: string;
11
14
  label: string | number;
12
15
  };
13
16
 
14
17
  const props = withDefaults(defineProps<Props>(), {
15
18
  disabled: false,
16
- element: ELEMENT_TEXT_AS_ICON.BUTTON,
19
+ element: ELEMENT_BROAD.BUTTON,
17
20
  href: undefined,
18
21
  label: undefined,
19
22
  });
23
+
24
+ const rootElement = computed(() => (props.element === ELEMENT.LINK ? InternalBaseLink : props.element));
20
25
  </script>
21
26
 
22
27
  <template>
23
28
  <component
24
29
  :class="[
25
- props.element === ELEMENT_TEXT_AS_ICON.LINK ? 'tide-link-as-button-icon' : 'tide-button-icon',
30
+ 'tide-button-icon',
26
31
  [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] : '',
32
+ props.element === ELEMENT_BROAD.LINK ? [CSS.UNDERLINE.REST.OFF] : '',
33
+ props.element === ELEMENT_BROAD.DIV ? [CSS.CURSOR.POINTER] : '',
29
34
  [CSS.FONT.ROLE.HEADLINE_3],
30
35
  ]"
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
- "
36
+ :disabled="props.element === ELEMENT_BROAD.BUTTON && props.disabled"
37
+ :href="props.element === ELEMENT_BROAD.LINK && props.href ? props.href : undefined"
38
+ :type="element === ELEMENT_BROAD.BUTTON ? ELEMENT_BROAD.BUTTON : undefined"
39
+ :is="rootElement"
40
40
  >
41
41
  <span :class="['label', CSS.DISPLAY.FLEX, CSS.AXIS1.CENTER, CSS.AXIS2.CENTER]">
42
42
  {{ 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,15 @@
1
1
  <script lang="ts" setup>
2
+ import { computed } from 'vue';
3
+
4
+ import InternalBaseLink from '@/components/InternalBaseLink.vue';
2
5
  import TideIcon from '@/components/TideIcon.vue';
3
6
  import { TYPE_CARD } from '@/types/Card';
7
+ import { ELEMENT_BROAD } from '@/types/Element';
4
8
  import { SIZE } from '@/types/Size';
5
9
  import { CSS } from '@/types/Styles';
6
10
 
7
11
  import type { CardType } from '@/types/Card';
12
+ import type { ElementBroad } from '@/types/Element';
8
13
  import type { Icon } from '@/types/Icon';
9
14
 
10
15
  type Props = {
@@ -16,13 +21,17 @@
16
21
  href?: string;
17
22
  };
18
23
 
19
- withDefaults(defineProps<Props>(), {
24
+ const props = withDefaults(defineProps<Props>(), {
20
25
  description: undefined,
21
26
  href: undefined,
22
27
  icon: undefined,
23
28
  selected: undefined,
24
29
  type: TYPE_CARD.INFORMATIONAL,
25
30
  });
31
+
32
+ const rootElement = computed<ElementBroad | typeof InternalBaseLink>(() =>
33
+ props.href ? InternalBaseLink : props.type === TYPE_CARD.INFORMATIONAL ? ELEMENT_BROAD.DIV : ELEMENT_BROAD.DIV
34
+ );
26
35
  </script>
27
36
 
28
37
  <template>
@@ -42,7 +51,8 @@
42
51
  type === TYPE_CARD.SELECTABLE && selected && 'selected',
43
52
  ]"
44
53
  :href="href"
45
- :is="href ? 'a' : type === TYPE_CARD.INFORMATIONAL ? 'div' : 'button'"
54
+ :type="rootElement === ELEMENT_BROAD.BUTTON ? ELEMENT_BROAD.BUTTON : undefined"
55
+ :is="rootElement"
46
56
  >
47
57
  <TideIcon
48
58
  :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 }}
@@ -273,7 +272,7 @@
273
272
 
274
273
  <slot name="misc" />
275
274
 
276
- <div :class="[CSS.POSITION.RELATIVE, CSS.DISPLAY.FLEX, CSS.AXIS1.CENTER]">
275
+ <div :class="[CSS.POSITION.RELATIVE, CSS.DISPLAY.FLEX]">
277
276
  <ul
278
277
  :class="[
279
278
  'tide-carousel-container',
@@ -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>
@@ -1,4 +1,7 @@
1
1
  <script lang="ts" setup>
2
+ import { computed } from 'vue';
3
+
4
+ import InternalBaseLink from '@/components/InternalBaseLink.vue';
2
5
  import TideIcon from '@/components/TideIcon.vue';
3
6
  import { ELEMENT } from '@/types/Element';
4
7
  import { ICON } from '@/types/Icon';
@@ -19,6 +22,8 @@
19
22
  href: undefined,
20
23
  isNewTab: false,
21
24
  });
25
+
26
+ const rootElement = computed(() => (props.element === ELEMENT.LINK ? InternalBaseLink : props.element));
22
27
  </script>
23
28
 
24
29
  <template>
@@ -39,7 +44,8 @@
39
44
  ]"
40
45
  :href="props.href"
41
46
  :target="props.isNewTab ? TARGET.BLANK : TARGET.SELF"
42
- :is="element"
47
+ :type="element === ELEMENT.BUTTON ? ELEMENT.BUTTON : undefined"
48
+ :is="rootElement"
43
49
  >
44
50
  <TideIcon :icon="ICON.SEARCH" />
45
51
 
@@ -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
 
@@ -1,5 +1,5 @@
1
1
  <script lang="ts" setup>
2
- import { defineAsyncComponent, markRaw, ref, watch } from 'vue';
2
+ import { markRaw, ref, watch } from 'vue';
3
3
 
4
4
  import { REALM } from '@/types/Realm';
5
5
  import { SIZE } from '@/types/Size';
@@ -28,22 +28,21 @@
28
28
  const name = formatPascalCase(id);
29
29
  const [prefix] = id.split('-');
30
30
  const realmFolder = REALM[prefix?.toUpperCase() as keyof typeof REALM];
31
- const component = defineAsyncComponent(() => {
32
- if (realmFolder) {
33
- return import(`../assets/svg/icons/realm/${realmFolder}/Icon${name}.svg?component`);
34
- }
35
31
 
36
- return import(`../assets/svg/icons/Icon${name}.svg?component`);
37
- });
32
+ const loader = realmFolder
33
+ ? () => import(`../assets/svg/icons/realm/${realmFolder}/Icon${name}.svg?component`)
34
+ : () => import(`../assets/svg/icons/Icon${name}.svg?component`);
38
35
 
39
- innerSVG.value = markRaw(component);
36
+ const mod = await loader();
37
+
38
+ innerSVG.value = markRaw(mod.default);
40
39
  },
41
40
  { immediate: true }
42
41
  );
43
42
  </script>
44
43
 
45
44
  <template>
46
- <div :class="['tide-icon', props.size]">
45
+ <div :class="['tide-icon', props.size, CSS.FLEX.SHRINK.OFF]">
47
46
  <Component
48
47
  :class="[CSS.DISPLAY.BLOCK]"
49
48
  :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"
@@ -1,4 +1,7 @@
1
1
  <script lang="ts" setup>
2
+ import { computed } from 'vue';
3
+
4
+ import InternalBaseLink from '@/components/InternalBaseLink.vue';
2
5
  import TideIcon from '@/components/TideIcon.vue';
3
6
  import { ELEMENT } from '@/types/Element';
4
7
  import { CSS } from '@/types/Styles';
@@ -26,6 +29,8 @@
26
29
  label: undefined,
27
30
  subtle: false,
28
31
  });
32
+
33
+ const rootElement = computed(() => (props.element === ELEMENT.LINK ? InternalBaseLink : props.element));
29
34
  </script>
30
35
 
31
36
  <template>
@@ -38,7 +43,8 @@
38
43
  ]"
39
44
  :href="props.href"
40
45
  :target="props.isNewTab ? TARGET.BLANK : TARGET.SELF"
41
- :is="props.element === ELEMENT.LINK ? ELEMENT.LINK : ELEMENT.BUTTON"
46
+ :type="element === ELEMENT.BUTTON ? ELEMENT.BUTTON : undefined"
47
+ :is="rootElement"
42
48
  >
43
49
  <TideIcon
44
50
  :class="[CSS.DISPLAY.INLINE_BLOCK, CSS.ALIGN.Y.MIDDLE, CSS.MARGIN.RIGHT.QUARTER]"
@@ -0,0 +1,83 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Renders a generic menu item intended to be used in navigation menus or dropdowns.
4
+ *
5
+ * @see the [Storybook interface](https://tide-design-system.netlify.app/?path=/docs/components-tidemenuitem--docs) for TideMenuItem
6
+ */
7
+ export default {};
8
+ </script>
9
+
10
+ <script setup lang="ts">
11
+ import TideIcon from '@/components/TideIcon.vue';
12
+ import { ELEMENT } from '@/types/Element';
13
+ import { SIZE } from '@/types/Size';
14
+ import { CSS } from '@/types/Styles';
15
+ import { TARGET } from '@/types/Target';
16
+
17
+ import type { Element } from '@/types/Element';
18
+ import type { Icon } from '@/types/Icon';
19
+
20
+ type Props = {
21
+ /** The element to render the root element as. */
22
+ element?: Element;
23
+
24
+ /** The href attribute to apply when the element is a link. */
25
+ href?: string;
26
+
27
+ /** Whether to open link in a new tab. */
28
+ isNewTab?: boolean;
29
+
30
+ /** Icon to show after the label. */
31
+ iconTrailing?: Icon;
32
+
33
+ /** The label to be displayed. */
34
+ label: string;
35
+ };
36
+
37
+ withDefaults(defineProps<Props>(), {
38
+ element: ELEMENT.LINK,
39
+ href: undefined,
40
+ iconTrailing: undefined,
41
+ isNewTab: false,
42
+ });
43
+ </script>
44
+
45
+ <template>
46
+ <component
47
+ :class="[
48
+ 'tide-menu-item',
49
+ CSS.AXIS2.CENTER,
50
+ CSS.BORDER.RADIUS.ZERO,
51
+ CSS.DISPLAY.FLEX,
52
+ CSS.ELLIPSIS,
53
+ CSS.FONT.ROLE.LABEL_2,
54
+ CSS.GAP.QUARTER,
55
+ CSS.PADDING.X.ONE,
56
+ CSS.PADDING.Y.HALF,
57
+ CSS.UNDERLINE.REST.OFF,
58
+ CSS.WIDTH.FULL,
59
+ ]"
60
+ :href="href"
61
+ :target="isNewTab ? TARGET.BLANK : undefined"
62
+ :is="element"
63
+ >
64
+ <span>{{ label }}</span>
65
+
66
+ <TideIcon
67
+ :class="[CSS.MARGIN.LEFT.AUTO]"
68
+ :icon="iconTrailing"
69
+ :size="SIZE.LARGE"
70
+ v-if="iconTrailing"
71
+ />
72
+ </component>
73
+ </template>
74
+
75
+ <style scoped>
76
+ .tide-menu-item {
77
+ height: 40px;
78
+ }
79
+
80
+ .tide-menu-item:hover {
81
+ background-color: var(--tide-surface-variant);
82
+ }
83
+ </style>