@teammodlr/ui-kit 3.0.0-beta.9 → 3.0.1

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 (110) hide show
  1. package/lib/components/cardcontextmenu/CardContextMenu.vue.d.ts +4 -2
  2. package/lib/components/checkbox/Checkbox.vue.d.ts +4 -4
  3. package/lib/components/clearbutton/ClearButton.vue.d.ts +9 -0
  4. package/lib/components/clearbutton/style/ClearButton.css.d.ts +2 -0
  5. package/lib/components/clearbutton/style/index.d.ts +1 -0
  6. package/lib/components/colorpicker/ColorPicker.vue.d.ts +6 -4
  7. package/lib/components/colorpicker/style/ColorPicker.css.d.ts +1 -0
  8. package/lib/components/dialog/Dialog.vue.d.ts +14 -6
  9. package/lib/components/dialog/style/Dialog.css.d.ts +2 -0
  10. package/lib/components/drawer/Drawer.vue.d.ts +146 -0
  11. package/lib/components/drawer/style/Drawer.css.d.ts +33 -0
  12. package/lib/components/drawer/style/index.d.ts +1 -0
  13. package/lib/components/dropdown/Dropdown.vue.d.ts +4 -2
  14. package/lib/components/form/Form.vue.d.ts +4 -2
  15. package/lib/components/hovercard/HoverCard.vue.d.ts +124 -0
  16. package/lib/components/hovercard/style/HoverCard.css.d.ts +3 -0
  17. package/lib/components/hovercard/style/index.d.ts +1 -0
  18. package/lib/components/index.d.ts +3 -0
  19. package/lib/components/input/Input.vue.d.ts +20 -5
  20. package/lib/components/input/style/Input.css.d.ts +4 -0
  21. package/lib/components/modal/Modal.vue.d.ts +11 -4
  22. package/lib/components/pagination/Pagination.vue.d.ts +6 -3
  23. package/lib/components/paneldialog/PanelDialog.vue.d.ts +175 -0
  24. package/lib/components/paneldialog/style/PanelDialog.css.d.ts +8 -0
  25. package/lib/components/paneldialog/style/index.d.ts +1 -0
  26. package/lib/components/popover/Popover.vue.d.ts +28 -2
  27. package/lib/components/radio/Radio.vue.d.ts +5 -5
  28. package/lib/components/radiobutton/RadioButton.vue.d.ts +4 -4
  29. package/lib/components/radiobuttongroup/RadioButtonGroup.vue.d.ts +4 -2
  30. package/lib/components/select/Select.vue.d.ts +94 -91
  31. package/lib/components/select/style/Select.css.d.ts +9 -1
  32. package/lib/components/slider/Slider.vue.d.ts +6 -3
  33. package/lib/components/switch/Switch.vue.d.ts +4 -4
  34. package/lib/components/tab/Tab.vue.d.ts +10 -2
  35. package/lib/components/tab/TabGroup.vue.d.ts +6 -3
  36. package/lib/components/table/Table.vue.d.ts +4 -2
  37. package/lib/components/textarea/Textarea.vue.d.ts +16 -5
  38. package/lib/components/textarea/style/Textarea.css.d.ts +2 -0
  39. package/lib/components/tree/Tree.vue.d.ts +6 -3
  40. package/lib/composables/useInput.d.ts +9 -2
  41. package/lib/index.cjs.js +3 -3
  42. package/lib/index.es.js +8526 -6393
  43. package/lib/style.css +1 -1
  44. package/lib/tsconfig.app.tsbuildinfo +1 -0
  45. package/lib/tsconfig.vitest.tsbuildinfo +1 -0
  46. package/package.json +7 -4
  47. package/src/components/cardcontextmenu/CardContextMenu.vue +3 -1
  48. package/src/components/cardcontextmenu/style/Dropdown.css.ts +7 -0
  49. package/src/components/clearbutton/ClearButton.vue +41 -0
  50. package/src/components/clearbutton/style/ClearButton.css.ts +37 -0
  51. package/src/components/clearbutton/style/index.ts +1 -0
  52. package/src/components/colorpicker/ColorPicker.vue +12 -12
  53. package/src/components/colorpicker/examples/basic.vue +8 -17
  54. package/src/components/colorpicker/style/ColorPicker.css.ts +6 -1
  55. package/src/components/dialog/Dialog.vue +12 -6
  56. package/src/components/dialog/style/Dialog.css.ts +30 -0
  57. package/src/components/drawer/Drawer.vue +280 -0
  58. package/src/components/drawer/examples/basic.vue +15 -0
  59. package/src/components/drawer/examples/custom-title.vue +21 -0
  60. package/src/components/drawer/examples/index.stories.ts +24 -0
  61. package/src/components/drawer/examples/index.ts +7 -0
  62. package/src/components/drawer/examples/no-mask.vue +15 -0
  63. package/src/components/drawer/examples/no-outside-close.vue +15 -0
  64. package/src/components/drawer/examples/placements.vue +28 -0
  65. package/src/components/drawer/examples/with-footer.vue +21 -0
  66. package/src/components/drawer/examples/with-trigger.vue +13 -0
  67. package/src/components/drawer/style/Drawer.css.ts +208 -0
  68. package/src/components/drawer/style/index.ts +1 -0
  69. package/src/components/dropdown/Dropdown.vue +4 -2
  70. package/src/components/form/Form.vue +3 -3
  71. package/src/components/hovercard/HoverCard.vue +165 -0
  72. package/src/components/hovercard/style/HoverCard.css.ts +175 -0
  73. package/src/components/hovercard/style/index.ts +1 -0
  74. package/src/components/index.ts +3 -0
  75. package/src/components/input/Input.vue +52 -4
  76. package/src/components/input/examples/clearable.vue +18 -0
  77. package/src/components/input/examples/index.stories.ts +3 -1
  78. package/src/components/input/examples/index.ts +1 -0
  79. package/src/components/input/style/Input.css.ts +19 -0
  80. package/src/components/messageprovider/MessageProvider.vue +33 -7
  81. package/src/components/modal/Modal.vue +28 -11
  82. package/src/components/pagination/Pagination.vue +4 -1
  83. package/src/components/paneldialog/PanelDialog.vue +509 -0
  84. package/src/components/paneldialog/examples/basic.vue +36 -0
  85. package/src/components/paneldialog/examples/index.stories.ts +22 -0
  86. package/src/components/paneldialog/examples/index.ts +2 -0
  87. package/src/components/paneldialog/examples/modal.vue +31 -0
  88. package/src/components/paneldialog/style/PanelDialog.css.ts +90 -0
  89. package/src/components/paneldialog/style/index.ts +1 -0
  90. package/src/components/popover/Popover.vue +9 -2
  91. package/src/components/radiobuttongroup/RadioButtonGroup.vue +3 -1
  92. package/src/components/select/Select.vue +211 -111
  93. package/src/components/select/examples/clearable.vue +18 -0
  94. package/src/components/select/examples/fill.vue +52 -0
  95. package/src/components/select/examples/index.stories.ts +4 -1
  96. package/src/components/select/examples/index.ts +3 -0
  97. package/src/components/select/examples/remote.vue +54 -0
  98. package/src/components/select/style/Select.css.ts +140 -24
  99. package/src/components/slider/Slider.vue +4 -1
  100. package/src/components/tab/Tab.vue +4 -2
  101. package/src/components/tab/TabGroup.vue +4 -4
  102. package/src/components/tab/style/Tab.css.ts +6 -0
  103. package/src/components/table/Table.vue +3 -3
  104. package/src/components/textarea/Textarea.vue +40 -4
  105. package/src/components/textarea/examples/clearable.vue +16 -0
  106. package/src/components/textarea/examples/index.stories.ts +2 -1
  107. package/src/components/textarea/examples/index.ts +1 -0
  108. package/src/components/textarea/style/Textarea.css.ts +10 -0
  109. package/src/components/tree/Tree.vue +4 -1
  110. package/src/composables/useInput.ts +10 -2
@@ -0,0 +1,24 @@
1
+ import MDrawer from '../Drawer.vue';
2
+ import {
3
+ MDrawerBasic,
4
+ MDrawerPlacements,
5
+ MDrawerNoMask,
6
+ MDrawerWithFooter,
7
+ MDrawerCustomTitle,
8
+ MDrawerWithTrigger,
9
+ MDrawerNoOutsideClose
10
+ } from './index';
11
+ import { createStory } from '../../../__storybook__';
12
+
13
+ export default {
14
+ component: MDrawer,
15
+ title: 'Components/Drawer'
16
+ };
17
+
18
+ export const Basic = createStory(MDrawerBasic);
19
+ export const WithTrigger = createStory(MDrawerWithTrigger);
20
+ export const Placements = createStory(MDrawerPlacements);
21
+ export const NoMask = createStory(MDrawerNoMask);
22
+ export const WithFooter = createStory(MDrawerWithFooter);
23
+ export const CustomTitle = createStory(MDrawerCustomTitle);
24
+ export const NoOutsideClose = createStory(MDrawerNoOutsideClose);
@@ -0,0 +1,7 @@
1
+ export { default as MDrawerBasic } from './basic.vue';
2
+ export { default as MDrawerPlacements } from './placements.vue';
3
+ export { default as MDrawerNoMask } from './no-mask.vue';
4
+ export { default as MDrawerWithFooter } from './with-footer.vue';
5
+ export { default as MDrawerCustomTitle } from './custom-title.vue';
6
+ export { default as MDrawerWithTrigger } from './with-trigger.vue';
7
+ export { default as MDrawerNoOutsideClose } from './no-outside-close.vue';
@@ -0,0 +1,15 @@
1
+ <template>
2
+ <MDrawer v-model:open="isOpen" title="No Backdrop" :show-mask="false">
3
+ <p style="margin: 0">This drawer has no backdrop overlay.</p>
4
+ </MDrawer>
5
+
6
+ <MButton @click="isOpen = true">Open (no backdrop)</MButton>
7
+ </template>
8
+
9
+ <script lang="ts" setup>
10
+ import { ref } from 'vue';
11
+ import MDrawer from '../Drawer.vue';
12
+ import MButton from '../../button/Button.vue';
13
+
14
+ const isOpen = ref(false);
15
+ </script>
@@ -0,0 +1,15 @@
1
+ <template>
2
+ <MDrawer ref="drawer" title="Persistent Drawer" :outside-close="false">
3
+ <p style="margin: 0">Clicking outside won't close this drawer. Use the × button or Escape.</p>
4
+ </MDrawer>
5
+
6
+ <MButton @click="drawer?.open()">Open Drawer</MButton>
7
+ </template>
8
+
9
+ <script lang="ts" setup>
10
+ import { ref } from 'vue';
11
+ import MDrawer from '../Drawer.vue';
12
+ import MButton from '../../button/Button.vue';
13
+
14
+ const drawer = ref<InstanceType<typeof MDrawer> | null>(null);
15
+ </script>
@@ -0,0 +1,28 @@
1
+ <template>
2
+ <MDrawer v-model:open="isOpen" :placement="activePlacement" :title="`Drawer — ${activePlacement}`">
3
+ <p style="margin: 0">Sliding in from the <strong>{{ activePlacement }}</strong>.</p>
4
+ </MDrawer>
5
+
6
+ <div style="display: flex; gap: 8px; flex-wrap: wrap">
7
+ <MButton @click="open('left')">Left</MButton>
8
+ <MButton @click="open('right')">Right</MButton>
9
+ <MButton @click="open('top')">Top</MButton>
10
+ <MButton @click="open('bottom')">Bottom</MButton>
11
+ </div>
12
+ </template>
13
+
14
+ <script lang="ts" setup>
15
+ import { ref } from 'vue';
16
+ import MDrawer from '../Drawer.vue';
17
+ import MButton from '../../button/Button.vue';
18
+
19
+ type Placement = 'left' | 'right' | 'top' | 'bottom';
20
+
21
+ const isOpen = ref(false);
22
+ const activePlacement = ref<Placement>('right');
23
+
24
+ function open(placement: Placement) {
25
+ activePlacement.value = placement;
26
+ isOpen.value = true;
27
+ }
28
+ </script>
@@ -0,0 +1,21 @@
1
+ <template>
2
+ <MDrawer v-model:open="isOpen" title="Drawer with Footer">
3
+ <p style="margin: 0">Drawer body content goes here.</p>
4
+ <template #footer>
5
+ <div style="display: flex; gap: 8px; justify-content: flex-end; width: 100%">
6
+ <MButton @click="isOpen = false">Cancel</MButton>
7
+ <MButton variant="primary" @click="isOpen = false">Save</MButton>
8
+ </div>
9
+ </template>
10
+ </MDrawer>
11
+
12
+ <MButton @click="isOpen = true">Open Drawer</MButton>
13
+ </template>
14
+
15
+ <script lang="ts" setup>
16
+ import { ref } from 'vue';
17
+ import MDrawer from '../Drawer.vue';
18
+ import MButton from '../../button/Button.vue';
19
+
20
+ const isOpen = ref(false);
21
+ </script>
@@ -0,0 +1,13 @@
1
+ <template>
2
+ <MDrawer title="Profile">
3
+ <template #trigger>
4
+ <MButton>Edit profile</MButton>
5
+ </template>
6
+ <p style="margin: 0">Trigger slot — no ref or v-model needed.</p>
7
+ </MDrawer>
8
+ </template>
9
+
10
+ <script lang="ts" setup>
11
+ import MDrawer from '../Drawer.vue';
12
+ import MButton from '../../button/Button.vue';
13
+ </script>
@@ -0,0 +1,208 @@
1
+ import {
2
+ globalKeyframes,
3
+ style,
4
+ styleVariants
5
+ } from '@vanilla-extract/css';
6
+ import { themeVars } from '../../../styles/theme.css';
7
+
8
+ export const mask = style({
9
+ position: 'fixed',
10
+ top: 0,
11
+ insetInlineEnd: 0,
12
+ bottom: 0,
13
+ insetInlineStart: 0,
14
+ height: '100%',
15
+ backgroundColor: 'hsla(0,0%,45.1%,0.7)'
16
+ });
17
+
18
+ const drawerBase = {
19
+ position: 'fixed' as const,
20
+ backgroundColor: '#fff',
21
+ display: 'flex',
22
+ flexDirection: 'column' as const,
23
+ boxShadow:
24
+ '0 6px 16px 0 rgba(0,0,0,0.08), 0 3px 6px -4px rgba(0,0,0,0.12), 0 9px 28px 8px rgba(0,0,0,0.05)',
25
+ selectors: {
26
+ '&:focus': { outline: 'none' },
27
+ '&:focus-visible': { outline: 'none' }
28
+ }
29
+ };
30
+
31
+ export const placement = styleVariants({
32
+ left: {
33
+ ...drawerBase,
34
+ top: 0,
35
+ left: 0,
36
+ bottom: 0,
37
+ height: '100%'
38
+ },
39
+ right: {
40
+ ...drawerBase,
41
+ top: 0,
42
+ right: 0,
43
+ bottom: 0,
44
+ height: '100%'
45
+ },
46
+ top: {
47
+ ...drawerBase,
48
+ top: 0,
49
+ left: 0,
50
+ right: 0,
51
+ width: '100%'
52
+ },
53
+ bottom: {
54
+ ...drawerBase,
55
+ bottom: 0,
56
+ left: 0,
57
+ right: 0,
58
+ width: '100%'
59
+ }
60
+ });
61
+
62
+ const DURATION = '0.28s';
63
+ const TIMING = 'cubic-bezier(0.16, 1, 0.3, 1)';
64
+
65
+ const SLIDE_LEFT_IN = 'drawer-slide-left-in';
66
+ const SLIDE_LEFT_OUT = 'drawer-slide-left-out';
67
+ const SLIDE_RIGHT_IN = 'drawer-slide-right-in';
68
+ const SLIDE_RIGHT_OUT = 'drawer-slide-right-out';
69
+ const SLIDE_TOP_IN = 'drawer-slide-top-in';
70
+ const SLIDE_TOP_OUT = 'drawer-slide-top-out';
71
+ const SLIDE_BOTTOM_IN = 'drawer-slide-bottom-in';
72
+ const SLIDE_BOTTOM_OUT = 'drawer-slide-bottom-out';
73
+
74
+ globalKeyframes(SLIDE_LEFT_IN, {
75
+ from: { transform: 'translateX(-100%)' },
76
+ to: { transform: 'translateX(0)' }
77
+ });
78
+ globalKeyframes(SLIDE_LEFT_OUT, {
79
+ from: { transform: 'translateX(0)' },
80
+ to: { transform: 'translateX(-100%)' }
81
+ });
82
+ globalKeyframes(SLIDE_RIGHT_IN, {
83
+ from: { transform: 'translateX(100%)' },
84
+ to: { transform: 'translateX(0)' }
85
+ });
86
+ globalKeyframes(SLIDE_RIGHT_OUT, {
87
+ from: { transform: 'translateX(0)' },
88
+ to: { transform: 'translateX(100%)' }
89
+ });
90
+ globalKeyframes(SLIDE_TOP_IN, {
91
+ from: { transform: 'translateY(-100%)' },
92
+ to: { transform: 'translateY(0)' }
93
+ });
94
+ globalKeyframes(SLIDE_TOP_OUT, {
95
+ from: { transform: 'translateY(0)' },
96
+ to: { transform: 'translateY(-100%)' }
97
+ });
98
+ globalKeyframes(SLIDE_BOTTOM_IN, {
99
+ from: { transform: 'translateY(100%)' },
100
+ to: { transform: 'translateY(0)' }
101
+ });
102
+ globalKeyframes(SLIDE_BOTTOM_OUT, {
103
+ from: { transform: 'translateY(0)' },
104
+ to: { transform: 'translateY(100%)' }
105
+ });
106
+
107
+ export const slideTransition = {
108
+ left: {
109
+ enterActive: style({ animation: `${DURATION} ${SLIDE_LEFT_IN} ${TIMING}`, willChange: 'transform' }),
110
+ leaveActive: style({ animation: `${DURATION} ${SLIDE_LEFT_OUT} ${TIMING}`, willChange: 'transform' }),
111
+ enterFrom: style({ transform: 'translateX(-100%)' }),
112
+ leaveTo: style({ transform: 'translateX(-100%)' })
113
+ },
114
+ right: {
115
+ enterActive: style({ animation: `${DURATION} ${SLIDE_RIGHT_IN} ${TIMING}`, willChange: 'transform' }),
116
+ leaveActive: style({ animation: `${DURATION} ${SLIDE_RIGHT_OUT} ${TIMING}`, willChange: 'transform' }),
117
+ enterFrom: style({ transform: 'translateX(100%)' }),
118
+ leaveTo: style({ transform: 'translateX(100%)' })
119
+ },
120
+ top: {
121
+ enterActive: style({ animation: `${DURATION} ${SLIDE_TOP_IN} ${TIMING}`, willChange: 'transform' }),
122
+ leaveActive: style({ animation: `${DURATION} ${SLIDE_TOP_OUT} ${TIMING}`, willChange: 'transform' }),
123
+ enterFrom: style({ transform: 'translateY(-100%)' }),
124
+ leaveTo: style({ transform: 'translateY(-100%)' })
125
+ },
126
+ bottom: {
127
+ enterActive: style({ animation: `${DURATION} ${SLIDE_BOTTOM_IN} ${TIMING}`, willChange: 'transform' }),
128
+ leaveActive: style({ animation: `${DURATION} ${SLIDE_BOTTOM_OUT} ${TIMING}`, willChange: 'transform' }),
129
+ enterFrom: style({ transform: 'translateY(100%)' }),
130
+ leaveTo: style({ transform: 'translateY(100%)' })
131
+ }
132
+ };
133
+
134
+ export const header = style({
135
+ display: 'flex',
136
+ justifyContent: 'space-between',
137
+ alignItems: 'center',
138
+ padding: `${themeVars.space[4]} ${themeVars.space[4]} 0 ${themeVars.space[4]}`,
139
+ flexShrink: '0'
140
+ });
141
+
142
+ export const title = style({
143
+ margin: 0,
144
+ fontSize: `${themeVars.fontSize.xlarge}`,
145
+ fontWeight: '500',
146
+ color: themeVars.color.font.default
147
+ });
148
+
149
+ export const close = style({
150
+ outline: 'none',
151
+ display: 'inline-flex',
152
+ alignItems: 'center',
153
+ justifyContent: 'center',
154
+ width: '1.5rem',
155
+ height: '1.5rem',
156
+ backgroundColor: 'transparent',
157
+ border: 'none',
158
+ padding: 0,
159
+ cursor: 'pointer',
160
+ borderRadius: themeVars.rounded.full,
161
+ position: 'relative',
162
+ transition: 'background-color 0.2s',
163
+ ':hover': {
164
+ backgroundColor: themeVars.color.gray[150]
165
+ },
166
+ ':focus-visible': {
167
+ outline: 'none',
168
+ backgroundColor: themeVars.color.gray[150]
169
+ },
170
+ '::before': {
171
+ content: '""',
172
+ position: 'absolute',
173
+ top: '50%',
174
+ left: '50%',
175
+ transform: 'translate(-50%, -50%) rotate(45deg)',
176
+ width: '0.8rem',
177
+ height: '2px',
178
+ backgroundColor: themeVars.color.gray[500],
179
+ transformOrigin: 'center'
180
+ },
181
+ '::after': {
182
+ content: '""',
183
+ position: 'absolute',
184
+ top: '50%',
185
+ left: '50%',
186
+ transform: 'translate(-50%, -50%) rotate(-45deg)',
187
+ width: '0.8rem',
188
+ height: '2px',
189
+ backgroundColor: themeVars.color.gray[500],
190
+ transformOrigin: 'center'
191
+ }
192
+ });
193
+
194
+ export const body = style({
195
+ padding: themeVars.space[4],
196
+ display: 'flex',
197
+ flexDirection: 'column',
198
+ flexGrow: '1',
199
+ overflowY: 'auto',
200
+ color: themeVars.color.font.text
201
+ });
202
+
203
+ export const footer = style({
204
+ display: 'flex',
205
+ alignItems: 'center',
206
+ padding: `0 ${themeVars.space[4]} ${themeVars.space[4]} ${themeVars.space[4]}`,
207
+ flexShrink: '0'
208
+ });
@@ -0,0 +1 @@
1
+ export * from './Drawer.css';
@@ -9,7 +9,7 @@
9
9
  </ContextMenuSubContent>
10
10
  </ContextMenuPortal>
11
11
  </ContextMenuSub>
12
- <ContextMenuRoot v-else-if="isContextMenu" v-model:open="isOpen" v-bind="$attrs">
12
+ <ContextMenuRoot v-else-if="isContextMenu" v-model:open="isOpen" :modal="modal" v-bind="$attrs">
13
13
  <ContextMenuTrigger as-child>
14
14
  <slot />
15
15
  </ContextMenuTrigger>
@@ -173,7 +173,9 @@ export default defineComponent({
173
173
  type: Number
174
174
  }
175
175
  },
176
- emits: ['update:open'],
176
+ emits: {
177
+ 'update:open': (_value: boolean) => true
178
+ },
177
179
  setup(props, { emit }) {
178
180
  const parentState = inject(DROPDOWN_STATE, null);
179
181
  const isSubmenu = computed(() => !!parentState);
@@ -51,9 +51,9 @@ export default defineComponent({
51
51
  type: String
52
52
  },
53
53
  },
54
- emits: [
55
- "submit"
56
- ],
54
+ emits: {
55
+ submit: (_valid: boolean) => true
56
+ },
57
57
  expose: ["submit"],
58
58
  setup(props, { emit }) {
59
59
  const inputs: Form[] = [];
@@ -0,0 +1,165 @@
1
+ <template>
2
+ <HoverCardRoot
3
+ v-model:open="isOpen"
4
+ :open-delay="openDelay"
5
+ :close-delay="closeDelay"
6
+ >
7
+ <HoverCardTrigger as-child>
8
+ <slot />
9
+ </HoverCardTrigger>
10
+ <HoverCardPortal :disabled="!transfer">
11
+ <HoverCardContent
12
+ :class="contentClasses"
13
+ :side="resolvedSide"
14
+ :align="resolvedAlign"
15
+ :side-offset="sideOffset"
16
+ :collision-padding="collisionPadding"
17
+ :style="{ zIndex: zIndex }"
18
+ >
19
+ <slot name="content" />
20
+ <HoverCardArrow v-if="showArrow" :class="style.arrow" :width="12" :height="6" />
21
+ </HoverCardContent>
22
+ </HoverCardPortal>
23
+ </HoverCardRoot>
24
+ </template>
25
+
26
+ <script lang="ts">
27
+ import {
28
+ HoverCardArrow,
29
+ HoverCardContent,
30
+ HoverCardPortal,
31
+ HoverCardRoot,
32
+ HoverCardTrigger,
33
+ type HoverCardContentProps
34
+ } from 'reka-ui';
35
+ import {
36
+ computed,
37
+ defineComponent,
38
+ ref,
39
+ useAttrs,
40
+ watch,
41
+ type PropType
42
+ } from 'vue';
43
+ import {
44
+ type Placement,
45
+ placementWhileList
46
+ } from '../../composables/usePopper';
47
+ import { useZIndex } from '../../composables/useZIndex';
48
+ import * as style from './style';
49
+
50
+ export default defineComponent({
51
+ name: 'MHoverCard',
52
+ inheritAttrs: false,
53
+ components: {
54
+ HoverCardArrow,
55
+ HoverCardContent,
56
+ HoverCardPortal,
57
+ HoverCardRoot,
58
+ HoverCardTrigger
59
+ },
60
+ props: {
61
+ open: {
62
+ type: Boolean,
63
+ default: false
64
+ },
65
+ placement: {
66
+ type: String as PropType<Placement>,
67
+ default: 'bottom',
68
+ validator: (value: Placement) => placementWhileList.includes(value)
69
+ },
70
+ transfer: {
71
+ type: Boolean,
72
+ default: true
73
+ },
74
+ sideOffset: {
75
+ type: Number,
76
+ default: 5
77
+ },
78
+ collisionPadding: {
79
+ type: [Number, Object] as PropType<number | { top?: number; right?: number; bottom?: number; left?: number }>,
80
+ default: 10
81
+ },
82
+ arrow: {
83
+ type: Boolean,
84
+ default: false
85
+ },
86
+ variant: {
87
+ type: String as PropType<'light' | 'dark'>,
88
+ default: 'light'
89
+ },
90
+ openDelay: {
91
+ type: Number,
92
+ default: 700
93
+ },
94
+ closeDelay: {
95
+ type: Number,
96
+ default: 300
97
+ },
98
+ zIndex: {
99
+ type: Number
100
+ }
101
+ },
102
+ emits: {
103
+ 'update:open': (_value: boolean) => true
104
+ },
105
+ setup(props, { emit }) {
106
+ const attrs = useAttrs();
107
+ const { nextZIndex } = useZIndex();
108
+ const isOpen = ref(props.open);
109
+ const zIndex = ref(props.zIndex ?? nextZIndex());
110
+
111
+ const resolvedPlacement = computed(() => {
112
+ const [side, align] = props.placement.split('-');
113
+
114
+ return {
115
+ side: side === 'top' || side === 'right' || side === 'bottom' || side === 'left'
116
+ ? side
117
+ : undefined,
118
+ align: align === 'start' || align === 'center' || align === 'end'
119
+ ? align
120
+ : undefined
121
+ } as {
122
+ side?: HoverCardContentProps['side'];
123
+ align?: HoverCardContentProps['align'];
124
+ };
125
+ });
126
+
127
+ const showArrow = computed(() => {
128
+ const { align } = resolvedPlacement.value;
129
+ return props.arrow && (!align || align === 'center');
130
+ });
131
+
132
+ const contentClasses = computed(() => {
133
+ return [
134
+ 'm-hover-card-content',
135
+ style.content,
136
+ {
137
+ [style.dark]: props.variant === 'dark'
138
+ },
139
+ attrs.class
140
+ ];
141
+ });
142
+
143
+ watch(
144
+ () => props.open,
145
+ value => {
146
+ isOpen.value = value;
147
+ }
148
+ );
149
+
150
+ watch(isOpen, value => {
151
+ emit('update:open', value);
152
+ });
153
+
154
+ return {
155
+ isOpen,
156
+ zIndex,
157
+ showArrow,
158
+ contentClasses,
159
+ resolvedSide: computed(() => resolvedPlacement.value.side),
160
+ resolvedAlign: computed(() => resolvedPlacement.value.align),
161
+ style
162
+ };
163
+ }
164
+ });
165
+ </script>