canvasframework 0.3.6
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/README.md +554 -0
- package/components/Accordion.js +252 -0
- package/components/AndroidDatePickerDialog.js +398 -0
- package/components/AppBar.js +225 -0
- package/components/Avatar.js +202 -0
- package/components/BottomNavigationBar.js +205 -0
- package/components/BottomSheet.js +374 -0
- package/components/Button.js +225 -0
- package/components/Card.js +193 -0
- package/components/Checkbox.js +180 -0
- package/components/Chip.js +212 -0
- package/components/CircularProgress.js +143 -0
- package/components/ContextMenu.js +116 -0
- package/components/DatePicker.js +257 -0
- package/components/Dialog.js +367 -0
- package/components/Divider.js +125 -0
- package/components/Drawer.js +261 -0
- package/components/FAB.js +270 -0
- package/components/FileUpload.js +315 -0
- package/components/IOSDatePickerWheel.js +268 -0
- package/components/ImageCarousel.js +193 -0
- package/components/ImageComponent.js +223 -0
- package/components/Input.js +309 -0
- package/components/List.js +94 -0
- package/components/ListItem.js +223 -0
- package/components/Modal.js +364 -0
- package/components/MultiSelectDialog.js +206 -0
- package/components/NumberInput.js +271 -0
- package/components/ProgressBar.js +88 -0
- package/components/RadioButton.js +142 -0
- package/components/SearchInput.js +315 -0
- package/components/SegmentedControl.js +202 -0
- package/components/Select.js +199 -0
- package/components/SelectDialog.js +255 -0
- package/components/Slider.js +113 -0
- package/components/Snackbar.js +243 -0
- package/components/Stepper.js +281 -0
- package/components/SwipeableListItem.js +179 -0
- package/components/Switch.js +147 -0
- package/components/Table.js +492 -0
- package/components/Tabs.js +125 -0
- package/components/Text.js +141 -0
- package/components/TextField.js +331 -0
- package/components/Toast.js +236 -0
- package/components/TreeView.js +420 -0
- package/components/Video.js +397 -0
- package/components/View.js +140 -0
- package/components/VirtualList.js +120 -0
- package/core/CanvasFramework.js +1271 -0
- package/core/CanvasWork.js +32 -0
- package/core/Component.js +153 -0
- package/core/LogicWorker.js +25 -0
- package/core/WebGLCanvasAdapter.js +1369 -0
- package/features/Column.js +43 -0
- package/features/Grid.js +47 -0
- package/features/LayoutComponent.js +43 -0
- package/features/OpenStreetMap.js +310 -0
- package/features/Positioned.js +33 -0
- package/features/PullToRefresh.js +328 -0
- package/features/Row.js +40 -0
- package/features/SignaturePad.js +257 -0
- package/features/Skeleton.js +84 -0
- package/features/Stack.js +21 -0
- package/index.js +101 -0
- package/manager/AccessibilityManager.js +107 -0
- package/manager/ErrorHandler.js +59 -0
- package/manager/FeatureFlags.js +60 -0
- package/manager/MemoryManager.js +107 -0
- package/manager/PerformanceMonitor.js +84 -0
- package/manager/SecurityManager.js +54 -0
- package/package.json +28 -0
- package/utils/AnimationEngine.js +428 -0
- package/utils/DataStore.js +403 -0
- package/utils/EventBus.js +407 -0
- package/utils/FetchClient.js +74 -0
- package/utils/FormValidator.js +355 -0
- package/utils/GeoLocationService.js +62 -0
- package/utils/I18n.js +207 -0
- package/utils/IndexedDBManager.js +273 -0
- package/utils/OfflineSyncManager.js +342 -0
- package/utils/QueryBuilder.js +478 -0
- package/utils/SafeArea.js +64 -0
- package/utils/SecureStorage.js +289 -0
- package/utils/StateManager.js +207 -0
- package/utils/WebSocketClient.js +66 -0
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constructeur de requêtes pour filtrage, tri et pagination de données
|
|
3
|
+
* @class
|
|
4
|
+
* @property {Array} data - Données source
|
|
5
|
+
* @property {Array} filters - Filtres appliqués
|
|
6
|
+
* @property {Array} sorts - Tris appliqués
|
|
7
|
+
* @property {number} offset - Offset de pagination
|
|
8
|
+
* @property {number} limit - Limite de résultats
|
|
9
|
+
*/
|
|
10
|
+
class QueryBuilder {
|
|
11
|
+
/**
|
|
12
|
+
* Crée une instance de QueryBuilder
|
|
13
|
+
* @param {Array} data - Données à interroger
|
|
14
|
+
*/
|
|
15
|
+
constructor(data = []) {
|
|
16
|
+
this.data = data;
|
|
17
|
+
this.filters = [];
|
|
18
|
+
this.sorts = [];
|
|
19
|
+
this.offset = 0;
|
|
20
|
+
this.limit = null;
|
|
21
|
+
this.grouped = null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Crée une nouvelle instance
|
|
26
|
+
* @param {Array} data - Données
|
|
27
|
+
* @returns {QueryBuilder} Nouvelle instance
|
|
28
|
+
*/
|
|
29
|
+
static from(data) {
|
|
30
|
+
return new QueryBuilder(data);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Filtre où la valeur est égale
|
|
35
|
+
* @param {string} field - Champ
|
|
36
|
+
* @param {*} value - Valeur
|
|
37
|
+
* @returns {QueryBuilder} Instance
|
|
38
|
+
*/
|
|
39
|
+
where(field, value) {
|
|
40
|
+
this.filters.push({ type: 'equals', field, value });
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Filtre où la valeur est différente
|
|
46
|
+
* @param {string} field - Champ
|
|
47
|
+
* @param {*} value - Valeur
|
|
48
|
+
* @returns {QueryBuilder} Instance
|
|
49
|
+
*/
|
|
50
|
+
whereNot(field, value) {
|
|
51
|
+
this.filters.push({ type: 'not', field, value });
|
|
52
|
+
return this;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Filtre avec opérateur personnalisé
|
|
57
|
+
* @param {string} field - Champ
|
|
58
|
+
* @param {string} operator - Opérateur (>, <, >=, <=, contains, startsWith, endsWith)
|
|
59
|
+
* @param {*} value - Valeur
|
|
60
|
+
* @returns {QueryBuilder} Instance
|
|
61
|
+
*/
|
|
62
|
+
whereOperator(field, operator, value) {
|
|
63
|
+
this.filters.push({ type: 'operator', field, operator, value });
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Filtre où la valeur est dans un tableau
|
|
69
|
+
* @param {string} field - Champ
|
|
70
|
+
* @param {Array} values - Valeurs
|
|
71
|
+
* @returns {QueryBuilder} Instance
|
|
72
|
+
*/
|
|
73
|
+
whereIn(field, values) {
|
|
74
|
+
this.filters.push({ type: 'in', field, values });
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Filtre où la valeur n'est pas dans un tableau
|
|
80
|
+
* @param {string} field - Champ
|
|
81
|
+
* @param {Array} values - Valeurs
|
|
82
|
+
* @returns {QueryBuilder} Instance
|
|
83
|
+
*/
|
|
84
|
+
whereNotIn(field, values) {
|
|
85
|
+
this.filters.push({ type: 'notIn', field, values });
|
|
86
|
+
return this;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Filtre avec une fonction personnalisée
|
|
91
|
+
* @param {Function} predicate - Fonction de filtre
|
|
92
|
+
* @returns {QueryBuilder} Instance
|
|
93
|
+
*/
|
|
94
|
+
whereCustom(predicate) {
|
|
95
|
+
this.filters.push({ type: 'custom', predicate });
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Filtre where null
|
|
101
|
+
* @param {string} field - Champ
|
|
102
|
+
* @returns {QueryBuilder} Instance
|
|
103
|
+
*/
|
|
104
|
+
whereNull(field) {
|
|
105
|
+
this.filters.push({ type: 'null', field });
|
|
106
|
+
return this;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Filtre where not null
|
|
111
|
+
* @param {string} field - Champ
|
|
112
|
+
* @returns {QueryBuilder} Instance
|
|
113
|
+
*/
|
|
114
|
+
whereNotNull(field) {
|
|
115
|
+
this.filters.push({ type: 'notNull', field });
|
|
116
|
+
return this;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Recherche textuelle (like)
|
|
121
|
+
* @param {string} field - Champ
|
|
122
|
+
* @param {string} search - Texte à chercher
|
|
123
|
+
* @param {boolean} [caseSensitive=false] - Sensible à la casse
|
|
124
|
+
* @returns {QueryBuilder} Instance
|
|
125
|
+
*/
|
|
126
|
+
search(field, search, caseSensitive = false) {
|
|
127
|
+
this.filters.push({ type: 'search', field, search, caseSensitive });
|
|
128
|
+
return this;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Tri ascendant
|
|
133
|
+
* @param {string} field - Champ
|
|
134
|
+
* @returns {QueryBuilder} Instance
|
|
135
|
+
*/
|
|
136
|
+
orderBy(field) {
|
|
137
|
+
this.sorts.push({ field, direction: 'asc' });
|
|
138
|
+
return this;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Tri descendant
|
|
143
|
+
* @param {string} field - Champ
|
|
144
|
+
* @returns {QueryBuilder} Instance
|
|
145
|
+
*/
|
|
146
|
+
orderByDesc(field) {
|
|
147
|
+
this.sorts.push({ field, direction: 'desc' });
|
|
148
|
+
return this;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Tri avec comparateur personnalisé
|
|
153
|
+
* @param {Function} compareFn - Fonction de comparaison
|
|
154
|
+
* @returns {QueryBuilder} Instance
|
|
155
|
+
*/
|
|
156
|
+
orderByCustom(compareFn) {
|
|
157
|
+
this.sorts.push({ type: 'custom', compareFn });
|
|
158
|
+
return this;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Limite le nombre de résultats
|
|
163
|
+
* @param {number} limit - Nombre max de résultats
|
|
164
|
+
* @returns {QueryBuilder} Instance
|
|
165
|
+
*/
|
|
166
|
+
take(limit) {
|
|
167
|
+
this.limit = limit;
|
|
168
|
+
return this;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Saute les N premiers résultats
|
|
173
|
+
* @param {number} offset - Nombre à sauter
|
|
174
|
+
* @returns {QueryBuilder} Instance
|
|
175
|
+
*/
|
|
176
|
+
skip(offset) {
|
|
177
|
+
this.offset = offset;
|
|
178
|
+
return this;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Pagination
|
|
183
|
+
* @param {number} page - Numéro de page (1-indexed)
|
|
184
|
+
* @param {number} perPage - Éléments par page
|
|
185
|
+
* @returns {QueryBuilder} Instance
|
|
186
|
+
*/
|
|
187
|
+
paginate(page, perPage) {
|
|
188
|
+
this.offset = (page - 1) * perPage;
|
|
189
|
+
this.limit = perPage;
|
|
190
|
+
return this;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Groupe par champ
|
|
195
|
+
* @param {string} field - Champ de groupement
|
|
196
|
+
* @returns {QueryBuilder} Instance
|
|
197
|
+
*/
|
|
198
|
+
groupBy(field) {
|
|
199
|
+
this.grouped = field;
|
|
200
|
+
return this;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Applique les filtres
|
|
205
|
+
* @param {Array} data - Données
|
|
206
|
+
* @returns {Array} Données filtrées
|
|
207
|
+
* @private
|
|
208
|
+
*/
|
|
209
|
+
applyFilters(data) {
|
|
210
|
+
let result = [...data];
|
|
211
|
+
|
|
212
|
+
for (let filter of this.filters) {
|
|
213
|
+
result = result.filter(item => {
|
|
214
|
+
switch (filter.type) {
|
|
215
|
+
case 'equals':
|
|
216
|
+
return item[filter.field] === filter.value;
|
|
217
|
+
|
|
218
|
+
case 'not':
|
|
219
|
+
return item[filter.field] !== filter.value;
|
|
220
|
+
|
|
221
|
+
case 'operator':
|
|
222
|
+
return this.applyOperator(item[filter.field], filter.operator, filter.value);
|
|
223
|
+
|
|
224
|
+
case 'in':
|
|
225
|
+
return filter.values.includes(item[filter.field]);
|
|
226
|
+
|
|
227
|
+
case 'notIn':
|
|
228
|
+
return !filter.values.includes(item[filter.field]);
|
|
229
|
+
|
|
230
|
+
case 'null':
|
|
231
|
+
return item[filter.field] == null;
|
|
232
|
+
|
|
233
|
+
case 'notNull':
|
|
234
|
+
return item[filter.field] != null;
|
|
235
|
+
|
|
236
|
+
case 'search':
|
|
237
|
+
const fieldValue = String(item[filter.field]);
|
|
238
|
+
const searchValue = filter.search;
|
|
239
|
+
|
|
240
|
+
if (filter.caseSensitive) {
|
|
241
|
+
return fieldValue.includes(searchValue);
|
|
242
|
+
} else {
|
|
243
|
+
return fieldValue.toLowerCase().includes(searchValue.toLowerCase());
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
case 'custom':
|
|
247
|
+
return filter.predicate(item);
|
|
248
|
+
|
|
249
|
+
default:
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return result;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Applique un opérateur
|
|
260
|
+
* @param {*} fieldValue - Valeur du champ
|
|
261
|
+
* @param {string} operator - Opérateur
|
|
262
|
+
* @param {*} value - Valeur de comparaison
|
|
263
|
+
* @returns {boolean} Résultat
|
|
264
|
+
* @private
|
|
265
|
+
*/
|
|
266
|
+
applyOperator(fieldValue, operator, value) {
|
|
267
|
+
switch (operator) {
|
|
268
|
+
case '>': return fieldValue > value;
|
|
269
|
+
case '<': return fieldValue < value;
|
|
270
|
+
case '>=': return fieldValue >= value;
|
|
271
|
+
case '<=': return fieldValue <= value;
|
|
272
|
+
case 'contains': return String(fieldValue).includes(value);
|
|
273
|
+
case 'startsWith': return String(fieldValue).startsWith(value);
|
|
274
|
+
case 'endsWith': return String(fieldValue).endsWith(value);
|
|
275
|
+
default: return false;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Applique les tris
|
|
281
|
+
* @param {Array} data - Données
|
|
282
|
+
* @returns {Array} Données triées
|
|
283
|
+
* @private
|
|
284
|
+
*/
|
|
285
|
+
applySorts(data) {
|
|
286
|
+
if (this.sorts.length === 0) return data;
|
|
287
|
+
|
|
288
|
+
return [...data].sort((a, b) => {
|
|
289
|
+
for (let sort of this.sorts) {
|
|
290
|
+
if (sort.type === 'custom') {
|
|
291
|
+
return sort.compareFn(a, b);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const aVal = a[sort.field];
|
|
295
|
+
const bVal = b[sort.field];
|
|
296
|
+
|
|
297
|
+
let comparison = 0;
|
|
298
|
+
|
|
299
|
+
if (aVal == null && bVal == null) continue;
|
|
300
|
+
if (aVal == null) return 1;
|
|
301
|
+
if (bVal == null) return -1;
|
|
302
|
+
|
|
303
|
+
if (typeof aVal === 'number' && typeof bVal === 'number') {
|
|
304
|
+
comparison = aVal - bVal;
|
|
305
|
+
} else {
|
|
306
|
+
comparison = String(aVal).localeCompare(String(bVal));
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (comparison !== 0) {
|
|
310
|
+
return sort.direction === 'asc' ? comparison : -comparison;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return 0;
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Applique la pagination
|
|
320
|
+
* @param {Array} data - Données
|
|
321
|
+
* @returns {Array} Données paginées
|
|
322
|
+
* @private
|
|
323
|
+
*/
|
|
324
|
+
applyPagination(data) {
|
|
325
|
+
const start = this.offset;
|
|
326
|
+
const end = this.limit ? start + this.limit : data.length;
|
|
327
|
+
return data.slice(start, end);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Applique le groupement
|
|
332
|
+
* @param {Array} data - Données
|
|
333
|
+
* @returns {Object} Données groupées
|
|
334
|
+
* @private
|
|
335
|
+
*/
|
|
336
|
+
applyGrouping(data) {
|
|
337
|
+
const groups = {};
|
|
338
|
+
|
|
339
|
+
for (let item of data) {
|
|
340
|
+
const key = item[this.grouped];
|
|
341
|
+
|
|
342
|
+
if (!groups[key]) {
|
|
343
|
+
groups[key] = [];
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
groups[key].push(item);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return groups;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Exécute la requête et retourne les résultats
|
|
354
|
+
* @returns {Array|Object} Résultats
|
|
355
|
+
*/
|
|
356
|
+
get() {
|
|
357
|
+
let result = this.applyFilters(this.data);
|
|
358
|
+
result = this.applySorts(result);
|
|
359
|
+
|
|
360
|
+
if (this.grouped) {
|
|
361
|
+
return this.applyGrouping(result);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
result = this.applyPagination(result);
|
|
365
|
+
return result;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Retourne le premier résultat
|
|
370
|
+
* @returns {*} Premier élément ou null
|
|
371
|
+
*/
|
|
372
|
+
first() {
|
|
373
|
+
const results = this.take(1).get();
|
|
374
|
+
return results.length > 0 ? results[0] : null;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Compte les résultats
|
|
379
|
+
* @returns {number} Nombre de résultats
|
|
380
|
+
*/
|
|
381
|
+
count() {
|
|
382
|
+
return this.applyFilters(this.data).length;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Vérifie si des résultats existent
|
|
387
|
+
* @returns {boolean} True si résultats
|
|
388
|
+
*/
|
|
389
|
+
exists() {
|
|
390
|
+
return this.count() > 0;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Obtient des valeurs uniques d'un champ
|
|
395
|
+
* @param {string} field - Champ
|
|
396
|
+
* @returns {Array} Valeurs uniques
|
|
397
|
+
*/
|
|
398
|
+
pluck(field) {
|
|
399
|
+
return this.get().map(item => item[field]);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Obtient des valeurs uniques
|
|
404
|
+
* @param {string} field - Champ
|
|
405
|
+
* @returns {Array} Valeurs uniques
|
|
406
|
+
*/
|
|
407
|
+
unique(field) {
|
|
408
|
+
return [...new Set(this.pluck(field))];
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Calcule la somme d'un champ
|
|
413
|
+
* @param {string} field - Champ
|
|
414
|
+
* @returns {number} Somme
|
|
415
|
+
*/
|
|
416
|
+
sum(field) {
|
|
417
|
+
return this.get().reduce((sum, item) => sum + (Number(item[field]) || 0), 0);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Calcule la moyenne d'un champ
|
|
422
|
+
* @param {string} field - Champ
|
|
423
|
+
* @returns {number} Moyenne
|
|
424
|
+
*/
|
|
425
|
+
avg(field) {
|
|
426
|
+
const items = this.get();
|
|
427
|
+
return items.length > 0 ? this.sum(field) / items.length : 0;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Trouve le minimum d'un champ
|
|
432
|
+
* @param {string} field - Champ
|
|
433
|
+
* @returns {number} Minimum
|
|
434
|
+
*/
|
|
435
|
+
min(field) {
|
|
436
|
+
const values = this.pluck(field).filter(v => v != null);
|
|
437
|
+
return values.length > 0 ? Math.min(...values) : null;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Trouve le maximum d'un champ
|
|
442
|
+
* @param {string} field - Champ
|
|
443
|
+
* @returns {number} Maximum
|
|
444
|
+
*/
|
|
445
|
+
max(field) {
|
|
446
|
+
const values = this.pluck(field).filter(v => v != null);
|
|
447
|
+
return values.length > 0 ? Math.max(...values) : null;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Clone la requête
|
|
452
|
+
* @returns {QueryBuilder} Nouveau builder
|
|
453
|
+
*/
|
|
454
|
+
clone() {
|
|
455
|
+
const builder = new QueryBuilder(this.data);
|
|
456
|
+
builder.filters = [...this.filters];
|
|
457
|
+
builder.sorts = [...this.sorts];
|
|
458
|
+
builder.offset = this.offset;
|
|
459
|
+
builder.limit = this.limit;
|
|
460
|
+
builder.grouped = this.grouped;
|
|
461
|
+
return builder;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Réinitialise la requête
|
|
466
|
+
* @returns {QueryBuilder} Instance
|
|
467
|
+
*/
|
|
468
|
+
reset() {
|
|
469
|
+
this.filters = [];
|
|
470
|
+
this.sorts = [];
|
|
471
|
+
this.offset = 0;
|
|
472
|
+
this.limit = null;
|
|
473
|
+
this.grouped = null;
|
|
474
|
+
return this;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
export default QueryBuilder;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gestion des zones sûres (safe area) pour les appareils avec encoches ou barres de navigation
|
|
3
|
+
* @class
|
|
4
|
+
* @static
|
|
5
|
+
* @example
|
|
6
|
+
* SafeArea.detect();
|
|
7
|
+
* SafeArea.applyToComponent(myComponent);
|
|
8
|
+
*/
|
|
9
|
+
class SafeArea {
|
|
10
|
+
/** @type {number} */
|
|
11
|
+
static top = 0;
|
|
12
|
+
/** @type {number} */
|
|
13
|
+
static bottom = 0;
|
|
14
|
+
/** @type {number} */
|
|
15
|
+
static left = 0;
|
|
16
|
+
/** @type {number} */
|
|
17
|
+
static right = 0;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Détecte et définit les marges de safe area en fonction de l'appareil
|
|
21
|
+
* @static
|
|
22
|
+
*/
|
|
23
|
+
static detect() {
|
|
24
|
+
// Détecter l'iPhone X et supérieur (notch)
|
|
25
|
+
const isIPhoneX = /iPhone/.test(navigator.userAgent) && window.screen.height >= 812;
|
|
26
|
+
|
|
27
|
+
if (isIPhoneX) {
|
|
28
|
+
// Portrait
|
|
29
|
+
if (window.innerHeight > window.innerWidth) {
|
|
30
|
+
this.top = 44; // Status bar + notch
|
|
31
|
+
this.bottom = 34; // Home indicator
|
|
32
|
+
} else {
|
|
33
|
+
// Landscape
|
|
34
|
+
this.top = 0;
|
|
35
|
+
this.bottom = 21;
|
|
36
|
+
this.left = 44;
|
|
37
|
+
this.right = 44;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Détecter Android avec notch via CSS
|
|
42
|
+
if (CSS.supports('padding-top: env(safe-area-inset-top)')) {
|
|
43
|
+
const style = getComputedStyle(document.documentElement);
|
|
44
|
+
this.top = parseInt(style.getPropertyValue('env(safe-area-inset-top)')) || 0;
|
|
45
|
+
this.bottom = parseInt(style.getPropertyValue('env(safe-area-inset-bottom)')) || 0;
|
|
46
|
+
this.left = parseInt(style.getPropertyValue('env(safe-area-inset-left)')) || 0;
|
|
47
|
+
this.right = parseInt(style.getPropertyValue('env(safe-area-inset-right)')) || 0;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Applique les marges de safe area à un composant
|
|
53
|
+
* @static
|
|
54
|
+
* @param {Component} component - Composant à ajuster
|
|
55
|
+
*/
|
|
56
|
+
static applyToComponent(component) {
|
|
57
|
+
component.y += this.top;
|
|
58
|
+
component.x += this.left;
|
|
59
|
+
component.width -= (this.left + this.right);
|
|
60
|
+
component.height -= (this.top + this.bottom);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export default SafeArea;
|