Skip to content

Commit a4ebd65

Browse files
authored
feat(textarea): fix material textarea label positioning (#1518)
1 parent eaf0c4e commit a4ebd65

File tree

3 files changed

+66
-30
lines changed

3 files changed

+66
-30
lines changed

src/components/textarea/textarea.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,11 +456,12 @@ export default class IgcTextareaComponent extends FormAssociatedRequiredMixin(
456456
part=${partNameMap({
457457
...this.resolvePartNames(),
458458
labelled: this.label,
459+
placeholder: this.placeholder,
459460
})}
460461
>
461462
<div part="start">${this.renderPrefix()}</div>
462-
<div part="notch">${this.renderLabel()}</div>
463463
${this.renderInput()}
464+
<div part="notch">${this.renderLabel()}</div>
464465
<div part="filler"></div>
465466
<div part="end">${this.renderSuffix()}</div>
466467
</div>

src/components/textarea/themes/shared/textarea.material.scss

Lines changed: 62 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,34 @@ textarea {
3333
padding-inline: rem(4px);
3434
}
3535

36+
[part='label'] {
37+
position: absolute;
38+
transform-origin: top left;
39+
inset-block-start: calc($input-top-padding - #{rem(2px)});
40+
padding-inline-end: rem(4px);
41+
transition:
42+
transform 150ms cubic-bezier(.4, 0, .2, 1),
43+
color 150ms cubic-bezier(.4, 0, .2, 1),
44+
font-size 150ms cubic-bezier(.4, 0, .2, 1);
45+
will-change: transform;
46+
}
47+
48+
%label-filled {
49+
transform: translateY(0);
50+
inset-block-start: calc(#{$input-top-padding} / 4);
51+
font-size: rem(12px);
52+
}
53+
54+
%label-outlined-filled {
55+
display: inline-block;
56+
position: relative;
57+
inset-block: 0;
58+
transform: translateY(-50%);
59+
margin-block-end: auto;
60+
padding-inline-end: 0;
61+
font-size: rem(12px);
62+
}
63+
3664
:host(:not([outlined])) {
3765
[part~='container'] {
3866
overflow: hidden;
@@ -57,6 +85,11 @@ textarea {
5785
grid-area: 1 / 2 / span 1 / span 3;
5886
}
5987
}
88+
89+
[part~='placeholder'] [part='label'],
90+
[part~='filled'] [part='label'] {
91+
@extend %label-filled;
92+
}
6093
}
6194

6295
:host(:not([outlined]):hover) {
@@ -104,6 +137,8 @@ textarea {
104137

105138
:host(:focus-within) {
106139
[part~='label'] {
140+
@extend %label-filled;
141+
107142
color: var-get($theme, 'focused-secondary-color');
108143
}
109144
}
@@ -142,19 +177,6 @@ textarea {
142177
}
143178
}
144179

145-
[part~='label'] {
146-
position: absolute;
147-
max-width: 100%;
148-
padding-inline-end: rem(4px);
149-
line-height: normal;
150-
backface-visibility: hidden;
151-
will-change: transform;
152-
transform-origin: top left;
153-
transform: translateY(0);
154-
top: calc(#{$input-top-padding} / 4);
155-
font-size: rem(12px);
156-
}
157-
158180
[part='start'],
159181
[part='end'] {
160182
display: flex;
@@ -224,6 +246,18 @@ textarea {
224246
}
225247
}
226248

249+
[part~='container'][part~='filled'],
250+
[part~='container'][part~='placeholder'] {
251+
[part='notch'] {
252+
border-block-start: rem(2px) solid transparent;
253+
}
254+
}
255+
256+
[part~='placeholder'] [part='label'],
257+
[part~='filled'] [part='label'] {
258+
@extend %label-outlined-filled;
259+
}
260+
227261
[part='start'] {
228262
width: auto;
229263
border: {
@@ -246,16 +280,6 @@ textarea {
246280
}
247281
}
248282

249-
[part~='label'] {
250-
top: 0;
251-
transform: translateY(-50%);
252-
margin-block-end: auto;
253-
padding-inline-end: 0;
254-
display: inline-block;
255-
position: relative;
256-
background: transparent;
257-
}
258-
259283
[part='filler'] {
260284
border: {
261285
width: rem(1px);
@@ -267,8 +291,7 @@ textarea {
267291
}
268292

269293
[part='notch'] {
270-
border-block-start-color: transparent;
271-
border-block-end: rem(1px) solid var-get($theme, 'border-color');
294+
border-block: rem(1px) solid var-get($theme, 'border-color');
272295
padding: 0 rem(4px);
273296

274297
&:empty {
@@ -313,11 +336,16 @@ textarea {
313336
border-block-width: rem(2px);
314337
}
315338

339+
[part='label'] {
340+
@extend %label-outlined-filled;
341+
}
342+
316343
[part='filler'] {
317344
border-block-color: var-get($theme, 'focused-border-color');
318345
}
319346

320347
[part='notch'] {
348+
border-block-start-color: transparent;
321349
border-block-end-color: var-get($theme, 'focused-border-color');
322350
}
323351

@@ -364,12 +392,19 @@ textarea {
364392
:host([invalid][outlined]:not([disabled]):focus-within) {
365393
[part='start'],
366394
[part='filler'],
367-
[part='notch'],
368-
[part='end'] {
395+
[part='end'],
396+
[part='notch'] {
369397
border-color: var-get($theme, 'error-secondary-color');
370398
}
371399
}
372400

401+
:host([invalid][outlined]:not([disabled]):focus-within),
402+
:host([invalid][outlined]:not([disabled])) [part~='placeholder'] {
403+
[part='notch'] {
404+
border-block-start-color: transparent;
405+
}
406+
}
407+
373408
:host(:disabled[outlined]),
374409
:host([disabled][outlined]) {
375410
[part~='container'] {

stories/textarea.stories.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { sourceCode } from '@igniteui/material-icons-extended';
22
import type { Meta, StoryObj } from '@storybook/web-components';
3-
import { html, render } from 'lit';
3+
import { html, nothing, render } from 'lit';
44

55
import {
66
IgcIconComponent,
@@ -267,7 +267,7 @@ export const Default: Story = {
267267
name=${ifDefined(args.name)}
268268
label=${args.label}
269269
rows=${args.rows}
270-
placeholder=${args.placeholder}
270+
placeholder=${args.placeholder || nothing}
271271
resize=${args.resize}
272272
value=${ifDefined(args.value)}
273273
minlength=${ifDefined(args.minLength)}

0 commit comments

Comments
 (0)