|
1 |
| -<script lang="ts"> |
2 |
| -import type {Booleanish, ColorVariant} from '../../types' |
3 |
| -import {normalizeSlot, toFloat} from '../../utils' |
| 1 | +<template> |
| 2 | + <component :is="wrapTag" class="b-overlay-wrap position-relative" :aria-busy="computedAriaBusy"> |
| 3 | + <slot /> |
| 4 | + <b-transition |
| 5 | + :no-fade="noFadeBoolean" |
| 6 | + :trans-props="{enterToClass: 'show'}" |
| 7 | + name="fade" |
| 8 | + @on-after-enter="emit('shown')" |
| 9 | + @on-after-leave="emit('hidden')" |
| 10 | + > |
| 11 | + <component |
| 12 | + :is="overlayTag" |
| 13 | + v-if="showBoolean" |
| 14 | + :class="overlayClasses" |
| 15 | + :style="overlayStyles" |
| 16 | + @click="emit('click', $event)" |
| 17 | + > |
| 18 | + <div class="position-absolute" :class="blurClasses" :style="blurStyles" /> |
| 19 | + |
| 20 | + <div class="position-absolute" :style="spinWrapperStyles"> |
| 21 | + <slot name="overlay" v-bind="spinnerAttrs"> |
| 22 | + <b-spinner v-bind="spinnerAttrs" /> |
| 23 | + </slot> |
| 24 | + </div> |
| 25 | + </component> |
| 26 | + </b-transition> |
| 27 | + </component> |
| 28 | +</template> |
| 29 | + |
| 30 | +<script setup lang="ts"> |
| 31 | +import type {Booleanish, ColorVariant, SpinnerType} from '../../types' |
4 | 32 | import {useBooleanish} from '../../composables'
|
5 |
| -import {computed, defineComponent, h, PropType, resolveComponent, toRef} from 'vue' |
| 33 | +import {computed, CSSProperties, toRef} from 'vue' |
6 | 34 | import BTransition from '../BTransition/BTransition.vue'
|
| 35 | +import BSpinner from '../BSpinner.vue' |
7 | 36 |
|
8 |
| -const POSITION_COVER = {top: 0, left: 0, bottom: 0, right: 0} |
9 |
| -const SLOT_NAME_DEFAULT = 'default' |
10 |
| -const SLOT_NAME_OVERLAY = 'overlay' |
| 37 | +const POSITION_COVER: CSSProperties = {top: 0, left: 0, bottom: 0, right: 0} |
11 | 38 |
|
12 |
| -interface ISlotScope { |
13 |
| - spinnerType: string | null |
14 |
| - spinnerVariant: string | null |
15 |
| - spinnerSmall: boolean |
| 39 | +interface Props { |
| 40 | + bgColor?: string |
| 41 | + blur?: string |
| 42 | + fixed?: Booleanish |
| 43 | + noCenter?: Booleanish |
| 44 | + noFade?: Booleanish |
| 45 | + noWrap?: Booleanish |
| 46 | + opacity?: number | string |
| 47 | + overlayTag?: string |
| 48 | + rounded?: boolean | string |
| 49 | + show?: Booleanish |
| 50 | + spinnerSmall?: Booleanish |
| 51 | + spinnerType?: SpinnerType |
| 52 | + spinnerVariant?: ColorVariant |
| 53 | + variant?: |
| 54 | + | 'transparent' |
| 55 | + | 'white' |
| 56 | + | 'light' |
| 57 | + | 'dark' |
| 58 | + | 'primary' |
| 59 | + | 'secondary' |
| 60 | + | 'success' |
| 61 | + | 'danger' |
| 62 | + | 'warning' |
| 63 | + | 'info' // ColorVariant | 'white' | 'transparent' |
| 64 | + wrapTag?: string |
| 65 | + zIndex?: number | string |
16 | 66 | }
|
17 | 67 |
|
18 |
| -export default defineComponent({ |
19 |
| - components: {BTransition}, |
20 |
| - props: { |
21 |
| - bgColor: {type: String, required: false}, |
22 |
| - blur: {type: String, default: '2px'}, |
23 |
| - fixed: {type: [Boolean, String] as PropType<Booleanish>, default: false}, |
24 |
| - noCenter: {type: [Boolean, String] as PropType<Booleanish>, default: false}, |
25 |
| - noFade: {type: [Boolean, String] as PropType<Booleanish>, default: false}, |
26 |
| - // If `true, does not render the default slot |
27 |
| - // and switches to absolute positioning |
28 |
| - noWrap: {type: [Boolean, String] as PropType<Booleanish>, default: false}, |
29 |
| - opacity: { |
30 |
| - type: [Number, String], |
31 |
| - default: 0.85, |
32 |
| - validator: (value: number | string) => { |
33 |
| - const number = toFloat(value, 0) |
34 |
| - return number >= 0 && number <= 1 |
35 |
| - }, |
36 |
| - }, |
37 |
| - overlayTag: {type: String, default: 'div'}, |
38 |
| - rounded: {type: [Boolean, String], default: false}, |
39 |
| - show: {type: [Boolean, String] as PropType<Booleanish>, default: false}, |
40 |
| - spinnerSmall: {type: [Boolean, String] as PropType<Booleanish>, default: false}, |
41 |
| - spinnerType: {type: String, default: 'border'}, |
42 |
| - spinnerVariant: {type: String, required: false}, |
43 |
| - variant: {type: String as PropType<ColorVariant>, default: 'light'}, |
44 |
| - wrapTag: {type: String, default: 'div'}, |
45 |
| - zIndex: {type: [Number, String], default: 10}, |
46 |
| - }, |
47 |
| - emits: ['click', 'hidden', 'shown'], |
48 |
| - setup(props, {slots, emit}) { |
49 |
| - const fixedBoolean = useBooleanish(toRef(props, 'fixed')) |
50 |
| - const noCenterBoolean = useBooleanish(toRef(props, 'noCenter')) |
51 |
| - const noFadeBoolean = useBooleanish(toRef(props, 'noFade')) |
52 |
| - const noWrapBoolean = useBooleanish(toRef(props, 'noWrap')) |
53 |
| - const showBoolean = useBooleanish(toRef(props, 'show')) |
54 |
| - const spinnerSmallBoolean = useBooleanish(toRef(props, 'spinnerSmall')) |
55 |
| -
|
56 |
| - const computedRounded = computed(() => |
57 |
| - props.rounded === true || props.rounded === '' |
58 |
| - ? 'rounded' |
59 |
| - : !props.rounded |
60 |
| - ? '' |
61 |
| - : `rounded-${props.rounded}` |
62 |
| - ) |
63 |
| -
|
64 |
| - const computedVariant = computed(() => |
65 |
| - props.variant && !props.bgColor ? `bg-${props.variant}` : '' |
66 |
| - ) |
67 |
| -
|
68 |
| - const computedSlotScope = computed(() => ({ |
69 |
| - spinnerType: props.spinnerType || null, |
70 |
| - spinnerVariant: props.spinnerVariant || null, |
71 |
| - spinnerSmall: spinnerSmallBoolean.value, |
72 |
| - })) |
73 |
| -
|
74 |
| - return () => { |
75 |
| - const defaultOverlayFn = (scope: ISlotScope) => |
76 |
| - h(resolveComponent('BSpinner'), { |
77 |
| - type: scope.spinnerType, |
78 |
| - variant: scope.spinnerVariant, |
79 |
| - small: spinnerSmallBoolean.value, |
80 |
| - }) |
81 |
| -
|
82 |
| - let $overlay: any = '' |
83 |
| -
|
84 |
| - if (showBoolean.value) { |
85 |
| - const $background = h('div', { |
86 |
| - class: ['position-absolute', computedVariant.value, computedRounded.value], |
87 |
| - style: { |
88 |
| - ...POSITION_COVER, |
89 |
| - opacity: props.opacity, |
90 |
| - backgroundColor: props.bgColor || null, |
91 |
| - backdropFilter: props.blur ? `blur(${props.blur})` : null, |
92 |
| - }, |
93 |
| - }) |
94 |
| -
|
95 |
| - const $content = h( |
96 |
| - 'div', |
97 |
| - { |
98 |
| - class: 'position-absolute', |
99 |
| - style: noCenterBoolean.value |
100 |
| - ? {...POSITION_COVER} |
101 |
| - : {top: '50%', left: '50%', transform: 'translateX(-50%) translateY(-50%)'}, |
102 |
| - }, |
103 |
| - normalizeSlot(SLOT_NAME_OVERLAY, computedSlotScope.value, slots) || |
104 |
| - defaultOverlayFn(computedSlotScope.value) || |
105 |
| - '' |
106 |
| - ) |
107 |
| -
|
108 |
| - $overlay = h( |
109 |
| - props.overlayTag, |
110 |
| - { |
111 |
| - class: [ |
112 |
| - 'b-overlay', |
113 |
| - { |
114 |
| - 'position-absolute': |
115 |
| - !noWrapBoolean.value || (noWrapBoolean.value && !fixedBoolean.value), |
116 |
| - 'position-fixed': noWrapBoolean.value && fixedBoolean.value, |
117 |
| - }, |
118 |
| - ], |
119 |
| - style: { |
120 |
| - ...POSITION_COVER, |
121 |
| - zIndex: props.zIndex || 10, |
122 |
| - }, |
123 |
| - onClick: (event: MouseEvent) => emit('click', event), |
124 |
| - key: 'overlay', |
125 |
| - }, |
126 |
| - [$background, $content] |
127 |
| - ) |
128 |
| - } |
| 68 | +const props = withDefaults(defineProps<Props>(), { |
| 69 | + blur: '2px', |
| 70 | + fixed: false, |
| 71 | + noCenter: false, |
| 72 | + noFade: false, |
| 73 | + noWrap: false, |
| 74 | + opacity: 0.85, |
| 75 | + overlayTag: 'div', |
| 76 | + rounded: false, |
| 77 | + show: false, |
| 78 | + spinnerSmall: false, |
| 79 | + spinnerType: 'border', |
| 80 | + variant: 'light', |
| 81 | + wrapTag: 'div', |
| 82 | + zIndex: 10, |
| 83 | +}) |
| 84 | +
|
| 85 | +interface Emits { |
| 86 | + (e: 'click', value: MouseEvent): void |
| 87 | + (e: 'hidden'): void |
| 88 | + (e: 'shown'): void |
| 89 | +} |
| 90 | +
|
| 91 | +const emit = defineEmits<Emits>() |
| 92 | +
|
| 93 | +const fixedBoolean = useBooleanish(toRef(props, 'fixed')) |
| 94 | +const noCenterBoolean = useBooleanish(toRef(props, 'noCenter')) |
| 95 | +const noFadeBoolean = useBooleanish(toRef(props, 'noFade')) |
| 96 | +const noWrapBoolean = useBooleanish(toRef(props, 'noWrap')) |
| 97 | +const showBoolean = useBooleanish(toRef(props, 'show')) |
| 98 | +const spinnerSmallBoolean = useBooleanish(toRef(props, 'spinnerSmall')) |
| 99 | +
|
| 100 | +const computedRounded = computed(() => |
| 101 | + props.rounded === true || props.rounded === '' |
| 102 | + ? 'rounded' |
| 103 | + : props.rounded === false |
| 104 | + ? '' |
| 105 | + : `rounded-${props.rounded}` |
| 106 | +) |
129 | 107 |
|
130 |
| - const getOverlayTransition = () => |
131 |
| - h( |
132 |
| - BTransition, |
133 |
| - { |
134 |
| - noFade: noFadeBoolean.value, |
135 |
| - transProps: {enterToClass: 'show'}, |
136 |
| - name: 'fade', |
137 |
| - onAfterEnter: () => emit('shown'), |
138 |
| - onAfterLeave: () => emit('hidden'), |
139 |
| - }, |
140 |
| - {default: () => $overlay} |
141 |
| - ) |
142 |
| -
|
143 |
| - if (noWrapBoolean.value) return getOverlayTransition() |
144 |
| -
|
145 |
| - const wrapper = h( |
146 |
| - props.wrapTag, |
147 |
| - { |
148 |
| - 'class': ['b-overlay-wrap position-relative'], |
149 |
| - 'aria-busy': showBoolean.value ? 'true' : null, |
150 |
| - }, |
151 |
| - [h('span', normalizeSlot(SLOT_NAME_DEFAULT, {}, slots)), getOverlayTransition()] |
152 |
| - ) |
153 |
| - return wrapper |
154 |
| - } |
| 108 | +const computedVariant = computed(() => |
| 109 | + props.variant && !props.bgColor ? `bg-${props.variant}` : '' |
| 110 | +) |
| 111 | +
|
| 112 | +const computedAriaBusy = computed(() => (showBoolean.value ? 'true' : null)) |
| 113 | +
|
| 114 | +const spinnerAttrs = computed(() => ({ |
| 115 | + type: props.spinnerType || undefined, |
| 116 | + variant: props.spinnerVariant || undefined, |
| 117 | + small: spinnerSmallBoolean.value, |
| 118 | +})) |
| 119 | +
|
| 120 | +const overlayStyles = computed(() => ({ |
| 121 | + ...POSITION_COVER, |
| 122 | + zIndex: props.zIndex || 10, |
| 123 | +})) |
| 124 | +
|
| 125 | +const overlayClasses = computed(() => [ |
| 126 | + 'b-overlay', |
| 127 | + { |
| 128 | + 'position-absolute': !noWrapBoolean.value || !fixedBoolean.value, |
| 129 | + 'position-fixed': noWrapBoolean.value && fixedBoolean.value, |
155 | 130 | },
|
156 |
| -}) |
| 131 | +]) |
| 132 | +
|
| 133 | +const blurClasses = computed(() => [computedVariant.value, computedRounded.value]) |
| 134 | +
|
| 135 | +const blurStyles = computed(() => ({ |
| 136 | + ...POSITION_COVER, |
| 137 | + opacity: props.opacity, |
| 138 | + backgroundColor: props.bgColor || undefined, |
| 139 | + backdropFilter: blur ? `blur(${blur})` : undefined, |
| 140 | +})) |
| 141 | +
|
| 142 | +const spinWrapperStyles = computed(() => |
| 143 | + noCenterBoolean.value |
| 144 | + ? {...POSITION_COVER} |
| 145 | + : { |
| 146 | + top: '50%', |
| 147 | + left: '50%', |
| 148 | + transform: 'translateX(-50%) translateY(-50%)', |
| 149 | + } |
| 150 | +) |
157 | 151 | </script>
|
0 commit comments