ezfw-core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/components/EzBaseComponent.ts +648 -0
  2. package/components/EzComponent.ts +89 -0
  3. package/components/EzInput.module.scss +183 -0
  4. package/components/EzInput.ts +104 -0
  5. package/components/EzLabel.ts +22 -0
  6. package/components/EzOutlet.ts +181 -0
  7. package/components/HtmlWrapper.ts +305 -0
  8. package/components/avatar/EzAvatar.module.scss +200 -0
  9. package/components/avatar/EzAvatar.ts +130 -0
  10. package/components/badge/EzBadge.module.scss +202 -0
  11. package/components/badge/EzBadge.ts +77 -0
  12. package/components/button/EzButton.module.scss +402 -0
  13. package/components/button/EzButton.ts +175 -0
  14. package/components/button/EzButtonGroup.ts +48 -0
  15. package/components/card/EzCard.module.scss +71 -0
  16. package/components/card/EzCard.ts +120 -0
  17. package/components/chart/EzBarChart.ts +47 -0
  18. package/components/chart/EzChart.module.scss +14 -0
  19. package/components/chart/EzChart.ts +279 -0
  20. package/components/chart/EzDoughnutChart.ts +47 -0
  21. package/components/chart/EzLineChart.ts +53 -0
  22. package/components/checkbox/EzCheckbox.module.scss +145 -0
  23. package/components/checkbox/EzCheckbox.ts +115 -0
  24. package/components/dataview/EzDataView.module.scss +115 -0
  25. package/components/dataview/EzDataView.ts +355 -0
  26. package/components/dataview/modes/EzDataViewCards.ts +322 -0
  27. package/components/dataview/modes/EzDataViewGrid.ts +76 -0
  28. package/components/datepicker/EzDatePicker.module.scss +348 -0
  29. package/components/datepicker/EzDatePicker.ts +519 -0
  30. package/components/dialog/EzDialog.module.scss +180 -0
  31. package/components/dropdown/EzDropdown.module.scss +107 -0
  32. package/components/dropdown/EzDropdown.ts +235 -0
  33. package/components/feed/EzActivityFeed.module.scss +90 -0
  34. package/components/feed/EzActivityFeed.ts +78 -0
  35. package/components/form/EzForm.ts +364 -0
  36. package/components/form/EzValidators.test.js +421 -0
  37. package/components/form/EzValidators.ts +202 -0
  38. package/components/grid/EzGrid.scss +88 -0
  39. package/components/grid/EzGrid.ts +1085 -0
  40. package/components/grid/EzGridContainer.ts +104 -0
  41. package/components/grid/body/EzGridBody.scss +283 -0
  42. package/components/grid/body/EzGridBody.ts +549 -0
  43. package/components/grid/body/EzGridCell.ts +211 -0
  44. package/components/grid/body/EzGridRow.ts +196 -0
  45. package/components/grid/filter/EzGridFilters.scss +78 -0
  46. package/components/grid/filter/EzGridFilters.ts +285 -0
  47. package/components/grid/footer/EzGridFooter.scss +136 -0
  48. package/components/grid/footer/EzGridFooter.ts +448 -0
  49. package/components/grid/header/EzGridHeader.scss +199 -0
  50. package/components/grid/header/EzGridHeader.ts +430 -0
  51. package/components/grid/query/EzGridQuery.ts +81 -0
  52. package/components/grid/state/EzGridColumns.ts +155 -0
  53. package/components/grid/state/EzGridController.ts +470 -0
  54. package/components/grid/state/EzGridLifecycle.ts +136 -0
  55. package/components/grid/state/EzGridNormalizers.test.js +273 -0
  56. package/components/grid/state/EzGridNormalizers.ts +162 -0
  57. package/components/grid/state/EzGridParts.ts +233 -0
  58. package/components/grid/state/EzGridPersistence.ts +140 -0
  59. package/components/grid/state/EzGridRemote.test.js +573 -0
  60. package/components/grid/state/EzGridRemote.ts +335 -0
  61. package/components/grid/state/EzGridSelection.ts +231 -0
  62. package/components/grid/state/EzGridSort.ts +286 -0
  63. package/components/grid/title/EzGridActionBar.ts +98 -0
  64. package/components/grid/title/EzGridTitle.ts +114 -0
  65. package/components/grid/title/EzGridTitleBar.scss +65 -0
  66. package/components/grid/title/EzGridTitleBar.ts +87 -0
  67. package/components/grid/types.ts +607 -0
  68. package/components/panel/EzPanel.module.scss +133 -0
  69. package/components/panel/EzPanel.ts +147 -0
  70. package/components/radio/EzRadio.module.scss +190 -0
  71. package/components/radio/EzRadio.ts +149 -0
  72. package/components/select/EzSelect.module.scss +153 -0
  73. package/components/select/EzSelect.ts +238 -0
  74. package/components/skeleton/EzSkeleton.module.scss +95 -0
  75. package/components/skeleton/EzSkeleton.ts +70 -0
  76. package/components/store/EzStore.ts +344 -0
  77. package/components/switch/EzSwitch.module.scss +164 -0
  78. package/components/switch/EzSwitch.ts +117 -0
  79. package/components/tabs/EzTabPanel.module.scss +181 -0
  80. package/components/tabs/EzTabPanel.ts +402 -0
  81. package/components/textarea/EzTextarea.module.scss +131 -0
  82. package/components/textarea/EzTextarea.ts +161 -0
  83. package/components/timepicker/EzTimePicker.module.scss +282 -0
  84. package/components/timepicker/EzTimePicker.ts +540 -0
  85. package/components/toast/EzToast.module.scss +291 -0
  86. package/components/tooltip/EzTooltip.module.scss +124 -0
  87. package/components/tooltip/EzTooltip.ts +153 -0
  88. package/core/EzComponentTypes.ts +693 -0
  89. package/core/EzError.ts +63 -0
  90. package/core/EzModel.ts +268 -0
  91. package/core/EzTypes.ts +328 -0
  92. package/core/eventBus.ts +284 -0
  93. package/core/ez.ts +617 -0
  94. package/core/loader.ts +725 -0
  95. package/core/renderer.ts +1010 -0
  96. package/core/router.ts +490 -0
  97. package/core/services.ts +124 -0
  98. package/core/state.ts +142 -0
  99. package/core/utils.ts +81 -0
  100. package/package.json +51 -0
  101. package/services/RouteUI.js +17 -0
  102. package/services/crypto.js +64 -0
  103. package/services/dialog.js +222 -0
  104. package/services/fetchApi.js +63 -0
  105. package/services/firebase.js +30 -0
  106. package/services/toast.js +214 -0
  107. package/template/doc/EzDocs.js +15 -0
  108. package/template/doc/EzDocs.module.scss +627 -0
  109. package/template/doc/EzDocsController.js +164 -0
  110. package/template/doc/data/activityfeed/EzActivityFeedDoc.js +42 -0
  111. package/template/doc/data/avatar/EzAvatarDoc.js +71 -0
  112. package/template/doc/data/badge/EzBadgeDoc.js +92 -0
  113. package/template/doc/data/button/EzButtonDoc.js +77 -0
  114. package/template/doc/data/buttongroup/EzButtonGroupDoc.js +102 -0
  115. package/template/doc/data/card/EzCardDoc.js +39 -0
  116. package/template/doc/data/chart/EzChartDoc.js +60 -0
  117. package/template/doc/data/checkbox/EzCheckboxDoc.js +67 -0
  118. package/template/doc/data/component/EzComponentDoc.js +34 -0
  119. package/template/doc/data/cssmodules/CSSModulesDoc.js +70 -0
  120. package/template/doc/data/datepicker/EzDatePickerDoc.js +126 -0
  121. package/template/doc/data/dialog/EzDialogDoc.js +217 -0
  122. package/template/doc/data/dropdown/EzDropdownDoc.js +178 -0
  123. package/template/doc/data/form/EzFormDoc.js +90 -0
  124. package/template/doc/data/grid/EzGridDoc.js +99 -0
  125. package/template/doc/data/input/EzInputDoc.js +92 -0
  126. package/template/doc/data/label/EzLabelDoc.js +40 -0
  127. package/template/doc/data/model/EzModelDoc.js +53 -0
  128. package/template/doc/data/outlet/EzOutletDoc.js +63 -0
  129. package/template/doc/data/panel/EzPanelDoc.js +214 -0
  130. package/template/doc/data/radio/EzRadioDoc.js +174 -0
  131. package/template/doc/data/router/EzRouterDoc.js +75 -0
  132. package/template/doc/data/select/EzSelectDoc.js +37 -0
  133. package/template/doc/data/skeleton/EzSkeletonDoc.js +149 -0
  134. package/template/doc/data/switch/EzSwitchDoc.js +82 -0
  135. package/template/doc/data/tabpanel/EzTabPanelDoc.js +44 -0
  136. package/template/doc/data/textarea/EzTextareaDoc.js +131 -0
  137. package/template/doc/data/timepicker/EzTimePickerDoc.js +107 -0
  138. package/template/doc/data/tooltip/EzTooltipDoc.js +193 -0
  139. package/template/doc/data/validators/EzValidatorsDoc.js +37 -0
  140. package/template/doc/sidebar/EzDocsSidebar.js +32 -0
  141. package/template/doc/sidebar/category/EzDocsCategory.js +33 -0
  142. package/template/doc/sidebar/item/EzDocsComponentItem.js +24 -0
  143. package/template/doc/viewer/EzDocsViewer.js +18 -0
  144. package/template/doc/viewer/codepanel/EzDocsCodePanel.js +51 -0
  145. package/template/doc/viewer/content/EzDocsContent.js +315 -0
  146. package/template/doc/viewer/header/EzDocsViewerHeader.js +46 -0
  147. package/template/doc/viewer/showcase/EzDocsShowcase.js +59 -0
  148. package/template/doc/viewer/showcase/EzDocsShowcaseSection.js +25 -0
  149. package/template/doc/viewer/showcase/EzDocsVariantItem.js +29 -0
  150. package/template/doc/welcome/EzDocsWelcome.js +48 -0
  151. package/themes/ez-theme.scss +179 -0
  152. package/themes/nature-fresh.scss +169 -0
  153. package/types/global.d.ts +21 -0
  154. package/utils/cssModules.js +81 -0
@@ -0,0 +1,285 @@
1
+ // EzGrid/filter/EzGridFilters.ts
2
+
3
+ import './EzGridFilters.scss';
4
+ import { EzGridContainer, EzGridContainerConfig } from '../EzGridContainer.js';
5
+ import type { NormalizedColumn, FilterSnapshot } from '../types.js';
6
+
7
+ declare const ez: {
8
+ _createElement(config: any): Promise<HTMLElement | null>;
9
+ };
10
+
11
+ export interface FilterContext {
12
+ grid: EzGridFiltersRef | undefined;
13
+ column: NormalizedColumn;
14
+ value: any;
15
+ setValue: (value: any) => void;
16
+ field: string;
17
+ operator: string;
18
+ }
19
+
20
+ export interface ResolvedFilter {
21
+ enabled: boolean;
22
+ field?: string;
23
+ operator?: string;
24
+ itemRender?: any;
25
+ }
26
+
27
+ export interface EzGridFiltersRef {
28
+ _onFiltersChanged?: (snapshot: FilterSnapshot[] | null) => void;
29
+ }
30
+
31
+ export interface EzGridFiltersConfig extends EzGridContainerConfig {
32
+ grid?: EzGridFiltersRef;
33
+ columns?: (() => NormalizedColumn[]) | NormalizedColumn[];
34
+ filterMode?: 'onInput' | 'onEnter';
35
+ debounce?: number;
36
+ }
37
+
38
+ export class EzGridFilters extends EzGridContainer {
39
+ declare config: EzGridFiltersConfig;
40
+ declare el: HTMLElement | null;
41
+
42
+ grid: EzGridFiltersRef | undefined;
43
+ columns: () => NormalizedColumn[];
44
+ private _values: Record<string, any>;
45
+ private _filterMode: 'onInput' | 'onEnter';
46
+ private _debounceMs: number;
47
+ private _debounceTimer: ReturnType<typeof setTimeout> | null;
48
+
49
+ constructor(config: EzGridFiltersConfig = {}) {
50
+ super(config);
51
+
52
+ this.grid = config.grid;
53
+ const cols = config.columns;
54
+ this.columns = typeof cols === 'function'
55
+ ? cols
56
+ : () => (cols || []);
57
+
58
+ // Local, ephemeral filter values (not persisted)
59
+ this._values = {};
60
+
61
+ // Filter mode: 'onInput' (default) or 'onEnter'
62
+ // In remote mode, this is controlled by remote.filter config
63
+ this._filterMode = config.filterMode ?? 'onInput';
64
+
65
+ // Debounce config (default 300ms) - only used in onInput mode
66
+ this._debounceMs = config.debounce ?? 300;
67
+ this._debounceTimer = null;
68
+
69
+ this.config.cls = (this.config.cls || '') + ' ez-grid-filters';
70
+ this.config.layout = 'hbox';
71
+
72
+ this._buildFilters();
73
+ }
74
+
75
+ private _buildFilters(): void {
76
+ const columns = this.columns();
77
+
78
+ this.config.items = columns.map(col => {
79
+ const resolved = this._resolveFilter(col);
80
+
81
+ // Always occupy column space to keep alignment
82
+ // width / flex - same logic as EzGridCell
83
+ const baseCell: any = {
84
+ eztype: 'EzComponent',
85
+ cls: 'ez-grid-filter-cell',
86
+ flex: col.width != null ? undefined : (col.flex ?? 1),
87
+ style: col.width != null ? { width: `${col.width}px` } : undefined,
88
+ items: []
89
+ };
90
+
91
+ // No filter → empty placeholder
92
+ if (!resolved.enabled) {
93
+ return baseCell;
94
+ }
95
+
96
+ const field = resolved.field!;
97
+ const operator = resolved.operator!;
98
+
99
+ // NOTE: Initialize proper default value based on operator
100
+ const defaultValue = this._getDefaultValue(operator);
101
+
102
+ // NOTE: Ensure _values[field] exists with correct type
103
+ if (this._values[field] === undefined) {
104
+ this._values[field] = defaultValue;
105
+ }
106
+
107
+ const value = this._values[field];
108
+
109
+ // NOTE: setValue callback for custom itemRender
110
+ const setValue = (newValue: any) => {
111
+ this._values[field] = newValue;
112
+ this._emitSnapshot();
113
+ };
114
+
115
+ const ctx: FilterContext = {
116
+ grid: this.grid,
117
+ column: col,
118
+ value,
119
+ setValue,
120
+ field,
121
+ operator
122
+ };
123
+
124
+ const isCustomRender = typeof resolved.itemRender === 'function';
125
+
126
+ let itemCfg = isCustomRender
127
+ ? resolved.itemRender(ctx)
128
+ : { ...resolved.itemRender };
129
+
130
+ // WARNING:
131
+ // For custom itemRender, do NOT override attrs or handlers.
132
+ // Custom renders have full control via setValue callback.
133
+ if (!isCustomRender) {
134
+ itemCfg.attrs = {
135
+ ...(itemCfg.attrs || {}),
136
+ placeholder: col.text ?? '',
137
+ value
138
+ };
139
+
140
+ // Handler to update local value
141
+ const updateValue = (e: Event) => {
142
+ const newValue = (e?.target as HTMLInputElement)?.value ?? '';
143
+
144
+ if (newValue === '' || newValue == null) {
145
+ delete this._values[field];
146
+ } else {
147
+ this._values[field] = newValue;
148
+ }
149
+ };
150
+
151
+ if (this._filterMode === 'onEnter') {
152
+ // onEnter mode: only emit on Enter key
153
+ itemCfg.onInput = updateValue;
154
+ itemCfg.onChange = updateValue;
155
+
156
+ // Use _ezAfterRender to bind keydown since native inputs don't auto-bind onKeyDown
157
+ itemCfg._ezAfterRender = (el: HTMLInputElement) => {
158
+ el.addEventListener('keydown', (e: KeyboardEvent) => {
159
+ if (e.key === 'Enter') {
160
+ updateValue(e);
161
+ this._doEmitSnapshot(); // Direct emit, no debounce
162
+ }
163
+ });
164
+ };
165
+ } else {
166
+ // onInput mode: emit on every change (with debounce)
167
+ const handler = (e: Event) => {
168
+ updateValue(e);
169
+ this._emitSnapshot();
170
+ };
171
+ itemCfg.onInput = handler;
172
+ itemCfg.onChange = handler;
173
+ }
174
+ }
175
+
176
+ baseCell.items.push(itemCfg);
177
+
178
+ return baseCell;
179
+ });
180
+ }
181
+
182
+ private _getDefaultValue(operator: string): any {
183
+ // NOTE: 'between' operator expects array of two values
184
+ if (operator === 'between') {
185
+ return [null, null];
186
+ }
187
+ return '';
188
+ }
189
+
190
+ private _resolveFilter(col: NormalizedColumn): ResolvedFilter {
191
+ const raw = col.filter;
192
+
193
+ // No filter defined
194
+ if (raw == null) {
195
+ return { enabled: false };
196
+ }
197
+
198
+ // Empty object (from filter: true) → default '='
199
+ if (!raw.operator && !raw.itemRender) {
200
+ return {
201
+ enabled: true,
202
+ field: col.index,
203
+ operator: '=',
204
+ itemRender: {
205
+ eztype: 'input'
206
+ }
207
+ };
208
+ }
209
+
210
+ // filter: { operator, itemRender } - already normalized from EzGridNormalizers
211
+ return {
212
+ enabled: true,
213
+ field: col.index,
214
+ operator: raw.operator ?? '=',
215
+ itemRender: raw.itemRender ?? {
216
+ eztype: 'input'
217
+ }
218
+ };
219
+ }
220
+
221
+ private _emitSnapshot(): void {
222
+ // Clear existing debounce timer
223
+ if (this._debounceTimer) {
224
+ clearTimeout(this._debounceTimer);
225
+ }
226
+
227
+ // Debounce the actual emit
228
+ this._debounceTimer = setTimeout(() => {
229
+ this._doEmitSnapshot();
230
+ }, this._debounceMs);
231
+ }
232
+
233
+ private _doEmitSnapshot(): void {
234
+ const snapshot: FilterSnapshot[] = Object.keys(this._values).map(field => {
235
+ const value = this._values[field];
236
+
237
+ // Skip empty values
238
+ if (value === '' || value == null) return null;
239
+
240
+ // Skip empty arrays (for 'between' operator)
241
+ if (Array.isArray(value) && value.every(v => v == null || v === '')) return null;
242
+
243
+ const col = this.columns().find(c => c.index === field);
244
+ if (!col) return null;
245
+
246
+ const resolved = this._resolveFilter(col);
247
+ if (!resolved.enabled) return null;
248
+
249
+ return {
250
+ field,
251
+ operator: resolved.operator!,
252
+ value
253
+ };
254
+ }).filter((item): item is FilterSnapshot => item !== null);
255
+
256
+ this.grid?._onFiltersChanged?.(
257
+ snapshot.length ? snapshot : null
258
+ );
259
+ }
260
+
261
+ clear(): void {
262
+ this._values = {};
263
+ this._rebuildUI();
264
+ }
265
+
266
+ refresh(): void {
267
+ this._rebuildUI();
268
+ }
269
+
270
+ private async _rebuildUI(): Promise<void> {
271
+ this._buildFilters();
272
+
273
+ if (this.el) {
274
+ this.el.innerHTML = '';
275
+
276
+ const children = await Promise.all(
277
+ (this.config.items || []).map(item => ez._createElement(item))
278
+ );
279
+
280
+ for (const child of children) {
281
+ if (child) this.el.appendChild(child);
282
+ }
283
+ }
284
+ }
285
+ }
@@ -0,0 +1,136 @@
1
+ // ==========================================================
2
+ // EzGridFooter
3
+ // ==========================================================
4
+
5
+ .ez-grid {
6
+ .ez-grid-footer {
7
+ display: flex;
8
+ flex-direction: column;
9
+ border-top: 1px solid var(--ez-grid-border);
10
+ background-color: var(--ez-grid-surface);
11
+
12
+ font-size: var(--ez-grid-font-size);
13
+ font-weight: var(--ez-grid-font-weight-medium);
14
+ color: var(--ez-grid-text-secondary);
15
+ }
16
+
17
+ // ----------------------------------------------------------
18
+ // Stats row: column summaries
19
+ // ----------------------------------------------------------
20
+ .ez-grid-footer-stats {
21
+ display: flex;
22
+ border-bottom: 1px solid var(--ez-grid-border-light);
23
+ background-color: var(--ez-grid-surface-hover);
24
+ }
25
+
26
+ .ez-grid-footer-stat-cell {
27
+ display: flex;
28
+ align-items: center;
29
+ padding: 6px 12px;
30
+ min-height: 32px;
31
+ box-sizing: border-box;
32
+
33
+ font-size: calc(var(--ez-grid-font-size) - 1px);
34
+ font-weight: var(--ez-grid-font-weight-semibold);
35
+ color: var(--ez-grid-text);
36
+
37
+ white-space: nowrap;
38
+ overflow: hidden;
39
+ text-overflow: ellipsis;
40
+ }
41
+
42
+ // ----------------------------------------------------------
43
+ // Pagination row
44
+ // ----------------------------------------------------------
45
+ .ez-grid-footer-pagination {
46
+ display: flex;
47
+ min-height: var(--ez-grid-header-height);
48
+ align-items: center;
49
+ padding: 0 12px;
50
+ gap: 16px;
51
+ }
52
+
53
+ // ----------------------------------------------------------
54
+ // Page info: "1 - 25 of 100"
55
+ // ----------------------------------------------------------
56
+ .ez-grid-footer-info {
57
+ white-space: nowrap;
58
+ color: var(--ez-grid-text);
59
+ }
60
+
61
+ // ----------------------------------------------------------
62
+ // Page size selector
63
+ // ----------------------------------------------------------
64
+ .ez-grid-footer-pagesize {
65
+ align-items: center;
66
+ gap: 8px;
67
+ }
68
+
69
+ .ez-grid-footer-pagesize-label {
70
+ color: var(--ez-grid-text-muted);
71
+ white-space: nowrap;
72
+ }
73
+
74
+ // ----------------------------------------------------------
75
+ // Navigation buttons
76
+ // ----------------------------------------------------------
77
+ .ez-grid-footer-nav {
78
+ align-items: center;
79
+ gap: 4px;
80
+ }
81
+
82
+ .ez-grid-footer-nav-btn {
83
+ display: inline-flex;
84
+ align-items: center;
85
+ justify-content: center;
86
+ min-width: 32px;
87
+ height: 32px;
88
+ padding: 0 8px;
89
+
90
+ font-size: 1rem;
91
+ font-weight: var(--ez-grid-font-weight-medium);
92
+ color: var(--ez-grid-text-secondary);
93
+
94
+ background: var(--ez-grid-bg);
95
+ border: 1px solid var(--ez-grid-border);
96
+ border-radius: var(--ez-grid-radius-sm);
97
+
98
+ cursor: pointer;
99
+ transition: all 0.15s ease;
100
+
101
+ &:hover:not(:disabled) {
102
+ background: var(--ez-grid-surface-hover);
103
+ border-color: var(--ez-grid-text-muted);
104
+ color: var(--ez-grid-text);
105
+ }
106
+
107
+ &:active:not(:disabled) {
108
+ background: var(--ez-grid-surface-active);
109
+ }
110
+
111
+ &:disabled {
112
+ color: var(--ez-grid-border);
113
+ cursor: not-allowed;
114
+ background: var(--ez-grid-surface);
115
+ border-color: var(--ez-grid-border-light);
116
+ }
117
+ }
118
+
119
+ // ----------------------------------------------------------
120
+ // Page indicator: "1 / 5"
121
+ // ----------------------------------------------------------
122
+ .ez-grid-footer-page-indicator {
123
+ padding: 0 8px;
124
+ white-space: nowrap;
125
+ min-width: 48px;
126
+ text-align: center;
127
+ color: var(--ez-grid-text);
128
+ }
129
+
130
+ // ----------------------------------------------------------
131
+ // Reload button (separated from pagination)
132
+ // ----------------------------------------------------------
133
+ .ez-grid-footer-reload-btn {
134
+ margin-left: 8px;
135
+ }
136
+ }