Files
Brandy Carney 6fe716fd13 feat(button): allow button to increase in height when text wraps (#27547)
Issue number: N/A - this does not completely resolve an issue but it
enables users to opt-in to having text wrap in a button by setting a
minimum height

---------

## What is the current behavior?
The current behavior when text is really long in a button is:
- Default buttons expand in width until part of the text (and button) is
off the screen and not in the visible viewport
- Block and full buttons horizontally align the text in the center and
overflow it on both sides (but the overflow is not visible so the text
is cut off at the beginning and end)

## What is the new behavior?
Allow the button height to increase when text wraps and add some padding
so that buttons with wrapped text still look nice. This does **NOT**
wrap the text in a button by default. That will be done in FW-4599.

- Removed `text-overflow: ellipsis` since this does not have any effect
- Changes `height` setting to `min-height` on all button types (small,
large, default) and buttons inside of an item, toolbar or list header
- Increases `padding-top` and `padding-bottom` on the buttons so that
overflowing buttons have padding around them
- Changes `.button-native` display property from `block` to `flex` in
order for anchor tags (`<ion-button href="#">` to align their text
vertically
- Sets `flex-shrink: 0` on slotted `start`/`end` elements to prevent
icons (and other elements) from shrinking to make room for the text
- Adds e2e test for button wrapping including the different types of
buttons that were changed by this PR
- Adds `ion-text-wrap` to the `ion-button` elements used in this test to
verify the height / padding changes are working as desired (to be
removed with FW-4599)
- Screenshot diffs are in the following PR:
https://github.com/ionic-team/ionic-framework/pull/27561

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

This does **NOT** wrap the text in a button by default. It only enables
buttons to look nicer and auto adjust their height/padding when the text
is wrapping.

After internal discussion we decided that automatically making the text
wrap inside of a button may have undesired effects on existing apps. For
example, if someone has a button inside of a list header with a long
label, the button will now wrap if it has a space or dash in the text
content.

Developers should set `ion-text-wrap` on the `ion-button` to opt-in to
text wrapping in a button, and this will become the default as part of
FW-4599 (the next major release).

---------

Co-authored-by: ionitron <hi@ionicframework.com>
Co-authored-by: Liam DeBeasi <liamdebeasi@users.noreply.github.com>
2023-07-07 15:04:52 -04:00

342 lines
7.6 KiB
SCSS

@import "./button.vars";
// Button
// --------------------------------------------------
:host {
/**
* @prop --background: Background of the button
* @prop --background-activated: Background of the button when pressed. Note: setting this will interfere with the Material Design ripple.
* @prop --background-activated-opacity: Opacity of the button when pressed
* @prop --background-focused: Background of the button when focused with the tab key
* @prop --background-focused-opacity: Opacity of the button when focused with the tab key
* @prop --background-hover: Background of the button on hover
* @prop --background-hover-opacity: Opacity of the background on hover
*
* @prop --color: Text color of the button
* @prop --color-activated: Text color of the button when pressed
* @prop --color-focused: Text color of the button when focused with the tab key
* @prop --color-hover: Text color of the button when hover
*
* @prop --transition: Transition of the button
*
* @prop --border-radius: Border radius of the button
* @prop --border-width: Border width of the button
* @prop --border-style: Border style of the button
* @prop --border-color: Border color of the button
*
* @prop --ripple-color: Color of the button ripple effect
*
* @prop --box-shadow: Box shadow of the button
* @prop --opacity: Opacity of the button
*
* @prop --padding-top: Top padding of the button
* @prop --padding-end: Right padding if direction is left-to-right, and left padding if direction is right-to-left of the button
* @prop --padding-bottom: Bottom padding of the button
* @prop --padding-start: Left padding if direction is left-to-right, and right padding if direction is right-to-left of the button
*/
--overflow: hidden;
--ripple-color: currentColor;
--border-width: initial;
--border-color: initial;
--border-style: initial;
--color-activated: var(--color);
--color-focused: var(--color);
--color-hover: var(--color);
--box-shadow: none;
display: inline-block;
width: auto;
color: var(--color);
font-family: $font-family-base;
text-align: center;
text-decoration: none;
// TODO(FW-4599): change to normal
white-space: nowrap;
user-select: none;
vertical-align: top; // the better option for most scenarios
vertical-align: -webkit-baseline-middle; // the best for those that support it
font-kerning: none;
}
:host(.button-disabled) {
cursor: default;
opacity: .5;
pointer-events: none;
}
// Solid Button
// --------------------------------------------------
// Default Solid Color
:host(.button-solid) {
--background: #{ion-color(primary, base)};
--color: #{ion-color(primary, contrast)};
}
// Outline Button
// --------------------------------------------------
// Default Outline Color
:host(.button-outline) {
--border-color: #{ion-color(primary, base)};
--background: transparent;
--color: #{ion-color(primary, base)};
}
// Clear Button
// --------------------------------------------------
// Default Clear Color
:host(.button-clear) {
--border-width: 0;
--background: transparent;
--color: #{ion-color(primary, base)};
}
// Block Button
// --------------------------------------------------
:host(.button-block) {
display: block;
}
:host(.button-block) .button-native {
@include margin-horizontal(0);
width: 100%;
clear: both;
contain: content;
}
:host(.button-block) .button-native::after {
clear: both;
}
// Full Button
// --------------------------------------------------
:host(.button-full) {
display: block;
}
:host(.button-full) .button-native {
@include margin-horizontal(0);
width: 100%;
contain: content;
}
:host(.button-full:not(.button-round)) .button-native {
@include border-radius(0);
border-right-width: 0;
border-left-width: 0;
}
.button-native {
@include border-radius(var(--border-radius));
@include font-smoothing();
@include margin(0);
@include padding(var(--padding-top), var(--padding-end), var(--padding-bottom), var(--padding-start));
@include text-inherit();
display: flex;
position: relative;
align-items: center;
width: 100%;
height: 100%;
min-height: inherit;
transition: var(--transition);
border-width: var(--border-width);
border-style: var(--border-style);
border-color: var(--border-color);
outline: none;
background: var(--background);
line-height: 1;
box-shadow: var(--box-shadow);
contain: layout style;
cursor: pointer;
opacity: var(--opacity);
overflow: var(--overflow);
z-index: 0;
box-sizing: border-box;
appearance: none;
}
.button-native::-moz-focus-inner {
border: 0;
}
.button-inner {
display: flex;
position: relative;
flex-flow: row nowrap;
flex-shrink: 0;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
z-index: 1;
}
// Button Slots
// --------------------------------------------------
::slotted([slot=start]),
::slotted([slot=end]) {
flex-shrink: 0;
}
// Button Icons
// --------------------------------------------------
::slotted(ion-icon) {
font-size: 1.35em;
pointer-events: none;
}
::slotted(ion-icon[slot="start"]) {
@include margin(0, .3em, 0, -.3em);
}
::slotted(ion-icon[slot="end"]) {
@include margin(0, -.2em, 0, .3em);
}
::slotted(ion-icon[slot="icon-only"]) {
font-size: 1.8em;
}
// Button Ripple effect
// --------------------------------------------------
ion-ripple-effect {
color: var(--ripple-color);
}
// Button: States
// --------------------------------------------------
.button-native::after {
@include button-state();
}
// Button Focused
:host(.ion-focused) {
color: var(--color-focused);
}
:host(.ion-focused) .button-native::after {
background: var(--background-focused);
opacity: var(--background-focused-opacity);
}
// Button Hover
@media (any-hover: hover) {
:host(:hover) {
color: var(--color-hover);
}
:host(:hover) .button-native::after {
background: var(--background-hover);
opacity: var(--background-hover-opacity);
}
}
// Button Activated
:host(.ion-activated) {
color: var(--color-activated);
}
:host(.ion-activated) .button-native::after {
background: var(--background-activated);
opacity: var(--background-activated-opacity);
}
// Button Colors
// --------------------------------------------------
// Solid Button with Color
:host(.button-solid.ion-color) .button-native {
background: current-color(base);
color: current-color(contrast);
}
// Outline Button with Color
:host(.button-outline.ion-color) .button-native {
border-color: current-color(base);
background: transparent;
color: current-color(base);
}
// Clear Button with Color
:host(.button-clear.ion-color) .button-native {
background: transparent;
color: current-color(base);
}
// Button in Toolbar
// --------------------------------------------------
:host(.in-toolbar:not(.ion-color):not(.in-toolbar-color)) .button-native {
color: #{var(--ion-toolbar-color, var(--color))};
}
:host(.button-outline.in-toolbar:not(.ion-color):not(.in-toolbar-color)) .button-native {
border-color: #{var(--ion-toolbar-color, var(--color, var(--border-color)))};
}
:host(.button-solid.in-toolbar:not(.ion-color):not(.in-toolbar-color)) .button-native {
background: #{var(--ion-toolbar-color, var(--background))};
color: #{var(--ion-toolbar-background, var(--color))};
}
// Activated Button in Toolbar
// --------------------------------------------------
:host(.button-outline.ion-activated.in-toolbar:not(.ion-color):not(.in-toolbar-color)) .button-native {
background: var(--ion-toolbar-color, var(--color));
color: #{var(--ion-toolbar-background, var(--background), ion-color(primary, contrast))};
}