@bitrix24/b24ui-nuxt 2.1.11 → 2.1.15

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 (78) hide show
  1. package/.nuxt/b24ui.css +2 -0
  2. package/README-AI.md +1 -1
  3. package/dist/meta.d.mts +98705 -785
  4. package/dist/meta.mjs +98705 -785
  5. package/dist/module.json +1 -1
  6. package/dist/module.mjs +1 -1
  7. package/dist/runtime/air-design-tokens/003_b24_context_light.css +1 -1
  8. package/dist/runtime/air-design-tokens/004_b24_context_dark.css +1 -1
  9. package/dist/runtime/air-design-tokens/components/popup.css +1 -1
  10. package/dist/runtime/air-design-tokens/tw-style/font-size.css +1 -1
  11. package/dist/runtime/components/ContextMenu.d.vue.ts +6 -2
  12. package/dist/runtime/components/ContextMenu.vue.d.ts +6 -2
  13. package/dist/runtime/components/ContextMenuContent.vue +3 -2
  14. package/dist/runtime/components/DropdownMenu.d.vue.ts +6 -2
  15. package/dist/runtime/components/DropdownMenu.vue +2 -2
  16. package/dist/runtime/components/DropdownMenu.vue.d.ts +6 -2
  17. package/dist/runtime/components/DropdownMenuContent.vue +5 -4
  18. package/dist/runtime/components/Editor.d.vue.ts +87 -0
  19. package/dist/runtime/components/Editor.vue +185 -0
  20. package/dist/runtime/components/Editor.vue.d.ts +87 -0
  21. package/dist/runtime/components/EditorDragHandle.d.vue.ts +60 -0
  22. package/dist/runtime/components/EditorDragHandle.vue +128 -0
  23. package/dist/runtime/components/EditorDragHandle.vue.d.ts +60 -0
  24. package/dist/runtime/components/EditorEmojiMenu.d.vue.ts +35 -0
  25. package/dist/runtime/components/EditorEmojiMenu.vue +70 -0
  26. package/dist/runtime/components/EditorEmojiMenu.vue.d.ts +35 -0
  27. package/dist/runtime/components/EditorMentionMenu.d.vue.ts +39 -0
  28. package/dist/runtime/components/EditorMentionMenu.vue +74 -0
  29. package/dist/runtime/components/EditorMentionMenu.vue.d.ts +39 -0
  30. package/dist/runtime/components/EditorSuggestionMenu.d.vue.ts +52 -0
  31. package/dist/runtime/components/EditorSuggestionMenu.vue +79 -0
  32. package/dist/runtime/components/EditorSuggestionMenu.vue.d.ts +52 -0
  33. package/dist/runtime/components/EditorToolbar.d.vue.ts +80 -0
  34. package/dist/runtime/components/EditorToolbar.vue +241 -0
  35. package/dist/runtime/components/EditorToolbar.vue.d.ts +80 -0
  36. package/dist/runtime/components/FormField.vue +2 -2
  37. package/dist/runtime/components/InputMenu.d.vue.ts +2 -0
  38. package/dist/runtime/components/InputMenu.vue +14 -1
  39. package/dist/runtime/components/InputMenu.vue.d.ts +2 -0
  40. package/dist/runtime/components/PageCard.vue +2 -1
  41. package/dist/runtime/components/Pagination.d.vue.ts +0 -1
  42. package/dist/runtime/components/Pagination.vue.d.ts +0 -1
  43. package/dist/runtime/components/Select.d.vue.ts +2 -0
  44. package/dist/runtime/components/Select.vue +14 -1
  45. package/dist/runtime/components/Select.vue.d.ts +2 -0
  46. package/dist/runtime/components/SelectMenu.d.vue.ts +2 -0
  47. package/dist/runtime/components/SelectMenu.vue +14 -1
  48. package/dist/runtime/components/SelectMenu.vue.d.ts +2 -0
  49. package/dist/runtime/components/User.vue +0 -1
  50. package/dist/runtime/components/color-mode/ColorModeSelect.vue +1 -0
  51. package/dist/runtime/components/locale/LocaleSelect.vue +6 -3
  52. package/dist/runtime/components/prose/Accordion.vue +1 -1
  53. package/dist/runtime/components/prose/Callout.vue +0 -1
  54. package/dist/runtime/components/prose/Card.vue +0 -1
  55. package/dist/runtime/composables/defineShortcuts.d.ts +2 -1
  56. package/dist/runtime/composables/defineShortcuts.js +62 -15
  57. package/dist/runtime/composables/useEditorMenu.d.ts +64 -0
  58. package/dist/runtime/composables/useEditorMenu.js +448 -0
  59. package/dist/runtime/dictionary/icons.d.ts +1 -0
  60. package/dist/runtime/dictionary/icons.js +5 -3
  61. package/dist/runtime/locale/in.d.ts +3 -0
  62. package/dist/runtime/locale/in.js +136 -0
  63. package/dist/runtime/locale/index.d.ts +25 -23
  64. package/dist/runtime/locale/index.js +11 -10
  65. package/dist/runtime/types/editor.d.ts +69 -0
  66. package/dist/runtime/types/editor.js +0 -0
  67. package/dist/runtime/types/index.d.ts +7 -0
  68. package/dist/runtime/types/index.js +7 -0
  69. package/dist/runtime/types/prose.d.ts +2 -0
  70. package/dist/runtime/types/prose.js +2 -0
  71. package/dist/runtime/utils/editor.d.ts +69 -0
  72. package/dist/runtime/utils/editor.js +364 -0
  73. package/dist/runtime/utils/tv.js +2 -1
  74. package/dist/runtime/vue/components/color-mode/ColorModeSelect.vue +1 -0
  75. package/dist/shared/{b24ui-nuxt.Dh5A-7HA.mjs → b24ui-nuxt.BYDi-4ID.mjs} +323 -132
  76. package/dist/unplugin.mjs +1 -1
  77. package/dist/vite.mjs +1 -1
  78. package/package.json +22 -8
@@ -0,0 +1,80 @@
1
+ import type { AppConfig } from '@nuxt/schema';
2
+ import type { Editor } from '@tiptap/vue-3';
3
+ import type { BubbleMenuPluginProps } from '@tiptap/extension-bubble-menu';
4
+ import type { FloatingMenuPluginProps } from '@tiptap/extension-floating-menu';
5
+ import theme from '#build/b24ui/editor-toolbar';
6
+ import type { ButtonProps, DropdownMenuProps, DropdownMenuItem, TooltipProps, LinkPropsKeys } from '../types';
7
+ import type { EditorItem, EditorCustomHandlers } from '../types/editor';
8
+ import type { ArrayOrNested, DynamicSlots, MergeTypes, NestedItem } from '../types/utils';
9
+ import type { ComponentConfig } from '../types/tv';
10
+ type EditorToolbar = ComponentConfig<typeof theme, AppConfig, 'editorToolbar'>;
11
+ type ButtonItem = Omit<ButtonProps, 'type'> & {
12
+ 'slot'?: string;
13
+ 'tooltip'?: TooltipProps;
14
+ 'aria-label'?: string;
15
+ };
16
+ type EditorToolbarButtonItem<H extends EditorCustomHandlers = EditorCustomHandlers> = Omit<ButtonItem, LinkPropsKeys> & EditorItem<H>;
17
+ type EditorToolbarDropdownChildItem<H extends EditorCustomHandlers = EditorCustomHandlers> = DropdownMenuItem | (Omit<DropdownMenuItem, 'type'> & EditorItem<H>);
18
+ type EditorToolbarDropdownItem<H extends EditorCustomHandlers = EditorCustomHandlers> = ButtonItem & DropdownMenuProps<ArrayOrNested<EditorToolbarDropdownChildItem<H>>>;
19
+ export type EditorToolbarItem<H extends EditorCustomHandlers = EditorCustomHandlers> = ButtonItem | EditorToolbarButtonItem<H> | EditorToolbarDropdownItem<H>;
20
+ type EditorToolbarBaseProps<T extends ArrayOrNested<EditorToolbarItem> = ArrayOrNested<EditorToolbarItem>> = {
21
+ /**
22
+ * The element or component this component should render as.
23
+ * @defaultValue 'div'
24
+ */
25
+ as?: any;
26
+ /**
27
+ * The color of the toolbar controls.
28
+ * @defaultValue 'air-tertiary-no-accent'
29
+ */
30
+ color?: ButtonProps['color'];
31
+ /**
32
+ * The color of the active toolbar control.
33
+ * @defaultValue 'air-tertiary-accent'
34
+ */
35
+ activeColor?: ButtonProps['color'];
36
+ /**
37
+ * The size of the toolbar controls.
38
+ * @defaultValue 'sm'
39
+ */
40
+ size?: ButtonProps['size'];
41
+ items?: T;
42
+ editor: Editor;
43
+ class?: any;
44
+ b24ui?: EditorToolbar['slots'];
45
+ };
46
+ export type EditorToolbarProps<T extends ArrayOrNested<EditorToolbarItem> = ArrayOrNested<EditorToolbarItem>> = (EditorToolbarBaseProps<T> & {
47
+ layout?: 'fixed';
48
+ }) | (EditorToolbarBaseProps<T> & Partial<Omit<BubbleMenuPluginProps, 'editor' | 'element'>> & {
49
+ layout?: 'bubble';
50
+ }) | (EditorToolbarBaseProps<T> & Partial<Omit<FloatingMenuPluginProps, 'editor' | 'element'>> & {
51
+ layout?: 'floating';
52
+ });
53
+ type SlotPropsProps = {
54
+ index: number;
55
+ isActive: (item: EditorToolbarItem) => boolean;
56
+ isDisabled: (item: EditorToolbarItem) => boolean;
57
+ onClick: (e: MouseEvent, item: EditorToolbarItem) => void;
58
+ };
59
+ type SlotProps<T extends EditorToolbarItem> = (props: {
60
+ item: T;
61
+ } & SlotPropsProps) => any;
62
+ export type EditorToolbarSlots<A extends ArrayOrNested<EditorToolbarItem> = ArrayOrNested<EditorToolbarItem>, T extends NestedItem<A> = NestedItem<A>> = {
63
+ item: SlotProps<T>;
64
+ } & DynamicSlots<MergeTypes<T>, undefined, SlotPropsProps>;
65
+ declare const __VLS_export: <T extends ArrayOrNested<EditorToolbarItem>>(__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<{
66
+ props: __VLS_PrettifyLocal<EditorToolbarProps<T>> & import("vue").PublicProps & (typeof globalThis extends {
67
+ __VLS_PROPS_FALLBACK: infer P;
68
+ } ? P : {});
69
+ expose: (exposed: {}) => void;
70
+ attrs: any;
71
+ slots: EditorToolbarSlots<T, NestedItem<T>>;
72
+ emit: {};
73
+ }>) => import("vue").VNode & {
74
+ __ctx?: Awaited<typeof __VLS_setup>;
75
+ };
76
+ declare const _default: typeof __VLS_export;
77
+ export default _default;
78
+ type __VLS_PrettifyLocal<T> = {
79
+ [K in keyof T as K]: T[K];
80
+ } & {};
@@ -16,7 +16,7 @@ const props = defineProps({
16
16
  label: { type: String, required: false },
17
17
  description: { type: String, required: false },
18
18
  help: { type: String, required: false },
19
- error: { type: [Boolean, String], required: false },
19
+ error: { type: [Boolean, String], required: false, default: void 0 },
20
20
  hint: { type: String, required: false },
21
21
  size: { type: null, required: false },
22
22
  required: { type: Boolean, required: false },
@@ -83,7 +83,7 @@ provide(formFieldInjectionKey, computed(() => ({
83
83
  <div data-slot="container" :class="[(label || !!slots.label || description || !!slots.description) && b24ui.container({ class: props.b24ui?.container })]">
84
84
  <slot :error="error" />
85
85
 
86
- <div v-if="typeof error === 'string' && error || !!slots.error" :id="`${ariaId}-error`" data-slot="error" :class="b24ui.error({ class: props.b24ui?.error })">
86
+ <div v-if="props.error !== false && (typeof error === 'string' && error || !!slots.error)" :id="`${ariaId}-error`" data-slot="error" :class="b24ui.error({ class: props.b24ui?.error })">
87
87
  <slot name="error" :error="error">
88
88
  <div data-slot="errorWrapper" :class="b24ui.errorWrapper({ class: props.b24ui?.errorWrapper })">
89
89
  <WarningIcon data-slot="errorIcon" :class="b24ui.errorIcon()" />
@@ -3,6 +3,7 @@ import type { AppConfig } from '@nuxt/schema';
3
3
  import theme from '#build/b24ui/input-menu';
4
4
  import type { UseComponentIconsProps } from '../composables/useComponentIcons';
5
5
  import type { AvatarProps, ChipProps, BadgeProps, IconComponent } from '../types';
6
+ import type { ModelModifiers } from '../types/input';
6
7
  import type { InputHTMLAttributes } from '../types/html';
7
8
  import type { AcceptableValue, ArrayOrNested, GetItemKeys, GetModelValue, GetModelValueEmits, NestedItem, EmitsToProps } from '../types/utils';
8
9
  import type { ComponentConfig } from '../types/tv';
@@ -136,6 +137,7 @@ export interface InputMenuProps<T extends ArrayOrNested<InputMenuItem> = ArrayOr
136
137
  defaultValue?: GetModelValue<T, VK, M>;
137
138
  /** The controlled value of the InputMenu. Can be binded-with with `v-model`. */
138
139
  modelValue?: GetModelValue<T, VK, M>;
140
+ modelModifiers?: Omit<ModelModifiers<GetModelValue<T, VK, M>>, 'lazy'>;
139
141
  /** Whether multiple options can be selected or not. */
140
142
  multiple?: M & boolean;
141
143
  tag?: string;
@@ -14,7 +14,7 @@ import { useComponentIcons } from "../composables/useComponentIcons";
14
14
  import { useFormField } from "../composables/useFormField";
15
15
  import { useLocale } from "../composables/useLocale";
16
16
  import { usePortal } from "../composables/usePortal";
17
- import { compare, get, getDisplayValue, isArrayOfArray } from "../utils";
17
+ import { compare, get, getDisplayValue, isArrayOfArray, looseToNumber } from "../utils";
18
18
  import { getEstimateSize } from "../utils/virtualizer";
19
19
  import { tv } from "../utils/tv";
20
20
  import icons from "../dictionary/icons";
@@ -48,6 +48,7 @@ const props = defineProps({
48
48
  items: { type: null, required: false },
49
49
  defaultValue: { type: null, required: false },
50
50
  modelValue: { type: null, required: false },
51
+ modelModifiers: { type: Object, required: false },
51
52
  multiple: { type: Boolean, required: false },
52
53
  tag: { type: String, required: false },
53
54
  tagColor: { type: null, required: false },
@@ -184,6 +185,18 @@ function onUpdate(value) {
184
185
  if (toRaw(props.modelValue) === value) {
185
186
  return;
186
187
  }
188
+ if (props.modelModifiers?.trim) {
189
+ value = value?.trim() ?? null;
190
+ }
191
+ if (props.modelModifiers?.number) {
192
+ value = looseToNumber(value);
193
+ }
194
+ if (props.modelModifiers?.nullable) {
195
+ value ??= null;
196
+ }
197
+ if (props.modelModifiers?.optional) {
198
+ value ??= void 0;
199
+ }
187
200
  const event = new Event("change", { target: { value } });
188
201
  emits("change", event);
189
202
  emitFormChange();
@@ -3,6 +3,7 @@ import type { AppConfig } from '@nuxt/schema';
3
3
  import theme from '#build/b24ui/input-menu';
4
4
  import type { UseComponentIconsProps } from '../composables/useComponentIcons';
5
5
  import type { AvatarProps, ChipProps, BadgeProps, IconComponent } from '../types';
6
+ import type { ModelModifiers } from '../types/input';
6
7
  import type { InputHTMLAttributes } from '../types/html';
7
8
  import type { AcceptableValue, ArrayOrNested, GetItemKeys, GetModelValue, GetModelValueEmits, NestedItem, EmitsToProps } from '../types/utils';
8
9
  import type { ComponentConfig } from '../types/tv';
@@ -136,6 +137,7 @@ export interface InputMenuProps<T extends ArrayOrNested<InputMenuItem> = ArrayOr
136
137
  defaultValue?: GetModelValue<T, VK, M>;
137
138
  /** The controlled value of the InputMenu. Can be binded-with with `v-model`. */
138
139
  modelValue?: GetModelValue<T, VK, M>;
140
+ modelModifiers?: Omit<ModelModifiers<GetModelValue<T, VK, M>>, 'lazy'>;
139
141
  /** Whether multiple options can be selected or not. */
140
142
  multiple?: M & boolean;
141
143
  tag?: string;
@@ -51,6 +51,7 @@ const ariaLabel = computed(() => {
51
51
  :data-orientation="orientation"
52
52
  data-slot="root"
53
53
  :class="b24ui.root({ class: [props.b24ui?.root, props.class] })"
54
+ v-bind="$attrs"
54
55
  @click="onClick"
55
56
  >
56
57
  <div data-slot="container" :class="b24ui.container({ class: props.b24ui?.container })">
@@ -92,7 +93,7 @@ const ariaLabel = computed(() => {
92
93
  <B24Link
93
94
  v-if="to"
94
95
  :aria-label="ariaLabel"
95
- v-bind="{ to, target, ...$attrs }"
96
+ v-bind="{ to, target }"
96
97
  class="focus:outline-none peer"
97
98
  raw
98
99
  >
@@ -43,7 +43,6 @@ export interface PaginationProps extends Partial<Pick<PaginationRootProps, 'defa
43
43
  /**
44
44
  * The color of the pagination controls.
45
45
  * @defaultValue 'air-secondary-no-accent'
46
- * @IconComponent
47
46
  */
48
47
  color?: ButtonProps['color'];
49
48
  /**
@@ -43,7 +43,6 @@ export interface PaginationProps extends Partial<Pick<PaginationRootProps, 'defa
43
43
  /**
44
44
  * The color of the pagination controls.
45
45
  * @defaultValue 'air-secondary-no-accent'
46
- * @IconComponent
47
46
  */
48
47
  color?: ButtonProps['color'];
49
48
  /**
@@ -3,6 +3,7 @@ import type { AppConfig } from '@nuxt/schema';
3
3
  import theme from '#build/b24ui/select';
4
4
  import type { UseComponentIconsProps } from '../composables/useComponentIcons';
5
5
  import type { AvatarProps, ChipProps, BadgeProps, IconComponent } from '../types';
6
+ import type { ModelModifiers } from '../types/input';
6
7
  import type { ButtonHTMLAttributes } from '../types/html';
7
8
  import type { AcceptableValue, ArrayOrNested, GetItemKeys, GetModelValue, GetModelValueEmits, NestedItem, EmitsToProps } from '../types/utils';
8
9
  import type { ComponentConfig } from '../types/tv';
@@ -115,6 +116,7 @@ export interface SelectProps<T extends ArrayOrNested<SelectItem> = ArrayOrNested
115
116
  defaultValue?: GetModelValue<T, VK, M>;
116
117
  /** The controlled value of the Select. Can be bind as `v-model`. */
117
118
  modelValue?: GetModelValue<T, VK, M>;
119
+ modelModifiers?: Omit<ModelModifiers<GetModelValue<T, VK, M>>, 'lazy'>;
118
120
  /** Whether multiple options can be selected or not. */
119
121
  multiple?: M & boolean;
120
122
  /** Highlight the ring color like a focus state. */
@@ -12,7 +12,7 @@ import { useFieldGroup } from "../composables/useFieldGroup";
12
12
  import { useComponentIcons } from "../composables/useComponentIcons";
13
13
  import { useFormField } from "../composables/useFormField";
14
14
  import { usePortal } from "../composables/usePortal";
15
- import { get, getDisplayValue, isArrayOfArray } from "../utils";
15
+ import { get, getDisplayValue, isArrayOfArray, looseToNumber } from "../utils";
16
16
  import { tv } from "../utils/tv";
17
17
  import icons from "../dictionary/icons";
18
18
  import B24Badge from "./Badge.vue";
@@ -41,6 +41,7 @@ const props = defineProps({
41
41
  items: { type: null, required: false },
42
42
  defaultValue: { type: null, required: false },
43
43
  modelValue: { type: null, required: false },
44
+ modelModifiers: { type: Object, required: false },
44
45
  multiple: { type: Boolean, required: false },
45
46
  highlight: { type: Boolean, required: false },
46
47
  autofocus: { type: Boolean, required: false },
@@ -119,6 +120,18 @@ onMounted(() => {
119
120
  }, props.autofocusDelay);
120
121
  });
121
122
  function onUpdate(value) {
123
+ if (props.modelModifiers?.trim) {
124
+ value = value?.trim() ?? null;
125
+ }
126
+ if (props.modelModifiers?.number) {
127
+ value = looseToNumber(value);
128
+ }
129
+ if (props.modelModifiers?.nullable) {
130
+ value ??= null;
131
+ }
132
+ if (props.modelModifiers?.optional) {
133
+ value ??= void 0;
134
+ }
122
135
  const event = new Event("change", { target: { value } });
123
136
  emits("change", event);
124
137
  emitFormChange();
@@ -3,6 +3,7 @@ import type { AppConfig } from '@nuxt/schema';
3
3
  import theme from '#build/b24ui/select';
4
4
  import type { UseComponentIconsProps } from '../composables/useComponentIcons';
5
5
  import type { AvatarProps, ChipProps, BadgeProps, IconComponent } from '../types';
6
+ import type { ModelModifiers } from '../types/input';
6
7
  import type { ButtonHTMLAttributes } from '../types/html';
7
8
  import type { AcceptableValue, ArrayOrNested, GetItemKeys, GetModelValue, GetModelValueEmits, NestedItem, EmitsToProps } from '../types/utils';
8
9
  import type { ComponentConfig } from '../types/tv';
@@ -115,6 +116,7 @@ export interface SelectProps<T extends ArrayOrNested<SelectItem> = ArrayOrNested
115
116
  defaultValue?: GetModelValue<T, VK, M>;
116
117
  /** The controlled value of the Select. Can be bind as `v-model`. */
117
118
  modelValue?: GetModelValue<T, VK, M>;
119
+ modelModifiers?: Omit<ModelModifiers<GetModelValue<T, VK, M>>, 'lazy'>;
118
120
  /** Whether multiple options can be selected or not. */
119
121
  multiple?: M & boolean;
120
122
  /** Highlight the ring color like a focus state. */
@@ -3,6 +3,7 @@ import type { AppConfig } from '@nuxt/schema';
3
3
  import theme from '#build/b24ui/select-menu';
4
4
  import type { UseComponentIconsProps } from '../composables/useComponentIcons';
5
5
  import type { AvatarProps, ChipProps, InputProps, BadgeProps, IconComponent } from '../types';
6
+ import type { ModelModifiers } from '../types/input';
6
7
  import type { ButtonHTMLAttributes } from '../types/html';
7
8
  import type { AcceptableValue, ArrayOrNested, GetItemKeys, GetModelValue, GetModelValueEmits, NestedItem, EmitsToProps } from '../types/utils';
8
9
  import type { ComponentConfig } from '../types/tv';
@@ -142,6 +143,7 @@ export interface SelectMenuProps<T extends ArrayOrNested<SelectMenuItem> = Array
142
143
  defaultValue?: GetModelValue<T, VK, M>;
143
144
  /** The controlled value of the SelectMenu. Can be binded-with with `v-model`. */
144
145
  modelValue?: GetModelValue<T, VK, M>;
146
+ modelModifiers?: Omit<ModelModifiers<GetModelValue<T, VK, M>>, 'lazy'>;
145
147
  /** Whether multiple options can be selected or not. */
146
148
  multiple?: M & boolean;
147
149
  /** Highlight the ring color like a focus state. */
@@ -13,7 +13,7 @@ import { useComponentIcons } from "../composables/useComponentIcons";
13
13
  import { useFormField } from "../composables/useFormField";
14
14
  import { useLocale } from "../composables/useLocale";
15
15
  import { usePortal } from "../composables/usePortal";
16
- import { compare, get, getDisplayValue, isArrayOfArray } from "../utils";
16
+ import { compare, get, getDisplayValue, isArrayOfArray, looseToNumber } from "../utils";
17
17
  import { getEstimateSize } from "../utils/virtualizer";
18
18
  import { tv } from "../utils/tv";
19
19
  import icons from "../dictionary/icons";
@@ -47,6 +47,7 @@ const props = defineProps({
47
47
  items: { type: null, required: false },
48
48
  defaultValue: { type: null, required: false },
49
49
  modelValue: { type: null, required: false },
50
+ modelModifiers: { type: Object, required: false },
50
51
  multiple: { type: Boolean, required: false },
51
52
  highlight: { type: Boolean, required: false },
52
53
  createItem: { type: [Boolean, String, Object], required: false },
@@ -188,6 +189,18 @@ function onUpdate(value) {
188
189
  if (toRaw(props.modelValue) === value) {
189
190
  return;
190
191
  }
192
+ if (props.modelModifiers?.trim) {
193
+ value = value?.trim() ?? null;
194
+ }
195
+ if (props.modelModifiers?.number) {
196
+ value = looseToNumber(value);
197
+ }
198
+ if (props.modelModifiers?.nullable) {
199
+ value ??= null;
200
+ }
201
+ if (props.modelModifiers?.optional) {
202
+ value ??= void 0;
203
+ }
191
204
  const event = new Event("change", { target: { value } });
192
205
  emits("change", event);
193
206
  emitFormChange();
@@ -3,6 +3,7 @@ import type { AppConfig } from '@nuxt/schema';
3
3
  import theme from '#build/b24ui/select-menu';
4
4
  import type { UseComponentIconsProps } from '../composables/useComponentIcons';
5
5
  import type { AvatarProps, ChipProps, InputProps, BadgeProps, IconComponent } from '../types';
6
+ import type { ModelModifiers } from '../types/input';
6
7
  import type { ButtonHTMLAttributes } from '../types/html';
7
8
  import type { AcceptableValue, ArrayOrNested, GetItemKeys, GetModelValue, GetModelValueEmits, NestedItem, EmitsToProps } from '../types/utils';
8
9
  import type { ComponentConfig } from '../types/tv';
@@ -142,6 +143,7 @@ export interface SelectMenuProps<T extends ArrayOrNested<SelectMenuItem> = Array
142
143
  defaultValue?: GetModelValue<T, VK, M>;
143
144
  /** The controlled value of the SelectMenu. Can be binded-with with `v-model`. */
144
145
  modelValue?: GetModelValue<T, VK, M>;
146
+ modelModifiers?: Omit<ModelModifiers<GetModelValue<T, VK, M>>, 'lazy'>;
145
147
  /** Whether multiple options can be selected or not. */
146
148
  multiple?: M & boolean;
147
149
  /** Highlight the ring color like a focus state. */
@@ -77,7 +77,6 @@ const chipSize = computed(() => {
77
77
  :aria-label="name"
78
78
  v-bind="{ to, target, ...$attrs }"
79
79
  class="focus:outline-none peer"
80
- tabindex="-1"
81
80
  raw
82
81
  >
83
82
  <span class="absolute inset-0" aria-hidden="true" />
@@ -33,6 +33,7 @@ const props = defineProps({
33
33
  labelKey: { type: null, required: false },
34
34
  descriptionKey: { type: null, required: false },
35
35
  defaultValue: { type: null, required: false },
36
+ modelModifiers: { type: Object, required: false },
36
37
  multiple: { type: Boolean, required: false },
37
38
  highlight: { type: Boolean, required: false },
38
39
  createItem: { type: [Boolean, String, Object], required: false },
@@ -31,6 +31,7 @@ const props = defineProps({
31
31
  labelKey: { type: null, required: false, default: "name" },
32
32
  descriptionKey: { type: null, required: false },
33
33
  defaultValue: { type: null, required: false },
34
+ modelModifiers: { type: Object, required: false },
34
35
  multiple: { type: Boolean, required: false },
35
36
  highlight: { type: Boolean, required: false },
36
37
  createItem: { type: [Boolean, String, Object], required: false },
@@ -60,7 +61,7 @@ function getEmojiFlag(locale) {
60
61
  // English -> USA
61
62
  de: "de",
62
63
  // Deutsch
63
- la: "es",
64
+ es: "es",
64
65
  // Catalan -> Spain
65
66
  br: "br",
66
67
  // Português
@@ -92,8 +93,10 @@ function getEmojiFlag(locale) {
92
93
  // ภาษาไทย
93
94
  ar: "sa",
94
95
  // Arabic -> Saudi Arabia
95
- kz: "kz"
96
+ kk: "kz",
96
97
  // Kazakh -> Kazakhstan
98
+ hi: "in"
99
+ // Indian (हिन्दी)
97
100
  };
98
101
  const baseLanguage = locale.split("-")[0]?.toLowerCase() || locale;
99
102
  const countryCode = languageToCountry[baseLanguage] || locale.replace(/^.*-/, "").slice(0, 2);
@@ -115,7 +118,7 @@ function getEmojiFlag(locale) {
115
118
 
116
119
  <template #item-leading="{ item }">
117
120
  <span class="size-5 text-center">
118
- {{ getEmojiFlag(item.code) }}
121
+ {{ getEmojiFlag(item.locale) }}
119
122
  </span>
120
123
  </template>
121
124
  </B24SelectMenu>
@@ -37,7 +37,7 @@ onBeforeUpdate(() => rerenderCount.value++);
37
37
  </script>
38
38
 
39
39
  <template>
40
- <B24Accordion :type="type" :items="items" :unmount-on-hide="false" :class="props.class" :ui="transformUI(b24ui(), props.b24ui)">
40
+ <B24Accordion :type="type" :items="items" :unmount-on-hide="false" :class="props.class" :b24ui="transformUI(b24ui(), props.b24ui)">
41
41
  <template #content="{ item }">
42
42
  <component :is="item.component" />
43
43
  </template>
@@ -50,7 +50,6 @@ const iconFromIconName = computed(() => {
50
50
  v-if="to"
51
51
  v-bind="{ to, target, ...$attrs }"
52
52
  class="focus:outline-none"
53
- tabindex="-1"
54
53
  raw
55
54
  >
56
55
  <span class="absolute inset-0" aria-hidden="true" />
@@ -37,7 +37,6 @@ const ariaLabel = computed(() => (props.title || "Card link").trim());
37
37
  :aria-label="ariaLabel"
38
38
  v-bind="{ to, target, ...$attrs }"
39
39
  class="focus:outline-none"
40
- tabindex="-1"
41
40
  raw
42
41
  >
43
42
  <span class="absolute inset-0" aria-hidden="true" />
@@ -9,7 +9,8 @@ export interface ShortcutsConfig {
9
9
  }
10
10
  export interface ShortcutsOptions {
11
11
  chainDelay?: number;
12
+ layoutIndependent?: boolean;
12
13
  }
13
- export declare function extractShortcuts(items: any[] | any[][]): Record<string, Handler>;
14
+ export declare function extractShortcuts(items: any[] | any[][], separator?: '_' | '-'): Record<string, Handler>;
14
15
  export declare function defineShortcuts(config: MaybeRef<ShortcutsConfig>, options?: ShortcutsOptions): import("@vueuse/shared").Fn;
15
16
  export {};
@@ -4,12 +4,36 @@ import { useKbd } from "./useKbd.js";
4
4
  const chainedShortcutRegex = /^[^-]+.*-.*[^-]+$/;
5
5
  const combinedShortcutRegex = /^[^_]+.*_.*[^_]+$/;
6
6
  const shiftableKeys = ["arrowleft", "arrowright", "arrowup", "arrowright", "tab", "escape", "enter", "backspace"];
7
- export function extractShortcuts(items) {
7
+ function convertKeyToCode(key) {
8
+ if (/^[a-z]$/i.test(key)) {
9
+ return `Key${key.toUpperCase()}`;
10
+ }
11
+ if (/^\d$/.test(key)) {
12
+ return `Digit${key}`;
13
+ }
14
+ if (/^f\d+$/i.test(key)) {
15
+ return key.toUpperCase();
16
+ }
17
+ const specialKeys = {
18
+ space: "Space",
19
+ enter: "Enter",
20
+ escape: "Escape",
21
+ tab: "Tab",
22
+ backspace: "Backspace",
23
+ delete: "Delete",
24
+ arrowup: "ArrowUp",
25
+ arrowdown: "ArrowDown",
26
+ arrowleft: "ArrowLeft",
27
+ arrowright: "ArrowRight"
28
+ };
29
+ return specialKeys[key.toLowerCase()] || key;
30
+ }
31
+ export function extractShortcuts(items, separator = "_") {
8
32
  const shortcuts = {};
9
33
  function traverse(items2) {
10
34
  items2.forEach((item) => {
11
35
  if (item.kbds?.length && (item.onSelect || item.onClick)) {
12
- const shortcutKey = item.kbds.join("_");
36
+ const shortcutKey = item.kbds.join(separator);
13
37
  shortcuts[shortcutKey] = item.onSelect || item.onClick;
14
38
  }
15
39
  if (item.children) {
@@ -31,14 +55,16 @@ export function defineShortcuts(config, options = {}) {
31
55
  const debouncedClearChainedInput = useDebounceFn(clearChainedInput, options.chainDelay ?? 800);
32
56
  const { macOS } = useKbd();
33
57
  const activeElement = useActiveElement();
58
+ const layoutIndependent = options.layoutIndependent ?? false;
59
+ const shiftableCodes = shiftableKeys.map((k) => convertKeyToCode(k));
34
60
  const onKeyDown = (e) => {
35
61
  if (!e.key) {
36
62
  return;
37
63
  }
38
- const alphabetKey = /^[a-z]{1}$/i.test(e.key);
39
- const shiftableKey = shiftableKeys.includes(e.key.toLowerCase());
64
+ const alphabetKey = layoutIndependent ? /^Key[A-Z]$/i.test(e.code) : /^[a-z]{1}$/i.test(e.key);
65
+ const shiftableKey = layoutIndependent ? shiftableCodes.includes(e.code) : shiftableKeys.includes(e.key.toLowerCase());
40
66
  let chainedKey;
41
- chainedInputs.value.push(e.key);
67
+ chainedInputs.value.push(layoutIndependent ? e.code : e.key);
42
68
  if (chainedInputs.value.length >= 2) {
43
69
  chainedKey = chainedInputs.value.slice(-2).join("-");
44
70
  for (const shortcut of shortcuts.value.filter((s) => s.chained)) {
@@ -54,8 +80,14 @@ export function defineShortcuts(config, options = {}) {
54
80
  }
55
81
  }
56
82
  for (const shortcut of shortcuts.value.filter((s) => !s.chained)) {
57
- if (e.key.toLowerCase() !== shortcut.key) {
58
- continue;
83
+ if (layoutIndependent) {
84
+ if (e.code !== shortcut.key) {
85
+ continue;
86
+ }
87
+ } else {
88
+ if (e.key.toLowerCase() !== shortcut.key) {
89
+ continue;
90
+ }
59
91
  }
60
92
  if (e.metaKey !== shortcut.metaKey) {
61
93
  continue;
@@ -98,17 +130,32 @@ export function defineShortcuts(config, options = {}) {
98
130
  }
99
131
  const chained = key.includes("-") && key !== "-" && !key.includes("_");
100
132
  if (chained) {
101
- shortcut = {
102
- key: key.toLowerCase(),
103
- metaKey: false,
104
- ctrlKey: false,
105
- shiftKey: false,
106
- altKey: false
107
- };
133
+ if (layoutIndependent) {
134
+ const parts = key.split("-").map((p) => convertKeyToCode(p));
135
+ shortcut = {
136
+ key: parts.join("-"),
137
+ metaKey: false,
138
+ ctrlKey: false,
139
+ shiftKey: false,
140
+ altKey: false
141
+ };
142
+ } else {
143
+ shortcut = {
144
+ key: key.toLowerCase(),
145
+ metaKey: false,
146
+ ctrlKey: false,
147
+ shiftKey: false,
148
+ altKey: false
149
+ };
150
+ }
108
151
  } else {
109
152
  const keySplit = key.toLowerCase().split("_").map((k) => k);
153
+ let baseKey = keySplit.filter((k) => !["meta", "command", "ctrl", "shift", "alt", "option"].includes(k)).join("_");
154
+ if (layoutIndependent) {
155
+ baseKey = convertKeyToCode(baseKey);
156
+ }
110
157
  shortcut = {
111
- key: keySplit.filter((k) => !["meta", "command", "ctrl", "shift", "alt", "option"].includes(k)).join("_"),
158
+ key: baseKey,
112
159
  metaKey: keySplit.includes("meta") || keySplit.includes("command"),
113
160
  ctrlKey: keySplit.includes("ctrl"),
114
161
  shiftKey: keySplit.includes("shift"),
@@ -0,0 +1,64 @@
1
+ import type { Ref, ComputedRef, MaybeRef } from 'vue';
2
+ import type { Editor } from '@tiptap/vue-3';
3
+ import type { FloatingUIOptions } from '../types/editor';
4
+ export interface EditorMenuOptions<T = any> {
5
+ editor: Editor;
6
+ /**
7
+ * The trigger character (e.g., '/', '@', ':')
8
+ */
9
+ char: string;
10
+ /**
11
+ * Plugin key to identify this menu
12
+ */
13
+ pluginKey: string;
14
+ /**
15
+ * The items to display (can be a flat array or grouped)
16
+ */
17
+ items?: MaybeRef<T[] | T[][] | undefined>;
18
+ /**
19
+ * Fields to filter items by.
20
+ * @defaultValue ['label']
21
+ */
22
+ filterFields?: string[];
23
+ /**
24
+ * Function to filter items based on query
25
+ */
26
+ filter?: (items: T[], query: string) => T[];
27
+ /**
28
+ * Maximum number of items to display
29
+ * @defaultValue 42
30
+ */
31
+ limit?: number;
32
+ /**
33
+ * Function to execute when an item is selected
34
+ */
35
+ onSelect: (editor: Editor, range: any, item: T) => void;
36
+ /**
37
+ * Function to render each menu item
38
+ */
39
+ renderItem: (item: T, b24ui: ComputedRef<any>) => any;
40
+ /**
41
+ * The options for positioning the menu. Those are passed to Floating UI and include options for the placement, offset, flip, shift, size, autoPlacement, hide, and inline middleware.
42
+ * @defaultValue { strategy: 'absolute', placement: 'bottom-start', offset: 8, shift: { padding: 8 } }
43
+ * @see https://floating-ui.com/docs/computePosition#options
44
+ */
45
+ options?: FloatingUIOptions;
46
+ /**
47
+ * The DOM element to append the menu to. Default is the editor's parent element.
48
+ *
49
+ * Sometimes the menu needs to be appended to a different DOM context due to accessibility, clipping, or z-index issues.
50
+ *
51
+ * @type {HTMLElement}
52
+ * @default editor.view.dom.parentElement
53
+ */
54
+ appendTo?: HTMLElement | (() => HTMLElement);
55
+ /**
56
+ * UI styles computed ref
57
+ */
58
+ b24ui: ComputedRef<any>;
59
+ }
60
+ export declare function useEditorMenu<T = any>(options: EditorMenuOptions<T>): {
61
+ plugin: import("prosemirror-state").Plugin<any>;
62
+ destroy: () => void;
63
+ filteredItems: Ref<T[], T[]>;
64
+ };