Skip to content

Commit 5231b95

Browse files
authored
feat(VAlert): better aligment with prepend icon (#20700)
1 parent 3db9eb9 commit 5231b95

File tree

6 files changed

+70
-37
lines changed

6 files changed

+70
-37
lines changed

packages/api-generator/src/locale/en/VIconBtn.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
"baseVariant": "When active is a boolean, this variant is used when active is false.",
77
"hideOverlay": "Hides overlay from being displayed when active or focused.",
88
"iconColor": "Explicit color applied to the icon.",
9-
"iconSize": "The specific size of the icon, can use named sizes.",
10-
"iconSizes": "An array of tuples that define the icon sizes for each named size.",
119
"loading": "Displays circular progress bar in place of the icon.",
1210
"readonly": "Puts the button in a readonly state. Cannot be clicked or navigated to by keyboard.",
1311
"rotate": "The rotation of the icon in degrees.",

packages/api-generator/src/locale/en/generic.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
"hideOnLeave": "Hides the leaving element (no exit animation).",
2626
"group": "Creates a `transition-group` component. You can find more information in the [vue docs](https://vuejs.org/api/built-in-components.html#transitiongroup).",
2727
"icon": "Apply a specific icon using the [v-icon](/components/icons/) component.",
28+
"iconSize": "The specific size of the icon, can use named sizes.",
29+
"iconSizes": "An array of tuples that define the icon sizes for each named size.",
2830
"image": "Apply a specific image using [v-img](/components/images/).",
2931
"items": "An array of strings or objects used for automatically generating children components.",
3032
"label": "Sets the text of the [v-label](/api/v-label/) or [v-field-label](/api/v-field-label/) component.",

packages/vuetify/src/components/VAlert/VAlert.sass

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
@use '../../styles/settings'
12
@use '../../styles/tools'
23
@use './variables' as *
34

@@ -42,6 +43,10 @@
4243
&.v-alert--border-bottom
4344
padding-bottom: $alert-padding + $alert-border-thin-width + $modifier
4445

46+
&:not(:has(.v-alert-title))
47+
.v-alert__content
48+
padding-top: calc(($alert-title-line-height - settings.$line-height-root * 1rem) / 2)
49+
4550
.v-alert__border
4651
border-radius: inherit
4752
bottom: 0
@@ -70,6 +75,7 @@
7075
.v-alert__close
7176
flex: 0 1 auto
7277
grid-area: close
78+
margin-block: -2px
7379

7480
.v-alert__content
7581
align-self: center
@@ -78,7 +84,6 @@
7884

7985
.v-alert__append,
8086
.v-alert__close
81-
align-self: flex-start
8287
margin-inline-start: $alert-append-margin-inline-start
8388

8489
.v-alert__append
@@ -94,6 +99,7 @@
9499
align-items: center
95100
grid-area: prepend
96101
margin-inline-end: $alert-prepend-margin-inline-end
102+
min-height: $alert-title-line-height
97103

98104
.v-alert--prominent &
99105
align-self: center

packages/vuetify/src/components/VAlert/VAlert.tsx

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { makeDensityProps, useDensity } from '@/composables/density'
1414
import { makeDimensionProps, useDimension } from '@/composables/dimensions'
1515
import { makeElevationProps, useElevation } from '@/composables/elevation'
1616
import { IconValue } from '@/composables/icons'
17+
import { makeIconSizeProps, useIconSizes } from '@/composables/iconSizes'
1718
import { useLocale } from '@/composables/locale'
1819
import { makeLocationProps, useLocation } from '@/composables/location'
1920
import { makePositionProps, usePosition } from '@/composables/position'
@@ -76,6 +77,7 @@ export const makeVAlertProps = propsFactory({
7677
...makeDensityProps(),
7778
...makeDimensionProps(),
7879
...makeElevationProps(),
80+
...makeIconSizeProps(),
7981
...makeLocationProps(),
8082
...makePositionProps(),
8183
...makeRoundedProps(),
@@ -112,6 +114,7 @@ export const VAlert = genericComponent<VAlertSlots>()({
112114
return props.icon ?? `$${props.type}`
113115
})
114116

117+
const { iconSize } = useIconSizes(props, () => props.prominent ? 44 : 28)
115118
const { themeClasses } = provideTheme(props)
116119
const { colorClasses, colorStyles, variantClasses } = useVariant(() => ({
117120
color: props.color ?? props.type,
@@ -140,6 +143,12 @@ export const VAlert = genericComponent<VAlertSlots>()({
140143
const hasTitle = !!(slots.title || props.title)
141144
const hasClose = !!(slots.close || props.closable)
142145

146+
const iconProps = {
147+
density: props.density,
148+
icon: icon.value,
149+
size: iconSize.value,
150+
}
151+
143152
return isActive.value && (
144153
<props.tag
145154
class={[
@@ -184,23 +193,12 @@ export const VAlert = genericComponent<VAlertSlots>()({
184193
{ hasPrepend && (
185194
<div key="prepend" class="v-alert__prepend">
186195
{ !slots.prepend ? (
187-
<VIcon
188-
key="prepend-icon"
189-
density={ props.density }
190-
icon={ icon.value }
191-
size={ props.prominent ? 44 : 28 }
192-
/>
196+
<VIcon key="prepend-icon" { ...iconProps } />
193197
) : (
194198
<VDefaultsProvider
195199
key="prepend-defaults"
196200
disabled={ !icon.value }
197-
defaults={{
198-
VIcon: {
199-
density: props.density,
200-
icon: icon.value,
201-
size: props.prominent ? 44 : 28,
202-
},
203-
}}
201+
defaults={{ VIcon: { ...iconProps } }}
204202
v-slots:default={ slots.prepend }
205203
/>
206204
)}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Utilities
2+
import { computed } from 'vue'
3+
import { propsFactory } from '@/util'
4+
5+
// Types
6+
import type { ComputedGetter, PropType } from 'vue'
7+
import type { VIconBtnSizes } from '@/labs/VIconBtn/VIconBtn'
8+
9+
// Types
10+
export interface IconSizeProps {
11+
iconSize?: VIconBtnSizes | number | string
12+
iconSizes: [VIconBtnSizes, number][]
13+
}
14+
15+
// Composables
16+
export const makeIconSizeProps = propsFactory({
17+
iconSize: [Number, String] as PropType<VIconBtnSizes | number | string>,
18+
iconSizes: {
19+
type: Array as PropType<[VIconBtnSizes, number][]>,
20+
default: () => ([
21+
['x-small', 10],
22+
['small', 16],
23+
['default', 24],
24+
['large', 28],
25+
['x-large', 32],
26+
]),
27+
},
28+
}, 'iconSize')
29+
30+
export function useIconSizes (props: IconSizeProps, fallback: ComputedGetter<VIconBtnSizes | number | string | undefined>) {
31+
const iconSize = computed(() => {
32+
const iconSizeMap = new Map(props.iconSizes)
33+
const _iconSize = props.iconSize as VIconBtnSizes ?? fallback() ?? 'default'
34+
return iconSizeMap.has(_iconSize)
35+
? iconSizeMap.get(_iconSize)
36+
: _iconSize
37+
})
38+
39+
return { iconSize }
40+
}

packages/vuetify/src/labs/VIconBtn/VIconBtn.tsx

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { VProgressCircular } from '@/components/VProgressCircular'
1010
import { makeBorderProps, useBorder } from '@/composables/border'
1111
import { makeComponentProps } from '@/composables/component'
1212
import { makeElevationProps, useElevation } from '@/composables/elevation'
13+
import { makeIconSizeProps, useIconSizes } from '@/composables/iconSizes'
1314
import { useProxiedModel } from '@/composables/proxiedModel'
1415
import { makeRoundedProps, useRounded } from '@/composables/rounded'
1516
import { makeTagProps } from '@/composables/tag'
@@ -50,17 +51,6 @@ export const makeVIconBtnProps = propsFactory({
5051
hideOverlay: Boolean,
5152
icon: [String, Function, Object] as PropType<IconValue>,
5253
iconColor: String,
53-
iconSize: [Number, String] as PropType<VIconBtnSizes | number | string>,
54-
iconSizes: {
55-
type: Array as PropType<[VIconBtnSizes, number][]>,
56-
default: () => ([
57-
['x-small', 10],
58-
['small', 16],
59-
['default', 24],
60-
['large', 28],
61-
['x-large', 32],
62-
]),
63-
},
6454
loading: Boolean,
6555
opacity: [Number, String],
6656
readonly: Boolean,
@@ -87,6 +77,7 @@ export const makeVIconBtnProps = propsFactory({
8777
...makeBorderProps(),
8878
...makeComponentProps(),
8979
...makeElevationProps(),
80+
...makeIconSizeProps(),
9081
...makeRoundedProps(),
9182
...makeTagProps({ tag: 'button' }),
9283
...makeThemeProps(),
@@ -128,7 +119,6 @@ export const VIconBtn = genericComponent<VIconBtnSlots>()({
128119
}))
129120

130121
const btnSizeMap = new Map(props.sizes)
131-
const iconSizeMap = new Map(props.iconSizes)
132122

133123
function onClick () {
134124
if (
@@ -149,15 +139,14 @@ export const VIconBtn = genericComponent<VIconBtnSlots>()({
149139
const btnSize = hasNamedSize ? btnSizeMap.get(_btnSize) : _btnSize
150140
const btnHeight = props.height ?? btnSize
151141
const btnWidth = props.width ?? btnSize
142+
const { iconSize } = useIconSizes(props, () => new Map(props.iconSizes).get(_btnSize))
152143

153-
const _iconSize = props.iconSize as VIconBtnSizes
154-
const hasNamedIconSize = iconSizeMap.has(_iconSize)
155-
156-
const iconSize = !_iconSize
157-
? hasNamedSize ? iconSizeMap.get(_btnSize) : iconSizeMap.get('default')
158-
: hasNamedIconSize ? iconSizeMap.get(_iconSize) : _iconSize
159-
160-
const iconProps = { icon, size: iconSize, iconColor: props.iconColor, opacity: props.opacity }
144+
const iconProps = {
145+
icon,
146+
size: iconSize.value,
147+
iconColor: props.iconColor,
148+
opacity: props.opacity,
149+
}
161150

162151
return (
163152
<props.tag
@@ -217,7 +206,7 @@ export const VIconBtn = genericComponent<VIconBtnSlots>()({
217206
color={ typeof props.loading === 'boolean' ? undefined : props.loading }
218207
indeterminate="disable-shrink"
219208
width="2"
220-
size={ iconSize }
209+
size={ iconSize.value }
221210
/>
222211
)}
223212
</span>

0 commit comments

Comments
 (0)