@nuralyui/input 0.0.5 → 0.0.7

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.
@@ -19,30 +19,54 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
19
19
  step((generator = generator.apply(thisArg, _arguments || [])).next());
20
20
  });
21
21
  };
22
- import { LitElement, html, nothing } from 'lit';
22
+ import { LitElement, html } from 'lit';
23
23
  import { customElement, property, query, state } from 'lit/decorators.js';
24
24
  import { styles } from './input.style.js';
25
- import { EMPTY_STRING } from './input.constant.js';
26
- import { choose } from 'lit/directives/choose.js';
25
+ import { EMPTY_STRING } from './input.types.js';
27
26
  import { NuralyUIBaseMixin } from '../../shared/base-mixin.js';
27
+ import { InputValidationUtils } from './utils/input-validation.utils.js';
28
+ import { InputRenderUtils } from './utils/input-renderers.js';
28
29
  import '../icon/icon.component.js';
29
30
  let NrInputElement = class NrInputElement extends NuralyUIBaseMixin(LitElement) {
30
31
  constructor() {
31
32
  super(...arguments);
32
33
  this.disabled = false;
34
+ this.readonly = false;
33
35
  this.state = "default" /* INPUT_STATE.Default */;
34
36
  this.value = EMPTY_STRING;
35
37
  this.size = "medium" /* INPUT_SIZE.Medium */;
38
+ this.variant = "underlined" /* INPUT_VARIANT.Underlined */;
36
39
  this.type = "text" /* INPUT_TYPE.TEXT */;
37
40
  this.placeholder = EMPTY_STRING;
38
41
  this.autocomplete = 'off';
39
42
  this.withCopy = false;
43
+ this.allowClear = false;
44
+ this.showCount = false;
40
45
  this.inputType = EMPTY_STRING;
46
+ this.hasAddonBefore = false;
47
+ this.hasAddonAfter = false;
48
+ this.focused = false;
41
49
  /**
42
50
  * Required components that must be registered for this component to work properly
43
51
  */
44
52
  this.requiredComponents = ['hy-icon'];
45
53
  }
54
+ /**
55
+ * Get the character count display text
56
+ */
57
+ get characterCountDisplay() {
58
+ const currentLength = this.value.length;
59
+ if (this.maxLength) {
60
+ return `${currentLength}/${this.maxLength}`;
61
+ }
62
+ return `${currentLength}`;
63
+ }
64
+ /**
65
+ * Check if character count is over the limit
66
+ */
67
+ get isOverCharacterLimit() {
68
+ return this.maxLength ? this.value.length > this.maxLength : false;
69
+ }
46
70
  /**
47
71
  * Check for required dependencies when component is connected to DOM
48
72
  */
@@ -57,9 +81,16 @@ let NrInputElement = class NrInputElement extends NuralyUIBaseMixin(LitElement)
57
81
  this.value = this.min;
58
82
  }
59
83
  }
84
+ // Validate numeric properties when they change
85
+ if (_changedProperties.has('type') ||
86
+ _changedProperties.has('min') ||
87
+ _changedProperties.has('max') ||
88
+ _changedProperties.has('step')) {
89
+ InputValidationUtils.validateNumericProperties(this.type, this.min, this.max, this.step);
90
+ }
60
91
  }
61
92
  updated(_changedProperties) {
62
- if (_changedProperties.has('step') || _changedProperties.has('min') || _changedProperties.has('max')) {
93
+ if (_changedProperties.has('step') || _changedProperties.has('min') || _changedProperties.has('max') || _changedProperties.has('maxLength')) {
63
94
  const input = this.input;
64
95
  if (input) {
65
96
  if (this.step)
@@ -74,63 +105,293 @@ let NrInputElement = class NrInputElement extends NuralyUIBaseMixin(LitElement)
74
105
  input.setAttribute('max', this.max);
75
106
  else
76
107
  input.removeAttribute('max');
108
+ if (this.maxLength)
109
+ input.setAttribute('maxlength', this.maxLength.toString());
110
+ else
111
+ input.removeAttribute('maxlength');
77
112
  }
78
113
  }
79
114
  }
80
- _increment() {
81
- this.input.stepUp();
82
- this.value = this.input.value; // Sync the property
83
- this.dispatchEvent(new CustomEvent('nr-input', {
84
- detail: {
85
- value: this.value,
86
- target: this.input,
87
- action: 'increment'
88
- },
115
+ firstUpdated() {
116
+ this._checkInitialSlotContent();
117
+ }
118
+ /**
119
+ * Check initial slot content on first render
120
+ */
121
+ _checkInitialSlotContent() {
122
+ // Check for addon-before content
123
+ const addonBeforeElements = this.querySelectorAll('[slot="addon-before"]');
124
+ this.hasAddonBefore = addonBeforeElements.length > 0;
125
+ // Check for addon-after content
126
+ const addonAfterElements = this.querySelectorAll('[slot="addon-after"]');
127
+ this.hasAddonAfter = addonAfterElements.length > 0;
128
+ }
129
+ /**
130
+ * Handle slot changes to determine addon visibility
131
+ */
132
+ _handleSlotChange(e) {
133
+ const slot = e.target;
134
+ const slotName = slot.name;
135
+ if (slotName === 'addon-before') {
136
+ this.hasAddonBefore = slot.assignedElements().length > 0;
137
+ }
138
+ else if (slotName === 'addon-after') {
139
+ this.hasAddonAfter = slot.assignedElements().length > 0;
140
+ }
141
+ }
142
+ // ========================================
143
+ // FOCUS MANAGEMENT METHODS
144
+ // ========================================
145
+ /**
146
+ * Focus the input with advanced options
147
+ */
148
+ focus(options) {
149
+ if (!this.input)
150
+ return;
151
+ const focusOptions = Object.assign({ preventScroll: false }, options);
152
+ // Focus the input element
153
+ this.input.focus({ preventScroll: focusOptions.preventScroll });
154
+ // Handle cursor positioning
155
+ if (focusOptions.cursor !== undefined) {
156
+ this.setCursorPosition(focusOptions.cursor);
157
+ }
158
+ // Handle text selection
159
+ if (focusOptions.select) {
160
+ this.selectAll();
161
+ }
162
+ }
163
+ /**
164
+ * Blur the input with advanced options
165
+ */
166
+ blur(options) {
167
+ if (!this.input)
168
+ return;
169
+ const currentPosition = this.getCursorPosition();
170
+ this.input.blur();
171
+ // Restore cursor position if requested
172
+ if ((options === null || options === void 0 ? void 0 : options.restoreCursor) && currentPosition !== -1) {
173
+ // We'll restore it on next focus
174
+ this.input.dataset.restoreCursor = currentPosition.toString();
175
+ }
176
+ }
177
+ /**
178
+ * Select all text in the input
179
+ */
180
+ selectAll() {
181
+ if (!this.input)
182
+ return;
183
+ // Ensure the input is focused first
184
+ if (document.activeElement !== this.input) {
185
+ this.input.focus();
186
+ }
187
+ // Wait for next frame to ensure focus is complete
188
+ requestAnimationFrame(() => {
189
+ if (!this.input)
190
+ return;
191
+ this.input.select();
192
+ });
193
+ }
194
+ /**
195
+ * Select a range of text in the input
196
+ */
197
+ selectRange(start, end) {
198
+ if (!this.input)
199
+ return;
200
+ // Ensure the input is focused first
201
+ if (document.activeElement !== this.input) {
202
+ this.input.focus();
203
+ }
204
+ // Wait for next frame to ensure focus is complete
205
+ requestAnimationFrame(() => {
206
+ if (!this.input)
207
+ return;
208
+ const maxLength = this.input.value.length;
209
+ const validStart = Math.max(0, Math.min(start, maxLength));
210
+ const validEnd = Math.max(validStart, Math.min(end, maxLength));
211
+ this.input.setSelectionRange(validStart, validEnd);
212
+ });
213
+ }
214
+ /**
215
+ * Get the current cursor position
216
+ */
217
+ getCursorPosition() {
218
+ if (!this.input)
219
+ return -1;
220
+ return this.input.selectionStart || 0;
221
+ }
222
+ /**
223
+ * Set the cursor position
224
+ */
225
+ setCursorPosition(position) {
226
+ if (!this.input)
227
+ return;
228
+ // Ensure the input is focused first
229
+ if (document.activeElement !== this.input) {
230
+ this.input.focus();
231
+ }
232
+ requestAnimationFrame(() => {
233
+ if (!this.input)
234
+ return;
235
+ let pos;
236
+ const valueLength = this.value.length;
237
+ switch (position) {
238
+ case 'start':
239
+ pos = 0;
240
+ break;
241
+ case 'end':
242
+ pos = valueLength;
243
+ break;
244
+ case 'all':
245
+ this.selectAll();
246
+ return;
247
+ default:
248
+ pos = typeof position === 'number' ? Math.min(Math.max(0, position), valueLength) : 0;
249
+ }
250
+ this.input.setSelectionRange(pos, pos);
251
+ });
252
+ }
253
+ /**
254
+ * Get the currently selected text
255
+ */
256
+ getSelectedText() {
257
+ if (!this.input)
258
+ return '';
259
+ const start = this.input.selectionStart || 0;
260
+ const end = this.input.selectionEnd || 0;
261
+ return this.value.substring(start, end);
262
+ }
263
+ /**
264
+ * Check if the input is currently focused
265
+ */
266
+ isFocused() {
267
+ return this.focused && document.activeElement === this.input;
268
+ }
269
+ // ========================================
270
+ // EVENT HANDLING METHODS
271
+ // ========================================
272
+ /**
273
+ * Centralized event dispatcher to ensure consistent event structure
274
+ */
275
+ _dispatchInputEvent(eventName, detail) {
276
+ this.dispatchEvent(new CustomEvent(eventName, {
277
+ detail,
89
278
  bubbles: true
90
279
  }));
91
280
  }
92
- _decrement() {
93
- this.input.stepDown();
94
- this.value = this.input.value; // Sync the property
95
- this.dispatchEvent(new CustomEvent('nr-input', {
96
- detail: {
281
+ _handleKeyDown(keyDownEvent) {
282
+ // Prevent all key input when readonly
283
+ if (this.readonly) {
284
+ const allowedReadonlyKeys = [
285
+ 'Tab', 'Escape', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown',
286
+ 'Home', 'End', 'PageUp', 'PageDown'
287
+ ];
288
+ if (keyDownEvent.ctrlKey || keyDownEvent.metaKey) {
289
+ const allowedCombinations = ['KeyA', 'KeyC'];
290
+ if (allowedCombinations.includes(keyDownEvent.code)) {
291
+ return;
292
+ }
293
+ }
294
+ if (!allowedReadonlyKeys.includes(keyDownEvent.key)) {
295
+ keyDownEvent.preventDefault();
296
+ return;
297
+ }
298
+ }
299
+ // Handle Enter key
300
+ if (keyDownEvent.key === 'Enter') {
301
+ this._dispatchInputEvent('nr-enter', {
302
+ target: keyDownEvent.target,
97
303
  value: this.value,
98
- target: this.input,
99
- action: 'decrement'
100
- },
101
- bubbles: true
102
- }));
304
+ originalEvent: keyDownEvent
305
+ });
306
+ return;
307
+ }
308
+ // Prevent non-numeric input for number type
309
+ if (this.type === "number" /* INPUT_TYPE.NUMBER */) {
310
+ InputValidationUtils.preventNonNumericInput(keyDownEvent, this.min);
311
+ if (keyDownEvent.defaultPrevented) {
312
+ this._dispatchInputEvent('nr-invalid-key', {
313
+ key: keyDownEvent.key,
314
+ target: keyDownEvent.target,
315
+ value: this.value,
316
+ originalEvent: keyDownEvent
317
+ });
318
+ }
319
+ }
103
320
  }
104
321
  _valueChange(e) {
322
+ if (this.readonly) {
323
+ e.preventDefault();
324
+ return;
325
+ }
105
326
  const target = e.target;
106
- this.value = target.value;
107
- this.dispatchEvent(new CustomEvent('nr-input', {
108
- detail: {
109
- value: this.value,
327
+ const newValue = target.value;
328
+ // Check character limit
329
+ if (this.maxLength && newValue.length > this.maxLength) {
330
+ this._dispatchInputEvent('nr-character-limit-exceeded', {
331
+ value: newValue,
110
332
  target: target,
333
+ limit: this.maxLength,
111
334
  originalEvent: e
112
- },
113
- bubbles: true
114
- }));
335
+ });
336
+ // Note: HTML maxlength attribute usually prevents this, but we dispatch event for awareness
337
+ }
338
+ if (this.type === "number" /* INPUT_TYPE.NUMBER */ && newValue) {
339
+ const validation = InputValidationUtils.validateNumericValue(newValue, this.min, this.max);
340
+ if (!validation.isValid) {
341
+ console.warn(validation.warnings[0]);
342
+ this._dispatchInputEvent('nr-validation-error', {
343
+ value: newValue,
344
+ target: target,
345
+ error: validation.warnings[0],
346
+ originalEvent: e
347
+ });
348
+ return;
349
+ }
350
+ validation.warnings.forEach(warning => console.warn(warning));
351
+ }
352
+ this.value = newValue;
353
+ this._dispatchInputEvent('nr-input', {
354
+ value: this.value,
355
+ target: target,
356
+ originalEvent: e
357
+ });
115
358
  }
116
- handleKeyDown(keyDownEvent) {
117
- if (keyDownEvent.key === 'Enter') {
118
- this.dispatchEvent(new CustomEvent('nr-enter', {
119
- detail: {
120
- target: keyDownEvent.target,
121
- value: this.value,
122
- originalEvent: keyDownEvent
123
- },
124
- bubbles: true
125
- }));
359
+ _focusEvent(e) {
360
+ this.focused = true;
361
+ // Handle cursor restoration if requested
362
+ const input = e.target;
363
+ if (input.dataset.restoreCursor) {
364
+ const position = parseInt(input.dataset.restoreCursor, 10);
365
+ this.setCursorPosition(position);
366
+ delete input.dataset.restoreCursor;
126
367
  }
368
+ const focusDetail = {
369
+ focused: true,
370
+ cursorPosition: this.getCursorPosition(),
371
+ selectedText: this.getSelectedText()
372
+ };
373
+ this._dispatchInputEvent('nr-focus', Object.assign({ target: e.target, value: this.value }, focusDetail));
374
+ this._dispatchInputEvent('nr-focus-change', focusDetail);
375
+ }
376
+ _blurEvent(e) {
377
+ this.focused = false;
378
+ const focusDetail = {
379
+ focused: false,
380
+ cursorPosition: this.getCursorPosition(),
381
+ selectedText: this.getSelectedText()
382
+ };
383
+ this._dispatchInputEvent('nr-blur', Object.assign({ target: e.target, value: this.value }, focusDetail));
384
+ this._dispatchInputEvent('nr-focus-change', focusDetail);
127
385
  }
128
386
  _handleIconKeydown(keyDownEvent) {
129
387
  if (keyDownEvent.key === 'Enter' || keyDownEvent.key === ' ') {
130
388
  keyDownEvent.preventDefault();
131
389
  const target = keyDownEvent.target;
132
390
  if (target.id === 'copy-icon') {
133
- this.onCopy();
391
+ this._onCopy();
392
+ }
393
+ else if (target.id === 'clear-icon') {
394
+ this._onClear();
134
395
  }
135
396
  else if (target.id === 'password-icon') {
136
397
  this._togglePasswordIcon();
@@ -145,43 +406,80 @@ let NrInputElement = class NrInputElement extends NuralyUIBaseMixin(LitElement)
145
406
  }
146
407
  }
147
408
  }
148
- onCopy() {
409
+ _onCopy() {
149
410
  return __awaiter(this, void 0, void 0, function* () {
150
411
  try {
151
412
  const input = this.shadowRoot.getElementById('input');
152
413
  input.select();
153
414
  yield navigator.clipboard.writeText(input.value);
154
- this.dispatchEvent(new CustomEvent('nr-copy-success', {
155
- detail: { value: input.value },
156
- bubbles: true
157
- }));
415
+ this._dispatchInputEvent('nr-copy-success', { value: input.value });
158
416
  }
159
417
  catch (error) {
160
- this.dispatchEvent(new CustomEvent('nr-copy-error', {
161
- detail: { error },
162
- bubbles: true
163
- }));
418
+ this._dispatchInputEvent('nr-copy-error', { error });
164
419
  }
165
420
  });
166
421
  }
167
- _getAriaDescribedBy() {
168
- var _a;
169
- const describedBy = [];
170
- // Check if helper text slot has content
171
- const helperSlot = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('slot[name="helper-text"]');
172
- if (helperSlot && helperSlot.assignedNodes().length > 0) {
173
- describedBy.push('helper-text');
422
+ _onClear() {
423
+ if (this.disabled || this.readonly) {
424
+ return;
174
425
  }
175
- return describedBy.join(' ') || '';
426
+ const previousValue = this.value;
427
+ this.value = EMPTY_STRING;
428
+ // Update the input element value
429
+ if (this.input) {
430
+ this.input.value = EMPTY_STRING;
431
+ }
432
+ this._dispatchInputEvent('nr-clear', {
433
+ previousValue,
434
+ target: this.input
435
+ });
436
+ // Also dispatch input event for consistency
437
+ this._dispatchInputEvent('nr-input', {
438
+ value: this.value,
439
+ target: this.input,
440
+ action: 'clear'
441
+ });
176
442
  }
177
- _focusEvent(e) {
178
- this.dispatchEvent(new CustomEvent('nr-focus', {
179
- detail: {
180
- target: e.target,
181
- value: this.value
182
- },
183
- bubbles: true
184
- }));
443
+ // ========================================
444
+ // OPERATION METHODS
445
+ // ========================================
446
+ _increment() {
447
+ try {
448
+ this.input.stepUp();
449
+ this.value = this.input.value;
450
+ this._dispatchInputEvent('nr-input', {
451
+ value: this.value,
452
+ target: this.input,
453
+ action: 'increment'
454
+ });
455
+ }
456
+ catch (error) {
457
+ console.warn('Failed to increment value:', error);
458
+ this._dispatchInputEvent('nr-increment-error', {
459
+ error,
460
+ value: this.value,
461
+ target: this.input
462
+ });
463
+ }
464
+ }
465
+ _decrement() {
466
+ try {
467
+ this.input.stepDown();
468
+ this.value = this.input.value;
469
+ this._dispatchInputEvent('nr-input', {
470
+ value: this.value,
471
+ target: this.input,
472
+ action: 'decrement'
473
+ });
474
+ }
475
+ catch (error) {
476
+ console.warn('Failed to decrement value:', error);
477
+ this._dispatchInputEvent('nr-decrement-error', {
478
+ error,
479
+ value: this.value,
480
+ target: this.input
481
+ });
482
+ }
185
483
  }
186
484
  _togglePasswordIcon() {
187
485
  if (this.inputType === "password" /* INPUT_TYPE.PASSWORD */) {
@@ -191,84 +489,56 @@ let NrInputElement = class NrInputElement extends NuralyUIBaseMixin(LitElement)
191
489
  this.inputType = "password" /* INPUT_TYPE.PASSWORD */;
192
490
  }
193
491
  }
492
+ _getAriaDescribedBy() {
493
+ var _a;
494
+ const describedBy = [];
495
+ const helperSlot = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('slot[name="helper-text"]');
496
+ if (helperSlot && helperSlot.assignedNodes().length > 0) {
497
+ describedBy.push('helper-text');
498
+ }
499
+ return describedBy.join(' ') || '';
500
+ }
501
+ // ========================================
502
+ // RENDER METHODS
503
+ // ========================================
194
504
  render() {
195
505
  return html `
196
506
  <slot name="label"></slot>
197
- <div data-size=${this.size} id="input-container">
198
- <input
199
- id="input"
200
- .disabled=${this.disabled}
201
- .value=${this.value}
202
- .placeholder=${this.placeholder}
203
- .type="${this.inputType}"
204
- .autocomplete=${this.autocomplete}
205
- aria-invalid=${this.state === "error" /* INPUT_STATE.Error */ ? 'true' : 'false'}
206
- aria-describedby=${this._getAriaDescribedBy()}
207
- @input=${this._valueChange}
208
- @focus=${this._focusEvent}
209
- @keydown=${this.handleKeyDown}
210
- />
211
- ${this.withCopy
212
- ? html `<hy-icon
213
- name="copy"
214
- type="regular"
215
- id="copy-icon"
216
- role="button"
217
- aria-label="Copy input value"
218
- tabindex="0"
219
- @click=${!this.disabled ? this.onCopy : nothing}
220
- @keydown=${this._handleIconKeydown}
221
- ></hy-icon>`
222
- : nothing}
223
- ${choose(this.state, [
224
- ["default" /* INPUT_STATE.Default */, () => undefined],
225
- ["warning" /* INPUT_STATE.Warning */, () => html `<hy-icon name="warning" id="warning-icon"></hy-icon>`],
226
- ["error" /* INPUT_STATE.Error */, () => html `<hy-icon name="exclamation-circle" id="error-icon"></hy-icon>`],
227
- ])}
228
- ${this.state == "default" /* INPUT_STATE.Default */ && this.type == "calendar" /* INPUT_TYPE.CALENDAR */
229
- ? html `<hy-icon name="calendar" type="regular" id="calendar-icon"></hy-icon>`
230
- : nothing}
231
- ${this.type == "password" /* INPUT_TYPE.PASSWORD */
232
- ? choose(this.inputType, [
233
- [
234
- "text" /* INPUT_TYPE.TEXT */,
235
- () => html `<hy-icon
236
- name="eye-slash"
237
- type="regular"
238
- id="password-icon"
239
- role="button"
240
- aria-label="Hide password"
241
- tabindex="0"
242
- @click=${!this.disabled ? this._togglePasswordIcon : nothing}
243
- @keydown=${this._handleIconKeydown}
244
- ></hy-icon>`,
245
- ],
246
- [
247
- "password" /* INPUT_TYPE.PASSWORD */,
248
- () => html `<hy-icon
249
- name="eye"
250
- type="regular"
251
- id="password-icon"
252
- role="button"
253
- aria-label="Show password"
254
- tabindex="0"
255
- @click=${!this.disabled ? this._togglePasswordIcon : nothing}
256
- @keydown=${this._handleIconKeydown}
257
- ></hy-icon>`,
258
- ],
259
- ])
260
- : this.type == "number" /* INPUT_TYPE.NUMBER */
261
- ? html `
262
- <div id="number-icons">
263
- ${this.state != "default" /* INPUT_STATE.Default */ ? html `<span id="icons-separator">|</span>` : nothing}
264
- <hy-icon name="minus" @click=${!this.disabled ? this._decrement : nothing}></hy-icon>
265
- <span id="icons-separator">|</span>
266
- <hy-icon name="plus" @click=${!this.disabled ? this._increment : nothing}></hy-icon>
267
- </div>
268
- `
269
- : nothing}
507
+ <div class="input-wrapper" data-theme="${this.currentTheme}">
508
+ ${InputRenderUtils.renderAddonBefore(this.hasAddonBefore, (e) => this._handleSlotChange(e))}
509
+ <div data-size=${this.size} id="input-container">
510
+ ${InputRenderUtils.renderPrefix()}
511
+ <input
512
+ id="input"
513
+ .disabled=${this.disabled}
514
+ .readOnly=${this.readonly}
515
+ .value=${this.value}
516
+ .placeholder=${this.placeholder}
517
+ .type="${this.inputType}"
518
+ .autocomplete=${this.autocomplete}
519
+ aria-invalid=${this.state === "error" /* INPUT_STATE.Error */ ? 'true' : 'false'}
520
+ aria-describedby=${this._getAriaDescribedBy()}
521
+ @input=${this._valueChange}
522
+ @focus=${this._focusEvent}
523
+ @blur=${this._blurEvent}
524
+ @keydown=${this._handleKeyDown}
525
+ />
526
+ ${InputRenderUtils.renderSuffix()}
527
+ ${InputRenderUtils.renderCopyIcon(this.withCopy, this.disabled, this.readonly, () => this._onCopy(), (e) => this._handleIconKeydown(e))}
528
+ ${InputRenderUtils.renderClearIcon(this.allowClear, this.value, this.disabled, this.readonly, () => this._onClear(), (e) => this._handleIconKeydown(e))}
529
+ ${InputRenderUtils.renderStateIcon(this.state)}
530
+ ${InputRenderUtils.renderCalendarIcon(this.state, this.type)}
531
+ ${InputRenderUtils.renderPasswordIcon(this.type, this.inputType, this.disabled, this.readonly, () => this._togglePasswordIcon(), (e) => this._handleIconKeydown(e))}
532
+ ${InputRenderUtils.renderNumberIcons(this.type, this.state, this.disabled, this.readonly, () => this._increment(), () => this._decrement(), (e) => this._handleIconKeydown(e))}
533
+ </div>
534
+ ${InputRenderUtils.renderAddonAfter(this.hasAddonAfter, (e) => this._handleSlotChange(e))}
270
535
  </div>
271
536
  <slot name="helper-text"></slot>
537
+ ${this.showCount ? html `
538
+ <div class="character-count" ?data-over-limit=${this.isOverCharacterLimit}>
539
+ ${this.characterCountDisplay}
540
+ </div>
541
+ ` : ''}
272
542
  `;
273
543
  }
274
544
  };
@@ -276,6 +546,9 @@ NrInputElement.styles = styles;
276
546
  __decorate([
277
547
  property({ type: Boolean, reflect: true })
278
548
  ], NrInputElement.prototype, "disabled", void 0);
549
+ __decorate([
550
+ property({ type: Boolean, reflect: true })
551
+ ], NrInputElement.prototype, "readonly", void 0);
279
552
  __decorate([
280
553
  property({ type: String, reflect: true })
281
554
  ], NrInputElement.prototype, "state", void 0);
@@ -285,6 +558,9 @@ __decorate([
285
558
  __decorate([
286
559
  property({ type: String })
287
560
  ], NrInputElement.prototype, "size", void 0);
561
+ __decorate([
562
+ property({ type: String, reflect: true })
563
+ ], NrInputElement.prototype, "variant", void 0);
288
564
  __decorate([
289
565
  property({ reflect: true })
290
566
  ], NrInputElement.prototype, "type", void 0);
@@ -306,9 +582,27 @@ __decorate([
306
582
  __decorate([
307
583
  property({ type: Boolean, reflect: true })
308
584
  ], NrInputElement.prototype, "withCopy", void 0);
585
+ __decorate([
586
+ property({ type: Boolean, reflect: true })
587
+ ], NrInputElement.prototype, "allowClear", void 0);
588
+ __decorate([
589
+ property({ type: Boolean, reflect: true })
590
+ ], NrInputElement.prototype, "showCount", void 0);
591
+ __decorate([
592
+ property({ type: Number })
593
+ ], NrInputElement.prototype, "maxLength", void 0);
309
594
  __decorate([
310
595
  state()
311
596
  ], NrInputElement.prototype, "inputType", void 0);
597
+ __decorate([
598
+ state()
599
+ ], NrInputElement.prototype, "hasAddonBefore", void 0);
600
+ __decorate([
601
+ state()
602
+ ], NrInputElement.prototype, "hasAddonAfter", void 0);
603
+ __decorate([
604
+ state()
605
+ ], NrInputElement.prototype, "focused", void 0);
312
606
  __decorate([
313
607
  query('#input')
314
608
  ], NrInputElement.prototype, "input", void 0);