Skip to content

Commit fa62c74

Browse files
author
issayah
committed
BModal script setup conversion
1 parent da7b4fe commit fa62c74

File tree

1 file changed

+206
-179
lines changed

1 file changed

+206
-179
lines changed

src/components/BModal.vue

Lines changed: 206 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -56,188 +56,215 @@
5656
</teleport>
5757
</template>
5858

59-
<script lang="ts">
60-
import {computed, defineComponent, onMounted, PropType, ref, watch} from 'vue'
59+
<script setup lang="ts">
60+
// import type {BModalEmits, BModalProps} from '@/types/components'
6161
import Modal from 'bootstrap/js/dist/modal'
62-
import BButton from '../components/BButton/BButton.vue'
63-
import useEventListener from '../composables/useEventListener'
64-
import type {ColorVariant, InputSize} from '../types'
62+
import BButton from '@/components/BButton/BButton.vue'
63+
import useEventListener from '@/composables/useEventListener'
64+
import type {ColorVariant, InputSize} from '@/types'
6565
66-
export default defineComponent({
67-
name: 'BModal',
68-
components: {BButton},
69-
inheritAttrs: false,
70-
props: {
71-
bodyBgVariant: {type: String as PropType<ColorVariant>, required: false},
72-
bodyClass: {type: String, required: false},
73-
bodyTextVariant: {type: String as PropType<ColorVariant>, required: false},
74-
busy: {type: Boolean, default: false},
75-
buttonSize: {type: String as PropType<InputSize>, default: 'md'},
76-
cancelDisabled: {type: Boolean, default: false},
77-
cancelTitle: {type: String, default: 'Cancel'},
78-
cancelVariant: {type: String as PropType<ColorVariant>, default: 'secondary'},
79-
centered: {type: Boolean, default: false},
80-
contentClass: {type: String, required: false},
81-
dialogClass: {type: String, required: false},
82-
footerBgVariant: {type: String as PropType<ColorVariant>, required: false},
83-
footerBorderVariant: {type: String as PropType<ColorVariant>, required: false},
84-
footerClass: {type: String, required: false},
85-
footerTextVariant: {type: String as PropType<ColorVariant>, required: false},
86-
fullscreen: {type: [Boolean, String], default: false},
87-
headerBgVariant: {type: String as PropType<ColorVariant>, required: false},
88-
headerBorderVariant: {type: String as PropType<ColorVariant>, required: false},
89-
headerClass: {type: String, required: false},
90-
headerCloseLabel: {type: String, default: 'Close'},
91-
headerCloseWhite: {type: Boolean, default: false},
92-
headerTextVariant: {type: String as PropType<ColorVariant>, required: false},
93-
hideBackdrop: {type: Boolean, default: false},
94-
hideFooter: {type: Boolean, default: false},
95-
hideHeader: {type: Boolean, default: false},
96-
hideHeaderClose: {type: Boolean, default: false},
97-
id: {type: String, required: false},
98-
modalClass: {type: String, required: false},
99-
modelValue: {type: Boolean, default: false},
100-
noCloseOnBackdrop: {type: Boolean, default: false},
101-
noCloseOnEsc: {type: Boolean, default: false},
102-
noFade: {type: Boolean, default: false},
103-
noFocus: {type: Boolean, default: false},
104-
okDisabled: {type: Boolean, default: false},
105-
okOnly: {type: Boolean, default: false},
106-
okTitle: {type: String, default: 'Ok'},
107-
okVariant: {type: String as PropType<ColorVariant>, default: 'primary'},
108-
scrollable: {type: Boolean, default: false},
109-
show: {type: Boolean, default: false},
110-
size: {type: String, required: false},
111-
title: {type: String, required: false},
112-
titleClass: {type: String, required: false},
113-
titleSrOnly: {type: Boolean, default: false},
114-
titleTag: {type: String, default: 'h5'},
66+
interface BModalProps {
67+
bodyBgVariant?: ColorVariant
68+
bodyClass?: string
69+
bodyTextVariant?: ColorVariant
70+
busy?: boolean
71+
buttonSize?: InputSize
72+
cancelDisabled?: boolean
73+
cancelTitle?: string
74+
cancelVariant?: ColorVariant
75+
centered?: boolean
76+
contentClass?: string
77+
dialogClass?: string
78+
footerBgVariant?: ColorVariant
79+
footerBorderVariant?: ColorVariant
80+
footerClass?: string
81+
footerTextVariant?: ColorVariant
82+
fullscreen?: boolean | string
83+
headerBgVariant?: ColorVariant
84+
headerBorderVariant?: ColorVariant
85+
headerClass?: string
86+
headerCloseLabel?: string
87+
headerCloseWhite?: boolean
88+
headerTextVariant?: ColorVariant
89+
hideBackdrop?: boolean
90+
hideFooter?: boolean
91+
hideHeader?: boolean
92+
hideHeaderClose?: boolean
93+
id?: string
94+
modalClass?: string
95+
modelValue?: boolean
96+
noCloseOnBackdrop?: boolean
97+
noCloseOnEsc?: boolean
98+
noFade?: boolean
99+
noFocus?: boolean
100+
okDisabled?: boolean
101+
okOnly?: boolean
102+
okTitle?: string
103+
okVariant?: ColorVariant
104+
scrollable?: boolean
105+
show?: boolean
106+
size?: string
107+
title?: string
108+
titleClass?: string
109+
titleSrOnly?: boolean
110+
titleTag?: string
111+
}
112+
113+
const props = withDefaults(defineProps<BModalProps>(), {
114+
busy: false,
115+
buttonSize: 'md',
116+
cancelDisabled: false,
117+
cancelTitle: 'Cancel',
118+
cancelVariant: 'secondary',
119+
centered: false,
120+
fullscreen: false,
121+
headerCloseLabel: 'Close',
122+
headerCloseWhite: false,
123+
hideBackdrop: false,
124+
hideFooter: false,
125+
hideHeader: false,
126+
hideHeaderClose: false,
127+
modelValue: false,
128+
noCloseOnBackdrop: false,
129+
noCloseOnEsc: false,
130+
noFade: false,
131+
noFocus: false,
132+
okDisabled: false,
133+
okOnly: false,
134+
okTitle: 'Ok',
135+
okVariant: 'primary',
136+
scrollable: false,
137+
show: false,
138+
titleSrOnly: false,
139+
titleTag: 'h5',
140+
})
141+
142+
interface BModalEmits {
143+
(e: 'update:modelValue', value: boolean): void
144+
(e: 'show', value: Event): void
145+
(e: 'shown', value: Event): void
146+
(e: 'hide', value: Event): void
147+
(e: 'hidden', value: Event): void
148+
(e: 'hide-prevented', value: Event): void
149+
(e: 'ok'): void
150+
(e: 'cancel'): void
151+
}
152+
153+
const emit = defineEmits<BModalEmits>()
154+
155+
const slots = useSlots()
156+
157+
const element = ref<HTMLElement>()
158+
const instance = ref<Modal>()
159+
const modalClasses = computed(() => [
160+
{
161+
fade: !props.noFade,
162+
show: props.show,
115163
},
116-
emits: ['update:modelValue', 'show', 'shown', 'hide', 'hidden', 'hide-prevented', 'ok', 'cancel'],
117-
setup(props, {emit, slots}) {
118-
const element = ref<HTMLElement>()
119-
const instance = ref<Modal>()
120-
const modalClasses = computed(() => [
121-
{
122-
fade: !props.noFade,
123-
show: props.show,
124-
},
125-
props.modalClass,
126-
])
127-
const modalDialogClasses = computed(() => [
128-
{
129-
'modal-fullscreen': typeof props.fullscreen === 'boolean' ? props.fullscreen : false,
130-
[`modal-fullscreen-${props.fullscreen}-down`]:
131-
typeof props.fullscreen === 'string' ? props.fullscreen : false,
132-
[`modal-${props.size}`]: props.size,
133-
'modal-dialog-centered': props.centered,
134-
'modal-dialog-scrollable': props.scrollable,
135-
},
136-
props.dialogClass,
137-
])
138-
139-
const computedBodyClasses = computed(() => [
140-
{
141-
[`bg-${props.bodyBgVariant}`]: props.bodyBgVariant,
142-
[`text-${props.bodyTextVariant}`]: props.bodyTextVariant,
143-
},
144-
props.bodyClass,
145-
])
146-
147-
const computedHeaderClasses = computed(() => [
148-
{
149-
[`bg-${props.headerBgVariant}`]: props.headerBgVariant,
150-
[`border-${props.headerBorderVariant}`]: props.headerBorderVariant,
151-
[`text-${props.headerTextVariant}`]: props.headerTextVariant,
152-
},
153-
props.headerClass,
154-
])
155-
156-
const computedFooterClasses = computed(() => [
157-
{
158-
[`bg-${props.footerBgVariant}`]: props.footerBgVariant,
159-
[`border-${props.footerBorderVariant}`]: props.footerBorderVariant,
160-
[`text-${props.footerTextVariant}`]: props.footerTextVariant,
161-
},
162-
props.footerClass,
163-
])
164-
165-
const computedTitleClasses = computed(() => [
166-
{
167-
['visually-hidden']: props.titleSrOnly,
168-
},
169-
props.titleClass,
170-
])
171-
172-
const hasHeaderCloseSlot = computed(() => !!slots['header-close'])
173-
const computedCloseButtonClasses = computed(() => [
174-
{
175-
[`btn-close-content`]: hasHeaderCloseSlot.value,
176-
[`d-flex`]: hasHeaderCloseSlot.value,
177-
[`btn-close-white`]: !hasHeaderCloseSlot.value && props.headerCloseWhite,
178-
},
179-
])
180-
181-
const disableCancel = computed(() => props.cancelDisabled || props.busy)
182-
const disableOk = computed(() => props.okDisabled || props.busy)
183-
184-
useEventListener(element, 'shown.bs.modal', (e) => emit('shown', e))
185-
useEventListener(element, 'hidden.bs.modal', (e) => emit('hidden', e))
186-
useEventListener(element, 'hidePrevented.bs.modal', (e) => emit('hide-prevented', e))
187-
188-
useEventListener(element, 'show.bs.modal', (e) => {
189-
emit('show', e)
190-
if (!e.defaultPrevented) {
191-
emit('update:modelValue', true)
192-
}
193-
})
194-
195-
useEventListener(element, 'hide.bs.modal', (e) => {
196-
emit('hide', e)
197-
if (!e.defaultPrevented) {
198-
emit('update:modelValue', false)
199-
}
200-
})
201-
202-
onMounted(() => {
203-
instance.value = new Modal(element.value as HTMLElement, {
204-
backdrop: props.hideBackdrop
205-
? false
206-
: props.noCloseOnBackdrop
207-
? 'static'
208-
: !props.hideBackdrop,
209-
keyboard: !props.noCloseOnEsc,
210-
focus: !props.noFocus,
211-
})
212-
213-
if (props.modelValue) {
214-
instance.value?.show()
215-
}
216-
})
217-
218-
watch(
219-
() => props.modelValue,
220-
(value) => {
221-
if (value) {
222-
instance.value?.show()
223-
} else {
224-
instance.value?.hide()
225-
}
226-
}
227-
)
228-
229-
return {
230-
element,
231-
disableCancel,
232-
disableOk,
233-
modalClasses,
234-
modalDialogClasses,
235-
computedBodyClasses,
236-
computedFooterClasses,
237-
computedHeaderClasses,
238-
computedTitleClasses,
239-
computedCloseButtonClasses,
240-
}
164+
props.modalClass,
165+
])
166+
const modalDialogClasses = computed(() => [
167+
{
168+
'modal-fullscreen': typeof props.fullscreen === 'boolean' ? props.fullscreen : false,
169+
[`modal-fullscreen-${props.fullscreen}-down`]:
170+
typeof props.fullscreen === 'string' ? props.fullscreen : false,
171+
[`modal-${props.size}`]: props.size,
172+
'modal-dialog-centered': props.centered,
173+
'modal-dialog-scrollable': props.scrollable,
174+
},
175+
props.dialogClass,
176+
])
177+
178+
const computedBodyClasses = computed(() => [
179+
{
180+
[`bg-${props.bodyBgVariant}`]: props.bodyBgVariant,
181+
[`text-${props.bodyTextVariant}`]: props.bodyTextVariant,
182+
},
183+
props.bodyClass,
184+
])
185+
186+
const computedHeaderClasses = computed(() => [
187+
{
188+
[`bg-${props.headerBgVariant}`]: props.headerBgVariant,
189+
[`border-${props.headerBorderVariant}`]: props.headerBorderVariant,
190+
[`text-${props.headerTextVariant}`]: props.headerTextVariant,
191+
},
192+
props.headerClass,
193+
])
194+
195+
const computedFooterClasses = computed(() => [
196+
{
197+
[`bg-${props.footerBgVariant}`]: props.footerBgVariant,
198+
[`border-${props.footerBorderVariant}`]: props.footerBorderVariant,
199+
[`text-${props.footerTextVariant}`]: props.footerTextVariant,
200+
},
201+
props.footerClass,
202+
])
203+
204+
const computedTitleClasses = computed(() => [
205+
{
206+
['visually-hidden']: props.titleSrOnly,
241207
},
208+
props.titleClass,
209+
])
210+
211+
const hasHeaderCloseSlot = computed<boolean>(() => !!slots['header-close'])
212+
const computedCloseButtonClasses = computed(() => [
213+
{
214+
[`btn-close-content`]: hasHeaderCloseSlot.value,
215+
[`d-flex`]: hasHeaderCloseSlot.value,
216+
[`btn-close-white`]: !hasHeaderCloseSlot.value && props.headerCloseWhite,
217+
},
218+
])
219+
220+
const disableCancel = computed<boolean>(() => props.cancelDisabled || props.busy)
221+
const disableOk = computed<boolean>(() => props.okDisabled || props.busy)
222+
223+
useEventListener(element, 'shown.bs.modal', (e) => emit('shown', e))
224+
useEventListener(element, 'hidden.bs.modal', (e) => emit('hidden', e))
225+
useEventListener(element, 'hidePrevented.bs.modal', (e) => emit('hide-prevented', e))
226+
227+
useEventListener(element, 'show.bs.modal', (e) => {
228+
emit('show', e)
229+
if (!e.defaultPrevented) {
230+
emit('update:modelValue', true)
231+
}
232+
})
233+
234+
useEventListener(element, 'hide.bs.modal', (e) => {
235+
emit('hide', e)
236+
if (!e.defaultPrevented) {
237+
emit('update:modelValue', false)
238+
}
239+
})
240+
241+
onMounted(() => {
242+
instance.value = new Modal(element.value as HTMLElement, {
243+
backdrop: props.hideBackdrop ? false : props.noCloseOnBackdrop ? 'static' : !props.hideBackdrop,
244+
keyboard: !props.noCloseOnEsc,
245+
focus: !props.noFocus,
246+
})
247+
248+
if (props.modelValue) {
249+
instance.value?.show()
250+
}
251+
})
252+
253+
watch(
254+
() => props.modelValue,
255+
(value) => {
256+
if (value) {
257+
instance.value?.show()
258+
} else {
259+
instance.value?.hide()
260+
}
261+
}
262+
)
263+
</script>
264+
265+
<script lang="ts">
266+
import {computed, defineComponent, onMounted, ref, useSlots, watch} from 'vue'
267+
export default defineComponent({
268+
inheritAttrs: false,
242269
})
243270
</script>

0 commit comments

Comments
 (0)