Compare commits

...

4 Commits

Author SHA1 Message Date
amandaesmith3
819d48edee add comments, extra space cleanup 2024-03-18 13:17:02 -05:00
amandaesmith3
cd1213ef04 add POC for select (incomplete) 2024-03-18 12:17:10 -05:00
amandaesmith3
343d0ae7a5 add POC for textarea 2024-03-18 12:17:00 -05:00
amandaesmith3
2282f7f8c3 add POC for input 2024-03-18 12:16:53 -05:00
12 changed files with 117 additions and 107 deletions

View File

@@ -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

View File

@@ -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);
/**

View File

@@ -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

View File

@@ -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;

View File

@@ -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()}

View File

@@ -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;
}

View File

@@ -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()}

View File

@@ -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

View File

@@ -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);
/**

View File

@@ -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 {

View File

@@ -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);
}
/**

View File

@@ -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>