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.
- package/components/EzBaseComponent.ts +648 -0
- package/components/EzComponent.ts +89 -0
- package/components/EzInput.module.scss +183 -0
- package/components/EzInput.ts +104 -0
- package/components/EzLabel.ts +22 -0
- package/components/EzOutlet.ts +181 -0
- package/components/HtmlWrapper.ts +305 -0
- package/components/avatar/EzAvatar.module.scss +200 -0
- package/components/avatar/EzAvatar.ts +130 -0
- package/components/badge/EzBadge.module.scss +202 -0
- package/components/badge/EzBadge.ts +77 -0
- package/components/button/EzButton.module.scss +402 -0
- package/components/button/EzButton.ts +175 -0
- package/components/button/EzButtonGroup.ts +48 -0
- package/components/card/EzCard.module.scss +71 -0
- package/components/card/EzCard.ts +120 -0
- package/components/chart/EzBarChart.ts +47 -0
- package/components/chart/EzChart.module.scss +14 -0
- package/components/chart/EzChart.ts +279 -0
- package/components/chart/EzDoughnutChart.ts +47 -0
- package/components/chart/EzLineChart.ts +53 -0
- package/components/checkbox/EzCheckbox.module.scss +145 -0
- package/components/checkbox/EzCheckbox.ts +115 -0
- package/components/dataview/EzDataView.module.scss +115 -0
- package/components/dataview/EzDataView.ts +355 -0
- package/components/dataview/modes/EzDataViewCards.ts +322 -0
- package/components/dataview/modes/EzDataViewGrid.ts +76 -0
- package/components/datepicker/EzDatePicker.module.scss +348 -0
- package/components/datepicker/EzDatePicker.ts +519 -0
- package/components/dialog/EzDialog.module.scss +180 -0
- package/components/dropdown/EzDropdown.module.scss +107 -0
- package/components/dropdown/EzDropdown.ts +235 -0
- package/components/feed/EzActivityFeed.module.scss +90 -0
- package/components/feed/EzActivityFeed.ts +78 -0
- package/components/form/EzForm.ts +364 -0
- package/components/form/EzValidators.test.js +421 -0
- package/components/form/EzValidators.ts +202 -0
- package/components/grid/EzGrid.scss +88 -0
- package/components/grid/EzGrid.ts +1085 -0
- package/components/grid/EzGridContainer.ts +104 -0
- package/components/grid/body/EzGridBody.scss +283 -0
- package/components/grid/body/EzGridBody.ts +549 -0
- package/components/grid/body/EzGridCell.ts +211 -0
- package/components/grid/body/EzGridRow.ts +196 -0
- package/components/grid/filter/EzGridFilters.scss +78 -0
- package/components/grid/filter/EzGridFilters.ts +285 -0
- package/components/grid/footer/EzGridFooter.scss +136 -0
- package/components/grid/footer/EzGridFooter.ts +448 -0
- package/components/grid/header/EzGridHeader.scss +199 -0
- package/components/grid/header/EzGridHeader.ts +430 -0
- package/components/grid/query/EzGridQuery.ts +81 -0
- package/components/grid/state/EzGridColumns.ts +155 -0
- package/components/grid/state/EzGridController.ts +470 -0
- package/components/grid/state/EzGridLifecycle.ts +136 -0
- package/components/grid/state/EzGridNormalizers.test.js +273 -0
- package/components/grid/state/EzGridNormalizers.ts +162 -0
- package/components/grid/state/EzGridParts.ts +233 -0
- package/components/grid/state/EzGridPersistence.ts +140 -0
- package/components/grid/state/EzGridRemote.test.js +573 -0
- package/components/grid/state/EzGridRemote.ts +335 -0
- package/components/grid/state/EzGridSelection.ts +231 -0
- package/components/grid/state/EzGridSort.ts +286 -0
- package/components/grid/title/EzGridActionBar.ts +98 -0
- package/components/grid/title/EzGridTitle.ts +114 -0
- package/components/grid/title/EzGridTitleBar.scss +65 -0
- package/components/grid/title/EzGridTitleBar.ts +87 -0
- package/components/grid/types.ts +607 -0
- package/components/panel/EzPanel.module.scss +133 -0
- package/components/panel/EzPanel.ts +147 -0
- package/components/radio/EzRadio.module.scss +190 -0
- package/components/radio/EzRadio.ts +149 -0
- package/components/select/EzSelect.module.scss +153 -0
- package/components/select/EzSelect.ts +238 -0
- package/components/skeleton/EzSkeleton.module.scss +95 -0
- package/components/skeleton/EzSkeleton.ts +70 -0
- package/components/store/EzStore.ts +344 -0
- package/components/switch/EzSwitch.module.scss +164 -0
- package/components/switch/EzSwitch.ts +117 -0
- package/components/tabs/EzTabPanel.module.scss +181 -0
- package/components/tabs/EzTabPanel.ts +402 -0
- package/components/textarea/EzTextarea.module.scss +131 -0
- package/components/textarea/EzTextarea.ts +161 -0
- package/components/timepicker/EzTimePicker.module.scss +282 -0
- package/components/timepicker/EzTimePicker.ts +540 -0
- package/components/toast/EzToast.module.scss +291 -0
- package/components/tooltip/EzTooltip.module.scss +124 -0
- package/components/tooltip/EzTooltip.ts +153 -0
- package/core/EzComponentTypes.ts +693 -0
- package/core/EzError.ts +63 -0
- package/core/EzModel.ts +268 -0
- package/core/EzTypes.ts +328 -0
- package/core/eventBus.ts +284 -0
- package/core/ez.ts +617 -0
- package/core/loader.ts +725 -0
- package/core/renderer.ts +1010 -0
- package/core/router.ts +490 -0
- package/core/services.ts +124 -0
- package/core/state.ts +142 -0
- package/core/utils.ts +81 -0
- package/package.json +51 -0
- package/services/RouteUI.js +17 -0
- package/services/crypto.js +64 -0
- package/services/dialog.js +222 -0
- package/services/fetchApi.js +63 -0
- package/services/firebase.js +30 -0
- package/services/toast.js +214 -0
- package/template/doc/EzDocs.js +15 -0
- package/template/doc/EzDocs.module.scss +627 -0
- package/template/doc/EzDocsController.js +164 -0
- package/template/doc/data/activityfeed/EzActivityFeedDoc.js +42 -0
- package/template/doc/data/avatar/EzAvatarDoc.js +71 -0
- package/template/doc/data/badge/EzBadgeDoc.js +92 -0
- package/template/doc/data/button/EzButtonDoc.js +77 -0
- package/template/doc/data/buttongroup/EzButtonGroupDoc.js +102 -0
- package/template/doc/data/card/EzCardDoc.js +39 -0
- package/template/doc/data/chart/EzChartDoc.js +60 -0
- package/template/doc/data/checkbox/EzCheckboxDoc.js +67 -0
- package/template/doc/data/component/EzComponentDoc.js +34 -0
- package/template/doc/data/cssmodules/CSSModulesDoc.js +70 -0
- package/template/doc/data/datepicker/EzDatePickerDoc.js +126 -0
- package/template/doc/data/dialog/EzDialogDoc.js +217 -0
- package/template/doc/data/dropdown/EzDropdownDoc.js +178 -0
- package/template/doc/data/form/EzFormDoc.js +90 -0
- package/template/doc/data/grid/EzGridDoc.js +99 -0
- package/template/doc/data/input/EzInputDoc.js +92 -0
- package/template/doc/data/label/EzLabelDoc.js +40 -0
- package/template/doc/data/model/EzModelDoc.js +53 -0
- package/template/doc/data/outlet/EzOutletDoc.js +63 -0
- package/template/doc/data/panel/EzPanelDoc.js +214 -0
- package/template/doc/data/radio/EzRadioDoc.js +174 -0
- package/template/doc/data/router/EzRouterDoc.js +75 -0
- package/template/doc/data/select/EzSelectDoc.js +37 -0
- package/template/doc/data/skeleton/EzSkeletonDoc.js +149 -0
- package/template/doc/data/switch/EzSwitchDoc.js +82 -0
- package/template/doc/data/tabpanel/EzTabPanelDoc.js +44 -0
- package/template/doc/data/textarea/EzTextareaDoc.js +131 -0
- package/template/doc/data/timepicker/EzTimePickerDoc.js +107 -0
- package/template/doc/data/tooltip/EzTooltipDoc.js +193 -0
- package/template/doc/data/validators/EzValidatorsDoc.js +37 -0
- package/template/doc/sidebar/EzDocsSidebar.js +32 -0
- package/template/doc/sidebar/category/EzDocsCategory.js +33 -0
- package/template/doc/sidebar/item/EzDocsComponentItem.js +24 -0
- package/template/doc/viewer/EzDocsViewer.js +18 -0
- package/template/doc/viewer/codepanel/EzDocsCodePanel.js +51 -0
- package/template/doc/viewer/content/EzDocsContent.js +315 -0
- package/template/doc/viewer/header/EzDocsViewerHeader.js +46 -0
- package/template/doc/viewer/showcase/EzDocsShowcase.js +59 -0
- package/template/doc/viewer/showcase/EzDocsShowcaseSection.js +25 -0
- package/template/doc/viewer/showcase/EzDocsVariantItem.js +29 -0
- package/template/doc/welcome/EzDocsWelcome.js +48 -0
- package/themes/ez-theme.scss +179 -0
- package/themes/nature-fresh.scss +169 -0
- package/types/global.d.ts +21 -0
- package/utils/cssModules.js +81 -0
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
// EzGrid/state/EzGridRemote.ts
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
EzGridController,
|
|
5
|
+
SortSnapshot,
|
|
6
|
+
FilterSnapshot,
|
|
7
|
+
RowData,
|
|
8
|
+
RemoteApiConfig,
|
|
9
|
+
RemoteSourceConfig,
|
|
10
|
+
RemoteListenTo,
|
|
11
|
+
RemoteConfigInput,
|
|
12
|
+
TransportParams,
|
|
13
|
+
TransportResult,
|
|
14
|
+
BeforeLoadContext,
|
|
15
|
+
AfterLoadContext,
|
|
16
|
+
ErrorContext,
|
|
17
|
+
FilterMode
|
|
18
|
+
} from '../types.js';
|
|
19
|
+
|
|
20
|
+
declare const ez: {
|
|
21
|
+
_api: {
|
|
22
|
+
request: (url: string, options?: RequestInit) => Promise<unknown>;
|
|
23
|
+
};
|
|
24
|
+
getController: (name: string) => unknown;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export interface NormalizedRemoteConfig {
|
|
28
|
+
api: RemoteApiConfig;
|
|
29
|
+
source: {
|
|
30
|
+
dataPath: string;
|
|
31
|
+
countPath: string;
|
|
32
|
+
};
|
|
33
|
+
filter: FilterMode;
|
|
34
|
+
listenTo: RemoteListenTo;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface RemoteParams {
|
|
38
|
+
page: number;
|
|
39
|
+
pageSize: number;
|
|
40
|
+
sort: SortSnapshot | null;
|
|
41
|
+
filters: FilterSnapshot[] | null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface RemoteResult {
|
|
45
|
+
data: RowData[];
|
|
46
|
+
total: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface EzGridRemoteRef {
|
|
50
|
+
config?: {
|
|
51
|
+
controller?: string;
|
|
52
|
+
};
|
|
53
|
+
controller?: EzGridController;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Re-export types for backwards compatibility
|
|
57
|
+
export type {
|
|
58
|
+
RemoteApiConfig,
|
|
59
|
+
RemoteSourceConfig,
|
|
60
|
+
RemoteListenTo,
|
|
61
|
+
RemoteConfigInput,
|
|
62
|
+
BeforeLoadContext,
|
|
63
|
+
AfterLoadContext,
|
|
64
|
+
ErrorContext
|
|
65
|
+
} from '../types.js';
|
|
66
|
+
|
|
67
|
+
export class EzGridRemote {
|
|
68
|
+
grid: EzGridRemoteRef;
|
|
69
|
+
config: NormalizedRemoteConfig | null;
|
|
70
|
+
private _isLoading: boolean;
|
|
71
|
+
|
|
72
|
+
constructor(grid: EzGridRemoteRef, config: RemoteConfigInput) {
|
|
73
|
+
this.grid = grid;
|
|
74
|
+
this.config = this._normalizeConfig(config);
|
|
75
|
+
this._isLoading = false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ==========================================================
|
|
79
|
+
// Callback Resolution
|
|
80
|
+
// ==========================================================
|
|
81
|
+
|
|
82
|
+
private _resolveCallback<T extends (...args: any[]) => any>(
|
|
83
|
+
callback: T | string | undefined
|
|
84
|
+
): T | null {
|
|
85
|
+
if (typeof callback === 'function') {
|
|
86
|
+
return callback;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (typeof callback === 'string') {
|
|
90
|
+
// Format: "ControllerName:functionName"
|
|
91
|
+
if (callback.includes(':')) {
|
|
92
|
+
const [controllerName, fnName] = callback.split(':');
|
|
93
|
+
const controller = ez.getController(controllerName) as Record<string, unknown>;
|
|
94
|
+
|
|
95
|
+
if (controller && typeof controller[fnName] === 'function') {
|
|
96
|
+
return (controller[fnName] as Function).bind(controller);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
console.warn(`[EzGridRemote] Could not resolve ${callback}`);
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Format: "functionName" - use component's controller from config
|
|
104
|
+
const controllerName = this.grid?.config?.controller;
|
|
105
|
+
if (controllerName) {
|
|
106
|
+
const controller = ez.getController(controllerName) as Record<string, unknown>;
|
|
107
|
+
if (controller && typeof controller[callback] === 'function') {
|
|
108
|
+
return (controller[callback] as Function).bind(controller);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
console.warn(`[EzGridRemote] Could not resolve ${callback} in ${controllerName || 'undefined'} controller`);
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ==========================================================
|
|
120
|
+
// Configuration
|
|
121
|
+
// ==========================================================
|
|
122
|
+
|
|
123
|
+
private _normalizeConfig(config: RemoteConfigInput | null | undefined): NormalizedRemoteConfig | null {
|
|
124
|
+
if (!config) return null;
|
|
125
|
+
|
|
126
|
+
const api: RemoteApiConfig = typeof config.api === 'string'
|
|
127
|
+
? { read: config.api, create: config.api, update: config.api, delete: config.api }
|
|
128
|
+
: config.api || {};
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
api,
|
|
132
|
+
source: {
|
|
133
|
+
dataPath: config.source?.dataPath ?? 'data',
|
|
134
|
+
countPath: config.source?.countPath ?? 'count'
|
|
135
|
+
},
|
|
136
|
+
filter: config.filter ?? 'onInput',
|
|
137
|
+
listenTo: config.listenTo || {}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
isEnabled(): boolean {
|
|
142
|
+
return this.config !== null && !!this.config.api?.read;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ==========================================================
|
|
146
|
+
// URL Building
|
|
147
|
+
// ==========================================================
|
|
148
|
+
|
|
149
|
+
private _buildReadUrl(params: RemoteParams): string {
|
|
150
|
+
const { api } = this.config!;
|
|
151
|
+
let url = api.read || '';
|
|
152
|
+
|
|
153
|
+
// Build query params
|
|
154
|
+
const queryParams = new URLSearchParams();
|
|
155
|
+
|
|
156
|
+
if (params.page != null) {
|
|
157
|
+
queryParams.set('page', String(params.page));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (params.pageSize != null) {
|
|
161
|
+
queryParams.set('pageSize', String(params.pageSize));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Sort params
|
|
165
|
+
if (params.sort) {
|
|
166
|
+
const sorter = Array.isArray(params.sort) ? params.sort[0] : params.sort;
|
|
167
|
+
if (sorter) {
|
|
168
|
+
queryParams.set('sortBy', sorter.property);
|
|
169
|
+
queryParams.set('sortDir', sorter.direction);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Filter params
|
|
174
|
+
if (params.filters && params.filters.length > 0) {
|
|
175
|
+
queryParams.set('filters', JSON.stringify(params.filters));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const queryString = queryParams.toString();
|
|
179
|
+
if (queryString) {
|
|
180
|
+
url += (url.includes('?') ? '&' : '?') + queryString;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return url;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ==========================================================
|
|
187
|
+
// Response Parsing
|
|
188
|
+
// ==========================================================
|
|
189
|
+
|
|
190
|
+
private _getByPath(obj: any, path: string): any {
|
|
191
|
+
if (!obj || !path) return obj;
|
|
192
|
+
|
|
193
|
+
const parts = path.split('.');
|
|
194
|
+
let value = obj;
|
|
195
|
+
|
|
196
|
+
for (const part of parts) {
|
|
197
|
+
if (value == null) return undefined;
|
|
198
|
+
value = value[part];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return value;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private _parseResponse(response: any): RemoteResult {
|
|
205
|
+
const { dataPath, countPath } = this.config!.source;
|
|
206
|
+
|
|
207
|
+
const data = this._getByPath(response, dataPath);
|
|
208
|
+
const total = this._getByPath(response, countPath);
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
data: Array.isArray(data) ? data : [],
|
|
212
|
+
total: typeof total === 'number' ? total : (Array.isArray(data) ? data.length : 0)
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ==========================================================
|
|
217
|
+
// Data Loading
|
|
218
|
+
// ==========================================================
|
|
219
|
+
|
|
220
|
+
async load(params: Partial<RemoteParams> = {}): Promise<RemoteResult> {
|
|
221
|
+
if (!this.isEnabled()) {
|
|
222
|
+
console.warn('[EzGridRemote] Remote mode not configured');
|
|
223
|
+
return { data: [], total: 0 };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (this._isLoading) {
|
|
227
|
+
console.warn('[EzGridRemote] Load already in progress');
|
|
228
|
+
return { data: [], total: 0 };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const controller = this.grid.controller;
|
|
232
|
+
const state = controller?.state;
|
|
233
|
+
const { listenTo } = this.config!;
|
|
234
|
+
|
|
235
|
+
// Build final params
|
|
236
|
+
let finalParams: RemoteParams = {
|
|
237
|
+
page: params.page ?? state?.page ?? 1,
|
|
238
|
+
pageSize: params.pageSize ?? state?.pageSize ?? 25,
|
|
239
|
+
sort: params.sort ?? controller?._lastSortSnapshot ?? null,
|
|
240
|
+
filters: params.filters ?? (controller?._lastFilterSnapshot as any[] | null) ?? null
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// Hook: beforeLoad - can modify params
|
|
244
|
+
const beforeLoadFn = this._resolveCallback(listenTo.beforeLoad);
|
|
245
|
+
if (beforeLoadFn) {
|
|
246
|
+
const modifiedParams = beforeLoadFn(finalParams as any, { grid: this.grid as any, remote: this as any });
|
|
247
|
+
if (modifiedParams) {
|
|
248
|
+
finalParams = modifiedParams as RemoteParams;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
this._isLoading = true;
|
|
253
|
+
controller?.setLoading?.(true);
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
const url = this._buildReadUrl(finalParams);
|
|
257
|
+
|
|
258
|
+
// Use ez._api for the request
|
|
259
|
+
const response = await ez._api.request(url);
|
|
260
|
+
|
|
261
|
+
let result = this._parseResponse(response);
|
|
262
|
+
|
|
263
|
+
// Hook: afterLoad - can modify result or just observe
|
|
264
|
+
const afterLoadFn = this._resolveCallback(listenTo.afterLoad);
|
|
265
|
+
if (afterLoadFn) {
|
|
266
|
+
const ctx = {
|
|
267
|
+
grid: this.grid as any,
|
|
268
|
+
remote: this as any,
|
|
269
|
+
params: finalParams as any,
|
|
270
|
+
response
|
|
271
|
+
};
|
|
272
|
+
const modifiedResult = afterLoadFn(result as any, ctx);
|
|
273
|
+
if (modifiedResult) {
|
|
274
|
+
result = modifiedResult as RemoteResult;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return result;
|
|
279
|
+
|
|
280
|
+
} catch (error) {
|
|
281
|
+
console.error('[EzGridRemote] Load failed:', error);
|
|
282
|
+
|
|
283
|
+
// Hook: onError
|
|
284
|
+
const onErrorFn = this._resolveCallback(listenTo.onError);
|
|
285
|
+
if (onErrorFn) {
|
|
286
|
+
onErrorFn(error as Error, { grid: this.grid as any, remote: this as any, params: finalParams as any });
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
controller?.setError?.(error as Error);
|
|
290
|
+
return { data: [], total: 0 };
|
|
291
|
+
|
|
292
|
+
} finally {
|
|
293
|
+
this._isLoading = false;
|
|
294
|
+
controller?.setLoading?.(false);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ==========================================================
|
|
299
|
+
// CRUD Operations (future)
|
|
300
|
+
// ==========================================================
|
|
301
|
+
|
|
302
|
+
async create(data: any): Promise<any> {
|
|
303
|
+
if (!this.config?.api?.create) {
|
|
304
|
+
throw new Error('Create endpoint not configured');
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return ez._api.request(this.config.api.create, {
|
|
308
|
+
method: 'POST',
|
|
309
|
+
body: data
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
async update(id: string | number, data: any): Promise<any> {
|
|
314
|
+
if (!this.config?.api?.update) {
|
|
315
|
+
throw new Error('Update endpoint not configured');
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const url = `${this.config.api.update}/${id}`;
|
|
319
|
+
return ez._api.request(url, {
|
|
320
|
+
method: 'PUT',
|
|
321
|
+
body: data
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
async delete(id: string | number): Promise<any> {
|
|
326
|
+
if (!this.config?.api?.delete) {
|
|
327
|
+
throw new Error('Delete endpoint not configured');
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const url = `${this.config.api.delete}/${id}`;
|
|
331
|
+
return ez._api.request(url, {
|
|
332
|
+
method: 'DELETE'
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
// EzGrid/state/EzGridSelection.ts
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
SelectionMode,
|
|
5
|
+
SelectionConfig,
|
|
6
|
+
SelectionSnapshot,
|
|
7
|
+
RowData,
|
|
8
|
+
RowKeyFn,
|
|
9
|
+
RowCallback
|
|
10
|
+
} from '../types.js';
|
|
11
|
+
|
|
12
|
+
export interface EzGridSelectionRef {
|
|
13
|
+
config: {
|
|
14
|
+
onRowClick?: RowCallback | string;
|
|
15
|
+
onRowDoubleClick?: RowCallback | string;
|
|
16
|
+
rowKey?: string | RowKeyFn;
|
|
17
|
+
};
|
|
18
|
+
controller?: {
|
|
19
|
+
state?: {
|
|
20
|
+
data: RowData[];
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
_onSelectionChanged: () => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Re-export types for backwards compatibility
|
|
27
|
+
export type { SelectionMode, SelectionConfig, SelectionSnapshot } from '../types.js';
|
|
28
|
+
|
|
29
|
+
export class EzGridSelection {
|
|
30
|
+
grid: EzGridSelectionRef;
|
|
31
|
+
enabled: boolean;
|
|
32
|
+
mode: SelectionMode;
|
|
33
|
+
toggle: boolean;
|
|
34
|
+
selected: Set<string>;
|
|
35
|
+
lastIndex: number | null;
|
|
36
|
+
|
|
37
|
+
constructor(grid: EzGridSelectionRef, config: boolean | SelectionConfig | null | undefined) {
|
|
38
|
+
this.grid = grid;
|
|
39
|
+
this.mode = 'single';
|
|
40
|
+
this.toggle = false;
|
|
41
|
+
this.selected = new Set();
|
|
42
|
+
this.lastIndex = null;
|
|
43
|
+
|
|
44
|
+
if (config === false) {
|
|
45
|
+
this.enabled = false;
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
this.enabled = true;
|
|
50
|
+
|
|
51
|
+
const hasRowActions =
|
|
52
|
+
typeof grid.config.onRowClick === 'function' ||
|
|
53
|
+
typeof grid.config.onRowDoubleClick === 'function';
|
|
54
|
+
|
|
55
|
+
let mode: SelectionMode = 'single';
|
|
56
|
+
let toggle = false;
|
|
57
|
+
let selected = new Set<string>();
|
|
58
|
+
|
|
59
|
+
if (config === true || config == null) {
|
|
60
|
+
// defaults
|
|
61
|
+
} else if (typeof config === 'object') {
|
|
62
|
+
mode = config.mode === 'multi' ? 'multi' : 'single';
|
|
63
|
+
toggle = !!config.toggle;
|
|
64
|
+
|
|
65
|
+
if (Array.isArray(config.selected)) {
|
|
66
|
+
selected = new Set(config.selected.map(String));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (mode === 'single' && toggle && hasRowActions) {
|
|
71
|
+
if (import.meta.env.DEV) {
|
|
72
|
+
console.warn(
|
|
73
|
+
'[EzGrid] selection.toggle=true was ignore due ' +
|
|
74
|
+
'onRowClick/onRowDoubleClick. in single mode, the selection ' +
|
|
75
|
+
'represent active context and cant be empty.'
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
toggle = false;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
this.mode = mode;
|
|
82
|
+
this.toggle = toggle;
|
|
83
|
+
this.selected = selected;
|
|
84
|
+
this.lastIndex = null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
isEnabled(): boolean {
|
|
88
|
+
return this.enabled === true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
isSelected(row: any): boolean {
|
|
92
|
+
if (!this.enabled) return false;
|
|
93
|
+
|
|
94
|
+
const key = this._getRowKeyString(row);
|
|
95
|
+
if (key == null) return false;
|
|
96
|
+
|
|
97
|
+
return this.selected.has(key);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
getSelection(): string[] {
|
|
101
|
+
if (!this.enabled) return [];
|
|
102
|
+
return Array.from(this.selected);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
clear(): void {
|
|
106
|
+
if (!this.enabled) return;
|
|
107
|
+
|
|
108
|
+
this.selected.clear();
|
|
109
|
+
this.lastIndex = null;
|
|
110
|
+
this._changed();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
toggleRow(row: any, index: number): void {
|
|
114
|
+
if (!this.enabled) return;
|
|
115
|
+
|
|
116
|
+
const key = this._getRowKeyString(row);
|
|
117
|
+
if (key == null) return;
|
|
118
|
+
|
|
119
|
+
// Single mode: always select, toggle only if enabled
|
|
120
|
+
if (this.mode !== 'multi') {
|
|
121
|
+
if (this.selected.has(key) && this.toggle) {
|
|
122
|
+
this.selected.delete(key);
|
|
123
|
+
} else {
|
|
124
|
+
this.selected.clear();
|
|
125
|
+
this.selected.add(key);
|
|
126
|
+
}
|
|
127
|
+
this.lastIndex = index;
|
|
128
|
+
this._changed();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Multi mode: always allow toggle (deselect on re-click)
|
|
133
|
+
if (this.selected.has(key)) {
|
|
134
|
+
this.selected.delete(key);
|
|
135
|
+
} else {
|
|
136
|
+
this.selected.add(key);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
this.lastIndex = index;
|
|
140
|
+
this._changed();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
selectRange(toIndex: number): void {
|
|
144
|
+
if (!this.enabled) return;
|
|
145
|
+
if (this.mode !== 'multi') return;
|
|
146
|
+
if (this.lastIndex == null) return;
|
|
147
|
+
|
|
148
|
+
const data = this.grid.controller?.state?.data || [];
|
|
149
|
+
const from = Math.min(this.lastIndex, toIndex);
|
|
150
|
+
const to = Math.max(this.lastIndex, toIndex);
|
|
151
|
+
|
|
152
|
+
for (let i = from; i <= to; i++) {
|
|
153
|
+
const row = data[i];
|
|
154
|
+
const key = this._getRowKeyString(row);
|
|
155
|
+
if (key != null) this.selected.add(key);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
this._changed();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
selectAll(): void {
|
|
162
|
+
if (!this.enabled) return;
|
|
163
|
+
|
|
164
|
+
const data = this.grid.controller?.state?.data || [];
|
|
165
|
+
this.selected.clear();
|
|
166
|
+
|
|
167
|
+
for (const row of data) {
|
|
168
|
+
const key = this._getRowKeyString(row);
|
|
169
|
+
if (key != null) this.selected.add(key);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
this.lastIndex = null;
|
|
173
|
+
this._changed();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
isAllSelected(): boolean {
|
|
177
|
+
const data = this.grid.controller?.state?.data || [];
|
|
178
|
+
if (!data.length) return false;
|
|
179
|
+
|
|
180
|
+
let count = 0;
|
|
181
|
+
for (const row of data) {
|
|
182
|
+
const key = this._getRowKey(row);
|
|
183
|
+
if (key != null) count++;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return this.selected.size === count;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
snapshot(): SelectionSnapshot | null {
|
|
190
|
+
if (!this.enabled) return null;
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
mode: this.mode,
|
|
194
|
+
selected: Array.from(this.selected),
|
|
195
|
+
lastIndex: this.lastIndex
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
restore(snapshot: SelectionSnapshot | null): void {
|
|
200
|
+
if (!this.enabled || !snapshot) return;
|
|
201
|
+
|
|
202
|
+
// WARNING: Selection mode MUST come from grid config, not from persistence.
|
|
203
|
+
// Restoring `mode` from snapshot can silently downgrade multi-selection
|
|
204
|
+
// into single-selection when statefulPersist is enabled.
|
|
205
|
+
// Only derived state is allowed to be restored here.
|
|
206
|
+
this.selected = new Set(
|
|
207
|
+
Array.isArray(snapshot.selected)
|
|
208
|
+
? snapshot.selected.map(String)
|
|
209
|
+
: []
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
this.lastIndex =
|
|
213
|
+
typeof snapshot.lastIndex === 'number'
|
|
214
|
+
? snapshot.lastIndex
|
|
215
|
+
: null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private _changed(): void {
|
|
219
|
+
this.grid._onSelectionChanged();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
private _getRowKey(row: any): string | number | null {
|
|
223
|
+
const key = this.grid.config.rowKey;
|
|
224
|
+
return typeof key === 'function' ? key(row) : row?.[key as string];
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private _getRowKeyString(row: any): string | null {
|
|
228
|
+
const key = this._getRowKey(row);
|
|
229
|
+
return key == null ? null : String(key);
|
|
230
|
+
}
|
|
231
|
+
}
|