tide-design-system 2.1.6 → 2.1.8

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 (182) hide show
  1. package/.storybook/main.ts +1 -0
  2. package/dist/IconAcute-ad6bc5da.js +16 -0
  3. package/dist/IconAcute-b1e43123.cjs +2 -0
  4. package/dist/IconAlignSpaceEven-7ee811f8.cjs +2 -0
  5. package/dist/IconAlignSpaceEven-a1d90758.js +16 -0
  6. package/dist/IconAtvAtv-c2e11398.cjs +2 -0
  7. package/dist/IconAtvAtv-d10d11c0.js +20 -0
  8. package/dist/IconAtvDuneBuggy-458e7a9c.js +20 -0
  9. package/dist/IconAtvDuneBuggy-ff1f7a23.cjs +2 -0
  10. package/dist/IconAtvGoKart-0eab678e.js +20 -0
  11. package/dist/IconAtvGoKart-d1ad6a03.cjs +2 -0
  12. package/dist/IconAtvGolfCart-9b3569bc.cjs +2 -0
  13. package/dist/IconAtvGolfCart-c549f7f3.js +20 -0
  14. package/dist/IconAtvSandRail-d71aa55b.cjs +2 -0
  15. package/dist/IconAtvSandRail-e8913dd9.js +20 -0
  16. package/dist/IconAtvSideBySide-50377701.js +20 -0
  17. package/dist/IconAtvSideBySide-f734e90f.cjs +2 -0
  18. package/dist/IconAtvTrailer-062da3ab.js +20 -0
  19. package/dist/IconAtvTrailer-ee1d6ec1.cjs +2 -0
  20. package/dist/IconAutoRenew-00afcc33.js +16 -0
  21. package/dist/IconAutoRenew-bcd665a4.cjs +2 -0
  22. package/dist/IconBoatmartPwc-7a3c368a.js +20 -0
  23. package/dist/IconBoatmartPwc-9de80564.cjs +2 -0
  24. package/dist/IconBoatmartTrailer-68276601.js +20 -0
  25. package/dist/IconBoatmartTrailer-ff1f376f.cjs +2 -0
  26. package/dist/IconBolt-36fc8ef9.cjs +2 -0
  27. package/dist/IconBolt-83121ee2.js +16 -0
  28. package/dist/IconCallQuality-1edf4c35.js +16 -0
  29. package/dist/IconCallQuality-56ee01fb.cjs +2 -0
  30. package/dist/IconCarRental-7709e091.js +16 -0
  31. package/dist/IconCarRental-b0af42ff.cjs +2 -0
  32. package/dist/IconContract-0acea45b.cjs +2 -0
  33. package/dist/IconContract-289a7094.js +16 -0
  34. package/dist/IconCrash-8d91a8db.cjs +2 -0
  35. package/dist/IconCrash-d87f6517.js +16 -0
  36. package/dist/IconDashboardCustomize-5321091b.cjs +2 -0
  37. package/dist/IconDashboardCustomize-f74fde35.js +16 -0
  38. package/dist/IconDirectionsBoat-11aaf49c.cjs +2 -0
  39. package/dist/IconDirectionsBoat-4d41fb87.js +16 -0
  40. package/dist/IconDomain-05540c31.cjs +2 -0
  41. package/dist/IconDomain-377b91aa.js +16 -0
  42. package/dist/IconEngineering-a74e5efe.cjs +2 -0
  43. package/dist/IconEngineering-e18db558.js +16 -0
  44. package/dist/IconFlag-0243bbb9.cjs +2 -0
  45. package/dist/IconFlag-8bafa50c.js +16 -0
  46. package/dist/IconGears-a52f8ce9.js +16 -0
  47. package/dist/IconGears-c3b1ba71.cjs +2 -0
  48. package/dist/IconGlobeLocationPin-4b7fb00b.js +16 -0
  49. package/dist/IconGlobeLocationPin-b869a4e7.cjs +2 -0
  50. package/dist/IconHandyman-73c7c329.js +16 -0
  51. package/dist/IconHandyman-ffc0fafb.cjs +2 -0
  52. package/dist/IconInformation-790c598a.js +16 -0
  53. package/dist/IconInformation-be043a15.cjs +2 -0
  54. package/dist/IconMoneyBag-4a01a10a.js +16 -0
  55. package/dist/IconMoneyBag-a3d297f5.cjs +2 -0
  56. package/dist/IconPersonSearch-1ccccee1.js +16 -0
  57. package/dist/IconPersonSearch-b0c1a4e5.cjs +2 -0
  58. package/dist/IconPolicy-0a3a02bc.js +16 -0
  59. package/dist/IconPolicy-8f5ca682.cjs +2 -0
  60. package/dist/IconPower-bfe4f81a.js +16 -0
  61. package/dist/IconPower-d41e067d.cjs +2 -0
  62. package/dist/IconPriorityHigh-aae28a02.cjs +2 -0
  63. package/dist/IconPriorityHigh-ab1b4a95.js +16 -0
  64. package/dist/IconRequestQuote-e24db541.cjs +2 -0
  65. package/dist/IconRequestQuote-f6364b1e.js +16 -0
  66. package/dist/IconResetWrench-5b5c4f74.cjs +2 -0
  67. package/dist/IconResetWrench-8791a5a9.js +16 -0
  68. package/dist/IconRoad-3124bbd8.cjs +2 -0
  69. package/dist/IconRoad-efce130e.js +16 -0
  70. package/dist/IconRoundedCorner-53cb2cfc.cjs +2 -0
  71. package/dist/IconRoundedCorner-adbcfd09.js +16 -0
  72. package/dist/IconSailing-19762955.js +16 -0
  73. package/dist/IconSailing-fa136eea.cjs +2 -0
  74. package/dist/IconSchool-0bca17f9.cjs +2 -0
  75. package/dist/IconSchool-2f3ff8d7.js +16 -0
  76. package/dist/IconSensors-c4387bc9.js +16 -0
  77. package/dist/IconSensors-f35e7566.cjs +2 -0
  78. package/dist/IconSettings-32dd2125.js +16 -0
  79. package/dist/IconSettings-b8848783.cjs +2 -0
  80. package/dist/IconShieldCheck-0c47755d.js +16 -0
  81. package/dist/IconShieldCheck-b57fd628.cjs +2 -0
  82. package/dist/IconSummarize-69ab122a.js +16 -0
  83. package/dist/IconSummarize-f73dec0e.cjs +2 -0
  84. package/dist/IconSwapHoriz-2d9550c3.js +16 -0
  85. package/dist/IconSwapHoriz-4a33bd68.cjs +2 -0
  86. package/dist/IconVerified-04c12500.cjs +2 -0
  87. package/dist/IconVerified-a78449ea.js +16 -0
  88. package/dist/IconWarehouse-23085045.cjs +2 -0
  89. package/dist/IconWarehouse-764da944.js +16 -0
  90. package/dist/IconWorkspacePremium-bc3aeb0c.cjs +2 -0
  91. package/dist/IconWorkspacePremium-efd0a531.js +16 -0
  92. package/dist/IconWrench-6c52ef4f.js +16 -0
  93. package/dist/IconWrench-bd1d2907.cjs +2 -0
  94. package/dist/css/realm/aero.css +3 -3
  95. package/dist/css/realm/boatmart.css +1 -1
  96. package/dist/css/realm/cycle.css +1 -1
  97. package/dist/css/realm/equipment.css +1 -1
  98. package/dist/css/realm/pwc.css +1 -1
  99. package/dist/css/reset.css +7 -0
  100. package/dist/style.css +1 -1
  101. package/dist/tide-design-system.cjs +2 -2
  102. package/dist/tide-design-system.esm.d.ts +93 -27
  103. package/dist/tide-design-system.esm.js +869 -816
  104. package/dist/utilities/event.ts +4 -0
  105. package/dist/utilities/storybook.ts +4 -0
  106. package/dist/utilities/viewport.ts +44 -0
  107. package/index.ts +2 -4
  108. package/package.json +4 -1
  109. package/src/assets/css/realm/aero.css +3 -3
  110. package/src/assets/css/realm/boatmart.css +1 -1
  111. package/src/assets/css/realm/cycle.css +1 -1
  112. package/src/assets/css/realm/equipment.css +1 -1
  113. package/src/assets/css/realm/pwc.css +1 -1
  114. package/src/assets/css/reset.css +7 -0
  115. package/src/assets/svg/icons/IconAcute.svg +3 -0
  116. package/src/assets/svg/icons/IconAutoRenew.svg +3 -0
  117. package/src/assets/svg/icons/IconBolt.svg +3 -0
  118. package/src/assets/svg/icons/IconCallQuality.svg +3 -0
  119. package/src/assets/svg/icons/IconCarRental.svg +3 -0
  120. package/src/assets/svg/icons/IconContract.svg +3 -0
  121. package/src/assets/svg/icons/IconCrash.svg +3 -0
  122. package/src/assets/svg/icons/IconDashboardCustomize.svg +3 -0
  123. package/src/assets/svg/icons/IconDirectionsBoat.svg +3 -0
  124. package/src/assets/svg/icons/IconDomain.svg +3 -0
  125. package/src/assets/svg/icons/IconEngineering.svg +3 -0
  126. package/src/assets/svg/icons/IconFlag.svg +3 -0
  127. package/src/assets/svg/icons/IconGears.svg +3 -0
  128. package/src/assets/svg/icons/IconGlobeLocationPin.svg +3 -0
  129. package/src/assets/svg/icons/IconHandyman.svg +3 -0
  130. package/src/assets/svg/icons/IconInformation.svg +3 -0
  131. package/src/assets/svg/icons/IconMoneyBag.svg +3 -0
  132. package/src/assets/svg/icons/IconPersonSearch.svg +3 -0
  133. package/src/assets/svg/icons/IconPolicy.svg +3 -0
  134. package/src/assets/svg/icons/IconPower.svg +3 -0
  135. package/src/assets/svg/icons/IconPriorityHigh.svg +3 -0
  136. package/src/assets/svg/icons/IconRequestQuote.svg +3 -0
  137. package/src/assets/svg/icons/IconResetWrench.svg +3 -0
  138. package/src/assets/svg/icons/IconRoad.svg +3 -0
  139. package/src/assets/svg/icons/IconSailing.svg +3 -0
  140. package/src/assets/svg/icons/IconSchool.svg +3 -0
  141. package/src/assets/svg/icons/IconSensors.svg +3 -0
  142. package/src/assets/svg/icons/IconSettings.svg +3 -0
  143. package/src/assets/svg/icons/IconShieldCheck.svg +3 -0
  144. package/src/assets/svg/icons/IconSummarize.svg +3 -0
  145. package/src/assets/svg/icons/IconSwapHoriz.svg +3 -0
  146. package/src/assets/svg/icons/IconVerified.svg +3 -0
  147. package/src/assets/svg/icons/IconWarehouse.svg +3 -0
  148. package/src/assets/svg/icons/IconWorkspacePremium.svg +3 -0
  149. package/src/assets/svg/icons/IconWrench.svg +3 -0
  150. package/src/assets/svg/icons/realm/atv/IconAtvAtv.svg +3 -0
  151. package/src/assets/svg/icons/realm/atv/IconAtvDuneBuggy.svg +3 -0
  152. package/src/assets/svg/icons/realm/atv/IconAtvGoKart.svg +3 -0
  153. package/src/assets/svg/icons/realm/atv/IconAtvGolfCart.svg +3 -0
  154. package/src/assets/svg/icons/realm/atv/IconAtvSandRail.svg +3 -0
  155. package/src/assets/svg/icons/realm/atv/IconAtvSideBySide.svg +3 -0
  156. package/src/assets/svg/icons/realm/atv/IconAtvTrailer.svg +3 -0
  157. package/src/assets/svg/icons/realm/boatmart/IconBoatmartPwc.svg +3 -0
  158. package/src/assets/svg/icons/realm/boatmart/IconBoatmartTrailer.svg +3 -0
  159. package/src/components/TideCard.vue +3 -7
  160. package/src/components/TideInputTextarea.vue +10 -3
  161. package/src/components/TideModal.vue +164 -132
  162. package/src/components/TidePopover.vue +167 -0
  163. package/src/stories/TideAccordionItem.stories.ts +1 -0
  164. package/src/stories/TideButtonSegmented.stories.ts +1 -0
  165. package/src/stories/TideCard.stories.ts +1 -11
  166. package/src/stories/TideCarousel.stories.ts +1 -0
  167. package/src/stories/TideIcon.stories.ts +4 -1
  168. package/src/stories/TideModal.stories.ts +68 -6
  169. package/src/stories/TidePagination.stories.ts +1 -0
  170. package/src/stories/TidePopover.stories.ts +98 -0
  171. package/src/stories/TideSwitch.stories.ts +1 -0
  172. package/src/types/Card.ts +0 -7
  173. package/src/types/Icon.ts +50 -2
  174. package/src/utilities/event.ts +4 -0
  175. package/src/utilities/storybook.ts +4 -0
  176. package/src/utilities/viewport.ts +44 -0
  177. package/dist/IconAlignSpace-5d64a8e4.js +0 -16
  178. package/dist/IconAlignSpace-7ee811f8.cjs +0 -2
  179. package/dist/IconRoundedCorners-53cb2cfc.cjs +0 -2
  180. package/dist/IconRoundedCorners-e21c4321.js +0 -16
  181. /package/src/assets/svg/icons/{IconAlignSpace.svg → IconAlignSpaceEven.svg} +0 -0
  182. /package/src/assets/svg/icons/{IconRoundedCorners.svg → IconRoundedCorner.svg} +0 -0
@@ -1,187 +1,219 @@
1
1
  <script lang="ts" setup>
2
- import { onMounted, ref, watch } from 'vue';
2
+ import { nextTick, onMounted, ref, watch } from 'vue';
3
3
 
4
- import TideIcon from '@/components/TideIcon.vue';
4
+ import TideButtonIcon from '@/components/TideButtonIcon.vue';
5
+ import { BREAKPOINT } from '@/types/Breakpoint';
5
6
  import { ICON } from '@/types/Icon';
7
+ import { PRIORITY } from '@/types/Priority';
6
8
  import { CSS } from '@/types/Styles';
9
+ import { setScrollLock } from '@/utilities/viewport';
10
+
11
+ import type { Ref } from 'vue';
7
12
 
8
13
  type Props = {
14
+ isBackButton?: boolean;
15
+ isDismissible?: boolean;
9
16
  isOpen: boolean;
10
17
  title?: string;
11
18
  width?: string;
12
19
  };
13
20
 
14
- const props = defineProps<Props>();
15
-
16
- const emit = defineEmits(['close']);
17
-
18
- const isOpen = ref(props.isOpen);
19
- const savedScrollPosition = ref<number | null>(null); // TODO: replace body scroll lock with global structure?
20
-
21
- const handleClose = () => {
22
- isOpen.value = false;
21
+ const props = withDefaults(defineProps<Props>(), {
22
+ isBackButton: false,
23
+ isDismissible: true,
24
+ title: undefined,
25
+ width: undefined,
26
+ });
23
27
 
24
- updateModalDisplay();
25
- emit('close');
28
+ type Emits = {
29
+ (e: 'close'): void;
30
+ (e: 'back'): void;
26
31
  };
27
32
 
28
- const updateModalDisplay = () => {
29
- if (isOpen.value) {
30
- savedScrollPosition.value = window.scrollY;
31
- document.body.style.overflow = 'hidden';
32
- savedScrollPosition.value && window.scrollTo(0, savedScrollPosition.value);
33
+ const emit = defineEmits<Emits>();
33
34
 
34
- addOpenListeners();
35
- } else {
36
- document.body.style.overflow = '';
35
+ const modalContent: Ref<HTMLDivElement | undefined> = ref();
36
+ const modalDialog: Ref<HTMLDialogElement | undefined> = ref();
37
37
 
38
- removeOpenListeners();
39
- }
38
+ const triggerNativeDialogOpen = () => {
39
+ modalDialog.value?.showModal();
40
40
  };
41
41
 
42
- const handleKeyDown = (event: KeyboardEvent) => {
43
- if (event.key === 'Escape') {
44
- event.stopPropagation();
45
- handleClose();
46
- }
47
- };
48
-
49
- const addOpenListeners = () => {
50
- window.addEventListener('keydown', handleKeyDown);
42
+ const triggerNativeDialogClose = () => {
43
+ modalDialog.value?.close();
51
44
  };
52
45
 
53
- const removeOpenListeners = () => {
54
- window.removeEventListener('keydown', handleKeyDown);
46
+ const scrollContentToTop = () => {
47
+ nextTick(() => {
48
+ if (!modalContent.value) return;
49
+ modalContent.value.scrollTop = 0;
50
+ });
55
51
  };
56
52
 
57
- onMounted(() => {
58
- updateModalDisplay();
59
- });
60
-
61
53
  watch(
62
54
  () => props.isOpen,
63
55
  (newValue) => {
64
- isOpen.value = newValue;
65
- updateModalDisplay();
56
+ if (!modalDialog.value) return;
57
+ if (newValue) {
58
+ triggerNativeDialogOpen();
59
+ scrollContentToTop();
60
+ } else {
61
+ triggerNativeDialogClose();
62
+ }
63
+ setScrollLock(newValue);
66
64
  }
67
65
  );
66
+
67
+ onMounted(() => {
68
+ if (props.isOpen) {
69
+ triggerNativeDialogOpen();
70
+ }
71
+ });
68
72
  </script>
69
73
 
70
74
  <template>
71
- <Teleport to="body">
75
+ <dialog
76
+ :class="['tide-modal', CSS.BG.INITIAL, CSS.HEIGHT.FULL, CSS.WIDTH.FULL, CSS.OVERFLOW.XY.HIDDEN]"
77
+ ref="modalDialog"
78
+ :style="{ '--modal-width': props.width }"
79
+ @click.self="emit('close')"
80
+ @close="emit('close')"
81
+ >
72
82
  <div
73
83
  :class="[
74
- 'tide-modal-wrapper',
75
- CSS.POSITION.ABSOLUTE,
84
+ 'tide-modal-body',
85
+ CSS.BG.SURFACE.DEFAULT,
86
+ CSS.BORDER.RADIUS.ONE,
76
87
  CSS.DISPLAY.FLEX,
77
- CSS.AXIS1.CENTER,
78
- CSS.AXIS2.CENTER,
79
- CSS.POSITIONING.TOP,
80
- CSS.POSITIONING.LEFT,
88
+ CSS.FLEX.DIRECTION.COLUMN,
89
+ CSS.OVERFLOW.XY.HIDDEN,
90
+ CSS.POSITION.ABSOLUTE,
91
+ CSS.POSITIONING.BOTTOM,
92
+ CSS.SHADOW.TOP,
81
93
  CSS.WIDTH.FULL,
82
- CSS.HEIGHT.FULL,
83
- props.isOpen ? 'active' : CSS.POINTER_EVENTS.OFF,
84
- CSS.OVERFLOW.Y.HIDDEN,
94
+ CSS.WIDTH.MAX_FULL,
95
+ CSS.withBreakpoint([CSS.SHADOW.BOTTOM], BREAKPOINT.SM),
85
96
  ]"
86
97
  >
87
- <div
88
- :class="['tide-modal-bg', CSS.POSITION.ABSOLUTE, CSS.WIDTH.FULL, CSS.HEIGHT.FULL]"
89
- :style="{ '--tide-modal-width': props.width }"
90
- @click.self="handleClose"
91
- />
98
+ <header
99
+ :class="[
100
+ 'tide-modal-header',
101
+ CSS.DISPLAY.FLEX,
102
+ CSS.AXIS2.CENTER,
103
+ CSS.GAP.HALF,
104
+ CSS.PADDING.Y.ONE,
105
+ CSS.BORDER.BOTTOM.ONE,
106
+ CSS.BORDER.COLOR.LOW,
107
+ ]"
108
+ >
109
+ <TideButtonIcon
110
+ :icon="ICON.CHEVRON_LEFT"
111
+ :priority="PRIORITY.QUATERNARY"
112
+ @click="emit('back')"
113
+ title="Back"
114
+ v-if="isBackButton"
115
+ />
116
+
117
+ <div
118
+ :class="[CSS.FONT.ROLE.HEADLINE_2]"
119
+ v-text="title"
120
+ />
121
+
122
+ <TideButtonIcon
123
+ :class="[CSS.FLEX.GROW.OFF, CSS.FLEX.SHRINK.OFF, CSS.MARGIN.LEFT.AUTO]"
124
+ :icon="ICON.CLOSE"
125
+ :priority="PRIORITY.QUATERNARY"
126
+ @click="triggerNativeDialogClose"
127
+ v-if="isDismissible"
128
+ />
129
+ </header>
92
130
 
93
131
  <div
94
132
  :class="[
95
- 'tide-modal',
96
- CSS.BG.SURFACE.DEFAULT,
97
- CSS.FONT.COLOR.SURFACE.DEFAULT,
98
- CSS.POSITION.ABSOLUTE,
99
- CSS.DISPLAY.FLEX,
100
- CSS.FLEX.DIRECTION.COLUMN,
101
- CSS.BORDER.RADIUS.ONE,
102
- CSS.OVERFLOW.XY.HIDDEN,
103
- CSS.SHADOW.BOTTOM,
133
+ 'tide-modal-content',
134
+ CSS.DISPLAY.GRID,
135
+ CSS.OVERFLOW.Y.AUTO,
136
+ CSS.OVERFLOW.X.HIDDEN,
137
+ CSS.WIDTH.FULL,
138
+ CSS.PADDING.Y.TWO,
104
139
  ]"
140
+ ref="modalContent"
105
141
  >
106
- <header
107
- :class="[
108
- 'tide-modal-header',
109
- CSS.POSITION.RELATIVE,
110
- CSS.DISPLAY.FLEX,
111
- CSS.AXIS2.CENTER,
112
- CSS.PADDING.Y.ONE,
113
- CSS.PADDING.X.TWO,
114
- CSS.BORDER.BOTTOM.ONE,
115
- CSS.BORDER.COLOR.LOW,
116
- ]"
117
- >
118
- <div
119
- :class="[CSS.FLEX.GROW.ON, CSS.FONT.SIZE.TWENTY, CSS.FONT.WEIGHT.SEVEN_HUNDRED]"
120
- v-if="title"
121
- >
122
- {{ title }}
123
- </div>
124
-
125
- <button
126
- :class="[CSS.POSITION.ABSOLUTE, CSS.POSITIONING.RIGHT, CSS.MARGIN.RIGHT.TWO]"
127
- @click="handleClose"
128
- title="Close"
129
- >
130
- <TideIcon :icon="ICON.CLOSE" />
131
- </button>
132
- </header>
133
-
134
- <div :class="['tide-modal-content', CSS.PADDING.FULL.TWO, CSS.OVERFLOW.Y.AUTO]">
135
- <slot />
136
- </div>
137
-
138
- <template v-if="$slots.footer">
139
- <footer
140
- :class="[
141
- 'tide-bg-surface',
142
- CSS.POSITION.STICKY,
143
- CSS.POSITIONING.LEFT,
144
- CSS.POSITIONING.BOTTOM,
145
- CSS.DISPLAY.FLEX,
146
- CSS.AXIS1.END,
147
- CSS.GAP.ONE,
148
- CSS.PADDING.X.TWO,
149
- CSS.PADDING.Y.ONE,
150
- CSS.SHADOW.TOP,
151
- ]"
152
- >
153
- <slot name="footer" />
154
- </footer>
155
- </template>
142
+ <slot />
156
143
  </div>
144
+
145
+ <footer
146
+ :class="['tide-modal-footer', CSS.AXIS1.END, CSS.DISPLAY.FLEX, CSS.GAP.TWO, CSS.PADDING.Y.ONE, CSS.SHADOW.TOP]"
147
+ v-if="$slots.footer"
148
+ >
149
+ <slot name="footer" />
150
+ </footer>
157
151
  </div>
158
- </Teleport>
152
+ </dialog>
159
153
  </template>
160
154
 
161
155
  <style scoped>
162
156
  .tide-modal {
163
- width: var(--tide-modal-width);
164
- max-width: calc(100% - 2rem);
165
- max-height: calc(100% - 2rem);
166
- box-shadow: 0px 10px 25px 0px rgba(18, 43, 58, 0.2);
167
- transform: translateY(100vh);
168
- transition: var(--tide-animate);
169
- transition-property: transform;
157
+ --modal-padding-x: 20px;
158
+ max-width: unset;
159
+ max-height: unset;
160
+ place-items: center;
161
+ display: none;
162
+ transition: all var(--tide-animate) allow-discrete;
163
+ transform: translateY(100%);
170
164
  }
171
-
172
- .tide-modal-bg {
165
+ .tide-modal:open {
166
+ display: grid;
167
+ transform: translateY(0%);
168
+ }
169
+ .tide-modal::backdrop {
170
+ background-color: transparent;
171
+ transition: all var(--tide-animate) allow-discrete;
173
172
  backdrop-filter: blur(0px);
174
- background-color: rgba(255, 255, 255, 0);
175
- transition: var(--tide-animate);
176
- transition-property: backdrop-filter, background-color;
173
+ }
174
+ .tide-modal:open::backdrop {
175
+ background-color: var(--tide-transparent-400);
176
+ backdrop-filter: blur(4px);
177
+ }
178
+ @starting-style {
179
+ .tide-modal:open {
180
+ display: grid;
181
+ transform: translateY(100%);
182
+ }
183
+ .tide-modal:open::backdrop {
184
+ background-color: transparent;
185
+ backdrop-filter: blur(4px);
186
+ }
187
+ }
188
+ .tide-modal-body {
189
+ width: var(--modal-width, 576px);
190
+ max-height: calc(100% - var(--tide-spacing-2));
177
191
  }
178
192
 
179
- .active .tide-modal {
180
- transform: translateY(0);
193
+ .tide-modal-header,
194
+ .tide-modal-footer {
195
+ padding-inline: var(--modal-padding-x);
196
+ }
197
+ .tide-modal-content {
198
+ grid-template-columns: var(--modal-padding-x) 1fr var(--modal-padding-x);
199
+ }
200
+ :where(.tide-modal-content):deep(> :where(*)) {
201
+ grid-column: 2;
181
202
  }
182
203
 
183
- .active .tide-modal-bg {
184
- backdrop-filter: blur(8px);
185
- background-color: var(--tide-transparent-400);
204
+ @media (max-width: 767px) {
205
+ .tide-modal-body {
206
+ border-bottom-left-radius: 0;
207
+ border-bottom-right-radius: 0;
208
+ }
209
+ }
210
+ @media (min-width: 768px) {
211
+ .tide-modal {
212
+ --modal-padding-x: var(--tide-spacing-2);
213
+ }
214
+ .tide-modal-body {
215
+ max-height: 85%;
216
+ bottom: initial;
217
+ }
186
218
  }
187
219
  </style>
@@ -0,0 +1,167 @@
1
+ <script lang="ts" setup>
2
+ import { autoPlacement, autoUpdate, offset as offsetMiddleware, shift, useFloating } from '@floating-ui/vue';
3
+ import { computed, onBeforeMount, onMounted, onUnmounted, ref, watch, watchEffect } from 'vue';
4
+
5
+ import { CSS } from '@/types/Styles';
6
+ import { isClickOutside } from '@/utilities/event';
7
+ import { TOP_LAYER_ID, initFauxTopLayer } from '@/utilities/viewport';
8
+
9
+ import type { Ref } from 'vue';
10
+
11
+ type Props = {
12
+ anchorId: string;
13
+ offset?: number;
14
+ };
15
+
16
+ const props = withDefaults(defineProps<Props>(), {
17
+ offset: 16,
18
+ });
19
+
20
+ const anchor: Ref<HTMLElement | null> = ref(null);
21
+ const floating: Ref<HTMLElement | null> = ref(null);
22
+ const root: Ref<HTMLElement | null> = ref(null);
23
+ const isHovered = ref(false);
24
+ const isToggledOpen = ref(false);
25
+ const middleware = ref([autoPlacement(), offsetMiddleware({ mainAxis: props.offset }), shift({ padding: 16 })]);
26
+
27
+ const isShowPopover = computed(() => isHovered.value || isToggledOpen.value);
28
+
29
+ const { floatingStyles } = useFloating(anchor, floating, {
30
+ middleware,
31
+ strategy: 'fixed',
32
+ whileElementsMounted: autoUpdate,
33
+ });
34
+
35
+ const handlePermanentOpenBodyClick = (e: MouseEvent) => {
36
+ if (!anchor.value || !floating.value) return;
37
+ if (isClickOutside(e, [anchor.value, floating.value])) {
38
+ e.stopImmediatePropagation();
39
+ isToggledOpen.value = false;
40
+ }
41
+ };
42
+
43
+ const handleAnchorElementMouseOver = () => {
44
+ isHovered.value = true;
45
+ };
46
+
47
+ const handleAnchorElementMouseLeave = () => {
48
+ isHovered.value = false;
49
+ };
50
+
51
+ const handleAnchorElementClick = () => {
52
+ isToggledOpen.value = true;
53
+ };
54
+
55
+ const addListenersToAnchorElement = () => {
56
+ if (!anchor.value) return null;
57
+ anchor.value.addEventListener('mouseover', handleAnchorElementMouseOver);
58
+ anchor.value.addEventListener('mouseleave', handleAnchorElementMouseLeave);
59
+ anchor.value.addEventListener('click', handleAnchorElementClick);
60
+ };
61
+
62
+ const removeListenersFromAnchorElement = () => {
63
+ if (!anchor.value) return null;
64
+ anchor.value.removeEventListener('mouseover', handleAnchorElementMouseOver);
65
+ anchor.value.removeEventListener('mouseleave', handleAnchorElementMouseLeave);
66
+ anchor.value.removeEventListener('click', handleAnchorElementClick);
67
+ };
68
+
69
+ const handlePermanentOpenBodyKeydown = (e: KeyboardEvent) => {
70
+ if (e.key === 'Escape') {
71
+ isToggledOpen.value = false;
72
+ }
73
+ };
74
+
75
+ const addOpenListenersToRoot = () => {
76
+ if (!root.value) return;
77
+ root.value.addEventListener('click', handlePermanentOpenBodyClick, true);
78
+ root.value.addEventListener('keydown', handlePermanentOpenBodyKeydown);
79
+ };
80
+
81
+ const removeOpenListenersFromRoot = () => {
82
+ if (!root.value) return;
83
+ root.value.removeEventListener('click', handlePermanentOpenBodyClick, true);
84
+ root.value.removeEventListener('keydown', handlePermanentOpenBodyKeydown);
85
+ };
86
+
87
+ const updateAnchorElement = () => {
88
+ anchor.value = document.getElementById(props.anchorId);
89
+ };
90
+
91
+ watch(
92
+ () => isToggledOpen.value,
93
+ (newValue) => {
94
+ if (newValue) {
95
+ addOpenListenersToRoot();
96
+ } else {
97
+ removeOpenListenersFromRoot();
98
+ }
99
+ }
100
+ );
101
+
102
+ watch(
103
+ () => props.anchorId,
104
+ () => {
105
+ removeListenersFromAnchorElement();
106
+ updateAnchorElement();
107
+ addListenersToAnchorElement();
108
+ }
109
+ );
110
+
111
+ watchEffect(() => {
112
+ middleware.value = [autoPlacement(), offsetMiddleware({ mainAxis: props.offset }), shift({ padding: 16 })];
113
+ });
114
+
115
+ onBeforeMount(() => {
116
+ initFauxTopLayer();
117
+ });
118
+
119
+ onMounted(() => {
120
+ updateAnchorElement();
121
+ root.value = document.documentElement;
122
+ addListenersToAnchorElement();
123
+ });
124
+
125
+ onUnmounted(() => {
126
+ removeListenersFromAnchorElement();
127
+ });
128
+ </script>
129
+
130
+ <template>
131
+ <Teleport :to="`#${TOP_LAYER_ID}`">
132
+ <Transition>
133
+ <div
134
+ :class="[
135
+ 'tide-popover',
136
+ CSS.BG.SURFACE.DEFAULT,
137
+ CSS.BORDER.COLOR.LOW,
138
+ CSS.BORDER.FULL.ONE,
139
+ CSS.BORDER.RADIUS.HALF,
140
+ CSS.FONT.ROLE.BODY_2,
141
+ CSS.PADDING.FULL.ONE,
142
+ CSS.SHADOW.BOTTOM,
143
+ ]"
144
+ ref="floating"
145
+ :style="{ ...floatingStyles, maxWidth: `calc(100% - ${props.offset * 2}px)` }"
146
+ v-show="isShowPopover"
147
+ >
148
+ <slot />
149
+ </div>
150
+ </Transition>
151
+ </Teleport>
152
+ </template>
153
+
154
+ <style scoped>
155
+ .v-enter-from,
156
+ .v-leave-to {
157
+ opacity: 0;
158
+ }
159
+ .v-enter-active,
160
+ .v-leave-active {
161
+ transition: opacity var(--tide-animate);
162
+ }
163
+ .v-enter-to,
164
+ .v-leave-from {
165
+ opacity: 1;
166
+ }
167
+ </style>
@@ -43,6 +43,7 @@ export default {
43
43
  isEmit: true,
44
44
  name: 'toggle',
45
45
  table: {
46
+ category: 'Events',
46
47
  defaultValue: { summary: 'None' },
47
48
  type: { summary: '(isExpanded: boolean) => void' },
48
49
  },
@@ -68,6 +68,7 @@ export default {
68
68
  handleChange: {
69
69
  ...change,
70
70
  table: {
71
+ category: 'Events',
71
72
  defaultValue: { summary: 'None' },
72
73
  type: { summary: '(tabIndex: number) => void' },
73
74
  },
@@ -1,7 +1,7 @@
1
1
  import { action } from '@storybook/addon-actions';
2
2
 
3
3
  import TideCard from '@/components/TideCard.vue';
4
- import { POSITION_CARD_ICON as STANDARD_POSITION_CARD_ICON, TYPE_CARD as STANDARD_TYPE_CARD } from '@/types/Card';
4
+ import { TYPE_CARD as STANDARD_TYPE_CARD } from '@/types/Card';
5
5
  import { ICON } from '@/types/Icon';
6
6
  import {
7
7
  argTypeBooleanUnrequired,
@@ -13,7 +13,6 @@ import {
13
13
  } from '@/utilities/storybook';
14
14
 
15
15
  const TYPE_CARD = prependNoneAsUndefined(STANDARD_TYPE_CARD);
16
- const POSITION_CARD_ICON = prependNoneAsUndefined(STANDARD_POSITION_CARD_ICON);
17
16
  const CARD_ICON = prependNoneAsUndefined(ICON);
18
17
 
19
18
  const render = (args: any) => ({
@@ -67,14 +66,6 @@ export default {
67
66
  type: { summary: 'Icon' },
68
67
  },
69
68
  },
70
- iconPosition: {
71
- ...formatArgType({ POSITION_CARD_ICON }),
72
- description: 'Position of the icon relative to the content.',
73
- table: {
74
- defaultValue: { summary: 'LEFT' },
75
- type: { summary: 'CardIconPosition' },
76
- },
77
- },
78
69
  selected: {
79
70
  ...argTypeBooleanUnrequired,
80
71
  description: 'Determines whether the Card is selected (for selectable cards).',
@@ -93,7 +84,6 @@ export default {
93
84
  description: '',
94
85
  heading: 'Demo',
95
86
  icon: CARD_ICON.None,
96
- iconPosition: POSITION_CARD_ICON.None,
97
87
  selected: undefined,
98
88
  type: TYPE_CARD.None,
99
89
  },
@@ -75,6 +75,7 @@ export default {
75
75
  handleChange: {
76
76
  ...change,
77
77
  table: {
78
+ category: 'Events',
78
79
  defaultValue: { summary: 'None' },
79
80
  type: { summary: '(currentSlide: number) => void' },
80
81
  },
@@ -3,7 +3,10 @@ import { ICON as STANDARD_ICON, ICON_REALM as STANDARD_ICON_REALM } from '@/type
3
3
  import * as STANDARD_SIZE from '@/types/Size';
4
4
  import { formatArgType, parameters, prependNoneAsUndefined } from '@/utilities/storybook';
5
5
 
6
- const ICON = prependNoneAsUndefined({ ...STANDARD_ICON, ...STANDARD_ICON_REALM.RV });
6
+ const ICON = prependNoneAsUndefined({
7
+ ...STANDARD_ICON,
8
+ ...Object.assign({}, ...Object.values(STANDARD_ICON_REALM)),
9
+ });
7
10
  const SIZE = prependNoneAsUndefined(STANDARD_SIZE.SIZE);
8
11
 
9
12
  export default {