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,286 @@
1
+ // EzGrid/state/EzGridSort.ts
2
+
3
+ import type {
4
+ SortDirection,
5
+ Sorter,
6
+ SortConfig,
7
+ SortSnapshot,
8
+ NormalizedColumn,
9
+ RowData
10
+ } from '../types.js';
11
+
12
+ export interface EzGridSortRef {
13
+ controller?: {
14
+ state: {
15
+ data: RowData[];
16
+ };
17
+ setSortSnapshot?: (snapshot: SortSnapshot | null) => void;
18
+ supportsRemoteSort?: boolean;
19
+ sort?: (columnId: string, direction: SortDirection) => void;
20
+ };
21
+ stateful?: boolean;
22
+ columns: NormalizedColumn[];
23
+ headerInstance?: {
24
+ refreshColumns?: () => void;
25
+ };
26
+ _persistence?: {
27
+ updateSortState: (snapshot: Sorter[] | null) => void;
28
+ save: () => void;
29
+ };
30
+ _hasLocalSortBaseline?: boolean;
31
+ _localSortBaseline?: RowData[] | null;
32
+ refreshBody: () => Promise<void>;
33
+ }
34
+
35
+ // Re-export types for backwards compatibility
36
+ export type { SortDirection, Sorter, SortConfig, SortSnapshot } from '../types.js';
37
+
38
+ export class EzGridSort {
39
+ grid: EzGridSortRef;
40
+ sorters: Sorter[];
41
+
42
+ constructor(grid: EzGridSortRef, config: SortConfig | SortConfig[] | null = null) {
43
+ this.grid = grid;
44
+
45
+ // WARNING: Sort state must be owned exclusively by this module.
46
+ // EzGrid should never mutate sorters directly.
47
+ this.sorters = [];
48
+
49
+ if (config) {
50
+ this._initFromConfig(config);
51
+ }
52
+ }
53
+
54
+ // ==========================================================
55
+ // Initialization
56
+ // ==========================================================
57
+
58
+ private _initFromConfig(config: SortConfig | SortConfig[]): void {
59
+ // NOTE: Supports both single sorter and array of sorters
60
+ if (Array.isArray(config)) {
61
+ this.sorters = config
62
+ .map(c => this._normalizeSorter(c))
63
+ .filter((s): s is Sorter => s !== null);
64
+ } else {
65
+ const s = this._normalizeSorter(config);
66
+ if (s) this.sorters = [s];
67
+ }
68
+ }
69
+
70
+ private _normalizeSorter(sorter: SortConfig | null): Sorter | null {
71
+ if (!sorter || !sorter.property) return null;
72
+
73
+ return {
74
+ property: sorter.property,
75
+ direction:
76
+ sorter.direction === 'DESC' ? 'DESC' : 'ASC'
77
+ };
78
+ }
79
+
80
+ // ==========================================================
81
+ // Public API
82
+ // ==========================================================
83
+
84
+ getSorters(): Sorter[] {
85
+ return this.sorters;
86
+ }
87
+
88
+ getSorterFor(property: string): Sorter | null {
89
+ return this.sorters.find(s => s.property === property) || null;
90
+ }
91
+
92
+ isSorted(property: string): boolean {
93
+ return !!this.getSorterFor(property);
94
+ }
95
+
96
+ toggleSort(property: string): void {
97
+ const existing = this.getSorterFor(property);
98
+
99
+ if (!existing) {
100
+ this.sorters = [{
101
+ property,
102
+ direction: 'ASC'
103
+ }];
104
+ } else if (existing.direction === 'ASC') {
105
+ existing.direction = 'DESC';
106
+ } else {
107
+ // WARNING: Clearing sort removes all sorters for now.
108
+ // This matches current single-sort behavior.
109
+ this.sorters = [];
110
+ }
111
+
112
+ this._changed();
113
+ }
114
+
115
+ clear(): void {
116
+ if (this.sorters.length === 0) return;
117
+
118
+ this.sorters = [];
119
+ this._changed();
120
+ }
121
+
122
+ sortBy(colId: string, direction?: SortDirection): void {
123
+ if (!colId) return;
124
+
125
+ const dir: SortDirection = direction === 'DESC' ? 'DESC' : 'ASC';
126
+ this.sorters = [{ property: colId, direction: dir }];
127
+ this._changed();
128
+ }
129
+
130
+ // ==========================================================
131
+ // Snapshot / Restore
132
+ // ==========================================================
133
+
134
+ snapshot(): Sorter[] | null {
135
+ if (!this.sorters.length) return null;
136
+
137
+ return this.sorters.map(s => ({
138
+ property: s.property,
139
+ direction: s.direction
140
+ }));
141
+ }
142
+
143
+ restore(snapshot: Sorter[] | null): void {
144
+ if (!Array.isArray(snapshot)) {
145
+ this.sorters = [];
146
+ return;
147
+ }
148
+
149
+ this.sorters = snapshot
150
+ .map(s => this._normalizeSorter(s))
151
+ .filter((s): s is Sorter => s !== null);
152
+ }
153
+
154
+ getSortSnapshot(): Sorter | null {
155
+ // NOTE: Returns snapshot format for remote controller
156
+ const sorter = this.sorters[0];
157
+ if (!sorter) return null;
158
+
159
+ return {
160
+ property: sorter.property,
161
+ direction: sorter.direction
162
+ };
163
+ }
164
+
165
+ // ==========================================================
166
+ // Sort Handler (main logic moved from EzGrid)
167
+ // ==========================================================
168
+
169
+ handleSortChanged(): void {
170
+ const grid = this.grid;
171
+ const sorter = this.sorters[0] ?? null;
172
+
173
+ if (!sorter) {
174
+ this._handleClearSort();
175
+ return;
176
+ }
177
+
178
+ const { property: columnId, direction } = sorter;
179
+
180
+ // Forward active sort snapshot to controller (remote contract)
181
+ grid.controller?.setSortSnapshot?.(this.getSortSnapshot());
182
+
183
+ // Save state if stateful
184
+ if (grid.stateful) {
185
+ grid._persistence?.updateSortState(this.snapshot());
186
+ grid._persistence?.save();
187
+ }
188
+
189
+ // Refresh header to show sort indicators
190
+ grid.headerInstance?.refreshColumns?.();
191
+
192
+ // Remote sorting
193
+ if (grid.controller?.supportsRemoteSort) {
194
+ grid.controller.sort?.(columnId, direction);
195
+ return;
196
+ }
197
+
198
+ // Local sorting
199
+ this.applyLocalSort();
200
+ }
201
+
202
+ private _handleClearSort(): void {
203
+ const grid = this.grid;
204
+
205
+ // Restore baseline data for local mode
206
+ if (!grid.controller?.supportsRemoteSort) {
207
+ if (grid._hasLocalSortBaseline === true && Array.isArray(grid._localSortBaseline)) {
208
+ grid.controller!.state.data = [...grid._localSortBaseline];
209
+ }
210
+
211
+ grid._localSortBaseline = null;
212
+ grid._hasLocalSortBaseline = false;
213
+ }
214
+
215
+ // Clear persisted state
216
+ if (grid.stateful) {
217
+ grid._persistence?.updateSortState(null);
218
+ grid._persistence?.save();
219
+ }
220
+
221
+ // Refresh header to clear sort icons
222
+ grid.headerInstance?.refreshColumns?.();
223
+
224
+ // Forward cleared sort snapshot to controller
225
+ grid.controller?.setSortSnapshot?.(null);
226
+
227
+ // Refresh body to reflect unsorted data
228
+ void grid.refreshBody();
229
+ }
230
+
231
+ // ==========================================================
232
+ // Local Sort Execution
233
+ // ==========================================================
234
+
235
+ applyLocalSort(): void {
236
+ const grid = this.grid;
237
+ const sorter = this.sorters[0];
238
+
239
+ if (!sorter) return;
240
+
241
+ const { property: columnId, direction } = sorter;
242
+
243
+ if (!columnId || !direction) {
244
+ void grid.refreshBody();
245
+ return;
246
+ }
247
+
248
+ const col = grid.columns.find(c => c._id === columnId);
249
+ if (!col || !col.index) return;
250
+
251
+ // Capture baseline once per dataset, before applying the first local sort
252
+ if (grid._hasLocalSortBaseline !== true) {
253
+ grid._localSortBaseline = [...grid.controller!.state.data];
254
+ grid._hasLocalSortBaseline = true;
255
+ }
256
+
257
+ const data = [...grid.controller!.state.data];
258
+
259
+ data.sort((a, b) => {
260
+ const va = a?.[col.index!];
261
+ const vb = b?.[col.index!];
262
+
263
+ const isAsc = direction === 'ASC';
264
+
265
+ if (va == null && vb == null) return 0;
266
+ if (va == null) return isAsc ? -1 : 1;
267
+ if (vb == null) return isAsc ? 1 : -1;
268
+
269
+ if (va < vb) return isAsc ? -1 : 1;
270
+ if (va > vb) return isAsc ? 1 : -1;
271
+ return 0;
272
+ });
273
+
274
+ grid.controller!.state.data = data;
275
+ void grid.refreshBody();
276
+ }
277
+
278
+ // ==========================================================
279
+ // Internal
280
+ // ==========================================================
281
+
282
+ private _changed(): void {
283
+ // NOTE: Delegate to handler
284
+ this.handleSortChanged();
285
+ }
286
+ }
@@ -0,0 +1,98 @@
1
+ // EzGrid/title/EzGridActionBar.ts
2
+
3
+ import { EzGridContainer, EzGridContainerConfig } from '../EzGridContainer.js';
4
+
5
+ export interface ToolConfig {
6
+ requiresSelection?: boolean;
7
+ minSelection?: number;
8
+ onClick?: (grid: any) => void;
9
+ [key: string]: any;
10
+ }
11
+
12
+ export interface ToolInstance {
13
+ tool: ToolConfig;
14
+ cfg: any;
15
+ }
16
+
17
+ export interface EzGridActionBarRef {
18
+ on?: (event: string, callback: (e: { keys: string[] }) => void) => void;
19
+ getSelection?: () => string[];
20
+ }
21
+
22
+ export interface EzGridActionBarConfig extends EzGridContainerConfig {
23
+ grid?: EzGridActionBarRef;
24
+ tools?: ToolConfig[];
25
+ }
26
+
27
+ export class EzGridActionBar extends EzGridContainer {
28
+ declare config: EzGridActionBarConfig;
29
+ declare el: HTMLElement | null;
30
+
31
+ grid: EzGridActionBarRef | undefined;
32
+ tools: ToolConfig[];
33
+ private _toolInstances: ToolInstance[];
34
+
35
+ constructor(config: EzGridActionBarConfig = {}) {
36
+ super(config);
37
+
38
+ this.grid = config.grid;
39
+ this.tools = Array.isArray(config.tools) ? config.tools : [];
40
+
41
+ this.config.cls = 'ez-grid-titlebar-tools';
42
+ this.config.layout = 'hbox';
43
+
44
+ this._toolInstances = [];
45
+
46
+ this._buildItems();
47
+ this._bindSelection();
48
+ }
49
+
50
+ private _buildItems(): void {
51
+ this._toolInstances = [];
52
+
53
+ this.config.items = this.tools.map(tool => {
54
+ const cfg: any = {
55
+ ...tool,
56
+ disabled: true,
57
+ onClick: () => {
58
+ tool.onClick?.(this.grid);
59
+ }
60
+ };
61
+
62
+ this._toolInstances.push({ tool, cfg });
63
+ return cfg;
64
+ });
65
+ }
66
+
67
+ private _bindSelection(): void {
68
+ if (!this.grid?.on) return;
69
+
70
+ this.grid.on('selectionchange', (e: { keys: string[] }) => {
71
+ this._updateTools(e.keys.length);
72
+ });
73
+
74
+ // initial state
75
+ this._updateTools(this.grid.getSelection?.()?.length ?? 0);
76
+ }
77
+
78
+ private _updateTools(count: number): void {
79
+ for (const { tool, cfg } of this._toolInstances) {
80
+ let enabled = true;
81
+
82
+ if (tool.requiresSelection === true) {
83
+ enabled = count > 0;
84
+ }
85
+
86
+ if (typeof tool.minSelection === 'number') {
87
+ enabled = count >= tool.minSelection;
88
+ }
89
+
90
+ cfg.disabled = !enabled;
91
+ }
92
+
93
+ // force repaint
94
+ if (this.el) {
95
+ this.el.querySelectorAll('[disabled]').forEach(() => {});
96
+ }
97
+ }
98
+ }
@@ -0,0 +1,114 @@
1
+ // EzGrid/title/EzGridTitle.ts
2
+
3
+ import { EzGridContainer, EzGridContainerConfig } from '../EzGridContainer.js';
4
+ import type { TitleBarContext, RowData } from '../types.js';
5
+
6
+ declare const ez: {
7
+ _createElement(config: any): Promise<HTMLElement | null>;
8
+ };
9
+
10
+ export interface EzGridTitleRef {
11
+ on?: (event: string, callback: () => void) => void;
12
+ getSelection: () => string[];
13
+ isSelected: (row: any) => boolean;
14
+ controller?: {
15
+ state?: {
16
+ data: any[];
17
+ };
18
+ };
19
+ }
20
+
21
+ export interface EzGridTitleConfig extends EzGridContainerConfig {
22
+ grid?: EzGridTitleRef;
23
+ title?: string | ((ctx: TitleBarContext) => any);
24
+ ctx?: TitleBarContext;
25
+ showSelectionCount?: boolean;
26
+ }
27
+
28
+ export class EzGridTitle extends EzGridContainer {
29
+ declare config: EzGridTitleConfig;
30
+
31
+ grid: EzGridTitleRef | undefined;
32
+ title: string | ((ctx: TitleBarContext) => any) | undefined;
33
+ ctx: TitleBarContext | undefined;
34
+ showSelectionCount: boolean;
35
+ private _el: HTMLElement | null;
36
+
37
+ constructor(config: EzGridTitleConfig = {}) {
38
+ super(config);
39
+
40
+ this.grid = config.grid;
41
+ this.title = config.title;
42
+ this.ctx = config.ctx;
43
+ this.showSelectionCount = config.showSelectionCount === true;
44
+
45
+ this.config.cls = 'ez-grid-titlebar-title';
46
+ this.config.flex = 1;
47
+
48
+ this._el = null;
49
+ }
50
+
51
+ async render(): Promise<HTMLDivElement> {
52
+ const el = document.createElement('div');
53
+
54
+ el.className = this.config.cls as string;
55
+ el.style.flex = String(this.config.flex);
56
+
57
+ this._el = el;
58
+
59
+ // initial render
60
+ await this._renderTitle();
61
+
62
+ // reactive only if function
63
+ if ((typeof this.title === 'function' || this.showSelectionCount === true) && this.grid?.on) {
64
+ this.grid.on('selectionchange', () => {
65
+ this._renderTitle();
66
+ });
67
+ }
68
+
69
+ return el;
70
+ }
71
+
72
+ private async _renderTitle(): Promise<void> {
73
+ if (!this._el) return;
74
+
75
+ // clear previous content
76
+ this._el.innerHTML = '';
77
+
78
+ let result: any = this.title;
79
+
80
+ if (typeof this.title === 'function') {
81
+ const ctx: TitleBarContext = {
82
+ ...this.ctx!,
83
+ selection: {
84
+ keys: this.grid!.getSelection(),
85
+ rows: this.grid!.controller?.state?.data?.filter(row =>
86
+ this.grid!.isSelected(row)
87
+ ) ?? [],
88
+ count: this.grid!.getSelection().length
89
+ }
90
+ };
91
+
92
+ result = this.title(ctx);
93
+ }
94
+
95
+ // append selection count (sugar)
96
+ if (
97
+ this.showSelectionCount === true &&
98
+ typeof result === 'string'
99
+ ) {
100
+ const count = this.grid!.getSelection().length;
101
+ result = `${result} (${count})`;
102
+ }
103
+
104
+ // simple string
105
+ if (typeof result === 'string') {
106
+ this._el.textContent = result;
107
+ }
108
+ // EZ component
109
+ else if (typeof result === 'object') {
110
+ const child = await ez._createElement(result);
111
+ if (child) this._el.appendChild(child);
112
+ }
113
+ }
114
+ }
@@ -0,0 +1,65 @@
1
+ // ==========================================================
2
+ // EzGridTitleBar
3
+ // ==========================================================
4
+
5
+ .ez-grid-titlebar {
6
+ display: flex;
7
+ align-items: center;
8
+
9
+ min-height: 48px;
10
+ padding: 0 16px;
11
+
12
+ background-color: var(--ez-grid-surface);
13
+ border-bottom: 1px solid var(--ez-grid-border);
14
+
15
+ // ----------------------------------------------------------
16
+ // Title
17
+ // ----------------------------------------------------------
18
+ .ez-grid-titlebar-title {
19
+ display: flex;
20
+ align-items: center;
21
+
22
+ font-size: var(--ez-grid-font-size-lg);
23
+ font-weight: var(--ez-grid-font-weight-semibold);
24
+ color: var(--ez-grid-text);
25
+
26
+ white-space: nowrap;
27
+ overflow: hidden;
28
+ text-overflow: ellipsis;
29
+ }
30
+
31
+ // ----------------------------------------------------------
32
+ // Action bar (tools)
33
+ // ----------------------------------------------------------
34
+ .ez-grid-titlebar-tools {
35
+ display: flex;
36
+ align-items: center;
37
+ gap: 8px;
38
+
39
+ button,
40
+ .ez-button {
41
+ height: 32px;
42
+ padding: 0 12px;
43
+
44
+ font-size: var(--ez-grid-font-size);
45
+ font-weight: var(--ez-grid-font-weight-medium);
46
+
47
+ border-radius: var(--ez-grid-radius-sm);
48
+ background-color: var(--ez-grid-bg);
49
+ border: 1px solid var(--ez-grid-border);
50
+ color: var(--ez-grid-text);
51
+
52
+ cursor: pointer;
53
+ transition: all 0.15s ease;
54
+
55
+ &:hover {
56
+ background-color: var(--ez-grid-surface-hover);
57
+ border-color: var(--ez-grid-border);
58
+ }
59
+
60
+ &:active {
61
+ background-color: var(--ez-grid-surface-active);
62
+ }
63
+ }
64
+ }
65
+ }
@@ -0,0 +1,87 @@
1
+ // EzGrid/title/EzGridTitleBar.ts
2
+
3
+ import './EzGridTitleBar.scss';
4
+ import { EzGridContainer, EzGridContainerConfig } from '../EzGridContainer.js';
5
+ import type { TitleBarContext } from '../types.js';
6
+
7
+ export interface EzGridTitleBarConfig extends EzGridContainerConfig {
8
+ title?: string | ((ctx: TitleBarContext) => any);
9
+ tools?: any[] | ((ctx: TitleBarContext) => any[]);
10
+ bind?: any;
11
+ grid?: any;
12
+ titleOverride?: ((ctx: TitleBarContext) => any) | null;
13
+ toolsOverride?: ((ctx: TitleBarContext) => any[]) | null;
14
+ ctx?: TitleBarContext;
15
+ }
16
+
17
+ export class EzGridTitleBar extends EzGridContainer {
18
+ declare config: EzGridTitleBarConfig;
19
+
20
+ constructor(config: EzGridTitleBarConfig = {}) {
21
+ super(config);
22
+
23
+ this.config.cls = (this.config.cls || '') + ' ez-grid-titlebar';
24
+ this.config.layout = 'hbox';
25
+
26
+ this._buildItems();
27
+ }
28
+
29
+ private _buildItems(): void {
30
+ const {
31
+ title,
32
+ tools,
33
+ bind,
34
+ grid,
35
+ titleOverride,
36
+ toolsOverride,
37
+ ctx
38
+ } = this.config;
39
+
40
+ const items: any[] = [];
41
+
42
+ // -----------------------------
43
+ // TITLE (default / override)
44
+ // -----------------------------
45
+ let resolvedTitle: any = title;
46
+
47
+ if (typeof titleOverride === 'function') {
48
+ resolvedTitle = titleOverride;
49
+ }
50
+
51
+ if (resolvedTitle) {
52
+ items.push({
53
+ eztype: 'EzGridTitle',
54
+ title: resolvedTitle,
55
+ grid,
56
+ ctx,
57
+ showSelectionCount: this.config.grid?.config?.showSelectionCount === true
58
+ });
59
+ }
60
+
61
+ // -----------------------------
62
+ // TOOLS (default / override)
63
+ // -----------------------------
64
+ let resolvedTools: any = tools;
65
+
66
+ if (typeof toolsOverride === 'function' && ctx) {
67
+ resolvedTools = toolsOverride(ctx);
68
+ }
69
+
70
+ if (resolvedTools) {
71
+ // override total del action bar
72
+ if (!Array.isArray(resolvedTools)) {
73
+ items.push(resolvedTools);
74
+ }
75
+ // default EzGridActionBar
76
+ else {
77
+ items.push({
78
+ eztype: 'EzGridActionBar',
79
+ tools: resolvedTools,
80
+ grid
81
+ });
82
+ }
83
+ }
84
+
85
+ this.config.items = items;
86
+ }
87
+ }