@nuralyui/input 0.0.9 → 0.0.11

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.
Files changed (56) hide show
  1. package/bundle.js +1348 -0
  2. package/input.component.d.ts +118 -26
  3. package/input.component.js +230 -229
  4. package/input.component.js.map +1 -1
  5. package/input.style.js +433 -128
  6. package/input.style.js.map +1 -1
  7. package/input.types.d.ts +90 -1
  8. package/input.types.js +73 -0
  9. package/input.types.js.map +1 -1
  10. package/package.json +16 -2
  11. package/demo/input-demo.d.ts +0 -19
  12. package/demo/input-demo.d.ts.map +0 -1
  13. package/demo/input-demo.js +0 -339
  14. package/demo/input-demo.js.map +0 -1
  15. package/index.d.ts.map +0 -1
  16. package/input.component.d.ts.map +0 -1
  17. package/input.style.d.ts.map +0 -1
  18. package/input.style.variable.d.ts +0 -2
  19. package/input.style.variable.d.ts.map +0 -1
  20. package/input.style.variable.js +0 -174
  21. package/input.style.variable.js.map +0 -1
  22. package/input.types.d.ts.map +0 -1
  23. package/mixins/focus-mixin.d.ts +0 -60
  24. package/mixins/focus-mixin.d.ts.map +0 -1
  25. package/mixins/focus-mixin.js +0 -65
  26. package/mixins/focus-mixin.js.map +0 -1
  27. package/mixins/index.d.ts +0 -9
  28. package/mixins/index.d.ts.map +0 -1
  29. package/mixins/index.js +0 -9
  30. package/mixins/index.js.map +0 -1
  31. package/mixins/number-mixin.d.ts +0 -51
  32. package/mixins/number-mixin.d.ts.map +0 -1
  33. package/mixins/number-mixin.js +0 -131
  34. package/mixins/number-mixin.js.map +0 -1
  35. package/mixins/selection-mixin.d.ts +0 -57
  36. package/mixins/selection-mixin.d.ts.map +0 -1
  37. package/mixins/selection-mixin.js +0 -80
  38. package/mixins/selection-mixin.js.map +0 -1
  39. package/react.d.ts.map +0 -1
  40. package/test/hy-input_test.d.ts +0 -2
  41. package/test/hy-input_test.d.ts.map +0 -1
  42. package/test/hy-input_test.js +0 -159
  43. package/test/hy-input_test.js.map +0 -1
  44. package/utils/index.d.ts +0 -8
  45. package/utils/index.d.ts.map +0 -1
  46. package/utils/index.js +0 -8
  47. package/utils/index.js.map +0 -1
  48. package/utils/input-renderers.d.ts +0 -54
  49. package/utils/input-renderers.d.ts.map +0 -1
  50. package/utils/input-renderers.js +0 -174
  51. package/utils/input-renderers.js.map +0 -1
  52. package/utils/input-validation.utils.d.ts +0 -26
  53. package/utils/input-validation.utils.d.ts.map +0 -1
  54. package/utils/input-validation.utils.js +0 -105
  55. package/utils/input-validation.utils.js.map +0 -1
  56. package/validation.d.ts.map +0 -1
@@ -21,54 +21,120 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
21
21
  import { LitElement, html } from 'lit';
22
22
  import { customElement, property, state } from 'lit/decorators.js';
23
23
  import { styles } from './input.style.js';
24
+ import '../icon/icon.component.js';
24
25
  import { EMPTY_STRING } from './input.types.js';
25
- import { NuralyUIBaseMixin } from '../../shared/base-mixin.js';
26
+ import { NuralyUIBaseMixin } from '@nuralyui/common/mixins';
26
27
  import { InputValidationUtils, InputRenderUtils } from './utils/index.js';
27
28
  import { SelectionMixin, FocusMixin, NumberMixin } from './mixins/index.js';
28
- const InputBaseMixin = NumberMixin(FocusMixin(SelectionMixin(NuralyUIBaseMixin(LitElement))));
29
- let NrInputElement = class NrInputElement extends InputBaseMixin {
29
+ import { InputValidationController, InputEventController } from './controllers/index.js';
30
+ /**
31
+ * Versatile input component with validation, multiple types, and interactive features.
32
+ *
33
+ * @example
34
+ * ```html
35
+ * <nr-input type="text" placeholder="Enter name"></nr-input>
36
+ * <nr-input type="password"></nr-input>
37
+ * <nr-input type="number" min="0" max="100"></nr-input>
38
+ * ```
39
+ *
40
+ * @fires nr-input - Value changes
41
+ * @fires nr-focus - Input focused
42
+ * @fires nr-blur - Input blurred
43
+ * @fires nr-enter - Enter key pressed
44
+ * @fires nr-clear - Clear button clicked
45
+ *
46
+ * @slot label - Input label
47
+ * @slot helper-text - Helper text
48
+ * @slot addon-before - Content before input
49
+ * @slot addon-after - Content after input
50
+ */
51
+ let NrInputElement = class NrInputElement extends NumberMixin(FocusMixin(SelectionMixin(NuralyUIBaseMixin(LitElement)))) {
30
52
  constructor() {
31
53
  super(...arguments);
32
- // ========================================
33
- // PROPERTIES
34
- // ========================================
54
+ this.validationController = new InputValidationController(this);
55
+ this.eventController = new InputEventController(this);
56
+ /** Disables the input */
35
57
  this.disabled = false;
58
+ /** Makes the input read-only */
36
59
  this.readonly = false;
60
+ /** Visual state (default, success, warning, error) */
37
61
  this.state = "default" /* INPUT_STATE.Default */;
62
+ /** Current input value */
38
63
  this.value = EMPTY_STRING;
64
+ /** Input size (small, medium, large) */
39
65
  this.size = "medium" /* INPUT_SIZE.Medium */;
66
+ /** Visual variant (outlined, underlined, filled) */
40
67
  this.variant = "underlined" /* INPUT_VARIANT.Underlined */;
68
+ /** Input type (text, password, number, email, etc.) */
41
69
  this.type = "text" /* INPUT_TYPE.TEXT */;
70
+ /** Placeholder text */
42
71
  this.placeholder = EMPTY_STRING;
72
+ /** HTML autocomplete attribute */
43
73
  this.autocomplete = 'off';
74
+ /** Shows copy button */
44
75
  this.withCopy = false;
76
+ /** Shows clear button */
45
77
  this.allowClear = false;
78
+ /** Shows character counter */
46
79
  this.showCount = false;
47
- // ========================================
48
- // STATE
49
- // ========================================
80
+ /** Array of validation rules */
81
+ this.rules = [];
82
+ /** Validate on change */
83
+ this.validateOnChangeInput = true;
84
+ /** Validate on blur */
85
+ this.validateOnBlurInput = true;
86
+ /** Show validation status icon */
87
+ this.hasFeedback = false;
88
+ /** Allow validation warnings */
89
+ this.allowWarnings = false;
90
+ /** Custom validation trigger */
91
+ this.validationTrigger = 'change';
50
92
  this.inputType = EMPTY_STRING;
51
93
  this.hasAddonBefore = false;
52
94
  this.hasAddonAfter = false;
53
95
  this.focused = false;
54
- // ========================================
55
- // LIFECYCLE METHODS
56
- // ========================================
96
+ this.requiredComponents = ['nr-icon'];
57
97
  /**
58
- * Required components that must be registered for this component to work properly
98
+ * Handle validation events from the controller
59
99
  */
60
- this.requiredComponents = ['nr-icon'];
100
+ this._handleValidationEvent = (event) => {
101
+ const customEvent = event;
102
+ const detail = customEvent.detail;
103
+ this.validationMessage = detail.validationMessage || '';
104
+ let newState = "default" /* INPUT_STATE.Default */;
105
+ if (detail.validationResult.hasError) {
106
+ newState = "error" /* INPUT_STATE.Error */;
107
+ }
108
+ else if (detail.validationResult.hasWarning && this.allowWarnings) {
109
+ newState = "warning" /* INPUT_STATE.Warning */;
110
+ }
111
+ else if (detail.validationResult.isValid && this.value && this.hasFeedback) {
112
+ newState = "success" /* INPUT_STATE.Success */;
113
+ }
114
+ if (this.state !== newState) {
115
+ this.state = newState;
116
+ }
117
+ this.requestUpdate();
118
+ };
119
+ this._handleKeyDown = (keyDownEvent) => {
120
+ this.eventController.handleKeyDown(keyDownEvent);
121
+ };
122
+ this._valueChange = (e) => {
123
+ this.eventController.handleValueChange(e);
124
+ };
125
+ this._focusEvent = (e) => {
126
+ this.eventController.handleFocus(e);
127
+ };
128
+ this._blurEvent = (e) => {
129
+ this.eventController.handleBlur(e);
130
+ };
131
+ this._handleIconKeydown = (keyDownEvent) => {
132
+ this.eventController.handleIconKeydown(keyDownEvent);
133
+ };
61
134
  }
62
- // Use manual query instead of @query decorator to avoid TypeScript mixin issues
63
135
  get _input() {
64
136
  return this.shadowRoot.querySelector('#input');
65
137
  }
66
- // ========================================
67
- // COMPUTED PROPERTIES
68
- // ========================================
69
- /**
70
- * Get the character count display text
71
- */
72
138
  get characterCountDisplay() {
73
139
  const currentLength = this.value.length;
74
140
  if (this.maxLength) {
@@ -76,52 +142,48 @@ let NrInputElement = class NrInputElement extends InputBaseMixin {
76
142
  }
77
143
  return `${currentLength}`;
78
144
  }
79
- /**
80
- * Check if character count is over the limit
81
- */
82
145
  get isOverCharacterLimit() {
83
146
  return this.maxLength ? this.value.length > this.maxLength : false;
84
147
  }
85
- // ========================================
86
- // COMPUTED PROPERTIES
87
- // ========================================
88
- /**
89
- * Get the input element
90
- */
91
148
  get input() {
92
149
  return this._input;
93
150
  }
94
- /**
95
- * Override inputElement getter from mixins to use our @query property
96
- */
97
151
  get inputElement() {
98
152
  return this._input;
99
153
  }
100
- /**
101
- * Check for required dependencies when component is connected to DOM
102
- */
103
154
  connectedCallback() {
104
155
  super.connectedCallback();
156
+ this.addEventListener('nr-validation', this._handleValidationEvent);
157
+ }
158
+ disconnectedCallback() {
159
+ var _a, _b;
160
+ super.disconnectedCallback();
161
+ this.removeEventListener('nr-validation', this._handleValidationEvent);
162
+ (_b = (_a = this.validationController).clearDebounceTimer) === null || _b === void 0 ? void 0 : _b.call(_a);
105
163
  }
106
164
  willUpdate(_changedProperties) {
107
165
  super.willUpdate(_changedProperties);
108
- // Initialize inputType when type changes or on first render
109
166
  if (_changedProperties.has('type') || !this.inputType) {
110
167
  this.inputType = this.type;
111
168
  }
112
- // Set default value for number inputs with min
113
169
  if (_changedProperties.has('type') || _changedProperties.has('min')) {
114
170
  if (this.type === "number" /* INPUT_TYPE.NUMBER */ && this.min && !this.value) {
115
171
  this.value = this.min;
116
172
  }
117
173
  }
118
- // Validate numeric properties when they change
119
174
  if (_changedProperties.has('type') ||
120
175
  _changedProperties.has('min') ||
121
176
  _changedProperties.has('max') ||
122
177
  _changedProperties.has('step')) {
123
178
  InputValidationUtils.validateNumericProperties(this.type, this.min, this.max, this.step);
124
179
  }
180
+ if (_changedProperties.has('type') ||
181
+ _changedProperties.has('required') ||
182
+ _changedProperties.has('maxLength') ||
183
+ _changedProperties.has('min') ||
184
+ _changedProperties.has('max')) {
185
+ this.validationController.setupValidationRules();
186
+ }
125
187
  }
126
188
  updated(_changedProperties) {
127
189
  if (_changedProperties.has('step') || _changedProperties.has('min') || _changedProperties.has('max') || _changedProperties.has('maxLength')) {
@@ -146,23 +208,12 @@ let NrInputElement = class NrInputElement extends InputBaseMixin {
146
208
  firstUpdated() {
147
209
  this._checkInitialSlotContent();
148
210
  }
149
- // ========================================
150
- // PRIVATE METHODS
151
- // ========================================
152
- /**
153
- * Check initial slot content on first render
154
- */
155
211
  _checkInitialSlotContent() {
156
- // Check for addon-before content
157
212
  const addonBeforeElements = this.querySelectorAll('[slot="addon-before"]');
158
213
  this.hasAddonBefore = addonBeforeElements.length > 0;
159
- // Check for addon-after content
160
214
  const addonAfterElements = this.querySelectorAll('[slot="addon-after"]');
161
215
  this.hasAddonAfter = addonAfterElements.length > 0;
162
216
  }
163
- /**
164
- * Handle slot changes to determine addon visibility
165
- */
166
217
  _handleSlotChange(e) {
167
218
  const slot = e.target;
168
219
  const slotName = slot.name;
@@ -173,188 +224,22 @@ let NrInputElement = class NrInputElement extends InputBaseMixin {
173
224
  this.hasAddonAfter = slot.assignedElements().length > 0;
174
225
  }
175
226
  }
176
- // ========================================
177
- // FOCUS MANAGEMENT METHODS
178
- // ========================================
179
- // Focus methods are provided by InputMixin
180
- // ========================================
181
- // EVENT HANDLING METHODS
182
- // ========================================
183
- // Event handling methods moved to EventHandlerMixin
184
- _handleKeyDown(keyDownEvent) {
185
- // Prevent all key input when readonly - use mixin utility
186
- if (this.readonly && !this.isReadonlyKeyAllowed(keyDownEvent)) {
187
- keyDownEvent.preventDefault();
188
- return;
189
- }
190
- // Handle Enter key
191
- if (keyDownEvent.key === 'Enter') {
192
- this.dispatchCustomEvent('nr-enter', {
193
- target: keyDownEvent.target,
194
- value: this.value,
195
- originalEvent: keyDownEvent
196
- });
197
- return;
198
- }
199
- // Prevent non-numeric input for number type
200
- if (this.type === "number" /* INPUT_TYPE.NUMBER */) {
201
- InputValidationUtils.preventNonNumericInput(keyDownEvent, this.min);
202
- if (keyDownEvent.defaultPrevented) {
203
- this.dispatchCustomEvent('nr-invalid-key', {
204
- key: keyDownEvent.key,
205
- target: keyDownEvent.target,
206
- value: this.value,
207
- originalEvent: keyDownEvent
208
- });
209
- }
210
- }
211
- }
212
- _valueChange(e) {
213
- if (this.readonly) {
214
- e.preventDefault();
215
- return;
216
- }
217
- const target = e.target;
218
- const newValue = target.value;
219
- // Check character limit
220
- if (this.maxLength && newValue.length > this.maxLength) {
221
- this.dispatchCustomEvent('nr-character-limit-exceeded', {
222
- value: newValue,
223
- target: target,
224
- limit: this.maxLength,
225
- originalEvent: e
226
- });
227
- // Note: HTML maxlength attribute usually prevents this, but we dispatch event for awareness
228
- }
229
- if (this.type === "number" /* INPUT_TYPE.NUMBER */ && newValue) {
230
- const validation = InputValidationUtils.validateNumericValue(newValue, this.min, this.max);
231
- if (!validation.isValid) {
232
- console.warn(validation.warnings[0]);
233
- this.dispatchValidationEvent('nr-validation-error', {
234
- value: newValue,
235
- target: target,
236
- error: validation.warnings[0],
237
- originalEvent: e,
238
- isValid: false
239
- });
240
- return;
241
- }
242
- validation.warnings.forEach(warning => console.warn(warning));
243
- }
244
- this.value = newValue;
245
- this.dispatchInputEvent('nr-input', {
246
- value: this.value,
247
- target: target,
248
- originalEvent: e
249
- });
250
- }
251
- _focusEvent(e) {
252
- var _a;
253
- this.focused = true;
254
- // Handle cursor restoration if requested
255
- const input = e.target;
256
- if (input.dataset.restoreCursor) {
257
- const position = parseInt(input.dataset.restoreCursor, 10);
258
- this.setCursorPosition(position);
259
- delete input.dataset.restoreCursor;
260
- }
261
- const focusDetail = {
262
- focused: true,
263
- cursorPosition: (_a = this.getCursorPosition()) !== null && _a !== void 0 ? _a : undefined,
264
- selectedText: this.getSelectedText()
265
- };
266
- this.dispatchFocusEvent('nr-focus', Object.assign({ target: e.target, value: this.value }, focusDetail));
267
- this.dispatchFocusEvent('nr-focus-change', focusDetail);
268
- }
269
- _blurEvent(e) {
270
- var _a;
271
- this.focused = false;
272
- const focusDetail = {
273
- focused: false,
274
- cursorPosition: (_a = this.getCursorPosition()) !== null && _a !== void 0 ? _a : undefined,
275
- selectedText: this.getSelectedText()
276
- };
277
- this.dispatchFocusEvent('nr-blur', Object.assign({ target: e.target, value: this.value }, focusDetail));
278
- this.dispatchFocusEvent('nr-focus-change', focusDetail);
279
- }
280
- _handleIconKeydown(keyDownEvent) {
281
- if (this.isActivationKey(keyDownEvent)) {
282
- keyDownEvent.preventDefault();
283
- const target = keyDownEvent.target;
284
- if (target.id === 'copy-icon') {
285
- this._onCopy();
286
- }
287
- else if (target.id === 'clear-icon') {
288
- this._onClear();
289
- }
290
- else if (target.id === 'password-icon') {
291
- this._togglePasswordIcon();
292
- }
293
- else if (target.closest('#number-icons')) {
294
- if (target.getAttribute('name') === 'plus') {
295
- this._increment();
296
- }
297
- else if (target.getAttribute('name') === 'minus') {
298
- this._decrement();
299
- }
300
- }
301
- }
302
- }
303
227
  _onCopy() {
304
228
  return __awaiter(this, void 0, void 0, function* () {
305
- try {
306
- const input = this.shadowRoot.getElementById('input');
307
- input.select();
308
- yield navigator.clipboard.writeText(input.value);
309
- this.dispatchActionEvent('nr-copy-success', {
310
- value: input.value,
311
- action: 'copy'
312
- });
313
- }
314
- catch (error) {
315
- this.dispatchCustomEvent('nr-copy-error', { error });
316
- }
229
+ yield this.eventController.handleCopy();
317
230
  });
318
231
  }
319
232
  _onClear() {
320
- if (this.disabled || this.readonly) {
321
- return;
322
- }
323
- const previousValue = this.value;
324
- this.value = EMPTY_STRING;
325
- // Update the input element value
326
- if (this.input) {
327
- this.input.value = EMPTY_STRING;
328
- }
329
- this.dispatchActionEvent('nr-clear', {
330
- previousValue,
331
- newValue: this.value,
332
- target: this.input,
333
- action: 'clear'
334
- });
335
- // Also dispatch input event for consistency
336
- this.dispatchInputEvent('nr-input', {
337
- value: this.value,
338
- target: this.input,
339
- action: 'clear'
340
- });
233
+ this.eventController.handleClear();
341
234
  }
342
- // ========================================
343
- // OPERATION METHODS
344
- // ========================================
345
235
  _increment() {
346
- this.increment();
236
+ this.eventController.handleIncrement();
347
237
  }
348
238
  _decrement() {
349
- this.decrement();
239
+ this.eventController.handleDecrement();
350
240
  }
351
241
  _togglePasswordIcon() {
352
- if (this.inputType === "password" /* INPUT_TYPE.PASSWORD */) {
353
- this.inputType = "text" /* INPUT_TYPE.TEXT */;
354
- }
355
- else if (this.inputType === "text" /* INPUT_TYPE.TEXT */ && this.type === "password" /* INPUT_TYPE.PASSWORD */) {
356
- this.inputType = "password" /* INPUT_TYPE.PASSWORD */;
357
- }
242
+ this.eventController.handleTogglePassword();
358
243
  }
359
244
  _getAriaDescribedBy() {
360
245
  var _a;
@@ -365,25 +250,107 @@ let NrInputElement = class NrInputElement extends InputBaseMixin {
365
250
  }
366
251
  return describedBy.join(' ') || '';
367
252
  }
368
- // ========================================
369
- // RENDER METHODS
370
- // ========================================
253
+ /**
254
+ * Setup default validation rules based on input properties
255
+ */
256
+ /**
257
+ * Override the form mixin's validateValue method with controller logic
258
+ */
259
+ validateValue(_value) {
260
+ return this.validationController.validate();
261
+ }
262
+ /**
263
+ * Add validation rule dynamically
264
+ */
265
+ addRule(rule) {
266
+ this.validationController.addRule(rule);
267
+ }
268
+ /**
269
+ * Remove validation rule
270
+ */
271
+ removeRule(predicate) {
272
+ this.validationController.removeRule(predicate);
273
+ }
274
+ /**
275
+ * Clear all validation rules
276
+ */
277
+ clearRules() {
278
+ this.validationController.clearRules();
279
+ }
280
+ /**
281
+ * Get current validation status
282
+ */
283
+ getValidationStatus() {
284
+ return this.validationController.getValidationStatus();
285
+ }
286
+ /**
287
+ * Trigger validation manually
288
+ */
289
+ validateInput() {
290
+ return __awaiter(this, void 0, void 0, function* () {
291
+ const result = this.validationController.validate();
292
+ if (this.validationController.isValidating) {
293
+ return new Promise((resolve) => {
294
+ const checkValidation = () => {
295
+ if (!this.validationController.isValidating) {
296
+ resolve(this.validationController.isValid);
297
+ }
298
+ else {
299
+ setTimeout(checkValidation, 50);
300
+ }
301
+ };
302
+ checkValidation();
303
+ });
304
+ }
305
+ return result;
306
+ });
307
+ }
308
+ /**
309
+ * Set validation state externally (for form integration)
310
+ */
311
+ setValidationStatus(result) {
312
+ this.validationController.setValidationStatus(result);
313
+ }
314
+ /**
315
+ * Get validation classes for CSS styling
316
+ */
317
+ getValidationClasses() {
318
+ return this.validationController.getValidationClasses();
319
+ }
320
+ /**
321
+ * Render validation feedback icon
322
+ */
323
+ renderValidationIcon() {
324
+ return this.validationController.renderValidationIcon();
325
+ }
326
+ /**
327
+ * Render validation message
328
+ */
329
+ renderValidationMessage() {
330
+ return this.validationController.renderValidationMessage();
331
+ }
371
332
  render() {
333
+ const validationClasses = this.getValidationClasses();
334
+ const validationRenderState = this.validationController.getValidationRenderState();
372
335
  return html `
373
336
  <slot name="label"></slot>
374
- <div class="input-wrapper" data-theme="${this.currentTheme}">
337
+ <div class="input-wrapper ${Object.entries(validationClasses).filter(([, value]) => value).map(([key]) => key).join(' ')}"
338
+ part="input-wrapper"
339
+ data-theme="${this.currentTheme}"
340
+ ?data-validating="${validationRenderState.isValidating}">
375
341
  ${InputRenderUtils.renderAddonBefore(this.hasAddonBefore, (e) => this._handleSlotChange(e))}
376
- <div data-size=${this.size} id="input-container">
342
+ <div data-size=${this.size} id="input-container" part="input-container">
377
343
  ${InputRenderUtils.renderPrefix()}
378
344
  <input
379
345
  id="input"
346
+ part="input"
380
347
  .disabled=${this.disabled}
381
348
  .readOnly=${this.readonly}
382
349
  .value=${this.value}
383
350
  .placeholder=${this.placeholder}
384
351
  .type="${this.inputType}"
385
352
  .autocomplete=${this.autocomplete}
386
- aria-invalid=${this.state === "error" /* INPUT_STATE.Error */ ? 'true' : 'false'}
353
+ aria-invalid=${validationRenderState.validationResult.hasError ? 'true' : 'false'}
387
354
  aria-describedby=${this._getAriaDescribedBy()}
388
355
  @input=${this._valueChange}
389
356
  @focus=${this._focusEvent}
@@ -393,7 +360,7 @@ let NrInputElement = class NrInputElement extends InputBaseMixin {
393
360
  ${InputRenderUtils.renderSuffix()}
394
361
  ${InputRenderUtils.renderCopyIcon(this.withCopy, this.disabled, this.readonly, () => this._onCopy(), (e) => this._handleIconKeydown(e))}
395
362
  ${InputRenderUtils.renderClearIcon(this.allowClear, this.value, this.disabled, this.readonly, () => this._onClear(), (e) => this._handleIconKeydown(e))}
396
- ${InputRenderUtils.renderStateIcon(this.state)}
363
+ ${validationRenderState.hasValidationFeedback ? this.renderValidationIcon() : InputRenderUtils.renderStateIcon(this.state)}
397
364
  ${InputRenderUtils.renderCalendarIcon(this.state, this.type)}
398
365
  ${InputRenderUtils.renderPasswordIcon(this.type, this.inputType, this.disabled, this.readonly, () => this._togglePasswordIcon(), (e) => this._handleIconKeydown(e))}
399
366
  ${InputRenderUtils.renderNumberIcons(this.type, this.state, this.disabled, this.readonly, () => this._increment(), () => this._decrement(), (e) => this._handleIconKeydown(e))}
@@ -401,8 +368,9 @@ let NrInputElement = class NrInputElement extends InputBaseMixin {
401
368
  ${InputRenderUtils.renderAddonAfter(this.hasAddonAfter, (e) => this._handleSlotChange(e))}
402
369
  </div>
403
370
  <slot name="helper-text"></slot>
371
+ ${this.renderValidationMessage()}
404
372
  ${this.showCount ? html `
405
- <div class="character-count" ?data-over-limit=${this.isOverCharacterLimit}>
373
+ <div class="character-count" part="character-count" ?data-over-limit=${this.isOverCharacterLimit}>
406
374
  ${this.characterCountDisplay}
407
375
  </div>
408
376
  ` : ''}
@@ -446,6 +414,12 @@ __decorate([
446
414
  __decorate([
447
415
  property({ type: String })
448
416
  ], NrInputElement.prototype, "autocomplete", void 0);
417
+ __decorate([
418
+ property({ type: String })
419
+ ], NrInputElement.prototype, "name", void 0);
420
+ __decorate([
421
+ property({ type: Boolean })
422
+ ], NrInputElement.prototype, "required", void 0);
449
423
  __decorate([
450
424
  property({ type: Boolean, reflect: true })
451
425
  ], NrInputElement.prototype, "withCopy", void 0);
@@ -458,6 +432,33 @@ __decorate([
458
432
  __decorate([
459
433
  property({ type: Number })
460
434
  ], NrInputElement.prototype, "maxLength", void 0);
435
+ __decorate([
436
+ property({ type: Array })
437
+ ], NrInputElement.prototype, "rules", void 0);
438
+ __decorate([
439
+ property({ type: Boolean, attribute: 'validate-on-change' })
440
+ ], NrInputElement.prototype, "validateOnChangeInput", void 0);
441
+ __decorate([
442
+ property({ type: Boolean, attribute: 'validate-on-blur' })
443
+ ], NrInputElement.prototype, "validateOnBlurInput", void 0);
444
+ __decorate([
445
+ property({ type: Boolean, attribute: 'has-feedback' })
446
+ ], NrInputElement.prototype, "hasFeedback", void 0);
447
+ __decorate([
448
+ property({ type: Boolean, attribute: 'allow-warnings' })
449
+ ], NrInputElement.prototype, "allowWarnings", void 0);
450
+ __decorate([
451
+ property({ type: String, attribute: 'validation-trigger' })
452
+ ], NrInputElement.prototype, "validationTrigger", void 0);
453
+ __decorate([
454
+ property({ type: Number, attribute: 'validation-debounce' })
455
+ ], NrInputElement.prototype, "validationDebounce", void 0);
456
+ __decorate([
457
+ property({ type: String })
458
+ ], NrInputElement.prototype, "label", void 0);
459
+ __decorate([
460
+ state()
461
+ ], NrInputElement.prototype, "validationMessage", void 0);
461
462
  __decorate([
462
463
  state()
463
464
  ], NrInputElement.prototype, "inputType", void 0);