@bitrix24/b24ui-nuxt 0.5.3 → 0.5.4

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 (46) hide show
  1. package/.nuxt/b24ui/button.ts +10 -10
  2. package/.nuxt/b24ui/container.ts +1 -1
  3. package/.nuxt/b24ui/navigation-menu.ts +2 -2
  4. package/.nuxt/b24ui/switch.ts +1 -1
  5. package/.nuxt/b24ui/tabs.ts +1 -1
  6. package/dist/meta.cjs +47928 -45201
  7. package/dist/meta.d.cts +47928 -45201
  8. package/dist/meta.d.mts +47928 -45201
  9. package/dist/meta.d.ts +47928 -45201
  10. package/dist/meta.mjs +47928 -45201
  11. package/dist/module.cjs +1 -1
  12. package/dist/module.json +1 -1
  13. package/dist/module.mjs +1 -1
  14. package/dist/runtime/components/Alert.vue +1 -1
  15. package/dist/runtime/components/App.vue +1 -1
  16. package/dist/runtime/components/Button.vue +1 -1
  17. package/dist/runtime/components/Calendar.vue +40 -12
  18. package/dist/runtime/components/DescriptionList.vue +99 -85
  19. package/dist/runtime/components/DropdownMenu.vue +23 -12
  20. package/dist/runtime/components/DropdownMenuContent.vue +22 -15
  21. package/dist/runtime/components/InputMenu.vue +91 -53
  22. package/dist/runtime/components/Link.vue +3 -0
  23. package/dist/runtime/components/LinkBase.vue +1 -0
  24. package/dist/runtime/components/Modal.vue +1 -1
  25. package/dist/runtime/components/NavigationMenu.vue +49 -25
  26. package/dist/runtime/components/RadioGroup.vue +23 -12
  27. package/dist/runtime/components/Select.vue +74 -47
  28. package/dist/runtime/components/SelectMenu.vue +95 -56
  29. package/dist/runtime/components/Slideover.vue +1 -1
  30. package/dist/runtime/components/Tabs.vue +6 -5
  31. package/dist/runtime/components/Toast.vue +1 -1
  32. package/dist/runtime/composables/defineLocale.js +1 -0
  33. package/dist/runtime/types/utils.d.ts +28 -7
  34. package/dist/runtime/utils/index.d.ts +1 -0
  35. package/dist/runtime/utils/index.js +3 -0
  36. package/dist/runtime/utils/link.d.ts +2 -25
  37. package/dist/runtime/utils/link.js +31 -1
  38. package/dist/runtime/vue/components/Link.vue +3 -0
  39. package/dist/runtime/vue/stubs.d.ts +2 -2
  40. package/dist/shared/{b24ui-nuxt.Bh_5o1_9.cjs → b24ui-nuxt.BfU7TRfz.cjs} +16 -15
  41. package/dist/shared/{b24ui-nuxt.BIuy4yic.mjs → b24ui-nuxt.CTERD7XY.mjs} +16 -15
  42. package/dist/unplugin.cjs +1 -1
  43. package/dist/unplugin.mjs +1 -1
  44. package/dist/vite.cjs +1 -1
  45. package/dist/vite.mjs +1 -1
  46. package/package.json +27 -12
package/dist/module.cjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  const defu = require('defu');
4
4
  const kit = require('@nuxt/kit');
5
- const templates = require('./shared/b24ui-nuxt.Bh_5o1_9.cjs');
5
+ const templates = require('./shared/b24ui-nuxt.BfU7TRfz.cjs');
6
6
  require('node:url');
7
7
  require('scule');
8
8
 
package/dist/module.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "nuxt": ">=3.16.0"
6
6
  },
7
7
  "docs": "https://bitrix24.github.io/b24ui/guide/installation-nuxt-app.html",
8
- "version": "0.5.3",
8
+ "version": "0.5.4",
9
9
  "builder": {
10
10
  "@nuxt/module-builder": "0.8.4",
11
11
  "unbuild": "2.0.0"
package/dist/module.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { defu } from 'defu';
2
2
  import { defineNuxtModule, createResolver, addVitePlugin, addPlugin, addComponentsDir, addImportsDir, hasNuxtModule, installModule } from '@nuxt/kit';
3
- import { d as defaultOptions, a as getDefaultUiConfig, b as addTemplates } from './shared/b24ui-nuxt.BIuy4yic.mjs';
3
+ import { d as defaultOptions, a as getDefaultUiConfig, b as addTemplates } from './shared/b24ui-nuxt.CTERD7XY.mjs';
4
4
  import 'node:url';
5
5
  import 'scule';
6
6
 
@@ -72,7 +72,7 @@ export interface AlertSlots {
72
72
  title(props?: {}): any
73
73
  description(props?: {}): any
74
74
  actions(props?: {}): any
75
- close(props: { b24ui: any }): any
75
+ close(props: { b24ui: ReturnType<typeof alert> }): any
76
76
  }
77
77
  </script>
78
78
 
@@ -17,7 +17,7 @@ export default {
17
17
  }
18
18
  </script>
19
19
 
20
- <script setup lang="ts" generic="T extends Messages = Messages">
20
+ <script setup lang="ts" generic="T extends Messages">
21
21
  import { toRef, useId, provide } from 'vue'
22
22
  import { ConfigProvider, TooltipProvider, useForwardProps } from 'reka-ui'
23
23
  import { reactivePick } from '@vueuse/core'
@@ -184,7 +184,7 @@ const b24ui = computed(() => tv({
184
184
  :type="type"
185
185
  :disabled="disabled || isLoading"
186
186
  :class="b24ui.base({ class: [props.class, props.b24ui?.base] })"
187
- v-bind="omit(linkProps, ['type', 'disabled'])"
187
+ v-bind="omit(linkProps, ['type', 'disabled', 'onClick'])"
188
188
  custom
189
189
  >
190
190
  <B24LinkBase
@@ -1,12 +1,12 @@
1
1
  <script lang="ts">
2
2
  import type { VariantProps } from 'tailwind-variants'
3
- import type { CalendarRootProps, CalendarRootEmits, RangeCalendarRootEmits, DateRange, CalendarCellTriggerProps } from 'reka-ui'
3
+ import type { CalendarRootProps, CalendarRootEmits, RangeCalendarRootProps, RangeCalendarRootEmits, DateRange, CalendarCellTriggerProps } from 'reka-ui'
4
4
  import type { DateValue } from '@internationalized/date'
5
5
  import type { AppConfig } from '@nuxt/schema'
6
+ import type { IconComponent, ButtonProps } from '../types'
6
7
  import _appConfig from '#build/app.config'
7
8
  import theme from '#build/b24ui/calendar'
8
9
  import { tv } from '../utils/tv'
9
- import type { IconComponent } from '../types'
10
10
  import type { PartialString } from '../types/utils'
11
11
 
12
12
  const appConfigCalendar = _appConfig as AppConfig & { b24ui: { calendar: Partial<typeof theme> } }
@@ -15,13 +15,21 @@ const calendar = tv({ extend: tv(theme), ...(appConfigCalendar.b24ui?.calendar |
15
15
 
16
16
  type CalendarVariants = VariantProps<typeof calendar>
17
17
 
18
- type CalendarModelValue<R extends boolean = false, M extends boolean = false> = R extends true
18
+ type CalendarDefaultValue<R extends boolean = false, M extends boolean = false> = R extends true
19
19
  ? DateRange
20
20
  : M extends true
21
21
  ? DateValue[]
22
22
  : DateValue
23
+ type CalendarModelValue<R extends boolean = false, M extends boolean = false> = R extends true
24
+ ? (DateRange | null)
25
+ : M extends true
26
+ ? (DateValue[] | undefined)
27
+ : (DateValue | undefined)
23
28
 
24
- export interface CalendarProps<R extends boolean, M extends boolean> extends Omit<CalendarRootProps, 'as' | 'asChild' | 'modelValue' | 'defaultValue' | 'dir' | 'locale' | 'calendarLabel' | 'multiple'> {
29
+ type _CalendarRootProps = Omit<CalendarRootProps, 'as' | 'asChild' | 'modelValue' | 'defaultValue' | 'dir' | 'locale' | 'calendarLabel' | 'multiple'>
30
+ type _RangeCalendarRootProps = Omit<RangeCalendarRootProps, 'as' | 'asChild' | 'modelValue' | 'defaultValue' | 'dir' | 'locale' | 'calendarLabel' | 'multiple'>
31
+
32
+ export interface CalendarProps<R extends boolean = false, M extends boolean = false> extends _RangeCalendarRootProps, _CalendarRootProps {
25
33
  /**
26
34
  * The element or component this component should render as.
27
35
  * @defaultValue 'div'
@@ -33,24 +41,44 @@ export interface CalendarProps<R extends boolean, M extends boolean> extends Omi
33
41
  * @IconComponent
34
42
  */
35
43
  nextYearIcon?: IconComponent
44
+ /**
45
+ * Configure the next year button.
46
+ * `{ color: 'link' }`{lang="ts"}
47
+ */
48
+ nextYear?: ButtonProps
36
49
  /**
37
50
  * The icon to use for the next month control.
38
51
  * @defaultValue icons.chevronRight
39
52
  * @IconComponent
40
53
  */
41
54
  nextMonthIcon?: IconComponent
55
+ /**
56
+ * Configure the next month button.
57
+ * `{ color: 'link' }`{lang="ts"}
58
+ */
59
+ nextMonth?: ButtonProps
42
60
  /**
43
61
  * The icon to use for the previous year control.
44
62
  * @defaultValue icons.chevronDoubleLeft
45
63
  * @IconComponent
46
64
  */
47
65
  prevYearIcon?: IconComponent
66
+ /**
67
+ * Configure the prev year button.
68
+ * `{ color: 'link' }`{lang="ts"}
69
+ */
70
+ prevYear?: ButtonProps
48
71
  /**
49
72
  * The icon to use for the previous month control.
50
73
  * @defaultValue icons.chevronLeft
51
74
  * @IconComponent
52
75
  */
53
76
  prevMonthIcon?: IconComponent
77
+ /**
78
+ * Configure the prev month button.
79
+ * `{ color: 'link' }`{lang="ts"}
80
+ */
81
+ prevMonth?: ButtonProps
54
82
  /**
55
83
  * @defaultValue 'primary'
56
84
  */
@@ -67,7 +95,7 @@ export interface CalendarProps<R extends boolean, M extends boolean> extends Omi
67
95
  monthControls?: boolean
68
96
  /** Show year controls */
69
97
  yearControls?: boolean
70
- defaultValue?: CalendarModelValue<R, M>
98
+ defaultValue?: CalendarDefaultValue<R, M>
71
99
  modelValue?: CalendarModelValue<R, M>
72
100
  class?: any
73
101
  b24ui?: PartialString<typeof calendar.slots>
@@ -84,7 +112,7 @@ export interface CalendarSlots {
84
112
  }
85
113
  </script>
86
114
 
87
- <script setup lang="ts" generic="R extends boolean = false, M extends boolean = false">
115
+ <script setup lang="ts" generic="R extends boolean, M extends boolean">
88
116
  import { computed } from 'vue'
89
117
  import { useForwardPropsEmits } from 'reka-ui'
90
118
  import { Calendar as SingleCalendar, RangeCalendar } from 'reka-ui/namespaced'
@@ -137,18 +165,18 @@ const btnSize = computed(() => {
137
165
  <Calendar.Root
138
166
  v-slot="{ weekDays, grid }"
139
167
  v-bind="rootProps"
140
- :model-value="(modelValue as CalendarModelValue<true & false>)"
141
- :default-value="(defaultValue as CalendarModelValue<true & false>)"
168
+ :model-value="modelValue"
169
+ :default-value="defaultValue"
142
170
  :locale="locale"
143
171
  :dir="dir"
144
172
  :class="b24ui.root({ class: [props.class, props.b24ui?.root] })"
145
173
  >
146
174
  <Calendar.Header :class="b24ui.header({ class: props.b24ui?.header })">
147
175
  <Calendar.Prev v-if="props.yearControls" :prev-page="(date: DateValue) => paginateYear(date, -1)" :aria-label="t('calendar.prevYear')" as-child>
148
- <B24Button :icon="prevYearIcon" :size="btnSize" color="link" />
176
+ <B24Button :icon="prevYearIcon" :size="btnSize" color="link" v-bind="props.prevYear" />
149
177
  </Calendar.Prev>
150
178
  <Calendar.Prev v-if="props.monthControls" :aria-label="t('calendar.prevMonth')" as-child>
151
- <B24Button :icon="prevMonthIcon" :size="btnSize" color="link" />
179
+ <B24Button :icon="prevMonthIcon" :size="btnSize" color="link" v-bind="props.prevMonth" />
152
180
  </Calendar.Prev>
153
181
  <Calendar.Heading v-slot="{ headingValue }" :class="b24ui.heading({ class: props.b24ui?.heading })">
154
182
  <slot name="heading" :value="headingValue">
@@ -156,10 +184,10 @@ const btnSize = computed(() => {
156
184
  </slot>
157
185
  </Calendar.Heading>
158
186
  <Calendar.Next v-if="props.monthControls" :aria-label="t('calendar.nextMonth')" as-child>
159
- <B24Button :icon="nextMonthIcon" :size="btnSize" color="link" />
187
+ <B24Button :icon="nextMonthIcon" :size="btnSize" color="link" v-bind="props.nextMonth" />
160
188
  </Calendar.Next>
161
189
  <Calendar.Next v-if="props.yearControls" :next-page="(date: DateValue) => paginateYear(date, 1)" :aria-label="t('calendar.nextYear')" as-child>
162
- <B24Button :icon="nextYearIcon" :size="btnSize" color="link" />
190
+ <B24Button :icon="nextYearIcon" :size="btnSize" color="link" v-bind="props.nextYear" />
163
191
  </Calendar.Next>
164
192
  </Calendar.Header>
165
193
  <div :class="b24ui.body({ class: props.b24ui?.body })">
@@ -37,9 +37,10 @@ export interface DescriptionListItem {
37
37
  actions?: ButtonProps[]
38
38
  class?: any
39
39
  b24ui?: Partial<typeof descriptionList.slots>
40
+ [key: string]: any
40
41
  }
41
42
 
42
- export interface DescriptionListProps<T> {
43
+ export interface DescriptionListProps<T extends DescriptionListItem = DescriptionListItem> {
43
44
  legend?: string
44
45
  text?: string
45
46
  /**
@@ -61,17 +62,18 @@ export interface DescriptionListProps<T> {
61
62
  b24ui?: Partial<typeof descriptionList.slots>
62
63
  }
63
64
 
64
- type SlotProps<T> = (props: { item: T, index: number }) => any
65
+ type SlotProps<T extends DescriptionListItem> = (props: { item: T, index: number }) => any
65
66
 
66
- export type DescriptionListSlots<T extends { slot?: string }> = {
67
+ export type DescriptionListSlots<T extends DescriptionListItem = DescriptionListItem> = {
67
68
  legend(props?: {}): any
68
69
  text(props?: {}): any
69
70
  leading: SlotProps<T>
70
71
  label: SlotProps<T>
71
72
  description: SlotProps<T>
72
73
  actions: SlotProps<T>
74
+ content: SlotProps<T>
73
75
  footer(props?: { b24ui: any }): any
74
- } & DynamicSlots<T, SlotProps<T>>
76
+ } & DynamicSlots<T, undefined, { index: number }>
75
77
  </script>
76
78
 
77
79
  <script setup lang="ts" generic="T extends DescriptionListItem">
@@ -91,6 +93,14 @@ const b24ui = computed(() => descriptionList({
91
93
  }))
92
94
 
93
95
  function normalizeItem(item: any) {
96
+ if (item === null) {
97
+ return {
98
+ label: undefined,
99
+ description: undefined,
100
+ orientation: undefined
101
+ }
102
+ }
103
+
94
104
  const label = get(item, props.labelKey as string)
95
105
  const description = get(item, props.descriptionKey as string)
96
106
  const orientation = item?.orientation || 'vertical'
@@ -113,9 +123,7 @@ const normalizedItems = computed(() => {
113
123
  </script>
114
124
 
115
125
  <template>
116
- <div
117
- :class="b24ui.root({ class: [props.class, props.b24ui?.root] })"
118
- >
126
+ <div :class="b24ui.root({ class: [props.class, props.b24ui?.root] })">
119
127
  <h2 v-if="legend || !!slots.legend" :class="b24ui.legend({ class: props.b24ui?.legend })">
120
128
  <slot name="legend">
121
129
  {{ legend }}
@@ -132,95 +140,101 @@ const normalizedItems = computed(() => {
132
140
  v-for="(item, index) in normalizedItems"
133
141
  :key="index"
134
142
  >
135
- <dt
136
- :class="b24ui.labelWrapper({
137
- class: [
138
- props.b24ui?.labelWrapper,
139
- item?.b24ui?.labelWrapper
140
- ]
141
- })"
143
+ <slot
144
+ :name="((item.slot || 'content') as keyof DescriptionListSlots<T>)"
145
+ :item="(item as Extract<T, { slot: string; }>)"
146
+ :index="index"
142
147
  >
143
- <slot name="leading" :item="item" :index="index">
144
- <Component
145
- :is="item.icon"
146
- v-if="item.icon"
147
- :class="b24ui.icon({
148
- class: [
149
- props.b24ui?.icon,
150
- item?.b24ui?.icon
151
- ]
152
- })"
153
- />
154
- <B24Avatar
155
- v-else-if="item.avatar"
156
- :size="((props.b24ui?.avatarSize || b24ui.avatarSize()) as AvatarProps['size'])"
157
- v-bind="item.avatar"
158
- :class="b24ui.avatar({
159
- class: [
160
- props.b24ui?.avatar,
161
- item?.b24ui?.avatar
162
- ]
163
- })"
164
- />
165
- </slot>
166
- <span
167
- :class="b24ui.label({
148
+ <dt
149
+ :class="b24ui.labelWrapper({
168
150
  class: [
169
- item?.class,
170
- props.b24ui?.label,
171
- item?.b24ui?.label
151
+ props.b24ui?.labelWrapper,
152
+ item?.b24ui?.labelWrapper
172
153
  ]
173
154
  })"
174
155
  >
175
- <slot name="label" :item="item" :index="index">
176
- {{ item.label }}
177
- </slot>
178
- </span>
179
- </dt>
180
- <dd
181
- :data-orientation="item.orientation"
182
- :class="b24ui.descriptionWrapper({
183
- class: [
184
- props.b24ui?.descriptionWrapper,
185
- item?.b24ui?.descriptionWrapper
186
- ],
187
- orientation: item.orientation
188
- })"
189
- >
190
- <span
191
- :class="b24ui.description({
192
- class: [
193
- item?.class,
194
- props.b24ui?.description,
195
- item?.b24ui?.description
196
- ],
197
- orientation: item.orientation
198
- })"
199
- >
200
- <slot name="description" :item="item" :index="index">
201
- {{ item.description }}
156
+ <slot name="leading" :item="item" :index="index">
157
+ <Component
158
+ :is="item.icon"
159
+ v-if="item.icon"
160
+ :class="b24ui.icon({
161
+ class: [
162
+ props.b24ui?.icon,
163
+ item?.b24ui?.icon
164
+ ]
165
+ })"
166
+ />
167
+ <B24Avatar
168
+ v-else-if="item.avatar"
169
+ :size="((props.b24ui?.avatarSize || b24ui.avatarSize()) as AvatarProps['size'])"
170
+ v-bind="item.avatar"
171
+ :class="b24ui.avatar({
172
+ class: [
173
+ props.b24ui?.avatar,
174
+ item?.b24ui?.avatar
175
+ ]
176
+ })"
177
+ />
202
178
  </slot>
203
- </span>
204
- <span
205
- v-if="item.actions?.length || !!slots.actions"
206
- :class="b24ui.actions({
179
+ <span
180
+ :class="b24ui.label({
181
+ class: [
182
+ item?.class,
183
+ props.b24ui?.label,
184
+ item?.b24ui?.label
185
+ ]
186
+ })"
187
+ >
188
+ <slot name="label" :item="item" :index="index">
189
+ {{ item.label }}
190
+ </slot>
191
+ </span>
192
+ </dt>
193
+ <dd
194
+ :data-orientation="item.orientation"
195
+ :class="b24ui.descriptionWrapper({
207
196
  class: [
208
- props.b24ui?.actions,
209
- item?.b24ui?.actions
197
+ props.b24ui?.descriptionWrapper,
198
+ item?.b24ui?.descriptionWrapper
210
199
  ],
211
200
  orientation: item.orientation
212
201
  })"
213
202
  >
214
- <slot name="actions" :item="item" :index="index">
215
- <B24Button
216
- v-for="(action, indexActions) in item.actions"
217
- :key="indexActions"
218
- size="xs"
219
- v-bind="action"
220
- />
221
- </slot>
222
- </span>
223
- </dd>
203
+ <span
204
+ :class="b24ui.description({
205
+ class: [
206
+ item?.class,
207
+ props.b24ui?.description,
208
+ item?.b24ui?.description
209
+ ],
210
+ orientation: item.orientation
211
+ })"
212
+ >
213
+ <slot name="description" :item="item" :index="index">
214
+ {{ item.description }}
215
+ </slot>
216
+ </span>
217
+ <span
218
+ v-if="item.actions?.length || !!slots.actions"
219
+ :class="b24ui.actions({
220
+ class: [
221
+ props.b24ui?.actions,
222
+ item?.b24ui?.actions
223
+ ],
224
+ orientation: item.orientation
225
+ })"
226
+ >
227
+ <slot name="actions" :item="item" :index="index">
228
+ <B24Button
229
+ v-for="(action, indexActions) in item.actions"
230
+ :key="indexActions"
231
+ size="xs"
232
+ v-bind="action"
233
+ />
234
+ </slot>
235
+ </span>
236
+ </dd>
237
+ </slot>
224
238
  </template>
225
239
  </dl>
226
240
  <div
@@ -7,7 +7,14 @@ import _appConfig from '#build/app.config'
7
7
  import theme from '#build/b24ui/dropdown-menu'
8
8
  import { tv } from '../utils/tv'
9
9
  import type { AvatarProps, KbdProps, LinkProps, IconComponent } from '../types'
10
- import type { DynamicSlots, PartialString, EmitsToProps } from '../types/utils'
10
+ import type {
11
+ ArrayOrNested,
12
+ DynamicSlots,
13
+ MergeTypes,
14
+ NestedItem,
15
+ PartialString,
16
+ EmitsToProps
17
+ } from '../types/utils'
11
18
 
12
19
  const appConfigDropdownMenu = _appConfig as AppConfig & { b24ui: { dropdownMenu: Partial<typeof theme> } }
13
20
 
@@ -37,17 +44,18 @@ export interface DropdownMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'cust
37
44
  checked?: boolean
38
45
  open?: boolean
39
46
  defaultOpen?: boolean
40
- children?: DropdownMenuItem[] | DropdownMenuItem[][]
47
+ children?: ArrayOrNested<DropdownMenuItem>
41
48
  onSelect?(e: Event): void
42
49
  onUpdateChecked?(checked: boolean): void
50
+ [key: string]: any
43
51
  }
44
52
 
45
- export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'> {
53
+ export interface DropdownMenuProps<T extends ArrayOrNested<DropdownMenuItem> = ArrayOrNested<DropdownMenuItem>> extends Omit<DropdownMenuRootProps, 'dir'> {
46
54
  /**
47
55
  * @defaultValue 'md'
48
56
  */
49
57
  size?: DropdownMenuVariants['size']
50
- items?: T[] | T[][]
58
+ items?: T
51
59
  /**
52
60
  * The icon displayed when an item is checked.
53
61
  * @defaultValue icons.check
@@ -80,7 +88,7 @@ export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'>
80
88
  * The key used to get the label from the item.
81
89
  * @defaultValue 'label'
82
90
  */
83
- labelKey?: string
91
+ labelKey?: keyof NestedItem<T>
84
92
  /**
85
93
  * @defaultValue false
86
94
  */
@@ -91,19 +99,22 @@ export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'>
91
99
 
92
100
  export interface DropdownMenuEmits extends DropdownMenuRootEmits {}
93
101
 
94
- type SlotProps<T> = (props: { item: T, active?: boolean, index: number }) => any
102
+ type SlotProps<T extends DropdownMenuItem> = (props: { item: T, active?: boolean, index: number }) => any
95
103
 
96
- export type DropdownMenuSlots<T extends { slot?: string }> = {
104
+ export type DropdownMenuSlots<
105
+ A extends ArrayOrNested<DropdownMenuItem> = ArrayOrNested<DropdownMenuItem>,
106
+ T extends NestedItem<A> = NestedItem<A>
107
+ > = {
97
108
  'default'(props: { open: boolean }): any
98
109
  'item': SlotProps<T>
99
110
  'item-leading': SlotProps<T>
100
111
  'item-label': SlotProps<T>
101
112
  'item-trailing': SlotProps<T>
102
- } & DynamicSlots<T, SlotProps<T>>
113
+ } & DynamicSlots<MergeTypes<T>, 'leading' | 'label' | 'trailing', { active?: boolean, index: number }>
103
114
 
104
115
  </script>
105
116
 
106
- <script setup lang="ts" generic="T extends DropdownMenuItem">
117
+ <script setup lang="ts" generic="T extends ArrayOrNested<DropdownMenuItem>">
107
118
  import { computed, toRef } from 'vue'
108
119
  import { defu } from 'defu'
109
120
  import { DropdownMenuRoot, DropdownMenuTrigger, DropdownMenuArrow, useForwardPropsEmits } from 'reka-ui'
@@ -123,7 +134,7 @@ const slots = defineSlots<DropdownMenuSlots<T>>()
123
134
  const rootProps = useForwardPropsEmits(reactivePick(props, 'defaultOpen', 'open', 'modal'), emits)
124
135
  const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8 }) as DropdownMenuContentProps)
125
136
  const arrowProps = toRef(() => props.arrow as DropdownMenuArrowProps)
126
- const proxySlots = omit(slots, ['default']) as Record<string, DropdownMenuSlots<T>[string]>
137
+ const proxySlots = omit(slots, ['default'])
127
138
 
128
139
  const b24ui = computed(() => dropdownMenu({
129
140
  size: props.size
@@ -143,12 +154,12 @@ const b24ui = computed(() => dropdownMenu({
143
154
  v-bind="contentProps"
144
155
  :items="items"
145
156
  :portal="portal"
146
- :label-key="labelKey"
157
+ :label-key="(labelKey as keyof NestedItem<T>)"
147
158
  :checked-icon="checkedIcon"
148
159
  :external-icon="externalIcon"
149
160
  >
150
161
  <template v-for="(_, name) in proxySlots" #[name]="slotData">
151
- <slot :name="name" v-bind="slotData" />
162
+ <slot :name="(name as keyof DropdownMenuSlots<T>)" v-bind="slotData" />
152
163
  </template>
153
164
 
154
165
  <DropdownMenuArrow v-if="!!arrow" v-bind="arrowProps" :class="b24ui.arrow({ class: props.b24ui?.arrow })" />
@@ -4,14 +4,15 @@ import type { DropdownMenuContentProps as RekaDropdownMenuContentProps, Dropdown
4
4
  import theme from '#build/b24ui/dropdown-menu'
5
5
  import { tv } from '../utils/tv'
6
6
  import type { KbdProps, AvatarProps, DropdownMenuItem, DropdownMenuSlots, IconComponent } from '../types'
7
+ import type { ArrayOrNested, NestedItem } from '../types/utils'
7
8
 
8
9
  const _dropdownMenu = tv(theme)()
9
10
 
10
- interface DropdownMenuContentProps<T> extends Omit<RekaDropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'> {
11
- items?: T[] | T[][]
11
+ interface DropdownMenuContentProps<T extends ArrayOrNested<DropdownMenuItem>> extends Omit<RekaDropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'> {
12
+ items?: T
12
13
  portal?: boolean
13
14
  sub?: boolean
14
- labelKey: string
15
+ labelKey: keyof NestedItem<T>
15
16
  /**
16
17
  * @IconComponent
17
18
  */
@@ -27,17 +28,17 @@ interface DropdownMenuContentProps<T> extends Omit<RekaDropdownMenuContentProps,
27
28
 
28
29
  interface DropdownMenuContentEmits extends RekaDropdownMenuContentEmits {}
29
30
 
30
- type DropdownMenuContentSlots<T extends { slot?: string }> = Omit<DropdownMenuSlots<T>, 'default'> & {
31
+ type DropdownMenuContentSlots<T extends ArrayOrNested<DropdownMenuItem>> = Omit<DropdownMenuSlots<T>, 'default'> & {
31
32
  default(props?: {}): any
32
33
  }
33
34
  </script>
34
35
 
35
- <script setup lang="ts" generic="T extends DropdownMenuItem">
36
+ <script setup lang="ts" generic="T extends ArrayOrNested<DropdownMenuItem>">
36
37
  import { computed } from 'vue'
37
38
  import { DropdownMenu } from 'reka-ui/namespaced'
38
39
  import { useForwardPropsEmits } from 'reka-ui'
39
40
  import { reactiveOmit, createReusableTemplate } from '@vueuse/core'
40
- import { omit, get } from '../utils'
41
+ import { omit, get, isArrayOfArray } from '../utils'
41
42
  import { pickLinkProps } from '../utils/link'
42
43
  import icons from '../dictionary/icons'
43
44
  import B24LinkBase from './LinkBase.vue'
@@ -52,17 +53,23 @@ const emits = defineEmits<DropdownMenuContentEmits>()
52
53
  const slots = defineSlots<DropdownMenuContentSlots<T>>()
53
54
 
54
55
  const contentProps = useForwardPropsEmits(reactiveOmit(props, 'sub', 'items', 'portal', 'labelKey', 'checkedIcon', 'externalIcon', 'class', 'b24ui', 'b24uiOverride'), emits)
55
- const proxySlots = omit(slots, ['default']) as Record<string, DropdownMenuContentSlots<T>[string]>
56
+ const proxySlots = omit(slots, ['default'])
56
57
 
57
58
  const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate<{ item: DropdownMenuItem, active?: boolean, index: number }>()
58
59
 
59
- const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0]) ? props.items : [props.items]) as T[][] : [])
60
+ const groups = computed<DropdownMenuItem[][]>(() =>
61
+ props.items?.length
62
+ ? isArrayOfArray(props.items)
63
+ ? props.items
64
+ : [props.items]
65
+ : []
66
+ )
60
67
  </script>
61
68
 
62
69
  <template>
63
70
  <DefineItemTemplate v-slot="{ item, active, index }">
64
- <slot :name="item.slot || 'item'" :item="(item as T)" :index="index">
65
- <slot :name="item.slot ? `${item.slot}-leading`: 'item-leading'" :item="(item as T)" :active="active" :index="index">
71
+ <slot :name="((item.slot || 'item') as keyof DropdownMenuContentSlots<T>)" :item="(item as Extract<NestedItem<T>, { slot: string; }>)" :index="index">
72
+ <slot :name="((item.slot ? `${item.slot}-leading`: 'item-leading') as keyof DropdownMenuContentSlots<T>)" :item="(item as Extract<NestedItem<T>, { slot: string; }>)" :active="active" :index="index">
66
73
  <Component
67
74
  :is="icons.loading"
68
75
  v-if="item.loading"
@@ -81,8 +88,8 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
81
88
  />
82
89
  </slot>
83
90
 
84
- <span v-if="get(item, props.labelKey as string) || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="b24ui.itemLabel({ class: b24uiOverride?.itemLabel, active })">
85
- <slot :name="item.slot ? `${item.slot}-label`: 'item-label'" :item="(item as T)" :active="active" :index="index">
91
+ <span v-if="get(item, props.labelKey as string) || !!slots[(item.slot ? `${item.slot}-label`: 'item-label') as keyof DropdownMenuContentSlots<T>]" :class="b24ui.itemLabel({ class: b24uiOverride?.itemLabel, active })">
92
+ <slot :name="((item.slot ? `${item.slot}-label`: 'item-label') as keyof DropdownMenuContentSlots<T>)" :item="(item as Extract<NestedItem<T>, { slot: string; }>)" :active="active" :index="index">
86
93
  {{ get(item, props.labelKey as string) }}
87
94
  </slot>
88
95
  <Component
@@ -93,7 +100,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
93
100
  </span>
94
101
 
95
102
  <span :class="b24ui.itemTrailing({ class: b24uiOverride?.itemTrailing })">
96
- <slot :name="item.slot ? `${item.slot}-trailing`: 'item-trailing'" :item="(item as T)" :active="active" :index="index">
103
+ <slot :name="((item.slot ? `${item.slot}-trailing`: 'item-trailing') as keyof DropdownMenuContentSlots<T>)" :item="(item as Extract<NestedItem<T>, { slot: string; }>)" :active="active" :index="index">
97
104
  <Component
98
105
  :is="icons.chevronRight"
99
106
  v-if="item.children?.length"
@@ -139,7 +146,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
139
146
  :b24ui="b24ui"
140
147
  :b24ui-override="b24uiOverride"
141
148
  :portal="portal"
142
- :items="item.children"
149
+ :items="(item.children as T)"
143
150
  side="right"
144
151
  align="start"
145
152
  :align-offset="-4"
@@ -150,7 +157,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
150
157
  v-bind="item.content"
151
158
  >
152
159
  <template v-for="(_, name) in proxySlots" #[name]="slotData: any">
153
- <slot :name="name" v-bind="slotData" />
160
+ <slot :name="(name as keyof DropdownMenuContentSlots<T>)" v-bind="slotData" />
154
161
  </template>
155
162
  </B24DropdownMenuContent>
156
163
  </DropdownMenu.Sub>