canvasframework 0.5.18 → 0.5.20
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 +30 -0
- package/components/Accordion.js +265 -0
- package/components/AndroidDatePickerDialog.js +406 -0
- package/components/AppBar.js +398 -0
- package/components/AudioPlayer.js +611 -0
- package/components/Avatar.js +202 -0
- package/components/Banner.js +342 -0
- package/components/BottomNavigationBar.js +433 -0
- package/components/BottomSheet.js +234 -0
- package/components/Button.js +358 -0
- package/components/Camera.js +644 -0
- package/components/Card.js +193 -0
- package/components/Chart.js +700 -0
- package/components/Checkbox.js +166 -0
- package/components/Chip.js +212 -0
- package/components/CircularProgress.js +327 -0
- package/components/ContextMenu.js +116 -0
- package/components/DatePicker.js +298 -0
- package/components/Dialog.js +337 -0
- package/components/Divider.js +125 -0
- package/components/Drawer.js +276 -0
- package/components/FAB.js +270 -0
- package/components/FileUpload.js +315 -0
- package/components/FloatedCamera.js +644 -0
- package/components/IOSDatePickerWheel.js +430 -0
- package/components/ImageCarousel.js +219 -0
- package/components/ImageComponent.js +223 -0
- package/components/Input.js +831 -0
- package/components/InputDatalist.js +723 -0
- package/components/InputTags.js +624 -0
- package/components/List.js +95 -0
- package/components/ListItem.js +269 -0
- package/components/Modal.js +364 -0
- package/components/MorphingFAB.js +428 -0
- package/components/MultiSelectDialog.js +206 -0
- package/components/NumberInput.js +271 -0
- package/components/PasswordInput.js +462 -0
- package/components/ProgressBar.js +88 -0
- package/components/QRCodeReader.js +539 -0
- package/components/RadioButton.js +151 -0
- package/components/SearchInput.js +315 -0
- package/components/SegmentedControl.js +357 -0
- package/components/Select.js +199 -0
- package/components/SelectDialog.js +255 -0
- package/components/Slider.js +113 -0
- package/components/SliverAppBar.js +139 -0
- package/components/Snackbar.js +243 -0
- package/components/SpeedDialFAB.js +397 -0
- package/components/Stepper.js +281 -0
- package/components/SwipeableListItem.js +327 -0
- package/components/Switch.js +147 -0
- package/components/Table.js +492 -0
- package/components/Tabs.js +423 -0
- package/components/Text.js +141 -0
- package/components/TextField.js +151 -0
- package/components/TimePicker.js +934 -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 +3045 -0
- package/core/Component.js +243 -0
- package/core/ThemeManager.js +358 -0
- package/core/UIBuilder.js +267 -0
- package/core/WebGLCanvasAdapter.js +782 -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 +193 -0
- package/features/Stack.js +21 -0
- package/index.js +119 -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 +22 -16
- package/utils/AnimationEngine.js +734 -0
- package/utils/CryptoManager.js +303 -0
- package/utils/DataStore.js +403 -0
- package/utils/DevTools.js +1618 -0
- package/utils/DevToolsConsole.js +201 -0
- package/utils/EventBus.js +407 -0
- package/utils/FetchClient.js +74 -0
- package/utils/FirebaseAuth.js +653 -0
- package/utils/FirebaseCore.js +246 -0
- package/utils/FirebaseFirestore.js +581 -0
- package/utils/FirebaseFunctions.js +97 -0
- package/utils/FirebaseRealtimeDB.js +498 -0
- package/utils/FirebaseStorage.js +612 -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/InspectionOverlay.js +308 -0
- package/utils/NotificationManager.js +60 -0
- package/utils/OfflineSyncManager.js +342 -0
- package/utils/PayPalPayment.js +678 -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/StripePayment.js +552 -0
- package/utils/WebSocketClient.js +66 -0
- package/dist/canvasframework.js +0 -2
- package/dist/canvasframework.js.LICENSE.txt +0 -1
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gestionnaire IndexedDB pour stockage de grandes quantités de données
|
|
3
|
+
* @class
|
|
4
|
+
* @property {string} dbName - Nom de la base
|
|
5
|
+
* @property {number} version - Version
|
|
6
|
+
* @property {IDBDatabase} db - Instance DB
|
|
7
|
+
*/
|
|
8
|
+
class IndexedDBManager {
|
|
9
|
+
/**
|
|
10
|
+
* Crée une instance de IndexedDBManager
|
|
11
|
+
* @param {string} dbName - Nom de la base
|
|
12
|
+
* @param {number} [version=1] - Version
|
|
13
|
+
*/
|
|
14
|
+
constructor(dbName, version = 1) {
|
|
15
|
+
this.dbName = dbName;
|
|
16
|
+
this.version = version;
|
|
17
|
+
this.db = null;
|
|
18
|
+
this.stores = new Map();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Initialise la base de données
|
|
23
|
+
* @param {Object} schema - Schéma {storeName: {keyPath, indexes}}
|
|
24
|
+
* @returns {Promise<IndexedDBManager>} Instance
|
|
25
|
+
*/
|
|
26
|
+
async init(schema) {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const request = indexedDB.open(this.dbName, this.version);
|
|
29
|
+
|
|
30
|
+
request.onerror = () => reject(request.error);
|
|
31
|
+
request.onsuccess = () => {
|
|
32
|
+
this.db = request.result;
|
|
33
|
+
resolve(this);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
request.onupgradeneeded = (event) => {
|
|
37
|
+
const db = event.target.result;
|
|
38
|
+
|
|
39
|
+
for (let storeName in schema) {
|
|
40
|
+
const config = schema[storeName];
|
|
41
|
+
|
|
42
|
+
// Créer l'object store s'il n'existe pas
|
|
43
|
+
if (!db.objectStoreNames.contains(storeName)) {
|
|
44
|
+
const store = db.createObjectStore(storeName, {
|
|
45
|
+
keyPath: config.keyPath || 'id',
|
|
46
|
+
autoIncrement: config.autoIncrement || false
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Créer les indexes
|
|
50
|
+
if (config.indexes) {
|
|
51
|
+
for (let indexName in config.indexes) {
|
|
52
|
+
const indexConfig = config.indexes[indexName];
|
|
53
|
+
store.createIndex(
|
|
54
|
+
indexName,
|
|
55
|
+
indexConfig.keyPath || indexName,
|
|
56
|
+
{ unique: indexConfig.unique || false }
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Ajoute un élément
|
|
68
|
+
* @param {string} storeName - Nom du store
|
|
69
|
+
* @param {*} item - Élément à ajouter
|
|
70
|
+
* @returns {Promise<*>} Clé générée
|
|
71
|
+
*/
|
|
72
|
+
async add(storeName, item) {
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
const transaction = this.db.transaction([storeName], 'readwrite');
|
|
75
|
+
const store = transaction.objectStore(storeName);
|
|
76
|
+
const request = store.add(item);
|
|
77
|
+
|
|
78
|
+
request.onsuccess = () => resolve(request.result);
|
|
79
|
+
request.onerror = () => reject(request.error);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Met à jour un élément
|
|
85
|
+
* @param {string} storeName - Nom du store
|
|
86
|
+
* @param {*} item - Élément à mettre à jour
|
|
87
|
+
* @returns {Promise<*>} Clé
|
|
88
|
+
*/
|
|
89
|
+
async put(storeName, item) {
|
|
90
|
+
return new Promise((resolve, reject) => {
|
|
91
|
+
const transaction = this.db.transaction([storeName], 'readwrite');
|
|
92
|
+
const store = transaction.objectStore(storeName);
|
|
93
|
+
const request = store.put(item);
|
|
94
|
+
|
|
95
|
+
request.onsuccess = () => resolve(request.result);
|
|
96
|
+
request.onerror = () => reject(request.error);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Récupère un élément par sa clé
|
|
102
|
+
* @param {string} storeName - Nom du store
|
|
103
|
+
* @param {*} key - Clé
|
|
104
|
+
* @returns {Promise<*>} Élément
|
|
105
|
+
*/
|
|
106
|
+
async get(storeName, key) {
|
|
107
|
+
return new Promise((resolve, reject) => {
|
|
108
|
+
const transaction = this.db.transaction([storeName], 'readonly');
|
|
109
|
+
const store = transaction.objectStore(storeName);
|
|
110
|
+
const request = store.get(key);
|
|
111
|
+
|
|
112
|
+
request.onsuccess = () => resolve(request.result);
|
|
113
|
+
request.onerror = () => reject(request.error);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Récupère tous les éléments
|
|
119
|
+
* @param {string} storeName - Nom du store
|
|
120
|
+
* @returns {Promise<Array>} Liste des éléments
|
|
121
|
+
*/
|
|
122
|
+
async getAll(storeName) {
|
|
123
|
+
return new Promise((resolve, reject) => {
|
|
124
|
+
const transaction = this.db.transaction([storeName], 'readonly');
|
|
125
|
+
const store = transaction.objectStore(storeName);
|
|
126
|
+
const request = store.getAll();
|
|
127
|
+
|
|
128
|
+
request.onsuccess = () => resolve(request.result);
|
|
129
|
+
request.onerror = () => reject(request.error);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Cherche par index
|
|
135
|
+
* @param {string} storeName - Nom du store
|
|
136
|
+
* @param {string} indexName - Nom de l'index
|
|
137
|
+
* @param {*} value - Valeur à chercher
|
|
138
|
+
* @returns {Promise<Array>} Résultats
|
|
139
|
+
*/
|
|
140
|
+
async getByIndex(storeName, indexName, value) {
|
|
141
|
+
return new Promise((resolve, reject) => {
|
|
142
|
+
const transaction = this.db.transaction([storeName], 'readonly');
|
|
143
|
+
const store = transaction.objectStore(storeName);
|
|
144
|
+
const index = store.index(indexName);
|
|
145
|
+
const request = index.getAll(value);
|
|
146
|
+
|
|
147
|
+
request.onsuccess = () => resolve(request.result);
|
|
148
|
+
request.onerror = () => reject(request.error);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Query avec filtre
|
|
154
|
+
* @param {string} storeName - Nom du store
|
|
155
|
+
* @param {Function} predicate - Fonction de filtre
|
|
156
|
+
* @returns {Promise<Array>} Résultats
|
|
157
|
+
*/
|
|
158
|
+
async query(storeName, predicate) {
|
|
159
|
+
const all = await this.getAll(storeName);
|
|
160
|
+
return all.filter(predicate);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Supprime un élément
|
|
165
|
+
* @param {string} storeName - Nom du store
|
|
166
|
+
* @param {*} key - Clé
|
|
167
|
+
* @returns {Promise<void>}
|
|
168
|
+
*/
|
|
169
|
+
async delete(storeName, key) {
|
|
170
|
+
return new Promise((resolve, reject) => {
|
|
171
|
+
const transaction = this.db.transaction([storeName], 'readwrite');
|
|
172
|
+
const store = transaction.objectStore(storeName);
|
|
173
|
+
const request = store.delete(key);
|
|
174
|
+
|
|
175
|
+
request.onsuccess = () => resolve();
|
|
176
|
+
request.onerror = () => reject(request.error);
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Vide un store
|
|
182
|
+
* @param {string} storeName - Nom du store
|
|
183
|
+
* @returns {Promise<void>}
|
|
184
|
+
*/
|
|
185
|
+
async clear(storeName) {
|
|
186
|
+
return new Promise((resolve, reject) => {
|
|
187
|
+
const transaction = this.db.transaction([storeName], 'readwrite');
|
|
188
|
+
const store = transaction.objectStore(storeName);
|
|
189
|
+
const request = store.clear();
|
|
190
|
+
|
|
191
|
+
request.onsuccess = () => resolve();
|
|
192
|
+
request.onerror = () => reject(request.error);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Compte les éléments
|
|
198
|
+
* @param {string} storeName - Nom du store
|
|
199
|
+
* @returns {Promise<number>} Nombre d'éléments
|
|
200
|
+
*/
|
|
201
|
+
async count(storeName) {
|
|
202
|
+
return new Promise((resolve, reject) => {
|
|
203
|
+
const transaction = this.db.transaction([storeName], 'readonly');
|
|
204
|
+
const store = transaction.objectStore(storeName);
|
|
205
|
+
const request = store.count();
|
|
206
|
+
|
|
207
|
+
request.onsuccess = () => resolve(request.result);
|
|
208
|
+
request.onerror = () => reject(request.error);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Ajoute plusieurs éléments (batch)
|
|
214
|
+
* @param {string} storeName - Nom du store
|
|
215
|
+
* @param {Array} items - Éléments
|
|
216
|
+
* @returns {Promise<void>}
|
|
217
|
+
*/
|
|
218
|
+
async bulkAdd(storeName, items) {
|
|
219
|
+
return new Promise((resolve, reject) => {
|
|
220
|
+
const transaction = this.db.transaction([storeName], 'readwrite');
|
|
221
|
+
const store = transaction.objectStore(storeName);
|
|
222
|
+
|
|
223
|
+
items.forEach(item => store.add(item));
|
|
224
|
+
|
|
225
|
+
transaction.oncomplete = () => resolve();
|
|
226
|
+
transaction.onerror = () => reject(transaction.error);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Supprime plusieurs éléments
|
|
232
|
+
* @param {string} storeName - Nom du store
|
|
233
|
+
* @param {Array} keys - Clés
|
|
234
|
+
* @returns {Promise<void>}
|
|
235
|
+
*/
|
|
236
|
+
async bulkDelete(storeName, keys) {
|
|
237
|
+
return new Promise((resolve, reject) => {
|
|
238
|
+
const transaction = this.db.transaction([storeName], 'readwrite');
|
|
239
|
+
const store = transaction.objectStore(storeName);
|
|
240
|
+
|
|
241
|
+
keys.forEach(key => store.delete(key));
|
|
242
|
+
|
|
243
|
+
transaction.oncomplete = () => resolve();
|
|
244
|
+
transaction.onerror = () => reject(transaction.error);
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Ferme la connexion
|
|
250
|
+
*/
|
|
251
|
+
close() {
|
|
252
|
+
if (this.db) {
|
|
253
|
+
this.db.close();
|
|
254
|
+
this.db = null;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Supprime la base de données
|
|
260
|
+
* @returns {Promise<void>}
|
|
261
|
+
*/
|
|
262
|
+
async destroy() {
|
|
263
|
+
this.close();
|
|
264
|
+
|
|
265
|
+
return new Promise((resolve, reject) => {
|
|
266
|
+
const request = indexedDB.deleteDatabase(this.dbName);
|
|
267
|
+
request.onsuccess = () => resolve();
|
|
268
|
+
request.onerror = () => reject(request.error);
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export default IndexedDBManager;
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Overlay d'inspection pour CanvasFramework
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
class InspectionOverlay {
|
|
6
|
+
constructor(framework) {
|
|
7
|
+
this.framework = framework;
|
|
8
|
+
this.isEnabled = false;
|
|
9
|
+
this.hoveredComp = null;
|
|
10
|
+
this.selectedComp = null;
|
|
11
|
+
this.showGrid = false;
|
|
12
|
+
this.showBounds = true;
|
|
13
|
+
this.showMetrics = true;
|
|
14
|
+
this.gridSize = 50;
|
|
15
|
+
|
|
16
|
+
this.setup();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
setup() {
|
|
20
|
+
// Créer le canvas overlay
|
|
21
|
+
this.overlayCanvas = document.createElement('canvas');
|
|
22
|
+
this.overlayCanvas.style.cssText = `
|
|
23
|
+
position: absolute;
|
|
24
|
+
top: 0;
|
|
25
|
+
left: 0;
|
|
26
|
+
pointer-events: none;
|
|
27
|
+
z-index: 9998;
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
this.overlayCtx = this.overlayCanvas.getContext('2d');
|
|
31
|
+
|
|
32
|
+
// Redimensionner avec le canvas principal
|
|
33
|
+
this.syncWithMainCanvas();
|
|
34
|
+
|
|
35
|
+
// Événements
|
|
36
|
+
this.framework.canvas.addEventListener('mousemove', this.handleMouseMove.bind(this));
|
|
37
|
+
this.framework.canvas.addEventListener('click', this.handleClick.bind(this));
|
|
38
|
+
|
|
39
|
+
window.addEventListener('resize', this.syncWithMainCanvas.bind(this));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
syncWithMainCanvas() {
|
|
43
|
+
const rect = this.framework.canvas.getBoundingClientRect();
|
|
44
|
+
this.overlayCanvas.width = rect.width * (window.devicePixelRatio || 1);
|
|
45
|
+
this.overlayCanvas.height = rect.height * (window.devicePixelRatio || 1);
|
|
46
|
+
this.overlayCanvas.style.width = rect.width + 'px';
|
|
47
|
+
this.overlayCanvas.style.height = rect.height + 'px';
|
|
48
|
+
|
|
49
|
+
if (this.overlayCtx) {
|
|
50
|
+
this.overlayCtx.scale(window.devicePixelRatio || 1, window.devicePixelRatio || 1);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
handleMouseMove(e) {
|
|
55
|
+
if (!this.isEnabled) return;
|
|
56
|
+
|
|
57
|
+
const rect = this.framework.canvas.getBoundingClientRect();
|
|
58
|
+
const x = e.clientX - rect.left;
|
|
59
|
+
const y = e.clientY - rect.top;
|
|
60
|
+
|
|
61
|
+
// Trouver le composant sous la souris
|
|
62
|
+
this.hoveredComp = this.findComponentAt(x, y);
|
|
63
|
+
this.render();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
handleClick(e) {
|
|
67
|
+
if (!this.isEnabled) return;
|
|
68
|
+
|
|
69
|
+
if (this.hoveredComp) {
|
|
70
|
+
this.selectedComp = this.hoveredComp;
|
|
71
|
+
|
|
72
|
+
// Ouvrir DevTools si disponible
|
|
73
|
+
if (window.devTools) {
|
|
74
|
+
window.devTools.selectComponent(this.selectedComp);
|
|
75
|
+
window.devTools.switchTab('properties');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
findComponentAt(x, y) {
|
|
81
|
+
const adjustedY = y - this.framework.scrollOffset;
|
|
82
|
+
|
|
83
|
+
for (let i = this.framework.components.length - 1; i >= 0; i--) {
|
|
84
|
+
const comp = this.framework.components[i];
|
|
85
|
+
if (comp.visible && comp.isPointInside(x, adjustedY)) {
|
|
86
|
+
return comp;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
render() {
|
|
93
|
+
if (!this.isEnabled) return;
|
|
94
|
+
|
|
95
|
+
this.overlayCtx.clearRect(0, 0,
|
|
96
|
+
this.overlayCanvas.width / (window.devicePixelRatio || 1),
|
|
97
|
+
this.overlayCanvas.height / (window.devicePixelRatio || 1)
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
// Grille
|
|
101
|
+
if (this.showGrid) {
|
|
102
|
+
this.drawGrid();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Tous les composants
|
|
106
|
+
if (this.showBounds) {
|
|
107
|
+
this.drawAllComponents();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Composant survolé
|
|
111
|
+
if (this.hoveredComp) {
|
|
112
|
+
this.drawHoveredComponent();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Composant sélectionné
|
|
116
|
+
if (this.selectedComp) {
|
|
117
|
+
this.drawSelectedComponent();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Métriques
|
|
121
|
+
if (this.showMetrics) {
|
|
122
|
+
this.drawMetrics();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
drawGrid() {
|
|
127
|
+
this.overlayCtx.save();
|
|
128
|
+
this.overlayCtx.strokeStyle = 'rgba(100, 100, 100, 0.3)';
|
|
129
|
+
this.overlayCtx.lineWidth = 1;
|
|
130
|
+
|
|
131
|
+
// Lignes verticales
|
|
132
|
+
for (let x = 0; x < this.overlayCanvas.width; x += this.gridSize) {
|
|
133
|
+
this.overlayCtx.beginPath();
|
|
134
|
+
this.overlayCtx.moveTo(x, 0);
|
|
135
|
+
this.overlayCtx.lineTo(x, this.overlayCanvas.height);
|
|
136
|
+
this.overlayCtx.stroke();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Lignes horizontales
|
|
140
|
+
for (let y = 0; y < this.overlayCanvas.height; y += this.gridSize) {
|
|
141
|
+
this.overlayCtx.beginPath();
|
|
142
|
+
this.overlayCtx.moveTo(0, y);
|
|
143
|
+
this.overlayCtx.lineTo(this.overlayCanvas.width, y);
|
|
144
|
+
this.overlayCtx.stroke();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
this.overlayCtx.restore();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
drawAllComponents() {
|
|
151
|
+
this.framework.components.forEach(comp => {
|
|
152
|
+
if (!comp.visible) return;
|
|
153
|
+
|
|
154
|
+
const isFixed = this.framework.isFixedComponent(comp);
|
|
155
|
+
const y = isFixed ? comp.y : comp.y + this.framework.scrollOffset;
|
|
156
|
+
|
|
157
|
+
this.overlayCtx.save();
|
|
158
|
+
this.overlayCtx.strokeStyle = 'rgba(0, 150, 255, 0.2)';
|
|
159
|
+
this.overlayCtx.lineWidth = 1;
|
|
160
|
+
this.overlayCtx.strokeRect(comp.x, y, comp.width, comp.height);
|
|
161
|
+
this.overlayCtx.restore();
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
drawHoveredComponent() {
|
|
166
|
+
const comp = this.hoveredComp;
|
|
167
|
+
const isFixed = this.framework.isFixedComponent(comp);
|
|
168
|
+
const y = isFixed ? comp.y : comp.y + this.framework.scrollOffset;
|
|
169
|
+
|
|
170
|
+
this.overlayCtx.save();
|
|
171
|
+
|
|
172
|
+
// Fond semi-transparent
|
|
173
|
+
this.overlayCtx.fillStyle = 'rgba(100, 200, 255, 0.1)';
|
|
174
|
+
this.overlayCtx.fillRect(comp.x, y, comp.width, comp.height);
|
|
175
|
+
|
|
176
|
+
// Bordure
|
|
177
|
+
this.overlayCtx.strokeStyle = '#4ec9b0';
|
|
178
|
+
this.overlayCtx.lineWidth = 2;
|
|
179
|
+
this.overlayCtx.strokeRect(comp.x, y, comp.width, comp.height);
|
|
180
|
+
|
|
181
|
+
// Infos
|
|
182
|
+
this.overlayCtx.fillStyle = '#4ec9b0';
|
|
183
|
+
this.overlayCtx.font = '12px monospace';
|
|
184
|
+
this.overlayCtx.fillText(
|
|
185
|
+
`${comp.constructor.name} (${comp.width}x${comp.height})`,
|
|
186
|
+
comp.x,
|
|
187
|
+
y - 5
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
this.overlayCtx.restore();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
drawSelectedComponent() {
|
|
194
|
+
const comp = this.selectedComp;
|
|
195
|
+
if (!comp.visible) return;
|
|
196
|
+
|
|
197
|
+
const isFixed = this.framework.isFixedComponent(comp);
|
|
198
|
+
const y = isFixed ? comp.y : comp.y + this.framework.scrollOffset;
|
|
199
|
+
|
|
200
|
+
this.overlayCtx.save();
|
|
201
|
+
|
|
202
|
+
// Bordure rouge pointillée
|
|
203
|
+
this.overlayCtx.strokeStyle = '#ff5555';
|
|
204
|
+
this.overlayCtx.lineWidth = 2;
|
|
205
|
+
this.overlayCtx.setLineDash([5, 3]);
|
|
206
|
+
this.overlayCtx.strokeRect(comp.x - 2, y - 2, comp.width + 4, comp.height + 4);
|
|
207
|
+
|
|
208
|
+
// Mesures
|
|
209
|
+
this.overlayCtx.strokeStyle = '#ff5555';
|
|
210
|
+
this.overlayCtx.lineWidth = 1;
|
|
211
|
+
|
|
212
|
+
// Largeur
|
|
213
|
+
this.overlayCtx.beginPath();
|
|
214
|
+
this.overlayCtx.moveTo(comp.x, y - 20);
|
|
215
|
+
this.overlayCtx.lineTo(comp.x + comp.width, y - 20);
|
|
216
|
+
this.overlayCtx.moveTo(comp.x, y - 25);
|
|
217
|
+
this.overlayCtx.lineTo(comp.x, y - 15);
|
|
218
|
+
this.overlayCtx.moveTo(comp.x + comp.width, y - 25);
|
|
219
|
+
this.overlayCtx.lineTo(comp.x + comp.width, y - 15);
|
|
220
|
+
this.overlayCtx.stroke();
|
|
221
|
+
|
|
222
|
+
this.overlayCtx.fillStyle = '#ff5555';
|
|
223
|
+
this.overlayCtx.font = '10px monospace';
|
|
224
|
+
this.overlayCtx.textAlign = 'center';
|
|
225
|
+
this.overlayCtx.fillText(
|
|
226
|
+
`${comp.width}px`,
|
|
227
|
+
comp.x + comp.width / 2,
|
|
228
|
+
y - 25
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
// Hauteur
|
|
232
|
+
this.overlayCtx.beginPath();
|
|
233
|
+
this.overlayCtx.moveTo(comp.x - 20, y);
|
|
234
|
+
this.overlayCtx.lineTo(comp.x - 20, y + comp.height);
|
|
235
|
+
this.overlayCtx.moveTo(comp.x - 25, y);
|
|
236
|
+
this.overlayCtx.lineTo(comp.x - 15, y);
|
|
237
|
+
this.overlayCtx.moveTo(comp.x - 25, y + comp.height);
|
|
238
|
+
this.overlayCtx.lineTo(comp.x - 15, y + comp.height);
|
|
239
|
+
this.overlayCtx.stroke();
|
|
240
|
+
|
|
241
|
+
this.overlayCtx.save();
|
|
242
|
+
this.overlayCtx.translate(comp.x - 30, y + comp.height / 2);
|
|
243
|
+
this.overlayCtx.rotate(-Math.PI / 2);
|
|
244
|
+
this.overlayCtx.fillText(
|
|
245
|
+
`${comp.height}px`,
|
|
246
|
+
0,
|
|
247
|
+
0
|
|
248
|
+
);
|
|
249
|
+
this.overlayCtx.restore();
|
|
250
|
+
|
|
251
|
+
this.overlayCtx.restore();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
drawMetrics() {
|
|
255
|
+
this.overlayCtx.save();
|
|
256
|
+
this.overlayCtx.fillStyle = 'rgba(0, 0, 0, 0.7)';
|
|
257
|
+
this.overlayCtx.fillRect(10, 10, 200, 90);
|
|
258
|
+
|
|
259
|
+
this.overlayCtx.fillStyle = '#fff';
|
|
260
|
+
this.overlayCtx.font = '12px monospace';
|
|
261
|
+
this.overlayCtx.textAlign = 'left';
|
|
262
|
+
|
|
263
|
+
this.overlayCtx.fillText(`Composants: ${this.framework.components.length}`, 20, 30);
|
|
264
|
+
this.overlayCtx.fillText(`FPS: ${this.framework.fps}`, 20, 50);
|
|
265
|
+
this.overlayCtx.fillText(`Scroll: ${Math.round(this.framework.scrollOffset)}px`, 20, 70);
|
|
266
|
+
this.overlayCtx.fillText(`Route: ${this.framework.currentRoute}`, 20, 90);
|
|
267
|
+
|
|
268
|
+
if (this.hoveredComp) {
|
|
269
|
+
this.overlayCtx.fillText(
|
|
270
|
+
`Survol: ${this.hoveredComp.constructor.name}`,
|
|
271
|
+
20,
|
|
272
|
+
110
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
this.overlayCtx.restore();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
enable() {
|
|
280
|
+
this.isEnabled = true;
|
|
281
|
+
document.body.appendChild(this.overlayCanvas);
|
|
282
|
+
this.render();
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
disable() {
|
|
286
|
+
this.isEnabled = false;
|
|
287
|
+
if (this.overlayCanvas.parentNode) {
|
|
288
|
+
this.overlayCanvas.parentNode.removeChild(this.overlayCanvas);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
toggle() {
|
|
293
|
+
if (this.isEnabled) {
|
|
294
|
+
this.disable();
|
|
295
|
+
} else {
|
|
296
|
+
this.enable();
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
setOptions(options) {
|
|
301
|
+
Object.assign(this, options);
|
|
302
|
+
if (this.isEnabled) {
|
|
303
|
+
this.render();
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export default InspectionOverlay;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
|
|
2
|
+
// NotificationManager.js
|
|
3
|
+
export default class NotificationManager {
|
|
4
|
+
constructor(defaults = {}) {
|
|
5
|
+
this.defaults = {
|
|
6
|
+
icon: defaults.icon || null,
|
|
7
|
+
silent: defaults.silent || false,
|
|
8
|
+
requireInteraction: defaults.requireInteraction || false,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// Vérifier si l'API est disponible
|
|
12
|
+
this.isSupported = "Notification" in window;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Demander la permission si nécessaire
|
|
16
|
+
async requestPermission() {
|
|
17
|
+
if (!this.isSupported) return false;
|
|
18
|
+
if (Notification.permission === "granted") return true;
|
|
19
|
+
if (Notification.permission !== "denied") {
|
|
20
|
+
const permission = await Notification.requestPermission();
|
|
21
|
+
return permission === "granted";
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Créer une notification
|
|
27
|
+
async notify(title, options = {}) {
|
|
28
|
+
if (!this.isSupported) {
|
|
29
|
+
console.warn("Notifications API non supportée");
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const hasPermission = await this.requestPermission();
|
|
34
|
+
if (!hasPermission) return null;
|
|
35
|
+
|
|
36
|
+
const notifOptions = {
|
|
37
|
+
...this.defaults,
|
|
38
|
+
...options,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const notification = new Notification(title, notifOptions);
|
|
42
|
+
|
|
43
|
+
// Callbacks
|
|
44
|
+
if (options.onClick) {
|
|
45
|
+
notification.onclick = options.onClick;
|
|
46
|
+
}
|
|
47
|
+
if (options.onClose) {
|
|
48
|
+
notification.onclose = options.onClose;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Auto-close après duration (si défini)
|
|
52
|
+
if (options.duration && options.duration > 0) {
|
|
53
|
+
setTimeout(() => {
|
|
54
|
+
notification.close();
|
|
55
|
+
}, options.duration);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return notification;
|
|
59
|
+
}
|
|
60
|
+
}
|