@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.
- package/.nuxt/b24ui/button.ts +9 -2
- package/.nuxt/b24ui/checkbox.ts +1 -1
- package/.nuxt/b24ui/dropdown-menu.ts +235 -0
- package/.nuxt/b24ui/index.ts +2 -0
- package/.nuxt/b24ui/input-menu.ts +1 -1
- package/.nuxt/b24ui/modal.ts +28 -0
- package/.nuxt/b24ui/radio-group.ts +1 -1
- package/.nuxt/b24ui/range.ts +1 -1
- package/.nuxt/b24ui/select-menu.ts +1 -1
- package/.nuxt/b24ui/select.ts +1 -1
- package/.nuxt/b24ui/switch.ts +1 -1
- package/.nuxt/b24ui/tabs.ts +2 -2
- package/.nuxt/b24ui/toaster.ts +4 -4
- package/.nuxt/b24ui.css +2 -0
- package/dist/meta.cjs +9050 -1027
- package/dist/meta.d.cts +9050 -1027
- package/dist/meta.d.mts +9050 -1027
- package/dist/meta.d.ts +9050 -1027
- package/dist/meta.mjs +9050 -1027
- package/dist/module.cjs +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +1 -1
- package/dist/runtime/components/App.vue +2 -2
- package/dist/runtime/components/DropdownMenu.vue +135 -0
- package/dist/runtime/components/DropdownMenuContent.vue +182 -0
- package/dist/runtime/components/Modal.vue +179 -0
- package/dist/runtime/components/ModalDialogClose.vue +17 -0
- package/dist/runtime/components/ModalProvider.vue +12 -0
- package/dist/runtime/composables/useComponentIcons.d.ts +2 -2
- package/dist/runtime/composables/useModal.d.ts +17 -0
- package/dist/runtime/composables/useModal.js +46 -0
- package/dist/runtime/index.css +1 -1
- package/dist/runtime/plugins/modal.d.ts +2 -0
- package/dist/runtime/plugins/modal.js +10 -0
- package/dist/runtime/types/index.d.ts +3 -0
- package/dist/runtime/types/index.js +3 -0
- package/dist/runtime/vue/components/Link.vue +1 -0
- package/dist/shared/{b24ui-nuxt.D5cXbZSx.cjs → b24ui-nuxt.Ce3hzs_q.cjs} +440 -26
- package/dist/shared/{b24ui-nuxt.CrjojW8t.mjs → b24ui-nuxt.DY8ePXC7.mjs} +440 -26
- package/dist/unplugin.cjs +1 -1
- package/dist/unplugin.mjs +1 -1
- package/dist/vite.cjs +1 -1
- package/dist/vite.mjs +1 -1
- package/package.json +12 -8
package/dist/module.cjs
CHANGED
package/dist/module.json
CHANGED
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.
|
|
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
|
-
|
|
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
|
-
|
|
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<
|
|
19
|
-
trailingIconName: import("vue").ComputedRef<
|
|
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);
|
package/dist/runtime/index.css
CHANGED
|
@@ -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,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
|
+
});
|