diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f5c359f10..85fb695eb 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - "packages/bootstrap-vue-next": "0.24.17", - "packages/nuxt": "0.24.17" + "packages/bootstrap-vue-next": "0.24.23", + "packages/nuxt": "0.24.23" } diff --git a/apps/docs/src/components/ComponentReference.vue b/apps/docs/src/components/ComponentReference.vue index 94e4fe11a..98853b768 100644 --- a/apps/docs/src/components/ComponentReference.vue +++ b/apps/docs/src/components/ComponentReference.vue @@ -77,14 +77,16 @@ diff --git a/packages/bootstrap-vue-next/src/components/BDropdown/BDropdownItem.vue b/packages/bootstrap-vue-next/src/components/BDropdown/BDropdownItem.vue index 80316fb4e..0bf8d5014 100644 --- a/packages/bootstrap-vue-next/src/components/BDropdown/BDropdownItem.vue +++ b/packages/bootstrap-vue-next/src/components/BDropdown/BDropdownItem.vue @@ -27,6 +27,7 @@ import type {BDropdownItemProps} from '../../types/ComponentProps' import {useBLinkHelper} from '../../composables/useBLinkHelper' import {collapseInjectionKey, dropdownInjectionKey, navbarInjectionKey} from '../../utils/keys' import {useDefaults} from '../../composables/useDefaults' +import {useColorVariantClasses} from '../../composables/useColorVariantClasses' defineOptions({ inheritAttrs: false, @@ -74,12 +75,17 @@ defineSlots<{ const {computedLink, computedLinkProps} = useBLinkHelper(props) +const colorClasses = useColorVariantClasses( + computed(() => ({ + textVariant: props.variant, + })) +) const computedClasses = computed(() => [ props.linkClass, + colorClasses.value, { active: props.active, disabled: props.disabled, - [`text-${props.variant}`]: props.variant !== null, }, ]) diff --git a/packages/bootstrap-vue-next/src/components/BDropdown/BDropdownItemButton.vue b/packages/bootstrap-vue-next/src/components/BDropdown/BDropdownItemButton.vue index c49e88377..e50697b7c 100644 --- a/packages/bootstrap-vue-next/src/components/BDropdown/BDropdownItemButton.vue +++ b/packages/bootstrap-vue-next/src/components/BDropdown/BDropdownItemButton.vue @@ -15,6 +15,7 @@ diff --git a/packages/bootstrap-vue-next/src/components/BFormFile/BFormFile.vue b/packages/bootstrap-vue-next/src/components/BFormFile/BFormFile.vue index 9d0006f8a..5967d78d5 100644 --- a/packages/bootstrap-vue-next/src/components/BFormFile/BFormFile.vue +++ b/packages/bootstrap-vue-next/src/components/BFormFile/BFormFile.vue @@ -56,7 +56,7 @@ const _props = withDefaults(defineProps>(), { accept: '', autofocus: false, // eslint-disable-next-line vue/require-valid-default-prop - capture: false, + capture: undefined, directory: false, disabled: false, form: undefined, diff --git a/packages/bootstrap-vue-next/src/components/BFormFile/form-file.spec.ts b/packages/bootstrap-vue-next/src/components/BFormFile/form-file.spec.ts index 295ad09df..606542d98 100644 --- a/packages/bootstrap-vue-next/src/components/BFormFile/form-file.spec.ts +++ b/packages/bootstrap-vue-next/src/components/BFormFile/form-file.spec.ts @@ -229,7 +229,7 @@ describe('form-file', () => { props: {capture: undefined}, }) const $input = wrapper.get('input') - expect($input.attributes('capture')).toBe('false') + expect($input.attributes('capture')).toBe(undefined) }) it('input element has set attr accept to empty when prop accept is true', () => { diff --git a/packages/bootstrap-vue-next/src/components/BFormTags/BFormTag.vue b/packages/bootstrap-vue-next/src/components/BFormTags/BFormTag.vue index cd3ffdb8f..327037945 100644 --- a/packages/bootstrap-vue-next/src/components/BFormTags/BFormTag.vue +++ b/packages/bootstrap-vue-next/src/components/BFormTags/BFormTag.vue @@ -28,6 +28,7 @@ import {useDefaults} from '../../composables/useDefaults' import {useId} from '../../composables/useId' import type {BFormTagProps} from '../../types/ComponentProps' import BCloseButton from '../BButton/BCloseButton.vue' +import {useColorVariantClasses} from '../../composables/useColorVariantClasses' const _props = withDefaults(defineProps(), { disabled: false, @@ -57,9 +58,12 @@ const tagText = computed( ) const taglabelId = computed(() => `${computedId.value}taglabel__`) -const computedClasses = computed(() => ({ - [`text-bg-${props.variant}`]: props.variant !== null, - 'rounded-pill': props.pill, - 'disabled': props.disabled, -})) +const colorClasses = useColorVariantClasses(props) +const computedClasses = computed(() => [ + colorClasses.value, + { + 'rounded-pill': props.pill, + 'disabled': props.disabled, + }, +]) diff --git a/packages/bootstrap-vue-next/src/components/BModal/BModal.vue b/packages/bootstrap-vue-next/src/components/BModal/BModal.vue index 6156402df..ceea62344 100644 --- a/packages/bootstrap-vue-next/src/components/BModal/BModal.vue +++ b/packages/bootstrap-vue-next/src/components/BModal/BModal.vue @@ -1,7 +1,7 @@ @@ -116,7 +126,6 @@ import BButton from '../BButton/BButton.vue' import BCloseButton from '../BButton/BCloseButton.vue' import {useDefaults} from '../../composables/useDefaults' import {useId} from '../../composables/useId' -import {useFadeTransition} from '../../composables/useTransitions' import {useSafeScrollLock} from '../../composables/useSafeScrollLock' import {isEmptySlot} from '../../utils/dom' import {useColorVariantClasses} from '../../composables/useColorVariantClasses' @@ -259,8 +268,6 @@ const {needsFallback} = useActivatedFocusTrap({ }, }) -const fadeTransitionProps = useFadeTransition(true) - onKeyStroke( 'Escape', () => { @@ -310,46 +317,33 @@ const modalDialogClasses = computed(() => [ }, ]) -const resolvedBodyBgClasses = useColorVariantClasses(() => ({ +const bodyColorClasses = useColorVariantClasses(() => ({ bgVariant: props.bodyBgVariant, textVariant: props.bodyTextVariant, variant: props.bodyVariant, })) +const bodyClasses = computed(() => [props.bodyClass, bodyColorClasses.value]) -const bodyClasses = computed(() => [props.bodyClass, resolvedBodyBgClasses.value]) - -const resolvedHeaderBgClasses = useColorVariantClasses(() => ({ +const headerColorClasses = useColorVariantClasses(() => ({ bgVariant: props.headerBgVariant, textVariant: props.headerTextVariant, variant: props.headerVariant, + borderVariant: props.headerBorderVariant, })) - -const headerClasses = computed(() => [ - props.headerClass, - resolvedHeaderBgClasses.value, - { - [`border-${props.headerBorderVariant}`]: props.headerBorderVariant !== null, - }, -]) +const headerClasses = computed(() => [props.headerClass, headerColorClasses.value]) const headerCloseAttrs = computed(() => ({ variant: hasHeaderCloseSlot.value ? props.headerCloseVariant : undefined, class: props.headerCloseClass, })) -const resolvedFooterBgClasses = useColorVariantClasses(() => ({ +const footerColorClasses = useColorVariantClasses(() => ({ bgVariant: props.footerBgVariant, textVariant: props.footerTextVariant, variant: props.footerVariant, + borderVariant: props.footerBorderVariant, })) - -const footerClasses = computed(() => [ - props.footerClass, - resolvedFooterBgClasses.value, - { - [`border-${props.footerBorderVariant}`]: props.footerBorderVariant !== null, - }, -]) +const footerClasses = computed(() => [props.footerClass, footerColorClasses.value]) const titleClasses = computed(() => [ props.titleClass, @@ -373,6 +367,17 @@ const buildTriggerableEvent = ( componentId: computedId.value, }) +const fadeIn = (el: Element) => { + if (el) { + el.classList.add('show') + } +} +const fadeOut = (el: Element) => { + if (el) { + el.classList.remove('show') + } +} + watch(modelValue, (newValue, oldValue) => { if (newValue === oldValue) return if (newValue === true) { @@ -447,7 +452,8 @@ const pickFocusItem = () => { const onBeforeEnter = () => { showFn() } -const onAfterEnter = () => { +const onAfterEnter = (el: Element) => { + fadeIn(el) isActive.value = true pickFocusItem() emit('shown', buildTriggerableEvent('shown')) @@ -458,10 +464,11 @@ const onLeave = () => { isActive.value = false isLeaving.value = true } -const onAfterLeave = () => { +const onAfterLeave = (el: Element) => { emit('hidden', buildTriggerableEvent('hidden')) if (props.lazy === true) lazyLoadCompleted.value = false isLeaving.value = false + fadeOut(el) } const {activePosition, activeModalCount, stackWithoutSelf} = useModalManager( diff --git a/packages/bootstrap-vue-next/src/components/BModal/modal.spec.ts b/packages/bootstrap-vue-next/src/components/BModal/modal.spec.ts index 52920cfb8..599028f31 100644 --- a/packages/bootstrap-vue-next/src/components/BModal/modal.spec.ts +++ b/packages/bootstrap-vue-next/src/components/BModal/modal.spec.ts @@ -366,19 +366,25 @@ describe('modal', () => { // initial closed state let $modal = wrapper.find('div.modal') expect($modal.isVisible()).toBe(false) - expect(document.body.attributes.getNamedItem('style')?.textContent ?? '').toBe('') + expect(document.body.attributes.getNamedItem('style')?.textContent ?? '').not.toContain( + 'overflow: hidden;' + ) // open stated await wrapper.setProps({modelValue: true}) expect($modal.isVisible()).toBe(true) - expect(document.body.attributes.getNamedItem('style')?.textContent).toBe('overflow: hidden;') + expect(document.body.attributes.getNamedItem('style')?.textContent).toContain( + 'overflow: hidden;' + ) // closed state $modal = wrapper.find('div.modal') await $modal.trigger('keydown.Escape') $modal = wrapper.find('div.modal') expect($modal.isVisible()).toBe(false) - expect(document.body.attributes.getNamedItem('style')?.textContent ?? '').toBe('') + expect(document.body.attributes.getNamedItem('style')?.textContent ?? '').not.toContain( + 'overflow: hidden;' + ) wrapper.unmount() }) diff --git a/packages/bootstrap-vue-next/src/components/BNavbar/BNavbar.vue b/packages/bootstrap-vue-next/src/components/BNavbar/BNavbar.vue index f912cc4c5..b98aaf684 100644 --- a/packages/bootstrap-vue-next/src/components/BNavbar/BNavbar.vue +++ b/packages/bootstrap-vue-next/src/components/BNavbar/BNavbar.vue @@ -13,6 +13,7 @@ import type {BNavbarProps} from '../../types/ComponentProps' import {useDefaults} from '../../composables/useDefaults' import {useContainerClasses} from '../../composables/useContainerClasses' import {navbarInjectionKey} from '../../utils/keys' +import {useColorVariantClasses} from '../../composables/useColorVariantClasses' const _props = withDefaults(defineProps(), { autoClose: true, @@ -35,14 +36,21 @@ const computedRole = computed(() => (props.tag === 'nav' ? undefined : 'navigati const containerClass = useContainerClasses(() => props.container) -const computedClasses = computed(() => ({ - 'd-print': props.print, - [`sticky-${props.sticky}`]: props.sticky !== undefined, - [`bg-${props.variant}`]: props.variant !== null, - [`fixed-${props.fixed}`]: props.fixed !== undefined, - 'navbar-expand': props.toggleable === false, - [`navbar-expand-${props.toggleable}`]: typeof props.toggleable === 'string', -})) +const colorClasses = useColorVariantClasses( + computed(() => ({ + bgVariant: props.variant, + })) +) +const computedClasses = computed(() => [ + colorClasses.value, + { + 'd-print': props.print, + [`sticky-${props.sticky}`]: props.sticky !== undefined, + [`fixed-${props.fixed}`]: props.fixed !== undefined, + 'navbar-expand': props.toggleable === false, + [`navbar-expand-${props.toggleable}`]: typeof props.toggleable === 'string', + }, +]) provide(navbarInjectionKey, { tag: toRef(() => props.tag), diff --git a/packages/bootstrap-vue-next/src/components/BOverlay/BOverlay.vue b/packages/bootstrap-vue-next/src/components/BOverlay/BOverlay.vue index 0919aadf2..5ac5ccc1b 100644 --- a/packages/bootstrap-vue-next/src/components/BOverlay/BOverlay.vue +++ b/packages/bootstrap-vue-next/src/components/BOverlay/BOverlay.vue @@ -41,6 +41,8 @@ import BTransition from '../BTransition.vue' import BSpinner from '../BSpinner/BSpinner.vue' import ConditionalWrapper from '../ConditionalWrapper.vue' import {useRadiusElementClasses} from '../../composables/useRadiusElementClasses' +import {useColorVariantClasses} from '../../composables/useColorVariantClasses' +import type {BgColorVariant} from '../../types/ColorTypes' const _props = withDefaults(defineProps(), { blur: '2px', @@ -92,10 +94,6 @@ const radiusElementClasses = useRadiusElementClasses(() => ({ roundedEnd: props.roundedEnd, })) -const computedVariant = computed(() => - props.variant !== null && !props.bgColor ? `bg-${props.variant}` : '' -) - const computedAriaBusy = computed(() => (props.show ? true : null)) const spinnerAttrs = computed(() => ({ @@ -114,7 +112,12 @@ const overlayClasses = computed(() => ({ 'position-fixed': props.noWrap && props.fixed, })) -const blurClasses = computed(() => [computedVariant.value, radiusElementClasses.value]) +const colorClasses = useColorVariantClasses( + computed(() => ({ + bgVariant: props.variant !== null && !props.bgColor ? (props.variant as BgColorVariant) : null, + })) +) +const blurClasses = computed(() => [colorClasses.value, radiusElementClasses.value]) const blurStyles = computed(() => ({ ...positionStyles, diff --git a/packages/bootstrap-vue-next/src/components/BPlaceholder/BPlaceholder.vue b/packages/bootstrap-vue-next/src/components/BPlaceholder/BPlaceholder.vue index dbbdc0b35..7196da3ad 100644 --- a/packages/bootstrap-vue-next/src/components/BPlaceholder/BPlaceholder.vue +++ b/packages/bootstrap-vue-next/src/components/BPlaceholder/BPlaceholder.vue @@ -14,6 +14,7 @@ import {computed, type CSSProperties} from 'vue' import type {BPlaceholderProps} from '../../types/ComponentProps' import {useDefaults} from '../../composables/useDefaults' +import {useColorVariantClasses} from '../../composables/useColorVariantClasses' defineOptions({ inheritAttrs: false, @@ -46,11 +47,18 @@ const colsString = computed(() => : props.cols ) -const computedClasses = computed(() => ({ - [`col-${colsString.value}`]: colsString.value !== undefined && widthString.value === undefined, - [`bg-${props.variant}`]: props.variant !== null, - [`placeholder-${props.size}`]: props.size !== 'md', -})) +const colorClasses = useColorVariantClasses( + computed(() => ({ + bgVariant: props.variant, + })) +) +const computedClasses = computed(() => [ + colorClasses.value, + { + [`col-${colsString.value}`]: colsString.value !== undefined && widthString.value === undefined, + [`placeholder-${props.size}`]: props.size !== 'md', + }, +]) const wrapperClasses = computed(() => ({ [`placeholder-${props.animation}`]: props.animation !== undefined, diff --git a/packages/bootstrap-vue-next/src/components/BProgress/BProgressBar.vue b/packages/bootstrap-vue-next/src/components/BProgress/BProgressBar.vue index 6ac34cef3..0e85df5e3 100644 --- a/packages/bootstrap-vue-next/src/components/BProgress/BProgressBar.vue +++ b/packages/bootstrap-vue-next/src/components/BProgress/BProgressBar.vue @@ -45,10 +45,9 @@ defineSlots<{ const parentData = inject(progressInjectionKey, null) -const resolvedBackgroundClasses = useColorVariantClasses(props) - +const colorClasses = useColorVariantClasses(props) const computedClasses = computed(() => [ - resolvedBackgroundClasses.value, + colorClasses.value, { 'progress-bar-animated': props.animated || parentData?.animated.value, 'progress-bar-striped': diff --git a/packages/bootstrap-vue-next/src/components/BSpinner/BSpinner.vue b/packages/bootstrap-vue-next/src/components/BSpinner/BSpinner.vue index 32b98a4a3..010de87a0 100644 --- a/packages/bootstrap-vue-next/src/components/BSpinner/BSpinner.vue +++ b/packages/bootstrap-vue-next/src/components/BSpinner/BSpinner.vue @@ -16,6 +16,7 @@ import {computed} from 'vue' import type {BSpinnerProps} from '../../types/ComponentProps' import {isEmptySlot} from '../../utils/dom' import {useDefaults} from '../../composables/useDefaults' +import {useColorVariantClasses} from '../../composables/useColorVariantClasses' const _props = withDefaults(defineProps(), { label: undefined, @@ -33,11 +34,16 @@ const slots = defineSlots<{ label?: (props: Record) => any }>() +const colorClasses = useColorVariantClasses( + computed(() => ({ + textVariant: props.variant, + })) +) const computedClasses = computed(() => [ `spinner-${props.type}`, + colorClasses.value, { [`spinner-${props.type}-sm`]: props.small, - [`text-${props.variant}`]: props.variant !== null, }, ]) diff --git a/packages/bootstrap-vue-next/src/components/BTable/BTable.vue b/packages/bootstrap-vue-next/src/components/BTable/BTable.vue index 2ec947b2d..5f56fcbcc 100644 --- a/packages/bootstrap-vue-next/src/components/BTable/BTable.vue +++ b/packages/bootstrap-vue-next/src/components/BTable/BTable.vue @@ -1,4 +1,5 @@ @@ -138,8 +145,6 @@ import {useToNumber} from '@vueuse/core' import {computed, onMounted, type Ref, ref, watch} from 'vue' import {formatItem} from '../../utils/formatItem' -import BOverlay from '../BOverlay/BOverlay.vue' -import BSpinner from '../BSpinner/BSpinner.vue' import BTableLite from './BTableLite.vue' import BTd from './BTd.vue' import BTr from './BTr.vue' @@ -150,8 +155,10 @@ import { isTableItem, type NoProviderTypes, type TableField, + type TableFieldFormatter, type TableFieldRaw, type TableItem, + type TableRowEvent, type TableRowType, type TableStrictClassValue, } from '../../types/TableTypes' @@ -237,10 +244,12 @@ const props = useDefaults(_props, 'BTable') const emit = defineEmits<{ 'filtered': [value: T[]] 'head-clicked': [key: string, field: TableField, event: MouseEvent, isFooter: boolean] - 'row-clicked': [item: T, index: number, event: MouseEvent] - 'row-dbl-clicked': [item: T, index: number, event: MouseEvent] - 'row-hovered': [item: T, index: number, event: MouseEvent] - 'row-unhovered': [item: T, index: number, event: MouseEvent] + 'row-clicked': TableRowEvent + 'row-dblclicked': TableRowEvent + 'row-contextmenu': TableRowEvent + 'row-hovered': TableRowEvent + 'row-unhovered': TableRowEvent + 'row-middle-clicked': TableRowEvent 'row-selected': [value: T] 'row-unselected': [value: T] 'sorted': [value: BTableSortBy] @@ -266,6 +275,19 @@ const selectedItemsToSet = computed({ selectedItemsModel.value = [...val] }, }) + +watch(selectedItemsToSet, (newValue, oldValue) => { + Array.from(oldValue) + .filter((item) => !newValue.has(item)) + .forEach((item) => { + emit('row-unselected', item) + }) + Array.from(newValue) + .filter((item) => !oldValue.has(item)) + .forEach((item) => { + emit('row-selected', item) + }) +}) /** * This is to avoid the issue of directly mutating the array structure and to properly trigger the computed setter. * The utils also conveniently emit the proper events after @@ -275,7 +297,6 @@ const selectedItemsSetUtilities = { const value = new Set(selectedItemsToSet.value) value.add(item) selectedItemsToSet.value = value - emit('row-selected', item) }, clear: () => { selectedItemsToSet.value.forEach((item) => { @@ -298,13 +319,9 @@ const selectedItemsSetUtilities = { value.delete(item) } selectedItemsToSet.value = value - emit('row-unselected', item) }, set: (items: T[]) => { selectedItemsToSet.value = new Set(items) - selectedItemsToSet.value.forEach((item) => { - emit('row-unselected', item) - }) }, has: (item: T) => { if (!props.primaryKey) return selectedItemsToSet.value.has(item) @@ -408,6 +425,8 @@ const getRowClasses = (item: T | null, type: TableRowType): TableStrictClassValu : null, ] +const getFormatter = (value: TableField): TableFieldFormatter | undefined => + typeof value.sortByFormatted === 'function' ? value.sortByFormatted : value.formatter const computedItems = computed(() => { const sortItems = (items: T[]) => { // "undefined" values are set by us, we do this so we dont wipe out the comparer @@ -429,12 +448,9 @@ const computedItems = computed(() => { }) const val = get(ob, sortOption.key as keyof TableItem) if (isTableField(sortField) && !!sortField.sortByFormatted) { - const formatter = - typeof sortField.sortByFormatted === 'function' - ? sortField.sortByFormatted - : sortField.formatter + const formatter = getFormatter(sortField) if (formatter) { - return formatItem(ob, String(sortField.key), formatter) as string + return String(formatItem(ob, String(sortField.key), formatter)) } } return typeof val === 'object' && val !== null @@ -474,12 +490,9 @@ const computedItems = computed(() => { return false }) if (isTableField(filterField) && !!filterField.filterByFormatted) { - const formatter = - typeof filterField.filterByFormatted === 'function' - ? filterField.filterByFormatted - : filterField.formatter + const formatter = getFormatter(filterField) if (formatter) { - return formatter(val, String(filterField.key), item) as string + return String(formatter(val, String(filterField.key), item)) } } return typeof val === 'object' ? JSON.stringify(Object.values(val)) : val.toString() @@ -580,10 +593,10 @@ const handleRowSelection = ( // This is where range is different, due to the difference in shift } else if (shiftClicked) { const lastSelectedItem = [...selectedItemsToSet.value].pop() - const lastSelectedIndex = props.items.findIndex((i) => i === lastSelectedItem) + const lastSelectedIndex = computedItems.value.findIndex((i) => i === lastSelectedItem) const selectStartIndex = Math.min(lastSelectedIndex, index) const selectEndIndex = Math.max(lastSelectedIndex, index) - const items = props.items.slice(selectStartIndex, selectEndIndex + 1) + const items = computedItems.value.slice(selectStartIndex, selectEndIndex + 1) selectedItemsSetUtilities.set(items) // If nothing is being held, then we just behave like it's single mode } else { @@ -764,12 +777,7 @@ defineExpose({ refresh: callItemsProvider, selectAllRows: () => { if (!props.selectable) return - const unselectableItems = selectedItemsToSet.value.size > 0 ? [...selectedItemsToSet.value] : [] selectedItemsToSet.value = new Set([...computedItems.value]) - selectedItemsToSet.value.forEach((item) => { - if (unselectableItems.includes(item)) return - emit('row-selected', item) - }) }, selectRow: (index: number) => { if (!props.selectable) return diff --git a/packages/bootstrap-vue-next/src/components/BTable/BTableLite.vue b/packages/bootstrap-vue-next/src/components/BTable/BTableLite.vue index e69bf3ba8..c816fc966 100644 --- a/packages/bootstrap-vue-next/src/components/BTable/BTableLite.vue +++ b/packages/bootstrap-vue-next/src/components/BTable/BTableLite.vue @@ -83,9 +83,11 @@ :variant="isTableItem(item) ? item._rowVariant : undefined" v-bind="callTbodyTrAttrs(item, 'row')" @click="!filterEvent($event) && emit('row-clicked', item, itemIndex, $event)" - @dblclick="!filterEvent($event) && emit('row-dbl-clicked', item, itemIndex, $event)" + @dblclick="!filterEvent($event) && emit('row-dblclicked', item, itemIndex, $event)" + @contextmenu="!filterEvent($event) && emit('row-contextmenu', item, itemIndex, $event)" @mouseenter="!filterEvent($event) && emit('row-hovered', item, itemIndex, $event)" @mouseleave="!filterEvent($event) && emit('row-unhovered', item, itemIndex, $event)" + @mousedown="handleMiddleClick(item, itemIndex, $event)" > , event: MouseEvent, isFooter: boolean] - 'row-clicked': [item: T, index: number, event: MouseEvent] - 'row-dbl-clicked': [item: T, index: number, event: MouseEvent] - 'row-hovered': [item: T, index: number, event: MouseEvent] - 'row-unhovered': [item: T, index: number, event: MouseEvent] + 'row-clicked': TableRowEvent + 'row-dblclicked': TableRowEvent + 'row-contextmenu': TableRowEvent + 'row-hovered': TableRowEvent + 'row-unhovered': TableRowEvent + 'row-middle-clicked': TableRowEvent }>() const generateDetailsItem = (item: TableItem): [object, boolean | undefined] => [ item, item._showDetails, ] -const detailsMap = ref( - new WeakMap( - props.items.reduce( - (acc, el) => { - if (isTableItem(el)) { - acc.push(generateDetailsItem(el)) - } - return acc - }, - [] as [object, boolean | undefined][] - ) - ) -) +const detailsMap = ref(new WeakMap()) watch( () => props.items, (items) => { items.forEach((item) => { if (!isTableItem(item)) return - const detailsItem = generateDetailsItem(item) - detailsMap.value.set(detailsItem[0], detailsItem[1]) + detailsMap.value.set(...generateDetailsItem(item)) }) }, - {deep: true} + {deep: true, immediate: true} ) const computedTableClasses = computed(() => [ @@ -432,6 +424,11 @@ const getFieldRowClasses = (field: Readonly, tr: T) => { ] } +const handleMiddleClick = (item: T, itemIndex: number, event: MouseEvent) => { + if (event.button === 1 && !filterEvent(event)) { + emit('row-middle-clicked', item, itemIndex, event) + } +} const callTbodyTrAttrs = (item: T | null, type: TableRowType) => props.tbodyTrAttrs ? typeof props.tbodyTrAttrs === 'function' diff --git a/packages/bootstrap-vue-next/src/components/BTable/BTableSimple.vue b/packages/bootstrap-vue-next/src/components/BTable/BTableSimple.vue index 29d18c783..4a41608d5 100644 --- a/packages/bootstrap-vue-next/src/components/BTable/BTableSimple.vue +++ b/packages/bootstrap-vue-next/src/components/BTable/BTableSimple.vue @@ -15,6 +15,7 @@ import {computed, type StyleValue} from 'vue' import type {BTableSimpleProps} from '../../types/ComponentProps' import {useDefaults} from '../../composables/useDefaults' import {useNumberishToStyle} from '../../composables/useNumberishToStyle' +import {useColorVariantClasses} from '../../composables/useColorVariantClasses' const defaultStickyHeaderHeight = '300px' @@ -46,14 +47,19 @@ defineSlots<{ default?: (props: Record) => any }>() +const colorClasses = useColorVariantClasses( + computed(() => ({ + borderVariant: props.borderVariant, + })) +) const computedClasses = computed(() => [ props.tableClass, 'table', 'b-table', + colorClasses.value, { 'table-bordered': props.bordered, 'table-borderless': props.borderless, - [`border-${props.borderVariant}`]: props.borderVariant !== null, 'caption-top': props.captionTop, 'table-dark': props.dark, 'table-hover': props.hover, diff --git a/packages/bootstrap-vue-next/src/components/BTable/table.spec.ts b/packages/bootstrap-vue-next/src/components/BTable/table.spec.ts index 9827ffa8d..c64fbe3da 100644 --- a/packages/bootstrap-vue-next/src/components/BTable/table.spec.ts +++ b/packages/bootstrap-vue-next/src/components/BTable/table.spec.ts @@ -476,6 +476,37 @@ describe('object-persistence', () => { expect($trs[2].classes()).toContain('selected') expect($trs[3].classes()).toContain('selected') }) + it('shift click correct order', async () => { + const wrapper = mount(BTable, { + props: { + 'selectMode': 'range', + 'selectable': true, + 'items': [ + {id: 1, text: 'C'}, + {id: 2, text: 'A'}, + {id: 3, text: 'B'}, + {id: 4, text: 'D'}, + ], + 'fields': [ + {key: 'id', sortable: true}, + {key: 'text', sortable: true}, + ], + 'selectedItems': [], + 'onUpdate:selectedItems': (value) => wrapper.setProps({selectedItems: value}), + 'sortBy': [{key: 'text', order: 'asc'}], + 'primaryKey': 'id', + }, + }) + const [first, second, third, fourth] = wrapper.findAll('tr') + await fourth.trigger('click') + const event = new MouseEvent('click', {shiftKey: true}) + third.element.dispatchEvent(event) + await nextTick() + expect(first.classes()).not.toContain('selected') + expect(second.classes()).not.toContain('selected') + expect(third.classes()).toContain('selected') + expect(fourth.classes()).toContain('selected') + }) }) }) }) diff --git a/packages/bootstrap-vue-next/src/components/BToast/BToast.vue b/packages/bootstrap-vue-next/src/components/BToast/BToast.vue index 1f51f82b9..01df2d738 100644 --- a/packages/bootstrap-vue-next/src/components/BToast/BToast.vue +++ b/packages/bootstrap-vue-next/src/components/BToast/BToast.vue @@ -139,7 +139,6 @@ const modelValue = defineModel>({d const {computedLink, computedLinkProps} = useBLinkHelper(props) // TODO solid is never used -const resolvedBackgroundClasses = useColorVariantClasses(props) const countdownLength = computed(() => typeof modelValue.value === 'boolean' ? 0 : modelValue.value ) @@ -176,8 +175,9 @@ const isToastVisible = computed(() => : isActive.value || (props.showOnPause && isPaused.value) ) +const colorClasses = useColorVariantClasses(props) const computedClasses = computed(() => [ - resolvedBackgroundClasses.value, + colorClasses.value, { show: isToastVisible.value, }, diff --git a/packages/bootstrap-vue-next/src/components/BCard/card-head-foot.spec.ts b/packages/bootstrap-vue-next/src/components/card-head-foot.spec.ts similarity index 100% rename from packages/bootstrap-vue-next/src/components/BCard/card-head-foot.spec.ts rename to packages/bootstrap-vue-next/src/components/card-head-foot.spec.ts diff --git a/packages/bootstrap-vue-next/src/composables/useActivatedFocusTrap.ts b/packages/bootstrap-vue-next/src/composables/useActivatedFocusTrap.ts index f9a3a8d43..3879f8d5c 100644 --- a/packages/bootstrap-vue-next/src/composables/useActivatedFocusTrap.ts +++ b/packages/bootstrap-vue-next/src/composables/useActivatedFocusTrap.ts @@ -37,6 +37,7 @@ export const useActivatedFocusTrap = ( focusTrapOpts: UseFocusTrapOptions = { allowOutsideClick: true, fallbackFocus: fallbackFocus.ref.value ?? undefined, + escapeDeactivates: false, } ) => { const resolvedIsActive = readonly(toRef(isActive)) diff --git a/packages/bootstrap-vue-next/src/composables/useColorVariantClasses.ts b/packages/bootstrap-vue-next/src/composables/useColorVariantClasses.ts index 60e0932c0..a83e0ce34 100644 --- a/packages/bootstrap-vue-next/src/composables/useColorVariantClasses.ts +++ b/packages/bootstrap-vue-next/src/composables/useColorVariantClasses.ts @@ -1,18 +1,21 @@ import {computed, type MaybeRefOrGetter, toValue} from 'vue' -import type {ColorExtendables} from '../types/ColorTypes' +import type {BorderColorVariant, ColorExtendables} from '../types/ColorTypes' -export const useColorVariantClasses = (obj: MaybeRefOrGetter) => +export const useColorVariantClasses = ( + obj: MaybeRefOrGetter +) => computed(() => { let props = toValue(obj) props = { - ...props, variant: props.variant ?? null, bgVariant: props.bgVariant ?? null, textVariant: props.textVariant ?? null, + borderVariant: props.borderVariant ?? null, } return { [`text-bg-${props.variant}`]: props.variant !== null, [`text-${props.textVariant}`]: props.textVariant !== null, [`bg-${props.bgVariant}`]: props.bgVariant !== null, + [`border-${props.borderVariant}`]: props.borderVariant !== null, } }) diff --git a/packages/bootstrap-vue-next/src/composables/useId.ts b/packages/bootstrap-vue-next/src/composables/useId.ts index f6db7ede8..4a0a70ad3 100644 --- a/packages/bootstrap-vue-next/src/composables/useId.ts +++ b/packages/bootstrap-vue-next/src/composables/useId.ts @@ -1,9 +1,10 @@ import {computed, type ComputedRef, type MaybeRefOrGetter, toValue, useId as vueUseId} from 'vue' +import {bvnPrefix} from '../utils/bvnPrefix' export const useId = ( id?: MaybeRefOrGetter, suffix?: string ): ComputedRef => { const genId = vueUseId() - return computed(() => toValue(id) || `__BVID__${genId}___BV_${suffix}__`) + return computed(() => toValue(id) || bvnPrefix(genId || '', suffix)) } diff --git a/packages/bootstrap-vue-next/src/composables/useSafeScrollLock.ts b/packages/bootstrap-vue-next/src/composables/useSafeScrollLock.ts index 00480c2f7..5eaf62de7 100644 --- a/packages/bootstrap-vue-next/src/composables/useSafeScrollLock.ts +++ b/packages/bootstrap-vue-next/src/composables/useSafeScrollLock.ts @@ -1,11 +1,25 @@ -import {computed, type MaybeRefOrGetter, onMounted, readonly, toRef, toValue, watch} from 'vue' +import { + computed, + type MaybeRefOrGetter, + onMounted, + onUnmounted, + readonly, + toRef, + toValue, + useId, + watch, +} from 'vue' import {useScrollLock} from '@vueuse/core' +let prevousRightPadding = '' +const lockRegistry = new Map() + export const useSafeScrollLock = ( isOpen: MaybeRefOrGetter, bodyScroll: MaybeRefOrGetter ) => { const resolvedIsOpen = readonly(toRef(isOpen)) + const id = useId() /** * We use the inverse because bodyScrolling === true means we allow scrolling, while bodyScrolling === false means we disallow @@ -13,13 +27,40 @@ export const useSafeScrollLock = ( const inverseBodyScrollingValue = computed(() => !toValue(bodyScroll)) onMounted(() => { + lockRegistry.set(id, false) const isLocked = useScrollLock( document.body, resolvedIsOpen.value && inverseBodyScrollingValue.value ) - watch([resolvedIsOpen, inverseBodyScrollingValue], ([modelVal, bodyVal]) => { - isLocked.value = modelVal && bodyVal - }) + watch( + [resolvedIsOpen, inverseBodyScrollingValue], + ([modelVal, bodyVal]) => { + const scrollBarGap = window.innerWidth - document.documentElement.clientWidth + const hasLocked = Array.from(lockRegistry.values()).some((val) => val === true) + + const myLocked = modelVal && bodyVal + lockRegistry.set(id, myLocked) + + if (myLocked && !hasLocked && !isLocked.value) { + isLocked.value = true + if (scrollBarGap > 0) { + prevousRightPadding = document.body.style.paddingRight + document.body.style.paddingRight = `${scrollBarGap + prevousRightPadding}px` + } + } + const hasLockedAfter = Array.from(lockRegistry.values()).some((val) => val === true) + + if (hasLocked && !hasLockedAfter) { + lockRegistry.set(id, false) + isLocked.value = false + document.body.style.paddingRight = prevousRightPadding + } + }, + {immediate: true} + ) + }) + onUnmounted(() => { + lockRegistry.delete(id) }) } diff --git a/packages/bootstrap-vue-next/src/composables/useTransitions.ts b/packages/bootstrap-vue-next/src/composables/useTransitions.ts deleted file mode 100644 index 16b9079cf..000000000 --- a/packages/bootstrap-vue-next/src/composables/useTransitions.ts +++ /dev/null @@ -1,22 +0,0 @@ -import {computed, type MaybeRefOrGetter, toValue} from 'vue' - -export const useFadeTransition = (noFade: MaybeRefOrGetter) => - computed(() => { - const NO_FADE_PROPS = { - name: '', - enterActiveClass: '', - enterToClass: '', - leaveActiveClass: '', - leaveToClass: 'showing', - enterFromClass: 'showing', - leaveFromClass: '', - css: true, - } - const FADE_PROPS = { - ...NO_FADE_PROPS, - name: 'fade', - enterActiveClass: 'fade showing', - leaveActiveClass: 'fade showing', - } - return toValue(noFade) ? NO_FADE_PROPS : FADE_PROPS - }) diff --git a/packages/bootstrap-vue-next/src/directives/BPopover/index.ts b/packages/bootstrap-vue-next/src/directives/BPopover/index.ts index 7f5e23ae0..8976ad8cf 100644 --- a/packages/bootstrap-vue-next/src/directives/BPopover/index.ts +++ b/packages/bootstrap-vue-next/src/directives/BPopover/index.ts @@ -10,25 +10,23 @@ import { import {defaultsKey} from '../../utils/keys' export const vBPopover: Directive = { - mounted(el, binding, vnode) { - // @ts-expect-error type doesn't have runtime ctx - const defaults = vnode.ctx?.appContext?.provides?.[defaultsKey as symbol]?.value + mounted(el, binding) { + const defaults = binding.instance?.$.appContext?.provides?.[defaultsKey as symbol]?.value const isActive = resolveActiveStatus(binding.value) if (!isActive) return const text = resolveContent(binding.value, el) if (!text.content && !text.title) return - el.$__binding = JSON.stringify(binding) + el.$__binding = JSON.stringify([binding.modifiers, binding.value]) bind(el, binding, { ...(defaults['BPopover'] || {}), ...resolveDirectiveProps(binding, el), ...text, }) }, - updated(el, binding, vnode) { - // @ts-expect-error type doesn't have runtime ctx - const defaults = vnode.ctx?.appContext?.provides?.[defaultsKey as symbol]?.value + updated(el, binding) { + const defaults = binding.instance?.$.appContext?.provides?.[defaultsKey as symbol]?.value const isActive = resolveActiveStatus(binding.value) if (!isActive) return @@ -37,14 +35,14 @@ export const vBPopover: Directive = { if (!text.content && !text.title) return delete binding.oldValue - if (el.$__binding === JSON.stringify(binding)) return + if (el.$__binding === JSON.stringify([binding.modifiers, binding.value])) return unbind(el) bind(el, binding, { ...(defaults['BPopover'] || {}), ...resolveDirectiveProps(binding, el), ...text, }) - el.$__binding = JSON.stringify(binding) + el.$__binding = JSON.stringify([binding.modifiers, binding.value]) }, beforeUnmount(el) { unbind(el) diff --git a/packages/bootstrap-vue-next/src/directives/BTooltip/index.ts b/packages/bootstrap-vue-next/src/directives/BTooltip/index.ts index 1c1db2e00..1cc475bbd 100644 --- a/packages/bootstrap-vue-next/src/directives/BTooltip/index.ts +++ b/packages/bootstrap-vue-next/src/directives/BTooltip/index.ts @@ -10,9 +10,8 @@ import { import {defaultsKey} from '../../utils/keys' export const vBTooltip: Directive = { - mounted(el, binding, vnode) { - // @ts-expect-error type doesn't have runtime ctx - const defaults = vnode.ctx?.appContext?.provides?.[defaultsKey as symbol]?.value + mounted(el, binding) { + const defaults = binding.instance?.$.appContext?.provides?.[defaultsKey as symbol]?.value const isActive = resolveActiveStatus(binding.value) if (!isActive) return @@ -20,7 +19,7 @@ export const vBTooltip: Directive = { const text = resolveContent(binding.value, el) if (!text.content && !text.title) return - el.$__binding = JSON.stringify(binding) + el.$__binding = JSON.stringify([binding.modifiers, binding.value]) bind(el, binding, { noninteractive: true, ...(defaults['BTooltip'] || {}), @@ -29,9 +28,8 @@ export const vBTooltip: Directive = { tooltip: isActive, }) }, - updated(el, binding, vnode) { - // @ts-expect-error type doesn't have runtime ctx - const defaults = vnode.ctx?.appContext?.provides?.[defaultsKey as symbol]?.value + updated(el, binding) { + const defaults = binding.instance?.$.appContext?.provides?.[defaultsKey as symbol]?.value const isActive = resolveActiveStatus(binding.value) if (!isActive) return @@ -40,7 +38,7 @@ export const vBTooltip: Directive = { if (!text.content && !text.title) return delete binding.oldValue - if (el.$__binding === JSON.stringify(binding)) return + if (el.$__binding === JSON.stringify([binding.modifiers, binding.value])) return unbind(el) bind(el, binding, { noninteractive: true, @@ -49,7 +47,7 @@ export const vBTooltip: Directive = { title: text.title ?? text.content ?? '', tooltip: isActive, }) - el.$__binding = JSON.stringify(binding) + el.$__binding = JSON.stringify([binding.modifiers, binding.value]) }, beforeUnmount(el) { unbind(el) diff --git a/packages/bootstrap-vue-next/src/types/ColorTypes.ts b/packages/bootstrap-vue-next/src/types/ColorTypes.ts index f87d39a22..ea5aba748 100644 --- a/packages/bootstrap-vue-next/src/types/ColorTypes.ts +++ b/packages/bootstrap-vue-next/src/types/ColorTypes.ts @@ -8,10 +8,28 @@ export interface BaseColorVariant { light: unknown dark: unknown } - export type ColorVariant = keyof BaseColorVariant -export interface BaseTextColorVariant extends BaseColorVariant { +export type ColorVariantSubtle = `${ColorVariant}-subtle` +export type BaseColorVariantSubtle = { + [K in ColorVariantSubtle]: unknown +} +export type ColorVariantEmphasis = `${ColorVariant}-emphasis` +export type BaseColorVariantEmphasis = { + [K in ColorVariantEmphasis]: unknown +} + +export type ColorVariantOutline = `outline-${ColorVariant}` +export type BaseColorVariantOutline = { + [K in ColorVariantOutline]: unknown +} + +export interface BaseButtonVariant extends BaseColorVariant, BaseColorVariantOutline { + link: unknown +} +export type ButtonVariant = keyof BaseButtonVariant + +export interface BaseTextColorVariant extends BaseColorVariant, BaseColorVariantEmphasis { 'white': unknown 'body': unknown 'body-secondary': unknown @@ -19,25 +37,16 @@ export interface BaseTextColorVariant extends BaseColorVariant { 'white-50': unknown 'reset': unknown } - export type TextColorVariant = keyof BaseTextColorVariant +export interface BaseBgColorVariant extends BaseColorVariant, BaseColorVariantSubtle {} +export type BgColorVariant = keyof BaseBgColorVariant + +export interface BaseBorderColorVariant extends BaseColorVariant, BaseColorVariantSubtle {} +export type BorderColorVariant = keyof BaseBorderColorVariant + export type ColorExtendables = { variant?: ColorVariant | null - bgVariant?: ColorVariant | null + bgVariant?: BgColorVariant | null textVariant?: TextColorVariant | null } - -export interface BaseButtonVariant extends BaseColorVariant { - 'link': unknown - 'outline-primary': unknown - 'outline-secondary': unknown - 'outline-success': unknown - 'outline-danger': unknown - 'outline-warning': unknown - 'outline-info': unknown - 'outline-light': unknown - 'outline-dark': unknown -} - -export type ButtonVariant = keyof BaseButtonVariant diff --git a/packages/bootstrap-vue-next/src/types/ComponentProps.ts b/packages/bootstrap-vue-next/src/types/ComponentProps.ts index 3473139b5..22b51d693 100644 --- a/packages/bootstrap-vue-next/src/types/ComponentProps.ts +++ b/packages/bootstrap-vue-next/src/types/ComponentProps.ts @@ -2,7 +2,14 @@ import type {Boundary, Middleware, Padding, RootBoundary, Strategy} from '@float import type {ComponentPublicInstance, TransitionProps} from 'vue' import type {RouteLocationRaw} from 'vue-router' import type {LinkTarget} from './LinkTarget' -import type {ButtonVariant, ColorExtendables, ColorVariant, TextColorVariant} from './ColorTypes' +import type { + BgColorVariant, + BorderColorVariant, + ButtonVariant, + ColorExtendables, + ColorVariant, + TextColorVariant, +} from './ColorTypes' import type {AttrsValue, ClassValue} from './AnyValuedAttributes' import type {CheckboxOptionRaw, CheckboxValue} from './CheckboxTypes' import type {Size} from './Size' @@ -464,7 +471,7 @@ export interface BNavbarProps { sticky?: Extract tag?: string toggleable?: boolean | Breakpoint - variant?: ColorVariant | null + variant?: BgColorVariant | null } export interface BNavbarBrandProps extends Omit { @@ -525,7 +532,7 @@ export interface BOverlayProps extends RadiusElementExtendables { spinnerSmall?: boolean spinnerType?: SpinnerType spinnerVariant?: ColorVariant | null - variant?: ColorVariant | 'white' | 'transparent' | null + variant?: BgColorVariant | 'white' | 'transparent' | null wrapTag?: string zIndex?: Numberish } @@ -568,7 +575,7 @@ export interface BPlaceholderProps { cols?: Numberish size?: PlaceholderSize tag?: string - variant?: ColorVariant | null + variant?: BgColorVariant | null width?: Numberish wrapperTag?: string } @@ -749,7 +756,7 @@ export interface BAvatarProps RadiusElementExtendables { alt?: string badge?: boolean | string - badgeBgVariant?: ColorVariant | null + badgeBgVariant?: BgColorVariant | null badgePlacement?: CombinedPlacement badgeTextVariant?: TextColorVariant | null badgeVariant?: ColorVariant | null @@ -825,23 +832,23 @@ export interface BCloseButtonProps { export interface BCardProps extends ColorExtendables { align?: AlignmentTextHorizontal - bodyBgVariant?: ColorVariant | null + bodyBgVariant?: BgColorVariant | null bodyClass?: ClassValue bodyTag?: string bodyText?: string bodyTextVariant?: TextColorVariant | null - borderVariant?: ColorVariant | null + borderVariant?: BorderColorVariant | null footer?: string - footerBgVariant?: ColorVariant | null - footerBorderVariant?: ColorVariant | null + footerBgVariant?: BgColorVariant | null + footerBorderVariant?: BorderColorVariant | null footerClass?: ClassValue footerHtml?: string footerTag?: string footerTextVariant?: TextColorVariant | null footerVariant?: ColorVariant | null header?: string - headerBgVariant?: ColorVariant | null - headerBorderVariant?: ColorVariant | null + headerBgVariant?: BgColorVariant | null + headerBorderVariant?: BorderColorVariant | null headerClass?: ClassValue headerHtml?: string headerTag?: string @@ -976,7 +983,7 @@ export interface BFormProps { export interface BTableSimpleProps { bordered?: boolean borderless?: boolean - borderVariant?: ColorVariant | null + borderVariant?: BorderColorVariant | null captionTop?: boolean dark?: boolean fixed?: boolean @@ -1242,7 +1249,7 @@ export interface BTooltipProps extends Omit { } export interface BCardHeadFootProps extends ColorExtendables { - borderVariant?: ColorVariant | null + borderVariant?: BorderColorVariant | null html?: string tag?: string text?: string @@ -1253,7 +1260,7 @@ export interface BModalProps extends TeleporterProps { autofocusButton?: 'ok' | 'cancel' | 'close' body?: string bodyAttrs?: Readonly - bodyBgVariant?: ColorVariant | null + bodyBgVariant?: BgColorVariant | null bodyClass?: ClassValue bodyScrolling?: boolean bodyTextVariant?: TextColorVariant | null @@ -1266,14 +1273,14 @@ export interface BModalProps extends TeleporterProps { centered?: boolean contentClass?: ClassValue dialogClass?: ClassValue - footerBgVariant?: ColorVariant | null - footerBorderVariant?: ColorVariant | null + footerBgVariant?: BgColorVariant | null + footerBorderVariant?: BorderColorVariant | null footerClass?: ClassValue footerTextVariant?: TextColorVariant | null footerVariant?: ColorVariant | null fullscreen?: boolean | Breakpoint - headerBgVariant?: ColorVariant | null - headerBorderVariant?: ColorVariant | null + headerBgVariant?: BgColorVariant | null + headerBorderVariant?: BorderColorVariant | null headerClass?: ClassValue headerCloseClass?: ClassValue headerCloseLabel?: string diff --git a/packages/bootstrap-vue-next/src/types/TableTypes.ts b/packages/bootstrap-vue-next/src/types/TableTypes.ts index 913de1c52..62de7729f 100644 --- a/packages/bootstrap-vue-next/src/types/TableTypes.ts +++ b/packages/bootstrap-vue-next/src/types/TableTypes.ts @@ -4,6 +4,8 @@ import type {MaybePromise} from './MaybePromise' import type {LiteralUnion} from './LiteralUnion' import type {AttrsValue, ClassValue} from './AnyValuedAttributes' +export type TableRowEvent = [item: T, index: number, event: MouseEvent] + export type TableItem> = T & { _rowVariant?: ColorVariant | null _cellVariants?: Partial> diff --git a/packages/bootstrap-vue-next/src/utils/bvnPrefix.ts b/packages/bootstrap-vue-next/src/utils/bvnPrefix.ts new file mode 100644 index 000000000..4adb71432 --- /dev/null +++ b/packages/bootstrap-vue-next/src/utils/bvnPrefix.ts @@ -0,0 +1,4 @@ +export const bvnPrefix = (value: string, suffix: string = '') => { + const suffixWithTrail = `${suffix}___` + return `___BVN__ID__${value}__${suffix ? suffixWithTrail : ''}` +} diff --git a/packages/bootstrap-vue-next/src/utils/keys.ts b/packages/bootstrap-vue-next/src/utils/keys.ts index 65a6f0949..dac788d7e 100644 --- a/packages/bootstrap-vue-next/src/utils/keys.ts +++ b/packages/bootstrap-vue-next/src/utils/keys.ts @@ -5,7 +5,12 @@ import type {Numberish} from '../types/CommonTypes' import type {LiteralUnion} from '../types/LiteralUnion' import type {Size} from '../types/Size' import type {RadiusElement} from '../types/RadiusElement' -import type {ButtonVariant, ColorVariant, TextColorVariant} from '../types/ColorTypes' +import type { + BgColorVariant, + ButtonVariant, + ColorVariant, + TextColorVariant, +} from '../types/ColorTypes' import type {CheckboxValue} from '../types/CheckboxTypes' import type {RadioValue} from '../types/RadioTypes' import type {BreadcrumbItemRaw} from '../types/BreadcrumbTypes' @@ -22,13 +27,16 @@ import type { TooltipOrchestratorShowParam, } from '../types/ComponentOrchestratorTypes' import type {BvnComponentProps} from '../types/BootstrapVueOptions' +import {bvnPrefix} from './bvnPrefix' + +const createBvnInjectionKey = (name: string) => bvnPrefix(name) as unknown as symbol // Type cast to symbol, these should be static // BCarousel export const carouselInjectionKey: InjectionKey<{ background: Readonly> width: Readonly> height: Readonly> -}> = Symbol('bvn::carousel') +}> = createBvnInjectionKey('carousel') // BTabs export const tabsInjectionKey: InjectionKey<{ @@ -42,7 +50,7 @@ export const tabsInjectionKey: InjectionKey<{ inactiveTabClass: Readonly> tabClass: Readonly> activeId: Readonly> -}> = Symbol('bvn::tabs') +}> = createBvnInjectionKey('tabs') // BProgress export const progressInjectionKey: InjectionKey<{ @@ -51,12 +59,12 @@ export const progressInjectionKey: InjectionKey<{ showProgress: Readonly> showValue: Readonly> striped: Readonly> -}> = Symbol('bvn::progress') +}> = createBvnInjectionKey('progress') // BListGroup export const listGroupInjectionKey: InjectionKey<{ numbered: Readonly> -}> = Symbol('bvn::listGroup') +}> = createBvnInjectionKey('listGroup') // BAvatarGroup export const avatarGroupInjectionKey: InjectionKey<{ @@ -69,16 +77,16 @@ export const avatarGroupInjectionKey: InjectionKey<{ roundedStart: Readonly> roundedEnd: Readonly> variant: Readonly> - bgVariant: Readonly> + bgVariant: Readonly> textVariant: Readonly> -}> = Symbol('bvn::avatarGroup') +}> = createBvnInjectionKey('avatarGroup') // BAccordion export const accordionInjectionKey: InjectionKey<{ openItem: Readonly> free: Readonly> setOpenItem: (id: string) => void -}> = Symbol('bvn::accordion') +}> = createBvnInjectionKey('accordion') // BFormCheckboxGroup export const checkboxGroupKey: InjectionKey<{ @@ -95,7 +103,7 @@ export const checkboxGroupKey: InjectionKey<{ required: Readonly> buttons: Readonly> disabled: Readonly> -}> = Symbol('bvn::checkboxGroup') +}> = createBvnInjectionKey('checkboxGroup') export const radioGroupKey: InjectionKey<{ modelValue: Ref @@ -110,7 +118,7 @@ export const radioGroupKey: InjectionKey<{ reverse: Readonly> required: Readonly> disabled: Readonly> -}> = Symbol('bvn::radioGroup') +}> = createBvnInjectionKey('radioGroup') // Collapse export const collapseInjectionKey: InjectionKey<{ @@ -120,7 +128,7 @@ export const collapseInjectionKey: InjectionKey<{ readonly toggle?: () => void visible?: Readonly> isNav?: Readonly> -}> = Symbol('bvn::collapse') +}> = createBvnInjectionKey('collapse') export const dropdownInjectionKey: InjectionKey<{ id?: Readonly> @@ -129,22 +137,22 @@ export const dropdownInjectionKey: InjectionKey<{ readonly toggle?: () => void visible?: Readonly> isNav?: Readonly> -}> = Symbol('bvn::collapse') +}> = createBvnInjectionKey('dropdown') export const navbarInjectionKey: InjectionKey<{ tag?: Readonly> autoClose?: Readonly> -}> = Symbol('bvn::navbar') +}> = createBvnInjectionKey('navbar') export const rtlPluginKey: InjectionKey<{ isRtl: Ref locale: Ref -}> = Symbol('bvn::rtlPlugin') +}> = createBvnInjectionKey('rtlPlugin') export const breadcrumbPluginKey: InjectionKey<{ items: Ref reset: () => void -}> = Symbol('bvn::breadcrumbPlugin') +}> = createBvnInjectionKey('breadcrumbPlugin') export const modalManagerPluginKey: InjectionKey<{ stack: ComputedRef @@ -155,11 +163,12 @@ export const modalManagerPluginKey: InjectionKey<{ registry: Readonly>> pushRegistry: (modal: Readonly) => void removeRegistry: (modal: Readonly) => void -}> = Symbol('bvn::modalManagerPlugin') +}> = createBvnInjectionKey('modalManagerPlugin') -export const defaultsKey: InjectionKey>> = Symbol('bvn::defaults') +export const defaultsKey: InjectionKey>> = + createBvnInjectionKey('defaults') -export const inputGroupKey: InjectionKey = Symbol('bvn::inputGroup') +export const inputGroupKey: InjectionKey = createBvnInjectionKey('inputGroup') export const toastPluginKey: InjectionKey<{ toasts: Ref @@ -167,7 +176,7 @@ export const toastPluginKey: InjectionKey<{ show: (obj: ToastOrchestratorShowParam) => symbol remove: (self: symbol) => void leave: (self: symbol) => void -}> = Symbol('bvn::toastPlugin') +}> = createBvnInjectionKey('toastPlugin') export const modalControllerPluginKey: InjectionKey<{ modals: Ref> @@ -175,18 +184,18 @@ export const modalControllerPluginKey: InjectionKey<{ confirm: (obj: ModalOrchestratorShowParam) => Promise remove: (self: symbol) => void leave: (self: symbol) => void -}> = Symbol('bvn::modalControllerPlugin') +}> = createBvnInjectionKey('modalControllerPlugin') export const tooltipPluginKey: InjectionKey<{ tooltips: Ref> show: (obj: TooltipOrchestratorShowParam) => symbol remove: (self: symbol) => void set: (self: symbol, val: Partial) => void -}> = Symbol('bvn::toastPlugin') +}> = createBvnInjectionKey('tooltipPlugin') export const popoverPluginKey: InjectionKey<{ popovers: Ref> show: (obj: PopoverOrchestratorShowParam) => symbol remove: (self: symbol) => void set: (self: symbol, val: Partial) => void -}> = Symbol('bvn::toastPlugin') +}> = createBvnInjectionKey('popoverPlugin') diff --git a/packages/bootstrap-vue-next/tests/composables/useColorVariantClasses.spec.ts b/packages/bootstrap-vue-next/tests/composables/useColorVariantClasses.spec.ts index 992838bf5..7a78ee025 100644 --- a/packages/bootstrap-vue-next/tests/composables/useColorVariantClasses.spec.ts +++ b/packages/bootstrap-vue-next/tests/composables/useColorVariantClasses.spec.ts @@ -23,6 +23,22 @@ describe('useColorVariantClasses blackbox test', () => { 'text-bg-danger': true, 'text-null': false, 'bg-null': false, + 'border-null': false, + }) + }) + + it('value returns text-bg-{type} when prop variant', () => { + const backgroundVariant = useColorVariantClasses(() => ({ + bgVariant: null, + textVariant: null, + variant: null, + borderVariant: 'danger-subtle', + })) + expect(backgroundVariant.value).toEqual({ + 'text-bg-null': false, + 'text-null': false, + 'bg-null': false, + 'border-danger-subtle': true, }) }) @@ -36,6 +52,7 @@ describe('useColorVariantClasses blackbox test', () => { 'text-bg-null': false, 'text-null': false, 'bg-danger': true, + 'border-null': false, }) }) @@ -48,6 +65,7 @@ describe('useColorVariantClasses blackbox test', () => { expect(backgroundVariant.value).toEqual({ 'text-bg-null': false, 'text-danger': true, + 'border-null': false, 'bg-null': false, }) }) @@ -62,6 +80,7 @@ describe('useColorVariantClasses blackbox test', () => { 'text-bg-null': false, 'text-danger': true, 'bg-danger': true, + 'border-null': false, }) }) @@ -74,6 +93,7 @@ describe('useColorVariantClasses blackbox test', () => { expect(backgroundVariant.value).toEqual({ 'text-bg-success': true, 'text-info': true, + 'border-null': false, 'bg-danger': true, }) }) @@ -89,6 +109,7 @@ describe('useColorVariantClasses blackbox test', () => { 'text-bg-null': false, 'text-danger': true, 'bg-danger': true, + 'border-null': false, }) react.bgVariant = 'info' react.textVariant = 'info' @@ -97,6 +118,7 @@ describe('useColorVariantClasses blackbox test', () => { 'text-bg-danger': true, 'text-info': true, 'bg-info': true, + 'border-null': false, }) }) }) diff --git a/packages/nuxt/CHANGELOG.md b/packages/nuxt/CHANGELOG.md index c1ce12c3d..64b3523c6 100644 --- a/packages/nuxt/CHANGELOG.md +++ b/packages/nuxt/CHANGELOG.md @@ -1,5 +1,101 @@ # Changelog +## [0.24.23](https://github.com/bootstrap-vue-next/bootstrap-vue-next/compare/nuxt-v0.24.22...nuxt-v0.24.23) (2024-09-26) + + +### Miscellaneous Chores + +* **nuxt:** Synchronize main group versions + + +### Dependencies + +* The following workspace dependencies were updated + * devDependencies + * bootstrap-vue-next bumped to 0.24.23 + * peerDependencies + * bootstrap-vue-next bumped to 0.24.23 + +## [0.24.22](https://github.com/bootstrap-vue-next/bootstrap-vue-next/compare/nuxt-v0.24.21...nuxt-v0.24.22) (2024-09-24) + + +### Miscellaneous Chores + +* **nuxt:** Synchronize main group versions + + +### Dependencies + +* The following workspace dependencies were updated + * devDependencies + * bootstrap-vue-next bumped to 0.24.22 + * peerDependencies + * bootstrap-vue-next bumped to 0.24.22 + +## [0.24.21](https://github.com/bootstrap-vue-next/bootstrap-vue-next/compare/nuxt-v0.24.20...nuxt-v0.24.21) (2024-09-22) + + +### Miscellaneous Chores + +* **nuxt:** Synchronize main group versions + + +### Dependencies + +* The following workspace dependencies were updated + * devDependencies + * bootstrap-vue-next bumped to 0.24.21 + * peerDependencies + * bootstrap-vue-next bumped to 0.24.21 + +## [0.24.20](https://github.com/bootstrap-vue-next/bootstrap-vue-next/compare/nuxt-v0.24.19...nuxt-v0.24.20) (2024-09-21) + + +### Miscellaneous Chores + +* **nuxt:** Synchronize main group versions + + +### Dependencies + +* The following workspace dependencies were updated + * devDependencies + * bootstrap-vue-next bumped to 0.24.20 + * peerDependencies + * bootstrap-vue-next bumped to 0.24.20 + +## [0.24.19](https://github.com/bootstrap-vue-next/bootstrap-vue-next/compare/nuxt-v0.24.18...nuxt-v0.24.19) (2024-09-21) + + +### Miscellaneous Chores + +* **nuxt:** Synchronize main group versions + + +### Dependencies + +* The following workspace dependencies were updated + * devDependencies + * bootstrap-vue-next bumped to 0.24.19 + * peerDependencies + * bootstrap-vue-next bumped to 0.24.19 + +## [0.24.18](https://github.com/bootstrap-vue-next/bootstrap-vue-next/compare/nuxt-v0.24.17...nuxt-v0.24.18) (2024-09-17) + + +### Miscellaneous Chores + +* **nuxt:** Synchronize main group versions + + +### Dependencies + +* The following workspace dependencies were updated + * devDependencies + * bootstrap-vue-next bumped to 0.24.18 + * peerDependencies + * bootstrap-vue-next bumped to 0.24.18 + ## [0.24.17](https://github.com/bootstrap-vue-next/bootstrap-vue-next/compare/nuxt-v0.24.16...nuxt-v0.24.17) (2024-09-16) diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index 833bad597..3baee13ed 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -1,7 +1,7 @@ { "name": "@bootstrap-vue-next/nuxt", "description": "Nuxt Module for BootstrapVueNext", - "version": "0.24.17", + "version": "0.24.23", "license": "MIT", "author": { "name": "Issayah", diff --git a/templates/vite/src/main.ts b/templates/vite/src/main.ts index 3f6353583..9f01101ee 100644 --- a/templates/vite/src/main.ts +++ b/templates/vite/src/main.ts @@ -13,6 +13,6 @@ for (const name in Components) { app.component(name, Components[name]) } for (const name in Directives) { - app.directive(name, Directives[name]) + app.directive(name.replace(/^v/, ''), Directives[name]) } app.mount('#app')