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,43 @@
1
+ import LayoutComponent from './LayoutComponent.js';
2
+
3
+ class Column extends LayoutComponent {
4
+ constructor(framework, options = {}) {
5
+ super(framework, options);
6
+ this.align = options.align || 'start'; // start | center | end | stretch
7
+ }
8
+
9
+ layout() {
10
+ let y = this.y;
11
+ let maxWidth = 0;
12
+
13
+ for (const child of this.children) {
14
+ child.y = y;
15
+
16
+ if (this.align === 'center') {
17
+ child.x = this.x + (this.width - child.width) / 2;
18
+ } else if (this.align === 'end') {
19
+ child.x = this.x + this.width - child.width;
20
+ } else if (this.align === 'stretch') {
21
+ child.x = this.x;
22
+ child.width = this.width;
23
+ } else {
24
+ child.x = this.x;
25
+ }
26
+
27
+ y += child.height + this.spacing;
28
+ maxWidth = Math.max(maxWidth, child.width);
29
+ }
30
+
31
+ this.height = y - this.y - this.spacing;
32
+ if (!this.width) this.width = maxWidth;
33
+
34
+ // Layout récursif automatique des enfants
35
+ for (const child of this.children) {
36
+ if (typeof child.layoutRecursive === 'function') {
37
+ child.layoutRecursive();
38
+ }
39
+ }
40
+ }
41
+ }
42
+
43
+ export default Column;
@@ -0,0 +1,47 @@
1
+ import LayoutComponent from './LayoutComponent.js';
2
+
3
+ class Grid extends LayoutComponent {
4
+ constructor(framework, options = {}) {
5
+ super(framework, options);
6
+ this.columns = options.columns || 2;
7
+ }
8
+
9
+ layout() {
10
+ const cellWidth =
11
+ (this.width - this.spacing * (this.columns - 1)) / this.columns;
12
+
13
+ let x = this.x;
14
+ let y = this.y;
15
+ let col = 0;
16
+ let rowHeight = 0;
17
+
18
+ for (const child of this.children) {
19
+ child.x = x;
20
+ child.y = y;
21
+ child.width = cellWidth;
22
+
23
+ rowHeight = Math.max(rowHeight, child.height);
24
+
25
+ col++;
26
+ if (col === this.columns) {
27
+ col = 0;
28
+ x = this.x;
29
+ y += rowHeight + this.spacing;
30
+ rowHeight = 0;
31
+ } else {
32
+ x += cellWidth + this.spacing;
33
+ }
34
+ }
35
+
36
+ this.height = y - this.y + rowHeight;
37
+
38
+ // Layout récursif automatique des enfants
39
+ for (const child of this.children) {
40
+ if (typeof child.layoutRecursive === 'function') {
41
+ child.layoutRecursive();
42
+ }
43
+ }
44
+ }
45
+ }
46
+
47
+ export default Grid;
@@ -0,0 +1,43 @@
1
+ import Component from '../core/Component.js';
2
+
3
+ export function makeConstraints(component) {
4
+ return {
5
+ minWidth: 0,
6
+ minHeight: 0,
7
+ maxWidth: component.width || Infinity,
8
+ maxHeight: component.height || Infinity
9
+ };
10
+ }
11
+
12
+ class LayoutComponent extends Component {
13
+ constructor(framework, options = {}) {
14
+ super(framework, options);
15
+ this.children = options.children || [];
16
+ this.spacing = options.spacing || 0;
17
+ }
18
+
19
+ add(child) {
20
+ this.children.push(child);
21
+ return child;
22
+ }
23
+
24
+ layout() {
25
+ // À implémenter par Grid / Row / Column
26
+ }
27
+
28
+ // Layout récursif automatique
29
+ layoutRecursive() {
30
+ this.layout(); // calcul des positions/taille des enfants directs
31
+ for (const child of this.children) {
32
+ if (typeof child.layoutRecursive === 'function') {
33
+ child.layoutRecursive(); // appel récursif
34
+ }
35
+ }
36
+ }
37
+
38
+ draw(ctx) {
39
+ // Ne dessine rien par défaut, chaque enfant dessinera le sien
40
+ }
41
+ }
42
+
43
+ export default LayoutComponent;
@@ -0,0 +1,310 @@
1
+ import Component from '../core/Component.js';
2
+
3
+ /**
4
+ * Composant OpenStreetMap pour Canvas avec pan, zoom, multi-touch et markers.
5
+ * @class
6
+ * @extends Component
7
+ * @property {number} lat - Latitude du centre de la carte
8
+ * @property {number} lng - Longitude du centre de la carte
9
+ * @property {number} zoom - Niveau de zoom initial
10
+ * @property {number} tileSize - Taille des tuiles en pixels
11
+ * @property {Object} tiles - Cache des tuiles { "z_x_y": {img: Image, loaded: boolean} }
12
+ * @property {number} offsetX - Décalage X pour le drag
13
+ * @property {number} offsetY - Décalage Y pour le drag
14
+ * @property {boolean} dragging - Si le drag est actif
15
+ * @property {Object|null} lastTouch - Dernier point de touch/mouse {x, y}
16
+ * @property {number} velocityX - Vitesse de glissement X pour inertie
17
+ * @property {number} velocityY - Vitesse de glissement Y pour inertie
18
+ * @property {number} friction - Friction pour inertie
19
+ * @property {number} inertiaThreshold - Seuil minimal pour l'inertie
20
+ * @property {Array} markers - Liste des markers {lat, lng, icon}
21
+ * @property {number} maxZoom - Zoom max
22
+ * @property {number} minZoom - Zoom min
23
+ * @property {number|null} lastTouchDistance - Distance entre deux touches pour pinch zoom
24
+ */
25
+ class OpenStreetMap extends Component {
26
+ /**
27
+ * Crée une instance de OpenStreetMap
28
+ * @param {CanvasFramework} framework - Framework parent
29
+ * @param {Object} [options={}] - Options de configuration
30
+ * @param {number} [options.lat=0] - Latitude initiale
31
+ * @param {number} [options.lng=0] - Longitude initiale
32
+ * @param {number} [options.zoom=2] - Niveau de zoom initial
33
+ * @param {number} [options.tileSize=256] - Taille des tuiles
34
+ * @param {Array} [options.markers=[]] - Liste de markers {lat, lng, icon}
35
+ */
36
+ constructor(framework, options = {}) {
37
+ super(framework, options);
38
+
39
+ this.x = options.x || 0;
40
+ this.y = options.y || 0;
41
+ this.width = options.width || framework.canvas.width;
42
+ this.height = options.height || framework.canvas.height;
43
+
44
+ this.lat = options.lat || 0;
45
+ this.lng = options.lng || 0;
46
+ this.zoom = options.zoom || 2;
47
+ this.tileSize = options.tileSize || 256;
48
+ this.tiles = {};
49
+
50
+ this.offsetX = 0;
51
+ this.offsetY = 0;
52
+ this.dragging = false;
53
+ this.lastTouch = null;
54
+
55
+ this.velocityX = 0;
56
+ this.velocityY = 0;
57
+ this.friction = 0.92;
58
+ this.inertiaThreshold = 0.1;
59
+
60
+ this.markers = options.markers || [];
61
+ this.maxZoom = 19;
62
+ this.minZoom = 1;
63
+
64
+ this.lastTouchDistance = null;
65
+
66
+ // Événements souris
67
+ framework.canvas.addEventListener('mousedown', this.onMouseDown.bind(this));
68
+ framework.canvas.addEventListener('mousemove', this.onMouseMove.bind(this));
69
+ framework.canvas.addEventListener('mouseup', this.onMouseUp.bind(this));
70
+
71
+ // Événements tactiles
72
+ framework.canvas.addEventListener('touchstart', this.onTouchStart.bind(this), { passive: false });
73
+ framework.canvas.addEventListener('touchmove', this.onTouchMove.bind(this), { passive: false });
74
+ framework.canvas.addEventListener('touchend', this.onTouchEnd.bind(this));
75
+ }
76
+
77
+ /**
78
+ * Convertit lat/lng en coordonnées de tuiles
79
+ * @param {number} lat - Latitude
80
+ * @param {number} lng - Longitude
81
+ * @param {number} zoom - Niveau de zoom
82
+ * @returns {Object} {x, y} coordonnées de la tuile
83
+ */
84
+ latLngToTileXY(lat, lng, zoom) {
85
+ const n = Math.pow(2, zoom);
86
+ const x = n * ((lng + 180) / 360);
87
+ const latRad = lat * Math.PI / 180;
88
+ const y = n * (1 - (Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI)) / 2;
89
+ return { x, y };
90
+ }
91
+
92
+ /**
93
+ * Convertit coordonnées de tuiles en lat/lng
94
+ * @param {number} x - Coordonnée X de la tuile
95
+ * @param {number} y - Coordonnée Y de la tuile
96
+ * @param {number} zoom - Niveau de zoom
97
+ * @returns {Object} {lat, lng}
98
+ */
99
+ tileXYToLatLng(x, y, zoom) {
100
+ const n = Math.pow(2, zoom);
101
+ const lng = x / n * 360 - 180;
102
+ const latRad = Math.atan(Math.sinh(Math.PI * (1 - 2 * y / n)));
103
+ const lat = latRad * 180 / Math.PI;
104
+ return { lat, lng };
105
+ }
106
+
107
+ /**
108
+ * Charge une tuile OpenStreetMap
109
+ * @param {number} z - Zoom
110
+ * @param {number} x - Coordonnée X
111
+ * @param {number} y - Coordonnée Y
112
+ * @returns {Object} {img: Image, loaded: boolean}
113
+ */
114
+ loadTile(z, x, y) {
115
+ const key = `${z}_${x}_${y}`;
116
+ if (this.tiles[key]) return this.tiles[key];
117
+ const img = new Image();
118
+ img.crossOrigin = 'anonymous';
119
+ img.src = `https://tile.openstreetmap.org/${z}/${x}/${y}.png`;
120
+ this.tiles[key] = { img, loaded: false };
121
+ img.onload = () => { this.tiles[key].loaded = true; this.framework.markComponentDirty(this); };
122
+ return this.tiles[key];
123
+ }
124
+
125
+ /**
126
+ * Dessine la carte et les markers
127
+ * @param {CanvasRenderingContext2D} ctx - Contexte Canvas
128
+ */
129
+ draw(ctx) {
130
+ ctx.save();
131
+
132
+ // CORRECTION : Arrondir le zoom pour les tuiles
133
+ const zoomLevel = Math.floor(this.zoom);
134
+
135
+ const { x: tileX, y: tileY } = this.latLngToTileXY(this.lat, this.lng, zoomLevel);
136
+ const tilesX = Math.ceil(this.width / this.tileSize) + 2;
137
+ const tilesY = Math.ceil(this.height / this.tileSize) + 2;
138
+ const startX = Math.floor(tileX - tilesX / 2);
139
+ const startY = Math.floor(tileY - tilesY / 2);
140
+
141
+ for (let dx = 0; dx < tilesX; dx++) {
142
+ for (let dy = 0; dy < tilesY; dy++) {
143
+ const tx = startX + dx;
144
+ const ty = startY + dy;
145
+
146
+ // Utiliser zoomLevel arrondi
147
+ const tile = this.loadTile(zoomLevel, tx, ty);
148
+ const px = (tx - tileX) * this.tileSize + this.width / 2 + this.offsetX;
149
+ const py = (ty - tileY) * this.tileSize + this.height / 2 + this.offsetY;
150
+
151
+ if (tile.loaded) {
152
+ ctx.drawImage(tile.img, px, py, this.tileSize, this.tileSize);
153
+ } else {
154
+ ctx.fillStyle = '#E0E0E0';
155
+ ctx.fillRect(px, py, this.tileSize, this.tileSize);
156
+ }
157
+ }
158
+ }
159
+
160
+ // Dessiner les markers avec le zoom arrondi aussi
161
+ for (let marker of this.markers) {
162
+ const { x, y } = this.latLngToTileXY(marker.lat, marker.lng, zoomLevel);
163
+ const screenX = (x - tileX) * this.tileSize + this.width / 2 + this.offsetX;
164
+ const screenY = (y - tileY) * this.tileSize + this.height / 2 + this.offsetY;
165
+
166
+ if (marker.icon) {
167
+ ctx.drawImage(marker.icon, screenX - marker.icon.width / 2, screenY - marker.icon.height, marker.icon.width, marker.icon.height);
168
+ } else {
169
+ ctx.fillStyle = 'red';
170
+ ctx.beginPath();
171
+ ctx.arc(screenX, screenY, 6, 0, 2 * Math.PI);
172
+ ctx.fill();
173
+ }
174
+ }
175
+
176
+ ctx.restore();
177
+ this.updateInertia();
178
+ }
179
+
180
+ /** @private */
181
+ handleDragStart(x, y) {
182
+ this.dragging = true;
183
+ this.lastTouch = { x, y };
184
+ this.velocityX = 0;
185
+ this.velocityY = 0;
186
+ }
187
+
188
+ /** @private */
189
+ handleDragMove(x, y) {
190
+ if (!this.dragging) return;
191
+ const dx = x - this.lastTouch.x;
192
+ const dy = y - this.lastTouch.y;
193
+ this.offsetX += dx;
194
+ this.offsetY += dy;
195
+ this.velocityX = dx;
196
+ this.velocityY = dy;
197
+ this.lastTouch = { x, y };
198
+ this.framework.markComponentDirty(this);
199
+ }
200
+
201
+ /** @private */
202
+ handleDragEnd() {
203
+ this.dragging = false;
204
+ }
205
+
206
+ /** @private */
207
+ updateInertia() {
208
+ if (!this.dragging && (Math.abs(this.velocityX) > this.inertiaThreshold || Math.abs(this.velocityY) > this.inertiaThreshold)) {
209
+ this.offsetX += this.velocityX;
210
+ this.offsetY += this.velocityY;
211
+ this.velocityX *= this.friction;
212
+ this.velocityY *= this.friction;
213
+ this.framework.markComponentDirty(this);
214
+ } else {
215
+ this.velocityX = 0;
216
+ this.velocityY = 0;
217
+ }
218
+ }
219
+
220
+ /** @private */
221
+ updateCenter() {
222
+ const { x: tileX, y: tileY } = this.latLngToTileXY(this.lat, this.lng, this.zoom);
223
+ const newTileX = tileX - this.offsetX / this.tileSize;
224
+ const newTileY = tileY - this.offsetY / this.tileSize;
225
+ const { lat, lng } = this.tileXYToLatLng(newTileX, newTileY, this.zoom);
226
+ this.lat = lat;
227
+ this.lng = lng;
228
+ this.offsetX = 0;
229
+ this.offsetY = 0;
230
+ }
231
+
232
+ /** @param {MouseEvent} e */
233
+ onMouseDown(e) { this.handleDragStart(e.clientX, e.clientY); }
234
+
235
+ /** @param {MouseEvent} e */
236
+ onMouseMove(e) { this.handleDragMove(e.clientX, e.clientY); }
237
+
238
+ /** @param {MouseEvent} e */
239
+ onMouseUp(e) { this.handleDragEnd(); this.updateCenter(); }
240
+
241
+ /** @param {TouchEvent} e */
242
+ onTouchStart(e) {
243
+ e.preventDefault();
244
+ if (e.touches.length === 1) {
245
+ const t = e.touches[0];
246
+ this.handleDragStart(t.clientX, t.clientY);
247
+ } else if (e.touches.length === 2) {
248
+ this.lastTouchDistance = this.getTouchDistance(e.touches[0], e.touches[1]);
249
+ }
250
+ }
251
+
252
+ /** @param {TouchEvent} e */
253
+ onTouchMove(e) {
254
+ e.preventDefault();
255
+ if (e.touches.length === 1) {
256
+ const t = e.touches[0];
257
+ this.handleDragMove(t.clientX, t.clientY);
258
+ } else if (e.touches.length === 2) {
259
+ const newDistance = this.getTouchDistance(e.touches[0], e.touches[1]);
260
+ if (this.lastTouchDistance) {
261
+ const zoomChange = Math.log2(newDistance / this.lastTouchDistance);
262
+ this.zoom += zoomChange;
263
+ this.zoom = Math.min(this.maxZoom, Math.max(this.minZoom, this.zoom));
264
+ this.lastTouchDistance = newDistance;
265
+ this.framework.markComponentDirty(this);
266
+ }
267
+ }
268
+ }
269
+
270
+ /** @param {TouchEvent} e */
271
+ onTouchEnd(e) {
272
+ e.preventDefault();
273
+ this.handleDragEnd();
274
+ this.updateCenter();
275
+ this.lastTouchDistance = null;
276
+ }
277
+
278
+ /**
279
+ * Calcule la distance entre deux touches
280
+ * @param {Touch} t1
281
+ * @param {Touch} t2
282
+ * @returns {number} distance en pixels
283
+ */
284
+ getTouchDistance(t1, t2) {
285
+ const dx = t2.clientX - t1.clientX;
286
+ const dy = t2.clientY - t1.clientY;
287
+ return Math.sqrt(dx*dx + dy*dy);
288
+ }
289
+
290
+ /**
291
+ * Vérifie si un point est dans le composant
292
+ * @param {number} x
293
+ * @param {number} y
294
+ * @returns {boolean}
295
+ */
296
+ isPointInside(x, y) {
297
+ return x >= this.x && x <= this.x + this.width && y >= this.y && y <= this.y + this.height;
298
+ }
299
+
300
+ /**
301
+ * Ajoute un marker à la carte
302
+ * @param {Object} marker - {lat, lng, icon}
303
+ */
304
+ addMarker(marker) {
305
+ this.markers.push(marker);
306
+ this.framework.markComponentDirty(this);
307
+ }
308
+ }
309
+
310
+ export default OpenStreetMap;
@@ -0,0 +1,33 @@
1
+ import Component from '../core/Component.js';
2
+
3
+ class Positioned extends Component {
4
+ constructor(framework, options = {}) {
5
+ super(framework, options);
6
+ this.child = options.child;
7
+ this.left = options.left;
8
+ this.top = options.top;
9
+ this.right = options.right;
10
+ this.bottom = options.bottom;
11
+ }
12
+
13
+ layout() {
14
+ if (!this.child) return;
15
+
16
+ // Position selon les contraintes
17
+ if (this.left !== undefined) this.child.x = this.left;
18
+ else if (this.right !== undefined) this.child.x = this.width - this.child.width - this.right;
19
+ else this.child.x = 0;
20
+
21
+ if (this.top !== undefined) this.child.y = this.top;
22
+ else if (this.bottom !== undefined) this.child.y = this.height - this.child.height - this.bottom;
23
+ else this.child.y = 0;
24
+
25
+ this.child.layout && this.child.layout();
26
+ }
27
+
28
+ draw(ctx) {
29
+ this.child && this.child.draw(ctx);
30
+ }
31
+ }
32
+
33
+ export default Positioned;