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.
Files changed (85) hide show
  1. package/README.md +554 -0
  2. package/components/Accordion.js +252 -0
  3. package/components/AndroidDatePickerDialog.js +398 -0
  4. package/components/AppBar.js +225 -0
  5. package/components/Avatar.js +202 -0
  6. package/components/BottomNavigationBar.js +205 -0
  7. package/components/BottomSheet.js +374 -0
  8. package/components/Button.js +225 -0
  9. package/components/Card.js +193 -0
  10. package/components/Checkbox.js +180 -0
  11. package/components/Chip.js +212 -0
  12. package/components/CircularProgress.js +143 -0
  13. package/components/ContextMenu.js +116 -0
  14. package/components/DatePicker.js +257 -0
  15. package/components/Dialog.js +367 -0
  16. package/components/Divider.js +125 -0
  17. package/components/Drawer.js +261 -0
  18. package/components/FAB.js +270 -0
  19. package/components/FileUpload.js +315 -0
  20. package/components/IOSDatePickerWheel.js +268 -0
  21. package/components/ImageCarousel.js +193 -0
  22. package/components/ImageComponent.js +223 -0
  23. package/components/Input.js +309 -0
  24. package/components/List.js +94 -0
  25. package/components/ListItem.js +223 -0
  26. package/components/Modal.js +364 -0
  27. package/components/MultiSelectDialog.js +206 -0
  28. package/components/NumberInput.js +271 -0
  29. package/components/ProgressBar.js +88 -0
  30. package/components/RadioButton.js +142 -0
  31. package/components/SearchInput.js +315 -0
  32. package/components/SegmentedControl.js +202 -0
  33. package/components/Select.js +199 -0
  34. package/components/SelectDialog.js +255 -0
  35. package/components/Slider.js +113 -0
  36. package/components/Snackbar.js +243 -0
  37. package/components/Stepper.js +281 -0
  38. package/components/SwipeableListItem.js +179 -0
  39. package/components/Switch.js +147 -0
  40. package/components/Table.js +492 -0
  41. package/components/Tabs.js +125 -0
  42. package/components/Text.js +141 -0
  43. package/components/TextField.js +331 -0
  44. package/components/Toast.js +236 -0
  45. package/components/TreeView.js +420 -0
  46. package/components/Video.js +397 -0
  47. package/components/View.js +140 -0
  48. package/components/VirtualList.js +120 -0
  49. package/core/CanvasFramework.js +1271 -0
  50. package/core/CanvasWork.js +32 -0
  51. package/core/Component.js +153 -0
  52. package/core/LogicWorker.js +25 -0
  53. package/core/WebGLCanvasAdapter.js +1369 -0
  54. package/features/Column.js +43 -0
  55. package/features/Grid.js +47 -0
  56. package/features/LayoutComponent.js +43 -0
  57. package/features/OpenStreetMap.js +310 -0
  58. package/features/Positioned.js +33 -0
  59. package/features/PullToRefresh.js +328 -0
  60. package/features/Row.js +40 -0
  61. package/features/SignaturePad.js +257 -0
  62. package/features/Skeleton.js +84 -0
  63. package/features/Stack.js +21 -0
  64. package/index.js +101 -0
  65. package/manager/AccessibilityManager.js +107 -0
  66. package/manager/ErrorHandler.js +59 -0
  67. package/manager/FeatureFlags.js +60 -0
  68. package/manager/MemoryManager.js +107 -0
  69. package/manager/PerformanceMonitor.js +84 -0
  70. package/manager/SecurityManager.js +54 -0
  71. package/package.json +28 -0
  72. package/utils/AnimationEngine.js +428 -0
  73. package/utils/DataStore.js +403 -0
  74. package/utils/EventBus.js +407 -0
  75. package/utils/FetchClient.js +74 -0
  76. package/utils/FormValidator.js +355 -0
  77. package/utils/GeoLocationService.js +62 -0
  78. package/utils/I18n.js +207 -0
  79. package/utils/IndexedDBManager.js +273 -0
  80. package/utils/OfflineSyncManager.js +342 -0
  81. package/utils/QueryBuilder.js +478 -0
  82. package/utils/SafeArea.js +64 -0
  83. package/utils/SecureStorage.js +289 -0
  84. package/utils/StateManager.js +207 -0
  85. package/utils/WebSocketClient.js +66 -0
@@ -0,0 +1,420 @@
1
+ import Component from '../core/Component.js';
2
+
3
+ /**
4
+ * Arborescence pliable et sélectionnable
5
+ * @class
6
+ * @extends Component
7
+ * @property {Array} nodes - Nœuds de l'arbre
8
+ * @property {Set} expandedNodes - Nœuds dépliés
9
+ * @property {Object} selectedNode - Nœud sélectionné
10
+ * @property {number} indentSize - Taille de l'indentation
11
+ * @property {number} nodeHeight - Hauteur d'un nœud
12
+ * @property {boolean} showIcons - Afficher les icônes
13
+ * @property {string} expandIcon - Icône de dépliage
14
+ * @property {string} collapseIcon - Icône de repliage
15
+ * @property {Function} onNodeClick - Callback clic nœud
16
+ * @property {Function} onNodeExpand - Callback dépliage
17
+ */
18
+ class TreeView extends Component {
19
+ /**
20
+ * Crée une instance de TreeView
21
+ * @param {CanvasFramework} framework - Framework parent
22
+ * @param {Object} [options={}] - Options de configuration
23
+ * @param {Array} [options.nodes=[]] - Nœuds [{id, label, children, icon}]
24
+ * @param {Array} [options.defaultExpanded=[]] - IDs des nœuds dépliés par défaut
25
+ * @param {number} [options.indentSize=24] - Taille indentation
26
+ * @param {number} [options.nodeHeight=40] - Hauteur nœud
27
+ * @param {boolean} [options.showIcons=true] - Afficher icônes
28
+ * @param {Function} [options.onNodeClick] - Callback clic
29
+ * @param {Function} [options.onNodeExpand] - Callback dépliage
30
+ */
31
+ constructor(framework, options = {}) {
32
+ super(framework, options);
33
+
34
+ this.nodes = options.nodes || [];
35
+ this.expandedNodes = new Set(options.defaultExpanded || []);
36
+ this.selectedNode = null;
37
+ this.indentSize = options.indentSize || 24;
38
+ this.nodeHeight = options.nodeHeight || 40;
39
+ this.showIcons = options.showIcons !== false;
40
+
41
+ this.onNodeClick = options.onNodeClick || null;
42
+ this.onNodeExpand = options.onNodeExpand || null;
43
+
44
+ const platform = framework.platform;
45
+
46
+ // Styles selon la plateforme
47
+ if (platform === 'material') {
48
+ this.bgColor = '#FFFFFF';
49
+ this.hoverBg = 'rgba(0, 0, 0, 0.04)';
50
+ this.selectedBg = 'rgba(98, 0, 238, 0.08)';
51
+ this.textColor = '#000000';
52
+ this.iconColor = '#666666';
53
+ this.borderColor = 'rgba(0, 0, 0, 0.12)';
54
+ } else {
55
+ this.bgColor = '#FFFFFF';
56
+ this.hoverBg = 'rgba(0, 0, 0, 0.03)';
57
+ this.selectedBg = 'rgba(0, 122, 255, 0.1)';
58
+ this.textColor = '#000000';
59
+ this.iconColor = '#6C6C70';
60
+ this.borderColor = 'rgba(60, 60, 67, 0.29)';
61
+ }
62
+
63
+ // Calculer la hauteur totale
64
+ this.updateHeight();
65
+
66
+ // État de hover
67
+ this.hoveredNode = null;
68
+ }
69
+
70
+ /**
71
+ * Met à jour la hauteur du composant
72
+ * @private
73
+ */
74
+ updateHeight() {
75
+ const flattenedNodes = this.getFlattenedNodes();
76
+ this.height = flattenedNodes.length * this.nodeHeight;
77
+ }
78
+
79
+ /**
80
+ * Aplatit l'arbre en liste
81
+ * @param {Array} [nodes=this.nodes] - Nœuds à aplatir
82
+ * @param {number} [level=0] - Niveau de profondeur
83
+ * @returns {Array} Liste aplatie
84
+ * @private
85
+ */
86
+ getFlattenedNodes(nodes = this.nodes, level = 0) {
87
+ let result = [];
88
+
89
+ for (let node of nodes) {
90
+ result.push({ ...node, level });
91
+
92
+ if (node.children &&
93
+ node.children.length > 0 &&
94
+ this.expandedNodes.has(node.id)) {
95
+ result = result.concat(this.getFlattenedNodes(node.children, level + 1));
96
+ }
97
+ }
98
+
99
+ return result;
100
+ }
101
+
102
+ /**
103
+ * Toggle l'expansion d'un nœud
104
+ * @param {Object} node - Nœud à toggler
105
+ * @private
106
+ */
107
+ toggleNode(node) {
108
+ if (this.expandedNodes.has(node.id)) {
109
+ this.expandedNodes.delete(node.id);
110
+ } else {
111
+ this.expandedNodes.add(node.id);
112
+ if (this.onNodeExpand) {
113
+ this.onNodeExpand(node);
114
+ }
115
+ }
116
+ this.updateHeight();
117
+ }
118
+
119
+ /**
120
+ * Sélectionne un nœud
121
+ * @param {Object} node - Nœud à sélectionner
122
+ * @private
123
+ */
124
+ selectNode(node) {
125
+ this.selectedNode = node;
126
+ if (this.onNodeClick) {
127
+ this.onNodeClick(node);
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Dessine le composant
133
+ * @param {CanvasRenderingContext2D} ctx - Contexte de dessin
134
+ */
135
+ draw(ctx) {
136
+ ctx.save();
137
+
138
+ // Fond
139
+ ctx.fillStyle = this.bgColor;
140
+ ctx.fillRect(this.x, this.y, this.width, this.height);
141
+
142
+ // Bordure
143
+ ctx.strokeStyle = this.borderColor;
144
+ ctx.lineWidth = 1;
145
+ ctx.strokeRect(this.x, this.y, this.width, this.height);
146
+
147
+ // Dessiner les nœuds
148
+ const flattenedNodes = this.getFlattenedNodes();
149
+ flattenedNodes.forEach((node, index) => {
150
+ this.drawNode(ctx, node, index);
151
+ });
152
+
153
+ ctx.restore();
154
+ }
155
+
156
+ /**
157
+ * Dessine un nœud
158
+ * @param {CanvasRenderingContext2D} ctx - Contexte de dessin
159
+ * @param {Object} node - Nœud à dessiner
160
+ * @param {number} index - Index du nœud
161
+ * @private
162
+ */
163
+ drawNode(ctx, node, index) {
164
+ const nodeY = this.y + (index * this.nodeHeight);
165
+ const indent = node.level * this.indentSize;
166
+ const hasChildren = node.children && node.children.length > 0;
167
+ const isExpanded = this.expandedNodes.has(node.id);
168
+ const isSelected = this.selectedNode && this.selectedNode.id === node.id;
169
+ const isHovered = this.hoveredNode && this.hoveredNode.id === node.id;
170
+
171
+ // Fond
172
+ if (isSelected) {
173
+ ctx.fillStyle = this.selectedBg;
174
+ ctx.fillRect(this.x, nodeY, this.width, this.nodeHeight);
175
+ } else if (isHovered) {
176
+ ctx.fillStyle = this.hoverBg;
177
+ ctx.fillRect(this.x, nodeY, this.width, this.nodeHeight);
178
+ }
179
+
180
+ // Bordure inférieure
181
+ ctx.strokeStyle = this.borderColor;
182
+ ctx.lineWidth = 1;
183
+ ctx.beginPath();
184
+ ctx.moveTo(this.x, nodeY + this.nodeHeight);
185
+ ctx.lineTo(this.x + this.width, nodeY + this.nodeHeight);
186
+ ctx.stroke();
187
+
188
+ let currentX = this.x + 16 + indent;
189
+
190
+ // Icône expand/collapse
191
+ if (hasChildren) {
192
+ const iconSize = 16;
193
+ const iconX = currentX;
194
+ const iconY = nodeY + this.nodeHeight / 2;
195
+
196
+ ctx.strokeStyle = this.iconColor;
197
+ ctx.lineWidth = 2;
198
+ ctx.lineCap = 'round';
199
+
200
+ if (isExpanded) {
201
+ // Chevron down
202
+ ctx.beginPath();
203
+ ctx.moveTo(iconX, iconY - 4);
204
+ ctx.lineTo(iconX + iconSize / 2, iconY + 2);
205
+ ctx.lineTo(iconX + iconSize, iconY - 4);
206
+ ctx.stroke();
207
+ } else {
208
+ // Chevron right
209
+ ctx.beginPath();
210
+ ctx.moveTo(iconX + 2, iconY - iconSize / 2);
211
+ ctx.lineTo(iconX + 8, iconY);
212
+ ctx.lineTo(iconX + 2, iconY + iconSize / 2);
213
+ ctx.stroke();
214
+ }
215
+
216
+ currentX += iconSize + 8;
217
+ } else {
218
+ currentX += 24; // Espace pour aligner avec les nœuds parents
219
+ }
220
+
221
+ // Icône du nœud
222
+ if (this.showIcons && node.icon) {
223
+ const iconSize = 20;
224
+ const iconY = nodeY + this.nodeHeight / 2 - iconSize / 2;
225
+
226
+ // Dessiner un simple carré coloré comme icône (peut être personnalisé)
227
+ ctx.fillStyle = node.icon;
228
+ ctx.fillRect(currentX, iconY, iconSize, iconSize);
229
+
230
+ currentX += iconSize + 8;
231
+ } else if (this.showIcons) {
232
+ // Icône par défaut (dossier ou fichier)
233
+ const iconSize = 20;
234
+ const iconY = nodeY + this.nodeHeight / 2 - iconSize / 2;
235
+
236
+ ctx.strokeStyle = this.iconColor;
237
+ ctx.lineWidth = 2;
238
+
239
+ if (hasChildren) {
240
+ // Icône dossier
241
+ ctx.beginPath();
242
+ ctx.moveTo(currentX, iconY + 4);
243
+ ctx.lineTo(currentX, iconY + iconSize);
244
+ ctx.lineTo(currentX + iconSize, iconY + iconSize);
245
+ ctx.lineTo(currentX + iconSize, iconY + 4);
246
+ ctx.lineTo(currentX + iconSize * 0.6, iconY + 4);
247
+ ctx.lineTo(currentX + iconSize * 0.5, iconY);
248
+ ctx.lineTo(currentX, iconY);
249
+ ctx.closePath();
250
+ ctx.stroke();
251
+ } else {
252
+ // Icône fichier
253
+ ctx.beginPath();
254
+ ctx.moveTo(currentX, iconY);
255
+ ctx.lineTo(currentX + iconSize * 0.7, iconY);
256
+ ctx.lineTo(currentX + iconSize, iconY + iconSize * 0.3);
257
+ ctx.lineTo(currentX + iconSize, iconY + iconSize);
258
+ ctx.lineTo(currentX, iconY + iconSize);
259
+ ctx.closePath();
260
+ ctx.stroke();
261
+
262
+ // Coin plié
263
+ ctx.beginPath();
264
+ ctx.moveTo(currentX + iconSize * 0.7, iconY);
265
+ ctx.lineTo(currentX + iconSize * 0.7, iconY + iconSize * 0.3);
266
+ ctx.lineTo(currentX + iconSize, iconY + iconSize * 0.3);
267
+ ctx.stroke();
268
+ }
269
+
270
+ currentX += iconSize + 8;
271
+ }
272
+
273
+ // Label
274
+ ctx.fillStyle = this.textColor;
275
+ ctx.font = '14px -apple-system, BlinkMacSystemFont, Roboto, sans-serif';
276
+ ctx.textAlign = 'left';
277
+ ctx.textBaseline = 'middle';
278
+
279
+ const maxWidth = this.width - (currentX - this.x) - 16;
280
+ let displayText = node.label;
281
+
282
+ // Tronquer si nécessaire
283
+ if (ctx.measureText(displayText).width > maxWidth) {
284
+ while (ctx.measureText(displayText + '...').width > maxWidth && displayText.length > 0) {
285
+ displayText = displayText.slice(0, -1);
286
+ }
287
+ displayText += '...';
288
+ }
289
+
290
+ ctx.fillText(displayText, currentX, nodeY + this.nodeHeight / 2);
291
+ }
292
+
293
+ /**
294
+ * Trouve le nœud à une position
295
+ * @param {number} x - Coordonnée X
296
+ * @param {number} y - Coordonnée Y
297
+ * @returns {Object|null} Nœud trouvé
298
+ * @private
299
+ */
300
+ getNodeAtPosition(x, y) {
301
+ const relY = y - this.y;
302
+ const index = Math.floor(relY / this.nodeHeight);
303
+ const flattenedNodes = this.getFlattenedNodes();
304
+
305
+ if (index >= 0 && index < flattenedNodes.length) {
306
+ return flattenedNodes[index];
307
+ }
308
+
309
+ return null;
310
+ }
311
+
312
+ /**
313
+ * Vérifie si le clic est sur l'icône expand
314
+ * @param {number} x - Coordonnée X
315
+ * @param {Object} node - Nœud
316
+ * @returns {boolean} True si clic sur expand
317
+ * @private
318
+ */
319
+ isClickOnExpand(x, node) {
320
+ const indent = node.level * this.indentSize;
321
+ const expandX = this.x + 16 + indent;
322
+ return x >= expandX && x <= expandX + 16;
323
+ }
324
+
325
+ /**
326
+ * Vérifie si un point est dans les limites
327
+ * @param {number} x - Coordonnée X
328
+ * @param {number} y - Coordonnée Y
329
+ * @returns {boolean} True si le point est dans le composant
330
+ */
331
+ isPointInside(x, y) {
332
+ return x >= this.x &&
333
+ x <= this.x + this.width &&
334
+ y >= this.y &&
335
+ y <= this.y + this.height;
336
+ }
337
+
338
+ /**
339
+ * Gère le clic
340
+ */
341
+ onClick() {
342
+ // Géré dans onPress pour avoir les coordonnées
343
+ }
344
+
345
+ /**
346
+ * Gère la pression
347
+ * @param {number} x - Coordonnée X
348
+ * @param {number} y - Coordonnée Y
349
+ */
350
+ onPress(x, y) {
351
+ const node = this.getNodeAtPosition(x, y);
352
+
353
+ if (node) {
354
+ const hasChildren = node.children && node.children.length > 0;
355
+
356
+ if (hasChildren && this.isClickOnExpand(x, node)) {
357
+ // Clic sur l'icône expand/collapse
358
+ this.toggleNode(node);
359
+ } else {
360
+ // Clic sur le nœud lui-même
361
+ this.selectNode(node);
362
+ }
363
+ }
364
+ }
365
+
366
+ /**
367
+ * Gère le mouvement
368
+ * @param {number} x - Coordonnée X
369
+ * @param {number} y - Coordonnée Y
370
+ */
371
+ onMove(x, y) {
372
+ this.hoveredNode = this.getNodeAtPosition(x, y);
373
+ }
374
+
375
+ /**
376
+ * Développe tous les nœuds
377
+ */
378
+ expandAll() {
379
+ const expandRecursive = (nodes) => {
380
+ for (let node of nodes) {
381
+ if (node.children && node.children.length > 0) {
382
+ this.expandedNodes.add(node.id);
383
+ expandRecursive(node.children);
384
+ }
385
+ }
386
+ };
387
+
388
+ expandRecursive(this.nodes);
389
+ this.updateHeight();
390
+ }
391
+
392
+ /**
393
+ * Replie tous les nœuds
394
+ */
395
+ collapseAll() {
396
+ this.expandedNodes.clear();
397
+ this.updateHeight();
398
+ }
399
+
400
+ /**
401
+ * Trouve un nœud par son ID
402
+ * @param {string} id - ID du nœud
403
+ * @param {Array} [nodes=this.nodes] - Nœuds à chercher
404
+ * @returns {Object|null} Nœud trouvé
405
+ */
406
+ findNodeById(id, nodes = this.nodes) {
407
+ for (let node of nodes) {
408
+ if (node.id === id) return node;
409
+
410
+ if (node.children) {
411
+ const found = this.findNodeById(id, node.children);
412
+ if (found) return found;
413
+ }
414
+ }
415
+
416
+ return null;
417
+ }
418
+ }
419
+
420
+ export default TreeView;