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,289 @@
1
+ /**
2
+ * Système de stockage sécurisé avec chiffrement AES-GCM
3
+ * @class
4
+ * @example
5
+ * const storage = new SecureStorage('myapp_');
6
+ * await storage.init('password123');
7
+ * await storage.setSecure('token', 'secret-token');
8
+ * const token = await storage.getSecure('token');
9
+ */
10
+ class SecureStorage {
11
+ /**
12
+ * @constructs SecureStorage
13
+ * @param {string} [prefix='app_'] - Préfixe pour les clés localStorage
14
+ */
15
+ constructor(prefix = 'app_') {
16
+ /** @type {string} */
17
+ this.prefix = prefix;
18
+ /** @type {CryptoKey|null} */
19
+ this.encryptionKey = null;
20
+ /** @type {Map<string, any>} */
21
+ this.memoryCache = new Map();
22
+ }
23
+
24
+ /**
25
+ * Initialiser avec une clé de chiffrement
26
+ * @param {string} password - Mot de passe pour générer la clé de chiffrement
27
+ * @returns {Promise<boolean>} True si l'initialisation a réussi
28
+ */
29
+ async init(password) {
30
+ try {
31
+ // Générer une clé de chiffrement à partir du password
32
+ const encoder = new TextEncoder();
33
+ const data = encoder.encode(password);
34
+ const hash = await crypto.subtle.digest('SHA-256', data);
35
+
36
+ this.encryptionKey = await crypto.subtle.importKey(
37
+ 'raw',
38
+ hash,
39
+ { name: 'AES-GCM' },
40
+ false,
41
+ ['encrypt', 'decrypt']
42
+ );
43
+
44
+ return true;
45
+ } catch (error) {
46
+ console.error('Failed to initialize SecureStorage:', error);
47
+ return false;
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Chiffrer des données
53
+ * @param {*} data - Données à chiffrer
54
+ * @returns {Promise<string>} Données chiffrées en base64
55
+ * @throws {Error} Si SecureStorage n'est pas initialisé
56
+ * @private
57
+ */
58
+ async encrypt(data) {
59
+ if (!this.encryptionKey) {
60
+ throw new Error('SecureStorage not initialized. Call init() first.');
61
+ }
62
+
63
+ try {
64
+ const encoder = new TextEncoder();
65
+ const dataBuffer = encoder.encode(JSON.stringify(data));
66
+
67
+ // Générer un IV aléatoire
68
+ const iv = crypto.getRandomValues(new Uint8Array(12));
69
+
70
+ // Chiffrer
71
+ const encrypted = await crypto.subtle.encrypt(
72
+ { name: 'AES-GCM', iv },
73
+ this.encryptionKey,
74
+ dataBuffer
75
+ );
76
+
77
+ // Combiner IV et données chiffrées
78
+ const combined = new Uint8Array(iv.length + encrypted.byteLength);
79
+ combined.set(iv);
80
+ combined.set(new Uint8Array(encrypted), iv.length);
81
+
82
+ // Convertir en base64
83
+ return btoa(String.fromCharCode(...combined));
84
+ } catch (error) {
85
+ console.error('Encryption failed:', error);
86
+ throw error;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Déchiffrer des données
92
+ * @param {string} encryptedData - Données chiffrées en base64
93
+ * @returns {Promise<*>} Données déchiffrées
94
+ * @throws {Error} Si SecureStorage n'est pas initialisé
95
+ * @private
96
+ */
97
+ async decrypt(encryptedData) {
98
+ if (!this.encryptionKey) {
99
+ throw new Error('SecureStorage not initialized. Call init() first.');
100
+ }
101
+
102
+ try {
103
+ // Décoder base64
104
+ const combined = new Uint8Array(
105
+ atob(encryptedData).split('').map(char => char.charCodeAt(0))
106
+ );
107
+
108
+ // Extraire IV et données
109
+ const iv = combined.slice(0, 12);
110
+ const data = combined.slice(12);
111
+
112
+ // Déchiffrer
113
+ const decrypted = await crypto.subtle.decrypt(
114
+ { name: 'AES-GCM', iv },
115
+ this.encryptionKey,
116
+ data
117
+ );
118
+
119
+ // Décoder et parser
120
+ const decoder = new TextDecoder();
121
+ return JSON.parse(decoder.decode(decrypted));
122
+ } catch (error) {
123
+ console.error('Decryption failed:', error);
124
+ throw error;
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Sauvegarder une valeur de manière sécurisée (chiffrée)
130
+ * @param {string} key - Clé de stockage
131
+ * @param {*} value - Valeur à sauvegarder
132
+ * @returns {Promise<boolean>} True si la sauvegarde a réussi
133
+ */
134
+ async setSecure(key, value) {
135
+ try {
136
+ const encrypted = await this.encrypt(value);
137
+ localStorage.setItem(this.prefix + key, encrypted);
138
+ this.memoryCache.set(key, value);
139
+ return true;
140
+ } catch (error) {
141
+ console.error('Failed to save secure data:', error);
142
+ return false;
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Récupérer une valeur de manière sécurisée (déchiffrée)
148
+ * @param {string} key - Clé de stockage
149
+ * @param {*} [defaultValue=null] - Valeur par défaut si la clé n'existe pas
150
+ * @returns {Promise<*>} Valeur déchiffrée ou valeur par défaut
151
+ */
152
+ async getSecure(key, defaultValue = null) {
153
+ try {
154
+ // Vérifier le cache mémoire
155
+ if (this.memoryCache.has(key)) {
156
+ return this.memoryCache.get(key);
157
+ }
158
+
159
+ const encrypted = localStorage.getItem(this.prefix + key);
160
+ if (!encrypted) {
161
+ return defaultValue;
162
+ }
163
+
164
+ const value = await this.decrypt(encrypted);
165
+ this.memoryCache.set(key, value);
166
+ return value;
167
+ } catch (error) {
168
+ console.error('Failed to get secure data:', error);
169
+ return defaultValue;
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Sauvegarder une valeur en clair (pour données non sensibles)
175
+ * @param {string} key - Clé de stockage
176
+ * @param {*} value - Valeur à sauvegarder
177
+ * @returns {boolean} True si la sauvegarde a réussi
178
+ */
179
+ set(key, value) {
180
+ try {
181
+ localStorage.setItem(this.prefix + key, JSON.stringify(value));
182
+ return true;
183
+ } catch (error) {
184
+ console.error('Failed to save data:', error);
185
+ return false;
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Récupérer une valeur en clair
191
+ * @param {string} key - Clé de stockage
192
+ * @param {*} [defaultValue=null] - Valeur par défaut si la clé n'existe pas
193
+ * @returns {*} Valeur ou valeur par défaut
194
+ */
195
+ get(key, defaultValue = null) {
196
+ try {
197
+ const item = localStorage.getItem(this.prefix + key);
198
+ return item ? JSON.parse(item) : defaultValue;
199
+ } catch (error) {
200
+ console.error('Failed to get data:', error);
201
+ return defaultValue;
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Supprimer une clé
207
+ * @param {string} key - Clé à supprimer
208
+ */
209
+ remove(key) {
210
+ localStorage.removeItem(this.prefix + key);
211
+ this.memoryCache.delete(key);
212
+ }
213
+
214
+ /**
215
+ * Vérifier si une clé existe
216
+ * @param {string} key - Clé à vérifier
217
+ * @returns {boolean} True si la clé existe
218
+ */
219
+ has(key) {
220
+ return localStorage.getItem(this.prefix + key) !== null;
221
+ }
222
+
223
+ /**
224
+ * Vider le cache mémoire
225
+ */
226
+ clearCache() {
227
+ this.memoryCache.clear();
228
+ }
229
+
230
+ /**
231
+ * Tout supprimer (données avec le préfixe)
232
+ */
233
+ clear() {
234
+ const keys = Object.keys(localStorage);
235
+ keys.forEach(key => {
236
+ if (key.startsWith(this.prefix)) {
237
+ localStorage.removeItem(key);
238
+ }
239
+ });
240
+ this.clearCache();
241
+ }
242
+
243
+ /**
244
+ * Obtenir toutes les clés
245
+ * @returns {string[]} Liste des clés (sans le préfixe)
246
+ */
247
+ keys() {
248
+ return Object.keys(localStorage)
249
+ .filter(key => key.startsWith(this.prefix))
250
+ .map(key => key.substring(this.prefix.length));
251
+ }
252
+
253
+ /**
254
+ * Exporter toutes les données de manière sécurisée
255
+ * @returns {Promise<string>} Données chiffrées contenant toutes les clés/valeurs
256
+ */
257
+ async exportSecure() {
258
+ const data = {};
259
+ const keys = this.keys();
260
+
261
+ for (const key of keys) {
262
+ data[key] = await this.getSecure(key);
263
+ }
264
+
265
+ return await this.encrypt(data);
266
+ }
267
+
268
+ /**
269
+ * Importer des données de manière sécurisée
270
+ * @param {string} encryptedData - Données chiffrées exportées
271
+ * @returns {Promise<boolean>} True si l'import a réussi
272
+ */
273
+ async importSecure(encryptedData) {
274
+ try {
275
+ const data = await this.decrypt(encryptedData);
276
+
277
+ for (const [key, value] of Object.entries(data)) {
278
+ await this.setSecure(key, value);
279
+ }
280
+
281
+ return true;
282
+ } catch (error) {
283
+ console.error('Failed to import data:', error);
284
+ return false;
285
+ }
286
+ }
287
+ }
288
+
289
+ export default SecureStorage;
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Système de gestion d'état avec historique (undo/redo) et observateurs
3
+ * @class
4
+ * @example
5
+ * const state = new StateManager();
6
+ * state.set('user.name', 'John');
7
+ * const unsubscribe = state.subscribe('user.name', (newValue, oldValue) => {
8
+ * console.log('Name changed:', oldValue, '->', newValue);
9
+ * });
10
+ */
11
+ class StateManager {
12
+ /**
13
+ * @constructs StateManager
14
+ */
15
+ constructor() {
16
+ /** @type {Object} */
17
+ this.state = {};
18
+ /** @type {Map<string, Function[]>} */
19
+ this.listeners = new Map();
20
+ /** @type {Array<Object>} */
21
+ this.history = [];
22
+ /** @type {number} */
23
+ this.historyIndex = -1;
24
+ /** @type {number} */
25
+ this.maxHistory = 50;
26
+ }
27
+
28
+ /**
29
+ * Définir une valeur dans le state
30
+ * @param {string} key - Clé à définir
31
+ * @param {*} value - Valeur à assigner
32
+ * @param {boolean} [saveToHistory=true] - Sauvegarder dans l'historique
33
+ */
34
+ set(key, value, saveToHistory = true) {
35
+ const oldValue = this.state[key];
36
+
37
+ if (saveToHistory && oldValue !== value) {
38
+ this.addToHistory(key, oldValue, value);
39
+ }
40
+
41
+ this.state[key] = value;
42
+ this.notify(key, value, oldValue);
43
+ }
44
+
45
+ /**
46
+ * Récupérer une valeur du state
47
+ * @param {string} key - Clé à récupérer
48
+ * @param {*} [defaultValue=null] - Valeur par défaut si la clé n'existe pas
49
+ * @returns {*} La valeur de la clé ou la valeur par défaut
50
+ */
51
+ get(key, defaultValue = null) {
52
+ return this.state.hasOwnProperty(key) ? this.state[key] : defaultValue;
53
+ }
54
+
55
+ /**
56
+ * Mettre à jour plusieurs valeurs à la fois
57
+ * @param {Object} updates - Objet avec les clés/valeurs à mettre à jour
58
+ * @param {boolean} [saveToHistory=true] - Sauvegarder dans l'historique
59
+ */
60
+ update(updates, saveToHistory = true) {
61
+ Object.keys(updates).forEach(key => {
62
+ this.set(key, updates[key], saveToHistory);
63
+ });
64
+ }
65
+
66
+ /**
67
+ * Supprimer une clé du state
68
+ * @param {string} key - Clé à supprimer
69
+ */
70
+ delete(key) {
71
+ const oldValue = this.state[key];
72
+ delete this.state[key];
73
+ this.notify(key, undefined, oldValue);
74
+ }
75
+
76
+ /**
77
+ * S'abonner aux changements d'une clé
78
+ * @param {string} key - Clé à observer (ou '*' pour toutes les clés)
79
+ * @param {Function} callback - Fonction appelée lors des changements
80
+ * @returns {Function} Fonction de désabonnement
81
+ */
82
+ subscribe(key, callback) {
83
+ if (!this.listeners.has(key)) {
84
+ this.listeners.set(key, []);
85
+ }
86
+ this.listeners.get(key).push(callback);
87
+
88
+ // Retourner une fonction de désabonnement
89
+ return () => {
90
+ const callbacks = this.listeners.get(key);
91
+ const index = callbacks.indexOf(callback);
92
+ if (index > -1) {
93
+ callbacks.splice(index, 1);
94
+ }
95
+ };
96
+ }
97
+
98
+ /**
99
+ * Notifier les observateurs d'un changement
100
+ * @param {string} key - Clé qui a changé
101
+ * @param {*} newValue - Nouvelle valeur
102
+ * @param {*} oldValue - Ancienne valeur
103
+ * @private
104
+ */
105
+ notify(key, newValue, oldValue) {
106
+ if (this.listeners.has(key)) {
107
+ this.listeners.get(key).forEach(callback => {
108
+ callback(newValue, oldValue);
109
+ });
110
+ }
111
+
112
+ // Notifier les listeners globaux (*)
113
+ if (this.listeners.has('*')) {
114
+ this.listeners.get('*').forEach(callback => {
115
+ callback(key, newValue, oldValue);
116
+ });
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Ajouter une modification à l'historique
122
+ * @param {string} key - Clé modifiée
123
+ * @param {*} oldValue - Ancienne valeur
124
+ * @param {*} newValue - Nouvelle valeur
125
+ * @private
126
+ */
127
+ addToHistory(key, oldValue, newValue) {
128
+ // Supprimer l'historique après l'index actuel
129
+ this.history = this.history.slice(0, this.historyIndex + 1);
130
+
131
+ this.history.push({ key, oldValue, newValue, timestamp: Date.now() });
132
+
133
+ // Limiter la taille de l'historique
134
+ if (this.history.length > this.maxHistory) {
135
+ this.history.shift();
136
+ } else {
137
+ this.historyIndex++;
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Annuler la dernière modification
143
+ * @returns {boolean} True si une opération a été annulée
144
+ */
145
+ undo() {
146
+ if (this.historyIndex >= 0) {
147
+ const { key, oldValue } = this.history[this.historyIndex];
148
+ this.set(key, oldValue, false);
149
+ this.historyIndex--;
150
+ return true;
151
+ }
152
+ return false;
153
+ }
154
+
155
+ /**
156
+ * Rétablir la dernière modification annulée
157
+ * @returns {boolean} True si une opération a été rétablie
158
+ */
159
+ redo() {
160
+ if (this.historyIndex < this.history.length - 1) {
161
+ this.historyIndex++;
162
+ const { key, newValue } = this.history[this.historyIndex];
163
+ this.set(key, newValue, false);
164
+ return true;
165
+ }
166
+ return false;
167
+ }
168
+
169
+ /**
170
+ * Réinitialiser complètement le state
171
+ */
172
+ reset() {
173
+ const keys = Object.keys(this.state);
174
+ this.state = {};
175
+ keys.forEach(key => this.notify(key, undefined));
176
+ this.history = [];
177
+ this.historyIndex = -1;
178
+ }
179
+
180
+ /**
181
+ * Exporter le state en JSON
182
+ * @returns {string} Le state sérialisé en JSON
183
+ */
184
+ export() {
185
+ return JSON.stringify(this.state);
186
+ }
187
+
188
+ /**
189
+ * Importer un state depuis du JSON
190
+ * @param {string} jsonState - State sérialisé en JSON
191
+ * @returns {boolean} True si l'import a réussi
192
+ */
193
+ import(jsonState) {
194
+ try {
195
+ const newState = JSON.parse(jsonState);
196
+ Object.keys(newState).forEach(key => {
197
+ this.set(key, newState[key], false);
198
+ });
199
+ return true;
200
+ } catch (error) {
201
+ console.error('Error importing state:', error);
202
+ return false;
203
+ }
204
+ }
205
+ }
206
+
207
+ export default StateManager;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Client WebSocket avec events et JSON automatique
3
+ * @class
4
+ * @property {string} url - URL du serveur WebSocket
5
+ * @property {WebSocket|null} ws - Instance WebSocket
6
+ * @property {Object<string, Function[]>} listeners - Listeners par event ('open','message','close','error')
7
+ */
8
+ class WebSocketClient {
9
+ /**
10
+ * Crée une instance de WebSocketClient
11
+ * @param {string} url - URL du serveur WS
12
+ */
13
+ constructor(url) {
14
+ this.url = url;
15
+ this.ws = null;
16
+ this.listeners = { open: [], message: [], close: [], error: [] };
17
+ }
18
+
19
+ /**
20
+ * Connexion au serveur WebSocket
21
+ */
22
+ connect() {
23
+ this.ws = new WebSocket(this.url);
24
+
25
+ this.ws.onopen = (e) => this.listeners.open.forEach(fn => fn(e));
26
+ this.ws.onmessage = (e) => {
27
+ try {
28
+ const data = JSON.parse(e.data);
29
+ this.listeners.message.forEach(fn => fn(data));
30
+ } catch {
31
+ this.listeners.message.forEach(fn => fn(e.data));
32
+ }
33
+ };
34
+ this.ws.onclose = (e) => this.listeners.close.forEach(fn => fn(e));
35
+ this.ws.onerror = (e) => this.listeners.error.forEach(fn => fn(e));
36
+ }
37
+
38
+ /**
39
+ * Envoie des données
40
+ * @param {any} data - Données JSON ou string
41
+ */
42
+ send(data) {
43
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
44
+ throw new Error("WebSocket not connected");
45
+ }
46
+ this.ws.send(JSON.stringify(data));
47
+ }
48
+
49
+ /**
50
+ * Écoute un event
51
+ * @param {'open'|'message'|'close'|'error'} event - Nom de l'event
52
+ * @param {Function} callback - Callback
53
+ */
54
+ on(event, callback) {
55
+ if (this.listeners[event]) this.listeners[event].push(callback);
56
+ }
57
+
58
+ /**
59
+ * Déconnecte le WebSocket
60
+ */
61
+ disconnect() {
62
+ if (this.ws) this.ws.close();
63
+ }
64
+ }
65
+
66
+ export default WebSocketClient;