diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 5d1eff6638c..34135a5a2d1 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -469,7 +469,7 @@ export namespace Components { */ "color"?: Color; /** - * Set to `"bold"` for a badge with vibrant, bold colors or to `"subtle"` for a badge with muted, subtle colors. Only applies to the `ionic` theme. + * Set to `"bold"` for a badge with vibrant, bold colors or to `"subtle"` for a badge with muted, subtle colors. Defaults to `"bold"` if both the hue property and theme config are unset. */ "hue"?: 'bold' | 'subtle'; /** @@ -477,11 +477,11 @@ export namespace Components { */ "mode"?: "ios" | "md"; /** - * Set to `"rectangular"` for non-rounded corners. Set to `"soft"` for slightly rounded corners. Set to `"round"` for fully rounded corners. Defaults to `"round"` for the `ionic` theme, undefined for all other themes. + * Set to `"crisp"` for a badge with even slightly rounded corners, `"soft"` for a badge with slightly rounded corners, `"round"` for a badge with fully rounded corners, or `"rectangular"` for a badge without rounded corners. Defaults to `"soft"` if both the shape property and theme config are unset. */ - "shape"?: 'soft' | 'round | rectangular'; + "shape"?: 'crisp' | 'soft' | 'round' | 'rectangular'; /** - * Set to `"small"` for a small badge. Set to `"medium"` for a medium badge. Set to `"large"` for a large badge, when it is empty (no text or icon). Defaults to `"small"` for the `ionic` theme, undefined for all other themes. + * Set to `"small"` for a smaller size. Set to `"medium"` for a medium size. Set to `"large"` for a larger size. Defaults to `"small"` if both the size property and theme config are unset. */ "size"?: 'small' | 'medium' | 'large'; /** @@ -898,7 +898,7 @@ export namespace Components { */ "shape"?: IonChipShape; /** - * Set to `"small"` for a chip with less height and padding. Defaults to `"large"` if both the size property and theme config are unset. + * Set to `"small"` for a chip with less height and padding, or `"large"` for a chip with more height and padding. Defaults to `"large"` if both the size property and theme config are unset. */ "size"?: IonChipSize; } @@ -6400,7 +6400,7 @@ declare namespace LocalJSX { */ "color"?: Color; /** - * Set to `"bold"` for a badge with vibrant, bold colors or to `"subtle"` for a badge with muted, subtle colors. Only applies to the `ionic` theme. + * Set to `"bold"` for a badge with vibrant, bold colors or to `"subtle"` for a badge with muted, subtle colors. Defaults to `"bold"` if both the hue property and theme config are unset. */ "hue"?: 'bold' | 'subtle'; /** @@ -6408,11 +6408,11 @@ declare namespace LocalJSX { */ "mode"?: "ios" | "md"; /** - * Set to `"rectangular"` for non-rounded corners. Set to `"soft"` for slightly rounded corners. Set to `"round"` for fully rounded corners. Defaults to `"round"` for the `ionic` theme, undefined for all other themes. + * Set to `"crisp"` for a badge with even slightly rounded corners, `"soft"` for a badge with slightly rounded corners, `"round"` for a badge with fully rounded corners, or `"rectangular"` for a badge without rounded corners. Defaults to `"soft"` if both the shape property and theme config are unset. */ - "shape"?: 'soft' | 'round | rectangular'; + "shape"?: 'crisp' | 'soft' | 'round' | 'rectangular'; /** - * Set to `"small"` for a small badge. Set to `"medium"` for a medium badge. Set to `"large"` for a large badge, when it is empty (no text or icon). Defaults to `"small"` for the `ionic` theme, undefined for all other themes. + * Set to `"small"` for a smaller size. Set to `"medium"` for a medium size. Set to `"large"` for a larger size. Defaults to `"small"` if both the size property and theme config are unset. */ "size"?: 'small' | 'medium' | 'large'; /** @@ -6864,7 +6864,7 @@ declare namespace LocalJSX { */ "shape"?: IonChipShape; /** - * Set to `"small"` for a chip with less height and padding. Defaults to `"large"` if both the size property and theme config are unset. + * Set to `"small"` for a chip with less height and padding, or `"large"` for a chip with more height and padding. Defaults to `"large"` if both the size property and theme config are unset. */ "size"?: IonChipSize; } diff --git a/core/src/components/avatar/avatar.badge.scss b/core/src/components/avatar/avatar.badge.scss new file mode 100644 index 00000000000..42b6cb70a37 --- /dev/null +++ b/core/src/components/avatar/avatar.badge.scss @@ -0,0 +1,139 @@ +@use "../../themes/mixins" as mixins; + +// Avatar Slotted Badge +// --------------------------------------------- + +/** + * Positions an empty badge (status dot) at the correct offset, translating the badge itself to account for its dimensions since it has no content to center. The badge is translated on a 45° angle from the avatar edge. + * 0.1464 = (1 - cos(45°)) / 2 + * + * offset-x = avatarWidth * 0.1464 - badgeWidth / 2 + * offset-y = avatarHeight * 0.1464 - badgeHeight / 2 + * + * @param $avatar-width The width of the avatar + * @param $avatar-height The height of the avatar + * @param $badge-width The width of the badge + * @param $badge-height The height of the badge + * @param $position The position of the badge (top or bottom) + */ +@mixin badge-empty-translate($avatar-width, $avatar-height, $badge-width, $badge-height, $position) { + @include mixins.position(null, 0, null, null); + + @if $position == top { + @include mixins.transform( + translate( + calc((#{$avatar-width} * 0.1464 - #{$badge-width} / 2) * -1), + calc(#{$avatar-height} * 0.1464 - #{$badge-height} / 2) + ) + ); + } @else { + @include mixins.transform( + translate( + calc((#{$avatar-width} * 0.1464 - #{$badge-width} / 2) * -1), + calc((#{$avatar-height} * 0.1464 - #{$badge-height} / 2) * -1) + ) + ); + } +} + +/** + * Positions a non-empty badge at the correct offset from the avatar edge, then translates 50% to center the badge's content. The badge is translated on a 45° angle from the avatar edge. +* 0.1464 = (1 - cos(45°)) / 2 + * + * anchor-x = avatarWidth * 0.1464 + * anchor-y = avatarHeight * 0.1464 + * + * @param $avatar-width The width of the avatar + * @param $avatar-height The height of the avatar + * @param $position The position of the badge (top or bottom) + */ +@mixin badge-default-translate($avatar-width, $avatar-height, $position) { + @if $position == top { + @include mixins.transform(translate(50%, -50%)); + + top: calc(#{$avatar-height} * 0.1464); + bottom: auto; + } @else { + @include mixins.transform(translate(50%, 50%)); + + top: auto; + bottom: calc(#{$avatar-height} * 0.1464); + } + + @include mixins.position(null, calc(#{$avatar-width} * 0.1464), null, null); +} + +// Maps +$avatar-sizes: ( + xxsmall: ( + width: var(--ion-avatar-size-xxsmall-width, 16px), + height: var(--ion-avatar-size-xxsmall-height, 16px), + ), + xsmall: ( + width: var(--ion-avatar-size-xsmall-width, 24px), + height: var(--ion-avatar-size-xsmall-height, 24px), + ), + small: ( + width: var(--ion-avatar-size-small-width, 32px), + height: var(--ion-avatar-size-small-height, 32px), + ), + medium: ( + width: var(--ion-avatar-size-medium-width, 40px), + height: var(--ion-avatar-size-medium-height, 40px), + ), + large: ( + width: var(--ion-avatar-size-large-width, 48px), + height: var(--ion-avatar-size-large-height, 48px), + ), + xlarge: ( + width: var(--ion-avatar-size-xlarge-width, 56px), + height: var(--ion-avatar-size-xlarge-height, 56px), + ), +); + +$badge-sizes: ( + small: ( + width: var(--ion-badge-size-small-empty-min-width), + height: var(--ion-badge-size-small-empty-height), + ), + medium: ( + width: var(--ion-badge-size-medium-empty-min-width), + height: var(--ion-badge-size-medium-empty-height), + ), + large: ( + width: var(--ion-badge-size-large-empty-min-width), + height: var(--ion-badge-size-large-empty-height), + ), +); + +// Avatar Sizes +@each $avatar-name, $avatar-tokens in $avatar-sizes { + $aw: map-get($avatar-tokens, width); + $ah: map-get($avatar-tokens, height); + + // Badge Sizes + @each $badge-name, $badge-tokens in $badge-sizes { + $bw: map-get($badge-tokens, width); + $bh: map-get($badge-tokens, height); + + // Top positioned empty badges + :host(.avatar-#{$avatar-name}) ::slotted(ion-badge.badge-#{$badge-name}.badge-vertical-top[vertical]:empty) { + @include badge-empty-translate($aw, $ah, $bw, $bh, top); + } + + // Bottom positioned empty badges + :host(.avatar-#{$avatar-name}) ::slotted(ion-badge.badge-#{$badge-name}.badge-vertical-bottom[vertical]:empty) { + @include badge-empty-translate($aw, $ah, $bw, $bh, bottom); + } + } + + // Top positioned badges with content + :host(.avatar-#{$avatar-name}) ::slotted(ion-badge.badge-vertical-top[vertical]:not(:empty)) { + @include badge-default-translate($aw, $ah, top); + } + + // Bottom positioned badges with content + :host(.avatar-#{$avatar-name}) ::slotted(ion-badge.badge-vertical-bottom[vertical]:not(:empty)) { + @include badge-default-translate($aw, $ah, bottom); + } +} diff --git a/core/src/components/avatar/avatar.common.scss b/core/src/components/avatar/avatar.common.scss index e24c047ad72..3f5f402c409 100644 --- a/core/src/components/avatar/avatar.common.scss +++ b/core/src/components/avatar/avatar.common.scss @@ -1,4 +1,5 @@ -@import "../../themes/mixins.scss"; +@use "../../themes/mixins" as mixins; +@use "./avatar.badge"; // Avatar // -------------------------------------------------- @@ -7,7 +8,7 @@ /** * @prop --border-radius: Border radius of the avatar and inner image */ - @include border-radius(var(--border-radius)); + @include mixins.border-radius(var(--border-radius)); display: block; @@ -16,7 +17,7 @@ ::slotted(ion-img), ::slotted(img) { - @include border-radius(var(--border-radius)); + @include mixins.border-radius(var(--border-radius)); width: 100%; height: 100%; diff --git a/core/src/components/avatar/avatar.ionic.scss b/core/src/components/avatar/avatar.ionic.scss index 11ffcdc881a..d31e6c9c5cb 100644 --- a/core/src/components/avatar/avatar.ionic.scss +++ b/core/src/components/avatar/avatar.ionic.scss @@ -169,79 +169,6 @@ height: globals.$ion-scale-800; } -// Avatar Badge Empty (hint) -// -------------------------------------------------- - -:host ::slotted(ion-badge.badge-vertical-top:empty) { - @include globals.transform(translate(globals.$ion-scale-050, calc(globals.$ion-scale-050 * -1))); -} - -:host(.avatar-xxsmall) ::slotted(ion-badge.badge-vertical-top:empty) { - @include globals.transform(translate(globals.$ion-scale-100, calc(globals.$ion-scale-100 * -1))); -} - -:host ::slotted(ion-badge.badge-vertical-bottom:empty) { - @include globals.transform(translate(0, globals.$ion-scale-100)); -} - -:host(.avatar-xxsmall) ::slotted(ion-badge.badge-vertical-bottom:empty) { - @include globals.transform(translate(globals.$ion-scale-100, globals.$ion-scale-100)); -} - -// Avatar Badge Bottom (hint) -// -------------------------------------------------- - -:host ::slotted(ion-badge.badge-vertical-bottom:not(:empty)) { - @include globals.transform(translate(50%, 50%)); -} - -:host(.avatar-xxsmall) ::slotted(ion-badge.badge-vertical-bottom:not(:empty)) { - @include globals.position(null, globals.$ion-scale-100, globals.$ion-scale-100, null); - @include globals.transform(translate(100%, 100%)); -} - -:host(.avatar-xsmall) ::slotted(ion-badge.badge-vertical-bottom:not(:empty)) { - @include globals.position(null, calc(globals.$ion-scale-050 * -1), calc(globals.$ion-scale-050 * -1), null); -} - -:host(.avatar-small) ::slotted(ion-badge.badge-vertical-bottom:not(:empty)), -:host(.avatar-medium) ::slotted(ion-badge.badge-vertical-bottom:not(:empty)), -:host(.avatar-large) ::slotted(ion-badge.badge-vertical-bottom:not(:empty)) { - @include globals.position(null, globals.$ion-scale-050, globals.$ion-scale-050, null); -} - -:host(.avatar-xlarge) ::slotted(ion-badge.badge-vertical-bottom:not(:empty)) { - @include globals.position(null, globals.$ion-scale-150, globals.$ion-scale-150, null); -} - -// Avatar Badge Top (hint) -// -------------------------------------------------- - -:host ::slotted(ion-badge.badge-vertical-top:not(:empty)) { - @include globals.transform(translate(50%, -50%)); -} - -:host(.avatar-xxsmall) ::slotted(ion-badge.badge-vertical-top:not(:empty)) { - @include globals.position(globals.$ion-scale-050, 0, null, null); -} - -:host(.avatar-xsmall) ::slotted(ion-badge.badge-vertical-top:not(:empty)) { - @include globals.position(globals.$ion-scale-100, calc(globals.$ion-scale-050 * -1), null, null); -} - -:host(.avatar-small) ::slotted(ion-badge.badge-vertical-top:not(:empty)), -:host(.avatar-medium) ::slotted(ion-badge.badge-vertical-top:not(:empty)) { - @include globals.position(globals.$ion-scale-150, 0, null, null); -} - -:host(.avatar-large) ::slotted(ion-badge.badge-vertical-top:not(:empty)) { - @include globals.position(globals.$ion-scale-150, globals.$ion-scale-050, null, null); -} - -:host(.avatar-xlarge) ::slotted(ion-badge.badge-vertical-top:not(:empty)) { - @include globals.position(globals.$ion-scale-150, globals.$ion-scale-150, null, null); -} - // Avatar Disabled // -------------------------------------------------- :host(.avatar-disabled)::after { diff --git a/core/src/components/avatar/avatar.md.scss b/core/src/components/avatar/avatar.md.scss index cfcd2520280..b7679654770 100644 --- a/core/src/components/avatar/avatar.md.scss +++ b/core/src/components/avatar/avatar.md.scss @@ -11,22 +11,3 @@ width: $avatar-md-width; height: $avatar-md-height; } - -// Avatar Empty Badge (hint) -// -------------------------------------------------- - -::slotted(ion-badge.badge-vertical-top:empty) { - @include globals.transform(translate(-50%, 50%)); -} - -::slotted(ion-badge.badge-vertical-bottom:empty) { - @include globals.transform(translateX(-100%)); -} - -:host ::slotted(ion-badge.badge-vertical-top:not(:empty)) { - @include globals.transform(translate(0, 100%)); -} - -:host ::slotted(ion-badge.badge-vertical-bottom:not(:empty)) { - @include globals.transform(translate(0, -100%)); -} diff --git a/core/src/components/avatar/avatar.tsx b/core/src/components/avatar/avatar.tsx index da5ccb81707..a1a5a6cfcbd 100644 --- a/core/src/components/avatar/avatar.tsx +++ b/core/src/components/avatar/avatar.tsx @@ -54,14 +54,8 @@ export class Avatar implements ComponentInterface { } private getSize(): string | undefined { - const theme = getIonTheme(this); const { size } = this; - // TODO(ROU-10752): Remove theme check when sizes are defined for all themes. - if (theme !== 'ionic') { - return undefined; - } - if (size === undefined) { return 'medium'; } diff --git a/core/src/components/avatar/test/hint/index.html b/core/src/components/avatar/test/hint/index.html new file mode 100644 index 00000000000..765790530c2 --- /dev/null +++ b/core/src/components/avatar/test/hint/index.html @@ -0,0 +1,97 @@ + + +
+ +