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.
- package/README.md +554 -0
- package/components/Accordion.js +252 -0
- package/components/AndroidDatePickerDialog.js +398 -0
- package/components/AppBar.js +225 -0
- package/components/Avatar.js +202 -0
- package/components/BottomNavigationBar.js +205 -0
- package/components/BottomSheet.js +374 -0
- package/components/Button.js +225 -0
- package/components/Card.js +193 -0
- package/components/Checkbox.js +180 -0
- package/components/Chip.js +212 -0
- package/components/CircularProgress.js +143 -0
- package/components/ContextMenu.js +116 -0
- package/components/DatePicker.js +257 -0
- package/components/Dialog.js +367 -0
- package/components/Divider.js +125 -0
- package/components/Drawer.js +261 -0
- package/components/FAB.js +270 -0
- package/components/FileUpload.js +315 -0
- package/components/IOSDatePickerWheel.js +268 -0
- package/components/ImageCarousel.js +193 -0
- package/components/ImageComponent.js +223 -0
- package/components/Input.js +309 -0
- package/components/List.js +94 -0
- package/components/ListItem.js +223 -0
- package/components/Modal.js +364 -0
- package/components/MultiSelectDialog.js +206 -0
- package/components/NumberInput.js +271 -0
- package/components/ProgressBar.js +88 -0
- package/components/RadioButton.js +142 -0
- package/components/SearchInput.js +315 -0
- package/components/SegmentedControl.js +202 -0
- package/components/Select.js +199 -0
- package/components/SelectDialog.js +255 -0
- package/components/Slider.js +113 -0
- package/components/Snackbar.js +243 -0
- package/components/Stepper.js +281 -0
- package/components/SwipeableListItem.js +179 -0
- package/components/Switch.js +147 -0
- package/components/Table.js +492 -0
- package/components/Tabs.js +125 -0
- package/components/Text.js +141 -0
- package/components/TextField.js +331 -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 +1271 -0
- package/core/CanvasWork.js +32 -0
- package/core/Component.js +153 -0
- package/core/LogicWorker.js +25 -0
- package/core/WebGLCanvasAdapter.js +1369 -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 +84 -0
- package/features/Stack.js +21 -0
- package/index.js +101 -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 +28 -0
- package/utils/AnimationEngine.js +428 -0
- package/utils/DataStore.js +403 -0
- package/utils/EventBus.js +407 -0
- package/utils/FetchClient.js +74 -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/OfflineSyncManager.js +342 -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/WebSocketClient.js +66 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import Component from '../core/Component.js';
|
|
2
|
+
/**
|
|
3
|
+
* Boîte de dialogue modale
|
|
4
|
+
* @class
|
|
5
|
+
* @extends Component
|
|
6
|
+
* @property {string} title - Titre du dialog
|
|
7
|
+
* @property {string} message - Message du dialog
|
|
8
|
+
* @property {string[]} buttons - Liste des boutons
|
|
9
|
+
* @property {Function} onButtonClick - Callback au clic sur un bouton
|
|
10
|
+
* @property {number} dialogWidth - Largeur du dialog
|
|
11
|
+
* @property {number} dialogHeight - Hauteur du dialog
|
|
12
|
+
* @property {number} opacity - Opacité pour l'animation
|
|
13
|
+
* @property {Array} buttonRects - Positions des boutons
|
|
14
|
+
* @property {boolean} isVisible - Visibilité
|
|
15
|
+
* @property {number} pressedButtonIndex - Index du bouton pressé
|
|
16
|
+
*/
|
|
17
|
+
class Dialog extends Component {
|
|
18
|
+
/**
|
|
19
|
+
* Crée une instance de Dialog
|
|
20
|
+
* @param {CanvasFramework} framework - Framework parent
|
|
21
|
+
* @param {Object} [options={}] - Options de configuration
|
|
22
|
+
* @param {string} [options.title=''] - Titre
|
|
23
|
+
* @param {string} [options.message=''] - Message
|
|
24
|
+
* @param {string[]} [options.buttons=['OK']] - Boutons
|
|
25
|
+
* @param {Function} [options.onButtonClick] - Callback au clic sur bouton
|
|
26
|
+
*/
|
|
27
|
+
constructor(framework, options = {}) {
|
|
28
|
+
super(framework, {
|
|
29
|
+
x: 0,
|
|
30
|
+
y: 0,
|
|
31
|
+
width: framework.width,
|
|
32
|
+
height: framework.height,
|
|
33
|
+
visible: false
|
|
34
|
+
});
|
|
35
|
+
this.title = options.title || '';
|
|
36
|
+
this.message = options.message || '';
|
|
37
|
+
this.buttons = options.buttons || ['OK'];
|
|
38
|
+
this.onButtonClick = options.onButtonClick;
|
|
39
|
+
this.dialogWidth = Math.min(320, framework.width - 40);
|
|
40
|
+
this.dialogHeight = 200;
|
|
41
|
+
this.opacity = 0;
|
|
42
|
+
this.buttonRects = [];
|
|
43
|
+
this.isVisible = false;
|
|
44
|
+
this.pressedButtonIndex = -1;
|
|
45
|
+
|
|
46
|
+
// Définir onPress
|
|
47
|
+
this.onPress = this.handlePress.bind(this);
|
|
48
|
+
|
|
49
|
+
// Pour diviser le message en plusieurs lignes
|
|
50
|
+
this.messageLines = this.wrapText(this.message, this.dialogWidth - 40, '16px -apple-system, sans-serif');
|
|
51
|
+
|
|
52
|
+
// Ajuster la hauteur du dialog en fonction du message
|
|
53
|
+
if (this.messageLines.length > 2) {
|
|
54
|
+
this.dialogHeight = 150 + (this.messageLines.length - 2) * 20;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Divise le texte en plusieurs lignes
|
|
60
|
+
* @param {string} text - Texte à diviser
|
|
61
|
+
* @param {number} maxWidth - Largeur maximale
|
|
62
|
+
* @param {string} font - Police de caractères
|
|
63
|
+
* @returns {string[]} Tableau de lignes
|
|
64
|
+
* @private
|
|
65
|
+
*/
|
|
66
|
+
wrapText(text, maxWidth, font) {
|
|
67
|
+
const ctx = this.framework.ctx;
|
|
68
|
+
ctx.save();
|
|
69
|
+
ctx.font = font;
|
|
70
|
+
|
|
71
|
+
const words = text.split(' ');
|
|
72
|
+
const lines = [];
|
|
73
|
+
let currentLine = words[0];
|
|
74
|
+
|
|
75
|
+
for (let i = 1; i < words.length; i++) {
|
|
76
|
+
const word = words[i];
|
|
77
|
+
const width = ctx.measureText(currentLine + " " + word).width;
|
|
78
|
+
if (width < maxWidth) {
|
|
79
|
+
currentLine += " " + word;
|
|
80
|
+
} else {
|
|
81
|
+
lines.push(currentLine);
|
|
82
|
+
currentLine = word;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
lines.push(currentLine);
|
|
86
|
+
|
|
87
|
+
ctx.restore();
|
|
88
|
+
return lines;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Dessine le dialog
|
|
93
|
+
* @param {CanvasRenderingContext2D} ctx - Contexte de dessin
|
|
94
|
+
*/
|
|
95
|
+
draw(ctx) {
|
|
96
|
+
if (this.opacity <= 0 || !this.isVisible) return;
|
|
97
|
+
|
|
98
|
+
ctx.save();
|
|
99
|
+
ctx.globalAlpha = this.opacity;
|
|
100
|
+
|
|
101
|
+
// Overlay sombre
|
|
102
|
+
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
|
|
103
|
+
ctx.fillRect(0, 0, this.framework.width, this.framework.height);
|
|
104
|
+
|
|
105
|
+
// Dialog box
|
|
106
|
+
const dialogX = (this.framework.width - this.dialogWidth) / 2;
|
|
107
|
+
const dialogY = (this.framework.height - this.dialogHeight) / 2;
|
|
108
|
+
|
|
109
|
+
ctx.fillStyle = '#FFFFFF';
|
|
110
|
+
ctx.shadowColor = 'rgba(0, 0, 0, 0.3)';
|
|
111
|
+
ctx.shadowBlur = 20;
|
|
112
|
+
ctx.beginPath();
|
|
113
|
+
this.roundRect(ctx, dialogX, dialogY, this.dialogWidth, this.dialogHeight, 12);
|
|
114
|
+
ctx.fill();
|
|
115
|
+
|
|
116
|
+
ctx.shadowColor = 'transparent';
|
|
117
|
+
|
|
118
|
+
// Titre
|
|
119
|
+
ctx.fillStyle = '#000000';
|
|
120
|
+
ctx.font = 'bold 18px -apple-system, sans-serif';
|
|
121
|
+
ctx.textAlign = 'center';
|
|
122
|
+
ctx.textBaseline = 'middle';
|
|
123
|
+
ctx.fillText(this.title, dialogX + this.dialogWidth / 2, dialogY + 40);
|
|
124
|
+
|
|
125
|
+
// Message (lignes multiples)
|
|
126
|
+
ctx.fillStyle = '#666666';
|
|
127
|
+
ctx.font = '16px -apple-system, sans-serif';
|
|
128
|
+
ctx.textAlign = 'center';
|
|
129
|
+
|
|
130
|
+
for (let i = 0; i < this.messageLines.length; i++) {
|
|
131
|
+
ctx.fillText(
|
|
132
|
+
this.messageLines[i],
|
|
133
|
+
dialogX + this.dialogWidth / 2,
|
|
134
|
+
dialogY + 80 + (i * 24)
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Divider au-dessus des boutons
|
|
139
|
+
ctx.strokeStyle = '#E0E0E0';
|
|
140
|
+
ctx.lineWidth = 1;
|
|
141
|
+
ctx.beginPath();
|
|
142
|
+
ctx.moveTo(dialogX, dialogY + this.dialogHeight - 60);
|
|
143
|
+
ctx.lineTo(dialogX + this.dialogWidth, dialogY + this.dialogHeight - 60);
|
|
144
|
+
ctx.stroke();
|
|
145
|
+
|
|
146
|
+
// Boutons
|
|
147
|
+
this.buttonRects = [];
|
|
148
|
+
const buttonHeight = 50;
|
|
149
|
+
const buttonWidth = this.dialogWidth / this.buttons.length;
|
|
150
|
+
|
|
151
|
+
for (let i = 0; i < this.buttons.length; i++) {
|
|
152
|
+
const btnX = dialogX + i * buttonWidth;
|
|
153
|
+
const btnY = dialogY + this.dialogHeight - buttonHeight;
|
|
154
|
+
|
|
155
|
+
// Stocker la position du bouton
|
|
156
|
+
this.buttonRects.push({
|
|
157
|
+
x: btnX,
|
|
158
|
+
y: btnY,
|
|
159
|
+
width: buttonWidth,
|
|
160
|
+
height: buttonHeight
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Style du bouton
|
|
164
|
+
const isPrimary = i === this.buttons.length - 1; // Dernier bouton = primaire
|
|
165
|
+
ctx.fillStyle = isPrimary ? '#007AFF' : '#8E8E93';
|
|
166
|
+
ctx.font = 'bold 17px -apple-system, sans-serif';
|
|
167
|
+
ctx.textAlign = 'center';
|
|
168
|
+
ctx.textBaseline = 'middle';
|
|
169
|
+
|
|
170
|
+
// Effet hover
|
|
171
|
+
if (this.framework.hoveredComponent === this && this.isPointInButton(i, this.lastX, this.lastY)) {
|
|
172
|
+
ctx.globalAlpha = this.opacity * 0.8;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Effet pressé
|
|
176
|
+
if (this.pressedButtonIndex === i) {
|
|
177
|
+
ctx.globalAlpha = this.opacity * 0.6;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
ctx.fillText(this.buttons[i], btnX + buttonWidth / 2, btnY + buttonHeight / 2);
|
|
181
|
+
ctx.globalAlpha = this.opacity;
|
|
182
|
+
|
|
183
|
+
// Divider entre boutons (sauf pour le dernier)
|
|
184
|
+
if (i < this.buttons.length - 1) {
|
|
185
|
+
ctx.strokeStyle = '#E0E0E0';
|
|
186
|
+
ctx.lineWidth = 1;
|
|
187
|
+
ctx.beginPath();
|
|
188
|
+
ctx.moveTo(btnX + buttonWidth, btnY);
|
|
189
|
+
ctx.lineTo(btnX + buttonWidth, btnY + buttonHeight);
|
|
190
|
+
ctx.stroke();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
ctx.restore();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Vérifie si un point est dans un rectangle
|
|
199
|
+
* @param {number} x - Coordonnée X
|
|
200
|
+
* @param {number} y - Coordonnée Y
|
|
201
|
+
* @param {Object} rect - Rectangle {x, y, width, height}
|
|
202
|
+
* @returns {boolean} True si le point est dans le rectangle
|
|
203
|
+
* @private
|
|
204
|
+
*/
|
|
205
|
+
isPointInRect(x, y, rect) {
|
|
206
|
+
return x >= rect.x &&
|
|
207
|
+
x <= rect.x + rect.width &&
|
|
208
|
+
y >= rect.y &&
|
|
209
|
+
y <= rect.y + rect.height;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Vérifie si un point est dans un bouton spécifique
|
|
214
|
+
* @param {number} index - Index du bouton
|
|
215
|
+
* @param {number} x - Coordonnée X
|
|
216
|
+
* @param {number} y - Coordonnée Y
|
|
217
|
+
* @returns {boolean} True si le point est dans le bouton
|
|
218
|
+
* @private
|
|
219
|
+
*/
|
|
220
|
+
isPointInButton(index, x, y) {
|
|
221
|
+
if (index < 0 || index >= this.buttonRects.length) return false;
|
|
222
|
+
return this.isPointInRect(x, y, this.buttonRects[index]);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Vérifie si un point est dans le dialog
|
|
227
|
+
* @param {number} x - Coordonnée X
|
|
228
|
+
* @param {number} y - Coordonnée Y
|
|
229
|
+
* @returns {boolean} True si le point est dans le dialog
|
|
230
|
+
* @private
|
|
231
|
+
*/
|
|
232
|
+
isPointInDialog(x, y) {
|
|
233
|
+
const dialogX = (this.framework.width - this.dialogWidth) / 2;
|
|
234
|
+
const dialogY = (this.framework.height - this.dialogHeight) / 2;
|
|
235
|
+
|
|
236
|
+
return x >= dialogX &&
|
|
237
|
+
x <= dialogX + this.dialogWidth &&
|
|
238
|
+
y >= dialogY &&
|
|
239
|
+
y <= dialogY + this.dialogHeight;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Affiche le dialog
|
|
244
|
+
*/
|
|
245
|
+
show() {
|
|
246
|
+
this.isVisible = true;
|
|
247
|
+
this.visible = true;
|
|
248
|
+
|
|
249
|
+
// Réajuster la taille si nécessaire
|
|
250
|
+
this.messageLines = this.wrapText(this.message, this.dialogWidth - 40, '16px -apple-system, sans-serif');
|
|
251
|
+
if (this.messageLines.length > 2) {
|
|
252
|
+
this.dialogHeight = 150 + (this.messageLines.length - 2) * 20;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Animation d'apparition
|
|
256
|
+
const fadeIn = () => {
|
|
257
|
+
this.opacity += 0.1;
|
|
258
|
+
if (this.opacity < 1) {
|
|
259
|
+
requestAnimationFrame(fadeIn);
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
fadeIn();
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Cache le dialog
|
|
267
|
+
*/
|
|
268
|
+
hide() {
|
|
269
|
+
const fadeOut = () => {
|
|
270
|
+
this.opacity -= 0.1;
|
|
271
|
+
if (this.opacity > 0) {
|
|
272
|
+
requestAnimationFrame(fadeOut);
|
|
273
|
+
} else {
|
|
274
|
+
this.isVisible = false;
|
|
275
|
+
this.visible = false;
|
|
276
|
+
this.framework.remove(this);
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
fadeOut();
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Gère la pression (clic)
|
|
284
|
+
* @param {number} x - Coordonnée X
|
|
285
|
+
* @param {number} y - Coordonnée Y
|
|
286
|
+
* @private
|
|
287
|
+
*/
|
|
288
|
+
handlePress(x, y) {
|
|
289
|
+
// Stocker les dernières coordonnées pour le hover
|
|
290
|
+
this.lastX = x;
|
|
291
|
+
this.lastY = y;
|
|
292
|
+
|
|
293
|
+
// Vérifier si un bouton a été cliqué
|
|
294
|
+
for (let i = 0; i < this.buttonRects.length; i++) {
|
|
295
|
+
if (this.isPointInButton(i, x, y)) {
|
|
296
|
+
// Effet visuel du bouton pressé
|
|
297
|
+
this.pressedButtonIndex = i;
|
|
298
|
+
|
|
299
|
+
// Appeler le callback après un court délai pour l'effet visuel
|
|
300
|
+
setTimeout(() => {
|
|
301
|
+
if (this.onButtonClick) {
|
|
302
|
+
this.onButtonClick(i, this.buttons[i]);
|
|
303
|
+
}
|
|
304
|
+
this.hide();
|
|
305
|
+
}, 150);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Si on clique en dehors de la boîte de dialogue, fermer
|
|
311
|
+
if (!this.isPointInDialog(x, y)) {
|
|
312
|
+
this.hide();
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Gère le mouvement (hover)
|
|
318
|
+
* @param {number} x - Coordonnée X
|
|
319
|
+
* @param {number} y - Coordonnée Y
|
|
320
|
+
* @private
|
|
321
|
+
*/
|
|
322
|
+
handleMove(x, y) {
|
|
323
|
+
// Stocker pour le hover effect
|
|
324
|
+
this.lastX = x;
|
|
325
|
+
this.lastY = y;
|
|
326
|
+
|
|
327
|
+
// Mettre à jour le hoveredComponent
|
|
328
|
+
if (this.isPointInDialog(x, y)) {
|
|
329
|
+
this.framework.hoveredComponent = this;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Dessine un rectangle avec coins arrondis
|
|
335
|
+
* @param {CanvasRenderingContext2D} ctx - Contexte de dessin
|
|
336
|
+
* @param {number} x - Position X
|
|
337
|
+
* @param {number} y - Position Y
|
|
338
|
+
* @param {number} width - Largeur
|
|
339
|
+
* @param {number} height - Hauteur
|
|
340
|
+
* @param {number} radius - Rayon des coins
|
|
341
|
+
* @private
|
|
342
|
+
*/
|
|
343
|
+
roundRect(ctx, x, y, width, height, radius) {
|
|
344
|
+
ctx.beginPath();
|
|
345
|
+
ctx.moveTo(x + radius, y);
|
|
346
|
+
ctx.lineTo(x + width - radius, y);
|
|
347
|
+
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
|
|
348
|
+
ctx.lineTo(x + width, y + height - radius);
|
|
349
|
+
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
|
|
350
|
+
ctx.lineTo(x + radius, y + height);
|
|
351
|
+
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
|
|
352
|
+
ctx.lineTo(x, y + radius);
|
|
353
|
+
ctx.quadraticCurveTo(x, y, x + radius, y);
|
|
354
|
+
ctx.closePath();
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Vérifie si un point est dans les limites
|
|
359
|
+
* @returns {boolean} True si visible (capture tous les clics)
|
|
360
|
+
*/
|
|
361
|
+
isPointInside() {
|
|
362
|
+
// Le dialog capture tous les clics quand il est visible
|
|
363
|
+
return this.isVisible;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export default Dialog;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import Component from '../core/Component.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Séparateur visuel horizontal ou vertical
|
|
5
|
+
* @class
|
|
6
|
+
* @extends Component
|
|
7
|
+
* @property {string} orientation - 'horizontal' ou 'vertical'
|
|
8
|
+
* @property {number} thickness - Épaisseur du divider
|
|
9
|
+
* @property {string} color - Couleur du divider
|
|
10
|
+
* @property {number} margin - Marge autour du divider
|
|
11
|
+
* @property {string} style - 'solid', 'dashed', ou 'dotted'
|
|
12
|
+
* @property {boolean} hasInset - Si true, ajoute un inset pour les listes
|
|
13
|
+
* @property {number} insetStart - Début de l'inset (px)
|
|
14
|
+
* @property {string} variant - 'full', 'inset', 'middle'
|
|
15
|
+
*/
|
|
16
|
+
class Divider extends Component {
|
|
17
|
+
/**
|
|
18
|
+
* Crée une instance de Divider
|
|
19
|
+
* @param {CanvasFramework} framework - Framework parent
|
|
20
|
+
* @param {Object} [options={}] - Options de configuration
|
|
21
|
+
* @param {string} [options.orientation='horizontal'] - Orientation
|
|
22
|
+
* @param {number} [options.thickness] - Épaisseur (auto selon platform)
|
|
23
|
+
* @param {string} [options.color] - Couleur (auto selon platform)
|
|
24
|
+
* @param {number} [options.margin=0] - Marge
|
|
25
|
+
* @param {string} [options.style='solid'] - Style de ligne
|
|
26
|
+
* @param {string} [options.variant='full'] - Variante
|
|
27
|
+
* @param {number} [options.insetStart=16] - Début de l'inset
|
|
28
|
+
*/
|
|
29
|
+
constructor(framework, options = {}) {
|
|
30
|
+
super(framework, options);
|
|
31
|
+
|
|
32
|
+
this.orientation = options.orientation || 'horizontal';
|
|
33
|
+
this.margin = options.margin || 0;
|
|
34
|
+
this.style = options.style || 'solid';
|
|
35
|
+
this.variant = options.variant || 'full';
|
|
36
|
+
this.insetStart = options.insetStart || 16;
|
|
37
|
+
|
|
38
|
+
const platform = framework.platform;
|
|
39
|
+
|
|
40
|
+
// Styles selon la plateforme
|
|
41
|
+
if (platform === 'material') {
|
|
42
|
+
this.thickness = options.thickness || 1;
|
|
43
|
+
this.color = options.color || 'rgba(0, 0, 0, 0.12)';
|
|
44
|
+
} else {
|
|
45
|
+
this.thickness = options.thickness || 0.5;
|
|
46
|
+
this.color = options.color || 'rgba(60, 60, 67, 0.29)';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Ajuster les dimensions selon l'orientation
|
|
50
|
+
if (this.orientation === 'horizontal') {
|
|
51
|
+
this.height = this.thickness + (this.margin * 2);
|
|
52
|
+
} else {
|
|
53
|
+
this.width = this.thickness + (this.margin * 2);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Dessine le divider
|
|
59
|
+
* @param {CanvasRenderingContext2D} ctx - Contexte de dessin
|
|
60
|
+
*/
|
|
61
|
+
draw(ctx) {
|
|
62
|
+
ctx.save();
|
|
63
|
+
|
|
64
|
+
let startX = this.x;
|
|
65
|
+
let startY = this.y;
|
|
66
|
+
let endX = this.x;
|
|
67
|
+
let endY = this.y;
|
|
68
|
+
|
|
69
|
+
if (this.orientation === 'horizontal') {
|
|
70
|
+
startX = this.x;
|
|
71
|
+
endX = this.x + this.width;
|
|
72
|
+
startY = endY = this.y + this.margin + this.thickness / 2;
|
|
73
|
+
|
|
74
|
+
// Variantes
|
|
75
|
+
if (this.variant === 'inset') {
|
|
76
|
+
startX += this.insetStart;
|
|
77
|
+
} else if (this.variant === 'middle') {
|
|
78
|
+
const inset = this.width * 0.1;
|
|
79
|
+
startX += inset;
|
|
80
|
+
endX -= inset;
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
startY = this.y;
|
|
84
|
+
endY = this.y + this.height;
|
|
85
|
+
startX = endX = this.x + this.margin + this.thickness / 2;
|
|
86
|
+
|
|
87
|
+
// Variantes
|
|
88
|
+
if (this.variant === 'inset') {
|
|
89
|
+
startY += this.insetStart;
|
|
90
|
+
} else if (this.variant === 'middle') {
|
|
91
|
+
const inset = this.height * 0.1;
|
|
92
|
+
startY += inset;
|
|
93
|
+
endY -= inset;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
ctx.strokeStyle = this.color;
|
|
98
|
+
ctx.lineWidth = this.thickness;
|
|
99
|
+
|
|
100
|
+
// Styles de ligne
|
|
101
|
+
if (this.style === 'dashed') {
|
|
102
|
+
ctx.setLineDash([8, 4]);
|
|
103
|
+
} else if (this.style === 'dotted') {
|
|
104
|
+
ctx.setLineDash([2, 4]);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
ctx.beginPath();
|
|
108
|
+
ctx.moveTo(startX, startY);
|
|
109
|
+
ctx.lineTo(endX, endY);
|
|
110
|
+
ctx.stroke();
|
|
111
|
+
|
|
112
|
+
ctx.setLineDash([]);
|
|
113
|
+
ctx.restore();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Vérifie si un point est dans les limites (non cliquable)
|
|
118
|
+
* @returns {boolean} Toujours false
|
|
119
|
+
*/
|
|
120
|
+
isPointInside() {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export default Divider;
|