feat(item): add helper text, error text, counter, shape, and fill mode (#23354)

resolves #19619
This commit is contained in:
William Martin
2021-06-17 17:21:03 -04:00
committed by GitHub
parent dc48a9f1a2
commit faefe97da6
21 changed files with 1099 additions and 52 deletions

View File

@ -368,8 +368,8 @@ export class IonInput {
}
export declare interface IonItem extends Components.IonItem {
}
@ProxyCmp({ inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] })
@Component({ selector: "ion-item", changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] })
@ProxyCmp({ inputs: ["button", "color", "counter", "detail", "detailIcon", "disabled", "download", "fill", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "shape", "target", "type"] })
@Component({ selector: "ion-item", changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>", inputs: ["button", "color", "counter", "detail", "detailIcon", "disabled", "download", "fill", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "shape", "target", "type"] })
export class IonItem {
protected el: HTMLElement;
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {

View File

@ -512,16 +512,19 @@ ion-input,css-prop,--placeholder-opacity
ion-item,shadow
ion-item,prop,button,boolean,false,false,false
ion-item,prop,color,string | undefined,undefined,false,true
ion-item,prop,counter,boolean,true,false,false
ion-item,prop,detail,boolean | undefined,undefined,false,false
ion-item,prop,detailIcon,string,'chevron-forward',false,false
ion-item,prop,disabled,boolean,false,false,false
ion-item,prop,download,string | undefined,undefined,false,false
ion-item,prop,fill,"outline" | "solid" | undefined,undefined,false,false
ion-item,prop,href,string | undefined,undefined,false,false
ion-item,prop,lines,"full" | "inset" | "none" | undefined,undefined,false,false
ion-item,prop,mode,"ios" | "md",undefined,false,false
ion-item,prop,rel,string | undefined,undefined,false,false
ion-item,prop,routerAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
ion-item,prop,routerDirection,"back" | "forward" | "root",'forward',false,false
ion-item,prop,shape,"round" | undefined,undefined,false,false
ion-item,prop,target,string | undefined,undefined,false,false
ion-item,prop,type,"button" | "reset" | "submit",'button',false,false
ion-item,css-prop,--background

View File

@ -1013,6 +1013,10 @@ export namespace Components {
* The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
*/
"color"?: Color;
/**
* If `true`, a character counter will display the ratio of characters used and the total character limit. Only applies when the `maxlength` property is set on the inner `ion-input` or `ion-textarea`.
*/
"counter": boolean;
/**
* If `true`, a detail arrow will appear on the item. Defaults to `false` unless the `mode` is `ios` and an `href` or `button` property is present.
*/
@ -1029,6 +1033,10 @@ export namespace Components {
* This attribute instructs browsers to download a URL instead of navigating to it, so the user will be prompted to save it as a local file. If the attribute has a value, it is used as the pre-filled file name in the Save prompt (the user can still change the file name if they want).
*/
"download": string | undefined;
/**
* The fill for the item. If `'solid'` the item will have a background. If `'outline'` the item will be transparent with a border. Only available in `md` mode.
*/
"fill"?: 'outline' | 'solid';
/**
* Contains a URL or a URL fragment that the hyperlink points to. If this property is set, an anchor tag will be rendered.
*/
@ -1053,6 +1061,10 @@ export namespace Components {
* When using a router, it specifies the transition direction when navigating to another page using `href`.
*/
"routerDirection": RouterDirection;
/**
* The shape of the item. If "round" it will have increased border radius.
*/
"shape"?: 'round';
/**
* Specifies where to display the linked URL. Only applies when an `href` is provided. Special keywords: `"_blank"`, `"_self"`, `"_parent"`, `"_top"`.
*/
@ -4476,6 +4488,10 @@ declare namespace LocalJSX {
* The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
*/
"color"?: Color;
/**
* If `true`, a character counter will display the ratio of characters used and the total character limit. Only applies when the `maxlength` property is set on the inner `ion-input` or `ion-textarea`.
*/
"counter"?: boolean;
/**
* If `true`, a detail arrow will appear on the item. Defaults to `false` unless the `mode` is `ios` and an `href` or `button` property is present.
*/
@ -4492,6 +4508,10 @@ declare namespace LocalJSX {
* This attribute instructs browsers to download a URL instead of navigating to it, so the user will be prompted to save it as a local file. If the attribute has a value, it is used as the pre-filled file name in the Save prompt (the user can still change the file name if they want).
*/
"download"?: string | undefined;
/**
* The fill for the item. If `'solid'` the item will have a background. If `'outline'` the item will be transparent with a border. Only available in `md` mode.
*/
"fill"?: 'outline' | 'solid';
/**
* Contains a URL or a URL fragment that the hyperlink points to. If this property is set, an anchor tag will be rendered.
*/
@ -4516,6 +4536,10 @@ declare namespace LocalJSX {
* When using a router, it specifies the transition direction when navigating to another page using `href`.
*/
"routerDirection"?: RouterDirection;
/**
* The shape of the item. If "round" it will have increased border radius.
*/
"shape"?: 'round';
/**
* Specifies where to display the linked URL. Only applies when an `href` is provided. Special keywords: `"_blank"`, `"_self"`, `"_parent"`, `"_top"`.
*/

View File

@ -623,6 +623,7 @@ graph TD;
ion-button --> ion-ripple-effect
ion-item --> ion-icon
ion-item --> ion-ripple-effect
ion-item --> ion-note
ion-segment-button --> ion-ripple-effect
style ion-datetime fill:#f9f,stroke:#333,stroke-width:4px
```

View File

@ -19,11 +19,11 @@
--background-hover-opacity: .04;
--border-color: #{$item-ios-border-bottom-color};
--color: #{$item-ios-color};
--highlight-height: 0;
--highlight-height: 0px;
--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};
--bottom-padding-start: 0px;
font-size: $item-ios-font-size;
}
@ -88,6 +88,18 @@
--show-inset-highlight: 0;
}
.item-highlight,
.item-inner-highlight {
transition: none;
}
:host(.item-has-focus) .item-inner-highlight,
:host(.item-has-focus) .item-highlight {
border-top: none;
border-right: none;
border-left: none;
}
// iOS Item Slots
// --------------------------------------------------
@ -212,6 +224,12 @@
--padding-start: 0;
}
// Item Bottom
// --------------------------------------------------
:host(.item-has-start-slot) .item-bottom {
--bottom-padding-start: 48px;
}
// FROM TEXTAREA
// iOS Stacked & Floating Textarea

View File

@ -18,7 +18,6 @@
--color: #{$item-md-color};
--transition: opacity 15ms linear, background-color 15ms linear;
--padding-start: #{$item-md-padding-start};
--border-color: #{$item-md-border-bottom-color};
--inner-padding-end: #{$item-md-padding-end};
--inner-border-width: #{0 0 $item-md-border-bottom-width 0};
--highlight-height: 2px;
@ -42,6 +41,9 @@
}
}
:host(.item-has-focus) .item-native {
caret-color: var(--highlight-color-focused);
}
// Material Design Item Lines
// --------------------------------------------------
@ -83,6 +85,23 @@
--show-inset-highlight: 0;
}
/**
* When `fill="outline"`, reposition the highlight element to cover everything but the `.item-bottom`
*/
:host(.item-fill-outline) .item-highlight {
--position-offset: calc(-1 * var(--border-width));
@include position(var(--position-offset), null, null, var(--position-offset));
width: calc(100% + 2 * var(--border-width));
height: calc(100% + 2 * var(--border-width));
transition: none;
}
:host(.item-fill-outline.item-has-focus) .item-native {
border-color: transparent;
}
// Material Design Multi-line Item
// --------------------------------------------------
@ -107,6 +126,13 @@
@include margin-horizontal($item-md-end-slot-margin-start, $item-md-end-slot-margin-end);
}
:host(.item-fill-solid) ::slotted([slot="start"]),
:host(.item-fill-solid) ::slotted([slot="end"]),
:host(.item-fill-outline) ::slotted([slot="start"]),
:host(.item-fill-outline) ::slotted([slot="end"]) {
align-self: center;
}
// Material Design Slotted Icon
// --------------------------------------------------
@ -117,7 +143,7 @@
font-size: $item-md-icon-slot-font-size;
}
:host(.ion-color) ::slotted(ion-icon) {
:host(.ion-color:not(.item-fill-solid):not(.item-fill-outline)) ::slotted(ion-icon) {
color: current-color(contrast);
}
@ -133,6 +159,11 @@
@include margin-horizontal($item-md-icon-end-slot-margin-start, $item-md-icon-end-slot-margin-end);
}
:host(.item-fill-solid) ::slotted(ion-icon[slot="start"]),
:host(.item-fill-outline) ::slotted(ion-icon[slot="start"]) {
@include margin-horizontal($item-md-icon-start-slot-margin-start, $item-md-input-icon-start-slot-margin-end);
}
// Material Design Slotted Toggle
// --------------------------------------------------
@ -154,7 +185,7 @@
font-size: $item-md-note-slot-font-size;
}
::slotted(ion-note[slot]) {
::slotted(ion-note[slot]:not([slot="helper"]):not([slot="error"])) {
@include padding($item-md-note-slot-padding-top, $item-md-note-slot-padding-end, $item-md-note-slot-padding-bottom, $item-md-note-slot-padding-start);
}
@ -292,3 +323,97 @@
:host(.item-label-color) {
--highlight-color-focused: #{current-color(base)};
}
:host(.item-fill-solid.ion-color),
:host(.item-fill-outline.ion-color) {
--highlight-background: #{current-color(base)};
--highlight-color-focused: #{current-color(base)};
}
// Material Design Item: Fill Solid
// --------------------------------------------------
:host(.item-fill-solid) {
--background: #{$item-md-input-fill-solid-background-color};
--background-hover: #{$item-md-input-fill-solid-background-color-hover};
--background-focused: #{$item-md-input-fill-solid-background-color-focus};
--border-width: 0 0 #{$item-md-border-bottom-width} 0;
--inner-border-width: 0;
@include border-radius(4px, 4px, 0, 0);
}
:host(.item-fill-solid) .item-native {
--border-color: #{$item-md-input-fill-border-color};
}
:host(.item-fill-solid) .item-native:hover {
--background: var(--background-hover);
--border-color: #{$item-md-input-fill-border-color-hover};
}
:host(.item-fill-solid.item-has-focus) .item-native {
--background: var(--background-focused);
border-bottom-color: var(--highlight-color-focused);
}
:host(.item-fill-solid.item-shape-round) {
@include border-radius(16px, 16px, 0, 0);
}
// Material Design Item: Fill Outline
// --------------------------------------------------
:host(.item-fill-outline) {
--border-color: #{$item-md-input-fill-border-color};
--border-width: #{$item-md-border-bottom-width};
border: none;
overflow: visible;
}
:host(.item-fill-outline) .item-native {
--native-padding-left: 16px;
@include border-radius(4px);
}
:host(.item-fill-outline) .item-native:hover {
--border-color: #{$item-md-input-fill-border-color-hover};
}
:host(.item-fill-outline.item-shape-round) .item-native {
--inner-padding-start: 16px;
@include border-radius(28px);
}
:host(.item-fill-outline.item-shape-round) .item-bottom {
@include padding-horizontal(32px, null);
}
:host(.item-fill-outline.item-label-floating.item-has-focus) .item-native ::slotted(ion-input:not(:first-child)),
:host(.item-fill-outline.item-label-floating.item-has-value) .item-native ::slotted(ion-input:not(:first-child)),
:host(.item-fill-outline.item-label-floating.item-has-focus) .item-native ::slotted(ion-textarea:not(:first-child)),
:host(.item-fill-outline.item-label-floating.item-has-value) .item-native ::slotted(ion-textarea:not(:first-child)) {
transform: translateY(-25%);
}
// Material Design Item: Invalid
// --------------------------------------------------
:host(.item-fill-outline.ion-invalid:not(.ion-color)) .item-native,
:host(.item-fill-solid.ion-invalid:not(.ion-color)) .item-native {
caret-color: var(--highlight-color-invalid);
}
:host(.item-fill-outline.ion-invalid),
:host(.item-fill-outline.ion-invalid) .item-native,
:host(.item-fill-outline.ion-invalid:not(.ion-color)) .item-highlight,
:host(.item-fill-solid.ion-invalid:not(.ion-color)),
:host(.item-fill-solid.ion-invalid:not(.ion-color)) .item-native,
:host(.item-fill-solid.ion-invalid:not(.ion-color)) .item-highlight {
border-color: var(--highlight-color-invalid);
}

View File

@ -51,6 +51,18 @@ $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;
// Item Input
// --------------------------------------------------
/// @prop - Color of the item input background
$item-md-input-fill-solid-background-color: $background-color-step-50 !default;
/// @prop - Color of the item input background when hovered
$item-md-input-fill-solid-background-color-hover: $background-color-step-100 !default;
/// @prop - Color of the item input background when focused
$item-md-input-fill-solid-background-color-focus: $background-color-step-150 !default;
/// @prop - Color of the item input highlight
$item-md-input-highlight-color: ion-color(primary, base) !default;
@ -60,6 +72,12 @@ $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;
/// @prop - Color of the item border when `fill` is set
$item-md-input-fill-border-color: $background-color-step-500 !default;
/// @prop - Color of the item border when `fill` is set and hovered
$item-md-input-fill-border-color-hover: $background-color-step-750 !default;
// Item Label
// --------------------------------------------------
@ -76,6 +94,12 @@ $item-md-label-margin-bottom: 10px !default;
/// @prop - Margin start of the label
$item-md-label-margin-start: 0 !default;
/// @prop - X translation for floating labels
$item-md-fill-outline-label-translate-x: -32px !default;
/// @prop - Padding for floating labels
$item-md-fill-outline-label-padding: 4px !default;
// Item Slots
// --------------------------------------------------
@ -126,6 +150,9 @@ $item-md-icon-start-slot-margin-start: null !default;
/// @prop - Margin end for an icon in the start slot
$item-md-icon-start-slot-margin-end: 32px !default;
/// @prop - Margin end for an icon in the start slot
$item-md-input-icon-start-slot-margin-end: 8px !default;
/// @prop - Margin start for an icon in the end slot
$item-md-icon-end-slot-margin-start: 16px !default;

View File

@ -99,13 +99,13 @@
// Item: Color
// --------------------------------------------------
:host(.ion-color) .item-native {
:host(.ion-color:not(.item-fill-solid):not(.item-fill-outline)) .item-native {
background: current-color(base);
color: current-color(contrast);
}
:host(.ion-color) .item-native,
:host(.ion-color) .item-inner {
:host(.ion-color:not(.item-fill-solid):not(.item-fill-outline)) .item-native,
:host(.ion-color:not(.item-fill-solid):not(.item-fill-outline)) .item-inner {
border-color: current-color(shade);
}
@ -352,21 +352,53 @@ button, a {
.item-highlight,
.item-inner-highlight {
@include position(null, 0, 0, 0);
@include position(0, 0, 0, 0);
@include border-radius(inherit);
position: absolute;
background: var(--highlight-background);
width: 100%;
z-index: 1;
height: 100%;
transform: scaleX(0);
transition: transform 200ms, border-bottom-width 200ms;
z-index: 2;
box-sizing: border-box;
pointer-events: none;
}
.item-highlight {
height: var(--full-highlight-height);
:host(.item-has-focus) .item-highlight,
:host(.item-has-focus) .item-inner-highlight {
transform: scaleX(1);
border-style: var(--border-style);
border-color: var(--highlight-background);
}
.item-inner-highlight {
height: var(--inset-highlight-height);
:host(.item-has-focus) .item-highlight {
border-width: var(--full-highlight-height);
opacity: var(--show-full-highlight);
}
:host(.item-has-focus) .item-inner-highlight {
border-bottom-width: var(--inset-highlight-height);
opacity: var(--show-inset-highlight);
}
:host(.item-has-focus.item-fill-solid) .item-highlight {
border-width: calc(var(--full-highlight-height) - 1px);
}
:host(.item-has-focus) .item-inner-highlight,
:host(.item-has-focus:not(.item-fill-outline)) .item-highlight {
border-top: none;
border-right: none;
border-left: none;
}
@ -403,6 +435,13 @@ button, a {
--highlight-background: var(--highlight-color-invalid);
}
:host(.item-interactive.ion-invalid) ::slotted([slot="helper"]) {
display: none;
}
:host(.item-interactive.ion-invalid) ::slotted([slot="error"]) {
display: block;
}
// Item Select
// --------------------------------------------------
@ -476,3 +515,42 @@ button, a {
ion-ripple-effect {
color: var(--ripple-color);
}
// Item Button Ripple effect
// --------------------------------------------------
.item-bottom {
@include margin(0);
@include padding(
var(--padding-top),
var(--inner-padding-end),
var(--padding-bottom),
calc(var(--padding-start) + var(--ion-safe-area-left, 0px) + var(--bottom-padding-start, 0px))
);
display: flex;
justify-content: space-between;
}
:host(.item-fill-solid) ::slotted([slot="start"]),
:host(.item-fill-solid) ::slotted([slot="end"]),
:host(.item-fill-outline) ::slotted([slot="start"]),
:host(.item-fill-outline) ::slotted([slot="end"]) {
align-self: center;
}
::slotted([slot="helper"]),
::slotted([slot="error"]),
.item-counter {
padding-top: 5px;
font-size: 12px;
z-index: 1;
}
::slotted([slot="error"]) {
display: none;
color: var(--highlight-color-invalid);
}

View File

@ -5,6 +5,7 @@ import { AnimationBuilder, Color, CssClassMap, RouterDirection, StyleEventDetail
import { AnchorInterface, ButtonInterface } from '../../utils/element-interface';
import { raf } from '../../utils/helpers';
import { createColorClasses, hostContext, openURL } from '../../utils/theme';
import { InputChangeEventDetail } from '../input/input-interface';
/**
* @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use.
@ -72,6 +73,17 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
*/
@Prop() download: string | undefined;
/**
* The fill for the item. If `'solid'` the item will have a background. If
* `'outline'` the item will be transparent with a border. Only available in `md` mode.
*/
@Prop() fill?: 'outline' | 'solid';
/**
* The shape of the item. If "round" it will have increased
* border radius.
*/
@Prop() shape?: 'round';
/**
* Contains a URL or a URL fragment that the hyperlink points to.
* If this property is set, an anchor tag will be rendered.
@ -89,6 +101,11 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
*/
@Prop() lines?: 'full' | 'inset' | 'none';
/**
* If `true`, a character counter will display the ratio of characters used and the total character limit. Only applies when the `maxlength` property is set on the inner `ion-input` or `ion-textarea`.
*/
@Prop() counter = true;
/**
* When using a router, it specifies the transition animation when navigating to
* another page using `href`.
@ -113,6 +130,15 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
*/
@Prop() type: 'submit' | 'reset' | 'button' = 'button';
@State() counterString: string | null | undefined;
@Listen('ionChange')
handleIonChange(ev: CustomEvent<InputChangeEventDetail>) {
if (this.counter && ev.target === this.getFirstInput()) {
this.updateCounterOutput(ev.target as HTMLIonInputElement | HTMLIonTextareaElement);
}
}
@Listen('ionColor')
labelColorChanged(ev: CustomEvent<string>) {
const { color } = this;
@ -153,6 +179,14 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
}
}
connectedCallback() {
if (this.counter) {
this.updateCounterOutput(this.getFirstInput());
}
this.hasStartEl();
}
componentDidUpdate() {
// Do not use @Listen here to avoid making all items
// appear as clickable to screen readers
@ -239,7 +273,7 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
// Only focus the first input if we clicked on an ion-item
// and the first input exists
if (clickedItem && firstActive) {
if (clickedItem && (firstActive || !this.multipleInputs)) {
input.fireFocusEvents = false;
input.setBlur();
input.setFocus();
@ -249,12 +283,27 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
}
}
private updateCounterOutput(inputEl: HTMLIonInputElement | HTMLIonTextareaElement) {
if (this.counter && !this.multipleInputs && inputEl?.maxlength !== undefined) {
const length = inputEl?.value?.toString().length ?? '0';
this.counterString = `${length}/${inputEl.maxlength}`;
}
}
private hasStartEl() {
const startEl = this.el.querySelector('[slot="start"]');
if (startEl !== null) {
this.el.classList.add('item-has-start-slot');
}
}
render() {
const { detail, detailIcon, download, labelColorStyles, lines, disabled, href, rel, target, routerAnimation, routerDirection } = this;
const { counterString, detail, detailIcon, download, fill, labelColorStyles, lines, disabled, href, rel, shape, target, routerAnimation, routerDirection } = this;
const childStyles = {};
const mode = getIonMode(this);
const clickable = this.isClickable();
const canActivate = this.canActivate();
const hasFill = fill === 'outline' || fill === 'solid';
const TagType = clickable ? (href === undefined ? 'button' : 'a') : 'div' as any;
const attrs = (TagType === 'button')
? { type: this.type }
@ -284,33 +333,42 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
'item': true,
[mode]: true,
[`item-lines-${lines}`]: lines !== undefined,
[`item-fill-${fill}`]: fill !== undefined,
[`item-shape-${shape}`]: shape !== undefined,
'item-disabled': disabled,
'in-list': hostContext('ion-list', this.el),
'item-multiple-inputs': this.multipleInputs,
'ion-activatable': canActivate,
'ion-focusable': true,
'item-rtl': document.dir === 'rtl'
})
}}
>
<TagType
{...attrs}
class="item-native"
part="native"
disabled={disabled}
{...clickFn}
>
<slot name="start"></slot>
<div class="item-inner">
<div class="input-wrapper">
<slot></slot>
</div>
<slot name="end"></slot>
{showDetail && <ion-icon icon={detailIcon} lazy={false} class="item-detail-icon" part="detail-icon" aria-hidden="true"></ion-icon>}
<div class="item-inner-highlight"></div>
<TagType
{...attrs}
class="item-native"
part="native"
disabled={disabled}
{...clickFn}
>
<slot name="start"></slot>
<div class="item-inner">
<div class="input-wrapper">
<slot></slot>
</div>
{canActivate && mode === 'md' && <ion-ripple-effect></ion-ripple-effect>}
</TagType>
<div class="item-highlight"></div>
<slot name="end"></slot>
{showDetail && <ion-icon icon={detailIcon} lazy={false} class="item-detail-icon" part="detail-icon" aria-hidden="true"></ion-icon>}
<div class="item-inner-highlight"></div>
</div>
{canActivate && mode === 'md' && <ion-ripple-effect></ion-ripple-effect>}
{hasFill && <div class="item-highlight"></div>}
</TagType>
{!hasFill && <div class="item-highlight"></div>}
<div class="item-bottom">
<slot name="error"></slot>
<slot name="helper"></slot>
{counterString && <ion-note class="item-counter">{counterString}</ion-note>}
</div>
</Host>
);
}

View File

@ -369,6 +369,23 @@ The highlight color changes based on the item state, but all of the states use I
<ion-input placeholder="Placeholder"></ion-input>
</ion-item>
<ion-item fill="solid">
<ion-label position="floating">Input (Fill: Solid)</ion-label>
<ion-input></ion-input>
</ion-item>
<ion-item fill="outline">
<ion-label position="floating">Input (Fill: Outline)</ion-label>
<ion-input></ion-input>
</ion-item>
<ion-item>
<ion-label>Helper and Error Text</ion-label>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
<ion-note slot="error">Error Text</ion-note>
</ion-item>
<ion-item>
<ion-label>Checkbox</ion-label>
<ion-checkbox slot="start"></ion-checkbox>
@ -691,6 +708,23 @@ The highlight color changes based on the item state, but all of the states use I
<ion-input placeholder="Placeholder"></ion-input>
</ion-item>
<ion-item fill="solid">
<ion-label position="floating">Input (Fill: Solid)</ion-label>
<ion-input></ion-input>
</ion-item>
<ion-item fill="outline">
<ion-label position="floating">Input (Fill: Outline)</ion-label>
<ion-input></ion-input>
</ion-item>
<ion-item>
<ion-label>Helper and Error Text</ion-label>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
<ion-note slot="error">Error Text</ion-note>
</ion-item>
<ion-item>
<ion-label>Checkbox</ion-label>
<ion-checkbox slot="start"></ion-checkbox>
@ -707,7 +741,7 @@ The highlight color changes based on the item state, but all of the states use I
```tsx
import React from 'react';
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonItem, IonLabel, IonList, IonText, IonAvatar, IonThumbnail, IonButton, IonIcon, IonDatetime, IonSelect, IonSelectOption, IonToggle, IonInput, IonCheckbox, IonRange } from '@ionic/react';
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonItem, IonLabel, IonList, IonText, IonAvatar, IonThumbnail, IonButton, IonIcon, IonDatetime, IonSelect, IonSelectOption, IonToggle, IonInput, IonCheckbox, IonRange, IonNote } from '@ionic/react';
import { closeCircle, home, star, navigate, informationCircle, checkmarkCircle, shuffle } from 'ionicons/icons';
export const ItemExamples: React.FC = () => {
@ -998,6 +1032,23 @@ export const ItemExamples: React.FC = () => {
<IonInput placeholder="Placeholder"></IonInput>
</IonItem>
<IonItem fill="solid">
<IonLabel position="stacked">Input (Fill: Solid)</IonLabel>
<IonInput></IonInput>
</IonItem>
<IonItem fill="outline">
<IonLabel position="floating">Input (Fill: Outline)</IonLabel>
<IonInput></IonInput>
</IonItem>
<IonItem>
<IonLabel>Helper and Error Text</IonLabel>
<IonInput></IonInput>
<IonNote slot="helper">Helper Text</IonNote>
<IonNote slot="error">Error Text</IonNote>
</IonItem>
<IonItem>
<IonLabel>Checkbox</IonLabel>
<IonCheckbox slot="start" />
@ -1427,6 +1478,23 @@ export class ItemExample {
<ion-input placeholder="Placeholder"></ion-input>
</ion-item>,
<ion-item fill="solid">
<ion-label position="floating">Input (Fill: Solid)</ion-label>
<ion-input></ion-input>
</ion-item>,
<ion-item fill="outline">
<ion-label position="floating">Input (Fill: Outline)</ion-label>
<ion-input></ion-input>
</ion-item>,
<ion-item>
<ion-label>Helper and Error Text</ion-label>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
<ion-note slot="error">Error Text</ion-note>
</ion-item>,
<ion-item>
<ion-label>Checkbox</ion-label>
<ion-checkbox slot="start"></ion-checkbox>
@ -1767,6 +1835,23 @@ export class ItemExample {
<ion-input placeholder="Placeholder"></ion-input>
</ion-item>
<ion-item fill="solid">
<ion-label position="floating">Input (Fill: Solid)</ion-label>
<ion-input></ion-input>
</ion-item>
<ion-item fill="outline">
<ion-label position="floating">Input (Fill: Outline)</ion-label>
<ion-input></ion-input>
</ion-item>
<ion-item>
<ion-label>Helper and Error Text</ion-label>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
<ion-note slot="error">Error Text</ion-note>
</ion-item>
<ion-item>
<ion-label>Checkbox</ion-label>
<ion-checkbox slot="start"></ion-checkbox>
@ -1784,6 +1869,7 @@ import {
IonButton,
IonCheckbox,
IonDatetime,
IonNote,
IonIcon,
IonInput,
IonItem,
@ -1812,6 +1898,7 @@ export default defineComponent({
IonButton,
IonCheckbox,
IonDatetime,
IonNote,
IonIcon,
IonInput,
IonItem,
@ -1846,16 +1933,19 @@ export default defineComponent({
| ----------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | ------------------- |
| `button` | `button` | If `true`, a button tag will be rendered and the item will be tappable. | `boolean` | `false` |
| `color` | `color` | The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). | `string \| undefined` | `undefined` |
| `counter` | `counter` | If `true`, a character counter will display the ratio of characters used and the total character limit. Only applies when the `maxlength` property is set on the inner `ion-input` or `ion-textarea`. | `boolean` | `true` |
| `detail` | `detail` | If `true`, a detail arrow will appear on the item. Defaults to `false` unless the `mode` is `ios` and an `href` or `button` property is present. | `boolean \| undefined` | `undefined` |
| `detailIcon` | `detail-icon` | The icon to use when `detail` is set to `true`. | `string` | `'chevron-forward'` |
| `disabled` | `disabled` | If `true`, the user cannot interact with the item. | `boolean` | `false` |
| `download` | `download` | This attribute instructs browsers to download a URL instead of navigating to it, so the user will be prompted to save it as a local file. If the attribute has a value, it is used as the pre-filled file name in the Save prompt (the user can still change the file name if they want). | `string \| undefined` | `undefined` |
| `fill` | `fill` | The fill for the item. If `'solid'` the item will have a background. If `'outline'` the item will be transparent with a border. Only available in `md` mode. | `"outline" \| "solid" \| undefined` | `undefined` |
| `href` | `href` | Contains a URL or a URL fragment that the hyperlink points to. If this property is set, an anchor tag will be rendered. | `string \| undefined` | `undefined` |
| `lines` | `lines` | How the bottom border should be displayed on the item. | `"full" \| "inset" \| "none" \| undefined` | `undefined` |
| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` |
| `rel` | `rel` | Specifies the relationship of the target object to the link object. The value is a space-separated list of [link types](https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types). | `string \| undefined` | `undefined` |
| `routerAnimation` | -- | When using a router, it specifies the transition animation when navigating to another page using `href`. | `((baseEl: any, opts?: any) => Animation) \| undefined` | `undefined` |
| `routerDirection` | `router-direction` | When using a router, it specifies the transition direction when navigating to another page using `href`. | `"back" \| "forward" \| "root"` | `'forward'` |
| `shape` | `shape` | The shape of the item. If "round" it will have increased border radius. | `"round" \| undefined` | `undefined` |
| `target` | `target` | Specifies where to display the linked URL. Only applies when an `href` is provided. Special keywords: `"_blank"`, `"_self"`, `"_parent"`, `"_top"`. | `string \| undefined` | `undefined` |
| `type` | `type` | The type of the button. Only used when an `onclick` or `button` property is present. | `"button" \| "reset" \| "submit"` | `'button'` |
@ -1929,12 +2019,14 @@ export default defineComponent({
- ion-icon
- [ion-ripple-effect](../ripple-effect)
- [ion-note](../note)
### Graph
```mermaid
graph TD;
ion-item --> ion-icon
ion-item --> ion-ripple-effect
ion-item --> ion-note
ion-datetime --> ion-item
ion-select-popover --> ion-item
style ion-item fill:#f9f,stroke:#333,stroke-width:4px

View File

@ -0,0 +1,427 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<title>Item - Fill</title>
<meta name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script></head>
<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Item - Fill</ion-title>
</ion-toolbar>
</ion-header>
<ion-content id="content" class="ion-padding">
<p>Filled</p>
<ion-grid>
<ion-row>
<ion-col>
<ion-item fill="solid">
<ion-label position="floating">Standard</ion-label>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
<ion-note slot="error">Error Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item fill="solid">
<ion-icon slot="start" name="calendar-clear-outline"></ion-icon>
<ion-label position="floating">Standard</ion-label>
<ion-input value="Has Initial Value"></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
<ion-note slot="error">Error Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item fill="solid">
<ion-label position="floating">Standard</ion-label>
<ion-icon slot="end" name="trash"></ion-icon>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
</ion-row>
<ion-row>
<ion-col>
<ion-item class="ion-invalid" fill="solid">
<ion-label position="floating">Standard</ion-label>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
<ion-note slot="error">Error Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item class="ion-invalid" fill="solid">
<ion-icon slot="start" name="calendar-clear-outline"></ion-icon>
<ion-label position="floating">Standard</ion-label>
<ion-input value="Has Initial Value"></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
<ion-note slot="error">Error Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item class="ion-invalid" fill="solid">
<ion-label position="floating">Standard</ion-label>
<ion-icon slot="end" name="trash"></ion-icon>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
</ion-row>
</ion-grid>
<p>Shaped Filled</p>
<ion-grid>
<ion-row>
<ion-col>
<ion-item fill="solid" shape="round">
<ion-label position="floating">Standard</ion-label>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item fill="solid" shape="round">
<ion-icon slot="start" name="calendar-clear-outline"></ion-icon>
<ion-label position="floating">Standard</ion-label>
<ion-input value="Has Initial Value"></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item fill="solid" shape="round">
<ion-label position="floating">Standard</ion-label>
<ion-icon slot="end" name="trash"></ion-icon>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
</ion-row>
<ion-row>
<ion-col>
<ion-item class="ion-invalid" fill="solid" shape="round">
<ion-label position="floating">Standard</ion-label>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
<ion-note slot="error">Error Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item class="ion-invalid" fill="solid" shape="round">
<ion-icon slot="start" name="calendar-clear-outline"></ion-icon>
<ion-label position="floating">Standard</ion-label>
<ion-input value="Has Initial Value"></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
<ion-note slot="error">Error Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item class="ion-invalid" fill="solid" shape="round">
<ion-label position="floating">Standard</ion-label>
<ion-icon slot="end" name="trash"></ion-icon>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
</ion-row>
</ion-grid>
<p>Outlined</p>
<ion-grid>
<ion-row>
<ion-col>
<ion-item fill="outline">
<ion-label position="floating">Standards</ion-label>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item fill="outline">
<ion-icon slot="start" name="calendar-clear-outline"></ion-icon>
<ion-label position="floating">Standard</ion-label>
<ion-input value="Has Initial Value"></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item fill="outline">
<ion-label position="floating">Standard</ion-label>
<ion-icon slot="end" name="trash"></ion-icon>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
</ion-row>
<ion-row>
<ion-col>
<ion-item class="ion-invalid" fill="outline">
<ion-label position="floating">Standards</ion-label>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
<ion-note slot="error">Error Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item class="ion-invalid" fill="outline">
<ion-icon slot="start" name="calendar-clear-outline"></ion-icon>
<ion-label position="floating">Standard</ion-label>
<ion-input value="Has Initial Value"></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
<ion-note slot="error">Error Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item class="ion-invalid" fill="outline">
<ion-label position="floating">Standard</ion-label>
<ion-icon slot="end" name="trash"></ion-icon>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
</ion-row>
</ion-grid>
<p>Shaped Outlined</p>
<ion-grid>
<ion-row>
<ion-col>
<ion-item fill="outline" shape="round">
<ion-label position="floating">Standard</ion-label>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item fill="outline" shape="round">
<ion-icon slot="start" name="calendar-clear-outline"></ion-icon>
<ion-label position="floating">Standard</ion-label>
<ion-input value="Has Initial Value"></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item fill="outline" shape="round">
<ion-label position="floating">Standard</ion-label>
<ion-icon slot="end" name="trash"></ion-icon>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
</ion-row>
<ion-row>
<ion-col>
<ion-item class="ion-invalid" fill="outline" shape="round">
<ion-label position="floating">Standard</ion-label>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
<ion-note slot="error">Error Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item class="ion-invalid" fill="outline" shape="round">
<ion-icon slot="start" name="calendar-clear-outline"></ion-icon>
<ion-label position="floating">Standard</ion-label>
<ion-input value="Has Initial Value"></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
<ion-note slot="error">Error Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item class="ion-invalid" fill="outline" shape="round">
<ion-label position="floating">Standard</ion-label>
<ion-icon slot="end" name="trash"></ion-icon>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
</ion-row>
</ion-grid>
<p>Input Without Label</p>
<ion-grid>
<ion-row>
<ion-col>
<ion-item fill="solid">
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item fill="outline">
<ion-input value="Has Initial Value"></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item fill="outline" shape="round">
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
</ion-row>
</ion-grid>
<p>Input With Character Counter</p>
<ion-grid>
<ion-row>
<ion-col>
<ion-item fill="solid" counter="true">
<ion-input maxlength="10"></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item fill="outline" counter="true">
<ion-input value="Has Initial Value" maxlength="20"></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item fill="outline" shape="round" counter="true">
<ion-input maxlength="10"></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
</ion-row>
</ion-grid>
<p>Disable</p>
<ion-grid>
<ion-row>
<ion-col>
<ion-item disabled fill="solid">
<ion-label position="floating">Standard</ion-label>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item disabled fill="outline">
<ion-label position="floating">Standard</ion-label>
<ion-input value="Has Initial Value"></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item disabled fill="outline" shape="round">
<ion-label position="floating">Standard</ion-label>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
</ion-row>
<ion-row>
<ion-col>
<ion-item disabled fill="solid" counter="true">
<ion-label position="floating">Standard</ion-label>
<ion-textarea maxlength="100"></ion-textarea>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item disabled fill="outline" counter="true">
<ion-label position="floating">Standard</ion-label>
<ion-textarea value="Has Initial Value" maxlength="100"></ion-textarea>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item disabled fill="outline" shape="round" counter="true">
<ion-label position="floating">Standard</ion-label>
<ion-textarea maxlength="100"></ion-textarea>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
</ion-row>
</ion-grid>
<p>Textarea</p>
<ion-grid>
<ion-row>
<ion-col>
<ion-item fill="solid">
<ion-textarea></ion-textarea>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item fill="outline">
<ion-textarea value="Has Initial Value"></ion-textarea>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item fill="outline" shape="round">
<ion-textarea></ion-textarea>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
</ion-row>
<ion-row>
<ion-col>
<ion-item fill="solid" counter="true">
<ion-label position="floating">Standard</ion-label>
<ion-textarea maxlength="100"></ion-textarea>
<ion-note slot="helper">Helper Text</ion-note>
<ion-note slot="error">Error Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item fill="outline" counter="true">
<ion-label position="floating">Standard</ion-label>
<ion-textarea maxlength="100" value="Has Initial Value"></ion-textarea>
<ion-note slot="helper">Helper Text</ion-note>
<ion-note slot="error">Error Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item fill="outline" shape="round" counter="true">
<ion-label position="floating">Standard</ion-label>
<ion-textarea maxlength="100"></ion-textarea>
<ion-note slot="helper">Helper Text</ion-note>
<ion-note slot="error">Error Text</ion-note>
</ion-item>
</ion-col>
</ion-row>
<ion-row>
<ion-col>
<ion-item class="ion-invalid" fill="solid" counter="true">
<ion-label position="floating">Standard</ion-label>
<ion-textarea maxlength="100"></ion-textarea>
<ion-note slot="helper">Helper Text</ion-note>
<ion-note slot="error">Error Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item class="ion-invalid" fill="outline" counter="true">
<ion-label position="floating">Standard</ion-label>
<ion-textarea maxlength="100" value="Has Initial Value"></ion-textarea>
<ion-note slot="helper">Helper Text</ion-note>
<ion-note slot="error">Error Text</ion-note>
</ion-item>
</ion-col>
<ion-col>
<ion-item class="ion-invalid" fill="outline" shape="round" counter="true">
<ion-label position="floating">Standard</ion-label>
<ion-textarea maxlength="100"></ion-textarea>
<ion-note slot="helper">Helper Text</ion-note>
</ion-item>
</ion-col>
</ion-row>
</ion-grid>
</ion-content>
</ion-app>
</body>
</html>

View File

@ -306,6 +306,23 @@
<ion-input placeholder="Placeholder"></ion-input>
</ion-item>
<ion-item fill="solid">
<ion-label position="floating">Input (Fill: Solid)</ion-label>
<ion-input></ion-input>
</ion-item>
<ion-item fill="outline">
<ion-label position="floating">Input (Fill: Outline)</ion-label>
<ion-input></ion-input>
</ion-item>
<ion-item>
<ion-label>Helper and Error Text</ion-label>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
<ion-note slot="error">Error Text</ion-note>
</ion-item>
<ion-item>
<ion-label>Checkbox</ion-label>
<ion-checkbox slot="start"></ion-checkbox>

View File

@ -306,6 +306,23 @@
<ion-input placeholder="Placeholder"></ion-input>
</ion-item>
<ion-item fill="solid">
<ion-label position="floating">Input (Fill: Solid)</ion-label>
<ion-input></ion-input>
</ion-item>
<ion-item fill="outline">
<ion-label position="floating">Input (Fill: Outline)</ion-label>
<ion-input></ion-input>
</ion-item>
<ion-item>
<ion-label>Helper and Error Text</ion-label>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
<ion-note slot="error">Error Text</ion-note>
</ion-item>
<ion-item>
<ion-label>Checkbox</ion-label>
<ion-checkbox slot="start"></ion-checkbox>

View File

@ -1,6 +1,6 @@
```tsx
import React from 'react';
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonItem, IonLabel, IonList, IonText, IonAvatar, IonThumbnail, IonButton, IonIcon, IonDatetime, IonSelect, IonSelectOption, IonToggle, IonInput, IonCheckbox, IonRange } from '@ionic/react';
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonItem, IonLabel, IonList, IonText, IonAvatar, IonThumbnail, IonButton, IonIcon, IonDatetime, IonSelect, IonSelectOption, IonToggle, IonInput, IonCheckbox, IonRange, IonNote } from '@ionic/react';
import { closeCircle, home, star, navigate, informationCircle, checkmarkCircle, shuffle } from 'ionicons/icons';
export const ItemExamples: React.FC = () => {
@ -291,6 +291,23 @@ export const ItemExamples: React.FC = () => {
<IonInput placeholder="Placeholder"></IonInput>
</IonItem>
<IonItem fill="solid">
<IonLabel position="stacked">Input (Fill: Solid)</IonLabel>
<IonInput></IonInput>
</IonItem>
<IonItem fill="outline">
<IonLabel position="floating">Input (Fill: Outline)</IonLabel>
<IonInput></IonInput>
</IonItem>
<IonItem>
<IonLabel>Helper and Error Text</IonLabel>
<IonInput></IonInput>
<IonNote slot="helper">Helper Text</IonNote>
<IonNote slot="error">Error Text</IonNote>
</IonItem>
<IonItem>
<IonLabel>Checkbox</IonLabel>
<IonCheckbox slot="start" />

View File

@ -409,6 +409,23 @@ export class ItemExample {
<ion-input placeholder="Placeholder"></ion-input>
</ion-item>,
<ion-item fill="solid">
<ion-label position="floating">Input (Fill: Solid)</ion-label>
<ion-input></ion-input>
</ion-item>,
<ion-item fill="outline">
<ion-label position="floating">Input (Fill: Outline)</ion-label>
<ion-input></ion-input>
</ion-item>,
<ion-item>
<ion-label>Helper and Error Text</ion-label>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
<ion-note slot="error">Error Text</ion-note>
</ion-item>,
<ion-item>
<ion-label>Checkbox</ion-label>
<ion-checkbox slot="start"></ion-checkbox>

View File

@ -321,6 +321,23 @@
<ion-input placeholder="Placeholder"></ion-input>
</ion-item>
<ion-item fill="solid">
<ion-label position="floating">Input (Fill: Solid)</ion-label>
<ion-input></ion-input>
</ion-item>
<ion-item fill="outline">
<ion-label position="floating">Input (Fill: Outline)</ion-label>
<ion-input></ion-input>
</ion-item>
<ion-item>
<ion-label>Helper and Error Text</ion-label>
<ion-input></ion-input>
<ion-note slot="helper">Helper Text</ion-note>
<ion-note slot="error">Error Text</ion-note>
</ion-item>
<ion-item>
<ion-label>Checkbox</ion-label>
<ion-checkbox slot="start"></ion-checkbox>
@ -338,6 +355,7 @@ import {
IonButton,
IonCheckbox,
IonDatetime,
IonNote,
IonIcon,
IonInput,
IonItem,
@ -366,6 +384,7 @@ export default defineComponent({
IonButton,
IonCheckbox,
IonDatetime,
IonNote,
IonIcon,
IonInput,
IonItem,

View File

@ -17,8 +17,23 @@
* When translating the label, we need to use translateY
* instead of translate3d due to a WebKit bug: https://bugs.webkit.org/show_bug.cgi?id=215731
*/
:host(.label-stacked),
:host(.label-floating) {
@include margin(0, 0, 0, 0);
/* stylelint-disable property-blacklist */
transform-origin: top left;
/* stylelint-enable property-blacklist */
z-index: 3;
}
:host(.label-stacked.label-rtl),
:host(.label-floating.label-rtl) {
/* stylelint-disable property-blacklist */
transform-origin: top right;
/* stylelint-enable property-blacklist */
}
:host(.label-stacked) {
@include transform-origin(start, top);
@include transform(translateY(50%), scale(.75));
transition: color 150ms $label-md-transition-timing-function;
@ -26,28 +41,73 @@
:host(.label-floating) {
@include transform(translateY(96%));
@include transform-origin(start, top);
transition:
color 150ms $label-md-transition-timing-function,
transform 150ms $label-md-transition-timing-function;
}
:host-context(.item-textarea).label-floating {
@include transform(translateY(185%));
}
:host(.label-stacked),
:host(.label-floating) {
@include margin(0, 0, 0, 0);
}
:host-context(.item-has-focus).label-floating,
:host-context(.item-has-placeholder:not(.item-input)).label-floating,
:host-context(.item-has-value).label-floating {
@include transform(translateY(50%), scale(.75));
}
/**
* When translating the label inside of an ion-item with `fill="outline"`,
* add pseudo-elements to imitate fieldset-like padding without shifting the label
*/
:host-context(.item-fill-outline.item-has-focus).label-floating,
:host-context(.item-fill-outline.item-has-placeholder:not(.item-input)).label-floating,
:host-context(.item-fill-outline.item-has-value).label-floating {
@include transform(translateY(-6px), scale(.75));
position: relative;
max-width: min-content;
background-color: $item-md-background;
overflow: visible;
z-index: 3;
&::before,
&::after {
position: absolute;
width: $item-md-fill-outline-label-padding;
height: 100%;
background-color: $item-md-background;
content: "";
}
&::before {
/* stylelint-disable property-blacklist */
left: calc(-1 * #{$item-md-fill-outline-label-padding});
/* stylelint-enable property-blacklist */
}
&::after {
/* stylelint-disable property-blacklist */
right: calc(-1 * #{$item-md-fill-outline-label-padding});
/* stylelint-enable property-blacklist */
}
}
:host-context(.item-fill-outline.item-has-focus.item-has-start-slot).label-floating,
:host-context(.item-fill-outline.item-has-placeholder:not(.item-input).item-has-start-slot).label-floating,
:host-context(.item-fill-outline.item-has-value.item-has-start-slot).label-floating {
@include transform(translateX(#{$item-md-fill-outline-label-translate-x}), translateY(-6px), scale(.75));
}
:host-context(.item-fill-outline.item-has-focus.item-has-start-slot).label-floating.label-rtl,
:host-context(.item-fill-outline.item-has-placeholder:not(.item-input).item-has-start-slot).label-floating.label-rtl,
:host-context(.item-fill-outline.item-has-value.item-has-start-slot).label-floating.label-rtl {
@include transform(translateX(calc(-1 * #{$item-md-fill-outline-label-translate-x})), translateY(-6px), scale(.75));
}
:host-context(.item-has-focus).label-stacked:not(.ion-color),
:host-context(.item-has-focus).label-floating:not(.ion-color) {
color: $label-md-text-color-focused;
@ -58,6 +118,18 @@
color: #{current-color(contrast)};
}
:host-context(.item-fill-solid.item-has-focus.ion-color).label-stacked:not(.ion-color),
:host-context(.item-fill-solid.item-has-focus.ion-color).label-floating:not(.ion-color),
:host-context(.item-fill-outline.item-has-focus.ion-color).label-stacked:not(.ion-color),
:host-context(.item-fill-outline.item-has-focus.ion-color).label-floating:not(.ion-color) {
color: #{current-color(base)};
}
:host-context(.ion-invalid).label-stacked:not(.ion-color),
:host-context(.ion-invalid).label-floating:not(.ion-color) {
color: var(--highlight-color-invalid);
}
// MD Typography
// --------------------------------------------------

View File

@ -102,7 +102,8 @@ export class Label implements ComponentInterface {
class={createColorClasses(this.color, {
[mode]: true,
[`label-${position}`]: position !== undefined,
[`label-no-animate`]: (this.noAnimate)
[`label-no-animate`]: (this.noAnimate),
'label-rtl': document.dir === 'rtl'
})}
>
</Host>

View File

@ -166,6 +166,19 @@ export default defineComponent({
| `--color` | Color of the note |
## Dependencies
### Used by
- [ion-item](../item)
### Graph
```mermaid
graph TD;
ion-item --> ion-note
style ion-note fill:#f9f,stroke:#333,stroke-width:4px
```
----------------------------------------------
*Built with [StencilJS](https://stenciljs.com/)*

View File

@ -191,6 +191,24 @@
Card Button Item 2 focused
</ion-item>
</ion-card>
<ion-card>
<ion-card-content>
<ion-item fill="solid">
<ion-icon slot="start" name="calendar-clear-outline"></ion-icon>
<ion-label position="floating">Standard</ion-label>
<ion-input value="Has Initial Value"></ion-input>
<ion-note slot="error">Error Text</ion-note>
</ion-item>
<ion-item fill="outline">
<ion-label position="floating">Standard</ion-label>
<ion-icon slot="end" name="trash"></ion-icon>
<ion-input></ion-input>
<ion-note slot="error">Helper Text</ion-note>
</ion-item>
</ion-card-content>
</ion-card>
</section>
<section>

View File

@ -379,9 +379,12 @@ export const IonItem = /*@__PURE__*/ defineContainer<JSX.IonItem>('ion-item', Io
'detailIcon',
'disabled',
'download',
'fill',
'shape',
'href',
'rel',
'lines',
'counter',
'routerAnimation',
'routerDirection',
'target',