Skip to content

Commit f4ef16a

Browse files
author
issayah
committed
BDropdown script setup conversion and improvements
1 parent 3fbf5fd commit f4ef16a

File tree

10 files changed

+280
-255
lines changed

10 files changed

+280
-255
lines changed

src/components/BAvatar/BAvatar.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import {isNumber, isNumeric, isString} from '@/utils/inspect'
3434
import {toFloat} from '@/utils/number'
3535
import {injectionKey} from './BAvatarGroup.vue'
3636
37-
interface Props {
37+
interface BAvatarProps {
3838
alt: string
3939
ariaLabel: string
4040
badge?: boolean | string
@@ -55,7 +55,7 @@ interface Props {
5555
variant?: ColorVariant
5656
}
5757
58-
const props = withDefaults(defineProps<Props>(), {
58+
const props = withDefaults(defineProps<BAvatarProps>(), {
5959
alt: 'avatar',
6060
badge: false,
6161
badgeLeft: false,
@@ -70,12 +70,12 @@ const props = withDefaults(defineProps<Props>(), {
7070
variant: 'secondary',
7171
})
7272
73-
interface Emits {
73+
interface BAvatarEmits {
7474
(e: 'click', value: MouseEvent): void
7575
(e: 'img-error', value: Event): void
7676
}
7777
78-
const emit = defineEmits<Emits>()
78+
const emit = defineEmits<BAvatarEmits>()
7979
8080
const slots = useSlots()
8181

src/components/BDropdown/BDropdown.vue

Lines changed: 134 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -38,142 +38,151 @@
3838
</div>
3939
</template>
4040

41-
<script lang="ts">
42-
import * as Popper from '@popperjs/core'
41+
<script setup lang="ts">
42+
// import type {BDropdownEmits, BDropdownProps} from '@/types/components'
43+
import type Popper from '@popperjs/core'
4344
import Dropdown from 'bootstrap/js/dist/dropdown'
44-
import {ComponentPublicInstance, computed, defineComponent, onMounted, PropType, ref} from 'vue'
45-
import BButton from '../../components/BButton/BButton.vue'
46-
import {ButtonVariant, Size} from '../../types'
47-
import mergeDeep from '../../utils/mergeDeep'
48-
import useId from '../../composables/useId'
49-
import useEventListener from '../../composables/useEventListener'
50-
import {HTMLElement} from '../../types/safeTypes'
45+
import {ComponentPublicInstance, computed, onMounted, ref} from 'vue'
46+
import BButton from '@/components/BButton/BButton.vue'
47+
import type {ButtonType, ButtonVariant, Size} from '@/types'
48+
import mergeDeep from '@/utils/mergeDeep'
49+
import useId from '@/composables/useId'
50+
import useEventListener from '@/composables/useEventListener'
5151
52-
export default defineComponent({
53-
name: 'BDropdown',
54-
components: {BButton},
55-
props: {
56-
autoClose: {type: [Boolean, String], default: true},
57-
block: {type: Boolean, default: false},
58-
boundary: {
59-
type: [HTMLElement, String] as PropType<Popper.Boundary>,
60-
default: 'clippingParents',
61-
},
62-
dark: {type: Boolean, default: false},
63-
disabled: {type: Boolean, default: false},
64-
dropup: {type: Boolean, default: false},
65-
dropright: {type: Boolean, default: false},
66-
dropleft: {type: Boolean, default: false},
67-
id: {type: String},
68-
menuClass: {type: [Array, Object, String]},
69-
noFlip: {type: Boolean, default: false},
70-
offset: {type: [Number, String], default: 0},
71-
popperOpts: {type: Object, default: () => ({})},
72-
right: {type: Boolean, default: false},
73-
role: {type: String, default: 'menu'},
74-
size: {type: String as PropType<Size>},
75-
split: {type: Boolean, default: false},
76-
splitButtonType: {type: String as PropType<'button' | 'submit' | 'reset'>, default: 'button'},
77-
splitClass: {type: [Array, Object, String]},
78-
splitHref: {type: String, default: null},
79-
noCaret: {type: Boolean, default: false},
80-
splitVariant: {type: String as PropType<ButtonVariant>},
81-
text: {type: String},
82-
toggleClass: {type: [Array, Object, String]},
83-
toggleText: {type: String, default: 'Toggle dropdown'},
84-
variant: {type: String as PropType<ButtonVariant>, default: 'secondary'},
85-
},
86-
emits: ['show', 'shown', 'hide', 'hidden'],
87-
setup(props, {emit}) {
88-
const parent = ref<HTMLElement>()
89-
const dropdown = ref<ComponentPublicInstance<HTMLElement>>()
90-
const instance = ref<Dropdown>()
91-
const computedId = useId(props.id, 'dropdown')
52+
// TODO it seems that some of these props are actually just Popper options
53+
// So some of them could be converted to their pure types similar to Popper.Boundary
54+
interface BDropdownProps {
55+
id: string
56+
menuClass: Array<string> | Record<string, unknown> | string
57+
size: Size
58+
splitClass: Array<string> | Record<string, unknown> | string
59+
splitVariant: ButtonVariant
60+
text: string
61+
toggleClass: Array<string> | Record<string, unknown> | string
62+
autoClose?: boolean | 'inside' | 'outside'
63+
block?: boolean
64+
boundary?: Popper.Boundary
65+
dark?: boolean
66+
disabled?: boolean
67+
dropup?: boolean
68+
dropright?: boolean
69+
dropleft?: boolean
70+
noFlip?: boolean
71+
offset?: number | string
72+
popperOpts?: Record<string, unknown>
73+
right?: boolean
74+
role?: string
75+
split?: boolean
76+
splitButtonType?: ButtonType
77+
splitHref?: string
78+
noCaret?: boolean
79+
toggleText?: string
80+
variant?: ButtonVariant
81+
}
82+
83+
const props = withDefaults(defineProps<BDropdownProps>(), {
84+
autoClose: true,
85+
block: false,
86+
boundary: 'clippingParents',
87+
dark: false,
88+
disabled: false,
89+
dropup: false,
90+
dropright: false,
91+
dropleft: false,
92+
noFlip: false,
93+
offset: 0,
94+
popperOpts: () => ({}),
95+
right: false,
96+
role: 'menu',
97+
split: false,
98+
splitButtonType: 'button',
99+
splitHref: undefined,
100+
noCaret: false,
101+
toggleText: 'Toggle dropdown',
102+
variant: 'secondary',
103+
})
92104
93-
useEventListener(parent, 'show.bs.dropdown', () => emit('show'))
94-
useEventListener(parent, 'shown.bs.dropdown', () => emit('shown'))
95-
useEventListener(parent, 'hide.bs.dropdown', () => emit('hide'))
96-
useEventListener(parent, 'hidden.bs.dropdown', () => emit('hidden'))
105+
interface BDropdownEmits {
106+
(e: 'show'): void
107+
(e: 'shown'): void
108+
(e: 'hide'): void
109+
(e: 'hidden'): void
110+
}
97111
98-
const classes = computed(() => ({
99-
'd-grid': props.block,
100-
'd-flex': props.block && props.split,
101-
}))
112+
const emit = defineEmits<BDropdownEmits>()
102113
103-
const buttonClasses = computed(() => ({
104-
'dropdown-toggle': !props.split,
105-
'dropdown-toggle-no-caret': props.noCaret && !props.split,
106-
'w-100': props.split && props.block,
107-
}))
114+
const parent = ref<HTMLElement>()
115+
const dropdown = ref<ComponentPublicInstance<HTMLElement>>()
116+
const instance = ref<Dropdown>()
117+
const computedId = useId(props.id, 'dropdown')
108118
109-
const dropdownMenuClasses = computed(() => ({
110-
'dropdown-menu-dark': props.dark,
111-
}))
119+
useEventListener(parent, 'show.bs.dropdown', () => emit('show'))
120+
useEventListener(parent, 'shown.bs.dropdown', () => emit('shown'))
121+
useEventListener(parent, 'hide.bs.dropdown', () => emit('hide'))
122+
useEventListener(parent, 'hidden.bs.dropdown', () => emit('hidden'))
112123
113-
const buttonAttr = computed(() => ({
114-
'data-bs-toggle': props.split ? null : 'dropdown',
115-
'aria-expanded': props.split ? null : false,
116-
'ref': props.split ? null : dropdown,
117-
'href': props.split ? props.splitHref : null,
118-
}))
124+
const classes = computed(() => ({
125+
'd-grid': props.block,
126+
'd-flex': props.block && props.split,
127+
}))
119128
120-
const splitAttr = computed(() => ({
121-
ref: props.split ? dropdown : null,
122-
}))
129+
const buttonClasses = computed(() => ({
130+
'dropdown-toggle': !props.split,
131+
'dropdown-toggle-no-caret': props.noCaret && !props.split,
132+
'w-100': props.split && props.block,
133+
}))
123134
124-
const hide = () => {
125-
instance.value?.hide()
126-
}
135+
const dropdownMenuClasses = computed(() => ({
136+
'dropdown-menu-dark': props.dark,
137+
}))
127138
128-
onMounted(() => {
129-
instance.value = new Dropdown(
130-
dropdown.value?.$el,
131-
{
132-
autoClose: props.autoClose,
133-
boundary: props.boundary,
134-
offset: props.offset.toString(),
135-
reference: props.offset || props.split ? 'parent' : 'toggle',
136-
popperConfig: (defaultConfig?: Partial<Popper.Options>) => {
137-
const dropDownConfig = {
138-
placement: 'bottom-start',
139-
modifiers: !props.noFlip
140-
? []
141-
: [
142-
{
143-
name: 'flip',
144-
options: {
145-
fallbackPlacements: [],
146-
},
147-
},
148-
],
149-
}
139+
const buttonAttr = computed(() => ({
140+
'data-bs-toggle': props.split ? undefined : 'dropdown',
141+
'aria-expanded': props.split ? undefined : false,
142+
'ref': props.split ? undefined : dropdown,
143+
'href': props.split ? props.splitHref : undefined,
144+
}))
150145
151-
if (props.dropup) {
152-
dropDownConfig.placement = props.right ? 'top-end' : 'top-start'
153-
} else if (props.dropright) {
154-
dropDownConfig.placement = 'right-start'
155-
} else if (props.dropleft) {
156-
dropDownConfig.placement = 'left-start'
157-
} else if (props.right) {
158-
dropDownConfig.placement = 'bottom-end'
159-
}
160-
return mergeDeep(defaultConfig, mergeDeep(dropDownConfig, props.popperOpts))
161-
},
162-
} as unknown as Dropdown.Options /* TODO: remove when added in Dropdown options by https://www.npmjs.com/package/@types/bootstrap */
163-
)
164-
})
146+
const splitAttr = computed(() => ({
147+
ref: props.split ? dropdown : undefined,
148+
}))
165149
166-
return {
167-
parent,
168-
computedId,
169-
classes,
170-
buttonClasses,
171-
buttonAttr,
172-
splitAttr,
173-
dropdownMenuClasses,
174-
dropdown,
175-
hide,
176-
}
177-
},
150+
const hide = (): void => {
151+
instance.value?.hide()
152+
}
153+
154+
onMounted((): void => {
155+
instance.value = new Dropdown(dropdown.value?.$el, {
156+
autoClose: props.autoClose,
157+
boundary: props.boundary,
158+
offset: props.offset ? props.offset.toString() : '',
159+
reference: props.offset || props.split ? 'parent' : 'toggle',
160+
popperConfig: (defaultConfig?: Partial<Popper.Options>) => {
161+
const dropDownConfig = {
162+
placement: 'bottom-start',
163+
modifiers: !props.noFlip
164+
? []
165+
: [
166+
{
167+
name: 'flip',
168+
options: {
169+
fallbackPlacements: [],
170+
},
171+
},
172+
],
173+
}
174+
175+
if (props.dropup) {
176+
dropDownConfig.placement = props.right ? 'top-end' : 'top-start'
177+
} else if (props.dropright) {
178+
dropDownConfig.placement = 'right-start'
179+
} else if (props.dropleft) {
180+
dropDownConfig.placement = 'left-start'
181+
} else if (props.right) {
182+
dropDownConfig.placement = 'bottom-end'
183+
}
184+
return mergeDeep(defaultConfig, mergeDeep(dropDownConfig, props.popperOpts))
185+
},
186+
})
178187
})
179188
</script>

src/components/BDropdown/BDropdownDivider.vue

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
</li>
55
</template>
66

7-
<script lang="ts">
8-
import {defineComponent} from 'vue'
7+
<script setup lang="ts">
8+
// import type {BDropdownDividerProps} from '@/types/components'
99
10-
export default defineComponent({
11-
name: 'BDropdownDivider',
12-
props: {
13-
tag: {type: String, default: 'hr'},
14-
},
10+
interface BDropdownDividerProps {
11+
tag?: string
12+
}
13+
14+
withDefaults(defineProps<BDropdownDividerProps>(), {
15+
tag: 'hr',
1516
})
1617
</script>

src/components/BDropdown/BDropdownForm.vue

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,3 @@
55
</form>
66
</li>
77
</template>
8-
9-
<script lang="ts">
10-
import {defineComponent} from 'vue'
11-
12-
export default defineComponent({
13-
name: 'BDropdownForm',
14-
})
15-
</script>

0 commit comments

Comments
 (0)