@nuralyui/input 0.0.9 → 0.0.10
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 +1277 -0
- package/input.component.d.ts +118 -26
- package/input.component.js +229 -228
- package/input.component.js.map +1 -1
- package/input.style.js +154 -0
- package/input.style.js.map +1 -1
- package/input.style.variable.js +19 -0
- package/input.style.variable.js.map +1 -1
- package/input.types.d.ts +90 -1
- package/input.types.js +73 -0
- package/input.types.js.map +1 -1
- package/package.json +16 -2
- package/demo/input-demo.d.ts +0 -19
- package/demo/input-demo.d.ts.map +0 -1
- package/demo/input-demo.js +0 -339
- package/demo/input-demo.js.map +0 -1
- package/index.d.ts.map +0 -1
- package/input.component.d.ts.map +0 -1
- package/input.style.d.ts.map +0 -1
- package/input.style.variable.d.ts.map +0 -1
- package/input.types.d.ts.map +0 -1
- package/mixins/focus-mixin.d.ts +0 -60
- package/mixins/focus-mixin.d.ts.map +0 -1
- package/mixins/focus-mixin.js +0 -65
- package/mixins/focus-mixin.js.map +0 -1
- package/mixins/index.d.ts +0 -9
- package/mixins/index.d.ts.map +0 -1
- package/mixins/index.js +0 -9
- package/mixins/index.js.map +0 -1
- package/mixins/number-mixin.d.ts +0 -51
- package/mixins/number-mixin.d.ts.map +0 -1
- package/mixins/number-mixin.js +0 -131
- package/mixins/number-mixin.js.map +0 -1
- package/mixins/selection-mixin.d.ts +0 -57
- package/mixins/selection-mixin.d.ts.map +0 -1
- package/mixins/selection-mixin.js +0 -80
- package/mixins/selection-mixin.js.map +0 -1
- package/react.d.ts.map +0 -1
- package/test/hy-input_test.d.ts +0 -2
- package/test/hy-input_test.d.ts.map +0 -1
- package/test/hy-input_test.js +0 -159
- package/test/hy-input_test.js.map +0 -1
- package/utils/index.d.ts +0 -8
- package/utils/index.d.ts.map +0 -1
- package/utils/index.js +0 -8
- package/utils/index.js.map +0 -1
- package/utils/input-renderers.d.ts +0 -54
- package/utils/input-renderers.d.ts.map +0 -1
- package/utils/input-renderers.js +0 -174
- package/utils/input-renderers.js.map +0 -1
- package/utils/input-validation.utils.d.ts +0 -26
- package/utils/input-validation.utils.d.ts.map +0 -1
- package/utils/input-validation.utils.js +0 -105
- package/utils/input-validation.utils.js.map +0 -1
- package/validation.d.ts.map +0 -1
package/input.component.js
CHANGED
|
@@ -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
26
|
import { NuralyUIBaseMixin } from '../../shared/base-mixin.js';
|
|
26
27
|
import { InputValidationUtils, InputRenderUtils } from './utils/index.js';
|
|
27
28
|
import { SelectionMixin, FocusMixin, NumberMixin } from './mixins/index.js';
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
98
|
+
* Handle validation events from the controller
|
|
59
99
|
*/
|
|
60
|
-
this.
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
236
|
+
this.eventController.handleIncrement();
|
|
347
237
|
}
|
|
348
238
|
_decrement() {
|
|
349
|
-
this.
|
|
239
|
+
this.eventController.handleDecrement();
|
|
350
240
|
}
|
|
351
241
|
_togglePasswordIcon() {
|
|
352
|
-
|
|
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
|
-
|
|
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
|
|
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=${
|
|
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);
|