canvasframework 0.5.16 → 0.5.18

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 (112) hide show
  1. package/dist/canvasframework.js +2 -0
  2. package/dist/canvasframework.js.LICENSE.txt +1 -0
  3. package/package.json +18 -17
  4. package/components/Accordion.js +0 -265
  5. package/components/AndroidDatePickerDialog.js +0 -406
  6. package/components/AppBar.js +0 -398
  7. package/components/AudioPlayer.js +0 -611
  8. package/components/Avatar.js +0 -202
  9. package/components/Banner.js +0 -342
  10. package/components/BottomNavigationBar.js +0 -433
  11. package/components/BottomSheet.js +0 -234
  12. package/components/Button.js +0 -360
  13. package/components/Camera.js +0 -644
  14. package/components/Card.js +0 -193
  15. package/components/Chart.js +0 -700
  16. package/components/Checkbox.js +0 -166
  17. package/components/Chip.js +0 -212
  18. package/components/CircularProgress.js +0 -327
  19. package/components/ContextMenu.js +0 -116
  20. package/components/DatePicker.js +0 -298
  21. package/components/Dialog.js +0 -337
  22. package/components/Divider.js +0 -125
  23. package/components/Drawer.js +0 -276
  24. package/components/FAB.js +0 -270
  25. package/components/FileUpload.js +0 -315
  26. package/components/FloatedCamera.js +0 -644
  27. package/components/IOSDatePickerWheel.js +0 -430
  28. package/components/ImageCarousel.js +0 -219
  29. package/components/ImageComponent.js +0 -223
  30. package/components/Input.js +0 -831
  31. package/components/InputDatalist.js +0 -723
  32. package/components/InputTags.js +0 -624
  33. package/components/List.js +0 -95
  34. package/components/ListItem.js +0 -269
  35. package/components/Modal.js +0 -364
  36. package/components/MorphingFAB.js +0 -428
  37. package/components/MultiSelectDialog.js +0 -206
  38. package/components/NumberInput.js +0 -271
  39. package/components/PasswordInput.js +0 -462
  40. package/components/ProgressBar.js +0 -88
  41. package/components/QRCodeReader.js +0 -539
  42. package/components/RadioButton.js +0 -151
  43. package/components/SearchInput.js +0 -315
  44. package/components/SegmentedControl.js +0 -357
  45. package/components/Select.js +0 -199
  46. package/components/SelectDialog.js +0 -255
  47. package/components/Slider.js +0 -113
  48. package/components/SliverAppBar.js +0 -139
  49. package/components/Snackbar.js +0 -243
  50. package/components/SpeedDialFAB.js +0 -397
  51. package/components/Stepper.js +0 -281
  52. package/components/SwipeableListItem.js +0 -327
  53. package/components/Switch.js +0 -147
  54. package/components/Table.js +0 -492
  55. package/components/Tabs.js +0 -423
  56. package/components/Text.js +0 -141
  57. package/components/TextField.js +0 -151
  58. package/components/TimePicker.js +0 -934
  59. package/components/Toast.js +0 -236
  60. package/components/TreeView.js +0 -420
  61. package/components/Video.js +0 -397
  62. package/components/View.js +0 -140
  63. package/components/VirtualList.js +0 -120
  64. package/core/CanvasFramework.js +0 -3034
  65. package/core/Component.js +0 -243
  66. package/core/ThemeManager.js +0 -358
  67. package/core/UIBuilder.js +0 -267
  68. package/core/WebGLCanvasAdapter.js +0 -782
  69. package/features/Column.js +0 -43
  70. package/features/Grid.js +0 -47
  71. package/features/LayoutComponent.js +0 -43
  72. package/features/OpenStreetMap.js +0 -310
  73. package/features/Positioned.js +0 -33
  74. package/features/PullToRefresh.js +0 -328
  75. package/features/Row.js +0 -40
  76. package/features/SignaturePad.js +0 -257
  77. package/features/Skeleton.js +0 -193
  78. package/features/Stack.js +0 -21
  79. package/index.js +0 -119
  80. package/manager/AccessibilityManager.js +0 -107
  81. package/manager/ErrorHandler.js +0 -59
  82. package/manager/FeatureFlags.js +0 -60
  83. package/manager/MemoryManager.js +0 -107
  84. package/manager/PerformanceMonitor.js +0 -84
  85. package/manager/SecurityManager.js +0 -54
  86. package/utils/AnimationEngine.js +0 -734
  87. package/utils/CryptoManager.js +0 -303
  88. package/utils/DataStore.js +0 -403
  89. package/utils/DevTools.js +0 -1618
  90. package/utils/DevToolsConsole.js +0 -201
  91. package/utils/EventBus.js +0 -407
  92. package/utils/FetchClient.js +0 -74
  93. package/utils/FirebaseAuth.js +0 -653
  94. package/utils/FirebaseCore.js +0 -246
  95. package/utils/FirebaseFirestore.js +0 -581
  96. package/utils/FirebaseFunctions.js +0 -97
  97. package/utils/FirebaseRealtimeDB.js +0 -498
  98. package/utils/FirebaseStorage.js +0 -612
  99. package/utils/FormValidator.js +0 -355
  100. package/utils/GeoLocationService.js +0 -62
  101. package/utils/I18n.js +0 -207
  102. package/utils/IndexedDBManager.js +0 -273
  103. package/utils/InspectionOverlay.js +0 -308
  104. package/utils/NotificationManager.js +0 -60
  105. package/utils/OfflineSyncManager.js +0 -342
  106. package/utils/PayPalPayment.js +0 -678
  107. package/utils/QueryBuilder.js +0 -478
  108. package/utils/SafeArea.js +0 -64
  109. package/utils/SecureStorage.js +0 -289
  110. package/utils/StateManager.js +0 -207
  111. package/utils/StripePayment.js +0 -552
  112. package/utils/WebSocketClient.js +0 -66
@@ -1,236 +0,0 @@
1
- import Component from '../core/Component.js';
2
- /**
3
- * Toast (notification temporaire)
4
- * @class
5
- * @extends Component
6
- * @property {string} text - Message
7
- * @property {number} duration - Durée d'affichage
8
- * @property {number} fontSize - Taille de police
9
- * @property {number} padding - Padding interne
10
- * @property {number} opacity - Opacité
11
- * @property {string} platform - Plateforme
12
- * @property {boolean} isVisible - Visibilité
13
- * @property {number} targetY - Position Y cible
14
- * @property {number} minWidth - Largeur minimale
15
- * @property {number} maxWidth - Largeur maximale
16
- * @property {boolean} animating - En cours d'animation
17
- */
18
- class Toast extends Component {
19
- /**
20
- * Crée une instance de Toast
21
- * @param {CanvasFramework} framework - Framework parent
22
- * @param {Object} [options={}] - Options de configuration
23
- * @param {string} [options.text=''] - Message
24
- * @param {number} [options.duration=3000] - Durée en ms
25
- * @param {number} [options.x] - Position X (auto-centré)
26
- * @param {number} [options.y] - Position Y (en bas)
27
- */
28
- constructor(framework, options = {}) {
29
- super(framework, {
30
- x: 0,
31
- y: framework.height, // Commence hors écran en bas
32
- width: framework.width,
33
- height: 60, // Hauteur fixe pour le toast
34
- ...options
35
- });
36
-
37
- this.text = options.text || '';
38
- this.duration = options.duration || 3000;
39
- this.fontSize = 16;
40
- this.padding = 20;
41
- this.opacity = 0;
42
- this.platform = framework.platform;
43
- this.isVisible = false;
44
-
45
- // Position cible (en bas, légèrement remonté)
46
- this.targetY = framework.height - 100;
47
-
48
- // Calculer la largeur minimale
49
- this.minWidth = 200;
50
- this.maxWidth = Math.min(600, framework.width - 40);
51
-
52
- // Animation
53
- this.animating = false;
54
-
55
- // NE PAS appeler show() ici - laissé à l'appelant
56
- }
57
-
58
- /**
59
- * Affiche le toast
60
- */
61
- show() {
62
- this.isVisible = true;
63
- this.visible = true;
64
- this.animateIn();
65
-
66
- // Auto-dismiss après la durée
67
- setTimeout(() => {
68
- if (this.isVisible) {
69
- this.hide();
70
- }
71
- }, this.duration);
72
- }
73
-
74
- /**
75
- * Cache le toast
76
- */
77
- hide() {
78
- this.animateOut();
79
- }
80
-
81
- /**
82
- * Anime l'entrée
83
- * @private
84
- */
85
- animateIn() {
86
- if (this.animating) return;
87
- this.animating = true;
88
-
89
- const animate = () => {
90
- this.opacity += 0.1;
91
- this.y -= (this.y - this.targetY) * 0.2;
92
-
93
- if (this.opacity >= 1 && Math.abs(this.y - this.targetY) < 1) {
94
- this.opacity = 1;
95
- this.y = this.targetY;
96
- this.animating = false;
97
- return;
98
- }
99
-
100
- requestAnimationFrame(animate);
101
- };
102
-
103
- animate();
104
- }
105
-
106
- /**
107
- * Anime la sortie
108
- * @private
109
- */
110
- animateOut() {
111
- if (this.animating) return;
112
- this.animating = true;
113
-
114
- const animate = () => {
115
- this.opacity -= 0.1;
116
- this.y += 5;
117
-
118
- if (this.opacity <= 0) {
119
- this.opacity = 0;
120
- this.isVisible = false;
121
- this.visible = false;
122
- this.animating = false;
123
- this.framework.remove(this);
124
- return;
125
- }
126
-
127
- requestAnimationFrame(animate);
128
- };
129
-
130
- animate();
131
- }
132
-
133
- /**
134
- * Dessine le toast
135
- * @param {CanvasRenderingContext2D} ctx - Contexte de dessin
136
- */
137
- draw(ctx) {
138
- if (!this.isVisible || this.opacity <= 0) return;
139
-
140
- ctx.save();
141
- ctx.globalAlpha = this.opacity;
142
-
143
- // Calculer la largeur en fonction du texte
144
- ctx.font = `${this.fontSize}px -apple-system, BlinkMacSystemFont, 'Roboto', sans-serif`;
145
- const textWidth = ctx.measureText(this.text).width;
146
- const toastWidth = Math.min(
147
- this.maxWidth,
148
- Math.max(this.minWidth, textWidth + this.padding * 2)
149
- );
150
-
151
- // Position centrée horizontalement
152
- const toastX = (this.framework.width - toastWidth) / 2;
153
- const toastY = this.y;
154
-
155
- if (this.platform === 'material') {
156
- // Material Toast
157
- ctx.fillStyle = '#323232';
158
- ctx.shadowColor = 'rgba(0, 0, 0, 0.3)';
159
- ctx.shadowBlur = 15;
160
- ctx.shadowOffsetY = 4;
161
- ctx.beginPath();
162
- this.roundRect(ctx, toastX, toastY, toastWidth, this.height, 8);
163
- ctx.fill();
164
- } else {
165
- // Cupertino Toast
166
- ctx.fillStyle = 'rgba(0, 0, 0, 0.85)';
167
- ctx.shadowColor = 'rgba(0, 0, 0, 0.2)';
168
- ctx.shadowBlur = 20;
169
- ctx.shadowOffsetY = 4;
170
- ctx.beginPath();
171
- this.roundRect(ctx, toastX, toastY, toastWidth, this.height, 14);
172
- ctx.fill();
173
- }
174
-
175
- ctx.shadowColor = 'transparent';
176
- ctx.shadowBlur = 0;
177
-
178
- // Texte
179
- ctx.fillStyle = '#FFFFFF';
180
- ctx.font = `${this.fontSize}px -apple-system, BlinkMacSystemFont, 'Roboto', sans-serif`;
181
- ctx.textAlign = 'center';
182
- ctx.textBaseline = 'middle';
183
-
184
- // Tronquer le texte si nécessaire
185
- let displayText = this.text;
186
- if (textWidth > toastWidth - this.padding * 2) {
187
- // Trouver où couper le texte
188
- let truncated = this.text;
189
- for (let i = this.text.length; i > 0; i--) {
190
- truncated = this.text.substring(0, i) + '...';
191
- if (ctx.measureText(truncated).width <= toastWidth - this.padding * 2) {
192
- displayText = truncated;
193
- break;
194
- }
195
- }
196
- }
197
-
198
- ctx.fillText(displayText, toastX + toastWidth / 2, toastY + this.height / 2);
199
-
200
- ctx.restore();
201
- }
202
-
203
- /**
204
- * Dessine un rectangle avec coins arrondis
205
- * @param {CanvasRenderingContext2D} ctx - Contexte de dessin
206
- * @param {number} x - Position X
207
- * @param {number} y - Position Y
208
- * @param {number} width - Largeur
209
- * @param {number} height - Hauteur
210
- * @param {number} radius - Rayon des coins
211
- * @private
212
- */
213
- roundRect(ctx, x, y, width, height, radius) {
214
- ctx.beginPath();
215
- ctx.moveTo(x + radius, y);
216
- ctx.lineTo(x + width - radius, y);
217
- ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
218
- ctx.lineTo(x + width, y + height - radius);
219
- ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
220
- ctx.lineTo(x + radius, y + height);
221
- ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
222
- ctx.lineTo(x, y + radius);
223
- ctx.quadraticCurveTo(x, y, x + radius, y);
224
- ctx.closePath();
225
- }
226
-
227
- /**
228
- * Vérifie si un point est dans les limites
229
- * @returns {boolean} False (non cliquable)
230
- */
231
- isPointInside() {
232
- return false; // Non cliquable
233
- }
234
- }
235
-
236
- export default Toast;
@@ -1,420 +0,0 @@
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;