diff --git a/angular/src/directives/proxies.ts b/angular/src/directives/proxies.ts index 0bd316fb08..810079ef1c 100644 --- a/angular/src/directives/proxies.ts +++ b/angular/src/directives/proxies.ts @@ -363,12 +363,12 @@ export class Input { } export declare interface Item extends StencilComponents<'IonItem'> {} -@Component({ selector: 'ion-item', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: '', inputs: ['color', 'mode', 'button', 'detail', 'detailIcon', 'disabled', 'href', 'lines', 'routerDirection', 'state', 'type'] }) +@Component({ selector: 'ion-item', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: '', inputs: ['color', 'mode', 'button', 'detail', 'detailIcon', 'disabled', 'href', 'lines', 'routerDirection', 'type'] }) export class Item { constructor(r: ElementRef) { const el = r.nativeElement; - proxyInputs(this, el, ['color', 'mode', 'button', 'detail', 'detailIcon', 'disabled', 'href', 'lines', 'routerDirection', 'state', 'type']); + proxyInputs(this, el, ['color', 'mode', 'button', 'detail', 'detailIcon', 'disabled', 'href', 'lines', 'routerDirection', 'type']); } } diff --git a/core/src/components.d.ts b/core/src/components.d.ts index f9fccc9484..309653be67 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -2107,7 +2107,6 @@ export namespace Components { * When using a router, it specifies the transition direction when navigating to another page using `href`. */ 'routerDirection'?: RouterDirection; - 'state'?: 'valid' | 'invalid' | 'focus'; /** * The type of the button. Only used when an `onclick` or `button` property is present. Possible values are: `"submit"`, `"reset"` and `"button"`. Default value is: `"button"` */ @@ -2150,7 +2149,6 @@ export namespace Components { * When using a router, it specifies the transition direction when navigating to another page using `href`. */ 'routerDirection'?: RouterDirection; - 'state'?: 'valid' | 'invalid' | 'focus'; /** * The type of the button. Only used when an `onclick` or `button` property is present. Possible values are: `"submit"`, `"reset"` and `"button"`. Default value is: `"button"` */ @@ -4017,7 +4015,7 @@ export namespace Components { * The text to display on the ok button. Default: `OK`. */ 'okText': string; - 'open': (ev?: UIEvent | undefined) => Promise; + 'open': (ev?: UIEvent | undefined) => Promise; /** * The text to display when the select is empty. */ diff --git a/core/src/components/input/input.tsx b/core/src/components/input/input.tsx index 31ce341df3..41cf924c56 100644 --- a/core/src/components/input/input.tsx +++ b/core/src/components/input/input.tsx @@ -324,7 +324,6 @@ export class Input implements ComponentInterface { return { class: { ...createColorClasses(this.color), - 'in-item': hostContext('ion-item', this.el), 'has-value': this.hasValue(), 'has-focus': this.hasFocus diff --git a/core/src/components/input/test/basic/e2e.ts b/core/src/components/input/test/basic/e2e.ts index d3cd4c20a2..5fa349c6e8 100644 --- a/core/src/components/input/test/basic/e2e.ts +++ b/core/src/components/input/test/basic/e2e.ts @@ -5,6 +5,33 @@ it('input: basic', async () => { url: '/src/components/input/test/basic?ionic:animated=false' }); - const compare = await page.compareScreenshot(); + let compare = await page.compareScreenshot(); + expect(compare).toMatchScreenshot(); + + const fullInput = await page.find('#fullInput'); + await fullInput.click(); + + const fullItem = await page.find('#fullItem'); + expect(fullItem).toHaveClass('item-has-focus'); + + compare = await page.compareScreenshot('full input focused'); + expect(compare).toMatchScreenshot(); + + const insetInput = await page.find('#insetInput'); + await insetInput.click(); + + const insetItem = await page.find('#insetItem'); + expect(insetItem).toHaveClass('item-has-focus'); + + compare = await page.compareScreenshot('inset input focused'); + expect(compare).toMatchScreenshot(); + + const noneInput = await page.find('#noneInput'); + await noneInput.click(); + + const noneItem = await page.find('#noneItem'); + expect(noneItem).toHaveClass('item-has-focus'); + + compare = await page.compareScreenshot('no lines input focused'); expect(compare).toMatchScreenshot(); }); diff --git a/core/src/components/input/test/basic/index.html b/core/src/components/input/test/basic/index.html index f3182996ef..54e1d238f1 100644 --- a/core/src/components/input/test/basic/index.html +++ b/core/src/components/input/test/basic/index.html @@ -28,6 +28,18 @@ + + + + + + + + + + + + Default Label diff --git a/core/src/components/item/item.ios.scss b/core/src/components/item/item.ios.scss index 4e799ee059..82730ba7bc 100644 --- a/core/src/components/item/item.ios.scss +++ b/core/src/components/item/item.ios.scss @@ -9,16 +9,17 @@ --padding-start: #{$item-ios-padding-start}; --inner-padding-end: #{$item-ios-padding-end / 2}; --inner-border-width: #{0px 0px $item-ios-border-bottom-width 0px}; - - font-size: $item-ios-font-size; -} - -:host(:not(.ion-color)) { --background: var(--ion-item-background-color, transparent); --background-activated: #{$item-ios-background-color-active}; --border-color: #{$item-ios-border-bottom-color}; --color: #{$item-ios-text-color}; --detail-icon-color: #{$item-ios-border-bottom-color}; + --highlight-height: 0; + --highlight-color-focused: #{$item-ios-input-highlight-color}; + --highlight-color-valid: #{$item-ios-input-highlight-color-valid}; + --highlight-color-invalid: #{$item-ios-input-highlight-color-invalid}; + + font-size: $item-ios-font-size; } :host(.activated) { @@ -29,14 +30,24 @@ // iOS Item Lines // -------------------------------------------------- +// Default input items have an inset border +:host(.item-interactive) { + --show-full-highlight: 0; + --show-inset-highlight: 1; +} + // Full lines - apply the border to the item // Inset lines - apply the border to the item inner :host(.item-lines-full) { --border-width: #{0px 0px $item-ios-border-bottom-width 0px}; + --show-full-highlight: 1; + --show-inset-highlight: 0; } :host(.item-lines-inset) { --inner-border-width: #{0px 0px $item-ios-border-bottom-width 0px}; + --show-full-highlight: 0; + --show-inset-highlight: 1; } // Full lines - remove the border from the item inner (inset list items) @@ -45,11 +56,13 @@ :host(.item-lines-inset), :host(.item-lines-none) { --border-width: 0px; + --show-full-highlight: 0; } :host(.item-lines-full), :host(.item-lines-none) { --inner-border-width: 0px; + --show-inset-highlight: 0; } diff --git a/core/src/components/item/item.ios.vars.scss b/core/src/components/item/item.ios.vars.scss index e5ca3bd280..265e26b95d 100644 --- a/core/src/components/item/item.ios.vars.scss +++ b/core/src/components/item/item.ios.vars.scss @@ -63,6 +63,15 @@ $item-ios-border-bottom-color: $item-ios-border-color !default; /// @prop - Border bottom for the item $item-ios-border-bottom: $item-ios-border-bottom-width $item-ios-border-bottom-style $item-ios-border-bottom-color !default; +/// @prop - Color of the item input highlight +$item-ios-input-highlight-color: ion-color(primary, base) !default; + +/// @prop - Color of the item input highlight when valid +$item-ios-input-highlight-color-valid: ion-color(success, base) !default; + +/// @prop - Color of the item input highlight when invalid +$item-ios-input-highlight-color-invalid: ion-color(danger, base) !default; + // Item Slots // -------------------------------------------------- diff --git a/core/src/components/item/item.md.scss b/core/src/components/item/item.md.scss index 8366812741..3883184a93 100644 --- a/core/src/components/item/item.md.scss +++ b/core/src/components/item/item.md.scss @@ -8,8 +8,17 @@ :host { --transition: background-color 300ms cubic-bezier(.4, 0, .2, 1); --padding-start: #{$item-md-padding-start}; + --background: var(--ion-item-background-color, transparent); + --background-activated: #{$item-md-background-color-active}; + --color: #{$item-md-text-color}; + --border-color: #{$item-md-border-bottom-color}; --inner-padding-end: #{$item-md-padding-end / 2}; - --padding-start: #{$item-md-padding-start}; + --inner-border-width: #{0 0 $item-md-border-bottom-width 0}; + --detail-icon-color: #{$item-md-border-bottom-color}; + --highlight-height: 2px; + --highlight-color-focused: #{$item-md-input-highlight-color}; + --highlight-color-valid: #{$item-md-input-highlight-color-valid}; + --highlight-color-invalid: #{$item-md-input-highlight-color-invalid}; font-size: $item-md-font-size; font-weight: normal; @@ -17,31 +26,30 @@ text-transform: none; } -:host(:not(.ion-color)) { - --background: var(--ion-item-background-color, transparent); - --background-activated: #{$item-md-background-color-active}; - --border-color: #{$item-md-border-bottom-color}; - --color: #{$item-md-text-color}; - --detail-icon-color: #{$item-md-border-bottom-color}; -} - // Material Design Item Lines // -------------------------------------------------- -// Default input items have a border +// Default input items have a full border :host(.item-interactive) { --border-width: #{0 0 $item-md-border-bottom-width 0}; + --inner-border-width: 0; + --show-full-highlight: 1; + --show-inset-highlight: 0; } // Full lines - apply the border to the item // Inset lines - apply the border to the item inner :host(.item-lines-full) { --border-width: #{0 0 $item-md-border-bottom-width 0}; + --show-full-highlight: 1; + --show-inset-highlight: 0; } :host(.item-lines-inset) { --inner-border-width: #{0 0 $item-md-border-bottom-width 0}; + --show-full-highlight: 0; + --show-inset-highlight: 1; } // Full lines - remove the border from the item inner (inset list items) @@ -50,11 +58,13 @@ :host(.item-lines-inset), :host(.item-lines-none) { --border-width: 0; + --show-full-highlight: 0; } :host(.item-lines-full), :host(.item-lines-none) { --inner-border-width: 0; + --show-inset-highlight: 0; } // Material Design Item Detail Push diff --git a/core/src/components/item/item.md.vars.scss b/core/src/components/item/item.md.vars.scss index 2c70677182..88ec7c1c87 100644 --- a/core/src/components/item/item.md.vars.scss +++ b/core/src/components/item/item.md.vars.scss @@ -48,6 +48,15 @@ $item-md-border-bottom-color: $item-md-border-color !default; /// @prop - Border bottom for the item when lines are displayed $item-md-border-bottom: $item-md-border-bottom-width $item-md-border-bottom-style $item-md-border-color !default; +/// @prop - Color of the item input highlight +$item-md-input-highlight-color: ion-color(primary, base) !default; + +/// @prop - Color of the item input highlight when valid +$item-md-input-highlight-color-valid: ion-color(success, base) !default; + +/// @prop - Color of the item input highlight when invalid +$item-md-input-highlight-color-invalid: ion-color(danger, base) !default; + // Item Slots // -------------------------------------------------- diff --git a/core/src/components/item/item.scss b/core/src/components/item/item.scss index 3632a1350a..423243ecea 100644 --- a/core/src/components/item/item.scss +++ b/core/src/components/item/item.scss @@ -13,49 +13,52 @@ * @prop --border-width: Width of the item border * @prop --box-shadow: Box shadow of the item * @prop --color: Color of the item + * * @prop --detail-icon-color: Color of the item detail icon + * * @prop --inner-border-width: Width of the item inner border * @prop --inner-box-shadow: Box shadow of the item inner * @prop --inner-padding-bottom: Bottom padding of the item inner * @prop --inner-padding-end: End padding of the item inner * @prop --inner-padding-start: Start padding of the item inner * @prop --inner-padding-top: Top padding of the item inner + * * @prop --min-height: Minimum height of the item * @prop --padding-bottom: Bottom padding of the item * @prop --padding-end: End padding of the item * @prop --padding-start: Start padding of the item * @prop --padding-top: Top padding of the item * @prop --transition: Transition of the item + * + * @prop --highlight-height: The height of the highlight on the item + * @prop --highlight-color-focused: The color of the highlight on the item when focused + * @prop --highlight-color-valid: The color of the highlight on the item when valid + * @prop --highlight-color-invalid: The color of the highlight on the item when invalid */ --min-height: #{$item-min-height}; - --background: #{current-color(base)}; - --background-activated: #{current-color(tint)}; - --color: #{current-color(contrast)}; - --detail-icon-color: #{current-color(shade)}; --border-radius: 0px; --border-width: 0px; --border-style: solid; - --border-color: #{current-color(shade)}; - --inner-border-width: 0px; --padding-top: 0px; --padding-bottom: 0px; --padding-end: 0px; --padding-start: 0px; + --box-shadow: none; + --inner-border-width: 0px; --inner-padding-top: 0px; --inner-padding-bottom: 0px; --inner-padding-start: 0px; --inner-padding-end: 0px; - --box-shadow: none; --inner-box-shadow: none; - --highlight-color-focus: #{ion-color(primary, base)}; - --highlight-color-valid: #{ion-color(success, base)}; - --highlight-color-invalid: #{ion-color(danger, base)}; - --highlight-height: 2px; + --show-full-highlight: 0; + --show-inset-highlight: 0; @include font-smoothing(); display: block; + position: relative; + color: var(--color); font-family: $font-family-base; @@ -66,10 +69,40 @@ box-sizing: border-box; } + +// Item with Color +// -------------------------------------------------- + +:host(.ion-color) .item-native { + background: current-color(base); + color: current-color(contrast); +} + +:host(.ion-color) .item-native, +:host(.ion-color) .item-inner { + border-color: current-color(shade); +} + +:host(.ion-color) .item-detail-icon { + color: current-color(shade); +} + + +// Activated Item +// -------------------------------------------------- + :host(.activated) .item-native { background: var(--background-activated); } +:host(.ion-color.activated) .item-native { + background: current-color(tint); +} + + +// Disabled Item +// -------------------------------------------------- + :host(.item-disabled) { cursor: default; opacity: .3; @@ -77,15 +110,18 @@ } +// Native Item +// -------------------------------------------------- + .item-native { + @include border-radius(var(--border-radius)); + @include margin(0); @include padding( var(--padding-top), var(--padding-end), var(--padding-bottom), calc(var(--padding-start) + var(--ion-safe-area-left, 0px)) ); - @include border-radius(var(--border-radius)); - @include margin(0); @include text-inherit(); display: flex; @@ -119,13 +155,9 @@ button, a { -webkit-user-drag: none; } -.item-state { - @include position(null, 0, 0, 0); - position: absolute; - - height: var(--highlight-height); -} +// Inner Item +// -------------------------------------------------- .item-inner { @include margin(0); @@ -138,6 +170,8 @@ button, a { display: flex; + position: relative; + flex: 1; flex-direction: inherit; align-items: inherit; @@ -154,24 +188,9 @@ button, a { box-sizing: border-box; } -.input-wrapper { - display: flex; - flex: 1; - flex-direction: inherit; - align-items: inherit; - align-self: stretch; - - text-overflow: ellipsis; - - overflow: hidden; - box-sizing: border-box; -} - -:host([vertical-align-top]), -:host(.item-input) { - align-items: flex-start; -} +// Item Slots +// ----------------------------------------- ::slotted(ion-icon) { font-size: 1.6em; @@ -190,6 +209,25 @@ button, a { // Item Input // ----------------------------------------- +:host([vertical-align-top]), +:host(.item-input) { + align-items: flex-start; +} + +.input-wrapper { + display: flex; + + flex: 1; + flex-direction: inherit; + align-items: inherit; + align-self: stretch; + + text-overflow: ellipsis; + + overflow: hidden; + box-sizing: border-box; +} + :host(.item-label-stacked) .input-wrapper, :host(.item-label-floating) .input-wrapper { flex: 1; @@ -202,6 +240,55 @@ button, a { // pointer-events: auto; // } +// Item Input Highlight +// -------------------------------------------------- + +.item-highlight, +.item-inner-highlight { + @include position(null, 0, 0, 0); + + position: absolute; + + background: var(--highlight-background); +} + +.item-highlight { + height: var(--full-highlight-height); +} + +.item-inner-highlight { + height: var(--inset-highlight-height); +} + +// Item Input Focused +// -------------------------------------------------- + +:host(.item-interactive.item-has-focus) { + --highlight-background: var(--highlight-color-focused); + + // If the item has a full border and highlight is enabled, show the full item highlight + --full-highlight-height: #{calc(var(--highlight-height) * var(--show-full-highlight))}; + + // If the item has an inset border and highlight is enabled, show the inset item highlight + --inset-highlight-height: #{calc(var(--highlight-height) * var(--show-inset-highlight))}; +} + + +// Item Input Valid +// -------------------------------------------------- + +:host(.item-interactive.ion-valid) { + --highlight-background: var(--highlight-color-valid); +} + + +// Item Input Invalid +// -------------------------------------------------- + +:host(.item-interactive.ion-invalid) { + --highlight-background: var(--highlight-color-invalid); +} + // Item Select // ----------------------------------------- diff --git a/core/src/components/item/item.tsx b/core/src/components/item/item.tsx index 41bd8aab83..53fe1e398c 100644 --- a/core/src/components/item/item.tsx +++ b/core/src/components/item/item.tsx @@ -72,9 +72,6 @@ export class Item implements ComponentInterface { */ @Prop() routerDirection?: RouterDirection; - // TODO document this - @Prop() state?: 'valid' | 'invalid' | 'focus'; - /** * The type of the button. Only used when an `onclick` or `button` property is present. * Possible values are: `"submit"`, `"reset"` and `"button"`. @@ -146,14 +143,14 @@ export class Item implements ComponentInterface { } render() { - const { href, detail, mode, win, state, detailIcon, routerDirection, type } = this; + const { href, detail, mode, win, detailIcon, routerDirection, type } = this; const clickable = this.isClickable(); const TagType = clickable ? (href === undefined ? 'button' : 'a') : 'div'; const attrs = TagType === 'button' ? { type } : { href }; const showDetail = detail !== undefined ? detail : mode === 'ios' && clickable; - return ( + return [ {showDetail && } +
- {state &&
} {clickable && mode === 'md' && } -
- ); + , +
+ ]; } } diff --git a/core/src/components/item/readme.md b/core/src/components/item/readme.md index 82e103e16f..13f278a8c0 100644 --- a/core/src/components/item/readme.md +++ b/core/src/components/item/readme.md @@ -40,6 +40,17 @@ The below chart details the item slots and where it will place the element insid Items left align text and add an ellipsis when the text is wider than the item. See the [Utility Attributes Documentation](/docs/layout/css-utilities) for attributes that can be added to `` to transform the text. +## Input Highlight + +### Highlight Height + +Items containing an input will highlight the input with a different color border when focused, valid, or invalid. By default, `md` items have a highlight with a height set to `2px` and `ios` has no highlight (technically the height is set to `0`). The height can be changed using the `--highlight-height` CSS property. To turn off the highlight, set this variable to `0`. For more information on setting CSS properties, see the [theming documentation](/docs/theming/css-variables). + +### Highlight Color + +The highlight color changes based on the item state, but all of the states use Ionic colors by default. When focused, the input highlight will use the `primary` color. If the input is valid it will use the `success` color, and invalid inputs will use the `danger` color. See the [CSS Custom Properties](#css-custom-properties) section below for the highlight color variables. + + @@ -56,35 +67,38 @@ Items left align text and add an ellipsis when the text is wider than the item. | `lines` | `lines` | How the bottom border should be displayed on the item. Available options: `"full"`, `"inset"`, `"none"`. | `"full"`, `"inset"`, `"none"` | | `mode` | `mode` | The mode determines which platform styles to use. Possible values are: `"ios"` or `"md"`. | `Mode` | | `routerDirection` | `router-direction` | When using a router, it specifies the transition direction when navigating to another page using `href`. | `RouterDirection` | -| `state` | `state` | | `"valid"`, `"invalid"`, `"focus"` | | `type` | `type` | The type of the button. Only used when an `onclick` or `button` property is present. Possible values are: `"submit"`, `"reset"` and `"button"`. Default value is: `"button"` | `"submit"`, `"reset"`, `"button"` | ## CSS Custom Properties -| Name | Description | -| ------------------------ | -------------------------------- | -| `--background` | Background of the item | -| `--background-activated` | Background of the activated item | -| `--border-color` | Color of the item border | -| `--border-radius` | Radius of the item border | -| `--border-style` | Style of the item border | -| `--border-width` | Width of the item border | -| `--box-shadow` | Box shadow of the item | -| `--color` | Color of the item | -| `--detail-icon-color` | Color of the item detail icon | -| `--inner-border-width` | Width of the item inner border | -| `--inner-box-shadow` | Box shadow of the item inner | -| `--inner-padding-bottom` | Bottom padding of the item inner | -| `--inner-padding-end` | End padding of the item inner | -| `--inner-padding-start` | Start padding of the item inner | -| `--inner-padding-top` | Top padding of the item inner | -| `--min-height` | Minimum height of the item | -| `--padding-bottom` | Bottom padding of the item | -| `--padding-end` | End padding of the item | -| `--padding-start` | Start padding of the item | -| `--padding-top` | Top padding of the item | -| `--transition` | Transition of the item | +| Name | Description | +| --------------------------- | --------------------------------------------------- | +| `--background` | Background of the item | +| `--background-activated` | Background of the activated item | +| `--border-color` | Color of the item border | +| `--border-radius` | Radius of the item border | +| `--border-style` | Style of the item border | +| `--border-width` | Width of the item border | +| `--box-shadow` | Box shadow of the item | +| `--color` | Color of the item | +| `--detail-icon-color` | Color of the item detail icon | +| `--highlight-color-focused` | The color of the highlight on the item when focused | +| `--highlight-color-invalid` | The color of the highlight on the item when invalid | +| `--highlight-color-valid` | The color of the highlight on the item when valid | +| `--highlight-height` | The height of the highlight on the item | +| `--inner-border-width` | Width of the item inner border | +| `--inner-box-shadow` | Box shadow of the item inner | +| `--inner-padding-bottom` | Bottom padding of the item inner | +| `--inner-padding-end` | End padding of the item inner | +| `--inner-padding-start` | Start padding of the item inner | +| `--inner-padding-top` | Top padding of the item inner | +| `--min-height` | Minimum height of the item | +| `--padding-bottom` | Bottom padding of the item | +| `--padding-end` | End padding of the item | +| `--padding-start` | Start padding of the item | +| `--padding-top` | Top padding of the item | +| `--transition` | Transition of the item | ----------------------------------------------