Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 3.2k
feat(input, textarea, select): add section for start and end slots#3271
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
c3aa31cd3ef7a59496178a629f1ad723dddd921c48c941f37175da46c7bac20d704d5cFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -173,6 +173,20 @@ import InterfaceOptionsExample from '@site/static/usage/v7/select/customization/ | ||
| <InterfaceOptionsExample /> | ||
| ## Start and End Slots | ||
averyrousseau marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| The `start` and `end` slots can be used to place icons, buttons, or prefix/suffix text on either side of the select. If the slot content is clicked, the select will not open. | ||
| :::note | ||
| In most cases, [Icon](./icon.md) components placed in these slots should have `aria-hidden="true"`. See the [Icon accessibility docs](https://ionicframework.com/docs/api/icon#accessibility) for more information. | ||
| If slot content is meant to be interacted with, it should be wrapped in an interactive element such as a [Button](./button.md). This ensures that the content can be tabbed to. | ||
averyrousseau marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| ::: | ||
thetaPC marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| import StartEndSlots from '@site/static/usage/v7/select/start-end-slots/index.md'; | ||
| <StartEndSlots /> | ||
| ## Customization | ||
| There are two units that make up the Select component and each need to be styled separately. The `ion-select` element is represented on the view by the selected value(s), or placeholder if there is none, and dropdown icon. The interface, which is defined in the [Interfaces](#interfaces) section above, is the dialog that opens when clicking on the `ion-select`. The interface contains all of the options defined by adding `ion-select-option` elements. The following sections will go over the differences between styling these. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -109,6 +109,22 @@ import ClearOnEditPlayground from '@site/static/usage/v7/textarea/clear-on-edit/ | ||
| <ClearOnEditPlayground /> | ||
| ## Start and End Slots (experimental) | ||
| The `start` and `end` slots can be used to place icons, buttons, or prefix/suffix text on either side of the textarea. | ||
| Note that this feature is considered experimental because it relies on a simulated version of [Web Component slots](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_templates_and_slots). As a result, the simulated behavior may not exactly match the native slot behavior. | ||
| :::note | ||
| In most cases, [Icon](./icon.md) components placed in these slots should have `aria-hidden="true"`. See the [Icon accessibility docs](https://ionicframework.com/docs/api/icon#accessibility) for more information. | ||
| If slot content is meant to be interacted with, it should be wrapped in an interactive element such as a [Button](./button.md). This ensures that the content can be tabbed to. | ||
averyrousseau marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| ::: | ||
| import StartEndSlots from '@site/static/usage/v7/textarea/start-end-slots/index.md'; | ||
| <StartEndSlots /> | ||
| ## Migrating from Legacy Textarea Syntax | ||
| A simpler textarea syntax was introduced in Ionic 7.0. This new syntax reduces the boilerplate required to setup an textarea, resolves accessibility issues, and improves the developer experience. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| ```html | ||
| <ion-list> | ||
| <ion-item> | ||
| <ion-input labelPlacement="stacked" label="Email" placeholder="email@domain.com"> | ||
| <ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon> | ||
| <ion-button fill="clear" slot="end" aria-label="Show/hide"> | ||
| <ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon> | ||
| </ion-button> | ||
| </ion-input> | ||
| </ion-item> | ||
| </ion-list> | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <title>Input</title> | ||
| <link rel="stylesheet" href="../../../common.css" /> | ||
| <script src="../../../common.js"></script> | ||
| <script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@7/dist/ionic/ionic.esm.js"></script> | ||
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@7/css/ionic.bundle.css" /> | ||
| </head> | ||
| <body> | ||
| <ion-app> | ||
| <ion-content> | ||
| <div class="container"> | ||
| <ion-list> | ||
| <ion-item> | ||
| <ion-input label-placement="stacked" label="Email" placeholder="email@domain.com"> | ||
| <ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon> | ||
| <ion-button fill="clear" slot="end" aria-label="Show/hide"> | ||
| <ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon> | ||
| </ion-button> | ||
| </ion-input> | ||
| </ion-item> | ||
| </ion-list> | ||
| </div> | ||
| </ion-content> | ||
| </ion-app> | ||
| </body> | ||
| </html> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| import Playground from '@site/src/components/global/Playground'; | ||
| import javascript from './javascript.md'; | ||
| import react from './react.md'; | ||
| import vue from './vue.md'; | ||
| import angular from './angular.md'; | ||
| <Playground | ||
| version="7" | ||
| code={{ | ||
| javascript, | ||
| react, | ||
| vue, | ||
| angular, | ||
| }} | ||
| src="usage/v7/input/start-end-slots/demo.html" | ||
| /> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| ```html | ||
| <ion-list> | ||
| <ion-item> | ||
| <ion-input label-placement="stacked" label="Email" placeholder="email@domain.com"> | ||
| <ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon> | ||
| <ion-button fill="clear" slot="end" aria-label="Show/hide"> | ||
| <ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon> | ||
| </ion-button> | ||
| </ion-input> | ||
| </ion-item> | ||
| </ion-list> | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| ```tsx | ||
| import React from 'react'; | ||
| import { IonButton, IonIcon, IonInput, IonItem, IonList } from '@ionic/react'; | ||
| import { eye, lockClosed } from 'ionicons/icons'; | ||
| function Example() { | ||
| return ( | ||
| <IonList> | ||
| <IonItem> | ||
| <IonInput labelPlacement="stacked" label="Email" placeholder="email@domain.com"> | ||
| <IonIcon slot="start" icon={lockClosed} aria-hidden="true"></IonIcon> | ||
| <IonButton fill="clear" slot="end" aria-label="Show/hide"> | ||
| <IonIcon slot="icon-only" name={eye} aria-hidden="true"></IonIcon> | ||
| </IonButton> | ||
| </IonInput> | ||
| </IonItem> | ||
| </IonList> | ||
| ); | ||
| } | ||
| export default Example; | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| ```html | ||
| <template> | ||
| <ion-list> | ||
| <ion-item> | ||
| <ion-input label-placement="stacked" label="Email" placeholder="email@domain.com"> | ||
| <ion-icon slot="start" :icon="lockClosed" aria-hidden="true"></ion-icon> | ||
| <ion-button fill="clear" slot="end" aria-label="Show/hide"> | ||
| <ion-icon slot="icon-only" :icon="eye" aria-hidden="true"></ion-icon> | ||
| </ion-button> | ||
| </ion-input> | ||
| </ion-item> | ||
| </ion-list> | ||
| </template> | ||
| <script lang="ts"> | ||
| import { IonButton, IonIcon, IonInput, IonItem, IonList } from '@ionic/vue'; | ||
| import { eye, lockClosed } from 'ionicons/icons'; | ||
| import { defineComponent } from 'vue'; | ||
| export default defineComponent({ | ||
| components: { | ||
| IonButton, | ||
| IonIcon, | ||
| IonInput, | ||
| IonItem, | ||
| IonList, | ||
| }, | ||
| setup() { | ||
| return { eye, lockClosed }; | ||
| }, | ||
| }); | ||
| </script> | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| ```html | ||
| <ion-list> | ||
| <ion-item> | ||
| <ion-select labelPlacement="stacked" label="Favorite fruit" value="apple"> | ||
| <ion-icon slot="start" name="leaf" aria-hidden="true"></ion-icon> | ||
| <ion-select-option value="apple">Apple</ion-select-option> | ||
| <ion-select-option value="banana">Banana</ion-select-option> | ||
| <ion-select-option value="orange">Orange</ion-select-option> | ||
| <ion-button fill="clear" slot="end" aria-label="Show/hide password"> | ||
| <ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon> | ||
| </ion-button> | ||
| </ion-select> | ||
| </ion-item> | ||
| </ion-list> | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <title>Select</title> | ||
| <link rel="stylesheet" href="../../../common.css" /> | ||
| <script src="../../../common.js"></script> | ||
| <script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@7/dist/ionic/ionic.esm.js"></script> | ||
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@7/css/ionic.bundle.css" /> | ||
| <style> | ||
| /** | ||
| * This is to deal with a bug in 7.6 where a select in a list/item | ||
| * wrapped in a flex container will shrink to 0 width. When the bug | ||
| * is fixed, we can remove this. | ||
| */ | ||
| ion-list { | ||
| min-width: 400px; | ||
| } | ||
| </style> | ||
| </head> | ||
| <body> | ||
| <ion-app> | ||
| <ion-content> | ||
| <div class="container"> | ||
| <ion-list> | ||
| <ion-item> | ||
| <ion-select label-placement="stacked" label="Favorite fruit" value="apple"> | ||
| <ion-icon slot="start" name="leaf" aria-hidden="true"></ion-icon> | ||
| <ion-select-option value="apple">Apple</ion-select-option> | ||
| <ion-select-option value="banana">Banana</ion-select-option> | ||
| <ion-select-option value="orange">Orange</ion-select-option> | ||
| <ion-button fill="clear" slot="end" aria-label="Show/hide password"> | ||
| <ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon> | ||
| </ion-button> | ||
| </ion-select> | ||
| </ion-item> | ||
| </ion-list> | ||
| </div> | ||
| </ion-content> | ||
| </ion-app> | ||
| </body> | ||
| </html> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import Playground from '@site/src/components/global/Playground'; | ||
| import javascript from './javascript.md'; | ||
| import react from './react.md'; | ||
| import vue from './vue.md'; | ||
| import angular from './angular.md'; | ||
| <Playground | ||
| version="7" | ||
| code={{ | ||
| javascript, | ||
| react, | ||
| vue, | ||
| angular, | ||
| }} | ||
| src="usage/v7/select/start-end-slots/demo.html" | ||
| size="300px" | ||
| /> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| ```html | ||
| <ion-list> | ||
| <ion-item> | ||
| <ion-select label-placement="stacked" label="Favorite fruit" value="apple"> | ||
| <ion-icon slot="start" name="leaf" aria-hidden="true"></ion-icon> | ||
| <ion-select-option value="apple">Apple</ion-select-option> | ||
| <ion-select-option value="banana">Banana</ion-select-option> | ||
| <ion-select-option value="orange">Orange</ion-select-option> | ||
| <ion-button fill="clear" slot="end" aria-label="Show/hide password"> | ||
| <ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon> | ||
| </ion-button> | ||
| </ion-select> | ||
| </ion-item> | ||
| </ion-list> | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| ```tsx | ||
| import React from 'react'; | ||
| import { IonButton, IonIcon, IonItem, IonList, IonSelect, IonSelectOption } from '@ionic/react'; | ||
| import { eye, leaf } from 'ionicons/icons'; | ||
| function Example() { | ||
| return ( | ||
| <IonList> | ||
| <IonItem> | ||
| <IonSelect labelPlacement="stacked" label="Favorite fruit" value="apple"> | ||
| <IonIcon slot="start" icon={leaf} aria-hidden="true"></IonIcon> | ||
| <IonSelectOption value="apple">Apple</IonSelectOption> | ||
| <IonSelectOption value="banana">Banana</IonSelectOption> | ||
| <IonSelectOption value="orange">Orange</IonSelectOption> | ||
| <IonButton fill="clear" slot="end" aria-label="Show/hide password"> | ||
| <IonIcon slot="icon-only" icon={eye} aria-hidden="true"></IonIcon> | ||
| </IonButton> | ||
| </IonSelect> | ||
| </IonItem> | ||
| </IonList> | ||
| ); | ||
| } | ||
| export default Example; | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| ```html | ||
| <template> | ||
| <ion-list> | ||
| <ion-item> | ||
| <ion-select label-placement="stacked" label="Favorite fruit" value="apple"> | ||
| <ion-icon slot="start" :icon="leaf" aria-hidden="true"></ion-icon> | ||
| <ion-select-option value="apple">Apple</ion-select-option> | ||
| <ion-select-option value="banana">Banana</ion-select-option> | ||
| <ion-select-option value="orange">Orange</ion-select-option> | ||
| <ion-button fill="clear" slot="end" aria-label="Show/hide password"> | ||
| <ion-icon slot="icon-only" :icon="eye" aria-hidden="true"></ion-icon> | ||
| </ion-button> | ||
| </ion-select> | ||
| </ion-item> | ||
| </ion-list> | ||
| </template> | ||
| <script lang="ts"> | ||
| import { IonButton, IonIcon, IonItem, IonList, IonSelect, IonSelectOption } from '@ionic/vue'; | ||
| import { eye, leaf } from 'ionicons/icons'; | ||
| import { defineComponent } from 'vue'; | ||
| export default defineComponent({ | ||
| components: { | ||
| IonButton, | ||
| IonIcon, | ||
| IonItem, | ||
| IonList, | ||
| IonSelect, | ||
| IonSelectOption, | ||
| }, | ||
| setup() { | ||
| return { eye, leaf }; | ||
| }, | ||
| }); | ||
| </script> | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| ```html | ||
| <ion-list> | ||
| <ion-item> | ||
| <ion-textarea labelPlacement="stacked" label="Comments" placeholder="Enter your comments"> | ||
| <ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon> | ||
| <ion-button fill="clear" slot="end" aria-label="Show/hide"> | ||
| <ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon> | ||
| </ion-button> | ||
| </ion-textarea> | ||
| </ion-item> | ||
| </ion-list> | ||
| ``` |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.