Skip to content

Commit

Permalink
feat(segment): implement iOS 13 segment with animation (#19036)
Browse files Browse the repository at this point in the history
Changes
Closes #18663

* Converts Segment to shadow
* Enables gesture to swipe between segment buttons
* Adds indicator transition to slide the indicator between buttons
* Updates global theme variables
* Removes activated state, now handled by the gesture
* Updates iOS to latest iOS 13 UI
* Ensures customization is working for the buttons and indicator
* Updates the e2e tests
  • Loading branch information
brandyscarney authored and liamdebeasi committed Jan 14, 2020
1 parent 8e11f79 commit dc66ce4
Show file tree
Hide file tree
Showing 27 changed files with 1,263 additions and 466 deletions.
12 changes: 7 additions & 5 deletions core/api.txt
Expand Up @@ -986,13 +986,14 @@ ion-searchbar,css-prop,--placeholder-font-style
ion-searchbar,css-prop,--placeholder-font-weight
ion-searchbar,css-prop,--placeholder-opacity

ion-segment,scoped
ion-segment,shadow
ion-segment,prop,color,string | undefined,undefined,false,false
ion-segment,prop,disabled,boolean,false,false,false
ion-segment,prop,mode,"ios" | "md",undefined,false,false
ion-segment,prop,scrollable,boolean,false,false,false
ion-segment,prop,value,null | string | undefined,undefined,false,false
ion-segment,event,ionChange,SegmentChangeEventDetail,true
ion-segment,css-prop,--background

ion-segment-button,shadow
ion-segment-button,prop,checked,boolean,false,false,false
Expand All @@ -1003,20 +1004,21 @@ ion-segment-button,prop,type,"button" | "reset" | "submit",'button',false,false
ion-segment-button,prop,value,string,'ion-sb-' + (ids++),false,false
ion-segment-button,event,ionSelect,void,true
ion-segment-button,css-prop,--background
ion-segment-button,css-prop,--background-activated
ion-segment-button,css-prop,--background-checked
ion-segment-button,css-prop,--background-disabled
ion-segment-button,css-prop,--background-hover
ion-segment-button,css-prop,--border-color
ion-segment-button,css-prop,--border-radius
ion-segment-button,css-prop,--border-style
ion-segment-button,css-prop,--border-width
ion-segment-button,css-prop,--color
ion-segment-button,css-prop,--color-activated
ion-segment-button,css-prop,--color-checked
ion-segment-button,css-prop,--color-checked-disabled
ion-segment-button,css-prop,--color-disabled
ion-segment-button,css-prop,--color-hover
ion-segment-button,css-prop,--indicator-box-shadow
ion-segment-button,css-prop,--indicator-color
ion-segment-button,css-prop,--indicator-color-checked
ion-segment-button,css-prop,--indicator-transform
ion-segment-button,css-prop,--indicator-transition
ion-segment-button,css-prop,--margin-bottom
ion-segment-button,css-prop,--margin-end
ion-segment-button,css-prop,--margin-start
Expand Down
4 changes: 2 additions & 2 deletions core/src/components.d.ts
Expand Up @@ -2231,7 +2231,7 @@ export namespace Components {
*/
'mode'?: "ios" | "md";
/**
* If `true`, the segment buttons will overflow and the user can swipe to see them.
* If `true`, the segment buttons will overflow and the user can swipe to see them. In addition, this will disable the gesture to drag the indicator between the buttons in order to swipe to see hidden buttons.
*/
'scrollable': boolean;
/**
Expand Down Expand Up @@ -5492,7 +5492,7 @@ declare namespace LocalJSX {
*/
'onIonChange'?: (event: CustomEvent<SegmentChangeEventDetail>) => void;
/**
* If `true`, the segment buttons will overflow and the user can swipe to see them.
* If `true`, the segment buttons will overflow and the user can swipe to see them. In addition, this will disable the gesture to drag the indicator between the buttons in order to swipe to see hidden buttons.
*/
'scrollable'?: boolean;
/**
Expand Down
1 change: 1 addition & 0 deletions core/src/components/menu-button/menu-button.scss
Expand Up @@ -125,6 +125,7 @@ ion-icon {
// Menu Button in Toolbar: Global Theming
// --------------------------------------------------

// TODO this will not work in Safari - component is shadow not scoped
:host-context(ion-toolbar:not(.ion-color)) {
color: #{var(--ion-toolbar-color, var(--color))};
}
53 changes: 27 additions & 26 deletions core/src/components/segment-button/readme.md
Expand Up @@ -639,32 +639,33 @@ export const SegmentButtonExample: React.FC = () => (

## CSS Custom Properties

| Name | Description |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `--background` | Background of the segment button |
| `--background-activated` | Background of the segment button when pressed |
| `--background-checked` | Background of the checked segment button |
| `--background-hover` | Background of the segment button on hover |
| `--border-color` | Color of the segment button border |
| `--border-radius` | Radius of the segment button border |
| `--border-style` | Style of the segment button border |
| `--border-width` | Width of the segment button border |
| `--color` | Color of the segment button |
| `--color-activated` | Color of the segment button when pressed |
| `--color-checked` | Color of the checked segment button |
| `--color-checked-disabled` | Color of the checked & disabled segment button |
| `--color-disabled` | Color of the disabled segment button |
| `--indicator-color` | Color of the indicator (highlight) under the segment button |
| `--indicator-color-checked` | Color of the indicator (highlight) under the checked segment button |
| `--margin-bottom` | Bottom margin of the segment button |
| `--margin-end` | Right margin if direction is left-to-right, and left margin if direction is right-to-left of the segment button |
| `--margin-start` | Left margin if direction is left-to-right, and right margin if direction is right-to-left of the segment button |
| `--margin-top` | Top margin of the segment button |
| `--padding-bottom` | Bottom padding of the segment button |
| `--padding-end` | Right padding if direction is left-to-right, and left padding if direction is right-to-left of the segment button |
| `--padding-start` | Left padding if direction is left-to-right, and right padding if direction is right-to-left of the segment button |
| `--padding-top` | Top padding of the segment button |
| `--transition` | Transition of the segment button |
| Name | Description |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------- |
| `--background` | Background of the segment button |
| `--background-checked` | Background of the checked segment button |
| `--background-disabled` | Background of the disabled segment button |
| `--background-hover` | Background of the segment button on hover |
| `--border-color` | Color of the segment button border |
| `--border-radius` | Radius of the segment button border |
| `--border-style` | Style of the segment button border |
| `--border-width` | Width of the segment button border |
| `--color` | Color of the segment button |
| `--color-checked` | Color of the checked segment button |
| `--color-disabled` | Color of the disabled segment button |
| `--color-hover` | Color of the segment button on hover |
| `--indicator-box-shadow` | Box shadow on the indicator for the checked segment button |
| `--indicator-color` | Color of the indicator for the checked segment button |
| `--indicator-transform` | Transform of the indicator for the checked segment button |
| `--indicator-transition` | Transition of the indicator for the checked segment button |
| `--margin-bottom` | Bottom margin of the segment button |
| `--margin-end` | Right margin if direction is left-to-right, and left margin if direction is right-to-left of the segment button |
| `--margin-start` | Left margin if direction is left-to-right, and right margin if direction is right-to-left of the segment button |
| `--margin-top` | Top margin of the segment button |
| `--padding-bottom` | Bottom padding of the segment button |
| `--padding-end` | Right padding if direction is left-to-right, and left padding if direction is right-to-left of the segment button |
| `--padding-start` | Left padding if direction is left-to-right, and right padding if direction is right-to-left of the segment button |
| `--padding-top` | Top padding of the segment button |
| `--transition` | Transition of the segment button |


## Dependencies
Expand Down
105 changes: 89 additions & 16 deletions core/src/components/segment-button/segment-button.ios.scss
Expand Up @@ -5,24 +5,73 @@
// --------------------------------------------------

:host {
--background: #{$segment-button-ios-background-color};
--background-checked: #{$segment-button-ios-background-color-checked};
--background-hover: #{$segment-button-ios-background-color-hover};
--border-radius: #{$segment-button-ios-border-radius};
--border-width: #{$segment-button-ios-border-width};
--border-color: #{$segment-button-ios-border-color};
--border-style: solid;
--indicator-box-shadow: #{$segment-button-ios-box-shadow-checked};
--indicator-color: #{$segment-button-ios-indicator-color};
--indicator-transition: #{$segment-button-ios-transition-animated};
--indicator-transform: none;
--transition: #{$segment-button-ios-transition};
--padding-top: 0;
--padding-end: 13px;
--padding-bottom: 0;
--padding-start: 13px;

min-height: #{$segment-button-ios-height};
@include margin($segment-button-ios-margin, null, $segment-button-ios-margin, null);

position: relative;

flex-basis: 0;
flex-direction: row;

min-width: #{$segment-button-ios-min-width};

min-height: #{$segment-button-ios-min-height};

// Necessary for the z-index to work properly
transform: translate3d(0, 0, 0);

font-size: #{$segment-button-ios-font-size};

font-weight: 450;

line-height: #{$segment-button-ios-line-height};
}


// Segment Button: Indicator
// Segment Button: Borders
// --------------------------------------------------

.segment-button-indicator {
display: none;
:host::before {
@include margin(5px, 0);

transition: 160ms opacity ease-in-out;

transition-delay: 100ms;

border-left: var(--border-width) var(--border-style) var(--border-color);

content: "";
opacity: 1;

will-change: opacity;
}

:host(:first-of-type)::before {
border-left-color: transparent;
}


// Segment Button: Disabled
// --------------------------------------------------

:host(.segment-button-disabled) {
opacity: $segment-button-ios-opacity-disabled;
}


Expand All @@ -48,28 +97,52 @@
}


// Segment Button: Hover
// Segment Button: Checked Indicator
// --------------------------------------------------

.segment-button-indicator {
@include padding(null, $segment-button-ios-margin);
@include position(0, 0, 0, 0);
}

.segment-button-indicator-background {
@include border-radius($segment-button-ios-border-radius);

background: var(--indicator-color);
}

.segment-button-indicator-background {
transition: var(--indicator-transition);
}


// Segment Button: Checked Borders
// --------------------------------------------------

@media (any-hover: hover) {
:host(:hover:not(.segment-button-checked)) {
background: var(--background-hover);
}
:host(.segment-button-checked)::before,
:host(.segment-button-after-checked)::before {
opacity: 0;
}


// Segment Button: Activated
// Segment Button: Checked
// --------------------------------------------------

:host(.activated) {
background: var(--background-activated);
:host(.segment-button-checked) {
z-index: -1;
}


// Segment: Checked & Activated
// Segment Button: Toolbar
// --------------------------------------------------

:host(.segment-button-checked.activated) {
background: var(--background-checked);
color: var(--color-checked);
// Segment button indicator color should use the global variable with
// a fallback to the local variable
:host(.in-toolbar) .segment-button-indicator-background {
background: #{var(--ion-toolbar-segment-indicator-color, var(--indicator-color))};
}

// Do not use the global or local CSS variable if the toolbar has a color
:host(.in-toolbar-color) .segment-button-indicator-background {
background: #fff;
}
62 changes: 22 additions & 40 deletions core/src/components/segment-button/segment-button.ios.vars.scss
Expand Up @@ -7,37 +7,37 @@
$segment-button-ios-background-color: transparent !default;

/// @prop - Background of the checked segment button
$segment-button-ios-background-color-checked: ion-color(primary, base) !default;
$segment-button-ios-background-color-checked: transparent !default;

/// @prop - Opacity of the segment button on hover
$segment-button-ios-opacity-hover: .1 !default;
/// @prop - Background of the checked segment button indicator
$segment-button-ios-indicator-color: var(--ion-color-step-350, $background-color) !default;

/// @prop - Margin of the segment button
$segment-button-ios-margin: 2px !default;

/// @prop - Opacity of the segment button when pressed
$segment-button-ios-opacity-activated: .16 !default;
/// @prop - Opacity of the segment button on hover
$segment-button-ios-opacity-hover: .5 !default;

/// @prop - Opacity of the disabled segment button
$segment-button-ios-opacity-disabled: .3 !default;

/// @prop - Background of the segment button on hover
$segment-button-ios-background-color-hover: ion-color(primary, base, $segment-button-ios-opacity-hover) !default;

/// @prop - Background of the activated segment button
$segment-button-ios-background-color-activated: ion-color(primary, base, $segment-button-ios-opacity-activated) !default;
$segment-button-ios-background-color-hover: transparent !default;

/// @prop - Background of the disabled segment button
$segment-button-ios-background-color-disabled: ion-color(primary, base, $segment-button-ios-opacity-disabled) !default;

/// @prop - Text color of the segment button
$segment-button-ios-text-color: ion-color(primary, base) !default;

/// @prop - Text color of the checked segment button
$segment-button-ios-text-color-checked: ion-color(primary, contrast) !default;
/// @prop - Box shadow of the checked segment button
$segment-button-ios-box-shadow-checked: 0 0 5px rgba(0, 0, 0, 0.16) !default;

/// @prop - Border width of the segment button
$segment-button-ios-border-width: 1px !default;

/// @prop - Height of the segment button
$segment-button-ios-height: 24px !default;
/// @prop - Border color of the segment button
$segment-button-ios-border-color: rgba($text-color-rgb, 0.12) !default;

/// @prop - Minimum width of the segment button
$segment-button-ios-min-width: 70px !default;

/// @prop - Minimum height of the segment button
$segment-button-ios-min-height: 28px !default;

/// @prop - Line height of the segment button
$segment-button-ios-line-height: 37px !default;
Expand All @@ -46,31 +46,13 @@ $segment-button-ios-line-height: 37px !default;
$segment-button-ios-font-size: 13px !default;

/// @prop - Border radius of the segment button
$segment-button-ios-border-radius: 4px !default;

/// @prop - Border color of the segment button
$segment-button-ios-border-color: ion-color(primary, base) !default;
$segment-button-ios-border-radius: 7px !default;

/// @prop - Size of an icon in the segment button
$segment-button-ios-icon-size: 24px !default;

/// @prop - Line height of an icon in the segment button
$segment-button-ios-icon-line-height: 28px !default;

/// @prop - Transition of the segment button
$segment-button-ios-transition: 100ms all linear !default;

/// @prop - Max width of the segment button in a toolbar
$segment-button-ios-toolbar-button-max-width: 100px !default;

/// @prop - Line height of the segment button in a toolbar
$segment-button-ios-toolbar-line-height: 22px !default;

/// @prop - Font size of the segment button in a toolbar
$segment-button-ios-toolbar-font-size: 12px !default;

/// @prop - Size of an icon in the segment button in a toolbar
$segment-button-ios-toolbar-icon-size: 22px !default;

/// @prop - Line height of an icon in the segment button in a toolbar
$segment-button-ios-toolbar-icon-line-height: 24px !default;
/// @prop - Transition of the animated segment button
$segment-button-ios-transition-animated: transform 260ms cubic-bezier(0.4, 0, 0.2, 1) !default;

0 comments on commit dc66ce4

Please sign in to comment.