@bitrix24/b24ui-nuxt 2.1.3 → 2.1.5

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 (29) hide show
  1. package/dist/meta.d.mts +138 -113
  2. package/dist/meta.mjs +138 -113
  3. package/dist/module.json +1 -1
  4. package/dist/module.mjs +1 -1
  5. package/dist/runtime/components/CheckboxGroup.vue +3 -2
  6. package/dist/runtime/components/CommandPalette.vue +8 -2
  7. package/dist/runtime/components/DashboardSearchButton.vue +4 -3
  8. package/dist/runtime/components/InputMenu.vue +15 -13
  9. package/dist/runtime/components/NavigationMenu.d.vue.ts +44 -9
  10. package/dist/runtime/components/NavigationMenu.vue +19 -11
  11. package/dist/runtime/components/NavigationMenu.vue.d.ts +44 -9
  12. package/dist/runtime/components/RadioGroup.vue +5 -5
  13. package/dist/runtime/components/SelectMenu.vue +14 -11
  14. package/dist/runtime/components/Table.d.vue.ts +2 -2
  15. package/dist/runtime/components/Table.vue +15 -2
  16. package/dist/runtime/components/Table.vue.d.ts +2 -2
  17. package/dist/runtime/components/color-mode/ColorModeButton.d.vue.ts +2 -9
  18. package/dist/runtime/components/color-mode/ColorModeButton.vue +8 -13
  19. package/dist/runtime/components/color-mode/ColorModeButton.vue.d.ts +2 -9
  20. package/dist/runtime/components/content/ContentSearchButton.vue +4 -3
  21. package/dist/runtime/utils/virtualizer.d.ts +6 -0
  22. package/dist/runtime/utils/virtualizer.js +32 -0
  23. package/dist/shared/{b24ui-nuxt.mq1NUekN.mjs → b24ui-nuxt.Dspx5yCK.mjs} +85 -49
  24. package/dist/unplugin.mjs +1 -1
  25. package/dist/vite.mjs +1 -1
  26. package/package.json +1 -1
  27. package/dist/runtime/vue/components/color-mode/ColorModeButton.d.vue.ts +0 -12
  28. package/dist/runtime/vue/components/color-mode/ColorModeButton.vue +0 -83
  29. package/dist/runtime/vue/components/color-mode/ColorModeButton.vue.d.ts +0 -12
@@ -15,6 +15,7 @@ import { useFormField } from "../composables/useFormField";
15
15
  import { useLocale } from "../composables/useLocale";
16
16
  import { usePortal } from "../composables/usePortal";
17
17
  import { compare, get, getDisplayValue, isArrayOfArray } from "../utils";
18
+ import { getEstimateSize } from "../utils/virtualizer";
18
19
  import { tv } from "../utils/tv";
19
20
  import icons from "../dictionary/icons";
20
21
  import B24Badge from "./Badge.vue";
@@ -80,16 +81,12 @@ const rootProps = useForwardPropsEmits(reactivePick(props, "as", "modelValue", "
80
81
  const portalProps = usePortal(toRef(() => props.portal));
81
82
  const contentProps = toRef(() => defu(props.content, { side: "bottom", sideOffset: 8, collisionPadding: 8, position: "popper" }));
82
83
  const arrowProps = toRef(() => defu(typeof props.arrow === "boolean" ? {} : props.arrow, { width: 20, height: 10 }));
83
- const virtualizerProps = toRef(() => !!props.virtualize && defu(typeof props.virtualize === "boolean" ? {} : props.virtualize, {
84
- estimateSize: {
85
- xss: 20,
86
- xs: 24,
87
- sm: 28,
88
- md: 32,
89
- lg: 36,
90
- xl: 40
91
- }[props.size || "md"]
92
- }));
84
+ const virtualizerProps = toRef(() => {
85
+ if (!props.virtualize) return false;
86
+ return defu(typeof props.virtualize === "boolean" ? {} : props.virtualize, {
87
+ estimateSize: getEstimateSize(items.value, inputSize.value || "md", props.descriptionKey)
88
+ });
89
+ });
93
90
  const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, size: formGroupSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField(props);
94
91
  const { orientation, size: fieldGroupSize } = useFieldGroup(props);
95
92
  const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(toRef(() => defu(props, { trailingIcon: icons.chevronDown })));
@@ -230,6 +227,11 @@ function onRemoveTag(event, modelValue) {
230
227
  onUpdate(filteredValue);
231
228
  }
232
229
  }
230
+ function onCreate(e) {
231
+ e.preventDefault();
232
+ e.stopPropagation();
233
+ emits("create", searchTerm.value);
234
+ }
233
235
  function onSelect(e, item) {
234
236
  if (!isInputItem(item)) {
235
237
  return;
@@ -254,7 +256,7 @@ defineExpose({
254
256
  data-slot="item"
255
257
  :class="b24ui.item({ addNew: true, class: props.b24ui?.item })"
256
258
  :value="searchTerm"
257
- @select.prevent="emits('create', searchTerm)"
259
+ @select="onCreate"
258
260
  >
259
261
  <span data-slot="itemLabel" :class="b24ui.itemLabel({ addNew: true, class: props.b24ui?.itemLabel })">
260
262
  <slot name="create-item-label" :item="searchTerm">
@@ -353,7 +355,6 @@ defineExpose({
353
355
  ignore-filter
354
356
  @update:model-value="onUpdate"
355
357
  @update:open="onUpdateOpen"
356
- @keydown.enter="$event.preventDefault()"
357
358
  >
358
359
  <B24Badge
359
360
  v-if="!multiple && isTag"
@@ -410,7 +411,7 @@ defineExpose({
410
411
  :placeholder="placeholder"
411
412
  data-slot="tagsInput"
412
413
  :class="b24ui.tagsInput({ class: props.b24ui?.tagsInput })"
413
- @keydown.enter.prevent
414
+ @change.stop
414
415
  />
415
416
  </ComboboxInput>
416
417
  </TagsInputRoot>
@@ -426,6 +427,7 @@ defineExpose({
426
427
  :required="required"
427
428
  @blur="onBlur"
428
429
  @focus="onFocus"
430
+ @change.stop
429
431
  @update:model-value="searchTerm = $event"
430
432
  />
431
433
 
@@ -1,4 +1,4 @@
1
- import type { NavigationMenuRootProps, NavigationMenuRootEmits, NavigationMenuContentProps, NavigationMenuContentEmits, AccordionRootProps } from 'reka-ui';
1
+ import type { NavigationMenuRootProps, NavigationMenuContentProps, NavigationMenuContentEmits, AccordionRootProps } from 'reka-ui';
2
2
  import type { AppConfig } from '@nuxt/schema';
3
3
  import theme from '#build/b24ui/navigation-menu';
4
4
  import type { AvatarProps, BadgeProps, IconComponent, LinkProps, PopoverProps, TooltipProps } from '../types';
@@ -75,6 +75,9 @@ export interface NavigationMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'cu
75
75
  b24ui?: Pick<NavigationMenu['slots'], 'item' | 'linkLeadingAvatarSize' | 'linkLeadingAvatar' | 'linkLeadingIcon' | 'linkLabel' | 'linkLabelExternalIcon' | 'linkTrailing' | 'linkLeadingHint' | 'linkLeadingBadgeSize' | 'linkLeadingBadge' | 'linkTrailingIcon' | 'label' | 'link' | 'content' | 'childList' | 'childLabel' | 'childItem' | 'childLink' | 'childLinkIcon' | 'childLinkHint' | 'childLinkBadgeSize' | 'childLinkBadge' | 'childLinkWrapper' | 'childLinkLabel' | 'childLinkLabelExternalIcon' | 'popoverWrapper'>;
76
76
  [key: string]: any;
77
77
  }
78
+ type SingleOrMultipleType = 'single' | 'multiple';
79
+ type Orientation = NavigationMenuRootProps['orientation'];
80
+ type NavigationMenuModelValue<K extends SingleOrMultipleType = SingleOrMultipleType, O extends Orientation = Orientation> = O extends 'horizontal' ? string : K extends 'single' ? string : K extends 'multiple' ? string[] : string | string[];
78
81
  /**
79
82
  * @memo remove contentOrientation
80
83
  * @memo remove highlight
@@ -83,12 +86,37 @@ export interface NavigationMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'cu
83
86
  * @memo remove color
84
87
  * @memo remove variant (link) -> use variant.pill
85
88
  */
86
- export interface NavigationMenuProps<T extends ArrayOrNested<NavigationMenuItem> = ArrayOrNested<NavigationMenuItem>> extends Pick<NavigationMenuRootProps, 'modelValue' | 'defaultValue' | 'delayDuration' | 'disableClickTrigger' | 'disableHoverTrigger' | 'skipDelayDuration' | 'disablePointerLeaveClose' | 'unmountOnHide'>, Pick<AccordionRootProps, 'disabled' | 'type' | 'collapsible'> {
89
+ export interface NavigationMenuProps<T extends ArrayOrNested<NavigationMenuItem> = ArrayOrNested<NavigationMenuItem>, K extends SingleOrMultipleType = SingleOrMultipleType, O extends Orientation = Orientation> extends Pick<NavigationMenuRootProps, 'delayDuration' | 'disableClickTrigger' | 'disableHoverTrigger' | 'skipDelayDuration' | 'disablePointerLeaveClose' | 'unmountOnHide'>, Pick<AccordionRootProps, 'disabled' | 'collapsible'> {
87
90
  /**
88
91
  * The element or component this component should render as.
89
92
  * @defaultValue 'div'
90
93
  */
91
94
  as?: any;
95
+ /**
96
+ * Determines whether a "single" or "multiple" items can be selected at a time.
97
+ *
98
+ * Only works when `orientation` is `vertical`.
99
+ * @defaultValue 'multiple'
100
+ */
101
+ type?: K;
102
+ /**
103
+ * The controlled value of the active item(s).
104
+ * - In horizontal orientation: always `string`
105
+ * - In vertical orientation with `type="single"`: `string`
106
+ * - In vertical orientation with `type="multiple"`: `string[]`
107
+ *
108
+ * Use this when you need to control the state of the items. Can be binded with `v-model`
109
+ */
110
+ modelValue?: NavigationMenuModelValue<K, O>;
111
+ /**
112
+ * The default active value of the item(s).
113
+ * - In horizontal orientation: always `string`
114
+ * - In vertical orientation with `type="single"`: `string`
115
+ * - In vertical orientation with `type="multiple"`: `string[]`
116
+ *
117
+ * Use when you do not need to control the state of the item(s).
118
+ */
119
+ defaultValue?: NavigationMenuModelValue<K, O>;
92
120
  /**
93
121
  * The icon displayed to open the menu.
94
122
  * @defaultValue icons.chevronDown
@@ -107,7 +135,7 @@ export interface NavigationMenuProps<T extends ArrayOrNested<NavigationMenuItem>
107
135
  * The orientation of the menu.
108
136
  * @defaultValue 'horizontal'
109
137
  */
110
- orientation?: NavigationMenuRootProps['orientation'];
138
+ orientation?: O;
111
139
  /**
112
140
  * Collapse the navigation menu to only show icons.
113
141
  * Only works when `orientation` is `vertical`.
@@ -138,8 +166,15 @@ export interface NavigationMenuProps<T extends ArrayOrNested<NavigationMenuItem>
138
166
  class?: any;
139
167
  b24ui?: NavigationMenu['slots'];
140
168
  }
141
- export interface NavigationMenuEmits extends NavigationMenuRootEmits {
142
- }
169
+ export type NavigationMenuEmits<K extends SingleOrMultipleType = SingleOrMultipleType, O extends Orientation = Orientation> = {
170
+ /**
171
+ * Event handler called when the value changes.
172
+ * - In horizontal orientation: emits `string`
173
+ * - In vertical orientation with `type="single"`: emits `string | undefined`
174
+ * - In vertical orientation with `type="multiple"`: emits `string[] | undefined`
175
+ */
176
+ 'update:modelValue': [value: NavigationMenuModelValue<K, O> | undefined];
177
+ };
143
178
  type SlotProps<T extends NavigationMenuItem> = (props: {
144
179
  item: T;
145
180
  index: number;
@@ -169,14 +204,14 @@ export type NavigationMenuSlots<A extends ArrayOrNested<NavigationMenuItem> = Ar
169
204
  active?: boolean;
170
205
  b24ui: NavigationMenu['b24ui'];
171
206
  }>;
172
- declare const __VLS_export: <T extends ArrayOrNested<NavigationMenuItem>>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
173
- props: __VLS_PrettifyLocal<NavigationMenuProps<T> & {
174
- "onUpdate:modelValue"?: ((value: string) => any) | undefined;
207
+ declare const __VLS_export: <T extends ArrayOrNested<NavigationMenuItem>, K extends SingleOrMultipleType = SingleOrMultipleType, O extends Orientation = Orientation>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
208
+ props: __VLS_PrettifyLocal<NavigationMenuProps<T, K, O> & {
209
+ "onUpdate:modelValue"?: ((value: NavigationMenuModelValue<K, O> | undefined) => any) | undefined;
175
210
  }> & import("vue").PublicProps;
176
211
  expose: (exposed: {}) => void;
177
212
  attrs: any;
178
213
  slots: NavigationMenuSlots<T, NestedItem<T>>;
179
- emit: (evt: "update:modelValue", value: string) => void;
214
+ emit: (evt: "update:modelValue", value: NavigationMenuModelValue<K, O> | undefined) => void;
180
215
  }>) => import("vue").VNode & {
181
216
  __ctx?: Awaited<typeof __VLS_setup>;
182
217
  };
@@ -21,6 +21,9 @@ import B24Tooltip from "./Tooltip.vue";
21
21
  defineOptions({ inheritAttrs: false });
22
22
  const props = defineProps({
23
23
  as: { type: null, required: false },
24
+ type: { type: null, required: false, default: "multiple" },
25
+ modelValue: { type: null, required: false },
26
+ defaultValue: { type: null, required: false },
24
27
  trailingIcon: { type: [Function, Object], required: false },
25
28
  externalIcon: { type: [Boolean, Function, Object], required: false, default: true },
26
29
  items: { type: null, required: false },
@@ -32,8 +35,6 @@ const props = defineProps({
32
35
  labelKey: { type: null, required: false, default: "label" },
33
36
  class: { type: null, required: false },
34
37
  b24ui: { type: null, required: false },
35
- modelValue: { type: String, required: false },
36
- defaultValue: { type: String, required: false },
37
38
  delayDuration: { type: Number, required: false, default: 0 },
38
39
  disableClickTrigger: { type: Boolean, required: false },
39
40
  disableHoverTrigger: { type: Boolean, required: false },
@@ -41,7 +42,6 @@ const props = defineProps({
41
42
  disablePointerLeaveClose: { type: Boolean, required: false },
42
43
  unmountOnHide: { type: Boolean, required: false, default: true },
43
44
  disabled: { type: Boolean, required: false },
44
- type: { type: String, required: false, default: "multiple" },
45
45
  collapsible: { type: Boolean, required: false, default: true }
46
46
  });
47
47
  const emits = defineEmits(["update:modelValue"]);
@@ -49,8 +49,6 @@ const slots = defineSlots();
49
49
  const appConfig = useAppConfig();
50
50
  const rootProps = useForwardPropsEmits(computed(() => ({
51
51
  as: props.as,
52
- modelValue: props.modelValue,
53
- defaultValue: props.defaultValue,
54
52
  delayDuration: props.delayDuration,
55
53
  skipDelayDuration: props.skipDelayDuration,
56
54
  orientation: props.orientation,
@@ -133,7 +131,7 @@ function getAccordionDefaultValue(list, level = 0) {
133
131
  </slot>
134
132
 
135
133
  <span
136
- v-if="(!collapsed || orientation !== 'vertical') && (get(item, props.labelKey) || !!slots[item.slot ? `${item.slot}-label` : 'item-label'])"
134
+ v-if="get(item, props.labelKey) || !!slots[item.slot ? `${item.slot}-label` : 'item-label']"
137
135
  data-slot="linkLabel"
138
136
  :class="b24ui.linkLabel({ class: [props.b24ui?.linkLabel, item.b24ui?.linkLabel], active })"
139
137
  >
@@ -151,8 +149,10 @@ function getAccordionDefaultValue(list, level = 0) {
151
149
 
152
150
  <component
153
151
  :is="orientation === 'vertical' && item.children?.length && !collapsed ? AccordionTrigger : 'span'"
154
- v-if="(!collapsed || orientation !== 'vertical') && /* (item.badge || item.badge === 0) || */
155
- (orientation === 'horizontal' && (item.children?.length || !!slots[item.slot ? `${item.slot}-content` : 'item-content']) || orientation === 'vertical' && item.children?.length || item.trailingIcon || !!slots[item.slot ? `${item.slot}-trailing` : 'item-trailing'])"
152
+ v-if="
153
+ /* (item.badge || item.badge === 0) || */
154
+ orientation === 'horizontal' && (item.children?.length || !!slots[item.slot ? `${item.slot}-content` : 'item-content']) || orientation === 'vertical' && item.children?.length || item.trailingIcon || !!slots[item.slot ? `${item.slot}-trailing` : 'item-trailing']
155
+ "
156
156
  as="span"
157
157
  data-slot="linkTrailing"
158
158
  :class="b24ui.linkTrailing({ class: [props.b24ui?.linkTrailing, item.b24ui?.linkTrailing] })"
@@ -421,7 +421,14 @@ function getAccordionDefaultValue(list, level = 0) {
421
421
  </DefineItemTemplate>
422
422
 
423
423
  <NavigationMenuRoot
424
- v-bind="{ ...rootProps, ...$attrs }"
424
+ v-bind="{
425
+ ...rootProps,
426
+ ...orientation === 'horizontal' ? {
427
+ modelValue,
428
+ defaultValue
429
+ } : {},
430
+ ...$attrs
431
+ }"
425
432
  :data-collapsed="collapsed"
426
433
  data-component="section"
427
434
  data-slot="root"
@@ -433,9 +440,10 @@ function getAccordionDefaultValue(list, level = 0) {
433
440
  <component
434
441
  v-bind="orientation === 'vertical' && !collapsed ? {
435
442
  ...accordionProps,
436
- defaultValue: getAccordionDefaultValue(list)
443
+ modelValue,
444
+ defaultValue: defaultValue ?? getAccordionDefaultValue(list)
437
445
  } : {}"
438
- :is="orientation === 'vertical' && !collapsed ? AccordionRoot : NavigationMenuList"
446
+ :is="orientation === 'vertical' ? AccordionRoot : NavigationMenuList"
439
447
  as="ul"
440
448
  data-slot="list"
441
449
  :class="b24ui.list({ class: props.b24ui?.list })"
@@ -1,4 +1,4 @@
1
- import type { NavigationMenuRootProps, NavigationMenuRootEmits, NavigationMenuContentProps, NavigationMenuContentEmits, AccordionRootProps } from 'reka-ui';
1
+ import type { NavigationMenuRootProps, NavigationMenuContentProps, NavigationMenuContentEmits, AccordionRootProps } from 'reka-ui';
2
2
  import type { AppConfig } from '@nuxt/schema';
3
3
  import theme from '#build/b24ui/navigation-menu';
4
4
  import type { AvatarProps, BadgeProps, IconComponent, LinkProps, PopoverProps, TooltipProps } from '../types';
@@ -75,6 +75,9 @@ export interface NavigationMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'cu
75
75
  b24ui?: Pick<NavigationMenu['slots'], 'item' | 'linkLeadingAvatarSize' | 'linkLeadingAvatar' | 'linkLeadingIcon' | 'linkLabel' | 'linkLabelExternalIcon' | 'linkTrailing' | 'linkLeadingHint' | 'linkLeadingBadgeSize' | 'linkLeadingBadge' | 'linkTrailingIcon' | 'label' | 'link' | 'content' | 'childList' | 'childLabel' | 'childItem' | 'childLink' | 'childLinkIcon' | 'childLinkHint' | 'childLinkBadgeSize' | 'childLinkBadge' | 'childLinkWrapper' | 'childLinkLabel' | 'childLinkLabelExternalIcon' | 'popoverWrapper'>;
76
76
  [key: string]: any;
77
77
  }
78
+ type SingleOrMultipleType = 'single' | 'multiple';
79
+ type Orientation = NavigationMenuRootProps['orientation'];
80
+ type NavigationMenuModelValue<K extends SingleOrMultipleType = SingleOrMultipleType, O extends Orientation = Orientation> = O extends 'horizontal' ? string : K extends 'single' ? string : K extends 'multiple' ? string[] : string | string[];
78
81
  /**
79
82
  * @memo remove contentOrientation
80
83
  * @memo remove highlight
@@ -83,12 +86,37 @@ export interface NavigationMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'cu
83
86
  * @memo remove color
84
87
  * @memo remove variant (link) -> use variant.pill
85
88
  */
86
- export interface NavigationMenuProps<T extends ArrayOrNested<NavigationMenuItem> = ArrayOrNested<NavigationMenuItem>> extends Pick<NavigationMenuRootProps, 'modelValue' | 'defaultValue' | 'delayDuration' | 'disableClickTrigger' | 'disableHoverTrigger' | 'skipDelayDuration' | 'disablePointerLeaveClose' | 'unmountOnHide'>, Pick<AccordionRootProps, 'disabled' | 'type' | 'collapsible'> {
89
+ export interface NavigationMenuProps<T extends ArrayOrNested<NavigationMenuItem> = ArrayOrNested<NavigationMenuItem>, K extends SingleOrMultipleType = SingleOrMultipleType, O extends Orientation = Orientation> extends Pick<NavigationMenuRootProps, 'delayDuration' | 'disableClickTrigger' | 'disableHoverTrigger' | 'skipDelayDuration' | 'disablePointerLeaveClose' | 'unmountOnHide'>, Pick<AccordionRootProps, 'disabled' | 'collapsible'> {
87
90
  /**
88
91
  * The element or component this component should render as.
89
92
  * @defaultValue 'div'
90
93
  */
91
94
  as?: any;
95
+ /**
96
+ * Determines whether a "single" or "multiple" items can be selected at a time.
97
+ *
98
+ * Only works when `orientation` is `vertical`.
99
+ * @defaultValue 'multiple'
100
+ */
101
+ type?: K;
102
+ /**
103
+ * The controlled value of the active item(s).
104
+ * - In horizontal orientation: always `string`
105
+ * - In vertical orientation with `type="single"`: `string`
106
+ * - In vertical orientation with `type="multiple"`: `string[]`
107
+ *
108
+ * Use this when you need to control the state of the items. Can be binded with `v-model`
109
+ */
110
+ modelValue?: NavigationMenuModelValue<K, O>;
111
+ /**
112
+ * The default active value of the item(s).
113
+ * - In horizontal orientation: always `string`
114
+ * - In vertical orientation with `type="single"`: `string`
115
+ * - In vertical orientation with `type="multiple"`: `string[]`
116
+ *
117
+ * Use when you do not need to control the state of the item(s).
118
+ */
119
+ defaultValue?: NavigationMenuModelValue<K, O>;
92
120
  /**
93
121
  * The icon displayed to open the menu.
94
122
  * @defaultValue icons.chevronDown
@@ -107,7 +135,7 @@ export interface NavigationMenuProps<T extends ArrayOrNested<NavigationMenuItem>
107
135
  * The orientation of the menu.
108
136
  * @defaultValue 'horizontal'
109
137
  */
110
- orientation?: NavigationMenuRootProps['orientation'];
138
+ orientation?: O;
111
139
  /**
112
140
  * Collapse the navigation menu to only show icons.
113
141
  * Only works when `orientation` is `vertical`.
@@ -138,8 +166,15 @@ export interface NavigationMenuProps<T extends ArrayOrNested<NavigationMenuItem>
138
166
  class?: any;
139
167
  b24ui?: NavigationMenu['slots'];
140
168
  }
141
- export interface NavigationMenuEmits extends NavigationMenuRootEmits {
142
- }
169
+ export type NavigationMenuEmits<K extends SingleOrMultipleType = SingleOrMultipleType, O extends Orientation = Orientation> = {
170
+ /**
171
+ * Event handler called when the value changes.
172
+ * - In horizontal orientation: emits `string`
173
+ * - In vertical orientation with `type="single"`: emits `string | undefined`
174
+ * - In vertical orientation with `type="multiple"`: emits `string[] | undefined`
175
+ */
176
+ 'update:modelValue': [value: NavigationMenuModelValue<K, O> | undefined];
177
+ };
143
178
  type SlotProps<T extends NavigationMenuItem> = (props: {
144
179
  item: T;
145
180
  index: number;
@@ -169,14 +204,14 @@ export type NavigationMenuSlots<A extends ArrayOrNested<NavigationMenuItem> = Ar
169
204
  active?: boolean;
170
205
  b24ui: NavigationMenu['b24ui'];
171
206
  }>;
172
- declare const __VLS_export: <T extends ArrayOrNested<NavigationMenuItem>>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
173
- props: __VLS_PrettifyLocal<NavigationMenuProps<T> & {
174
- "onUpdate:modelValue"?: ((value: string) => any) | undefined;
207
+ declare const __VLS_export: <T extends ArrayOrNested<NavigationMenuItem>, K extends SingleOrMultipleType = SingleOrMultipleType, O extends Orientation = Orientation>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
208
+ props: __VLS_PrettifyLocal<NavigationMenuProps<T, K, O> & {
209
+ "onUpdate:modelValue"?: ((value: NavigationMenuModelValue<K, O> | undefined) => any) | undefined;
175
210
  }> & import("vue").PublicProps;
176
211
  expose: (exposed: {}) => void;
177
212
  attrs: any;
178
213
  slots: NavigationMenuSlots<T, NestedItem<T>>;
179
- emit: (evt: "update:modelValue", value: string) => void;
214
+ emit: (evt: "update:modelValue", value: NavigationMenuModelValue<K, O> | undefined) => void;
180
215
  }>) => import("vue").VNode & {
181
216
  __ctx?: Awaited<typeof __VLS_setup>;
182
217
  };
@@ -111,15 +111,15 @@ function onUpdate(value) {
111
111
  v-for="item in normalizedItems"
112
112
  :key="item.value"
113
113
  data-slot="item"
114
- :class="b24ui.item({ class: [props.b24ui?.item, item.b24ui?.item, item.class] })"
114
+ :class="b24ui.item({ class: [props.b24ui?.item, item.b24ui?.item, item.class], disabled: item.disabled || disabled })"
115
115
  >
116
116
  <div data-slot="container" :class="b24ui.container({ class: [props.b24ui?.container, item.b24ui?.container] })">
117
117
  <RRadioGroupItem
118
118
  :id="item.id"
119
119
  :value="item.value"
120
- :disabled="item.disabled"
120
+ :disabled="item.disabled || disabled"
121
121
  data-slot="base"
122
- :class="b24ui.base({ class: [props.b24ui?.base, item.b24ui?.base], disabled: item.disabled })"
122
+ :class="b24ui.base({ class: [props.b24ui?.base, item.b24ui?.base], disabled: item.disabled || disabled })"
123
123
  >
124
124
  <RadioGroupIndicator data-slot="indicator" :class="b24ui.indicator({ class: [props.b24ui?.indicator, item.b24ui?.indicator] })" />
125
125
  </RRadioGroupItem>
@@ -135,7 +135,7 @@ function onUpdate(value) {
135
135
  v-if="item.label || !!slots.label"
136
136
  :for="item.id"
137
137
  data-slot="label"
138
- :class="b24ui.label({ class: [props.b24ui?.label, item.b24ui?.label] })"
138
+ :class="b24ui.label({ class: [props.b24ui?.label, item.b24ui?.label], disabled: item.disabled || disabled })"
139
139
  >
140
140
  <slot name="label" :item="item" :model-value="modelValue">
141
141
  {{ item.label }}
@@ -144,7 +144,7 @@ function onUpdate(value) {
144
144
  <p
145
145
  v-if="item.description || !!slots.description"
146
146
  data-slot="description"
147
- :class="b24ui.description({ class: [props.b24ui?.description, item.b24ui?.description] })"
147
+ :class="b24ui.description({ class: [props.b24ui?.description, item.b24ui?.description], disabled: item.disabled || disabled })"
148
148
  >
149
149
  <slot name="description" :item="item" :model-value="modelValue">
150
150
  {{ item.description }}
@@ -14,6 +14,7 @@ import { useFormField } from "../composables/useFormField";
14
14
  import { useLocale } from "../composables/useLocale";
15
15
  import { usePortal } from "../composables/usePortal";
16
16
  import { compare, get, getDisplayValue, isArrayOfArray } from "../utils";
17
+ import { getEstimateSize } from "../utils/virtualizer";
17
18
  import { tv } from "../utils/tv";
18
19
  import icons from "../dictionary/icons";
19
20
  import B24Badge from "./Badge.vue";
@@ -77,16 +78,12 @@ const rootProps = useForwardPropsEmits(reactivePick(props, "modelValue", "defaul
77
78
  const portalProps = usePortal(toRef(() => props.portal));
78
79
  const contentProps = toRef(() => defu(props.content, { side: "bottom", sideOffset: 8, collisionPadding: 8, position: "popper" }));
79
80
  const arrowProps = toRef(() => defu(typeof props.arrow === "boolean" ? {} : props.arrow, { width: 20, height: 10 }));
80
- const virtualizerProps = toRef(() => !!props.virtualize && defu(typeof props.virtualize === "boolean" ? {} : props.virtualize, {
81
- estimateSize: {
82
- xss: 20,
83
- xs: 24,
84
- sm: 28,
85
- md: 32,
86
- lg: 36,
87
- xl: 40
88
- }[props.size || "md"]
89
- }));
81
+ const virtualizerProps = toRef(() => {
82
+ if (!props.virtualize) return false;
83
+ return defu(typeof props.virtualize === "boolean" ? {} : props.virtualize, {
84
+ estimateSize: getEstimateSize(items.value, props.size || "md", props.descriptionKey)
85
+ });
86
+ });
90
87
  const searchInputProps = toRef(() => defu(props.searchInput, { placeholder: t("selectMenu.search"), type: "text", size: "md" }));
91
88
  const { emitFormBlur, emitFormFocus, emitFormInput, emitFormChange, size: formGroupSize, color, id, name, highlight, disabled, ariaAttrs } = useFormField(props);
92
89
  const { orientation, size: fieldGroupSize } = useFieldGroup(props);
@@ -218,6 +215,11 @@ function onUpdateOpen(value) {
218
215
  clearTimeout(timeoutId);
219
216
  }
220
217
  }
218
+ function onCreate(e) {
219
+ e.preventDefault();
220
+ e.stopPropagation();
221
+ emits("create", searchTerm.value);
222
+ }
221
223
  function onSelect(e, item) {
222
224
  if (!isSelectItem(item)) {
223
225
  return;
@@ -242,7 +244,7 @@ defineExpose({
242
244
  data-slot="item"
243
245
  :class="b24ui.item({ addNew: true, class: props.b24ui?.item })"
244
246
  :value="searchTerm"
245
- @select.prevent="emits('create', searchTerm)"
247
+ @select="onCreate"
246
248
  >
247
249
  <span data-slot="itemLabel" :class="b24ui.itemLabel({ addNew: true, class: props.b24ui?.itemLabel })">
248
250
  <slot name="create-item-label" :item="searchTerm">
@@ -440,6 +442,7 @@ defineExpose({
440
442
  v-bind="searchInputProps"
441
443
  data-slot="input"
442
444
  :class="b24ui.input({ class: props.b24ui?.input })"
445
+ @change.stop
443
446
  />
444
447
  </ComboboxInput>
445
448
 
@@ -63,10 +63,10 @@ export interface TableProps<T extends TableData = TableData> extends TableOption
63
63
  */
64
64
  overscan?: number;
65
65
  /**
66
- * Estimated size (in px) of each item
66
+ * Estimated size (in px) of each item, or a function that returns the size for a given index
67
67
  * @defaultValue 65
68
68
  */
69
- estimateSize?: number;
69
+ estimateSize?: number | ((index: number) => number);
70
70
  });
71
71
  /**
72
72
  * The text to display when the table is empty.
@@ -225,7 +225,20 @@ const virtualizer = !!props.virtualize && useVirtualizer({
225
225
  return rows.value.length;
226
226
  },
227
227
  getScrollElement: () => rootRef.value?.$el,
228
- estimateSize: () => virtualizerProps.value.estimateSize
228
+ estimateSize: (index) => {
229
+ const estimate = virtualizerProps.value.estimateSize;
230
+ return typeof estimate === "function" ? estimate(index) : estimate;
231
+ }
232
+ });
233
+ const renderedSize = computed(() => {
234
+ if (!virtualizer) {
235
+ return 0;
236
+ }
237
+ const virtualItems = virtualizer.value.getVirtualItems();
238
+ if (!virtualItems?.length) {
239
+ return 0;
240
+ }
241
+ return virtualItems.reduce((sum, item) => sum + item.size, 0);
229
242
  });
230
243
  function valueUpdater(updaterOrValue, ref2) {
231
244
  ref2.value = typeof updaterOrValue === "function" ? updaterOrValue(ref2.value) : updaterOrValue;
@@ -406,7 +419,7 @@ defineExpose({
406
419
  data-slot="tfoot"
407
420
  :class="b24ui.tfoot({ class: [props.b24ui?.tfoot] })"
408
421
  :style="virtualizer ? {
409
- transform: `translateY(${virtualizer.getTotalSize() - virtualizer.getVirtualItems().length * virtualizerProps.estimateSize}px)`
422
+ transform: `translateY(${virtualizer.getTotalSize() - renderedSize}px)`
410
423
  } : void 0"
411
424
  >
412
425
  <tr data-slot="separator" :class="b24ui.separator({ class: [props.b24ui?.separator] })" />
@@ -63,10 +63,10 @@ export interface TableProps<T extends TableData = TableData> extends TableOption
63
63
  */
64
64
  overscan?: number;
65
65
  /**
66
- * Estimated size (in px) of each item
66
+ * Estimated size (in px) of each item, or a function that returns the size for a given index
67
67
  * @defaultValue 65
68
68
  */
69
- estimateSize?: number;
69
+ estimateSize?: number | ((index: number) => number);
70
70
  });
71
71
  /**
72
72
  * The text to display when the table is empty.
@@ -5,15 +5,8 @@ export interface ColorModeButtonProps extends Omit<ButtonProps, 'color'> {
5
5
  */
6
6
  color?: ButtonProps['color'];
7
7
  }
8
- declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<ColorModeButtonProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<ColorModeButtonProps> & Readonly<{}>, {
8
+ declare const __VLS_export: import("vue").DefineComponent<ColorModeButtonProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<ColorModeButtonProps> & Readonly<{}>, {
9
9
  color: "default" | "link" | "air-primary" | "air-primary-success" | "air-primary-alert" | "air-primary-copilot" | "air-secondary" | "air-secondary-accent" | "air-secondary-accent-1" | "air-tertiary" | "danger" | "success" | "warning" | "primary" | "secondary" | "collab" | "ai" | "air-secondary-alert" | "air-secondary-accent-2" | "air-secondary-no-accent" | "air-tertiary-accent" | "air-tertiary-no-accent" | "air-selection" | "air-boost";
10
- }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
11
- fallback(props?: {}): any;
12
- }>;
10
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
11
  declare const _default: typeof __VLS_export;
14
12
  export default _default;
15
- type __VLS_WithSlots<T, S> = T & {
16
- new (): {
17
- $slots: S;
18
- };
19
- };
@@ -57,7 +57,6 @@ const props = defineProps({
57
57
  viewTransition: { type: Boolean, required: false },
58
58
  replace: { type: Boolean, required: false }
59
59
  });
60
- defineSlots();
61
60
  const { t } = useLocale();
62
61
  const colorMode = useColorMode();
63
62
  const buttonProps = useForwardProps(reactiveOmit(props, "icon"));
@@ -72,21 +71,17 @@ const isDark = computed({
72
71
  </script>
73
72
 
74
73
  <template>
75
- <ClientOnly v-if="!colorMode?.forced">
76
- <B24Button
77
- v-bind="{
74
+ <B24Button
75
+ v-bind="{
78
76
  ...buttonProps,
79
- 'icon': props.icon || (isDark ? icons.dark : icons.light),
80
77
  'aria-label': isDark ? t('colorMode.switchToLight') : t('colorMode.switchToDark'),
81
78
  ...$attrs
82
79
  }"
83
- @click="isDark = !isDark"
84
- />
85
-
86
- <template #fallback>
87
- <slot name="fallback">
88
- <div class="w-[32px] h-[34px]" />
89
- </slot>
80
+ @click="isDark = !isDark"
81
+ >
82
+ <template #leading="{ b24ui }">
83
+ <Component :is="icons.dark" data-slot="leadingIcon" :class="b24ui.leadingIcon({ class: props.b24ui?.leadingIcon })" class="hidden dark:inline" />
84
+ <Component :is="icons.light" data-slot="leadingIcon" :class="b24ui.leadingIcon({ class: props.b24ui?.leadingIcon })" class="inline dark:hidden" />
90
85
  </template>
91
- </ClientOnly>
86
+ </B24Button>
92
87
  </template>
@@ -5,15 +5,8 @@ export interface ColorModeButtonProps extends Omit<ButtonProps, 'color'> {
5
5
  */
6
6
  color?: ButtonProps['color'];
7
7
  }
8
- declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<ColorModeButtonProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<ColorModeButtonProps> & Readonly<{}>, {
8
+ declare const __VLS_export: import("vue").DefineComponent<ColorModeButtonProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<ColorModeButtonProps> & Readonly<{}>, {
9
9
  color: "default" | "link" | "air-primary" | "air-primary-success" | "air-primary-alert" | "air-primary-copilot" | "air-secondary" | "air-secondary-accent" | "air-secondary-accent-1" | "air-tertiary" | "danger" | "success" | "warning" | "primary" | "secondary" | "collab" | "ai" | "air-secondary-alert" | "air-secondary-accent-2" | "air-secondary-no-accent" | "air-tertiary-accent" | "air-tertiary-no-accent" | "air-selection" | "air-boost";
10
- }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
11
- fallback(props?: {}): any;
12
- }>;
10
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
13
11
  declare const _default: typeof __VLS_export;
14
12
  export default _default;
15
- type __VLS_WithSlots<T, S> = T & {
16
- new (): {
17
- $slots: S;
18
- };
19
- };
@@ -74,7 +74,9 @@ const tooltipProps = toRef(() => defu(typeof props.tooltip === "boolean" ? {} :
74
74
  const { t } = useLocale();
75
75
  const { open } = useContentSearch();
76
76
  const appConfig = useAppConfig();
77
- const b24ui = computed(() => tv({ extend: tv(theme), ...appConfig.b24ui?.contentSearchButton || {} })());
77
+ const b24ui = computed(() => tv({ extend: tv(theme), ...appConfig.b24ui?.contentSearchButton || {} })({
78
+ collapsed: props.collapsed
79
+ }));
78
80
  </script>
79
81
 
80
82
  <template>
@@ -85,7 +87,6 @@ const b24ui = computed(() => tv({ extend: tv(theme), ...appConfig.b24ui?.content
85
87
  v-bind="{
86
88
  ...buttonProps,
87
89
  ...collapsed ? {
88
- 'label': void 0,
89
90
  'aria-label': label || t('contentSearchButton.label')
90
91
  } : {
91
92
  color: 'air-secondary-no-accent'
@@ -101,7 +102,7 @@ const b24ui = computed(() => tv({ extend: tv(theme), ...appConfig.b24ui?.content
101
102
  <slot :name="name" v-bind="slotData" />
102
103
  </template>
103
104
 
104
- <template v-if="!collapsed" #trailing="{ b24ui: b24uiProxy }">
105
+ <template #trailing="{ b24ui: b24uiProxy }">
105
106
  <div data-slot="trailing" :class="b24ui.trailing({ class: props.b24ui?.trailing })">
106
107
  <slot name="trailing" :b24ui="b24uiProxy">
107
108
  <template v-if="kbds?.length">
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Get estimate size for virtualizers that checks each item individually
3
+ * NOTE: This requires Reka UI to support functions for estimateSize (https://github.com/unovue/reka-ui/pull/2288)
4
+ * Until then, we check if ANY item has a description and use that for all items
5
+ */
6
+ export declare function getEstimateSize(items: any[], size: 'xss' | 'xs' | 'sm' | 'md' | 'lg' | 'xl', descriptionKey?: string): number;