mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
819d48edee | ||
|
|
cd1213ef04 | ||
|
|
343d0ae7a5 | ||
|
|
2282f7f8c3 |
@@ -14,6 +14,14 @@
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
// Native Text Input
|
||||
// --------------------------------------------------
|
||||
|
||||
:host(.input-label-placement-floating) .native-input,
|
||||
:host(.input-label-placement-stacked) .native-input {
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
// Input - Disabled
|
||||
// ----------------------------------------------------------------
|
||||
// The input, label, helper text, char counter and placeholder
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
* This makes the label sit above the input.
|
||||
*/
|
||||
:host(.label-floating.input-fill-outline) .label-text-wrapper {
|
||||
@include transform(translateY(-32%), scale(#{$form-control-label-stacked-scale}));
|
||||
@include transform(translate(var(--start-slot-adjustment), -32%), scale(#{$form-control-label-stacked-scale}));
|
||||
@include margin(0);
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,6 +20,14 @@
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
// Native Text Input
|
||||
// --------------------------------------------------
|
||||
|
||||
:host(.input-label-placement-floating:not(.input-fill-outline)) .native-input,
|
||||
:host(.input-label-placement-stacked:not(.input-fill-outline)) .native-input {
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
// Input - Disabled
|
||||
// ----------------------------------------------------------------
|
||||
// The input, label, helper text, char counter and placeholder
|
||||
|
||||
@@ -238,7 +238,7 @@
|
||||
|
||||
flex-grow: 1;
|
||||
|
||||
align-items: stretch;
|
||||
align-items: center;
|
||||
|
||||
height: inherit;
|
||||
|
||||
@@ -261,9 +261,6 @@
|
||||
|
||||
flex-grow: 1;
|
||||
|
||||
// ensure start/end slot content is vertically centered
|
||||
align-items: center;
|
||||
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -526,8 +523,8 @@
|
||||
*/
|
||||
:host(.input-label-placement-stacked) .input-wrapper,
|
||||
:host(.input-label-placement-floating) .input-wrapper {
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -547,6 +544,8 @@
|
||||
* autofill background too.
|
||||
*/
|
||||
z-index: 2;
|
||||
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -596,6 +595,12 @@
|
||||
// Start/End Slots
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
::slotted([slot="start"]),
|
||||
::slotted([slot="end"]) {
|
||||
flex-shrink: 0;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
::slotted([slot="start"]) {
|
||||
margin-inline-end: $form-control-label-margin;
|
||||
margin-inline-start: 0;
|
||||
|
||||
@@ -626,7 +626,7 @@ export class Input implements ComponentInterface {
|
||||
* Renders the border container
|
||||
* when fill="outline".
|
||||
*/
|
||||
private renderLabelContainer() {
|
||||
private renderOutlineLabelContainer() {
|
||||
const mode = getIonMode(this);
|
||||
const hasOutlineFill = mode === 'md' && this.fill === 'outline';
|
||||
|
||||
@@ -653,15 +653,10 @@ export class Input implements ComponentInterface {
|
||||
</div>
|
||||
<div class="input-outline-end"></div>
|
||||
</div>,
|
||||
this.renderLabel(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* If not using the outline style,
|
||||
* we can render just the label.
|
||||
*/
|
||||
return this.renderLabel();
|
||||
return null;
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -672,27 +667,20 @@ export class Input implements ComponentInterface {
|
||||
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));
|
||||
labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus));
|
||||
|
||||
const startSlotEl = el.querySelector('[slot="start"]');
|
||||
|
||||
// 16px is the margin after the start slot content, which we also need to account for.
|
||||
const startSlotWidth = startSlotEl ? startSlotEl.clientWidth + 16 : 0;
|
||||
|
||||
return (
|
||||
<Host
|
||||
@@ -708,6 +696,9 @@ export class Input implements ComponentInterface {
|
||||
'in-item-color': hostContext('ion-item.ion-color', this.el),
|
||||
'input-disabled': disabled,
|
||||
})}
|
||||
style={{
|
||||
'--start-slot-adjustment': `-${startSlotWidth}px`
|
||||
}}
|
||||
>
|
||||
{/**
|
||||
* htmlFor is needed so that clicking the label always focuses
|
||||
@@ -716,9 +707,10 @@ export class Input implements ComponentInterface {
|
||||
* since it comes before the input in the DOM.
|
||||
*/}
|
||||
<label class="input-wrapper" htmlFor={inputId}>
|
||||
{this.renderLabelContainer()}
|
||||
<slot name="start"></slot>
|
||||
{this.renderOutlineLabelContainer()}
|
||||
<div class="native-wrapper">
|
||||
<slot name="start"></slot>
|
||||
{this.renderLabel()}
|
||||
<input
|
||||
class="native-input"
|
||||
ref={(input) => (this.nativeInput = input)}
|
||||
@@ -771,8 +763,8 @@ export class Input implements ComponentInterface {
|
||||
<ion-icon aria-hidden="true" icon={mode === 'ios' ? closeCircle : closeSharp}></ion-icon>
|
||||
</button>
|
||||
)}
|
||||
<slot name="end"></slot>
|
||||
</div>
|
||||
<slot name="end"></slot>
|
||||
{shouldRenderHighlight && <div class="input-highlight"></div>}
|
||||
</label>
|
||||
{this.renderBottomContent()}
|
||||
|
||||
@@ -229,7 +229,7 @@ button {
|
||||
transition: opacity 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.select-wrapper-inner {
|
||||
.select-wrapper-inner, .select-wrapper-inner-start {
|
||||
display: flex;
|
||||
|
||||
align-items: center;
|
||||
@@ -508,26 +508,12 @@ button {
|
||||
* The placeholder should be hidden when the label
|
||||
* is on top of the select. This prevents the label
|
||||
* from overlapping any placeholder value.
|
||||
*
|
||||
* TODO(FW-5592): Remove :not(.label-floating) piece
|
||||
*/
|
||||
:host(.select-label-placement-floating:not(.label-floating)) .native-wrapper .select-placeholder {
|
||||
:host(.select-label-placement-floating) .native-wrapper .select-placeholder {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* We don't use .label-floating here because that would
|
||||
* also include the case where the label is floating due
|
||||
* to content in the start/end slot. We want the opacity
|
||||
* to remain at the default in this case, since the select
|
||||
* isn't being actively interacted with.
|
||||
*
|
||||
* TODO(FW-5592): Change entire selector to:
|
||||
* :host(.label-floating.select-label-placement-floating) .native-wrapper .select-placeholder
|
||||
*/
|
||||
:host(.select-expanded.select-label-placement-floating) .native-wrapper .select-placeholder,
|
||||
:host(.ion-focused.select-label-placement-floating) .native-wrapper .select-placeholder,
|
||||
:host(.has-value.select-label-placement-floating) .native-wrapper .select-placeholder {
|
||||
:host(.label-floating.select-label-placement-floating) .native-wrapper .select-placeholder {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -785,7 +785,7 @@ export class Select implements ComponentInterface {
|
||||
* Renders the border container
|
||||
* when fill="outline".
|
||||
*/
|
||||
private renderLabelContainer() {
|
||||
private renderOutlineLabelContainer() {
|
||||
const mode = getIonMode(this);
|
||||
const hasOutlineFill = mode === 'md' && this.fill === 'outline';
|
||||
|
||||
@@ -811,16 +811,11 @@ export class Select implements ComponentInterface {
|
||||
</div>
|
||||
</div>
|
||||
<div class="select-outline-end"></div>
|
||||
</div>,
|
||||
this.renderLabel(),
|
||||
</div>
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* If not using the outline style,
|
||||
* we can render just the label.
|
||||
*/
|
||||
return this.renderLabel();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -932,7 +927,6 @@ export class Select implements ComponentInterface {
|
||||
const shouldRenderHighlight = mode === 'md' && fill !== 'outline' && !inItem;
|
||||
|
||||
const hasValue = this.hasValue();
|
||||
const hasStartEndSlots = el.querySelector('[slot="start"], [slot="end"]') !== null;
|
||||
|
||||
renderHiddenInput(true, el, name, parseValue(value), disabled);
|
||||
|
||||
@@ -941,20 +935,12 @@ export class Select implements ComponentInterface {
|
||||
* For floating labels, the label should move above the select if
|
||||
* the select has a value, is open, 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 select 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 || isExpanded || hasStartEndSlots));
|
||||
labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || isExpanded));
|
||||
|
||||
const startSlotEl = el.querySelector('[slot="start"]');
|
||||
const startSlotWidth = startSlotEl ? startSlotEl.clientWidth + 16 : 0;
|
||||
|
||||
return (
|
||||
<Host
|
||||
@@ -976,11 +962,22 @@ export class Select implements ComponentInterface {
|
||||
[`select-shape-${shape}`]: shape !== undefined,
|
||||
[`select-label-placement-${labelPlacement}`]: true,
|
||||
})}
|
||||
style={{
|
||||
'--start-slot-adjustment': `-${startSlotWidth}px`
|
||||
}}
|
||||
>
|
||||
<label class="select-wrapper" id="select-label">
|
||||
{this.renderLabelContainer()}
|
||||
<div class="select-wrapper-inner">
|
||||
{/**
|
||||
* Wrapping the start slot and label together ensures
|
||||
* they sit on the same side of the container when using
|
||||
* justify="space-between".
|
||||
*/}
|
||||
<div class="select-wrapper-inner-start">
|
||||
<slot name="start"></slot>
|
||||
{this.renderOutlineLabelContainer()}
|
||||
{this.renderLabel()}
|
||||
</div>
|
||||
<div class="select-wrapper-inner">
|
||||
<div class="native-wrapper" ref={(el) => (this.nativeWrapperEl = el)} part="container">
|
||||
{this.renderSelectText()}
|
||||
{this.renderListbox()}
|
||||
|
||||
@@ -17,6 +17,14 @@
|
||||
font-size: $textarea-ios-font-size;
|
||||
}
|
||||
|
||||
// Native Textarea
|
||||
// --------------------------------------------------
|
||||
|
||||
:host(.textarea-label-placement-floating) .native-textarea,
|
||||
:host(.textarea-label-placement-stacked) .native-textarea {
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
// Textarea - Disabled
|
||||
// ----------------------------------------------------------------
|
||||
// The textarea, label, helper text, char counter and placeholder
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
* This makes the label sit above the textarea.
|
||||
*/
|
||||
:host(.label-floating.textarea-fill-outline) .label-text-wrapper {
|
||||
@include transform(translateY(-32%), scale(#{$form-control-label-stacked-scale}));
|
||||
@include transform(translate(var(--start-slot-adjustment), -32%), scale(#{$form-control-label-stacked-scale}));
|
||||
@include margin(0);
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,14 @@
|
||||
font-size: $textarea-md-font-size;
|
||||
}
|
||||
|
||||
// Native Textarea
|
||||
// --------------------------------------------------
|
||||
|
||||
:host(.textarea-label-placement-floating:not(.textarea-fill-outline)) .native-textarea,
|
||||
:host(.textarea-label-placement-stacked:not(.textarea-fill-outline)) .native-textarea {
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
// Textarea Max Length Counter
|
||||
// ----------------------------------------------------------------
|
||||
.textarea-bottom .counter {
|
||||
|
||||
@@ -589,7 +589,7 @@
|
||||
*/
|
||||
:host(.textarea-label-placement-stacked) .textarea-wrapper,
|
||||
:host(.textarea-label-placement-floating) .textarea-wrapper {
|
||||
flex-direction: column;
|
||||
flex-direction: row;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
@@ -611,6 +611,8 @@
|
||||
* autofill background too.
|
||||
*/
|
||||
z-index: 2;
|
||||
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -628,7 +630,12 @@
|
||||
:host(.textarea-label-placement-stacked) ::slotted([slot="end"]),
|
||||
:host(.textarea-label-placement-floating) ::slotted([slot="start"]),
|
||||
:host(.textarea-label-placement-floating) ::slotted([slot="end"]) {
|
||||
margin-top: 8px;
|
||||
/**
|
||||
* 1em accounts for the padding applied to the native textarea.
|
||||
* This value may need adjusting to get everything lined up
|
||||
* in all conditions.
|
||||
*/
|
||||
margin-top: calc(1em + 8px);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -539,7 +539,7 @@ export class Textarea implements ComponentInterface {
|
||||
/**
|
||||
* Renders the border container when fill="outline".
|
||||
*/
|
||||
private renderLabelContainer() {
|
||||
private renderOutlineLabelContainer() {
|
||||
const mode = getIonMode(this);
|
||||
const hasOutlineFill = mode === 'md' && this.fill === 'outline';
|
||||
|
||||
@@ -566,14 +566,10 @@ export class Textarea implements ComponentInterface {
|
||||
</div>
|
||||
<div class="textarea-outline-end"></div>
|
||||
</div>,
|
||||
this.renderLabel(),
|
||||
];
|
||||
}
|
||||
/**
|
||||
* If not using the outline style,
|
||||
* we can render just the label.
|
||||
*/
|
||||
return this.renderLabel();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -628,27 +624,18 @@ export class Textarea implements ComponentInterface {
|
||||
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 textarea.
|
||||
* For floating labels, the label should move above the textarea if
|
||||
* the textarea 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 textarea 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));
|
||||
labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus));
|
||||
|
||||
const startSlotEl = el.querySelector('[slot="start"]');
|
||||
const startSlotWidth = startSlotEl ? startSlotEl.clientWidth + 16 : 0;
|
||||
|
||||
return (
|
||||
<Host
|
||||
@@ -662,6 +649,9 @@ export class Textarea implements ComponentInterface {
|
||||
[`textarea-label-placement-${labelPlacement}`]: true,
|
||||
'textarea-disabled': disabled,
|
||||
})}
|
||||
style={{
|
||||
'--start-slot-adjustment': `-${startSlotWidth}px`
|
||||
}}
|
||||
>
|
||||
{/**
|
||||
* htmlFor is needed so that clicking the label always focuses
|
||||
@@ -670,18 +660,19 @@ export class Textarea implements ComponentInterface {
|
||||
* since it comes before the textarea in the DOM.
|
||||
*/}
|
||||
<label class="textarea-wrapper" htmlFor={inputId}>
|
||||
{this.renderLabelContainer()}
|
||||
{/**
|
||||
* Some elements have their own padding styles which may
|
||||
* interfere with slot content alignment (such as icon-
|
||||
* only buttons setting --padding-top=0). To avoid this,
|
||||
* we wrap both the start and end slots in separate
|
||||
* elements and apply our padding styles to that instead.
|
||||
*/}
|
||||
<div class="start-slot-wrapper">
|
||||
<slot name="start"></slot>
|
||||
</div>
|
||||
{this.renderOutlineLabelContainer()}
|
||||
<div class="textarea-wrapper-inner">
|
||||
{/**
|
||||
* Some elements have their own padding styles which may
|
||||
* interfere with slot content alignment (such as icon-
|
||||
* only buttons setting --padding-top=0). To avoid this,
|
||||
* we wrap both the start and end slots in separate
|
||||
* elements and apply our padding styles to that instead.
|
||||
*/}
|
||||
<div class="start-slot-wrapper">
|
||||
<slot name="start"></slot>
|
||||
</div>
|
||||
{this.renderLabel()}
|
||||
<div class="native-wrapper" ref={(el) => (this.textareaWrapper = el)}>
|
||||
<textarea
|
||||
class="native-textarea"
|
||||
@@ -712,9 +703,9 @@ export class Textarea implements ComponentInterface {
|
||||
{value}
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="end-slot-wrapper">
|
||||
<slot name="end"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<div class="end-slot-wrapper">
|
||||
<slot name="end"></slot>
|
||||
</div>
|
||||
{shouldRenderHighlight && <div class="textarea-highlight"></div>}
|
||||
</label>
|
||||
|
||||
Reference in New Issue
Block a user