ezfw-core 1.0.16 → 1.0.17
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/components/EzBaseComponent.ts +70 -2
- package/components/checkbox/EzCheckbox.ts +2 -1
- package/components/input/EzInput.ts +168 -0
- package/core/ez.ts +4 -0
- package/core/loader.ts +5 -0
- package/core/renderer.ts +23 -2
- package/core/state.ts +23 -0
- package/modules.ts +2 -2
- package/package.json +1 -1
- package/components/EzInput.ts +0 -132
- /package/components/{EzInput.module.scss → input/EzInput.module.scss} +0 -0
|
@@ -14,6 +14,7 @@ declare const ez: {
|
|
|
14
14
|
getDeepValue(obj: unknown, path: string[]): unknown;
|
|
15
15
|
setDeepValue(obj: unknown, path: string[], value: unknown): void;
|
|
16
16
|
_createElement(config: EzComponentConfig): Promise<HTMLElement>;
|
|
17
|
+
hasStyles(name: string): boolean;
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
interface EzController {
|
|
@@ -41,6 +42,8 @@ interface BindConfig {
|
|
|
41
42
|
visible?: string;
|
|
42
43
|
cls?: string | (() => string);
|
|
43
44
|
html?: string | (() => string);
|
|
45
|
+
style?: Record<string, string> | (() => Record<string, string>);
|
|
46
|
+
text?: string | (() => string);
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
export interface EzComponentConfig {
|
|
@@ -218,6 +221,8 @@ export class EzBaseComponent {
|
|
|
218
221
|
this._applyDataBind(el, bind, ctrl);
|
|
219
222
|
this._applyVisibleBind(el, bind);
|
|
220
223
|
this._applyClsBind(el, bind);
|
|
224
|
+
this._applyStyleBind(el, bind);
|
|
225
|
+
this._applyTextBind(el, bind);
|
|
221
226
|
this._applyHtmlBind(el, bind);
|
|
222
227
|
}
|
|
223
228
|
}
|
|
@@ -290,7 +295,13 @@ export class EzBaseComponent {
|
|
|
290
295
|
const stop = effect(() => {
|
|
291
296
|
(async () => {
|
|
292
297
|
const itemFn = this.config.itemRender;
|
|
293
|
-
|
|
298
|
+
const controller = ez.getController(activeCtrl!);
|
|
299
|
+
let data = ez.getDeepValue(controller?.state, props!) as unknown[] | unknown;
|
|
300
|
+
|
|
301
|
+
// If not found in state, check computed properties
|
|
302
|
+
if (data == null && props?.length === 1 && controller?._computed?.[props[0]]) {
|
|
303
|
+
data = controller._computed[props[0]].value;
|
|
304
|
+
}
|
|
294
305
|
|
|
295
306
|
if (data == null) {
|
|
296
307
|
return;
|
|
@@ -327,7 +338,10 @@ export class EzBaseComponent {
|
|
|
327
338
|
if (!childCfg.controller) {
|
|
328
339
|
childCfg.controller = activeCtrl;
|
|
329
340
|
}
|
|
330
|
-
if
|
|
341
|
+
// Only inherit CSS if child doesn't have its own CSS module
|
|
342
|
+
const childHasOwnCss = childCfg.css ||
|
|
343
|
+
(childCfg.eztype && ez.hasStyles(childCfg.eztype));
|
|
344
|
+
if (!childHasOwnCss && this.config.css) {
|
|
331
345
|
childCfg.css = this.config.css;
|
|
332
346
|
childCfg._styleModule = this.config._styleModule;
|
|
333
347
|
}
|
|
@@ -472,6 +486,60 @@ export class EzBaseComponent {
|
|
|
472
486
|
this._effects!.push(stop);
|
|
473
487
|
}
|
|
474
488
|
|
|
489
|
+
private _applyStyleBind(el: HTMLElement, bind: BindConfig): void {
|
|
490
|
+
if (!bind.style) return;
|
|
491
|
+
|
|
492
|
+
let prevStyles: Record<string, string> = {};
|
|
493
|
+
|
|
494
|
+
const stop = effect(() => {
|
|
495
|
+
let next = bind.style;
|
|
496
|
+
|
|
497
|
+
if (typeof next === 'function') {
|
|
498
|
+
next = next();
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (typeof next !== 'object' || next === null) {
|
|
502
|
+
next = {};
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Remove previous styles that are not in the new object
|
|
506
|
+
for (const key of Object.keys(prevStyles)) {
|
|
507
|
+
if (!(key in next)) {
|
|
508
|
+
(el.style as unknown as Record<string, string>)[key] = '';
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// Apply new styles (supports camelCase like 'backgroundColor')
|
|
513
|
+
for (const [key, value] of Object.entries(next)) {
|
|
514
|
+
(el.style as unknown as Record<string, string>)[key] = value;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
prevStyles = { ...next };
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
this._effects!.push(stop);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
private _applyTextBind(el: HTMLElement, bind: BindConfig): void {
|
|
524
|
+
if (!bind.text) return;
|
|
525
|
+
|
|
526
|
+
const stop = effect(() => {
|
|
527
|
+
let next = bind.text;
|
|
528
|
+
|
|
529
|
+
if (typeof next === 'function') {
|
|
530
|
+
next = next();
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (typeof next !== 'string') {
|
|
534
|
+
next = String(next ?? '');
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
el.textContent = next;
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
this._effects!.push(stop);
|
|
541
|
+
}
|
|
542
|
+
|
|
475
543
|
applyStyles(el: HTMLElement): void {
|
|
476
544
|
if (this.config.style && typeof this.config.style === 'object') {
|
|
477
545
|
Object.assign(el.style, this.config.style);
|
|
@@ -8,6 +8,7 @@ export interface EzCheckboxConfig extends EzBaseComponentConfig {
|
|
|
8
8
|
size?: 'sm' | 'lg';
|
|
9
9
|
disabled?: boolean;
|
|
10
10
|
value?: boolean;
|
|
11
|
+
checked?: boolean;
|
|
11
12
|
name?: string;
|
|
12
13
|
formData?: string;
|
|
13
14
|
label?: string;
|
|
@@ -33,7 +34,7 @@ export class EzCheckbox extends EzBaseComponent {
|
|
|
33
34
|
const input = document.createElement('input');
|
|
34
35
|
input.type = 'checkbox';
|
|
35
36
|
input.className = cls('input');
|
|
36
|
-
input.checked = !!cfg.value;
|
|
37
|
+
input.checked = !!(cfg.checked ?? cfg.value);
|
|
37
38
|
|
|
38
39
|
if (cfg.disabled) input.disabled = true;
|
|
39
40
|
if (cfg.name) input.name = cfg.name;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import styles from './EzInput.module.scss';
|
|
2
|
+
import { cx } from '../../utils/cssModules.js';
|
|
3
|
+
import { EzBaseComponent, EzBaseComponentConfig } from '../EzBaseComponent.js';
|
|
4
|
+
|
|
5
|
+
const cls = cx(styles);
|
|
6
|
+
|
|
7
|
+
declare const ez: {
|
|
8
|
+
_createElement(config: unknown): Promise<HTMLElement>;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export interface EzInputConfig extends EzBaseComponentConfig {
|
|
12
|
+
size?: 'sm' | 'lg';
|
|
13
|
+
variant?: 'filled' | 'underline';
|
|
14
|
+
block?: boolean;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
readonly?: boolean;
|
|
17
|
+
icon?: string;
|
|
18
|
+
inputType?: string;
|
|
19
|
+
placeholder?: string;
|
|
20
|
+
value?: string | number;
|
|
21
|
+
name?: string;
|
|
22
|
+
formData?: string;
|
|
23
|
+
onKeydown?: ((e: KeyboardEvent) => void) | string;
|
|
24
|
+
onKeyup?: ((e: KeyboardEvent) => void) | string;
|
|
25
|
+
onEnter?: ((value: string) => void) | string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class EzInput extends EzBaseComponent {
|
|
29
|
+
declare config: EzInputConfig;
|
|
30
|
+
declare el: HTMLDivElement;
|
|
31
|
+
|
|
32
|
+
private _input: HTMLInputElement | null = null;
|
|
33
|
+
private _error: HTMLDivElement | null = null;
|
|
34
|
+
|
|
35
|
+
async render(): Promise<HTMLDivElement> {
|
|
36
|
+
const cfg = this.config;
|
|
37
|
+
|
|
38
|
+
// Build input config
|
|
39
|
+
const inputConfig: Record<string, unknown> = {
|
|
40
|
+
eztype: 'input',
|
|
41
|
+
cls: cls('input'),
|
|
42
|
+
type: cfg.inputType || 'text',
|
|
43
|
+
placeholder: cfg.placeholder || '',
|
|
44
|
+
disabled: cfg.disabled,
|
|
45
|
+
readonly: cfg.readonly,
|
|
46
|
+
value: cfg.value !== undefined ? String(cfg.value) : undefined,
|
|
47
|
+
style: cfg.style
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Handle form binding
|
|
51
|
+
if (cfg.name && cfg.formData) {
|
|
52
|
+
this.config.bind = `${cfg.formData}.${cfg.name}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (cfg.name) {
|
|
56
|
+
inputConfig['data-ez-field'] = cfg.name;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Handle keyboard events
|
|
60
|
+
if (cfg.onKeydown && typeof cfg.onKeydown === 'function') {
|
|
61
|
+
inputConfig.onKeyDown = cfg.onKeydown;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (cfg.onKeyup && typeof cfg.onKeyup === 'function') {
|
|
65
|
+
inputConfig.onKeyUp = cfg.onKeyup;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Build items array
|
|
69
|
+
const rowItems: unknown[] = [];
|
|
70
|
+
|
|
71
|
+
if (cfg.icon) {
|
|
72
|
+
rowItems.push({
|
|
73
|
+
eztype: 'i',
|
|
74
|
+
cls: [cls('icon'), `fa fa-${cfg.icon}`].join(' ')
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
rowItems.push(inputConfig);
|
|
79
|
+
|
|
80
|
+
// Create wrapper using ez._createElement
|
|
81
|
+
const wrapper = await ez._createElement({
|
|
82
|
+
eztype: 'div',
|
|
83
|
+
cls: this._buildClasses(),
|
|
84
|
+
items: [
|
|
85
|
+
{
|
|
86
|
+
eztype: 'div',
|
|
87
|
+
cls: cls('fieldError')
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
eztype: 'div',
|
|
91
|
+
cls: cls('inputRow'),
|
|
92
|
+
items: rowItems
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
}) as HTMLDivElement;
|
|
96
|
+
|
|
97
|
+
// Get references
|
|
98
|
+
this._error = wrapper.querySelector(`.${cls('fieldError')}`) as HTMLDivElement;
|
|
99
|
+
this._input = wrapper.querySelector('input') as HTMLInputElement;
|
|
100
|
+
|
|
101
|
+
// Apply bindings and onChange handler
|
|
102
|
+
if (this._input) {
|
|
103
|
+
this.applyCommonBindings(this._input);
|
|
104
|
+
|
|
105
|
+
const onChange = this._createOnChangeHandler();
|
|
106
|
+
if (onChange) {
|
|
107
|
+
this._input.addEventListener('input', e => {
|
|
108
|
+
onChange((e.target as HTMLInputElement).value);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Handle onEnter
|
|
113
|
+
if (cfg.onEnter && typeof cfg.onEnter === 'function') {
|
|
114
|
+
const onEnterFn = cfg.onEnter;
|
|
115
|
+
this._input.addEventListener('keydown', e => {
|
|
116
|
+
if (e.key === 'Enter') {
|
|
117
|
+
onEnterFn(this._input!.value);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
this.el = wrapper;
|
|
124
|
+
return wrapper;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private _buildClasses(): string {
|
|
128
|
+
const cfg = this.config;
|
|
129
|
+
const classes: string[] = [cls('inputGroup')];
|
|
130
|
+
|
|
131
|
+
if (cfg.size) classes.push(cls(cfg.size));
|
|
132
|
+
if (cfg.variant) classes.push(cls(cfg.variant));
|
|
133
|
+
if (cfg.block) classes.push(cls('block'));
|
|
134
|
+
if (cfg.disabled) classes.push(cls('disabled'));
|
|
135
|
+
|
|
136
|
+
return classes.join(' ');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
showError(message: string): void {
|
|
140
|
+
if (this.el && this._error) {
|
|
141
|
+
this.el.classList.add(cls('hasError'));
|
|
142
|
+
this._error.classList.add(cls('fieldErrorVisible'));
|
|
143
|
+
this._error.textContent = message;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
clearError(): void {
|
|
148
|
+
if (this.el && this._error) {
|
|
149
|
+
this.el.classList.remove(cls('hasError'));
|
|
150
|
+
this._error.classList.remove(cls('fieldErrorVisible'));
|
|
151
|
+
this._error.textContent = '';
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
getValue(): string {
|
|
156
|
+
return this._input?.value || '';
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
setValue(value: string): void {
|
|
160
|
+
if (this._input) {
|
|
161
|
+
this._input.value = value;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
focus(): void {
|
|
166
|
+
this._input?.focus();
|
|
167
|
+
}
|
|
168
|
+
}
|
package/core/ez.ts
CHANGED
|
@@ -346,6 +346,10 @@ const ez: EzFramework = {
|
|
|
346
346
|
});
|
|
347
347
|
},
|
|
348
348
|
|
|
349
|
+
getStylesSync(name: string): Record<string, string> | null {
|
|
350
|
+
return this._stylesCache[name] || null;
|
|
351
|
+
},
|
|
352
|
+
|
|
349
353
|
async resolveStyles(name: string): Promise<Record<string, string> | null> {
|
|
350
354
|
if (this._stylesCache[name]) {
|
|
351
355
|
return this._stylesCache[name];
|
package/core/loader.ts
CHANGED
|
@@ -273,6 +273,11 @@ export class EzLoader {
|
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
this.ez.registerComponent(eztype, exported);
|
|
276
|
+
|
|
277
|
+
// Pre-resolve CSS module if it exists (will be cached for sync access later)
|
|
278
|
+
if (this.ez.hasStyles(eztype)) {
|
|
279
|
+
await this.ez.resolveStyles(eztype);
|
|
280
|
+
}
|
|
276
281
|
}
|
|
277
282
|
|
|
278
283
|
async resolveController(name: string | object | null): Promise<unknown> {
|
package/core/renderer.ts
CHANGED
|
@@ -634,10 +634,12 @@ export class EzRenderer {
|
|
|
634
634
|
|
|
635
635
|
let merged: ComponentConfig = { ...Registered as ComponentConfig, ...rest };
|
|
636
636
|
|
|
637
|
+
// Auto-detect CSS module if component has one
|
|
637
638
|
if (!merged.css && this.ez.hasStyles(eztype)) {
|
|
638
639
|
merged.css = eztype;
|
|
639
640
|
}
|
|
640
641
|
|
|
642
|
+
// Load style module if css is set but module not loaded yet
|
|
641
643
|
if (merged.css && !merged._styleModule) {
|
|
642
644
|
merged._styleModule = (await this.ez.resolveStyles(merged.css)) || undefined;
|
|
643
645
|
}
|
|
@@ -663,8 +665,12 @@ export class EzRenderer {
|
|
|
663
665
|
if (typeof merged.template === 'function' && !targetIsClassComponent) {
|
|
664
666
|
const inheritedCss = merged.css;
|
|
665
667
|
const inheritedStyleModule = merged._styleModule;
|
|
668
|
+
const inheritedCls = merged.cls;
|
|
669
|
+
const inheritedLayout = merged.layout;
|
|
670
|
+
const inheritedEztype = merged.eztype;
|
|
671
|
+
const inheritedStyle = merged.style;
|
|
666
672
|
|
|
667
|
-
const tpl = merged.template(
|
|
673
|
+
const tpl = merged.template(merged, this.ez._controllers[activeController], controllerState);
|
|
668
674
|
if (!tpl || typeof tpl !== 'object') {
|
|
669
675
|
throw new EzError({
|
|
670
676
|
code: 'EZ_TEMPLATE_001',
|
|
@@ -681,6 +687,18 @@ export class EzRenderer {
|
|
|
681
687
|
merged.css = inheritedCss;
|
|
682
688
|
merged._styleModule = inheritedStyleModule;
|
|
683
689
|
}
|
|
690
|
+
if (inheritedCls && !merged.cls) {
|
|
691
|
+
merged.cls = inheritedCls;
|
|
692
|
+
}
|
|
693
|
+
if (inheritedLayout && !merged.layout) {
|
|
694
|
+
merged.layout = inheritedLayout;
|
|
695
|
+
}
|
|
696
|
+
if (inheritedEztype && !merged.eztype) {
|
|
697
|
+
merged.eztype = inheritedEztype;
|
|
698
|
+
}
|
|
699
|
+
if (inheritedStyle && !merged.style) {
|
|
700
|
+
merged.style = inheritedStyle;
|
|
701
|
+
}
|
|
684
702
|
}
|
|
685
703
|
|
|
686
704
|
el = await this.createElement(merged, activeController, controllerState);
|
|
@@ -766,7 +784,10 @@ export class EzRenderer {
|
|
|
766
784
|
localItem._ezSourcePath = parentSource || undefined;
|
|
767
785
|
}
|
|
768
786
|
|
|
769
|
-
if
|
|
787
|
+
// Only inherit CSS if child doesn't have its own CSS module
|
|
788
|
+
const childHasOwnCss = localItem.css ||
|
|
789
|
+
(localItem.eztype && this.ez.hasStyles(localItem.eztype));
|
|
790
|
+
if (parentCss && !childHasOwnCss) {
|
|
770
791
|
localItem.css = parentCss;
|
|
771
792
|
}
|
|
772
793
|
|
package/core/state.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { deepSignal } from 'deepsignal';
|
|
2
|
+
import { computed, type ReadonlySignal } from '@preact/signals';
|
|
2
3
|
import { EzError } from './EzError.js';
|
|
3
4
|
|
|
4
5
|
export interface ControllerDefinition {
|
|
5
6
|
state?: Record<string, unknown>;
|
|
7
|
+
computed?: Record<string, () => unknown>;
|
|
8
|
+
_computed?: Record<string, ReadonlySignal<unknown>>;
|
|
6
9
|
getState?: (key?: string) => unknown;
|
|
10
|
+
getComputed?: (key: string) => unknown;
|
|
7
11
|
[key: string]: unknown;
|
|
8
12
|
}
|
|
9
13
|
|
|
@@ -48,6 +52,25 @@ export class EzState {
|
|
|
48
52
|
return (definition.state as Record<string, unknown>)[key];
|
|
49
53
|
};
|
|
50
54
|
|
|
55
|
+
// Process computed properties
|
|
56
|
+
if (definition.computed) {
|
|
57
|
+
definition._computed = {};
|
|
58
|
+
for (const [key, fn] of Object.entries(definition.computed)) {
|
|
59
|
+
const computedSignal = computed(() => fn.call(definition));
|
|
60
|
+
definition._computed[key] = computedSignal;
|
|
61
|
+
|
|
62
|
+
// Make computed accessible directly on controller (e.g., ctrl.filteredTodos)
|
|
63
|
+
Object.defineProperty(definition, key, {
|
|
64
|
+
get: () => computedSignal.value,
|
|
65
|
+
enumerable: true
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
definition.getComputed = (key: string) => {
|
|
70
|
+
return definition._computed?.[key]?.value;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
51
74
|
this.ez._controllers[name] = definition;
|
|
52
75
|
}
|
|
53
76
|
|
package/modules.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
export const frameworkModules: Record<string, () => Promise<unknown>> = {
|
|
5
5
|
// Core components
|
|
6
6
|
'./components/EzComponent.ts': () => import('./components/EzComponent.js'),
|
|
7
|
-
'./components/EzInput.ts': () => import('./components/EzInput.js'),
|
|
7
|
+
'./components/EzInput.ts': () => import('./components/input/EzInput.js'),
|
|
8
8
|
'./components/EzLabel.ts': () => import('./components/EzLabel.js'),
|
|
9
9
|
'./components/EzOutlet.ts': () => import('./components/EzOutlet.js'),
|
|
10
10
|
'./components/EzBaseComponent.ts': () => import('./components/EzBaseComponent.js'),
|
|
@@ -82,7 +82,7 @@ export const frameworkModules: Record<string, () => Promise<unknown>> = {
|
|
|
82
82
|
};
|
|
83
83
|
|
|
84
84
|
export const frameworkStyles: Record<string, () => Promise<unknown>> = {
|
|
85
|
-
'./components/EzInput.module.scss': () => import('./components/EzInput.module.scss'),
|
|
85
|
+
'./components/EzInput.module.scss': () => import('./components/input/EzInput.module.scss'),
|
|
86
86
|
'./components/button/EzButton.module.scss': () => import('./components/button/EzButton.module.scss'),
|
|
87
87
|
'./components/card/EzCard.module.scss': () => import('./components/card/EzCard.module.scss'),
|
|
88
88
|
'./components/avatar/EzAvatar.module.scss': () => import('./components/avatar/EzAvatar.module.scss'),
|
package/package.json
CHANGED
package/components/EzInput.ts
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import styles from './EzInput.module.scss';
|
|
2
|
-
import { cx } from '../utils/cssModules.js';
|
|
3
|
-
import { EzBaseComponent, EzBaseComponentConfig } from './EzBaseComponent.js';
|
|
4
|
-
|
|
5
|
-
const cls = cx(styles);
|
|
6
|
-
|
|
7
|
-
export interface EzInputConfig extends EzBaseComponentConfig {
|
|
8
|
-
size?: 'sm' | 'lg';
|
|
9
|
-
variant?: 'filled' | 'underline';
|
|
10
|
-
block?: boolean;
|
|
11
|
-
disabled?: boolean;
|
|
12
|
-
readonly?: boolean;
|
|
13
|
-
icon?: string;
|
|
14
|
-
inputType?: string;
|
|
15
|
-
placeholder?: string;
|
|
16
|
-
value?: string | number;
|
|
17
|
-
name?: string;
|
|
18
|
-
formData?: string;
|
|
19
|
-
onKeydown?: (e: KeyboardEvent, ctrl: unknown) => void;
|
|
20
|
-
onKeyup?: (e: KeyboardEvent, ctrl: unknown) => void;
|
|
21
|
-
onEnter?: (value: string, ctrl: unknown) => void;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export class EzInput extends EzBaseComponent {
|
|
25
|
-
declare config: EzInputConfig;
|
|
26
|
-
|
|
27
|
-
private _wrapper: HTMLDivElement | null = null;
|
|
28
|
-
private _error: HTMLDivElement | null = null;
|
|
29
|
-
|
|
30
|
-
render(): HTMLDivElement {
|
|
31
|
-
const cfg = this.config;
|
|
32
|
-
|
|
33
|
-
const wrapper = document.createElement('div');
|
|
34
|
-
wrapper.className = cls(
|
|
35
|
-
'inputGroup',
|
|
36
|
-
cfg.size,
|
|
37
|
-
cfg.variant,
|
|
38
|
-
cfg.block && 'block',
|
|
39
|
-
cfg.disabled && 'disabled'
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
const error = document.createElement('div');
|
|
43
|
-
error.className = cls('fieldError');
|
|
44
|
-
wrapper.appendChild(error);
|
|
45
|
-
|
|
46
|
-
const row = document.createElement('div');
|
|
47
|
-
row.className = cls('inputRow');
|
|
48
|
-
|
|
49
|
-
if (cfg.icon) {
|
|
50
|
-
const icon = document.createElement('i');
|
|
51
|
-
icon.className = cls('icon', `fa fa-${cfg.icon}`);
|
|
52
|
-
row.appendChild(icon);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const input = document.createElement('input');
|
|
56
|
-
input.className = cls('input');
|
|
57
|
-
input.type = cfg.inputType || 'text';
|
|
58
|
-
input.placeholder = cfg.placeholder || '';
|
|
59
|
-
if (cfg.value !== undefined) input.value = String(cfg.value);
|
|
60
|
-
|
|
61
|
-
if (cfg.name && cfg.formData) {
|
|
62
|
-
this.config.bind = `${cfg.formData}.${cfg.name}`;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (cfg.name) {
|
|
66
|
-
input.setAttribute('data-ez-field', cfg.name);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (cfg.disabled) input.disabled = true;
|
|
70
|
-
if (cfg.readonly) input.readOnly = true;
|
|
71
|
-
|
|
72
|
-
this.applyCommonBindings(input);
|
|
73
|
-
this.applyStyles(input);
|
|
74
|
-
|
|
75
|
-
const onChange = this._createOnChangeHandler();
|
|
76
|
-
|
|
77
|
-
input.addEventListener('input', e => {
|
|
78
|
-
if (onChange) {
|
|
79
|
-
onChange((e.target as HTMLInputElement).value);
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
const getController = () => {
|
|
84
|
-
const ctrlName = this.config.controller;
|
|
85
|
-
return ctrlName ? ez.getControllerSync(ctrlName) : null;
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
if (cfg.onKeydown) {
|
|
89
|
-
input.addEventListener('keydown', e => {
|
|
90
|
-
cfg.onKeydown!(e, getController());
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (cfg.onKeyup) {
|
|
95
|
-
input.addEventListener('keyup', e => {
|
|
96
|
-
cfg.onKeyup!(e, getController());
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (cfg.onEnter) {
|
|
101
|
-
input.addEventListener('keydown', e => {
|
|
102
|
-
if (e.key === 'Enter') {
|
|
103
|
-
cfg.onEnter!(input.value, getController());
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
row.appendChild(input);
|
|
109
|
-
wrapper.appendChild(row);
|
|
110
|
-
|
|
111
|
-
this._wrapper = wrapper;
|
|
112
|
-
this._error = error;
|
|
113
|
-
|
|
114
|
-
return wrapper;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
showError(message: string): void {
|
|
118
|
-
if (this._wrapper && this._error) {
|
|
119
|
-
this._wrapper.classList.add(cls('hasError'));
|
|
120
|
-
this._error.classList.add(cls('fieldErrorVisible'));
|
|
121
|
-
this._error.textContent = message;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
clearError(): void {
|
|
126
|
-
if (this._wrapper && this._error) {
|
|
127
|
-
this._wrapper.classList.remove(cls('hasError'));
|
|
128
|
-
this._error.classList.remove(cls('fieldErrorVisible'));
|
|
129
|
-
this._error.textContent = '';
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
File without changes
|