diff --git a/core/src/components/input/input.md.outline.scss b/core/src/components/input/input.md.outline.scss index 4a1abfbbdc..ea202b74ab 100644 --- a/core/src/components/input/input.md.outline.scss +++ b/core/src/components/input/input.md.outline.scss @@ -89,9 +89,7 @@ /** * This makes the label sit above the input. */ -:host(.has-focus.input-fill-outline.input-label-placement-floating) .label-text-wrapper, -:host(.has-value.input-fill-outline.input-label-placement-floating) .label-text-wrapper, -:host(.input-fill-outline.input-label-placement-stacked) .label-text-wrapper { +:host(.label-floating.input-fill-outline) .label-text-wrapper { @include transform(translateY(-32%), scale(#{$form-control-label-stacked-scale})); @include margin(0); @@ -216,8 +214,6 @@ * the floating/stacked label. We simulate this "cut out" * by removing the top border from the notch fragment. */ -:host(.has-focus.input-fill-outline.input-label-placement-floating) .input-outline-notch, -:host(.has-value.input-fill-outline.input-label-placement-floating) .input-outline-notch, -:host(.input-fill-outline.input-label-placement-stacked) .input-outline-notch { +:host(.label-floating.input-fill-outline) .input-outline-notch { border-top: none; } diff --git a/core/src/components/input/input.md.solid.scss b/core/src/components/input/input.md.solid.scss index 5765f764ba..98cde92880 100644 --- a/core/src/components/input/input.md.solid.scss +++ b/core/src/components/input/input.md.solid.scss @@ -67,9 +67,7 @@ // Input Label // ---------------------------------------------------------------- -:host(.input-fill-solid.input-label-placement-stacked) .label-text-wrapper, -:host(.has-focus.input-fill-solid.input-label-placement-floating) .label-text-wrapper, -:host(.has-value.input-fill-solid.input-label-placement-floating) .label-text-wrapper { +:host(.label-floating.input-fill-solid.input-label-placement-floating) .label-text-wrapper { /** * Label text should not extend * beyond the bounds of the input. diff --git a/core/src/components/input/input.scss b/core/src/components/input/input.scss index d8aab456d5..7ebfb52680 100644 --- a/core/src/components/input/input.scss +++ b/core/src/components/input/input.scss @@ -322,6 +322,9 @@ flex-grow: 1; + // ensure start/end slot content is vertically centered + align-items: center; + width: 100%; } @@ -641,9 +644,7 @@ /** * This makes the label sit above the input. */ -:host(.input-label-placement-stacked) .label-text-wrapper, -:host(.has-focus.input-label-placement-floating) .label-text-wrapper, -:host(.has-value.input-label-placement-floating) .label-text-wrapper { +:host(.label-floating) .label-text-wrapper { @include transform(translateY(50%), scale(#{$form-control-label-stacked-scale})); /** @@ -652,3 +653,14 @@ */ max-width: calc(100% / #{$form-control-label-stacked-scale}); } + +// Start/End Slots +// ---------------------------------------------------------------- + +::slotted([slot="start"]) { + margin-inline-end: $form-control-label-margin; +} + +::slotted([slot="end"]) { + margin-inline-start: $form-control-label-margin; +} diff --git a/core/src/components/input/input.tsx b/core/src/components/input/input.tsx index 0021402242..28ee6816a6 100644 --- a/core/src/components/input/input.tsx +++ b/core/src/components/input/input.tsx @@ -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 ( -