@bitrix24/b24ui-nuxt 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.nuxt/b24ui/button.ts +9 -2
- package/.nuxt/b24ui/checkbox.ts +1 -1
- package/.nuxt/b24ui/index.ts +2 -0
- package/.nuxt/b24ui/input-menu.ts +1 -1
- package/.nuxt/b24ui/input-number.ts +417 -0
- 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/README.md +1 -1
- package/dist/meta.cjs +4306 -655
- package/dist/meta.d.cts +4306 -655
- package/dist/meta.d.mts +4306 -655
- package/dist/meta.d.ts +4306 -655
- package/dist/meta.mjs +4306 -655
- package/dist/module.cjs +2 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +2 -1
- package/dist/runtime/components/App.vue +2 -2
- package/dist/runtime/components/DropdownMenu.vue +3 -3
- package/dist/runtime/components/InputNumber.vue +218 -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/useModal.d.ts +17 -0
- package/dist/runtime/composables/useModal.js +46 -0
- 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/shared/{b24ui-nuxt.DgnM0VWe.cjs → b24ui-nuxt.DGjopCKm.cjs} +478 -26
- package/dist/shared/{b24ui-nuxt.CYvh5VlN.mjs → b24ui-nuxt.V9TzyCqH.mjs} +478 -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 +6 -4
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.
|
|
5
|
+
const templates = require('./shared/b24ui-nuxt.DGjopCKm.cjs');
|
|
6
6
|
require('node:url');
|
|
7
7
|
require('scule');
|
|
8
8
|
|
|
@@ -41,6 +41,7 @@ const module$1 = kit.defineNuxtModule({
|
|
|
41
41
|
await registerModule("@nuxtjs/color-mode", { classSuffix: "", disableTransition: true });
|
|
42
42
|
}
|
|
43
43
|
kit.addPlugin({ src: resolve("./runtime/plugins/colors") });
|
|
44
|
+
kit.addPlugin({ src: resolve("./runtime/plugins/modal") });
|
|
44
45
|
kit.addComponentsDir({
|
|
45
46
|
path: resolve("./runtime/components"),
|
|
46
47
|
prefix: "B24",
|
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.V9TzyCqH.mjs';
|
|
4
4
|
import 'node:url';
|
|
5
5
|
import 'scule';
|
|
6
6
|
|
|
@@ -38,6 +38,7 @@ const module = defineNuxtModule({
|
|
|
38
38
|
await registerModule("@nuxtjs/color-mode", { classSuffix: "", disableTransition: true });
|
|
39
39
|
}
|
|
40
40
|
addPlugin({ src: resolve("./runtime/plugins/colors") });
|
|
41
|
+
addPlugin({ src: resolve("./runtime/plugins/modal") });
|
|
41
42
|
addComponentsDir({
|
|
42
43
|
path: resolve("./runtime/components"),
|
|
43
44
|
prefix: "B24",
|
|
@@ -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>
|
|
@@ -89,7 +89,7 @@ import { defu } from 'defu'
|
|
|
89
89
|
import { DropdownMenuRoot, DropdownMenuTrigger, DropdownMenuArrow, useForwardPropsEmits } from 'reka-ui'
|
|
90
90
|
import { reactivePick } from '@vueuse/core'
|
|
91
91
|
import { omit } from '../utils'
|
|
92
|
-
import
|
|
92
|
+
import B24DropdownMenuContent from './DropdownMenuContent.vue'
|
|
93
93
|
|
|
94
94
|
const props = withDefaults(defineProps<DropdownMenuProps<T>>(), {
|
|
95
95
|
portal: true,
|
|
@@ -115,7 +115,7 @@ const b24ui = computed(() => dropdownMenu({
|
|
|
115
115
|
<slot :open="open" />
|
|
116
116
|
</DropdownMenuTrigger>
|
|
117
117
|
|
|
118
|
-
<
|
|
118
|
+
<B24DropdownMenuContent
|
|
119
119
|
:class="b24ui.content({ class: [!slots.default && props.class, props.b24ui?.content] })"
|
|
120
120
|
:b24ui="b24ui"
|
|
121
121
|
:b24ui-override="props.b24ui"
|
|
@@ -130,6 +130,6 @@ const b24ui = computed(() => dropdownMenu({
|
|
|
130
130
|
</template>
|
|
131
131
|
|
|
132
132
|
<DropdownMenuArrow v-if="!!arrow" v-bind="arrowProps" :class="b24ui.arrow({ class: props.b24ui?.arrow })" />
|
|
133
|
-
</
|
|
133
|
+
</B24DropdownMenuContent>
|
|
134
134
|
</DropdownMenuRoot>
|
|
135
135
|
</template>
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { VariantProps } from 'tailwind-variants'
|
|
3
|
+
import type { NumberFieldRootProps } from 'reka-ui'
|
|
4
|
+
import type { AppConfig } from '@nuxt/schema'
|
|
5
|
+
import _appConfig from '#build/app.config'
|
|
6
|
+
import theme from '#build/b24ui/input-number'
|
|
7
|
+
import { tv } from '../utils/tv'
|
|
8
|
+
import type { ButtonProps, IconComponent } from '../types'
|
|
9
|
+
|
|
10
|
+
const appConfigInputNumber = _appConfig as AppConfig & { b24ui: { inputNumber: Partial<typeof theme> } }
|
|
11
|
+
|
|
12
|
+
const inputNumber = tv({ extend: tv(theme), ...(appConfigInputNumber.b24ui?.inputNumber || {}) })
|
|
13
|
+
|
|
14
|
+
type InputNumberVariants = VariantProps<typeof inputNumber>
|
|
15
|
+
|
|
16
|
+
export interface InputNumberProps extends Pick<NumberFieldRootProps, 'modelValue' | 'defaultValue' | 'min' | 'max' | 'step' | 'disabled' | 'required' | 'id' | 'name' | 'formatOptions'> {
|
|
17
|
+
/**
|
|
18
|
+
* The element or component this component should render as.
|
|
19
|
+
* @defaultValue 'div'
|
|
20
|
+
*/
|
|
21
|
+
as?: any
|
|
22
|
+
/** The placeholder text when the input is empty. */
|
|
23
|
+
placeholder?: string
|
|
24
|
+
color?: InputNumberVariants['color']
|
|
25
|
+
size?: InputNumberVariants['size']
|
|
26
|
+
/** Removes padding from input. */
|
|
27
|
+
noPadding?: boolean
|
|
28
|
+
/** removes all borders (rings). */
|
|
29
|
+
noBorder?: boolean
|
|
30
|
+
/** removes all borders (rings) except the bottom one. */
|
|
31
|
+
underline?: boolean
|
|
32
|
+
/** Rounds the corners of the button. */
|
|
33
|
+
rounded?: boolean
|
|
34
|
+
tag?: string
|
|
35
|
+
tagColor?: InputNumberVariants['tagColor']
|
|
36
|
+
/** Highlight the ring color like a focus state. */
|
|
37
|
+
highlight?: boolean
|
|
38
|
+
class?: any
|
|
39
|
+
b24ui?: Partial<typeof inputNumber.slots>
|
|
40
|
+
/**
|
|
41
|
+
* The orientation of the input menu.
|
|
42
|
+
* @defaultValue 'horizontal'
|
|
43
|
+
*/
|
|
44
|
+
orientation?: 'vertical' | 'horizontal'
|
|
45
|
+
/**
|
|
46
|
+
* Configure the increment button. The `size` is inherited.
|
|
47
|
+
* @defaultValue { color: 'link', depth: 'light' }
|
|
48
|
+
*/
|
|
49
|
+
increment?: ButtonProps
|
|
50
|
+
/**
|
|
51
|
+
* The icon displayed to increment the value.
|
|
52
|
+
* @defaultValue icons.plus = Plus30Icon
|
|
53
|
+
*/
|
|
54
|
+
incrementIcon?: IconComponent
|
|
55
|
+
/**
|
|
56
|
+
* Configure the decrement button. The `size` is inherited.
|
|
57
|
+
* @defaultValue { color: 'link', depth: 'light' }
|
|
58
|
+
*/
|
|
59
|
+
decrement?: ButtonProps
|
|
60
|
+
/**
|
|
61
|
+
* The icon displayed to decrement the value.
|
|
62
|
+
* @defaultValue icons.minus = Minus30Icon
|
|
63
|
+
*/
|
|
64
|
+
decrementIcon?: IconComponent
|
|
65
|
+
autofocus?: boolean
|
|
66
|
+
autofocusDelay?: number
|
|
67
|
+
/**
|
|
68
|
+
* The locale to use for formatting and parsing numbers.
|
|
69
|
+
* @defaultValue B24App.locale.code
|
|
70
|
+
*/
|
|
71
|
+
locale?: string
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface InputNumberEmits {
|
|
75
|
+
(e: 'update:modelValue', payload: number): void
|
|
76
|
+
(e: 'blur', event: FocusEvent): void
|
|
77
|
+
(e: 'change', payload: Event): void
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface InputNumberSlots {
|
|
81
|
+
increment(props?: {}): any
|
|
82
|
+
decrement(props?: {}): any
|
|
83
|
+
}
|
|
84
|
+
</script>
|
|
85
|
+
|
|
86
|
+
<script setup lang="ts">
|
|
87
|
+
import { onMounted, ref, computed } from 'vue'
|
|
88
|
+
import { NumberFieldRoot, NumberFieldInput, NumberFieldDecrement, NumberFieldIncrement, useForwardPropsEmits } from 'reka-ui'
|
|
89
|
+
import { reactivePick } from '@vueuse/core'
|
|
90
|
+
import { useFormField } from '../composables/useFormField'
|
|
91
|
+
import { useLocale } from '../composables/useLocale'
|
|
92
|
+
import icons from '../dictionary/icons'
|
|
93
|
+
import B24Button from './Button.vue'
|
|
94
|
+
|
|
95
|
+
defineOptions({ inheritAttrs: false })
|
|
96
|
+
|
|
97
|
+
const props = withDefaults(defineProps<InputNumberProps>(), {
|
|
98
|
+
orientation: 'horizontal',
|
|
99
|
+
color: 'primary',
|
|
100
|
+
size: 'md'
|
|
101
|
+
})
|
|
102
|
+
const emits = defineEmits<InputNumberEmits>()
|
|
103
|
+
defineSlots<InputNumberSlots>()
|
|
104
|
+
|
|
105
|
+
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'min', 'max', 'step', 'formatOptions'), emits)
|
|
106
|
+
|
|
107
|
+
const { emitFormBlur, emitFormFocus, emitFormChange, emitFormInput, id, color, size, name, highlight, disabled, ariaAttrs } = useFormField<InputNumberProps>(props)
|
|
108
|
+
|
|
109
|
+
const { t, code: codeLocale } = useLocale()
|
|
110
|
+
const locale = computed(() => props.locale || codeLocale.value)
|
|
111
|
+
|
|
112
|
+
const isTag = computed(() => {
|
|
113
|
+
return props.tag
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
const b24ui = computed(() => inputNumber({
|
|
117
|
+
color: color.value,
|
|
118
|
+
size: size.value,
|
|
119
|
+
tagColor: props.tagColor,
|
|
120
|
+
highlight: highlight.value,
|
|
121
|
+
rounded: Boolean(props.rounded),
|
|
122
|
+
noPadding: Boolean(props.noPadding),
|
|
123
|
+
noBorder: Boolean(props.noBorder),
|
|
124
|
+
underline: Boolean(props.underline),
|
|
125
|
+
orientation: props.orientation
|
|
126
|
+
}))
|
|
127
|
+
|
|
128
|
+
const incrementIcon = computed(() => props.incrementIcon || (props.orientation === 'horizontal' ? icons.plus : icons.chevronUp))
|
|
129
|
+
const decrementIcon = computed(() => props.decrementIcon || (props.orientation === 'horizontal' ? icons.minus : icons.chevronDown))
|
|
130
|
+
|
|
131
|
+
const inputRef = ref<InstanceType<typeof NumberFieldInput> | null>(null)
|
|
132
|
+
|
|
133
|
+
function autoFocus() {
|
|
134
|
+
if (props.autofocus) {
|
|
135
|
+
inputRef.value?.$el?.focus()
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
onMounted(() => {
|
|
140
|
+
setTimeout(() => {
|
|
141
|
+
autoFocus()
|
|
142
|
+
}, props.autofocusDelay)
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
function onUpdate(value: number) {
|
|
146
|
+
// @ts-expect-error - 'target' does not exist in type 'EventInit'
|
|
147
|
+
const event = new Event('change', { target: { value } })
|
|
148
|
+
emits('change', event)
|
|
149
|
+
|
|
150
|
+
emitFormChange()
|
|
151
|
+
emitFormInput()
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function onBlur(event: FocusEvent) {
|
|
155
|
+
emitFormBlur()
|
|
156
|
+
emits('blur', event)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
defineExpose({
|
|
160
|
+
inputRef
|
|
161
|
+
})
|
|
162
|
+
</script>
|
|
163
|
+
|
|
164
|
+
<template>
|
|
165
|
+
<NumberFieldRoot
|
|
166
|
+
v-bind="rootProps"
|
|
167
|
+
:id="id"
|
|
168
|
+
:class="b24ui.root({ class: [props.class, props.b24ui?.root] })"
|
|
169
|
+
:name="name"
|
|
170
|
+
:disabled="disabled"
|
|
171
|
+
:locale="locale"
|
|
172
|
+
@update:model-value="onUpdate"
|
|
173
|
+
>
|
|
174
|
+
<div v-if="isTag" :class="b24ui.tag({ class: props.b24ui?.tag })">
|
|
175
|
+
{{ props.tag }}
|
|
176
|
+
</div>
|
|
177
|
+
|
|
178
|
+
<NumberFieldInput
|
|
179
|
+
v-bind="{ ...$attrs, ...ariaAttrs }"
|
|
180
|
+
ref="inputRef"
|
|
181
|
+
:placeholder="placeholder"
|
|
182
|
+
:required="required"
|
|
183
|
+
:class="b24ui.base({ class: props.b24ui?.base })"
|
|
184
|
+
@blur="onBlur"
|
|
185
|
+
@focus="emitFormFocus"
|
|
186
|
+
/>
|
|
187
|
+
|
|
188
|
+
<div :class="b24ui.increment({ class: props.b24ui?.increment })">
|
|
189
|
+
<NumberFieldIncrement as-child :disabled="disabled">
|
|
190
|
+
<slot name="increment">
|
|
191
|
+
<B24Button
|
|
192
|
+
:icon="incrementIcon"
|
|
193
|
+
:size="size"
|
|
194
|
+
color="link"
|
|
195
|
+
depth="light"
|
|
196
|
+
:aria-label="t('inputNumber.increment')"
|
|
197
|
+
v-bind="typeof increment === 'object' ? increment : undefined"
|
|
198
|
+
/>
|
|
199
|
+
</slot>
|
|
200
|
+
</NumberFieldIncrement>
|
|
201
|
+
</div>
|
|
202
|
+
|
|
203
|
+
<div :class="b24ui.decrement({ class: props.b24ui?.decrement })">
|
|
204
|
+
<NumberFieldDecrement as-child :disabled="disabled">
|
|
205
|
+
<slot name="decrement">
|
|
206
|
+
<B24Button
|
|
207
|
+
:icon="decrementIcon"
|
|
208
|
+
:size="size"
|
|
209
|
+
color="link"
|
|
210
|
+
depth="light"
|
|
211
|
+
:aria-label="t('inputNumber.decrement')"
|
|
212
|
+
v-bind="typeof decrement === 'object' ? decrement : undefined"
|
|
213
|
+
/>
|
|
214
|
+
</slot>
|
|
215
|
+
</NumberFieldDecrement>
|
|
216
|
+
</div>
|
|
217
|
+
</NumberFieldRoot>
|
|
218
|
+
</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>
|
|
@@ -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);
|
|
@@ -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
|
+
});
|
|
@@ -15,8 +15,11 @@ export * from '../components/Form.vue';
|
|
|
15
15
|
export * from '../components/FormField.vue';
|
|
16
16
|
export * from '../components/Input.vue';
|
|
17
17
|
export * from '../components/InputMenu.vue';
|
|
18
|
+
export * from '../components/InputNumber.vue';
|
|
18
19
|
export * from '../components/Kbd.vue';
|
|
19
20
|
export * from '../components/Link.vue';
|
|
21
|
+
export * from '../components/Modal.vue';
|
|
22
|
+
export * from '../components/ModalDialogClose.vue';
|
|
20
23
|
export * from '../components/Progress.vue';
|
|
21
24
|
export * from '../components/RadioGroup.vue';
|
|
22
25
|
export * from '../components/Range.vue';
|
|
@@ -15,8 +15,11 @@ export * from "../components/Form.vue";
|
|
|
15
15
|
export * from "../components/FormField.vue";
|
|
16
16
|
export * from "../components/Input.vue";
|
|
17
17
|
export * from "../components/InputMenu.vue";
|
|
18
|
+
export * from "../components/InputNumber.vue";
|
|
18
19
|
export * from "../components/Kbd.vue";
|
|
19
20
|
export * from "../components/Link.vue";
|
|
21
|
+
export * from "../components/Modal.vue";
|
|
22
|
+
export * from "../components/ModalDialogClose.vue";
|
|
20
23
|
export * from "../components/Progress.vue";
|
|
21
24
|
export * from "../components/RadioGroup.vue";
|
|
22
25
|
export * from "../components/Range.vue";
|