@justeattakeaway/pie-textarea 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,13 +28,21 @@
28
28
  },
29
29
  "default": "['auto', 'manual']"
30
30
  },
31
+ {
32
+ "kind": "variable",
33
+ "name": "statusTypes",
34
+ "type": {
35
+ "text": "['default', 'success', 'error']"
36
+ },
37
+ "default": "['default', 'success', 'error']"
38
+ },
31
39
  {
32
40
  "kind": "variable",
33
41
  "name": "defaultProps",
34
42
  "type": {
35
43
  "text": "DefaultProps"
36
44
  },
37
- "default": "{\n disabled: false,\n size: 'medium',\n resize: 'auto',\n label: '',\n value: '',\n autoFocus: false,\n readonly: false,\n required: false,\n}",
45
+ "default": "{\n disabled: false,\n size: 'medium',\n resize: 'auto',\n label: '',\n value: '',\n status: 'default',\n autoFocus: false,\n readonly: false,\n required: false,\n}",
38
46
  "description": "Default values for optional properties that have default fallback values in the component."
39
47
  }
40
48
  ],
@@ -55,6 +63,14 @@
55
63
  "module": "src/defs.js"
56
64
  }
57
65
  },
66
+ {
67
+ "kind": "js",
68
+ "name": "statusTypes",
69
+ "declaration": {
70
+ "name": "statusTypes",
71
+ "module": "src/defs.js"
72
+ }
73
+ },
58
74
  {
59
75
  "kind": "js",
60
76
  "name": "defaultProps",
@@ -150,11 +166,26 @@
150
166
  "privacy": "public",
151
167
  "attribute": "required"
152
168
  },
169
+ {
170
+ "kind": "field",
171
+ "name": "status",
172
+ "privacy": "public",
173
+ "attribute": "status"
174
+ },
175
+ {
176
+ "kind": "field",
177
+ "name": "assistiveText",
178
+ "type": {
179
+ "text": "TextareaProps['assistiveText']"
180
+ },
181
+ "privacy": "public",
182
+ "attribute": "assistiveText"
183
+ },
153
184
  {
154
185
  "kind": "field",
155
186
  "name": "name",
156
187
  "type": {
157
- "text": "TextareaProps['name'] | undefined"
188
+ "text": "TextareaProps['name']"
158
189
  },
159
190
  "privacy": "public",
160
191
  "attribute": "name"
@@ -163,7 +194,7 @@
163
194
  "kind": "field",
164
195
  "name": "autocomplete",
165
196
  "type": {
166
- "text": "TextareaProps['autocomplete'] | undefined"
197
+ "text": "TextareaProps['autocomplete']"
167
198
  },
168
199
  "privacy": "public",
169
200
  "attribute": "autocomplete"
@@ -176,6 +207,14 @@
176
207
  },
177
208
  "privacy": "private"
178
209
  },
210
+ {
211
+ "kind": "field",
212
+ "name": "focusTarget",
213
+ "type": {
214
+ "text": "HTMLElement"
215
+ },
216
+ "privacy": "public"
217
+ },
179
218
  {
180
219
  "kind": "field",
181
220
  "name": "_throttledResize",
@@ -272,6 +311,10 @@
272
311
  }
273
312
  }
274
313
  ]
314
+ },
315
+ {
316
+ "kind": "method",
317
+ "name": "renderAssistiveText"
275
318
  }
276
319
  ],
277
320
  "events": [
@@ -337,17 +380,28 @@
337
380
  "name": "required",
338
381
  "fieldName": "required"
339
382
  },
383
+ {
384
+ "name": "status",
385
+ "fieldName": "status"
386
+ },
387
+ {
388
+ "name": "assistiveText",
389
+ "type": {
390
+ "text": "TextareaProps['assistiveText']"
391
+ },
392
+ "fieldName": "assistiveText"
393
+ },
340
394
  {
341
395
  "name": "name",
342
396
  "type": {
343
- "text": "TextareaProps['name'] | undefined"
397
+ "text": "TextareaProps['name']"
344
398
  },
345
399
  "fieldName": "name"
346
400
  },
347
401
  {
348
402
  "name": "autocomplete",
349
403
  "type": {
350
- "text": "TextareaProps['autocomplete'] | undefined"
404
+ "text": "TextareaProps['autocomplete']"
351
405
  },
352
406
  "fieldName": "autocomplete"
353
407
  }
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ import type { FormControlInterface } from '@justeattakeaway/pie-webc-core';
4
4
  import type { GenericConstructor } from '@justeattakeaway/pie-webc-core';
5
5
  import type { LitElement } from 'lit';
6
6
  import type { nothing } from 'lit';
7
+ import type { PIEInputElement } from '@justeattakeaway/pie-webc-core';
7
8
  import type { PropertyValues } from 'lit';
8
9
  import type { RTLInterface } from '@justeattakeaway/pie-webc-core';
9
10
  import type { TemplateResult } from 'lit-html';
@@ -11,7 +12,7 @@ import type { TemplateResult } from 'lit-html';
11
12
  /**
12
13
  * The default values for the `TextareaProps` that are required (i.e. they have a fallback value in the component).
13
14
  */
14
- declare type DefaultProps = ComponentDefaultProps<TextareaProps, keyof Omit<TextareaProps, 'name' | 'autocomplete' | 'maxLength' | 'defaultValue'>>;
15
+ declare type DefaultProps = ComponentDefaultProps<TextareaProps, keyof Omit<TextareaProps, 'name' | 'autocomplete' | 'maxLength' | 'assistiveText' | 'defaultValue'>>;
15
16
 
16
17
  /**
17
18
  * Default values for optional properties that have default fallback values in the component.
@@ -23,7 +24,7 @@ export declare const defaultProps: DefaultProps;
23
24
  * @event {InputEvent} input - when the textarea value is changed.
24
25
  * @event {CustomEvent} change - when the textarea value is changed.
25
26
  */
26
- export declare class PieTextarea extends PieTextarea_base implements TextareaProps {
27
+ export declare class PieTextarea extends PieTextarea_base implements TextareaProps, PIEInputElement {
27
28
  static shadowRootOptions: {
28
29
  delegatesFocus: boolean;
29
30
  mode: ShadowRootMode;
@@ -39,9 +40,12 @@ export declare class PieTextarea extends PieTextarea_base implements TextareaPro
39
40
  readonly: boolean;
40
41
  autoFocus: boolean;
41
42
  required: boolean;
42
- name?: TextareaProps['name'];
43
- autocomplete?: TextareaProps['autocomplete'];
43
+ status: "default" | "error" | "success";
44
+ assistiveText: TextareaProps['assistiveText'];
45
+ name: TextareaProps['name'];
46
+ autocomplete: TextareaProps['autocomplete'];
44
47
  private _textarea;
48
+ focusTarget: HTMLElement;
45
49
  private _throttledResize;
46
50
  /**
47
51
  * (Read-only) returns a ValidityState with the validity states that this element is in.
@@ -73,6 +77,7 @@ export declare class PieTextarea extends PieTextarea_base implements TextareaPro
73
77
  private handleKeyDown;
74
78
  disconnectedCallback(): void;
75
79
  renderLabel(label: string, maxLength?: number): TemplateResult<1> | typeof nothing;
80
+ renderAssistiveText(): TemplateResult<1> | typeof nothing;
76
81
  render(): TemplateResult<1>;
77
82
  static styles: CSSResult;
78
83
  }
@@ -83,6 +88,8 @@ export declare const resizeModes: readonly ["auto", "manual"];
83
88
 
84
89
  export declare const sizes: readonly ["small", "medium", "large"];
85
90
 
91
+ export declare const statusTypes: readonly ["default", "success", "error"];
92
+
86
93
  export declare interface TextareaProps {
87
94
  /**
88
95
  * Same as the HTML disabled attribute - indicates whether the textarea is disabled.
@@ -106,6 +113,14 @@ export declare interface TextareaProps {
106
113
  * An optional default value to use when the textarea is reset.
107
114
  */
108
115
  defaultValue?: string;
116
+ /**
117
+ * An optional assistive text to display below the textarea element. Must be provided when the status is success or error.
118
+ */
119
+ assistiveText?: string;
120
+ /**
121
+ * The status of the textarea component / assistive text. Can be default, success or error.
122
+ */
123
+ status?: typeof statusTypes[number];
109
124
  /**
110
125
  * The name of the textarea (used as a key/value pair with `value`). This is required in order to work properly with forms.
111
126
  */
package/dist/index.js CHANGED
@@ -1,124 +1,127 @@
1
- import { LitElement as w, html as I, nothing as q, unsafeCSS as B } from "lit";
2
- import { property as c, query as N } from "lit/decorators.js";
3
- import { live as A } from "lit/directives/live.js";
4
- import { FormControlMixin as K, RtlMixin as G, wrapNativeEvent as H, validPropertyValues as F, defineCustomElement as U } from "@justeattakeaway/pie-webc-core";
5
- import { ifDefined as O } from "lit/directives/if-defined.js";
1
+ import { LitElement as M, html as L, nothing as F, unsafeCSS as A } from "lit";
2
+ import { property as l, query as P } from "lit/decorators.js";
3
+ import { classMap as K } from "lit/directives/class-map.js";
4
+ import { ifDefined as b } from "lit/directives/if-defined.js";
5
+ import { live as G } from "lit/directives/live.js";
6
+ import "@justeattakeaway/pie-assistive-text";
6
7
  import "@justeattakeaway/pie-form-label";
7
- var z = typeof globalThis < "u" ? globalThis : typeof window < "u" ? window : typeof global < "u" ? global : typeof self < "u" ? self : {};
8
- function X(t) {
8
+ import { FormControlMixin as H, RtlMixin as U, wrapNativeEvent as X, validPropertyValues as O, defineCustomElement as J } from "@justeattakeaway/pie-webc-core";
9
+ var T = typeof globalThis < "u" ? globalThis : typeof window < "u" ? window : typeof global < "u" ? global : typeof self < "u" ? self : {};
10
+ function Q(t) {
9
11
  return t && t.__esModule && Object.prototype.hasOwnProperty.call(t, "default") ? t.default : t;
10
12
  }
11
- var R = "Expected a function", C = 0 / 0, J = "[object Symbol]", Q = /^\s+|\s+$/g, Y = /^[-+]0x[0-9a-f]+$/i, Z = /^0b[01]+$/i, ee = /^0o[0-7]+$/i, te = parseInt, ae = typeof z == "object" && z && z.Object === Object && z, re = typeof self == "object" && self && self.Object === Object && self, ie = ae || re || Function("return this")(), ne = Object.prototype, oe = ne.toString, se = Math.max, le = Math.min, S = function() {
12
- return ie.Date.now();
13
+ var V = "Expected a function", R = 0 / 0, Y = "[object Symbol]", Z = /^\s+|\s+$/g, ee = /^[-+]0x[0-9a-f]+$/i, te = /^0b[01]+$/i, ae = /^0o[0-7]+$/i, re = parseInt, ie = typeof T == "object" && T && T.Object === Object && T, ne = typeof self == "object" && self && self.Object === Object && self, oe = ie || ne || Function("return this")(), se = Object.prototype, le = se.toString, de = Math.max, ce = Math.min, j = function() {
14
+ return oe.Date.now();
13
15
  };
14
- function de(t, e, a) {
15
- var r, i, u, d, s, p, g = 0, m = !1, v = !1, _ = !0;
16
+ function ue(t, e, a) {
17
+ var r, i, h, u, d, p, x = 0, k = !1, v = !1, g = !0;
16
18
  if (typeof t != "function")
17
- throw new TypeError(R);
18
- e = W(e) || 0, k(a) && (m = !!a.leading, v = "maxWait" in a, u = v ? se(W(a.maxWait) || 0, e) : u, _ = "trailing" in a ? !!a.trailing : _);
19
- function $(n) {
20
- var f = r, b = i;
21
- return r = i = void 0, g = n, d = t.apply(b, f), d;
19
+ throw new TypeError(V);
20
+ e = D(e) || 0, _(a) && (k = !!a.leading, v = "maxWait" in a, h = v ? de(D(a.maxWait) || 0, e) : h, g = "trailing" in a ? !!a.trailing : g);
21
+ function y(n) {
22
+ var f = r, z = i;
23
+ return r = i = void 0, x = n, u = t.apply(z, f), u;
22
24
  }
23
- function D(n) {
24
- return g = n, s = setTimeout(y, e), m ? $(n) : d;
25
+ function w(n) {
26
+ return x = n, d = setTimeout($, e), k ? y(n) : u;
25
27
  }
26
- function P(n) {
27
- var f = n - p, b = n - g, E = e - f;
28
- return v ? le(E, u - b) : E;
28
+ function q(n) {
29
+ var f = n - p, z = n - x, C = e - f;
30
+ return v ? ce(C, h - z) : C;
29
31
  }
30
- function L(n) {
31
- var f = n - p, b = n - g;
32
- return p === void 0 || f >= e || f < 0 || v && b >= u;
32
+ function I(n) {
33
+ var f = n - p, z = n - x;
34
+ return p === void 0 || f >= e || f < 0 || v && z >= h;
33
35
  }
34
- function y() {
35
- var n = S();
36
- if (L(n))
37
- return T(n);
38
- s = setTimeout(y, P(n));
36
+ function $() {
37
+ var n = j();
38
+ if (I(n))
39
+ return W(n);
40
+ d = setTimeout($, q(n));
39
41
  }
40
- function T(n) {
41
- return s = void 0, _ && r ? $(n) : (r = i = void 0, d);
42
+ function W(n) {
43
+ return d = void 0, g && r ? y(n) : (r = i = void 0, u);
42
44
  }
43
- function M() {
44
- s !== void 0 && clearTimeout(s), g = 0, r = p = i = s = void 0;
45
+ function B() {
46
+ d !== void 0 && clearTimeout(d), x = 0, r = p = i = d = void 0;
45
47
  }
46
- function V() {
47
- return s === void 0 ? d : T(S());
48
+ function N() {
49
+ return d === void 0 ? u : W(j());
48
50
  }
49
- function j() {
50
- var n = S(), f = L(n);
51
+ function S() {
52
+ var n = j(), f = I(n);
51
53
  if (r = arguments, i = this, p = n, f) {
52
- if (s === void 0)
53
- return D(p);
54
+ if (d === void 0)
55
+ return w(p);
54
56
  if (v)
55
- return s = setTimeout(y, e), $(p);
57
+ return d = setTimeout($, e), y(p);
56
58
  }
57
- return s === void 0 && (s = setTimeout(y, e)), d;
59
+ return d === void 0 && (d = setTimeout($, e)), u;
58
60
  }
59
- return j.cancel = M, j.flush = V, j;
61
+ return S.cancel = B, S.flush = N, S;
60
62
  }
61
- function ce(t, e, a) {
63
+ function he(t, e, a) {
62
64
  var r = !0, i = !0;
63
65
  if (typeof t != "function")
64
- throw new TypeError(R);
65
- return k(a) && (r = "leading" in a ? !!a.leading : r, i = "trailing" in a ? !!a.trailing : i), de(t, e, {
66
+ throw new TypeError(V);
67
+ return _(a) && (r = "leading" in a ? !!a.leading : r, i = "trailing" in a ? !!a.trailing : i), ue(t, e, {
66
68
  leading: r,
67
69
  maxWait: e,
68
70
  trailing: i
69
71
  });
70
72
  }
71
- function k(t) {
73
+ function _(t) {
72
74
  var e = typeof t;
73
75
  return !!t && (e == "object" || e == "function");
74
76
  }
75
- function he(t) {
77
+ function pe(t) {
76
78
  return !!t && typeof t == "object";
77
79
  }
78
- function ue(t) {
79
- return typeof t == "symbol" || he(t) && oe.call(t) == J;
80
+ function fe(t) {
81
+ return typeof t == "symbol" || pe(t) && le.call(t) == Y;
80
82
  }
81
- function W(t) {
83
+ function D(t) {
82
84
  if (typeof t == "number")
83
85
  return t;
84
- if (ue(t))
85
- return C;
86
- if (k(t)) {
86
+ if (fe(t))
87
+ return R;
88
+ if (_(t)) {
87
89
  var e = typeof t.valueOf == "function" ? t.valueOf() : t;
88
- t = k(e) ? e + "" : e;
90
+ t = _(e) ? e + "" : e;
89
91
  }
90
92
  if (typeof t != "string")
91
93
  return t === 0 ? t : +t;
92
- t = t.replace(Q, "");
93
- var a = Z.test(t);
94
- return a || ee.test(t) ? te(t.slice(2), a ? 2 : 8) : Y.test(t) ? C : +t;
94
+ t = t.replace(Z, "");
95
+ var a = te.test(t);
96
+ return a || ae.test(t) ? re(t.slice(2), a ? 2 : 8) : ee.test(t) ? R : +t;
95
97
  }
96
- var pe = ce;
97
- const fe = /* @__PURE__ */ X(pe), ge = `*,*:after,*:before{box-sizing:inherit}.c-textareaWrapper{--textarea-line-height: calc(var(--dt-font-body-l-line-height) * 1px);--textarea-border-thickness: 1px;--textarea-resize: none;--textarea-padding-inline: var(--dt-spacing-d);--textarea-padding-block: var(--dt-spacing-c);--textarea-background-color: var(--dt-color-container-default);--textarea-border-color: var(--dt-color-interactive-form);--textarea-content-color: var(--dt-color-content-default);--textarea-height: calc((var(--textarea-line-height) * 2) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));line-height:0}.c-textareaWrapper textarea{font-size:calc(var(--dt-font-body-l-size) * 1px);line-height:var(--textarea-line-height);font-family:var(--dt-font-body-l-family);resize:var(--textarea-resize);border:var(--textarea-border-thickness) solid var(--textarea-border-color);background-color:var(--textarea-background-color);color:var(--textarea-content-color);border-radius:var(--dt-radius-rounded-c);block-size:var(--textarea-height);max-block-size:var(--textarea-max-height);min-block-size:var(--textarea-min-height);padding-block-start:var(--textarea-padding-block);padding-block-end:var(--textarea-padding-block);padding-inline-start:var(--textarea-padding-inline);padding-inline-end:var(--textarea-padding-inline)}.c-textareaWrapper textarea[disabled]{--textarea-background-color: var(--dt-color-disabled-01);--textarea-border-color: var(--dt-color-disabled-01);--textarea-content-color: var(--dt-color-content-disabled)}@media (hover: hover){.c-textareaWrapper textarea:hover:not([disabled]){--textarea-background-color: hsl(var(--dt-color-container-default-h), var(--dt-color-container-default-s), calc(var(--dt-color-container-default-l) + calc(-1 * var(--dt-color-hover-01))))}}.c-textareaWrapper textarea:focus-visible{box-shadow:0 0 0 2px var(--dt-color-focus-inner),0 0 0 4px var(--dt-color-focus-outer);outline:none}.c-textareaWrapper[data-pie-size=large]{--textarea-padding-block: var(--dt-spacing-d)}.c-textareaWrapper[data-pie-size=small]{--textarea-padding-block: var(--dt-spacing-b)}.c-textareaWrapper[data-pie-resize=manual]{--textarea-resize: vertical;--textarea-min-height: calc((var(--textarea-line-height) * 1) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2))}@media (pointer: coarse){.c-textareaWrapper[data-pie-resize=manual]{--textarea-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));--textarea-min-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));--textarea-max-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));--textarea-resize: none}}.c-textareaWrapper[data-pie-resize=auto]{--textarea-max-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));--textarea-min-height: var(--textarea-height)}
98
- `, ve = ["small", "medium", "large"], xe = ["auto", "manual"], h = {
98
+ var xe = he;
99
+ const ve = /* @__PURE__ */ Q(xe), ge = `*,*:after,*:before{box-sizing:inherit}.c-textareaWrapper{--textarea-line-height: calc(var(--dt-font-body-l-line-height) * 1px);--textarea-border-thickness: 1px;--textarea-resize: none;--textarea-padding-inline: var(--dt-spacing-c);--textarea-padding-block: var(--dt-spacing-b);--textarea-background-color: var(--dt-color-container-default);--textarea-border-color: var(--dt-color-interactive-form);--textarea-content-color: var(--dt-color-content-default);--textarea-height: calc((var(--textarea-line-height) * 2) + (var(--textarea-padding-block) * 2));line-height:0;padding:var(--dt-spacing-a);border:var(--textarea-border-thickness) solid var(--textarea-border-color);background-color:var(--textarea-background-color);border-radius:var(--dt-radius-rounded-c);inline-size:100%}.c-textareaWrapper textarea{font-size:calc(var(--dt-font-body-l-size) * 1px);line-height:var(--textarea-line-height);font-family:var(--dt-font-body-l-family);resize:var(--textarea-resize);border:none;background-color:var(--textarea-background-color);color:var(--textarea-content-color);block-size:var(--textarea-height);max-block-size:var(--textarea-max-height);min-block-size:var(--textarea-min-height);inline-size:100%;padding-block-start:var(--textarea-padding-block);padding-block-end:var(--textarea-padding-block);padding-inline-start:var(--textarea-padding-inline);padding-inline-end:var(--textarea-padding-inline)}.c-textareaWrapper textarea:focus{box-shadow:none;outline:none}.c-textareaWrapper.c-textarea--disabled{--textarea-background-color: var(--dt-color-disabled-01);--textarea-border-color: var(--dt-color-disabled-01);--textarea-content-color: var(--dt-color-content-disabled)}@media (hover: hover){.c-textareaWrapper:hover:not(.c-textarea--disabled){--textarea-background-color: hsl(var(--dt-color-container-default-h), var(--dt-color-container-default-s), calc(var(--dt-color-container-default-l) + calc(-1 * var(--dt-color-hover-01))))}}.c-textareaWrapper:focus-within{box-shadow:0 0 0 2px var(--dt-color-focus-inner),0 0 0 4px var(--dt-color-focus-outer);outline:none}.c-textareaWrapper.c-textarea--large{--textarea-padding-block: var(--dt-spacing-c)}.c-textareaWrapper.c-textarea--small{--textarea-padding-block: var(--dt-spacing-a)}.c-textareaWrapper.c-textarea--resize-manual{--textarea-resize: vertical;--textarea-min-height: calc((var(--textarea-line-height) * 1) + (var(--textarea-padding-block) * 2))}@media (pointer: coarse){.c-textareaWrapper.c-textarea--resize-manual{--textarea-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2));--textarea-min-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2));--textarea-max-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2));--textarea-resize: none}}.c-textareaWrapper.c-textarea--resize-auto{--textarea-max-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2));--textarea-min-height: var(--textarea-height)}.c-textareaWrapper.c-textarea--error{--textarea-border-color: var(--dt-color-support-error)}
100
+ `, me = ["small", "medium", "large"], be = ["auto", "manual"], ye = ["default", "success", "error"], c = {
99
101
  disabled: !1,
100
102
  size: "medium",
101
103
  resize: "auto",
102
104
  label: "",
103
105
  value: "",
106
+ status: "default",
104
107
  autoFocus: !1,
105
108
  readonly: !1,
106
109
  required: !1
107
110
  };
108
- var be = Object.defineProperty, me = Object.getOwnPropertyDescriptor, l = (t, e, a, r) => {
109
- for (var i = r > 1 ? void 0 : r ? me(e, a) : e, u = t.length - 1, d; u >= 0; u--)
110
- (d = t[u]) && (i = (r ? d(e, a, i) : d(i)) || i);
111
- return r && i && be(e, a, i), i;
111
+ var ze = Object.defineProperty, ke = Object.getOwnPropertyDescriptor, s = (t, e, a, r) => {
112
+ for (var i = r > 1 ? void 0 : r ? ke(e, a) : e, h = t.length - 1, u; h >= 0; h--)
113
+ (u = t[h]) && (i = (r ? u(e, a, i) : u(i)) || i);
114
+ return r && i && ze(e, a, i), i;
112
115
  };
113
- const x = "pie-textarea";
114
- class o extends K(G(w)) {
116
+ const m = "pie-textarea", E = "assistive-text";
117
+ class o extends H(U(M)) {
115
118
  constructor() {
116
- super(...arguments), this.value = h.value, this.disabled = h.disabled, this.size = h.size, this.resize = h.resize, this.label = h.label, this.readonly = h.readonly, this.autoFocus = h.autoFocus, this.required = h.required, this._throttledResize = fe(() => {
117
- this.resize === "auto" && (this._textarea.style.height = "auto", this._textarea.style.height = `${this._textarea.scrollHeight + 2}px`);
119
+ super(...arguments), this.value = c.value, this.disabled = c.disabled, this.size = c.size, this.resize = c.resize, this.label = c.label, this.readonly = c.readonly, this.autoFocus = c.autoFocus, this.required = c.required, this.status = c.status, this._throttledResize = ve(() => {
120
+ this.resize === "auto" && (this._textarea.style.height = "auto", this._textarea.style.height = `${this._textarea.scrollHeight}px`);
118
121
  }, 100), this.handleInput = (e) => {
119
122
  this.value = e.target.value, this.restrictInputLength(), this._internals.setFormValue(this.value), this.handleResize();
120
123
  }, this.handleChange = (e) => {
121
- const a = H(e);
124
+ const a = X(e);
122
125
  this.dispatchEvent(a);
123
126
  }, this.handleKeyDown = (e) => {
124
127
  e.key === "Enter" && e.stopPropagation();
@@ -145,10 +148,10 @@ class o extends K(G(w)) {
145
148
  * Resets the value to the default value.
146
149
  */
147
150
  formResetCallback() {
148
- this.value = this.defaultValue ?? h.value, this._internals.setFormValue(this.value);
151
+ this.value = this.defaultValue ?? c.value, this._internals.setFormValue(this.value);
149
152
  }
150
153
  firstUpdated() {
151
- this.restrictInputLength(), this._internals.setFormValue(this.value), this._textarea.addEventListener("keydown", this.handleKeyDown);
154
+ this.restrictInputLength(), this._internals.setFormValue(this.value), window.addEventListener("resize", () => this.handleResize()), this._textarea.addEventListener("keydown", this.handleKeyDown);
152
155
  }
153
156
  handleResize() {
154
157
  this._throttledResize();
@@ -163,11 +166,21 @@ class o extends K(G(w)) {
163
166
  e.has("value") && (this.restrictInputLength(), this._internals.setFormValue(this.value)), this.resize === "auto" && (e.has("resize") || e.has("size")) && this.handleResize();
164
167
  }
165
168
  disconnectedCallback() {
166
- this._textarea.removeEventListener("keydown", this.handleKeyDown);
169
+ this._textarea.removeEventListener("keydown", this.handleKeyDown), window.removeEventListener("resize", () => this.handleResize());
167
170
  }
168
171
  renderLabel(e, a) {
169
172
  const r = a ? `${this.value.length}/${a}` : void 0;
170
- return e != null && e.length ? I`<pie-form-label for="${x}" trailing=${O(r)}>${e}</pie-form-label>` : q;
173
+ return e != null && e.length ? L`<pie-form-label for="${m}" trailing=${b(r)}>${e}</pie-form-label>` : F;
174
+ }
175
+ renderAssistiveText() {
176
+ return this.assistiveText ? L`
177
+ <pie-assistive-text
178
+ id="${E}"
179
+ variant=${b(this.status)}
180
+ data-test-id="pie-textarea-assistive-text">
181
+ ${this.assistiveText}
182
+ </pie-assistive-text>
183
+ ` : F;
171
184
  }
172
185
  render() {
173
186
  const {
@@ -175,84 +188,106 @@ class o extends K(G(w)) {
175
188
  resize: a,
176
189
  size: r,
177
190
  autocomplete: i,
178
- autoFocus: u,
179
- name: d,
180
- readonly: s,
191
+ autoFocus: h,
192
+ name: u,
193
+ readonly: d,
181
194
  value: p,
182
- required: g,
183
- label: m,
184
- maxLength: v
185
- } = this;
186
- return I`
195
+ required: x,
196
+ label: k,
197
+ maxLength: v,
198
+ status: g,
199
+ assistiveText: y
200
+ } = this, w = {
201
+ "c-textareaWrapper": !0,
202
+ "c-textarea--disabled": e,
203
+ "c-textarea--error": g === "error",
204
+ [`c-textarea--resize-${a}`]: !0,
205
+ [`c-textarea--${r}`]: !0
206
+ };
207
+ return L`<div>
208
+ ${this.renderLabel(k, v)}
187
209
  <div
188
- class="c-textareaWrapper"
189
- data-test-id="pie-textarea-wrapper"
190
- data-pie-size="${r}"
191
- data-pie-resize="${a}">
192
- ${this.renderLabel(m, v)}
210
+ class="${K(w)}"
211
+ data-test-id="pie-textarea-wrapper">
193
212
  <textarea
194
- id="${x}"
195
- data-test-id="${x}"
196
- name=${O(d)}
197
- autocomplete=${O(i)}
198
- .value=${A(p)}
199
- ?autofocus=${u}
200
- ?readonly=${s}
201
- ?required=${g}
213
+ id="${m}"
214
+ data-test-id="${m}"
215
+ name=${b(u)}
216
+ autocomplete=${b(i)}
217
+ .value=${G(p)}
218
+ ?autofocus=${h}
219
+ ?readonly=${d}
220
+ ?required=${x}
202
221
  ?disabled=${e}
222
+ aria-describedby=${b(y ? E : void 0)}
223
+ aria-invalid=${g === "error" ? "true" : "false"}
224
+ aria-errormessage="${b(g === "error" ? E : void 0)}"
203
225
  @input=${this.handleInput}
204
226
  @change=${this.handleChange}
205
227
  ></textarea>
206
- </div>`;
228
+ </div>
229
+ ${this.renderAssistiveText()}
230
+ </div>`;
207
231
  }
208
232
  }
209
- o.shadowRootOptions = { ...w.shadowRootOptions, delegatesFocus: !0 };
210
- o.styles = B(ge);
211
- l([
212
- c({ type: String })
233
+ o.shadowRootOptions = { ...M.shadowRootOptions, delegatesFocus: !0 };
234
+ o.styles = A(ge);
235
+ s([
236
+ l({ type: String })
213
237
  ], o.prototype, "value", 2);
214
- l([
215
- c({ type: String })
238
+ s([
239
+ l({ type: String })
216
240
  ], o.prototype, "defaultValue", 2);
217
- l([
218
- c({ type: Boolean, reflect: !0 })
241
+ s([
242
+ l({ type: Boolean, reflect: !0 })
219
243
  ], o.prototype, "disabled", 2);
220
- l([
221
- c({ type: String }),
222
- F(x, ve, h.size)
244
+ s([
245
+ l({ type: String }),
246
+ O(m, me, c.size)
223
247
  ], o.prototype, "size", 2);
224
- l([
225
- c({ type: String }),
226
- F(x, xe, h.resize)
248
+ s([
249
+ l({ type: String }),
250
+ O(m, be, c.resize)
227
251
  ], o.prototype, "resize", 2);
228
- l([
229
- c({ type: String })
252
+ s([
253
+ l({ type: String })
230
254
  ], o.prototype, "label", 2);
231
- l([
232
- c({ type: Number })
255
+ s([
256
+ l({ type: Number })
233
257
  ], o.prototype, "maxLength", 2);
234
- l([
235
- c({ type: Boolean })
258
+ s([
259
+ l({ type: Boolean })
236
260
  ], o.prototype, "readonly", 2);
237
- l([
238
- c({ type: Boolean })
261
+ s([
262
+ l({ type: Boolean })
239
263
  ], o.prototype, "autoFocus", 2);
240
- l([
241
- c({ type: Boolean })
264
+ s([
265
+ l({ type: Boolean })
242
266
  ], o.prototype, "required", 2);
243
- l([
244
- c({ type: String })
267
+ s([
268
+ l({ type: String }),
269
+ O(m, ye, c.status)
270
+ ], o.prototype, "status", 2);
271
+ s([
272
+ l({ type: String })
273
+ ], o.prototype, "assistiveText", 2);
274
+ s([
275
+ l({ type: String })
245
276
  ], o.prototype, "name", 2);
246
- l([
247
- c({ type: String })
277
+ s([
278
+ l({ type: String })
248
279
  ], o.prototype, "autocomplete", 2);
249
- l([
250
- N("textarea")
280
+ s([
281
+ P("textarea")
251
282
  ], o.prototype, "_textarea", 2);
252
- U(x, o);
283
+ s([
284
+ P("textarea")
285
+ ], o.prototype, "focusTarget", 2);
286
+ J(m, o);
253
287
  export {
254
288
  o as PieTextarea,
255
- h as defaultProps,
256
- xe as resizeModes,
257
- ve as sizes
289
+ c as defaultProps,
290
+ be as resizeModes,
291
+ me as sizes,
292
+ ye as statusTypes
258
293
  };
package/dist/react.d.ts CHANGED
@@ -4,6 +4,7 @@ import type { FormControlInterface } from '@justeattakeaway/pie-webc-core';
4
4
  import type { GenericConstructor } from '@justeattakeaway/pie-webc-core';
5
5
  import type { LitElement } from 'lit';
6
6
  import type { nothing } from 'lit';
7
+ import type { PIEInputElement } from '@justeattakeaway/pie-webc-core';
7
8
  import type { PropertyValues } from 'lit';
8
9
  import * as React_2 from 'react';
9
10
  import type { RTLInterface } from '@justeattakeaway/pie-webc-core';
@@ -12,7 +13,7 @@ import type { TemplateResult } from 'lit-html';
12
13
  /**
13
14
  * The default values for the `TextareaProps` that are required (i.e. they have a fallback value in the component).
14
15
  */
15
- declare type DefaultProps = ComponentDefaultProps<TextareaProps, keyof Omit<TextareaProps, 'name' | 'autocomplete' | 'maxLength' | 'defaultValue'>>;
16
+ declare type DefaultProps = ComponentDefaultProps<TextareaProps, keyof Omit<TextareaProps, 'name' | 'autocomplete' | 'maxLength' | 'assistiveText' | 'defaultValue'>>;
16
17
 
17
18
  /**
18
19
  * Default values for optional properties that have default fallback values in the component.
@@ -26,7 +27,7 @@ export declare const PieTextarea: React_2.ForwardRefExoticComponent<TextareaProp
26
27
  * @event {InputEvent} input - when the textarea value is changed.
27
28
  * @event {CustomEvent} change - when the textarea value is changed.
28
29
  */
29
- declare class PieTextarea_2 extends PieTextarea_base implements TextareaProps {
30
+ declare class PieTextarea_2 extends PieTextarea_base implements TextareaProps, PIEInputElement {
30
31
  static shadowRootOptions: {
31
32
  delegatesFocus: boolean;
32
33
  mode: ShadowRootMode;
@@ -42,9 +43,12 @@ declare class PieTextarea_2 extends PieTextarea_base implements TextareaProps {
42
43
  readonly: boolean;
43
44
  autoFocus: boolean;
44
45
  required: boolean;
45
- name?: TextareaProps['name'];
46
- autocomplete?: TextareaProps['autocomplete'];
46
+ status: "default" | "error" | "success";
47
+ assistiveText: TextareaProps['assistiveText'];
48
+ name: TextareaProps['name'];
49
+ autocomplete: TextareaProps['autocomplete'];
47
50
  private _textarea;
51
+ focusTarget: HTMLElement;
48
52
  private _throttledResize;
49
53
  /**
50
54
  * (Read-only) returns a ValidityState with the validity states that this element is in.
@@ -76,6 +80,7 @@ declare class PieTextarea_2 extends PieTextarea_base implements TextareaProps {
76
80
  private handleKeyDown;
77
81
  disconnectedCallback(): void;
78
82
  renderLabel(label: string, maxLength?: number): TemplateResult<1> | typeof nothing;
83
+ renderAssistiveText(): TemplateResult<1> | typeof nothing;
79
84
  render(): TemplateResult<1>;
80
85
  static styles: CSSResult;
81
86
  }
@@ -93,6 +98,8 @@ export declare const resizeModes: readonly ["auto", "manual"];
93
98
 
94
99
  export declare const sizes: readonly ["small", "medium", "large"];
95
100
 
101
+ export declare const statusTypes: readonly ["default", "success", "error"];
102
+
96
103
  export declare interface TextareaProps {
97
104
  /**
98
105
  * Same as the HTML disabled attribute - indicates whether the textarea is disabled.
@@ -116,6 +123,14 @@ export declare interface TextareaProps {
116
123
  * An optional default value to use when the textarea is reset.
117
124
  */
118
125
  defaultValue?: string;
126
+ /**
127
+ * An optional assistive text to display below the textarea element. Must be provided when the status is success or error.
128
+ */
129
+ assistiveText?: string;
130
+ /**
131
+ * The status of the textarea component / assistive text. Can be default, success or error.
132
+ */
133
+ status?: typeof statusTypes[number];
119
134
  /**
120
135
  * The name of the textarea (used as a key/value pair with `value`). This is required in order to work properly with forms.
121
136
  */
package/dist/react.js CHANGED
@@ -1,13 +1,15 @@
1
1
  import * as e from "react";
2
2
  import { createComponent as t } from "@lit/react";
3
3
  import { PieTextarea as a } from "./index.js";
4
- import { defaultProps as l, resizeModes as d, sizes as g } from "./index.js";
4
+ import { defaultProps as d, resizeModes as g, sizes as C, statusTypes as h } from "./index.js";
5
5
  import "lit";
6
6
  import "lit/decorators.js";
7
- import "lit/directives/live.js";
8
- import "@justeattakeaway/pie-webc-core";
7
+ import "lit/directives/class-map.js";
9
8
  import "lit/directives/if-defined.js";
9
+ import "lit/directives/live.js";
10
+ import "@justeattakeaway/pie-assistive-text";
10
11
  import "@justeattakeaway/pie-form-label";
12
+ import "@justeattakeaway/pie-webc-core";
11
13
  const r = t({
12
14
  displayName: "PieTextarea",
13
15
  elementClass: a,
@@ -19,10 +21,11 @@ const r = t({
19
21
  onChange: "change"
20
22
  // when the textarea value is changed.
21
23
  }
22
- }), P = r;
24
+ }), f = r;
23
25
  export {
24
- P as PieTextarea,
25
- l as defaultProps,
26
- d as resizeModes,
27
- g as sizes
26
+ f as PieTextarea,
27
+ d as defaultProps,
28
+ g as resizeModes,
29
+ C as sizes,
30
+ h as statusTypes
28
31
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@justeattakeaway/pie-textarea",
3
3
  "description": "PIE Design System Textarea built using Web Components",
4
- "version": "0.7.0",
4
+ "version": "0.8.0",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
@@ -36,7 +36,7 @@
36
36
  "license": "Apache-2.0",
37
37
  "devDependencies": {
38
38
  "@custom-elements-manifest/analyzer": "0.9.0",
39
- "@justeattakeaway/pie-components-config": "0.17.0",
39
+ "@justeattakeaway/pie-components-config": "0.18.0",
40
40
  "@justeattakeaway/pie-css": "0.12.1",
41
41
  "@types/lodash.throttle": "4.1.9",
42
42
  "cem-plugin-module-file-extensions": "0.0.5"
package/src/defs.ts CHANGED
@@ -2,6 +2,7 @@ import { type ComponentDefaultProps } from '@justeattakeaway/pie-webc-core';
2
2
 
3
3
  export const sizes = ['small', 'medium', 'large'] as const;
4
4
  export const resizeModes = ['auto', 'manual'] as const;
5
+ export const statusTypes = ['default', 'success', 'error'] as const;
5
6
 
6
7
  export interface TextareaProps {
7
8
  /**
@@ -31,6 +32,16 @@ export interface TextareaProps {
31
32
  */
32
33
  defaultValue?: string;
33
34
 
35
+ /**
36
+ * An optional assistive text to display below the textarea element. Must be provided when the status is success or error.
37
+ */
38
+ assistiveText?: string;
39
+
40
+ /**
41
+ * The status of the textarea component / assistive text. Can be default, success or error.
42
+ */
43
+ status?: typeof statusTypes[number];
44
+
34
45
  /**
35
46
  * The name of the textarea (used as a key/value pair with `value`). This is required in order to work properly with forms.
36
47
  */
@@ -75,7 +86,7 @@ export interface TextareaProps {
75
86
  /**
76
87
  * The default values for the `TextareaProps` that are required (i.e. they have a fallback value in the component).
77
88
  */
78
- type DefaultProps = ComponentDefaultProps<TextareaProps, keyof Omit<TextareaProps, 'name' | 'autocomplete' | 'maxLength' | 'defaultValue'>>;
89
+ type DefaultProps = ComponentDefaultProps<TextareaProps, keyof Omit<TextareaProps, 'name' | 'autocomplete' | 'maxLength' | 'assistiveText' | 'defaultValue'>>;
79
90
 
80
91
  /**
81
92
  * Default values for optional properties that have default fallback values in the component.
@@ -86,6 +97,7 @@ export const defaultProps: DefaultProps = {
86
97
  resize: 'auto',
87
98
  label: '',
88
99
  value: '',
100
+ status: 'default',
89
101
  autoFocus: false,
90
102
  readonly: false,
91
103
  required: false,
package/src/index.ts CHANGED
@@ -1,34 +1,35 @@
1
1
  import {
2
- LitElement, html, unsafeCSS, PropertyValues, nothing,
2
+ LitElement, html, unsafeCSS, type PropertyValues, nothing,
3
3
  } from 'lit';
4
-
5
4
  import { property, query } from 'lit/decorators.js';
5
+ import { classMap } from 'lit/directives/class-map.js';
6
+ import { ifDefined } from 'lit/directives/if-defined.js';
6
7
  import { live } from 'lit/directives/live.js';
7
8
  import throttle from 'lodash.throttle';
8
9
 
10
+ import '@justeattakeaway/pie-assistive-text';
11
+ import '@justeattakeaway/pie-form-label';
9
12
  import {
10
- validPropertyValues, RtlMixin, defineCustomElement, FormControlMixin, wrapNativeEvent,
13
+ validPropertyValues, RtlMixin, defineCustomElement, FormControlMixin, wrapNativeEvent, type PIEInputElement,
11
14
  } from '@justeattakeaway/pie-webc-core';
12
15
 
13
- import { ifDefined } from 'lit/directives/if-defined.js';
14
16
  import styles from './textarea.scss?inline';
15
17
  import {
16
- TextareaProps, defaultProps, sizes, resizeModes,
18
+ type TextareaProps, defaultProps, sizes, resizeModes, statusTypes,
17
19
  } from './defs';
18
20
 
19
- import '@justeattakeaway/pie-form-label';
20
-
21
21
  // Valid values available to consumers
22
22
  export * from './defs';
23
23
 
24
24
  const componentSelector = 'pie-textarea';
25
+ const assistiveTextIdValue = 'assistive-text';
25
26
 
26
27
  /**
27
28
  * @tagname pie-textarea
28
29
  * @event {InputEvent} input - when the textarea value is changed.
29
30
  * @event {CustomEvent} change - when the textarea value is changed.
30
31
  */
31
- export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implements TextareaProps {
32
+ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implements TextareaProps, PIEInputElement {
32
33
  static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true };
33
34
 
34
35
  @property({ type: String })
@@ -64,18 +65,28 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
64
65
  public required = defaultProps.required;
65
66
 
66
67
  @property({ type: String })
67
- public name?: TextareaProps['name'];
68
+ @validPropertyValues(componentSelector, statusTypes, defaultProps.status)
69
+ public status = defaultProps.status;
70
+
71
+ @property({ type: String })
72
+ public assistiveText: TextareaProps['assistiveText'];
73
+
74
+ @property({ type: String })
75
+ public name: TextareaProps['name'];
68
76
 
69
77
  @property({ type: String })
70
- public autocomplete?: TextareaProps['autocomplete'];
78
+ public autocomplete: TextareaProps['autocomplete'];
71
79
 
72
80
  @query('textarea')
73
81
  private _textarea!: HTMLTextAreaElement;
74
82
 
83
+ @query('textarea')
84
+ public focusTarget!: HTMLElement;
85
+
75
86
  private _throttledResize = throttle(() => {
76
87
  if (this.resize === 'auto') {
77
88
  this._textarea.style.height = 'auto';
78
- this._textarea.style.height = `${this._textarea.scrollHeight + 2}px`; // +2 for border thicknesses
89
+ this._textarea.style.height = `${this._textarea.scrollHeight}px`;
79
90
  }
80
91
  }, 100);
81
92
 
@@ -111,6 +122,7 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
111
122
  this.restrictInputLength();
112
123
  this._internals.setFormValue(this.value);
113
124
 
125
+ window.addEventListener('resize', () => this.handleResize());
114
126
  this._textarea.addEventListener('keydown', this.handleKeyDown);
115
127
  }
116
128
 
@@ -169,6 +181,7 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
169
181
 
170
182
  public disconnectedCallback (): void {
171
183
  this._textarea.removeEventListener('keydown', this.handleKeyDown);
184
+ window.removeEventListener('resize', () => this.handleResize());
172
185
  }
173
186
 
174
187
  renderLabel (label: string, maxLength?: number) {
@@ -179,6 +192,21 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
179
192
  : nothing;
180
193
  }
181
194
 
195
+ renderAssistiveText () {
196
+ if (!this.assistiveText) {
197
+ return nothing;
198
+ }
199
+
200
+ return html`
201
+ <pie-assistive-text
202
+ id="${assistiveTextIdValue}"
203
+ variant=${ifDefined(this.status)}
204
+ data-test-id="pie-textarea-assistive-text">
205
+ ${this.assistiveText}
206
+ </pie-assistive-text>
207
+ `;
208
+ }
209
+
182
210
  render () {
183
211
  const {
184
212
  disabled,
@@ -192,15 +220,23 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
192
220
  required,
193
221
  label,
194
222
  maxLength,
223
+ status,
224
+ assistiveText,
195
225
  } = this;
196
226
 
197
- return html`
227
+ const classes = {
228
+ 'c-textareaWrapper': true,
229
+ 'c-textarea--disabled': disabled,
230
+ 'c-textarea--error': status === 'error',
231
+ [`c-textarea--resize-${resize}`]: true,
232
+ [`c-textarea--${size}`]: true,
233
+ };
234
+
235
+ return html`<div>
236
+ ${this.renderLabel(label, maxLength)}
198
237
  <div
199
- class="c-textareaWrapper"
200
- data-test-id="pie-textarea-wrapper"
201
- data-pie-size="${size}"
202
- data-pie-resize="${resize}">
203
- ${this.renderLabel(label, maxLength)}
238
+ class="${classMap(classes)}"
239
+ data-test-id="pie-textarea-wrapper">
204
240
  <textarea
205
241
  id="${componentSelector}"
206
242
  data-test-id="${componentSelector}"
@@ -211,10 +247,15 @@ export class PieTextarea extends FormControlMixin(RtlMixin(LitElement)) implemen
211
247
  ?readonly=${readonly}
212
248
  ?required=${required}
213
249
  ?disabled=${disabled}
250
+ aria-describedby=${ifDefined(assistiveText ? assistiveTextIdValue : undefined)}
251
+ aria-invalid=${status === 'error' ? 'true' : 'false'}
252
+ aria-errormessage="${ifDefined(status === 'error' ? assistiveTextIdValue : undefined)}"
214
253
  @input=${this.handleInput}
215
254
  @change=${this.handleChange}
216
255
  ></textarea>
217
- </div>`;
256
+ </div>
257
+ ${this.renderAssistiveText()}
258
+ </div>`;
218
259
  }
219
260
 
220
261
  // Renders a `CSSResult` generated from SCSS by Vite
package/src/textarea.scss CHANGED
@@ -1,6 +1,7 @@
1
1
  @use '@justeattakeaway/pie-css/scss' as p;
2
2
 
3
3
  // Heights are being defined based on the line height of the text and the padding.
4
+ // Some of the padding is in the wrapper element to properly position the textarea resize handle.
4
5
  // Changing the `size` property affects the padding and therefore the height of the textarea.
5
6
  // Default height is two lines of text.
6
7
  // Minimum height in manual resize mode is one line of text.
@@ -9,81 +10,95 @@
9
10
  --textarea-line-height: #{p.line-height(--dt-font-body-l-line-height)};
10
11
  --textarea-border-thickness: 1px;
11
12
  --textarea-resize: none;
12
- --textarea-padding-inline: var(--dt-spacing-d);
13
- --textarea-padding-block: var(--dt-spacing-c);
13
+ --textarea-padding-inline: var(--dt-spacing-c);
14
+ --textarea-padding-block: var(--dt-spacing-b);
14
15
  --textarea-background-color: var(--dt-color-container-default);
15
16
  --textarea-border-color: var(--dt-color-interactive-form);
16
17
  --textarea-content-color: var(--dt-color-content-default);
17
18
 
18
19
  // Default height is two lines of text
19
- --textarea-height: calc((var(--textarea-line-height) * 2) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));
20
+ --textarea-height: calc((var(--textarea-line-height) * 2) + (var(--textarea-padding-block) * 2));
20
21
 
21
- line-height: 0; // Remove once there is text outside the textarea
22
+ line-height: 0;
23
+ padding: var(--dt-spacing-a);
24
+ border: var(--textarea-border-thickness) solid var(--textarea-border-color);
25
+ background-color: var(--textarea-background-color);
26
+ border-radius: var(--dt-radius-rounded-c);
27
+ inline-size: 100%;
22
28
 
23
29
  textarea {
24
30
  @include p.font-size(--dt-font-body-l-size);
25
31
  line-height: var(--textarea-line-height);
26
32
  font-family: var(--dt-font-body-l-family);
27
33
  resize: var(--textarea-resize);
28
- border: var(--textarea-border-thickness) solid var(--textarea-border-color);
34
+ border: none;
29
35
  background-color: var(--textarea-background-color);
30
36
  color: var(--textarea-content-color);
31
37
 
32
- border-radius: var(--dt-radius-rounded-c);
33
38
  block-size: var(--textarea-height);
34
39
  max-block-size: var(--textarea-max-height);
35
40
  min-block-size: var(--textarea-min-height);
41
+ inline-size: 100%;
36
42
 
37
43
  padding-block-start: var(--textarea-padding-block);
38
44
  padding-block-end: var(--textarea-padding-block);
39
45
  padding-inline-start: var(--textarea-padding-inline);
40
46
  padding-inline-end: var(--textarea-padding-inline);
41
47
 
42
- &[disabled] {
43
- --textarea-background-color: var(--dt-color-disabled-01);
44
- --textarea-border-color: var(--dt-color-disabled-01);
45
- --textarea-content-color: var(--dt-color-content-disabled);
48
+ &:focus {
49
+ box-shadow: none;
50
+ outline: none;
46
51
  }
52
+ }
47
53
 
48
- @media (hover: hover) {
49
- &:hover:not([disabled]) {
50
- --textarea-background-color: hsl(var(--dt-color-container-default-h), var(--dt-color-container-default-s), calc(var(--dt-color-container-default-l) + calc(-1 * var(--dt-color-hover-01))));
51
- }
52
- }
54
+ &.c-textarea--disabled {
55
+ --textarea-background-color: var(--dt-color-disabled-01);
56
+ --textarea-border-color: var(--dt-color-disabled-01);
57
+ --textarea-content-color: var(--dt-color-content-disabled);
58
+ }
53
59
 
54
- &:focus-visible {
55
- @include p.focus;
60
+ @media (hover: hover) {
61
+ &:hover:not(.c-textarea--disabled) {
62
+ --textarea-background-color: hsl(var(--dt-color-container-default-h), var(--dt-color-container-default-s), calc(var(--dt-color-container-default-l) + calc(-1 * var(--dt-color-hover-01))));
56
63
  }
57
64
  }
58
65
 
59
- &[data-pie-size="large"] {
60
- --textarea-padding-block: var(--dt-spacing-d);
66
+ &:focus-within {
67
+ @include p.focus;
61
68
  }
62
69
 
63
- &[data-pie-size="small"] {
64
- --textarea-padding-block: var(--dt-spacing-b);
70
+ &.c-textarea--large {
71
+ --textarea-padding-block: var(--dt-spacing-c);
65
72
  }
66
73
 
67
- &[data-pie-resize="manual"] {
74
+ &.c-textarea--small {
75
+ --textarea-padding-block: var(--dt-spacing-a);
76
+ }
77
+
78
+ &.c-textarea--resize-manual {
68
79
  --textarea-resize: vertical;
69
80
 
70
81
  // Minimum is one line of text
71
- --textarea-min-height: calc((var(--textarea-line-height) * 1) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2)); // One line of text
82
+ --textarea-min-height: calc((var(--textarea-line-height) * 1) + (var(--textarea-padding-block) * 2));
72
83
 
73
84
  @media (pointer: coarse) {
74
85
  // Fixed size for touch devices
75
- --textarea-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));
76
- --textarea-min-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));
77
- --textarea-max-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));
86
+ --textarea-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2));
87
+ --textarea-min-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2));
88
+ --textarea-max-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2));
78
89
  --textarea-resize: none;
79
90
  }
80
91
  }
81
92
 
82
- &[data-pie-resize="auto"] {
93
+ &.c-textarea--resize-auto {
83
94
  // Maximum is six lines of text
84
- --textarea-max-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2) + (var(--textarea-border-thickness) * 2));
95
+ --textarea-max-height: calc((var(--textarea-line-height) * 6) + (var(--textarea-padding-block) * 2));
85
96
 
86
97
  // Minimum is two lines of text
87
98
  --textarea-min-height: var(--textarea-height);
88
99
  }
100
+
101
+ &.c-textarea--error {
102
+ --textarea-border-color: var(--dt-color-support-error);
103
+ }
89
104
  }