@nuralyui/textarea 0.1.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.
- package/bundle.js +782 -0
- package/index.d.ts +8 -0
- package/index.js +7 -0
- package/index.js.map +1 -0
- package/package.json +34 -0
- package/react.d.ts +9 -0
- package/react.js +16 -0
- package/react.js.map +1 -0
- package/textarea.component.d.ts +204 -0
- package/textarea.component.js +670 -0
- package/textarea.component.js.map +1 -0
- package/textarea.style.d.ts +59 -0
- package/textarea.style.js +386 -0
- package/textarea.style.js.map +1 -0
- package/textarea.types.d.ts +102 -0
- package/textarea.types.js +14 -0
- package/textarea.types.js.map +1 -0
|
@@ -0,0 +1,670 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2023 Nuraly, Laabidi Aymen
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
7
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
9
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
10
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
11
|
+
};
|
|
12
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
13
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
14
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
15
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
16
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
17
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
18
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
import { LitElement, html } from 'lit';
|
|
22
|
+
import { customElement, property, state, query } from 'lit/decorators.js';
|
|
23
|
+
import { styles } from './textarea.style.js';
|
|
24
|
+
import '../icon/icon.component.js';
|
|
25
|
+
import { TEXTAREA_DEFAULTS, EMPTY_STRING } from './textarea.types.js';
|
|
26
|
+
import { NuralyUIBaseMixin } from '../../shared/base-mixin.js';
|
|
27
|
+
import { TextareaValidationController, TextareaEventController } from './controllers/index.js';
|
|
28
|
+
/**
|
|
29
|
+
* Versatile textarea component with validation, resize options, and interactive features.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```html
|
|
33
|
+
* <nr-textarea placeholder="Enter your message"></nr-textarea>
|
|
34
|
+
* <nr-textarea rows="5" resize="vertical"></nr-textarea>
|
|
35
|
+
* <nr-textarea max-length="500" show-count></nr-textarea>
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @fires nr-textarea-change - Value changes
|
|
39
|
+
* @fires nr-focus - Textarea focused
|
|
40
|
+
* @fires nr-blur - Textarea blurred
|
|
41
|
+
* @fires nr-clear - Clear button clicked
|
|
42
|
+
* @fires nr-resize - Textarea resized
|
|
43
|
+
*
|
|
44
|
+
* @slot label - Textarea label
|
|
45
|
+
* @slot helper-text - Helper text
|
|
46
|
+
* @slot addon-before - Content before textarea
|
|
47
|
+
* @slot addon-after - Content after textarea
|
|
48
|
+
*/
|
|
49
|
+
let NrTextareaElement = class NrTextareaElement extends NuralyUIBaseMixin(LitElement) {
|
|
50
|
+
constructor() {
|
|
51
|
+
super();
|
|
52
|
+
// Controllers
|
|
53
|
+
this.validationController = new TextareaValidationController(this);
|
|
54
|
+
this.eventController = new TextareaEventController(this);
|
|
55
|
+
/** Disables the textarea */
|
|
56
|
+
this.disabled = false;
|
|
57
|
+
/** Makes the textarea read-only */
|
|
58
|
+
this.readonly = false;
|
|
59
|
+
/** Visual state (default, success, warning, error) */
|
|
60
|
+
this.state = "default" /* TEXTAREA_STATE.Default */;
|
|
61
|
+
/** Current textarea value */
|
|
62
|
+
this.value = EMPTY_STRING;
|
|
63
|
+
/** Textarea size (small, medium, large) */
|
|
64
|
+
this.size = "medium" /* TEXTAREA_SIZE.Medium */;
|
|
65
|
+
/** Visual variant (outlined, underlined, filled) */
|
|
66
|
+
this.variant = "underlined" /* TEXTAREA_VARIANT.Underlined */;
|
|
67
|
+
/** Resize behavior (none, vertical, horizontal, both) */
|
|
68
|
+
this.resize = "vertical" /* TEXTAREA_RESIZE.Vertical */;
|
|
69
|
+
/** Number of visible text lines */
|
|
70
|
+
this.rows = TEXTAREA_DEFAULTS.ROWS;
|
|
71
|
+
/** Number of visible character columns */
|
|
72
|
+
this.cols = TEXTAREA_DEFAULTS.COLS;
|
|
73
|
+
/** Placeholder text */
|
|
74
|
+
this.placeholder = EMPTY_STRING;
|
|
75
|
+
/** HTML autocomplete attribute */
|
|
76
|
+
this.autocomplete = 'off';
|
|
77
|
+
/** Shows clear button */
|
|
78
|
+
this.allowClear = false;
|
|
79
|
+
/** Shows character counter */
|
|
80
|
+
this.showCount = false;
|
|
81
|
+
/** Auto-resize textarea based on content */
|
|
82
|
+
this.autoResize = false;
|
|
83
|
+
/** Array of validation rules */
|
|
84
|
+
this.rules = [];
|
|
85
|
+
/** Validate on change */
|
|
86
|
+
this.validateOnChange = true;
|
|
87
|
+
/** Validate on blur */
|
|
88
|
+
this.validateOnBlur = true;
|
|
89
|
+
/** Show validation status icon */
|
|
90
|
+
this.hasFeedback = false;
|
|
91
|
+
this.isFocused = false;
|
|
92
|
+
this.characterCount = 0;
|
|
93
|
+
// Initialize character count based on initial value
|
|
94
|
+
this.characterCount = this.value.length;
|
|
95
|
+
// Setup controller event listeners
|
|
96
|
+
this.setupControllerListeners();
|
|
97
|
+
}
|
|
98
|
+
/** Clearable alias for controller interface compatibility */
|
|
99
|
+
get clearable() {
|
|
100
|
+
return this.allowClear;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Setup controller event listeners
|
|
104
|
+
*/
|
|
105
|
+
setupControllerListeners() {
|
|
106
|
+
// Validation events
|
|
107
|
+
this.addEventListener('textarea-validation', (e) => {
|
|
108
|
+
this.validationResult = e.detail.validationResult;
|
|
109
|
+
});
|
|
110
|
+
// Input events
|
|
111
|
+
this.addEventListener('textarea-input', (e) => {
|
|
112
|
+
this.handleControllerInput(e.detail.value);
|
|
113
|
+
});
|
|
114
|
+
// Focus/blur events
|
|
115
|
+
this.addEventListener('textarea-focus', () => {
|
|
116
|
+
this.isFocused = true;
|
|
117
|
+
});
|
|
118
|
+
this.addEventListener('textarea-blur', () => {
|
|
119
|
+
this.isFocused = false;
|
|
120
|
+
if (this.validateOnBlur) {
|
|
121
|
+
this.validationController.validateOnBlurIfEnabled();
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
// Clear events
|
|
125
|
+
this.addEventListener('textarea-clear', (e) => {
|
|
126
|
+
this.handleControllerClear(e.detail.previousValue);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Handle input from controller
|
|
131
|
+
*/
|
|
132
|
+
handleControllerInput(newValue) {
|
|
133
|
+
// Handle max length
|
|
134
|
+
if (this.maxLength && newValue.length > this.maxLength) {
|
|
135
|
+
this.value = newValue.slice(0, this.maxLength);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
this.value = newValue;
|
|
139
|
+
}
|
|
140
|
+
this.characterCount = this.value.length;
|
|
141
|
+
this.autoResizeIfNeeded();
|
|
142
|
+
this.dispatchChangeEvent();
|
|
143
|
+
// Validate if change validation is enabled
|
|
144
|
+
if (this.validateOnChange) {
|
|
145
|
+
this.validationController.validateOnChangeIfEnabled(this.value);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Handle clear from controller
|
|
150
|
+
*/
|
|
151
|
+
handleControllerClear(previousValue) {
|
|
152
|
+
this.characterCount = 0;
|
|
153
|
+
this.dispatchChangeEvent();
|
|
154
|
+
// Custom clear event for backward compatibility
|
|
155
|
+
this.dispatchEvent(new CustomEvent('nr-clear', {
|
|
156
|
+
detail: { value: this.value, previousValue },
|
|
157
|
+
bubbles: true
|
|
158
|
+
}));
|
|
159
|
+
}
|
|
160
|
+
connectedCallback() {
|
|
161
|
+
super.connectedCallback();
|
|
162
|
+
this.setupResizeObserver();
|
|
163
|
+
}
|
|
164
|
+
disconnectedCallback() {
|
|
165
|
+
super.disconnectedCallback();
|
|
166
|
+
this.cleanupResizeObserver();
|
|
167
|
+
}
|
|
168
|
+
firstUpdated(changedProperties) {
|
|
169
|
+
super.firstUpdated(changedProperties);
|
|
170
|
+
this.updateTextareaValue();
|
|
171
|
+
this.autoResizeIfNeeded();
|
|
172
|
+
// Setup resize observer for the textarea element
|
|
173
|
+
if (this.resizeObserver && this.textareaElement) {
|
|
174
|
+
this.resizeObserver.observe(this.textareaElement);
|
|
175
|
+
}
|
|
176
|
+
// Setup event controllers with DOM elements
|
|
177
|
+
this.eventController.setupEventListeners();
|
|
178
|
+
}
|
|
179
|
+
updated(changedProperties) {
|
|
180
|
+
super.updated(changedProperties);
|
|
181
|
+
if (changedProperties.has('value')) {
|
|
182
|
+
this.characterCount = this.value.length;
|
|
183
|
+
this.updateTextareaValue();
|
|
184
|
+
this.autoResizeIfNeeded();
|
|
185
|
+
}
|
|
186
|
+
if (changedProperties.has('autoResize')) {
|
|
187
|
+
this.autoResizeIfNeeded();
|
|
188
|
+
}
|
|
189
|
+
// Update validation rules if they changed
|
|
190
|
+
if (changedProperties.has('rules')) {
|
|
191
|
+
this.validationController.clearValidation();
|
|
192
|
+
if (this.rules.length > 0 && this.value) {
|
|
193
|
+
this.validationController.validate(this.value);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
setupResizeObserver() {
|
|
198
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
199
|
+
this.resizeObserver = new ResizeObserver((entries) => {
|
|
200
|
+
for (const entry of entries) {
|
|
201
|
+
const { width, height } = entry.contentRect;
|
|
202
|
+
this.dispatchResizeEvent(width, height);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
cleanupResizeObserver() {
|
|
208
|
+
if (this.resizeObserver) {
|
|
209
|
+
this.resizeObserver.disconnect();
|
|
210
|
+
this.resizeObserver = undefined;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
updateTextareaValue() {
|
|
214
|
+
if (this.textareaElement && this.textareaElement.value !== this.value) {
|
|
215
|
+
this.textareaElement.value = this.value;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
autoResizeIfNeeded() {
|
|
219
|
+
if (!this.autoResize || !this.textareaElement)
|
|
220
|
+
return;
|
|
221
|
+
// Reset height to auto to get the natural scroll height
|
|
222
|
+
this.textareaElement.style.height = 'auto';
|
|
223
|
+
const scrollHeight = this.textareaElement.scrollHeight;
|
|
224
|
+
// Apply min/max height constraints
|
|
225
|
+
let newHeight = scrollHeight;
|
|
226
|
+
if (this.minHeight && newHeight < this.minHeight) {
|
|
227
|
+
newHeight = this.minHeight;
|
|
228
|
+
}
|
|
229
|
+
if (this.maxHeight && newHeight > this.maxHeight) {
|
|
230
|
+
newHeight = this.maxHeight;
|
|
231
|
+
}
|
|
232
|
+
this.textareaElement.style.height = `${newHeight}px`;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Handle input event (now delegated to event controller via addEventListener)
|
|
236
|
+
*/
|
|
237
|
+
handleInput(e) {
|
|
238
|
+
// This method is kept for backward compatibility but actual handling
|
|
239
|
+
// is now done through the event controller
|
|
240
|
+
this.eventController.handleInput(e);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Handle focus event (now delegated to event controller)
|
|
244
|
+
*/
|
|
245
|
+
handleFocus(e) {
|
|
246
|
+
this.eventController.handleFocus(e);
|
|
247
|
+
// Dispatch legacy event for backward compatibility
|
|
248
|
+
this.dispatchEvent(new CustomEvent('nr-focus', {
|
|
249
|
+
detail: {
|
|
250
|
+
focused: true,
|
|
251
|
+
originalEvent: e
|
|
252
|
+
},
|
|
253
|
+
bubbles: true
|
|
254
|
+
}));
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Handle blur event (now delegated to event controller)
|
|
258
|
+
*/
|
|
259
|
+
handleBlur(e) {
|
|
260
|
+
this.eventController.handleBlur(e);
|
|
261
|
+
// Dispatch legacy event for backward compatibility
|
|
262
|
+
this.dispatchEvent(new CustomEvent('nr-blur', {
|
|
263
|
+
detail: {
|
|
264
|
+
focused: false,
|
|
265
|
+
originalEvent: e
|
|
266
|
+
},
|
|
267
|
+
bubbles: true
|
|
268
|
+
}));
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Handle clear event (now delegated to event controller)
|
|
272
|
+
*/
|
|
273
|
+
handleClear() {
|
|
274
|
+
this.eventController.handleClear();
|
|
275
|
+
}
|
|
276
|
+
dispatchChangeEvent() {
|
|
277
|
+
const detail = {
|
|
278
|
+
value: this.value,
|
|
279
|
+
length: this.characterCount,
|
|
280
|
+
exceedsMaxLength: this.maxLength ? this.characterCount > this.maxLength : false,
|
|
281
|
+
validation: this.validationResult
|
|
282
|
+
};
|
|
283
|
+
this.dispatchEvent(new CustomEvent('nr-textarea-change', {
|
|
284
|
+
detail,
|
|
285
|
+
bubbles: true
|
|
286
|
+
}));
|
|
287
|
+
}
|
|
288
|
+
dispatchResizeEvent(width, height) {
|
|
289
|
+
const detail = {
|
|
290
|
+
width,
|
|
291
|
+
height,
|
|
292
|
+
direction: this.resize
|
|
293
|
+
};
|
|
294
|
+
this.dispatchEvent(new CustomEvent('nr-resize', {
|
|
295
|
+
detail,
|
|
296
|
+
bubbles: true
|
|
297
|
+
}));
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Focus the textarea with optional configuration
|
|
301
|
+
*/
|
|
302
|
+
focus(options) {
|
|
303
|
+
this.eventController.focus();
|
|
304
|
+
// Handle cursor positioning if needed
|
|
305
|
+
if ((options === null || options === void 0 ? void 0 : options.cursor) !== undefined || (options === null || options === void 0 ? void 0 : options.select)) {
|
|
306
|
+
setTimeout(() => {
|
|
307
|
+
if (!this.textareaElement)
|
|
308
|
+
return;
|
|
309
|
+
if ((options === null || options === void 0 ? void 0 : options.cursor) !== undefined) {
|
|
310
|
+
if (typeof options.cursor === 'number') {
|
|
311
|
+
this.eventController.setCursorPosition(options.cursor);
|
|
312
|
+
}
|
|
313
|
+
else if (options.cursor === 'start') {
|
|
314
|
+
this.eventController.setCursorPosition(0);
|
|
315
|
+
}
|
|
316
|
+
else if (options.cursor === 'end') {
|
|
317
|
+
this.eventController.setCursorPosition(this.value.length);
|
|
318
|
+
}
|
|
319
|
+
else if (options.cursor === 'all') {
|
|
320
|
+
this.eventController.selectAll();
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
else if (options === null || options === void 0 ? void 0 : options.select) {
|
|
324
|
+
this.eventController.selectAll();
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Blur the textarea
|
|
331
|
+
*/
|
|
332
|
+
blur() {
|
|
333
|
+
this.eventController.blur();
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Get validation status
|
|
337
|
+
*/
|
|
338
|
+
getValidationResult() {
|
|
339
|
+
return this.validationController.validationResult;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Trigger validation manually (ValidatableComponent interface)
|
|
343
|
+
*/
|
|
344
|
+
validate() {
|
|
345
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
346
|
+
const result = yield this.validationController.validate(this.value);
|
|
347
|
+
return result.isValid;
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Clear validation state
|
|
352
|
+
*/
|
|
353
|
+
clearValidation() {
|
|
354
|
+
this.validationController.clearValidation();
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Clear the textarea value
|
|
358
|
+
*/
|
|
359
|
+
clear() {
|
|
360
|
+
this.eventController.handleClear();
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Add a validation rule (ValidatableComponent interface)
|
|
364
|
+
*/
|
|
365
|
+
addRule(rule) {
|
|
366
|
+
this.validationController.addRule(rule);
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Remove validation rules matching predicate (ValidatableComponent interface)
|
|
370
|
+
*/
|
|
371
|
+
removeRule(predicate) {
|
|
372
|
+
this.validationController.removeRule(predicate);
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Clear all validation rules (ValidatableComponent interface)
|
|
376
|
+
*/
|
|
377
|
+
clearRules() {
|
|
378
|
+
this.validationController.clearRules();
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Get current validation status (ValidatableComponent interface)
|
|
382
|
+
*/
|
|
383
|
+
getValidationStatus() {
|
|
384
|
+
var _a;
|
|
385
|
+
const result = this.validationController.validationResult;
|
|
386
|
+
return {
|
|
387
|
+
isValid: (_a = result === null || result === void 0 ? void 0 : result.isValid) !== null && _a !== void 0 ? _a : true,
|
|
388
|
+
errors: (result === null || result === void 0 ? void 0 : result.level) === 'error' ? result.messages : [],
|
|
389
|
+
warnings: (result === null || result === void 0 ? void 0 : result.level) === 'warning' ? result.messages : []
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Check if the textarea value is valid
|
|
394
|
+
*/
|
|
395
|
+
isValid() {
|
|
396
|
+
return this.validationController.isValid();
|
|
397
|
+
}
|
|
398
|
+
// Form integration methods (FormFieldCapable interface)
|
|
399
|
+
/**
|
|
400
|
+
* Check validity (HTML form API compatibility)
|
|
401
|
+
*/
|
|
402
|
+
checkValidity() {
|
|
403
|
+
return this.validationController.isValid();
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Report validity (HTML form API compatibility)
|
|
407
|
+
*/
|
|
408
|
+
reportValidity() {
|
|
409
|
+
const isValid = this.validationController.isValid();
|
|
410
|
+
if (!isValid) {
|
|
411
|
+
// Trigger validation to show error messages
|
|
412
|
+
this.validationController.validate(this.value);
|
|
413
|
+
}
|
|
414
|
+
return isValid;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Set custom validity message (HTML form API compatibility)
|
|
418
|
+
*/
|
|
419
|
+
setCustomValidity(message) {
|
|
420
|
+
if (message) {
|
|
421
|
+
// Add custom validation rule
|
|
422
|
+
this.validationController.addRule({
|
|
423
|
+
validator: () => false,
|
|
424
|
+
message,
|
|
425
|
+
level: 'error',
|
|
426
|
+
blocking: true
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
// Remove custom validation rules
|
|
431
|
+
this.validationController.removeRule(rule => rule.message === message);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Trigger validation manually (enhanced version)
|
|
436
|
+
*/
|
|
437
|
+
validateTextarea() {
|
|
438
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
439
|
+
const result = yield this.validationController.validate(this.value);
|
|
440
|
+
return result.isValid;
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Set validation state externally (for form integration)
|
|
445
|
+
*/
|
|
446
|
+
setValidationStatus(result) {
|
|
447
|
+
this.validationResult = result;
|
|
448
|
+
// Update state based on validation result
|
|
449
|
+
if (result.level === 'error') {
|
|
450
|
+
this.state = "error" /* TEXTAREA_STATE.Error */;
|
|
451
|
+
}
|
|
452
|
+
else if (result.level === 'warning') {
|
|
453
|
+
this.state = "warning" /* TEXTAREA_STATE.Warning */;
|
|
454
|
+
}
|
|
455
|
+
else if (result.isValid) {
|
|
456
|
+
this.state = "success" /* TEXTAREA_STATE.Success */;
|
|
457
|
+
}
|
|
458
|
+
this.requestUpdate();
|
|
459
|
+
}
|
|
460
|
+
renderLabel() {
|
|
461
|
+
return html `
|
|
462
|
+
<div class="textarea-label">
|
|
463
|
+
<slot name="label"></slot>
|
|
464
|
+
${this.required ? html `<span class="required-indicator">*</span>` : ''}
|
|
465
|
+
</div>
|
|
466
|
+
`;
|
|
467
|
+
}
|
|
468
|
+
renderTextarea() {
|
|
469
|
+
return html `
|
|
470
|
+
<textarea
|
|
471
|
+
class="textarea-element"
|
|
472
|
+
.value=${this.value}
|
|
473
|
+
.disabled=${this.disabled}
|
|
474
|
+
.readOnly=${this.readonly}
|
|
475
|
+
.required=${this.required || false}
|
|
476
|
+
.rows=${this.rows}
|
|
477
|
+
.cols=${this.cols}
|
|
478
|
+
.placeholder=${this.placeholder}
|
|
479
|
+
.autocomplete=${this.autocomplete}
|
|
480
|
+
.name=${this.name || ''}
|
|
481
|
+
maxlength=${this.maxLength || ''}
|
|
482
|
+
style="resize: ${this.resize}"
|
|
483
|
+
@input=${this.handleInput}
|
|
484
|
+
@focus=${this.handleFocus}
|
|
485
|
+
@blur=${this.handleBlur}
|
|
486
|
+
></textarea>
|
|
487
|
+
`;
|
|
488
|
+
}
|
|
489
|
+
renderValidationIcon() {
|
|
490
|
+
if (!this.hasFeedback || !this.validationResult)
|
|
491
|
+
return '';
|
|
492
|
+
const iconName = this.validationResult.level === 'error' ? 'error' :
|
|
493
|
+
this.validationResult.level === 'warning' ? 'warning' : 'check-circle';
|
|
494
|
+
return html `
|
|
495
|
+
<nr-icon
|
|
496
|
+
class="validation-icon ${this.validationResult.level}"
|
|
497
|
+
name="${iconName}"
|
|
498
|
+
size="small"
|
|
499
|
+
></nr-icon>
|
|
500
|
+
`;
|
|
501
|
+
}
|
|
502
|
+
renderClearButton() {
|
|
503
|
+
if (!this.allowClear || !this.value || this.disabled || this.readonly)
|
|
504
|
+
return '';
|
|
505
|
+
return html `
|
|
506
|
+
<button
|
|
507
|
+
class="clear-button"
|
|
508
|
+
type="button"
|
|
509
|
+
@click=${this.handleClear}
|
|
510
|
+
aria-label="Clear textarea"
|
|
511
|
+
>
|
|
512
|
+
<nr-icon name="x" size="small"></nr-icon>
|
|
513
|
+
</button>
|
|
514
|
+
`;
|
|
515
|
+
}
|
|
516
|
+
renderCharacterCount() {
|
|
517
|
+
if (!this.showCount)
|
|
518
|
+
return '';
|
|
519
|
+
const maxText = this.maxLength ? ` / ${this.maxLength}` : '';
|
|
520
|
+
const isOverLimit = this.maxLength && this.characterCount > this.maxLength;
|
|
521
|
+
return html `
|
|
522
|
+
<div class="character-count ${isOverLimit ? 'over-limit' : ''}">
|
|
523
|
+
${this.characterCount}${maxText}
|
|
524
|
+
</div>
|
|
525
|
+
`;
|
|
526
|
+
}
|
|
527
|
+
renderHelperText() {
|
|
528
|
+
var _a, _b;
|
|
529
|
+
const hasHelperSlot = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('slot[name="helper-text"]');
|
|
530
|
+
const hasHelperSlotContent = hasHelperSlot === null || hasHelperSlot === void 0 ? void 0 : hasHelperSlot.assignedNodes().length;
|
|
531
|
+
const hasValidationMessages = (_b = this.validationResult) === null || _b === void 0 ? void 0 : _b.messages.length;
|
|
532
|
+
if (!hasHelperSlotContent && !hasValidationMessages)
|
|
533
|
+
return '';
|
|
534
|
+
return html `
|
|
535
|
+
<div class="helper-text">
|
|
536
|
+
${hasValidationMessages ?
|
|
537
|
+
this.validationResult.messages.map(msg => html `<div class="validation-message ${this.validationResult.level}">${msg}</div>`) :
|
|
538
|
+
html `<slot name="helper-text"></slot>`}
|
|
539
|
+
</div>
|
|
540
|
+
`;
|
|
541
|
+
}
|
|
542
|
+
render() {
|
|
543
|
+
const classes = [
|
|
544
|
+
'textarea-container',
|
|
545
|
+
`size-${this.size}`,
|
|
546
|
+
`variant-${this.variant}`,
|
|
547
|
+
`state-${this.state}`,
|
|
548
|
+
this.isFocused ? 'focused' : '',
|
|
549
|
+
this.disabled ? 'disabled' : '',
|
|
550
|
+
this.readonly ? 'readonly' : '',
|
|
551
|
+
this.validationResult ? `validation-${this.validationResult.level}` : ''
|
|
552
|
+
].filter(Boolean).join(' ');
|
|
553
|
+
return html `
|
|
554
|
+
<div class="${classes}">
|
|
555
|
+
${this.renderLabel()}
|
|
556
|
+
|
|
557
|
+
<div class="textarea-wrapper">
|
|
558
|
+
<div class="addon-before">
|
|
559
|
+
<slot name="addon-before"></slot>
|
|
560
|
+
</div>
|
|
561
|
+
|
|
562
|
+
<div class="textarea-input-container">
|
|
563
|
+
${this.renderTextarea()}
|
|
564
|
+
${this.renderValidationIcon()}
|
|
565
|
+
${this.renderClearButton()}
|
|
566
|
+
</div>
|
|
567
|
+
|
|
568
|
+
<div class="addon-after">
|
|
569
|
+
<slot name="addon-after"></slot>
|
|
570
|
+
</div>
|
|
571
|
+
</div>
|
|
572
|
+
|
|
573
|
+
<div class="textarea-footer">
|
|
574
|
+
${this.renderHelperText()}
|
|
575
|
+
${this.renderCharacterCount()}
|
|
576
|
+
</div>
|
|
577
|
+
</div>
|
|
578
|
+
`;
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
NrTextareaElement.styles = styles;
|
|
582
|
+
__decorate([
|
|
583
|
+
query('textarea')
|
|
584
|
+
], NrTextareaElement.prototype, "textareaElement", void 0);
|
|
585
|
+
__decorate([
|
|
586
|
+
property({ type: Boolean, reflect: true })
|
|
587
|
+
], NrTextareaElement.prototype, "disabled", void 0);
|
|
588
|
+
__decorate([
|
|
589
|
+
property({ type: Boolean, reflect: true })
|
|
590
|
+
], NrTextareaElement.prototype, "readonly", void 0);
|
|
591
|
+
__decorate([
|
|
592
|
+
property({ type: String, reflect: true })
|
|
593
|
+
], NrTextareaElement.prototype, "state", void 0);
|
|
594
|
+
__decorate([
|
|
595
|
+
property({ type: String })
|
|
596
|
+
], NrTextareaElement.prototype, "value", void 0);
|
|
597
|
+
__decorate([
|
|
598
|
+
property({ type: String })
|
|
599
|
+
], NrTextareaElement.prototype, "size", void 0);
|
|
600
|
+
__decorate([
|
|
601
|
+
property({ type: String, reflect: true })
|
|
602
|
+
], NrTextareaElement.prototype, "variant", void 0);
|
|
603
|
+
__decorate([
|
|
604
|
+
property({ type: String })
|
|
605
|
+
], NrTextareaElement.prototype, "resize", void 0);
|
|
606
|
+
__decorate([
|
|
607
|
+
property({ type: Number })
|
|
608
|
+
], NrTextareaElement.prototype, "rows", void 0);
|
|
609
|
+
__decorate([
|
|
610
|
+
property({ type: Number })
|
|
611
|
+
], NrTextareaElement.prototype, "cols", void 0);
|
|
612
|
+
__decorate([
|
|
613
|
+
property({ type: String })
|
|
614
|
+
], NrTextareaElement.prototype, "placeholder", void 0);
|
|
615
|
+
__decorate([
|
|
616
|
+
property({ type: String })
|
|
617
|
+
], NrTextareaElement.prototype, "autocomplete", void 0);
|
|
618
|
+
__decorate([
|
|
619
|
+
property({ type: String })
|
|
620
|
+
], NrTextareaElement.prototype, "name", void 0);
|
|
621
|
+
__decorate([
|
|
622
|
+
property({ type: Boolean })
|
|
623
|
+
], NrTextareaElement.prototype, "required", void 0);
|
|
624
|
+
__decorate([
|
|
625
|
+
property({ type: Boolean, reflect: true })
|
|
626
|
+
], NrTextareaElement.prototype, "allowClear", void 0);
|
|
627
|
+
__decorate([
|
|
628
|
+
property({ type: Boolean, reflect: true })
|
|
629
|
+
], NrTextareaElement.prototype, "showCount", void 0);
|
|
630
|
+
__decorate([
|
|
631
|
+
property({ type: Number })
|
|
632
|
+
], NrTextareaElement.prototype, "maxLength", void 0);
|
|
633
|
+
__decorate([
|
|
634
|
+
property({ type: Number })
|
|
635
|
+
], NrTextareaElement.prototype, "minHeight", void 0);
|
|
636
|
+
__decorate([
|
|
637
|
+
property({ type: Number })
|
|
638
|
+
], NrTextareaElement.prototype, "maxHeight", void 0);
|
|
639
|
+
__decorate([
|
|
640
|
+
property({ type: Boolean })
|
|
641
|
+
], NrTextareaElement.prototype, "autoResize", void 0);
|
|
642
|
+
__decorate([
|
|
643
|
+
property({ type: Array })
|
|
644
|
+
], NrTextareaElement.prototype, "rules", void 0);
|
|
645
|
+
__decorate([
|
|
646
|
+
property({ type: Boolean, attribute: 'validate-on-change' })
|
|
647
|
+
], NrTextareaElement.prototype, "validateOnChange", void 0);
|
|
648
|
+
__decorate([
|
|
649
|
+
property({ type: Boolean, attribute: 'validate-on-blur' })
|
|
650
|
+
], NrTextareaElement.prototype, "validateOnBlur", void 0);
|
|
651
|
+
__decorate([
|
|
652
|
+
property({ type: Boolean, attribute: 'has-feedback' })
|
|
653
|
+
], NrTextareaElement.prototype, "hasFeedback", void 0);
|
|
654
|
+
__decorate([
|
|
655
|
+
property({ type: String, attribute: 'validation-message' })
|
|
656
|
+
], NrTextareaElement.prototype, "validationMessage", void 0);
|
|
657
|
+
__decorate([
|
|
658
|
+
state()
|
|
659
|
+
], NrTextareaElement.prototype, "isFocused", void 0);
|
|
660
|
+
__decorate([
|
|
661
|
+
state()
|
|
662
|
+
], NrTextareaElement.prototype, "validationResult", void 0);
|
|
663
|
+
__decorate([
|
|
664
|
+
state()
|
|
665
|
+
], NrTextareaElement.prototype, "characterCount", void 0);
|
|
666
|
+
NrTextareaElement = __decorate([
|
|
667
|
+
customElement('nr-textarea')
|
|
668
|
+
], NrTextareaElement);
|
|
669
|
+
export { NrTextareaElement };
|
|
670
|
+
//# sourceMappingURL=textarea.component.js.map
|