@teammodlr/ui-kit 2.3.22 → 3.0.0-beta.2

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 (95) hide show
  1. package/lib/components/avatar/Avatar.vue.d.ts +2 -2
  2. package/lib/components/badge/style/Badge.css.d.ts +2 -2
  3. package/lib/components/button/Button.vue.d.ts +7 -4
  4. package/lib/components/button/style/Button.css.d.ts +3 -3
  5. package/lib/components/cardcontextmenu/CardContextMenu.vue.d.ts +5 -8
  6. package/lib/components/cardcontextmenu/CardContextMenuItem.vue.d.ts +11 -2
  7. package/lib/components/cardcontextmenu/CardContextMenuTriggerProvider.vue.d.ts +2 -0
  8. package/lib/components/cardcontextmenu/style/Dropdown.css.d.ts +2 -1
  9. package/lib/components/cardcontextmenu/symbol.d.ts +1 -0
  10. package/lib/components/checkbox/Checkbox.vue.d.ts +15 -4
  11. package/lib/components/collapse/CollapseItem.vue.d.ts +3 -2
  12. package/lib/components/colorpicker/ColorPicker.vue.d.ts +8 -9
  13. package/lib/components/confirm/Confirm.vue.d.ts +9 -0
  14. package/lib/components/confirm/props.d.ts +4 -0
  15. package/lib/components/dialog/Dialog.vue.d.ts +9 -4
  16. package/lib/components/dialogprovider/DialogProvider.vue.d.ts +1 -0
  17. package/lib/components/dialogprovider/types.d.ts +1 -0
  18. package/lib/components/dropdown/Dropdown.vue.d.ts +3 -1
  19. package/lib/components/dropdown/DropdownItem.vue.d.ts +11 -1
  20. package/lib/components/dropdown/DropdownSeperator.vue.d.ts +1 -0
  21. package/lib/components/dropdown/DropdownTriggerProvider.vue.d.ts +2 -0
  22. package/lib/components/dropdown/style/Dropdown.css.d.ts +2 -1
  23. package/lib/components/dropdown/symbol.d.ts +2 -0
  24. package/lib/components/image/Image.vue.d.ts +1 -1
  25. package/lib/components/messageprovider/Message.vue.d.ts +1 -1
  26. package/lib/components/messageprovider/MessageProvider.vue.d.ts +22 -24
  27. package/lib/components/messageprovider/style/Message.css.d.ts +1 -1
  28. package/lib/components/messageprovider/types.d.ts +5 -0
  29. package/lib/components/modal/Modal.vue.d.ts +18 -6
  30. package/lib/components/radio/Radio.vue.d.ts +2 -0
  31. package/lib/components/radiobuttongroup/RadioButtonGroup.vue.d.ts +1 -0
  32. package/lib/components/select/Select.vue.d.ts +23 -11
  33. package/lib/components/select/style/Select.css.d.ts +2 -0
  34. package/lib/components/switch/Switch.vue.d.ts +2 -0
  35. package/lib/components/tab/Tab.vue.d.ts +5 -2
  36. package/lib/components/tab/TabGroup.vue.d.ts +8 -3
  37. package/lib/components/textarea/Textarea.vue.d.ts +2 -1
  38. package/lib/components/tooltip/Tooltip.vue.d.ts +5 -20
  39. package/lib/components/tooltip/TooltipTrigger.vue.d.ts +1 -10
  40. package/lib/components/tree/Tree.vue.d.ts +3 -3
  41. package/lib/components/tree/TreeNode.vue.d.ts +26 -12
  42. package/lib/components/tree/types.d.ts +5 -5
  43. package/lib/index.cjs.js +5 -14
  44. package/lib/index.es.js +14765 -10217
  45. package/lib/style.css +1 -1
  46. package/package.json +12 -6
  47. package/src/components/avatar/Avatar.vue +32 -24
  48. package/src/components/button/Button.vue +24 -5
  49. package/src/components/cardcontextmenu/CardContextMenu.vue +68 -95
  50. package/src/components/cardcontextmenu/CardContextMenuItem.vue +40 -50
  51. package/src/components/cardcontextmenu/CardContextMenuSeperator.vue +9 -7
  52. package/src/components/cardcontextmenu/CardContextMenuTriggerProvider.vue +15 -0
  53. package/src/components/cardcontextmenu/style/Dropdown.css.ts +155 -2
  54. package/src/components/cardcontextmenu/symbol.ts +3 -0
  55. package/src/components/checkbox/Checkbox.vue +37 -12
  56. package/src/components/checkbox/style/Checkbox.css.ts +6 -6
  57. package/src/components/collapse/CollapseItem.vue +46 -21
  58. package/src/components/collapse/style/Collapse.css.ts +5 -1
  59. package/src/components/colorpicker/ColorPicker.vue +76 -45
  60. package/src/components/confirm/Confirm.vue +15 -8
  61. package/src/components/confirm/props.ts +4 -0
  62. package/src/components/dialog/Dialog.vue +169 -36
  63. package/src/components/dialogprovider/types.ts +1 -0
  64. package/src/components/dropdown/Dropdown.vue +46 -3
  65. package/src/components/dropdown/DropdownItem.vue +55 -25
  66. package/src/components/dropdown/DropdownSeperator.vue +19 -11
  67. package/src/components/dropdown/DropdownTriggerProvider.vue +15 -0
  68. package/src/components/dropdown/examples/context.vue +2 -2
  69. package/src/components/dropdown/style/Dropdown.css.ts +157 -4
  70. package/src/components/dropdown/symbol.ts +4 -0
  71. package/src/components/form/examples/basic.vue +19 -8
  72. package/src/components/messageprovider/MessageProvider.vue +136 -90
  73. package/src/components/messageprovider/examples/basic.vue +3 -1
  74. package/src/components/messageprovider/examples/index.stories.ts +37 -1
  75. package/src/components/messageprovider/types.ts +10 -0
  76. package/src/components/modal/Modal.vue +142 -67
  77. package/src/components/radio/Radio.vue +31 -17
  78. package/src/components/radiobutton/RadioButton.vue +15 -2
  79. package/src/components/radiobuttongroup/RadioButtonGroup.vue +11 -4
  80. package/src/components/select/Select.vue +309 -148
  81. package/src/components/select/examples/basic.vue +1 -1
  82. package/src/components/select/style/Select.css.ts +15 -0
  83. package/src/components/slider/Slider.vue +1 -1
  84. package/src/components/switch/Switch.vue +30 -10
  85. package/src/components/switch/style/Switch.css.ts +3 -5
  86. package/src/components/tab/Tab.vue +29 -20
  87. package/src/components/tab/TabGroup.vue +29 -38
  88. package/src/components/textarea/Textarea.vue +8 -4
  89. package/src/components/tooltip/Tooltip.vue +52 -141
  90. package/src/components/tooltip/TooltipTrigger.vue +14 -17
  91. package/src/components/tooltip/style/Tooltip.css.ts +148 -29
  92. package/src/components/tree/Tree.vue +138 -100
  93. package/src/components/tree/TreeNode.vue +105 -98
  94. package/src/components/tree/types.ts +4 -4
  95. package/src/styles/transitions.css.ts +36 -16
@@ -1,25 +1,40 @@
1
1
  <template>
2
- <Teleport to="body">
3
- <Transition :enter-active-class="fade.enterActive" :leave-active-class="fade.leaveActive"
4
- :enter-from-class="fade.enterFrom" :leave-to-class="fade.leaveTo">
5
- <div :class="style.root" v-if="open"
6
- :style="{ zIndex: zIndex, position: 'fixed', inset: '0', pointerEvents: modal ? 'all' : 'none' }">
7
- <div :class="backdropClasses" v-if="modal"></div>
8
- <div ref="wrapper" :class="wrapperClasses">
9
-
10
- <div ref="dialog" :style="computedStyle">
11
- <div tabindex="0" aria-hidden="true"
12
- style="width: 0px; height: 0px; overflow: hidden; outline: none;"></div>
2
+ <DialogRoot :open="isOpen" :modal="modal" @update:open="handleOpenChange">
3
+ <DialogPortal to="body">
4
+ <Transition type="animation" :enter-active-class="fade.enterActive" :leave-active-class="fade.leaveActive"
5
+ :enter-from-class="fade.enterFrom" :leave-to-class="fade.leaveTo">
6
+ <DialogOverlay v-if="isOpen && modal" force-mount :class="backdropClasses" style="z-index: 10" />
7
+ </Transition>
8
+ <Transition type="animation" :enter-active-class="modalScale.enterActive" :leave-active-class="modalScale.leaveActive"
9
+ :enter-from-class="modalScale.enterFrom" :leave-to-class="modalScale.leaveTo">
10
+
11
+ <DialogContent
12
+ v-if="isOpen"
13
+ ref="dialog"
14
+ force-mount
15
+ :style="computedStyle"
16
+ :disable-outside-pointer-events="modal"
17
+ @open-auto-focus="handleOpenAutoFocus"
18
+ @escape-key-down.prevent
19
+ @pointer-down-outside.prevent
20
+ @focus-outside="handleFocusOutside"
21
+ @interact-outside.prevent
22
+ >
23
+ <FocusScope :trapped="focusTrap" :loop="focusTrap">
13
24
  <slot v-if="$slots.container" name="container" :close="close"></slot>
14
25
  <template v-else>
15
26
  <div :class="style.content">
16
- <button role="button" :class="style.close" @click="close"></button>
27
+ <DialogClose as-child>
28
+ <button ref="closeButton" role="button" :class="style.close"></button>
29
+ </DialogClose>
17
30
  <div :class="headerClasses" ref="handle">
18
- <div :class="style.title">
19
- <slot name="title">
20
- {{ resolvedTitle }}
21
- </slot>
22
- </div>
31
+ <DialogTitle as-child>
32
+ <div :class="style.title">
33
+ <slot name="title">
34
+ {{ resolvedTitle }}
35
+ </slot>
36
+ </div>
37
+ </DialogTitle>
23
38
  </div>
24
39
  <div>
25
40
  <slot></slot>
@@ -29,35 +44,53 @@
29
44
  </div>
30
45
  </div>
31
46
  </template>
32
- <div tabindex="0" aria-hidden="true"
33
- style="width: 0px; height: 0px; overflow: hidden; outline: none;"></div>
34
- </div>
35
-
36
- </div>
37
- </div>
38
- </Transition>
39
- </Teleport>
47
+ </FocusScope>
48
+ </DialogContent>
49
+ </Transition>
50
+ </DialogPortal>
51
+ </DialogRoot>
40
52
  </template>
41
53
  <script lang="ts">
42
54
  import { fade, modalScale } from '../../styles/transitions.css';
55
+ import {
56
+ DialogClose,
57
+ DialogContent,
58
+ DialogOverlay,
59
+ DialogPortal,
60
+ DialogRoot,
61
+ DialogTitle,
62
+ FocusScope,
63
+ } from 'reka-ui';
43
64
  import {
44
65
  computed,
45
66
  defineComponent,
67
+ provide,
46
68
  ref,
69
+ shallowRef,
47
70
  watch,
48
71
  nextTick,
49
72
  isRef,
50
73
  type CSSProperties,
74
+ type ComponentPublicInstance,
51
75
  type PropType,
52
76
  type Ref,
53
77
  } from 'vue';
54
78
  import type { ComputedRef } from 'vue';
55
- import { useZIndex } from '../../composables/useZIndex';
79
+ import { useZIndex, ZINDEX } from '../../composables/useZIndex';
56
80
  import { useDraggable, useEventListener } from '@vueuse/core';
57
81
  import * as style from './style';
58
82
 
59
83
  export default defineComponent({
60
84
  name: 'Dialog',
85
+ components: {
86
+ DialogClose,
87
+ DialogContent,
88
+ DialogOverlay,
89
+ DialogPortal,
90
+ DialogRoot,
91
+ DialogTitle,
92
+ FocusScope,
93
+ },
61
94
  props: {
62
95
  title: {
63
96
  type: [String, Object] as PropType<string | Ref<string>>,
@@ -95,14 +128,37 @@ export default defineComponent({
95
128
  emits: ['onOpen', 'onClose', 'update:open', 'open'],
96
129
  setup(props, { emit, expose }) {
97
130
  const wrapper = ref<HTMLElement | null>(null);
98
- const dialog = ref<HTMLElement | null>(null);
131
+ const dialog = shallowRef<HTMLElement | ComponentPublicInstance | null>(null);
99
132
  const handle = ref<HTMLElement | null>(null);
133
+ const closeButton = ref<HTMLElement | null>(null);
100
134
  const { nextZIndex, current: maxZIndex } = useZIndex();
101
135
  const zIndex = ref(props.zIndex ?? nextZIndex());
136
+ provide(ZINDEX, zIndex);
137
+ const hasOpened = ref(false);
102
138
 
103
139
  const isDragging = ref<boolean>(false);
104
140
 
105
- const { x, y } = useDraggable(dialog, {
141
+ function resolveElement(
142
+ value: HTMLElement | ComponentPublicInstance | null | undefined
143
+ ): HTMLElement | null {
144
+ if (!value) {
145
+ return null;
146
+ }
147
+
148
+ if (value instanceof HTMLElement) {
149
+ return value;
150
+ }
151
+
152
+ if ('$el' in value && value.$el instanceof HTMLElement) {
153
+ return value.$el;
154
+ }
155
+
156
+ return null;
157
+ }
158
+
159
+ const dialogElement = computed(() => resolveElement(dialog.value));
160
+
161
+ const { x, y } = useDraggable(dialogElement, {
106
162
  handle,
107
163
  preventDefault: true,
108
164
  containerElement: wrapper,
@@ -115,6 +171,17 @@ export default defineComponent({
115
171
  isRef(props.title) ? props.title.value : props.title
116
172
  );
117
173
 
174
+ const focusableSelector = [
175
+ '[autofocus]',
176
+ 'input:not([type="hidden"])',
177
+ 'textarea',
178
+ 'select',
179
+ '[contenteditable="true"]',
180
+ 'button',
181
+ 'a[href]',
182
+ '[tabindex]:not([tabindex="-1"])'
183
+ ].join(', ');
184
+
118
185
  const backdropClasses = computed(() => ({
119
186
  [style.backdrop]: true,
120
187
  [style.backdropColor]: props.modal === true,
@@ -131,6 +198,13 @@ export default defineComponent({
131
198
  [style.notModal]: !props.modal,
132
199
  }));
133
200
 
201
+ const wrapperStyle: ComputedRef<CSSProperties> = computed(() => ({
202
+ zIndex: zIndex.value,
203
+ position: 'fixed',
204
+ inset: '0',
205
+ pointerEvents: props.modal ? 'all' : 'none'
206
+ }));
207
+
134
208
  const width = computed(() =>
135
209
  typeof props.width === 'number' ? `${props.width}px` : props.width
136
210
  );
@@ -144,8 +218,7 @@ export default defineComponent({
144
218
  }));
145
219
 
146
220
  function close() {
147
- emit('update:open', false);
148
- onCloseEmit();
221
+ handleOpenChange(false);
149
222
  }
150
223
 
151
224
  function onCloseEmit() {
@@ -157,9 +230,54 @@ export default defineComponent({
157
230
 
158
231
  function onEnter() {
159
232
  emit('onOpen');
233
+ emit('open');
160
234
  zIndex.value = nextZIndex();
161
235
  }
162
236
 
237
+ function handleFocusOutside(event: Event) {
238
+ if (!props.focusTrap) {
239
+ return;
240
+ }
241
+
242
+ event.preventDefault();
243
+ }
244
+
245
+ function handleOpenAutoFocus(event: Event) {
246
+ const element = dialogElement.value;
247
+
248
+ if (!element) {
249
+ return;
250
+ }
251
+
252
+ const firstFocusableElement = Array.from(
253
+ element.querySelectorAll<HTMLElement>(focusableSelector)
254
+ ).find(candidate => {
255
+ return candidate !== closeButton.value
256
+ && !candidate.matches(':disabled')
257
+ && candidate.getAttribute('aria-disabled') !== 'true'
258
+ && candidate.getAttribute('aria-hidden') !== 'true'
259
+ && candidate.getClientRects().length > 0;
260
+ });
261
+
262
+ if (!firstFocusableElement) {
263
+ return;
264
+ }
265
+
266
+ event.preventDefault();
267
+ firstFocusableElement.focus();
268
+ }
269
+
270
+ function handleOpenChange(value: boolean) {
271
+ if (!value && props.open) {
272
+ emit('update:open', false);
273
+ onCloseEmit();
274
+ hasOpened.value = false;
275
+ return;
276
+ }
277
+
278
+ emit('update:open', value);
279
+ }
280
+
163
281
  function onDragStart() {
164
282
  if (zIndex.value < maxZIndex.value) {
165
283
  zIndex.value = nextZIndex();
@@ -172,16 +290,20 @@ export default defineComponent({
172
290
  }
173
291
 
174
292
  function centerDialog() {
175
- if (dialog.value) {
176
- const boundingRect = dialog.value.getBoundingClientRect();
293
+ const element = dialogElement.value;
294
+
295
+ if (element) {
296
+ const boundingRect = element.getBoundingClientRect();
177
297
  x.value = (window.innerWidth - boundingRect.width) / 2;
178
298
  y.value = (window.innerHeight - boundingRect.height) / 2;
179
299
  }
180
300
  }
181
301
 
182
302
  function keepWithInBounds() {
183
- if (dialog.value) {
184
- const boundingRect = dialog.value.getBoundingClientRect();
303
+ const element = dialogElement.value;
304
+
305
+ if (element) {
306
+ const boundingRect = element.getBoundingClientRect();
185
307
  const maxX = window.innerWidth - boundingRect.width;
186
308
  const maxY = window.innerHeight - boundingRect.height;
187
309
 
@@ -206,7 +328,13 @@ export default defineComponent({
206
328
  if (newVal) {
207
329
  await nextTick();
208
330
  centerDialog();
209
- emit('onOpen');
331
+ zIndex.value = props.zIndex ?? nextZIndex();
332
+ if (!hasOpened.value) {
333
+ onEnter();
334
+ hasOpened.value = true;
335
+ }
336
+ } else {
337
+ hasOpened.value = false;
210
338
  }
211
339
  },
212
340
  { immediate: true }
@@ -220,6 +348,7 @@ export default defineComponent({
220
348
  wrapper,
221
349
  dialog,
222
350
  handle,
351
+ closeButton,
223
352
  zIndex,
224
353
  fade,
225
354
  modalScale,
@@ -229,9 +358,13 @@ export default defineComponent({
229
358
  backdropClasses,
230
359
  resolvedTitle,
231
360
  wrapperClasses,
361
+ wrapperStyle,
232
362
  close,
233
363
  onEnter,
234
- open: computed(() => props.open)
364
+ handleOpenChange,
365
+ handleOpenAutoFocus,
366
+ handleFocusOutside,
367
+ isOpen: computed(() => props.open)
235
368
  };
236
369
  },
237
370
  });
@@ -11,6 +11,7 @@ export interface DialogOptions {
11
11
  open?: boolean;
12
12
  draggable?: boolean;
13
13
  modal?: boolean;
14
+ focusTrap?: boolean;
14
15
  }
15
16
 
16
17
  export interface DialogReactive {
@@ -1,5 +1,15 @@
1
1
  <template>
2
- <ContextMenuRoot v-model:open="isOpen" v-if="contextMenu" v-bind="$attrs">
2
+ <ContextMenuSub v-model:open="isOpen" v-if="isContextMenu && isSubmenu">
3
+ <DropdownTriggerProvider>
4
+ <slot />
5
+ </DropdownTriggerProvider>
6
+ <ContextMenuPortal>
7
+ <ContextMenuSubContent :class="menuClasses" align="start" side="right" :side-offset="2" :align-offset="-6">
8
+ <slot name="menu" />
9
+ </ContextMenuSubContent>
10
+ </ContextMenuPortal>
11
+ </ContextMenuSub>
12
+ <ContextMenuRoot v-else-if="isContextMenu" v-model:open="isOpen" v-bind="$attrs">
3
13
  <ContextMenuTrigger as-child>
4
14
  <slot />
5
15
  </ContextMenuTrigger>
@@ -9,7 +19,17 @@
9
19
  </ContextMenuContent>
10
20
  </ContextMenuPortal>
11
21
  </ContextMenuRoot>
12
- <DropdownMenuRoot v-model:open="isOpen" v-else :modal="modal" v-bind="$attrs">
22
+ <DropdownMenuSub v-else-if="isSubmenu" v-model:open="isOpen">
23
+ <DropdownTriggerProvider>
24
+ <slot />
25
+ </DropdownTriggerProvider>
26
+ <DropdownMenuPortal>
27
+ <DropdownMenuSubContent :class="menuClasses" align="start" side="right" :side-offset="2" :align-offset="-6">
28
+ <slot name="menu" />
29
+ </DropdownMenuSubContent>
30
+ </DropdownMenuPortal>
31
+ </DropdownMenuSub>
32
+ <DropdownMenuRoot v-else v-model:open="isOpen" :modal="modal" v-bind="$attrs">
13
33
  <DropdownMenuTrigger as-child>
14
34
  <slot />
15
35
  </DropdownMenuTrigger>
@@ -53,10 +73,12 @@ import {
53
73
  ContextMenuSubContent,
54
74
  ContextMenuSubTrigger,
55
75
  ContextMenuTrigger,
56
- } from 'radix-vue';
76
+ } from 'reka-ui';
57
77
  import {
58
78
  computed,
59
79
  defineComponent,
80
+ inject,
81
+ provide,
60
82
  ref,
61
83
  watch,
62
84
  type PropType
@@ -65,6 +87,7 @@ import { useZIndex } from '../../composables/useZIndex';
65
87
  import { scale } from '../../styles/transitions.css';
66
88
  import * as style from './style';
67
89
  import { DROPDOWN_STATE } from './symbol';
90
+ import DropdownTriggerProvider from './DropdownTriggerProvider.vue';
68
91
 
69
92
  export default defineComponent({
70
93
  name: 'Dropdown',
@@ -99,6 +122,7 @@ export default defineComponent({
99
122
  ContextMenuSubContent,
100
123
  ContextMenuSubTrigger,
101
124
  ContextMenuTrigger,
125
+ DropdownTriggerProvider,
102
126
  },
103
127
  props: {
104
128
  open: {
@@ -151,6 +175,9 @@ export default defineComponent({
151
175
  },
152
176
  emits: ['update:open'],
153
177
  setup(props, { emit }) {
178
+ const parentState = inject(DROPDOWN_STATE, null);
179
+ const isSubmenu = computed(() => !!parentState);
180
+ const isContextMenu = computed(() => props.contextMenu || parentState?.contextMenu || false);
154
181
  const { nextZIndex } = useZIndex();
155
182
  const isOpen = ref(props.open);
156
183
  const placement = ref(props.placement);
@@ -158,6 +185,16 @@ export default defineComponent({
158
185
  const referenceRef = ref<HTMLElement>();
159
186
  const zIndex = ref(props.zIndex ?? nextZIndex());
160
187
 
188
+ provide(DROPDOWN_STATE, {
189
+ contextMenu: isContextMenu.value,
190
+ isOpen: isOpen.value,
191
+ handleSelect(labels: string[]) {
192
+ isOpen.value = false;
193
+ props.onSelect(labels);
194
+ parentState?.handleSelect(labels);
195
+ }
196
+ });
197
+
161
198
  watch(
162
199
  () => props.open,
163
200
  value => {
@@ -165,6 +202,10 @@ export default defineComponent({
165
202
  }
166
203
  );
167
204
 
205
+ watch(isOpen, value => {
206
+ emit('update:open', value);
207
+ });
208
+
168
209
  function toggleDropdown() {
169
210
  isOpen.value = !isOpen.value;
170
211
  }
@@ -199,6 +240,8 @@ export default defineComponent({
199
240
  computedReferenceClasses,
200
241
  transitionName,
201
242
  toggleDropdown,
243
+ isContextMenu,
244
+ isSubmenu,
202
245
  zIndex,
203
246
  scale
204
247
  };
@@ -1,54 +1,50 @@
1
1
  <template>
2
- <DropdownMenuItem :class="computedWrapperClasses" :as="computedType" :href="href">
3
- <div :class="style.menuItemIcon">
4
- <slot name="icon-left" />
5
- </div>
6
- <div :class="style.menuItemTextContainer">
7
- <span :class="style.menuItemText"><slot /></span>
8
- </div>
9
- <slot name="keyboard" />
10
- </DropdownMenuItem>
11
- <!-- <component
12
- :is="computedType"
13
- :href="href"
2
+ <component
3
+ :is="itemComponent"
14
4
  ref="wrapper"
15
5
  :class="computedWrapperClasses"
16
- role="menuitem"
17
- tabindex="0"
18
- @click="handleSelect"
19
- v-bind="$attrs">
6
+ :disabled="disabled"
7
+ v-bind="componentProps"
8
+ @select="!isSubmenuTrigger ? handleSelect : undefined"
9
+ >
20
10
  <div v-if="hasIconLeft" :class="style.menuItemIcon">
21
11
  <slot name="icon-left" />
22
12
  </div>
23
13
  <div :class="style.menuItemTextContainer">
24
14
  <span :class="style.menuItemText"><slot /></span>
25
15
  </div>
26
- <!-- <div v-if="hotkey" class="m-dropdown__hotkeys">
27
- <template v-for="key in hotkey.split('+')">
28
- <kbd>{{ key }}</kbd>
29
- </template>
30
- </div> -->
31
- <!-- <div v-if="hasIconRight" :class="style.menuItemIcon">
16
+ <slot name="keyboard" />
17
+ <div v-if="hasIconRight" :class="style.menuItemIconRight">
32
18
  <slot name="icon-right" />
33
19
  </div>
34
- </component> -->
20
+ </component>
35
21
  </template>
36
22
 
37
23
  <script lang="ts">
38
24
  import {
39
25
  computed,
40
26
  defineComponent,
27
+ inject,
41
28
  type PropType,
42
29
  ref
43
30
  } from 'vue';
44
- import { DropdownMenuItem } from "radix-vue";
31
+ import {
32
+ ContextMenuItem,
33
+ ContextMenuSubTrigger,
34
+ DropdownMenuItem,
35
+ DropdownMenuSubTrigger
36
+ } from 'reka-ui';
45
37
  import * as style from './style';
46
38
  import type { Variant } from '../../types/props';
39
+ import { DROPDOWN_STATE, DROPDOWN_TRIGGER_STATE } from './symbol';
47
40
 
48
41
  export default defineComponent({
49
42
  name: 'DropdownItem',
50
43
  components: {
44
+ ContextMenuItem,
45
+ ContextMenuSubTrigger,
51
46
  DropdownMenuItem
47
+ ,DropdownMenuSubTrigger
52
48
  },
53
49
  props: {
54
50
  label: {
@@ -86,15 +82,25 @@ export default defineComponent({
86
82
  },
87
83
  emits: [],
88
84
  setup(props, { slots }) {
85
+ const dropdownState = inject(DROPDOWN_STATE, null);
86
+ const isSubmenuTrigger = inject(DROPDOWN_TRIGGER_STATE, false);
89
87
 
90
88
  const hasIconLeft = computed(() => !!slots['icon-left']);
91
89
  const hasIconRight = computed(() => !!slots['icon-right']);
92
90
 
93
- const wrapper = ref(null);
91
+ const wrapper = ref<HTMLElement | null>(null);
94
92
  const computedWrapperClasses = computed(() => {
95
93
  return [style.menuItem, style.variant[props.variant]];
96
94
  });
97
95
 
96
+ const itemComponent = computed(() => {
97
+ if (isSubmenuTrigger) {
98
+ return dropdownState?.contextMenu ? ContextMenuSubTrigger : DropdownMenuSubTrigger;
99
+ }
100
+
101
+ return dropdownState?.contextMenu ? ContextMenuItem : DropdownMenuItem;
102
+ });
103
+
98
104
  const computedType = computed(() => {
99
105
  if (props.href) {
100
106
  return 'a';
@@ -103,11 +109,35 @@ export default defineComponent({
103
109
  return 'div';
104
110
  });
105
111
 
112
+ const componentProps = computed(() => {
113
+ if (isSubmenuTrigger) {
114
+ return {};
115
+ }
116
+
117
+ return {
118
+ as: computedType.value,
119
+ href: props.href
120
+ };
121
+ });
122
+
123
+ function handleSelect() {
124
+ if (props.disabled || isSubmenuTrigger) {
125
+ return;
126
+ }
127
+
128
+ dropdownState?.handleSelect([String(props.label ?? wrapper.value?.textContent?.trim() ?? '')]);
129
+ props.onSelect(props.label ?? wrapper.value?.textContent?.trim() ?? '');
130
+ }
131
+
106
132
  return {
107
133
  wrapper,
134
+ itemComponent,
135
+ isSubmenuTrigger,
108
136
  hasIconLeft,
109
137
  hasIconRight,
110
138
  computedWrapperClasses,
139
+ componentProps,
140
+ handleSelect,
111
141
  style,
112
142
  computedType
113
143
  };
@@ -1,26 +1,34 @@
1
1
  <template>
2
- <DropdownMenuSeparator :class="computedClasses" />
2
+ <component :is="separatorComponent" :class="computedClasses" />
3
3
  </template>
4
4
 
5
5
  <script lang="ts">
6
- import {computed, defineComponent} from "vue";
7
- import * as style from "./style";
8
- import { DropdownMenuSeparator } from 'radix-vue';
6
+ import { computed, defineComponent, inject } from 'vue';
7
+ import { ContextMenuSeparator, DropdownMenuSeparator } from 'reka-ui';
8
+ import * as style from './style';
9
+ import { DROPDOWN_STATE } from './symbol';
10
+
9
11
  export default defineComponent({
10
- name: "DropdownSeperator",
12
+ name: 'DropdownSeperator',
11
13
  components: {
14
+ ContextMenuSeparator,
12
15
  DropdownMenuSeparator
13
16
  },
14
17
  setup() {
18
+ const dropdownState = inject(DROPDOWN_STATE, null);
19
+
15
20
  const computedClasses = computed(() => {
16
- return [
17
- style.menuSeperator
18
- ]
21
+ return [style.menuSeperator];
22
+ });
23
+
24
+ const separatorComponent = computed(() => {
25
+ return dropdownState?.contextMenu ? ContextMenuSeparator : DropdownMenuSeparator;
19
26
  });
20
27
 
21
28
  return {
22
- computedClasses
23
- }
29
+ computedClasses,
30
+ separatorComponent
31
+ };
24
32
  }
25
- })
33
+ });
26
34
  </script>
@@ -0,0 +1,15 @@
1
+ <template>
2
+ <slot />
3
+ </template>
4
+
5
+ <script lang="ts">
6
+ import { defineComponent, provide } from 'vue';
7
+ import { DROPDOWN_TRIGGER_STATE } from './symbol';
8
+
9
+ export default defineComponent({
10
+ name: 'DropdownTriggerProvider',
11
+ setup() {
12
+ provide(DROPDOWN_TRIGGER_STATE, true);
13
+ }
14
+ });
15
+ </script>
@@ -2,7 +2,7 @@
2
2
  <div>
3
3
 
4
4
 
5
- <MDropdown v-model:open="isOpen" :style="{ top: `199px`, left: `9px` }">
5
+ <MDropdown contextMenu v-model:open="isOpen" :style="{ top: `199px`, left: `9px` }">
6
6
  <div style="background: #eee; width: 500px; height: 500px;">
7
7
  </div>
8
8
  <template #menu>
@@ -47,7 +47,7 @@
47
47
 
48
48
  <script lang="ts" setup>
49
49
  import { ref } from 'vue';
50
- const isOpen = ref(true);
50
+ const isOpen = ref(false);
51
51
  const handleClick = () => {
52
52
  alert('hi');
53
53
  }