@bitrix24/b24ui-nuxt 0.2.1 → 0.2.3

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 (44) hide show
  1. package/.nuxt/b24ui/button.ts +9 -2
  2. package/.nuxt/b24ui/checkbox.ts +1 -1
  3. package/.nuxt/b24ui/dropdown-menu.ts +235 -0
  4. package/.nuxt/b24ui/index.ts +2 -0
  5. package/.nuxt/b24ui/input-menu.ts +1 -1
  6. package/.nuxt/b24ui/modal.ts +28 -0
  7. package/.nuxt/b24ui/radio-group.ts +1 -1
  8. package/.nuxt/b24ui/range.ts +1 -1
  9. package/.nuxt/b24ui/select-menu.ts +1 -1
  10. package/.nuxt/b24ui/select.ts +1 -1
  11. package/.nuxt/b24ui/switch.ts +1 -1
  12. package/.nuxt/b24ui/tabs.ts +2 -2
  13. package/.nuxt/b24ui/toaster.ts +4 -4
  14. package/.nuxt/b24ui.css +2 -0
  15. package/dist/meta.cjs +9050 -1027
  16. package/dist/meta.d.cts +9050 -1027
  17. package/dist/meta.d.mts +9050 -1027
  18. package/dist/meta.d.ts +9050 -1027
  19. package/dist/meta.mjs +9050 -1027
  20. package/dist/module.cjs +1 -1
  21. package/dist/module.json +1 -1
  22. package/dist/module.mjs +1 -1
  23. package/dist/runtime/components/App.vue +2 -2
  24. package/dist/runtime/components/DropdownMenu.vue +135 -0
  25. package/dist/runtime/components/DropdownMenuContent.vue +182 -0
  26. package/dist/runtime/components/Modal.vue +179 -0
  27. package/dist/runtime/components/ModalDialogClose.vue +17 -0
  28. package/dist/runtime/components/ModalProvider.vue +12 -0
  29. package/dist/runtime/composables/useComponentIcons.d.ts +2 -2
  30. package/dist/runtime/composables/useModal.d.ts +17 -0
  31. package/dist/runtime/composables/useModal.js +46 -0
  32. package/dist/runtime/index.css +1 -1
  33. package/dist/runtime/plugins/modal.d.ts +2 -0
  34. package/dist/runtime/plugins/modal.js +10 -0
  35. package/dist/runtime/types/index.d.ts +3 -0
  36. package/dist/runtime/types/index.js +3 -0
  37. package/dist/runtime/vue/components/Link.vue +1 -0
  38. package/dist/shared/{b24ui-nuxt.D5cXbZSx.cjs → b24ui-nuxt.Ce3hzs_q.cjs} +440 -26
  39. package/dist/shared/{b24ui-nuxt.CrjojW8t.mjs → b24ui-nuxt.DY8ePXC7.mjs} +440 -26
  40. package/dist/unplugin.cjs +1 -1
  41. package/dist/unplugin.mjs +1 -1
  42. package/dist/vite.cjs +1 -1
  43. package/dist/vite.mjs +1 -1
  44. package/package.json +12 -8
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.D5cXbZSx.cjs');
5
+ const templates = require('./shared/b24ui-nuxt.Ce3hzs_q.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.13.1"
6
6
  },
7
7
  "docs": "https://bitrix24.github.io/b24ui/guide/installation-nuxt-app.html",
8
- "version": "0.2.1",
8
+ "version": "0.2.3",
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.CrjojW8t.mjs';
3
+ import { d as defaultOptions, a as getDefaultUiConfig, b as addTemplates } from './shared/b24ui-nuxt.DY8ePXC7.mjs';
4
4
  import 'node:url';
5
5
  import 'scule';
6
6
 
@@ -23,7 +23,7 @@ import { toRef, useId, provide } from 'vue'
23
23
  import { ConfigProvider, TooltipProvider, useForwardProps } from 'reka-ui'
24
24
  import { reactivePick } from '@vueuse/core'
25
25
  import B24Toaster from './Toaster.vue'
26
- // import B24ModalProvider from './ModalProvider.vue'
26
+ import B24ModalProvider from './ModalProvider.vue'
27
27
  // import B24SlideoverProvider from './SlideoverProvider.vue'
28
28
 
29
29
  const props = defineProps<AppProps>()
@@ -44,7 +44,7 @@ provide(localeContextInjectionKey, locale)
44
44
  <slot />
45
45
  </B24Toaster>
46
46
  <slot v-else />
47
- <!-- B24ModalProvider / -->
47
+ <B24ModalProvider />
48
48
  <!-- B24SlideoverProvider / -->
49
49
  </TooltipProvider>
50
50
  </ConfigProvider>
@@ -0,0 +1,135 @@
1
+ <script lang="ts">
2
+ import type { VariantProps } from 'tailwind-variants'
3
+ import type { DropdownMenuRootProps, DropdownMenuRootEmits, DropdownMenuContentProps, DropdownMenuArrowProps } from 'reka-ui'
4
+ import type { AppConfig } from '@nuxt/schema'
5
+ import _appConfig from '#build/app.config'
6
+ import theme from '#build/b24ui/dropdown-menu'
7
+ import { tv } from '../utils/tv'
8
+ import type { AvatarProps, KbdProps, LinkProps, IconComponent } from '../types'
9
+ import type { DynamicSlots, PartialString } from '../types/utils'
10
+
11
+ const appConfigDropdownMenu = _appConfig as AppConfig & { b24ui: { dropdownMenu: Partial<typeof theme> } }
12
+
13
+ const dropdownMenu = tv({ extend: tv(theme), ...(appConfigDropdownMenu.b24ui?.dropdownMenu || {}) })
14
+
15
+ type DropdownMenuVariants = VariantProps<typeof dropdownMenu>
16
+
17
+ export interface DropdownMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'custom'> {
18
+ label?: string
19
+ icon?: IconComponent
20
+ color?: DropdownMenuVariants['color']
21
+ avatar?: AvatarProps
22
+ content?: Omit<DropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'>
23
+ kbds?: KbdProps['value'][] | KbdProps[]
24
+ /**
25
+ * The item type.
26
+ * @defaultValue 'link'
27
+ */
28
+ type?: 'label' | 'separator' | 'link' | 'checkbox'
29
+ slot?: string
30
+ loading?: boolean
31
+ disabled?: boolean
32
+ checked?: boolean
33
+ open?: boolean
34
+ defaultOpen?: boolean
35
+ children?: DropdownMenuItem[] | DropdownMenuItem[][]
36
+ onSelect?(e: Event): void
37
+ onUpdateChecked?(checked: boolean): void
38
+ }
39
+
40
+ export interface DropdownMenuProps<T> extends Omit<DropdownMenuRootProps, 'dir'> {
41
+ size?: DropdownMenuVariants['size']
42
+ items?: T[] | T[][]
43
+ /**
44
+ * The icon displayed when an item is checked.
45
+ * @defaultValue icons.check = `CheckIcon`
46
+ */
47
+ checkedIcon?: IconComponent
48
+ /**
49
+ * The content of the menu.
50
+ * @defaultValue { side: 'bottom', sideOffset: 8, collisionPadding: 8 }
51
+ */
52
+ content?: Omit<DropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'>
53
+ /**
54
+ * Display an arrow alongside the menu.
55
+ * @defaultValue false
56
+ */
57
+ arrow?: boolean | Omit<DropdownMenuArrowProps, 'as' | 'asChild'>
58
+ /**
59
+ * Render the menu in a portal.
60
+ * @defaultValue true
61
+ */
62
+ portal?: boolean
63
+ /**
64
+ * The key used to get the label from the item.
65
+ * @defaultValue 'label'
66
+ */
67
+ labelKey?: string
68
+ disabled?: boolean
69
+ class?: any
70
+ b24ui?: PartialString<typeof dropdownMenu.slots>
71
+ }
72
+
73
+ export interface DropdownMenuEmits extends DropdownMenuRootEmits {}
74
+
75
+ type SlotProps<T> = (props: { item: T, active?: boolean, index: number }) => any
76
+
77
+ export type DropdownMenuSlots<T extends { slot?: string }> = {
78
+ 'default'(props: { open: boolean }): any
79
+ 'item': SlotProps<T>
80
+ 'item-leading': SlotProps<T>
81
+ 'item-label': SlotProps<T>
82
+ 'item-trailing': SlotProps<T>
83
+ } & DynamicSlots<T, SlotProps<T>>
84
+ </script>
85
+
86
+ <script setup lang="ts" generic="T extends DropdownMenuItem">
87
+ import { computed, toRef } from 'vue'
88
+ import { defu } from 'defu'
89
+ import { DropdownMenuRoot, DropdownMenuTrigger, DropdownMenuArrow, useForwardPropsEmits } from 'reka-ui'
90
+ import { reactivePick } from '@vueuse/core'
91
+ import { omit } from '../utils'
92
+ import UDropdownMenuContent from './DropdownMenuContent.vue'
93
+
94
+ const props = withDefaults(defineProps<DropdownMenuProps<T>>(), {
95
+ portal: true,
96
+ modal: true,
97
+ labelKey: 'label'
98
+ })
99
+ const emits = defineEmits<DropdownMenuEmits>()
100
+ const slots = defineSlots<DropdownMenuSlots<T>>()
101
+
102
+ const rootProps = useForwardPropsEmits(reactivePick(props, 'defaultOpen', 'open', 'modal'), emits)
103
+ const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, collisionPadding: 8 }) as DropdownMenuContentProps)
104
+ const arrowProps = toRef(() => props.arrow as DropdownMenuArrowProps)
105
+ const proxySlots = omit(slots, ['default']) as Record<string, DropdownMenuSlots<T>[string]>
106
+
107
+ const b24ui = computed(() => dropdownMenu({
108
+ size: props.size
109
+ }))
110
+ </script>
111
+
112
+ <template>
113
+ <DropdownMenuRoot v-slot="{ open }" v-bind="rootProps">
114
+ <DropdownMenuTrigger v-if="!!slots.default" as-child :class="props.class" :disabled="disabled">
115
+ <slot :open="open" />
116
+ </DropdownMenuTrigger>
117
+
118
+ <UDropdownMenuContent
119
+ :class="b24ui.content({ class: [!slots.default && props.class, props.b24ui?.content] })"
120
+ :b24ui="b24ui"
121
+ :b24ui-override="props.b24ui"
122
+ v-bind="contentProps"
123
+ :items="items"
124
+ :portal="portal"
125
+ :label-key="labelKey"
126
+ :checked-icon="checkedIcon"
127
+ >
128
+ <template v-for="(_, name) in proxySlots" #[name]="slotData: any">
129
+ <slot :name="name" v-bind="slotData" />
130
+ </template>
131
+
132
+ <DropdownMenuArrow v-if="!!arrow" v-bind="arrowProps" :class="b24ui.arrow({ class: props.b24ui?.arrow })" />
133
+ </UDropdownMenuContent>
134
+ </DropdownMenuRoot>
135
+ </template>
@@ -0,0 +1,182 @@
1
+ <!-- eslint-disable vue/block-tag-newline -->
2
+ <script lang="ts">
3
+ import type { DropdownMenuContentProps as RekaDropdownMenuContentProps, DropdownMenuContentEmits as RekaDropdownMenuContentEmits } from 'reka-ui'
4
+ import theme from '#build/b24ui/dropdown-menu'
5
+ import { tv } from '../utils/tv'
6
+ import type { KbdProps, AvatarProps, DropdownMenuItem, DropdownMenuSlots, IconComponent } from '../types'
7
+
8
+ const _dropdownMenu = tv(theme)()
9
+
10
+ interface DropdownMenuContentProps<T> extends Omit<RekaDropdownMenuContentProps, 'as' | 'asChild' | 'forceMount'> {
11
+ items?: T[] | T[][]
12
+ portal?: boolean
13
+ sub?: boolean
14
+ labelKey: string
15
+ checkedIcon?: IconComponent
16
+ class?: any
17
+ b24ui: typeof _dropdownMenu
18
+ b24uiOverride?: any
19
+ }
20
+
21
+ interface DropdownMenuContentEmits extends RekaDropdownMenuContentEmits {}
22
+
23
+ type DropdownMenuContentSlots<T extends { slot?: string }> = Omit<DropdownMenuSlots<T>, 'default'> & {
24
+ default(props?: {}): any
25
+ }
26
+ </script>
27
+
28
+ <script setup lang="ts" generic="T extends DropdownMenuItem">
29
+ import { computed } from 'vue'
30
+ import { DropdownMenu } from 'reka-ui/namespaced'
31
+ import { useForwardPropsEmits } from 'reka-ui'
32
+ import { reactiveOmit, createReusableTemplate } from '@vueuse/core'
33
+ import { omit, get } from '../utils'
34
+ import { pickLinkProps } from '../utils/link'
35
+ import icons from '../dictionary/icons'
36
+ import B24LinkBase from './LinkBase.vue'
37
+ import B24Link from './Link.vue'
38
+ import B24Avatar from './Avatar.vue'
39
+ import B24Kbd from './Kbd.vue'
40
+ // eslint-disable-next-line import/no-self-import
41
+ import B24DropdownMenuContent from './DropdownMenuContent.vue'
42
+
43
+ const props = defineProps<DropdownMenuContentProps<T>>()
44
+ const emits = defineEmits<DropdownMenuContentEmits>()
45
+ const slots = defineSlots<DropdownMenuContentSlots<T>>()
46
+
47
+ const contentProps = useForwardPropsEmits(reactiveOmit(props, 'sub', 'items', 'portal', 'labelKey', 'checkedIcon', 'class', 'b24ui', 'b24uiOverride'), emits)
48
+ const proxySlots = omit(slots, ['default']) as Record<string, DropdownMenuContentSlots<T>[string]>
49
+
50
+ const [DefineItemTemplate, ReuseItemTemplate] = createReusableTemplate<{ item: DropdownMenuItem, active?: boolean, index: number }>()
51
+
52
+ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0]) ? props.items : [props.items]) as T[][] : [])
53
+ </script>
54
+
55
+ <template>
56
+ <DefineItemTemplate v-slot="{ item, active, index }">
57
+ <slot :name="item.slot || 'item'" :item="(item as T)" :index="index">
58
+ <slot :name="item.slot ? `${item.slot}-leading`: 'item-leading'" :item="(item as T)" :active="active" :index="index">
59
+ <Component
60
+ :is="icons.loading"
61
+ v-if="item.loading"
62
+ :class="b24ui.itemLeadingIcon({ class: b24uiOverride?.itemLeadingIcon, color: item?.color, loading: true })"
63
+ />
64
+ <Component
65
+ :is="item.icon"
66
+ v-else-if="item.icon"
67
+ :class="b24ui.itemLeadingIcon({ class: b24uiOverride?.itemLeadingIcon, color: item?.color, active })"
68
+ />
69
+ <B24Avatar
70
+ v-else-if="item.avatar"
71
+ :size="((props.b24uiOverride?.itemLeadingAvatarSize || b24ui.itemLeadingAvatarSize()) as AvatarProps['size'])"
72
+ v-bind="item.avatar"
73
+ :class="b24ui.itemLeadingAvatar({ class: b24uiOverride?.itemLeadingAvatar, active })"
74
+ />
75
+ </slot>
76
+
77
+ <span v-if="get(item, props.labelKey as string) || !!slots[item.slot ? `${item.slot}-label`: 'item-label']" :class="b24ui.itemLabel({ class: b24uiOverride?.itemLabel, active })">
78
+ <slot :name="item.slot ? `${item.slot}-label`: 'item-label'" :item="(item as T)" :active="active" :index="index">
79
+ {{ get(item, props.labelKey as string) }}
80
+ </slot>
81
+ <Component
82
+ :is="icons.external"
83
+ v-if="item.target === '_blank'"
84
+ :class="b24ui.itemLabelExternalIcon({ class: b24uiOverride?.itemLabelExternalIcon, color: item?.color, active })"
85
+ />
86
+ </span>
87
+
88
+ <span :class="b24ui.itemTrailing({ class: b24uiOverride?.itemTrailing })">
89
+ <slot :name="item.slot ? `${item.slot}-trailing`: 'item-trailing'" :item="(item as T)" :active="active" :index="index">
90
+ <Component
91
+ :is="icons.chevronRight"
92
+ v-if="item.children?.length"
93
+ :class="b24ui.itemTrailingIcon({ class: b24uiOverride?.itemTrailingIcon, color: item?.color, active })"
94
+ />
95
+ <span v-else-if="item.kbds?.length" :class="b24ui.itemTrailingKbds({ class: b24uiOverride?.itemTrailingKbds })">
96
+ <B24Kbd v-for="(kbd, kbdIndex) in item.kbds" :key="kbdIndex" :size="((props.b24uiOverride?.itemTrailingKbdsSize || b24ui.itemTrailingKbdsSize()) as KbdProps['size'])" v-bind="typeof kbd === 'string' ? { value: kbd } : kbd" />
97
+ </span>
98
+ </slot>
99
+
100
+ <DropdownMenu.ItemIndicator as-child>
101
+ <Component
102
+ :is="checkedIcon || icons.check"
103
+ :class="b24ui.itemTrailingIcon({ class: b24uiOverride?.itemTrailingIcon, color: item?.color })"
104
+ />
105
+ </DropdownMenu.ItemIndicator>
106
+ </span>
107
+ </slot>
108
+ </DefineItemTemplate>
109
+
110
+ <DropdownMenu.Portal :disabled="!portal">
111
+ <component :is="sub ? DropdownMenu.SubContent : DropdownMenu.Content" :class="props.class" v-bind="contentProps">
112
+ <DropdownMenu.Group v-for="(group, groupIndex) in groups" :key="`group-${groupIndex}`" :class="b24ui.group({ class: b24uiOverride?.group })">
113
+ <template v-for="(item, index) in group" :key="`group-${groupIndex}-${index}`">
114
+ <DropdownMenu.Label v-if="item.type === 'label'" :class="b24ui.label({ class: b24uiOverride?.label })">
115
+ <ReuseItemTemplate :item="item" :index="index" />
116
+ </DropdownMenu.Label>
117
+ <DropdownMenu.Separator v-else-if="item.type === 'separator'" :class="b24ui.separator({ class: b24uiOverride?.separator })" />
118
+ <DropdownMenu.Sub v-else-if="item?.children?.length" :open="item.open" :default-open="item.defaultOpen">
119
+ <DropdownMenu.SubTrigger
120
+ as="button"
121
+ type="button"
122
+ :disabled="item.disabled"
123
+ :text-value="get(item, props.labelKey as string)"
124
+ :class="b24ui.item({ class: b24uiOverride?.item, color: item?.color })"
125
+ >
126
+ <ReuseItemTemplate :item="item" :index="index" />
127
+ </DropdownMenu.SubTrigger>
128
+
129
+ <B24DropdownMenuContent
130
+ sub
131
+ :class="props.class"
132
+ :b24ui="b24ui"
133
+ :b24ui-override="b24uiOverride"
134
+ :portal="portal"
135
+ :items="item.children"
136
+ side="right"
137
+ align="start"
138
+ :align-offset="-4"
139
+ :side-offset="3"
140
+ :label-key="labelKey"
141
+ :checked-icon="checkedIcon"
142
+ v-bind="item.content"
143
+ >
144
+ <template v-for="(_, name) in proxySlots" #[name]="slotData: any">
145
+ <slot :name="name" v-bind="slotData" />
146
+ </template>
147
+ </B24DropdownMenuContent>
148
+ </DropdownMenu.Sub>
149
+ <DropdownMenu.CheckboxItem
150
+ v-else-if="item.type === 'checkbox'"
151
+ :model-value="item.checked"
152
+ :disabled="item.disabled"
153
+ :text-value="get(item, props.labelKey as string)"
154
+ :class="b24ui.item({ class: [b24uiOverride?.item, item.class], color: item?.color })"
155
+ @update:model-value="item.onUpdateChecked"
156
+ @select="item.onSelect"
157
+ >
158
+ <ReuseItemTemplate :item="item" :index="index" />
159
+ </DropdownMenu.CheckboxItem>
160
+ <DropdownMenu.Item
161
+ v-else
162
+ as-child
163
+ :disabled="item.disabled"
164
+ :text-value="get(item, props.labelKey as string)"
165
+ @select="item.onSelect"
166
+ >
167
+ <B24Link v-slot="{ active, ...slotProps }" v-bind="pickLinkProps(item as Omit<DropdownMenuItem, 'type'>)" custom>
168
+ <B24LinkBase
169
+ v-bind="slotProps"
170
+ :class="b24ui.item({ class: [b24uiOverride?.item, item.class], color: item?.color, active })"
171
+ >
172
+ <ReuseItemTemplate :item="item" :active="active" :index="index" />
173
+ </B24LinkBase>
174
+ </B24Link>
175
+ </DropdownMenu.Item>
176
+ </template>
177
+ </DropdownMenu.Group>
178
+
179
+ <slot />
180
+ </component>
181
+ </DropdownMenu.Portal>
182
+ </template>
@@ -0,0 +1,179 @@
1
+ <script lang="ts">
2
+ import type { DialogRootProps, DialogRootEmits, DialogContentProps } from 'reka-ui'
3
+ import type { AppConfig } from '@nuxt/schema'
4
+ import _appConfig from '#build/app.config'
5
+ import theme from '#build/b24ui/modal'
6
+ import { tv } from '../utils/tv'
7
+ import type { ButtonProps, IconComponent } from '../types'
8
+
9
+ const appConfigModal = _appConfig as AppConfig & { b24ui: { modal: Partial<typeof theme> } }
10
+
11
+ const modal = tv({ extend: tv(theme), ...(appConfigModal.b24ui?.modal || {}) })
12
+
13
+ export interface ModalProps extends DialogRootProps {
14
+ title?: string
15
+ description?: string
16
+ /** The content of the modal. */
17
+ content?: Omit<DialogContentProps, 'as' | 'asChild' | 'forceMount'>
18
+ /**
19
+ * Render an overlay behind the modal.
20
+ * @defaultValue true
21
+ */
22
+ overlay?: boolean
23
+ /**
24
+ * Animate the modal when opening or closing.
25
+ * @defaultValue true
26
+ */
27
+ transition?: boolean
28
+ /**
29
+ * When `true`, the modal will take up the full screen.
30
+ * @defaultValue false
31
+ */
32
+ fullscreen?: boolean
33
+ /**
34
+ * Render the modal in a portal.
35
+ * @defaultValue true
36
+ */
37
+ portal?: boolean
38
+ /**
39
+ * Display a close button to dismiss the modal.
40
+ * `{ size: 'xs', color: 'link' }`{lang="ts-type"}
41
+ * @defaultValue true
42
+ */
43
+ close?: ButtonProps | boolean
44
+ /**
45
+ * The icon displayed in the close button.
46
+ * @defaultValue icons.close
47
+ */
48
+ closeIcon?: IconComponent
49
+ /**
50
+ * When `false`, the modal will not close when clicking outside or pressing escape.
51
+ * @defaultValue true
52
+ */
53
+ dismissible?: boolean
54
+ class?: any
55
+ b24ui?: Partial<typeof modal.slots>
56
+ }
57
+
58
+ export interface ModalEmits extends DialogRootEmits {}
59
+
60
+ export interface ModalSlots {
61
+ default(props: { open: boolean }): any
62
+ content(props?: {}): any
63
+ header(props?: {}): any
64
+ title(props?: {}): any
65
+ description(props?: {}): any
66
+ close(props: { b24ui: any }): any
67
+ body(props?: {}): any
68
+ footer(props?: {}): any
69
+ }
70
+ </script>
71
+
72
+ <script setup lang="ts">
73
+ import { computed, toRef } from 'vue'
74
+ import { DialogRoot, DialogTrigger, DialogPortal, DialogOverlay, DialogContent, DialogTitle, DialogDescription, DialogClose, VisuallyHidden, useForwardPropsEmits } from 'reka-ui'
75
+ import { reactivePick } from '@vueuse/core'
76
+ import { useLocale } from '../composables/useLocale'
77
+ import icons from '../dictionary/icons'
78
+ import B24Button from './Button.vue'
79
+
80
+ const props = withDefaults(defineProps<ModalProps>(), {
81
+ close: true,
82
+ portal: true,
83
+ overlay: true,
84
+ transition: true,
85
+ modal: true,
86
+ dismissible: true
87
+ })
88
+ const emits = defineEmits<ModalEmits>()
89
+ const slots = defineSlots<ModalSlots>()
90
+
91
+ const { t } = useLocale()
92
+
93
+ const rootProps = useForwardPropsEmits(reactivePick(props, 'open', 'defaultOpen', 'modal'), emits)
94
+ const contentProps = toRef(() => props.content)
95
+ const contentEvents = computed(() => {
96
+ if (!props.dismissible) {
97
+ return {
98
+ pointerDownOutside: (e: Event) => e.preventDefault(),
99
+ interactOutside: (e: Event) => e.preventDefault(),
100
+ escapeKeyDown: (e: Event) => e.preventDefault()
101
+ }
102
+ }
103
+
104
+ return {}
105
+ })
106
+
107
+ const b24ui = computed(() => modal({
108
+ transition: props.transition,
109
+ fullscreen: props.fullscreen
110
+ }))
111
+ </script>
112
+
113
+ <template>
114
+ <DialogRoot v-slot="{ open }" v-bind="rootProps">
115
+ <DialogTrigger v-if="!!slots.default" as-child :class="props.class">
116
+ <slot :open="open" />
117
+ </DialogTrigger>
118
+
119
+ <DialogPortal :disabled="!portal">
120
+ <DialogOverlay v-if="overlay" :class="b24ui.overlay({ class: props.b24ui?.overlay })" />
121
+
122
+ <DialogContent :class="b24ui.content({ class: [!slots.default && props.class, props.b24ui?.content] })" v-bind="contentProps" v-on="contentEvents">
123
+ <VisuallyHidden v-if="!!slots.content && ((title || !!slots.title) || (description || !!slots.description))">
124
+ <DialogTitle v-if="title || !!slots.title">
125
+ <slot name="title">
126
+ {{ title }}
127
+ </slot>
128
+ </DialogTitle>
129
+
130
+ <DialogDescription v-if="description || !!slots.description">
131
+ <slot name="description">
132
+ {{ description }}
133
+ </slot>
134
+ </DialogDescription>
135
+ </VisuallyHidden>
136
+
137
+ <slot name="content">
138
+ <div v-if="!!slots.header || (title || !!slots.title) || (description || !!slots.description) || (close || !!slots.close)" :class="b24ui.header({ class: props.b24ui?.header })">
139
+ <slot name="header">
140
+ <DialogTitle v-if="title || !!slots.title" :class="b24ui.title({ class: props.b24ui?.title })">
141
+ <slot name="title">
142
+ {{ title }}
143
+ </slot>
144
+ </DialogTitle>
145
+
146
+ <DialogDescription v-if="description || !!slots.description" :class="b24ui.description({ class: props.b24ui?.description })">
147
+ <slot name="description">
148
+ {{ description }}
149
+ </slot>
150
+ </DialogDescription>
151
+
152
+ <DialogClose as-child>
153
+ <slot name="close" :b24ui="b24ui">
154
+ <B24Button
155
+ v-if="close"
156
+ :icon="closeIcon || icons.close"
157
+ size="xs"
158
+ color="link"
159
+ :aria-label="t('modal.close')"
160
+ v-bind="typeof close === 'object' ? close : undefined"
161
+ :class="b24ui.close({ class: props.b24ui?.close })"
162
+ />
163
+ </slot>
164
+ </DialogClose>
165
+ </slot>
166
+ </div>
167
+
168
+ <div v-if="!!slots.body" :class="b24ui.body({ class: props.b24ui?.body })">
169
+ <slot name="body" />
170
+ </div>
171
+
172
+ <div v-if="!!slots.footer" :class="b24ui.footer({ class: props.b24ui?.footer })">
173
+ <slot name="footer" />
174
+ </div>
175
+ </slot>
176
+ </DialogContent>
177
+ </DialogPortal>
178
+ </DialogRoot>
179
+ </template>
@@ -0,0 +1,17 @@
1
+ <script lang="ts">
2
+ export interface ModalDialogCloseSlots {
3
+ default(props: {}): any
4
+ }
5
+ </script>
6
+
7
+ <script setup lang="ts">
8
+ import { DialogClose } from 'reka-ui'
9
+
10
+ defineSlots<ModalDialogCloseSlots>()
11
+ </script>
12
+
13
+ <template>
14
+ <DialogClose as-child>
15
+ <slot />
16
+ </DialogClose>
17
+ </template>
@@ -0,0 +1,12 @@
1
+ <script setup lang="ts">
2
+ import { inject } from 'vue'
3
+ import { useModal, modalInjectionKey } from '../composables/useModal'
4
+
5
+ const modalState = inject(modalInjectionKey)
6
+
7
+ const { isOpen } = useModal()
8
+ </script>
9
+
10
+ <template>
11
+ <component :is="modalState.component" v-if="modalState" v-bind="modalState.props" v-model:open="isOpen" />
12
+ </template>
@@ -15,6 +15,6 @@ export interface UseComponentIconsProps {
15
15
  export declare function useComponentIcons(componentProps: MaybeRefOrGetter<UseComponentIconsProps>): {
16
16
  isLeading: import("vue").ComputedRef<any>;
17
17
  isTrailing: import("vue").ComputedRef<boolean>;
18
- leadingIconName: import("vue").ComputedRef<IconComponent | undefined>;
19
- trailingIconName: import("vue").ComputedRef<IconComponent | undefined>;
18
+ leadingIconName: import("vue").ComputedRef<import("vue").FunctionalComponent<import("vue").HTMLAttributes & import("vue").VNodeProps, {}, any, {}> | undefined>;
19
+ trailingIconName: import("vue").ComputedRef<import("vue").FunctionalComponent<import("vue").HTMLAttributes & import("vue").VNodeProps, {}, any, {}> | undefined>;
20
20
  };
@@ -0,0 +1,17 @@
1
+ import type { ShallowRef, Component, InjectionKey } from 'vue';
2
+ import type { ComponentProps } from 'vue-component-type-helpers';
3
+ import type { ModalProps } from '../types';
4
+ export interface ModalState {
5
+ component: Component | string;
6
+ props: ModalProps;
7
+ }
8
+ export declare const modalInjectionKey: InjectionKey<ShallowRef<ModalState>>;
9
+ declare function _useModal(): {
10
+ open: <T extends Component>(component: T, props?: ModalProps & ComponentProps<T>) => void;
11
+ close: () => Promise<void>;
12
+ reset: () => void;
13
+ patch: <T extends Component = Record<string, never>>(props: Partial<ModalProps & ComponentProps<T>>) => void;
14
+ isOpen: import("vue").Ref<boolean, boolean>;
15
+ };
16
+ export declare const useModal: typeof _useModal;
17
+ export {};
@@ -0,0 +1,46 @@
1
+ import { ref, inject } from "vue";
2
+ import { createSharedComposable } from "@vueuse/core";
3
+ export const modalInjectionKey = Symbol("bitrix24-ui.modal");
4
+ function _useModal() {
5
+ const modalState = inject(modalInjectionKey);
6
+ const isOpen = ref(false);
7
+ function open(component, props) {
8
+ if (!modalState) {
9
+ throw new Error("useModal() is called without provider");
10
+ }
11
+ modalState.value = {
12
+ component,
13
+ props: props ?? {}
14
+ };
15
+ isOpen.value = true;
16
+ }
17
+ async function close() {
18
+ if (!modalState) return;
19
+ isOpen.value = false;
20
+ }
21
+ function reset() {
22
+ if (!modalState) return;
23
+ modalState.value = {
24
+ component: "div",
25
+ props: {}
26
+ };
27
+ }
28
+ function patch(props) {
29
+ if (!modalState) return;
30
+ modalState.value = {
31
+ ...modalState.value,
32
+ props: {
33
+ ...modalState.value.props,
34
+ ...props
35
+ }
36
+ };
37
+ }
38
+ return {
39
+ open,
40
+ close,
41
+ reset,
42
+ patch,
43
+ isOpen
44
+ };
45
+ }
46
+ export const useModal = createSharedComposable(_useModal);
@@ -1 +1 @@
1
- @plugin "@bitrix24/b24style";@import "./keyframes.css";@variant light (&:where(.light, .light *));@variant dark (&:where(.dark, .dark *));@layer base{body{@apply antialiased scheme-light dark:scheme-dark}}
1
+ @plugin "@bitrix24/b24style";@import "#build/b24ui.css";@import "./keyframes.css";@variant light (&:where(.light, .light *));@variant dark (&:where(.dark, .dark *));@layer base{body{@apply antialiased scheme-light dark:scheme-dark}}
@@ -0,0 +1,2 @@
1
+ declare const _default: import("#app").Plugin<Record<string, unknown>> & import("#app").ObjectPlugin<Record<string, unknown>>;
2
+ export default _default;
@@ -0,0 +1,10 @@
1
+ import { shallowRef } from "vue";
2
+ import { defineNuxtPlugin } from "#imports";
3
+ import { modalInjectionKey } from "../composables/useModal.js";
4
+ export default defineNuxtPlugin((nuxtApp) => {
5
+ const modalState = shallowRef({
6
+ component: "div",
7
+ props: {}
8
+ });
9
+ nuxtApp.vueApp.provide(modalInjectionKey, modalState);
10
+ });