@nysds/nys-textarea 1.13.1 → 1.14.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.
@@ -0,0 +1 @@
1
+ export * from "./nys-textarea";
@@ -0,0 +1,115 @@
1
+ import { LitElement } from "lit";
2
+ /**
3
+ * A multi-line text input for collecting longer responses like comments, descriptions, or feedback.
4
+ * Form-associated with validation support via ElementInternals.
5
+ *
6
+ * Use for detailed responses needing multiple lines. For single-line input, use `nys-textinput`.
7
+ * For predefined options, use `nys-select`, `nys-radiobutton`, or `nys-checkbox`.
8
+ *
9
+ * @summary Multi-line text input for comments, descriptions, and feedback.
10
+ * @element nys-textarea
11
+ *
12
+ * @slot description - Custom HTML description content below the label.
13
+ *
14
+ * @fires nys-input - Fired on input change. Detail: `{id, value}`.
15
+ * @fires nys-focus - Fired when textarea gains focus.
16
+ * @fires nys-blur - Fired when textarea loses focus. Triggers validation.
17
+ * @fires nys-select - Fired when user selects text. Detail: `{id, value}`.
18
+ *
19
+ * @example Basic textarea
20
+ * ```html
21
+ * <nys-textarea label="Comments" rows="4"></nys-textarea>
22
+ * ```
23
+ *
24
+ * @example Required with description
25
+ * ```html
26
+ * <nys-textarea label="Describe the incident" description="Please provide details" required></nys-textarea>
27
+ * ```
28
+ */
29
+ export declare class NysTextarea extends LitElement {
30
+ static styles: import("lit").CSSResult;
31
+ /** Unique identifier. Auto-generated if not provided. */
32
+ id: string;
33
+ /** Name for form submission. */
34
+ name: string;
35
+ /** Visible label text. Required for accessibility. */
36
+ label: string;
37
+ /** Helper text below label. Use slot for custom HTML. */
38
+ description: string;
39
+ /** Placeholder text. Don't use as label replacement. */
40
+ placeholder: string;
41
+ /** Current textarea value. */
42
+ value: string;
43
+ /** Prevents interaction. */
44
+ disabled: boolean;
45
+ /** Makes textarea read-only but focusable. */
46
+ readonly: boolean;
47
+ /** Marks as required. Shows "Required" flag and validates on blur. */
48
+ required: boolean;
49
+ /** Shows "Optional" flag. Use when most fields are required. */
50
+ optional: boolean;
51
+ /** Tooltip text shown on hover/focus of info icon. */
52
+ tooltip: string;
53
+ /** Adjusts colors for dark backgrounds. */
54
+ inverted: boolean;
55
+ /** Form `id` to associate with when textarea is outside form element. */
56
+ form: string | null;
57
+ /** Maximum character length. */
58
+ maxlength: number | null;
59
+ /**
60
+ * Textarea width: `sm` (88px), `md` (200px), `lg` (384px), `full` (100%, default).
61
+ * @default "full"
62
+ */
63
+ width: "sm" | "md" | "lg" | "full";
64
+ /**
65
+ * Visible height in lines.
66
+ * @default 4
67
+ */
68
+ rows: number;
69
+ /**
70
+ * Resize behavior: `vertical` (default, user can resize height), `none` (fixed size).
71
+ * @default "vertical"
72
+ */
73
+ resize: "vertical" | "none";
74
+ /** Shows error message when true. Set by validation or manually. */
75
+ showError: boolean;
76
+ /** Error message text. Shown only when `showError` is true. */
77
+ errorMessage: string;
78
+ updated(changedProperties: Map<string | number | symbol, unknown>): Promise<void>;
79
+ private _hasUserInteracted;
80
+ private _internals;
81
+ /**
82
+ * Lifecycle methods
83
+ * --------------------------------------------------------------------------
84
+ */
85
+ static formAssociated: boolean;
86
+ constructor();
87
+ connectedCallback(): void;
88
+ disconnectedCallback(): void;
89
+ firstUpdated(): void;
90
+ /**
91
+ * Form Integration
92
+ * --------------------------------------------------------------------------
93
+ */
94
+ private _setValue;
95
+ private _manageRequire;
96
+ private _setValidityMessage;
97
+ private _validate;
98
+ formResetCallback(): void;
99
+ /**
100
+ * Functions
101
+ * --------------------------------------------------------------------------
102
+ */
103
+ checkValidity(): boolean;
104
+ private _handleInvalid;
105
+ /**
106
+ * Event Handlers
107
+ * --------------------------------------------------------------------------
108
+ */
109
+ private _handleInput;
110
+ private _handleFocus;
111
+ private _handleBlur;
112
+ private _handleSelect;
113
+ private _handleSelectionChange;
114
+ render(): import("lit-html").TemplateResult<1>;
115
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,7 +1,7 @@
1
1
  import { LitElement as y, unsafeCSS as u, html as p } from "lit";
2
2
  import { property as a } from "lit/decorators.js";
3
3
  import { ifDefined as o } from "lit/directives/if-defined.js";
4
- const v = ':host{--_nys-textarea-width: 100%;--_nys-textarea-border-radius: var(--nys-radius-md, 4px);--_nys-textarea-border-width: var(--nys-border-width-sm, 1px);--_nys-textarea-border-color: var(--nys-color-neutral-400, #909395);--_nys-textarea-padding: var(--nys-space-200, 16px);--_nys-textarea-gap: var(--nys-space-50, 4px);--_nys-textarea-color: var(--nys-color-ink, #1b1b1b);--_nys-textarea-color--placeholder: var(--nys-color-text-weaker, var(--nys-color-neutral-500, #797c7f));--_nys-textarea-outline-color--hover: var(--nys-color-neutral-900, #1b1b1b);--_nys-textarea-outline-width: var(--nys-border-width-sm, 1px);--_nys-textarea-outline-color--focus: var(--nys-color-focus, #004dd1);--_nys-textarea-background-color--disabled: var(--nys-color-neutral-10, #f6f6f6);--_nys-textarea-border-color--disabled: var(--nys-color-neutral-200, #bec0c1);--_nys-textarea-color--disabled: var(--nys-color-text-disabled, var(--nys-color-neutral-200, #bec0c1));--_nys-textarea-font-family: var(--nys-font-family-ui, var(--nys-font-family-sans, "Proxima Nova", "Helvetica Neue", "Helvetica", "Arial", sans-serif));--_nys-textarea-font-size: var(--nys-font-size-ui-md, 16px);--_nys-textarea-font-weight: var(--nys-font-weight-regular, 400);--_nys-textarea-line-height: var(--nys-font-lineheight-ui-md, 24px);--nys-textarea-letterspacing-ui: var(--nys-font-letterspacing-ui-md, var(--nys-font-letterspacing-400, .044px))}:host([width=sm]){--_nys-textarea-width: var(--nys-form-width-sm, 88px)}:host([width=md]){--_nys-textarea-width: var(--nys-form-width-md, 200px)}:host([width=lg]){--_nys-textarea-width: var(--nys-form-width-lg, 384px)}:host([width=full]){--_nys-textarea-width: 100%;flex:1}:host([showError]){--_nys-textarea-border-color: var(--nys-color-danger, #b52c2c)}.nys-textarea{font-weight:var(--_nys-textarea-font-weight);font-family:var(--_nys-textarea-font-family);line-height:var(--_nys-textarea-line-height);letter-spacing:var(--nys-textarea-letterspacing-ui);color:var(--_nys-textarea-color);gap:var(--_nys-textarea-gap);display:flex;flex-direction:column}.nys-textarea__textarea{color:var(--_nys-textarea-color);font-size:var(--_nys-textarea-font-size);font-family:var(--_nys-textarea-font-family);border-radius:var(--_nys-textarea-border-radius);border:solid var(--_nys-textarea-border-color) var(--_nys-textarea-border-width);padding:var(--_nys-textarea-padding);width:var(--_nys-textarea-width);line-height:var(--_nys-textarea-line-height);max-width:var(--_nys-textarea-width);box-sizing:border-box;-webkit-box-sizing:border-box;-moz-box-sizing:border-box}.nys-textarea__textarea::placeholder{color:var(--_nys-textarea-color--placeholder)}.nys-textarea__textarea.none{resize:none}.nys-textarea__textarea:hover:not(:disabled):not(:focus):not([readonly]){outline:solid var(--_nys-textarea-outline-width) var(--_nys-textarea-outline-color--hover);border-color:var(--_nys-textarea-outline-color--hover)}.nys-textarea__textarea:focus:not([readonly]){outline:solid var(--_nys-textarea-outline-width) var(--_nys-textarea-outline-color--focus);border-color:var(--_nys-textarea-outline-color--focus);caret-color:var(--_nys-textarea-outline-color--focus)}.nys-textarea__textarea:disabled,.nys-textarea__textarea:disabled::placeholder{background-color:var(--_nys-textarea-background-color--disabled);border-color:var(--_nys-textarea-border-color--disabled);color:var(--_nys-textarea-color--disabled);cursor:not-allowed}';
4
+ const v = ':host{--_nys-textarea-width: 100%;--_nys-textarea-border-radius: var(--nys-radius-md, 4px);--_nys-textarea-border-width: var(--nys-border-width-sm, 1px);--_nys-textarea-border-color: var(--nys-color-neutral-400, #909395);--_nys-textarea-padding: var(--nys-space-200, 16px);--_nys-textarea-gap: var(--nys-space-50, 4px);--_nys-textarea-color: var(--nys-color-ink, #1b1b1b);--_nys-textarea-color--placeholder: var(--nys-color-text-weaker, var(--nys-color-neutral-500, #797c7f));--_nys-textarea-outline-color--hover: var(--nys-color-neutral-900, #1b1b1b);--_nys-textarea-outline-width: var(--nys-border-width-sm, 1px);--_nys-textarea-outline-color--focus: var(--nys-color-focus, #004dd1);--_nys-textarea-background-color--disabled: var(--nys-color-neutral-10, #f6f6f6);--_nys-textarea-border-color--disabled: var(--nys-color-neutral-200, #bec0c1);--_nys-textarea-color--disabled: var(--nys-color-text-disabled, var(--nys-color-neutral-200, #bec0c1));--_nys-textarea-font-family: var(--nys-font-family-ui, var(--nys-font-family-sans, "Proxima Nova", "Helvetica Neue", "Helvetica", "Arial", sans-serif));--_nys-textarea-font-size: var(--nys-font-size-ui-md, 16px);--_nys-textarea-font-weight: var(--nys-font-weight-regular, 400);--_nys-textarea-line-height: var(--nys-font-lineheight-ui-md, 24px);--nys-textarea-letterspacing-ui: var(--nys-font-letterspacing-ui-md, var(--nys-font-letterspacing-400, .044px))}:host([width=sm]){--_nys-textarea-width: var(--nys-form-width-sm, 88px)}:host([width=md]){--_nys-textarea-width: var(--nys-form-width-md, 200px)}:host([width=lg]){--_nys-textarea-width: var(--nys-form-width-lg, 384px)}:host([width=full]){--_nys-textarea-width: 100%;flex:1}:host([showError]){--_nys-textarea-border-color: var(--nys-color-danger, #b52c2c)}.nys-textarea{font-weight:var(--_nys-textarea-font-weight);font-family:var(--_nys-textarea-font-family);line-height:var(--_nys-textarea-line-height);letter-spacing:var(--nys-textarea-letterspacing-ui);color:var(--_nys-textarea-color);gap:var(--_nys-textarea-gap);display:flex;flex-direction:column}.nys-textarea__textarea{color:var(--_nys-textarea-color);font-size:var(--_nys-textarea-font-size);font-family:var(--_nys-textarea-font-family);border-radius:var(--_nys-textarea-border-radius);border:solid var(--_nys-textarea-border-color) var(--_nys-textarea-border-width);padding:var(--_nys-textarea-padding);width:var(--_nys-textarea-width);line-height:var(--_nys-textarea-line-height);max-width:var(--_nys-textarea-width);box-sizing:border-box}.nys-textarea__textarea::placeholder{color:var(--_nys-textarea-color--placeholder)}.nys-textarea__textarea.none{resize:none}.nys-textarea__textarea:hover:not(:disabled):not(:focus):not([readonly]){outline:solid var(--_nys-textarea-outline-width) var(--_nys-textarea-outline-color--hover);border-color:var(--_nys-textarea-outline-color--hover)}.nys-textarea__textarea:focus:not([readonly]){outline:solid var(--_nys-textarea-outline-width) var(--_nys-textarea-outline-color--focus);border-color:var(--_nys-textarea-outline-color--focus);caret-color:var(--_nys-textarea-outline-color--focus)}.nys-textarea__textarea:disabled,.nys-textarea__textarea:disabled::placeholder{background-color:var(--_nys-textarea-background-color--disabled);border-color:var(--_nys-textarea-border-color--disabled);color:var(--_nys-textarea-color--disabled);cursor:not-allowed}';
5
5
  var f = Object.defineProperty, s = (c, e, t, l) => {
6
6
  for (var i = void 0, d = c.length - 1, n; d >= 0; d--)
7
7
  (n = c[d]) && (i = n(e, t, i) || i);
@@ -126,9 +126,9 @@ const h = class h extends y {
126
126
  }
127
127
  render() {
128
128
  return p`
129
- <label class="nys-textarea">
129
+ <div class="nys-textarea">
130
130
  <nys-label
131
- for=${this.id + "--native"}
131
+ for=${this.id}
132
132
  label=${this.label}
133
133
  description=${this.description}
134
134
  flag=${this.required && !this.readonly ? "required" : this.optional ? "optional" : ""}
@@ -140,7 +140,7 @@ const h = class h extends y {
140
140
  <textarea
141
141
  class="nys-textarea__textarea ${this.resize}"
142
142
  name=${this.name}
143
- id=${this.id + "--native"}
143
+ id=${this.id}
144
144
  .value=${this.value}
145
145
  ?disabled=${this.disabled}
146
146
  ?required=${this.required && !this.readonly}
@@ -165,7 +165,7 @@ const h = class h extends y {
165
165
  ?showError=${this.showError}
166
166
  errorMessage=${this._internals.validationMessage || this.errorMessage}
167
167
  ></nys-errormessage>
168
- </label>
168
+ </div>
169
169
  `;
170
170
  }
171
171
  };
@@ -1 +1 @@
1
- {"version":3,"file":"nys-textarea.js","sources":["../src/nys-textarea.ts"],"sourcesContent":["import { LitElement, html, unsafeCSS } from \"lit\";\nimport { property } from \"lit/decorators.js\";\nimport { ifDefined } from \"lit/directives/if-defined.js\";\n// @ts-ignore: SCSS module imported via bundler as inline\nimport styles from \"./nys-textarea.scss?inline\";\n\nlet textareaIdCounter = 0;\n\n/**\n * A multi-line text input for collecting longer responses like comments, descriptions, or feedback.\n * Form-associated with validation support via ElementInternals.\n *\n * Use for detailed responses needing multiple lines. For single-line input, use `nys-textinput`.\n * For predefined options, use `nys-select`, `nys-radiobutton`, or `nys-checkbox`.\n *\n * @summary Multi-line text input for comments, descriptions, and feedback.\n * @element nys-textarea\n *\n * @slot description - Custom HTML description content below the label.\n *\n * @fires nys-input - Fired on input change. Detail: `{id, value}`.\n * @fires nys-focus - Fired when textarea gains focus.\n * @fires nys-blur - Fired when textarea loses focus. Triggers validation.\n * @fires nys-select - Fired when user selects text. Detail: `{id, value}`.\n *\n * @example Basic textarea\n * ```html\n * <nys-textarea label=\"Comments\" rows=\"4\"></nys-textarea>\n * ```\n *\n * @example Required with description\n * ```html\n * <nys-textarea label=\"Describe the incident\" description=\"Please provide details\" required></nys-textarea>\n * ```\n */\n\nexport class NysTextarea extends LitElement {\n static styles = unsafeCSS(styles);\n\n /** Unique identifier. Auto-generated if not provided. */\n @property({ type: String, reflect: true }) id = \"\";\n\n /** Name for form submission. */\n @property({ type: String, reflect: true }) name = \"\";\n\n /** Visible label text. Required for accessibility. */\n @property({ type: String }) label = \"\";\n\n /** Helper text below label. Use slot for custom HTML. */\n @property({ type: String }) description = \"\";\n\n /** Placeholder text. Don't use as label replacement. */\n @property({ type: String }) placeholder = \"\";\n\n /** Current textarea value. */\n @property({ type: String }) value = \"\";\n\n /** Prevents interaction. */\n @property({ type: Boolean, reflect: true }) disabled = false;\n\n /** Makes textarea read-only but focusable. */\n @property({ type: Boolean, reflect: true }) readonly = false;\n\n /** Marks as required. Shows \"Required\" flag and validates on blur. */\n @property({ type: Boolean, reflect: true }) required = false;\n\n /** Shows \"Optional\" flag. Use when most fields are required. */\n @property({ type: Boolean, reflect: true }) optional = false;\n\n /** Tooltip text shown on hover/focus of info icon. */\n @property({ type: String }) tooltip = \"\";\n\n /** Adjusts colors for dark backgrounds. */\n @property({ type: Boolean, reflect: true }) inverted = false;\n\n /** Form `id` to associate with when textarea is outside form element. */\n @property({ type: String, reflect: true }) form: string | null = null;\n\n /** Maximum character length. */\n @property({ type: Number }) maxlength: number | null = null;\n\n /**\n * Textarea width: `sm` (88px), `md` (200px), `lg` (384px), `full` (100%, default).\n * @default \"full\"\n */\n @property({ type: String, reflect: true }) width:\n | \"sm\"\n | \"md\"\n | \"lg\"\n | \"full\" = \"full\";\n\n /**\n * Visible height in lines.\n * @default 4\n */\n @property({ type: Number }) rows = 4;\n\n /**\n * Resize behavior: `vertical` (default, user can resize height), `none` (fixed size).\n * @default \"vertical\"\n */\n @property({ type: String, reflect: true }) resize: \"vertical\" | \"none\" =\n \"vertical\";\n\n /** Shows error message when true. Set by validation or manually. */\n @property({ type: Boolean, reflect: true }) showError = false;\n\n /** Error message text. Shown only when `showError` is true. */\n @property({ type: String }) errorMessage = \"\";\n\n async updated(changedProperties: Map<string | number | symbol, unknown>) {\n await Promise.resolve();\n if (changedProperties.has(\"rows\")) {\n this.rows = this.rows ?? 4;\n }\n if (\n changedProperties.has(\"readonly\") ||\n changedProperties.has(\"required\")\n ) {\n const input = this.shadowRoot?.querySelector(\"textarea\");\n\n if (input) input.required = this.required && !this.readonly;\n }\n }\n\n private _hasUserInteracted = false; // need this flag for \"eager mode\"\n private _internals: ElementInternals;\n\n /**\n * Lifecycle methods\n * --------------------------------------------------------------------------\n */\n\n static formAssociated = true; // allows use of elementInternals' API\n\n constructor() {\n super();\n this._internals = this.attachInternals();\n }\n\n // Generate a unique ID if one is not provided\n connectedCallback() {\n super.connectedCallback();\n if (!this.id) {\n this.id = `nys-textarea-${Date.now()}-${textareaIdCounter++}`;\n }\n this.addEventListener(\"invalid\", this._handleInvalid);\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n this.removeEventListener(\"invalid\", this._handleInvalid);\n }\n\n firstUpdated() {\n // This ensures our element always participates in the form\n this._setValue();\n }\n\n /**\n * Form Integration\n * --------------------------------------------------------------------------\n */\n\n private _setValue() {\n this._internals.setFormValue(this.value);\n this._manageRequire();\n }\n\n private _manageRequire() {\n const textarea = this.shadowRoot?.querySelector(\"textarea\");\n\n if (!textarea) return;\n\n const message = this.errorMessage || \"This field is required\";\n const isInvalid = this.required && !this.value;\n\n if (isInvalid) {\n this._internals.ariaRequired = \"true\";\n this._internals.setValidity({ valueMissing: true }, message, textarea);\n } else {\n this._internals.ariaRequired = \"false\"; // Reset when valid\n this._internals.setValidity({});\n this._hasUserInteracted = false; // Reset lazy validation when valid\n }\n }\n\n private _setValidityMessage(message: string = \"\") {\n const textarea = this.shadowRoot?.querySelector(\"textarea\");\n if (!textarea) return;\n\n // Toggle the HTML <div> tag error message\n this.showError = !!message;\n // If user sets errorMessage, this will always override the native validation message\n if (this.errorMessage?.trim() && message !== \"\") {\n message = this.errorMessage;\n }\n\n this._internals.setValidity(\n message ? { customError: true } : {},\n message,\n textarea,\n );\n }\n\n private _validate() {\n const textarea = this.shadowRoot?.querySelector(\"textarea\");\n if (!textarea) return;\n\n // Get the native validation state\n let message = textarea.validationMessage;\n\n this._setValidityMessage(message);\n }\n\n // This callback is automatically called when the parent form is reset.\n public formResetCallback() {\n this.value = \"\";\n\n const textarea = this.shadowRoot?.querySelector(\"textarea\");\n if (textarea) {\n textarea.value = \"\";\n textarea.setAttribute(\"aria-invalid\", \"false\");\n }\n\n // Reset validation UI\n this.showError = false;\n this._internals.setValidity({});\n\n // Re-render UI\n this.requestUpdate();\n }\n\n /**\n * Functions\n * --------------------------------------------------------------------------\n */\n\n // This helper function is called to perform the element's native validation.\n checkValidity(): boolean {\n const textarea = this.shadowRoot?.querySelector(\"textarea\");\n return textarea ? textarea.checkValidity() : true;\n }\n\n private _handleInvalid(event: Event) {\n event.preventDefault();\n this._hasUserInteracted = true; // Start aggressive mode due to form submission\n this._validate();\n\n const textarea = this.shadowRoot?.querySelector(\"textarea\");\n if (textarea) {\n // Focus only if this is the first invalid element (top-down approach)\n const form = this._internals.form;\n if (form) {\n const elements = Array.from(form.elements) as Array<\n HTMLElement & { checkValidity?: () => boolean }\n >;\n // Find the first element in the form that is invalid\n const firstInvalidElement = elements.find(\n (element) =>\n typeof element.checkValidity === \"function\" &&\n !element.checkValidity(),\n );\n if (firstInvalidElement === this) {\n textarea.focus();\n }\n } else {\n // If not part of a form, simply focus.\n textarea.focus();\n }\n }\n }\n\n /**\n * Event Handlers\n * --------------------------------------------------------------------------\n */\n\n // Handle input event to check pattern validity\n private _handleInput(event: Event) {\n const textarea = event.target as HTMLInputElement;\n this.value = textarea.value;\n this._internals.setFormValue(this.value);\n\n // Field is invalid after unfocused, validate aggressively on each input (e.g. Eager mode: a combination of aggressive and lazy.)\n if (this._hasUserInteracted) {\n this._validate();\n }\n\n this.dispatchEvent(\n new CustomEvent(\"nys-input\", {\n detail: { id: this.id, value: this.value },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n // Handle focus event\n private _handleFocus() {\n this.dispatchEvent(new Event(\"nys-focus\"));\n }\n\n // Handle blur event\n private _handleBlur() {\n if (!this._hasUserInteracted) {\n this._hasUserInteracted = true; // At initial unfocus: if textarea is invalid, start aggressive mode\n }\n\n this._validate();\n this.dispatchEvent(new Event(\"nys-blur\"));\n }\n\n private _handleSelect(e: Event) {\n const select = e.target as HTMLSelectElement;\n this.value = select.value;\n this.dispatchEvent(\n new CustomEvent(\"nys-select\", {\n detail: { id: this.id, value: this.value },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n private _handleSelectionChange(e: Event) {\n const select = e.target as HTMLSelectElement;\n this.value = select.value;\n this.dispatchEvent(\n new CustomEvent(\"nys-selectionchange\", {\n detail: { id: this.id, value: this.value },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n render() {\n return html`\n <label class=\"nys-textarea\">\n <nys-label\n for=${this.id + \"--native\"}\n label=${this.label}\n description=${this.description}\n flag=${this.required && !this.readonly\n ? \"required\"\n : this.optional\n ? \"optional\"\n : \"\"}\n tooltip=${this.tooltip}\n ?inverted=${this.inverted}\n >\n <slot name=\"description\" slot=\"description\">${this.description}</slot>\n </nys-label>\n <textarea\n class=\"nys-textarea__textarea ${this.resize}\"\n name=${this.name}\n id=${this.id + \"--native\"}\n .value=${this.value}\n ?disabled=${this.disabled}\n ?required=${this.required && !this.readonly}\n ?readonly=${this.readonly}\n aria-disabled=${ifDefined(this.disabled ? \"true\" : undefined)}\n aria-required=${ifDefined(this.required ? \"true\" : undefined)}\n aria-label=${ifDefined(this.label || undefined)}\n aria-description=${ifDefined(this.description || undefined)}\n placeholder=${ifDefined(\n this.placeholder ? this.placeholder : undefined,\n )}\n maxlength=${ifDefined(this.maxlength ?? undefined)}\n .rows=${this.rows}\n form=${ifDefined(this.form || undefined)}\n @input=${this._handleInput}\n @focus=\"${this._handleFocus}\"\n @blur=\"${this._handleBlur}\"\n @select=\"${this._handleSelect}\"\n @selectionchange=\"${this._handleSelectionChange}\"\n ></textarea>\n <nys-errormessage\n ?showError=${this.showError}\n errorMessage=${this._internals.validationMessage || this.errorMessage}\n ></nys-errormessage>\n </label>\n `;\n }\n}\n\nif (!customElements.get(\"nys-textarea\")) {\n customElements.define(\"nys-textarea\", NysTextarea);\n}\n"],"names":["textareaIdCounter","_NysTextarea","LitElement","changedProperties","input","textarea","message","event","form","element","select","html","ifDefined","unsafeCSS","styles","NysTextarea","__decorateClass","property"],"mappings":"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"}
1
+ {"version":3,"file":"nys-textarea.js","sources":["../src/nys-textarea.ts"],"sourcesContent":["import { LitElement, html, unsafeCSS } from \"lit\";\nimport { property } from \"lit/decorators.js\";\nimport { ifDefined } from \"lit/directives/if-defined.js\";\n// @ts-ignore: SCSS module imported via bundler as inline\nimport styles from \"./nys-textarea.scss?inline\";\n\nlet textareaIdCounter = 0;\n\n/**\n * A multi-line text input for collecting longer responses like comments, descriptions, or feedback.\n * Form-associated with validation support via ElementInternals.\n *\n * Use for detailed responses needing multiple lines. For single-line input, use `nys-textinput`.\n * For predefined options, use `nys-select`, `nys-radiobutton`, or `nys-checkbox`.\n *\n * @summary Multi-line text input for comments, descriptions, and feedback.\n * @element nys-textarea\n *\n * @slot description - Custom HTML description content below the label.\n *\n * @fires nys-input - Fired on input change. Detail: `{id, value}`.\n * @fires nys-focus - Fired when textarea gains focus.\n * @fires nys-blur - Fired when textarea loses focus. Triggers validation.\n * @fires nys-select - Fired when user selects text. Detail: `{id, value}`.\n *\n * @example Basic textarea\n * ```html\n * <nys-textarea label=\"Comments\" rows=\"4\"></nys-textarea>\n * ```\n *\n * @example Required with description\n * ```html\n * <nys-textarea label=\"Describe the incident\" description=\"Please provide details\" required></nys-textarea>\n * ```\n */\n\nexport class NysTextarea extends LitElement {\n static styles = unsafeCSS(styles);\n\n /** Unique identifier. Auto-generated if not provided. */\n @property({ type: String, reflect: true }) id = \"\";\n\n /** Name for form submission. */\n @property({ type: String, reflect: true }) name = \"\";\n\n /** Visible label text. Required for accessibility. */\n @property({ type: String }) label = \"\";\n\n /** Helper text below label. Use slot for custom HTML. */\n @property({ type: String }) description = \"\";\n\n /** Placeholder text. Don't use as label replacement. */\n @property({ type: String }) placeholder = \"\";\n\n /** Current textarea value. */\n @property({ type: String }) value = \"\";\n\n /** Prevents interaction. */\n @property({ type: Boolean, reflect: true }) disabled = false;\n\n /** Makes textarea read-only but focusable. */\n @property({ type: Boolean, reflect: true }) readonly = false;\n\n /** Marks as required. Shows \"Required\" flag and validates on blur. */\n @property({ type: Boolean, reflect: true }) required = false;\n\n /** Shows \"Optional\" flag. Use when most fields are required. */\n @property({ type: Boolean, reflect: true }) optional = false;\n\n /** Tooltip text shown on hover/focus of info icon. */\n @property({ type: String }) tooltip = \"\";\n\n /** Adjusts colors for dark backgrounds. */\n @property({ type: Boolean, reflect: true }) inverted = false;\n\n /** Form `id` to associate with when textarea is outside form element. */\n @property({ type: String, reflect: true }) form: string | null = null;\n\n /** Maximum character length. */\n @property({ type: Number }) maxlength: number | null = null;\n\n /**\n * Textarea width: `sm` (88px), `md` (200px), `lg` (384px), `full` (100%, default).\n * @default \"full\"\n */\n @property({ type: String, reflect: true }) width:\n | \"sm\"\n | \"md\"\n | \"lg\"\n | \"full\" = \"full\";\n\n /**\n * Visible height in lines.\n * @default 4\n */\n @property({ type: Number }) rows = 4;\n\n /**\n * Resize behavior: `vertical` (default, user can resize height), `none` (fixed size).\n * @default \"vertical\"\n */\n @property({ type: String, reflect: true }) resize: \"vertical\" | \"none\" =\n \"vertical\";\n\n /** Shows error message when true. Set by validation or manually. */\n @property({ type: Boolean, reflect: true }) showError = false;\n\n /** Error message text. Shown only when `showError` is true. */\n @property({ type: String }) errorMessage = \"\";\n\n async updated(changedProperties: Map<string | number | symbol, unknown>) {\n await Promise.resolve();\n if (changedProperties.has(\"rows\")) {\n this.rows = this.rows ?? 4;\n }\n if (\n changedProperties.has(\"readonly\") ||\n changedProperties.has(\"required\")\n ) {\n const input = this.shadowRoot?.querySelector(\"textarea\");\n\n if (input) input.required = this.required && !this.readonly;\n }\n }\n\n private _hasUserInteracted = false; // need this flag for \"eager mode\"\n private _internals: ElementInternals;\n\n /**\n * Lifecycle methods\n * --------------------------------------------------------------------------\n */\n\n static formAssociated = true; // allows use of elementInternals' API\n\n constructor() {\n super();\n this._internals = this.attachInternals();\n }\n\n // Generate a unique ID if one is not provided\n connectedCallback() {\n super.connectedCallback();\n if (!this.id) {\n this.id = `nys-textarea-${Date.now()}-${textareaIdCounter++}`;\n }\n this.addEventListener(\"invalid\", this._handleInvalid);\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n this.removeEventListener(\"invalid\", this._handleInvalid);\n }\n\n firstUpdated() {\n // This ensures our element always participates in the form\n this._setValue();\n }\n\n /**\n * Form Integration\n * --------------------------------------------------------------------------\n */\n\n private _setValue() {\n this._internals.setFormValue(this.value);\n this._manageRequire();\n }\n\n private _manageRequire() {\n const textarea = this.shadowRoot?.querySelector(\"textarea\");\n\n if (!textarea) return;\n\n const message = this.errorMessage || \"This field is required\";\n const isInvalid = this.required && !this.value;\n\n if (isInvalid) {\n this._internals.ariaRequired = \"true\";\n this._internals.setValidity({ valueMissing: true }, message, textarea);\n } else {\n this._internals.ariaRequired = \"false\"; // Reset when valid\n this._internals.setValidity({});\n this._hasUserInteracted = false; // Reset lazy validation when valid\n }\n }\n\n private _setValidityMessage(message: string = \"\") {\n const textarea = this.shadowRoot?.querySelector(\"textarea\");\n if (!textarea) return;\n\n // Toggle the HTML <div> tag error message\n this.showError = !!message;\n // If user sets errorMessage, this will always override the native validation message\n if (this.errorMessage?.trim() && message !== \"\") {\n message = this.errorMessage;\n }\n\n this._internals.setValidity(\n message ? { customError: true } : {},\n message,\n textarea,\n );\n }\n\n private _validate() {\n const textarea = this.shadowRoot?.querySelector(\"textarea\");\n if (!textarea) return;\n\n // Get the native validation state\n let message = textarea.validationMessage;\n\n this._setValidityMessage(message);\n }\n\n // This callback is automatically called when the parent form is reset.\n public formResetCallback() {\n this.value = \"\";\n\n const textarea = this.shadowRoot?.querySelector(\"textarea\");\n if (textarea) {\n textarea.value = \"\";\n textarea.setAttribute(\"aria-invalid\", \"false\");\n }\n\n // Reset validation UI\n this.showError = false;\n this._internals.setValidity({});\n\n // Re-render UI\n this.requestUpdate();\n }\n\n /**\n * Functions\n * --------------------------------------------------------------------------\n */\n\n // This helper function is called to perform the element's native validation.\n checkValidity(): boolean {\n const textarea = this.shadowRoot?.querySelector(\"textarea\");\n return textarea ? textarea.checkValidity() : true;\n }\n\n private _handleInvalid(event: Event) {\n event.preventDefault();\n this._hasUserInteracted = true; // Start aggressive mode due to form submission\n this._validate();\n\n const textarea = this.shadowRoot?.querySelector(\"textarea\");\n if (textarea) {\n // Focus only if this is the first invalid element (top-down approach)\n const form = this._internals.form;\n if (form) {\n const elements = Array.from(form.elements) as Array<\n HTMLElement & { checkValidity?: () => boolean }\n >;\n // Find the first element in the form that is invalid\n const firstInvalidElement = elements.find(\n (element) =>\n typeof element.checkValidity === \"function\" &&\n !element.checkValidity(),\n );\n if (firstInvalidElement === this) {\n textarea.focus();\n }\n } else {\n // If not part of a form, simply focus.\n textarea.focus();\n }\n }\n }\n\n /**\n * Event Handlers\n * --------------------------------------------------------------------------\n */\n\n // Handle input event to check pattern validity\n private _handleInput(event: Event) {\n const textarea = event.target as HTMLInputElement;\n this.value = textarea.value;\n this._internals.setFormValue(this.value);\n\n // Field is invalid after unfocused, validate aggressively on each input (e.g. Eager mode: a combination of aggressive and lazy.)\n if (this._hasUserInteracted) {\n this._validate();\n }\n\n this.dispatchEvent(\n new CustomEvent(\"nys-input\", {\n detail: { id: this.id, value: this.value },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n // Handle focus event\n private _handleFocus() {\n this.dispatchEvent(new Event(\"nys-focus\"));\n }\n\n // Handle blur event\n private _handleBlur() {\n if (!this._hasUserInteracted) {\n this._hasUserInteracted = true; // At initial unfocus: if textarea is invalid, start aggressive mode\n }\n\n this._validate();\n this.dispatchEvent(new Event(\"nys-blur\"));\n }\n\n private _handleSelect(e: Event) {\n const select = e.target as HTMLSelectElement;\n this.value = select.value;\n this.dispatchEvent(\n new CustomEvent(\"nys-select\", {\n detail: { id: this.id, value: this.value },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n private _handleSelectionChange(e: Event) {\n const select = e.target as HTMLSelectElement;\n this.value = select.value;\n this.dispatchEvent(\n new CustomEvent(\"nys-selectionchange\", {\n detail: { id: this.id, value: this.value },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n render() {\n return html`\n <div class=\"nys-textarea\">\n <nys-label\n for=${this.id}\n label=${this.label}\n description=${this.description}\n flag=${this.required && !this.readonly\n ? \"required\"\n : this.optional\n ? \"optional\"\n : \"\"}\n tooltip=${this.tooltip}\n ?inverted=${this.inverted}\n >\n <slot name=\"description\" slot=\"description\">${this.description}</slot>\n </nys-label>\n <textarea\n class=\"nys-textarea__textarea ${this.resize}\"\n name=${this.name}\n id=${this.id}\n .value=${this.value}\n ?disabled=${this.disabled}\n ?required=${this.required && !this.readonly}\n ?readonly=${this.readonly}\n aria-disabled=${ifDefined(this.disabled ? \"true\" : undefined)}\n aria-required=${ifDefined(this.required ? \"true\" : undefined)}\n aria-label=${ifDefined(this.label || undefined)}\n aria-description=${ifDefined(this.description || undefined)}\n placeholder=${ifDefined(\n this.placeholder ? this.placeholder : undefined,\n )}\n maxlength=${ifDefined(this.maxlength ?? undefined)}\n .rows=${this.rows}\n form=${ifDefined(this.form || undefined)}\n @input=${this._handleInput}\n @focus=\"${this._handleFocus}\"\n @blur=\"${this._handleBlur}\"\n @select=\"${this._handleSelect}\"\n @selectionchange=\"${this._handleSelectionChange}\"\n ></textarea>\n <nys-errormessage\n ?showError=${this.showError}\n errorMessage=${this._internals.validationMessage || this.errorMessage}\n ></nys-errormessage>\n </div>\n `;\n }\n}\n\nif (!customElements.get(\"nys-textarea\")) {\n customElements.define(\"nys-textarea\", NysTextarea);\n}\n"],"names":["textareaIdCounter","_NysTextarea","LitElement","changedProperties","input","textarea","message","event","form","element","select","html","ifDefined","unsafeCSS","styles","NysTextarea","__decorateClass","property"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nysds/nys-textarea",
3
- "version": "1.13.1",
3
+ "version": "1.14.0",
4
4
  "description": "The Textarea component from the NYS Design System.",
5
5
  "module": "dist/nys-textarea.js",
6
6
  "types": "dist/index.d.ts",
@@ -23,14 +23,14 @@
23
23
  "lit-analyze": "lit-analyzer '**/*.ts'"
24
24
  },
25
25
  "dependencies": {
26
- "@nysds/nys-icon": "^1.13.1",
27
- "@nysds/nys-label": "^1.13.1",
28
- "@nysds/nys-errormessage": "^1.13.1"
26
+ "@nysds/nys-icon": "^1.14.0",
27
+ "@nysds/nys-label": "^1.14.0",
28
+ "@nysds/nys-errormessage": "^1.14.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "lit": "^3.3.1",
32
32
  "typescript": "^5.9.3",
33
- "vite": "^7.1.12"
33
+ "vite": "^7.3.1"
34
34
  },
35
35
  "keywords": [
36
36
  "new-york-state",