mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-10 00:27:41 +08:00
feat(input, textarea, select): add start and end slots (#28583)
Issue number: Resolves #26297 --------- <!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the current behavior? <!-- Please describe the current behavior that you are modifying. --> With the modern form control syntax, it is not possible to add icon buttons or other decorators to the sides of `ion-input`, `ion-textarea`, or `ion-select`, as you can with `ion-item`. ## What is the new behavior? <!-- Please describe the behavior or changes that are being added by this PR. --> `start` and `end` slots added to each component. This PR is a combination of several others that were already approved. If needed, it might be easiest to review the PRs individually by looking at the commit history here. ## Does this introduce a breaking change? - [ ] Yes - [x] No <!-- If this introduces a breaking change, please describe the impact and migration path for existing applications below. --> ## Other information <!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. --> Docs PR: https://github.com/ionic-team/ionic-docs/pull/3271 Dev build: `7.5.4-dev.11701112913.1ea61220` --------- Co-authored-by: ionitron <hi@ionicframework.com> Co-authored-by: Maria Hutt <thetaPC@users.noreply.github.com> Co-authored-by: Liam DeBeasi <liamdebeasi@users.noreply.github.com>
This commit is contained in:
@ -26,6 +26,8 @@ import { getCounterText } from './input.utils';
|
||||
* @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use.
|
||||
*
|
||||
* @slot label - The label text to associate with the input. Use the `labelPlacement` property to control where the label is placed relative to the input. Use this if you need to render a label with custom HTML. (EXPERIMENTAL)
|
||||
* @slot start - Content to display at the leading edge of the input. (EXPERIMENTAL)
|
||||
* @slot end - Content to display at the trailing edge of the input. (EXPERIMENTAL)
|
||||
*/
|
||||
@Component({
|
||||
tag: 'ion-input',
|
||||
@ -369,7 +371,7 @@ export class Input implements ComponentInterface {
|
||||
const { el } = this;
|
||||
|
||||
this.legacyFormController = createLegacyFormController(el);
|
||||
this.slotMutationController = createSlotMutationController(el, 'label', () => forceUpdate(this));
|
||||
this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => forceUpdate(this));
|
||||
this.notchController = createNotchController(
|
||||
el,
|
||||
() => this.notchSpacerEl,
|
||||
@ -697,18 +699,42 @@ export class Input implements ComponentInterface {
|
||||
}
|
||||
|
||||
private renderInput() {
|
||||
const { disabled, fill, readonly, shape, inputId, labelPlacement } = this;
|
||||
const { disabled, fill, readonly, shape, inputId, labelPlacement, el, hasFocus } = this;
|
||||
const mode = getIonMode(this);
|
||||
const value = this.getValue();
|
||||
const inItem = hostContext('ion-item', this.el);
|
||||
const shouldRenderHighlight = mode === 'md' && fill !== 'outline' && !inItem;
|
||||
|
||||
const hasValue = this.hasValue();
|
||||
const hasStartEndSlots = el.querySelector('[slot="start"], [slot="end"]') !== null;
|
||||
|
||||
/**
|
||||
* If the label is stacked, it should always sit above the input.
|
||||
* For floating labels, the label should move above the input if
|
||||
* the input has a value, is focused, or has anything in either
|
||||
* the start or end slot.
|
||||
*
|
||||
* If there is content in the start slot, the label would overlap
|
||||
* it if not forced to float. This is also applied to the end slot
|
||||
* because with the default or solid fills, the input is not
|
||||
* vertically centered in the container, but the label is. This
|
||||
* causes the slots and label to appear vertically offset from each
|
||||
* other when the label isn't floating above the input. This doesn't
|
||||
* apply to the outline fill, but this was not accounted for to keep
|
||||
* things consistent.
|
||||
*
|
||||
* TODO(FW-5592): Remove hasStartEndSlots condition
|
||||
*/
|
||||
const labelShouldFloat =
|
||||
labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
|
||||
|
||||
return (
|
||||
<Host
|
||||
class={createColorClasses(this.color, {
|
||||
[mode]: true,
|
||||
'has-value': this.hasValue(),
|
||||
'has-focus': this.hasFocus,
|
||||
'has-value': hasValue,
|
||||
'has-focus': hasFocus,
|
||||
'label-floating': labelShouldFloat,
|
||||
[`input-fill-${fill}`]: fill !== undefined,
|
||||
[`input-shape-${shape}`]: shape !== undefined,
|
||||
[`input-label-placement-${labelPlacement}`]: true,
|
||||
@ -717,9 +743,16 @@ export class Input implements ComponentInterface {
|
||||
'input-disabled': disabled,
|
||||
})}
|
||||
>
|
||||
<label class="input-wrapper">
|
||||
{/**
|
||||
* htmlFor is needed so that clicking the label always focuses
|
||||
* the input. Otherwise, if the start slot has something
|
||||
* interactable, clicking the label would focus that instead
|
||||
* since it comes before the input in the DOM.
|
||||
*/}
|
||||
<label class="input-wrapper" htmlFor={inputId}>
|
||||
{this.renderLabelContainer()}
|
||||
<div class="native-wrapper">
|
||||
<slot name="start"></slot>
|
||||
<input
|
||||
class="native-input"
|
||||
ref={(input) => (this.nativeInput = input)}
|
||||
@ -774,6 +807,7 @@ export class Input implements ComponentInterface {
|
||||
<ion-icon aria-hidden="true" icon={mode === 'ios' ? closeCircle : closeSharp}></ion-icon>
|
||||
</button>
|
||||
)}
|
||||
<slot name="end"></slot>
|
||||
</div>
|
||||
{shouldRenderHighlight && <div class="input-highlight"></div>}
|
||||
</label>
|
||||
|
||||
Reference in New Issue
Block a user