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,271 @@
|
|
|
1
|
+
import Component from '../core/Component.js';
|
|
2
|
+
/**
|
|
3
|
+
* Champ de saisie numérique avec support multi-plateforme et validation
|
|
4
|
+
* @class
|
|
5
|
+
* @extends Component
|
|
6
|
+
* @param {Framework} framework - Instance du framework
|
|
7
|
+
* @param {Object} [options={}] - Options de configuration
|
|
8
|
+
* @param {number} [options.value=0] - Valeur initiale
|
|
9
|
+
* @param {number} [options.min] - Valeur minimale
|
|
10
|
+
* @param {number} [options.max] - Valeur maximale
|
|
11
|
+
* @param {number} [options.step=1] - Incrément/décrément
|
|
12
|
+
* @param {string} [options.placeholder='0'] - Texte par défaut
|
|
13
|
+
* @param {number} [options.fontSize=16] - Taille de police
|
|
14
|
+
* @param {Function} [options.onChange] - Callback lors du changement de valeur
|
|
15
|
+
* @param {number} [options.height] - Hauteur personnalisée (44 par défaut sur iOS)
|
|
16
|
+
* @example
|
|
17
|
+
* const numberInput = new NumberInput(framework, {
|
|
18
|
+
* value: 10,
|
|
19
|
+
* min: 0,
|
|
20
|
+
* max: 100,
|
|
21
|
+
* step: 5,
|
|
22
|
+
* onChange: (value) => console.log('New value:', value)
|
|
23
|
+
* });
|
|
24
|
+
*/
|
|
25
|
+
class NumberInput extends Component {
|
|
26
|
+
/**
|
|
27
|
+
* @constructs NumberInput
|
|
28
|
+
*/
|
|
29
|
+
constructor(framework, options = {}) {
|
|
30
|
+
super(framework, options);
|
|
31
|
+
/** @type {number|string} */
|
|
32
|
+
this.value = options.value || 0;
|
|
33
|
+
/** @type {number|undefined} */
|
|
34
|
+
this.min = options.min;
|
|
35
|
+
/** @type {number|undefined} */
|
|
36
|
+
this.max = options.max;
|
|
37
|
+
/** @type {number} */
|
|
38
|
+
this.step = options.step || 1;
|
|
39
|
+
/** @type {string} */
|
|
40
|
+
this.placeholder = options.placeholder || '0';
|
|
41
|
+
/** @type {string} */
|
|
42
|
+
this.platform = framework.platform;
|
|
43
|
+
/** @type {boolean} */
|
|
44
|
+
this.focused = false;
|
|
45
|
+
/** @type {number} */
|
|
46
|
+
this.fontSize = options.fontSize || 16;
|
|
47
|
+
/** @type {Function|undefined} */
|
|
48
|
+
this.onChange = options.onChange;
|
|
49
|
+
|
|
50
|
+
// Ajuster la hauteur pour iOS
|
|
51
|
+
if (this.platform === 'cupertino') {
|
|
52
|
+
this.height = options.height || 44;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
this.onFocus = this.handleFocus.bind(this);
|
|
56
|
+
this.onBlur = this.handleBlur.bind(this);
|
|
57
|
+
|
|
58
|
+
// CRÉER UN INPUT CACHÉ UNIQUE POUR CETTE INSTANCE
|
|
59
|
+
/** @type {string} */
|
|
60
|
+
this.hiddenInputId = `hidden-number-input-${Math.random().toString(36).substr(2, 9)}`;
|
|
61
|
+
this.setupHiddenInput();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Configure l'input caché dans le DOM pour la saisie clavier
|
|
66
|
+
* @private
|
|
67
|
+
*/
|
|
68
|
+
setupHiddenInput() {
|
|
69
|
+
// Créer un input caché unique pour cette instance
|
|
70
|
+
const hiddenInput = document.createElement('input');
|
|
71
|
+
hiddenInput.id = this.hiddenInputId;
|
|
72
|
+
hiddenInput.type = 'number';
|
|
73
|
+
hiddenInput.style.position = 'absolute';
|
|
74
|
+
hiddenInput.style.opacity = '0';
|
|
75
|
+
hiddenInput.style.width = '1px';
|
|
76
|
+
hiddenInput.style.height = '1px';
|
|
77
|
+
hiddenInput.style.left = '-9999px';
|
|
78
|
+
hiddenInput.style.top = '0px';
|
|
79
|
+
hiddenInput.style.fontSize = '16px'; // Forcer la taille de police
|
|
80
|
+
|
|
81
|
+
// Appliquer les contraintes
|
|
82
|
+
if (this.min !== undefined) hiddenInput.min = this.min;
|
|
83
|
+
if (this.max !== undefined) hiddenInput.max = this.max;
|
|
84
|
+
if (this.step) hiddenInput.step = this.step;
|
|
85
|
+
|
|
86
|
+
document.body.appendChild(hiddenInput);
|
|
87
|
+
|
|
88
|
+
hiddenInput.addEventListener('input', (e) => {
|
|
89
|
+
if (this.focused) {
|
|
90
|
+
let newValue = parseFloat(e.target.value);
|
|
91
|
+
|
|
92
|
+
// Gérer les valeurs vides
|
|
93
|
+
if (isNaN(newValue)) {
|
|
94
|
+
newValue = '';
|
|
95
|
+
} else {
|
|
96
|
+
// Appliquer min/max
|
|
97
|
+
if (this.min !== undefined) newValue = Math.max(this.min, newValue);
|
|
98
|
+
if (this.max !== undefined) newValue = Math.min(this.max, newValue);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
this.value = newValue;
|
|
102
|
+
|
|
103
|
+
if (this.onChange) this.onChange(this.value);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
hiddenInput.addEventListener('blur', () => {
|
|
108
|
+
this.handleBlur();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
hiddenInput.addEventListener('keypress', (e) => {
|
|
112
|
+
if (e.key === 'Enter') {
|
|
113
|
+
this.handleBlur();
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
/** @type {HTMLInputElement} */
|
|
118
|
+
this.hiddenInput = hiddenInput;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Gère le focus sur le champ
|
|
123
|
+
*/
|
|
124
|
+
handleFocus() {
|
|
125
|
+
if (!this.hiddenInput) return;
|
|
126
|
+
|
|
127
|
+
this.focused = true;
|
|
128
|
+
this.cursorVisible = true;
|
|
129
|
+
|
|
130
|
+
// Positionner et configurer l'input
|
|
131
|
+
const adjustedY = this.y + this.framework.scrollOffset;
|
|
132
|
+
|
|
133
|
+
// Sur mobile, on veut forcer le clavier numérique
|
|
134
|
+
this.hiddenInput.type = 'number';
|
|
135
|
+
this.hiddenInput.inputMode = 'decimal';
|
|
136
|
+
this.hiddenInput.pattern = '[0-9]*';
|
|
137
|
+
|
|
138
|
+
// Définir la valeur actuelle
|
|
139
|
+
this.hiddenInput.value = this.value !== null && this.value !== undefined ? this.value : '';
|
|
140
|
+
|
|
141
|
+
// Forcer le focus avec un délai pour éviter les problèmes de rendu
|
|
142
|
+
setTimeout(() => {
|
|
143
|
+
this.hiddenInput.focus();
|
|
144
|
+
}, 50);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Gère la perte de focus
|
|
149
|
+
*/
|
|
150
|
+
handleBlur() {
|
|
151
|
+
this.focused = false;
|
|
152
|
+
this.cursorVisible = false;
|
|
153
|
+
|
|
154
|
+
// S'assurer que la valeur est valide
|
|
155
|
+
if (this.hiddenInput && this.hiddenInput.value !== '') {
|
|
156
|
+
let val = parseFloat(this.hiddenInput.value);
|
|
157
|
+
if (!isNaN(val)) {
|
|
158
|
+
if (this.min !== undefined) val = Math.max(this.min, val);
|
|
159
|
+
if (this.max !== undefined) val = Math.min(this.max, val);
|
|
160
|
+
this.value = val;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Gère le clic sur le champ
|
|
167
|
+
*/
|
|
168
|
+
onClick() {
|
|
169
|
+
this.handleFocus();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Dessine le champ de saisie
|
|
174
|
+
* @param {CanvasRenderingContext2D} ctx - Contexte de dessin
|
|
175
|
+
*/
|
|
176
|
+
draw(ctx) {
|
|
177
|
+
ctx.save();
|
|
178
|
+
|
|
179
|
+
const displayValue = this.value !== null && this.value !== undefined ?
|
|
180
|
+
this.value.toString() : this.placeholder;
|
|
181
|
+
const isPlaceholder = this.value === null || this.value === undefined || this.value === '';
|
|
182
|
+
|
|
183
|
+
if (this.platform === 'material') {
|
|
184
|
+
// Material Design NumberInput
|
|
185
|
+
ctx.strokeStyle = this.focused ? '#6200EE' : '#CCCCCC';
|
|
186
|
+
ctx.lineWidth = this.focused ? 2 : 1;
|
|
187
|
+
ctx.beginPath();
|
|
188
|
+
ctx.moveTo(this.x, this.y + this.height);
|
|
189
|
+
ctx.lineTo(this.x + this.width, this.y + this.height);
|
|
190
|
+
ctx.stroke();
|
|
191
|
+
|
|
192
|
+
// Valeur
|
|
193
|
+
ctx.fillStyle = isPlaceholder ? '#999999' : '#000000';
|
|
194
|
+
ctx.font = `${this.fontSize}px Roboto, sans-serif`;
|
|
195
|
+
ctx.textAlign = 'left';
|
|
196
|
+
ctx.textBaseline = 'middle';
|
|
197
|
+
ctx.fillText(displayValue, this.x + 2, this.y + this.height / 2);
|
|
198
|
+
|
|
199
|
+
} else {
|
|
200
|
+
// Cupertino NumberInput
|
|
201
|
+
ctx.strokeStyle = this.focused ? '#007AFF' : '#C7C7CC';
|
|
202
|
+
ctx.lineWidth = 1;
|
|
203
|
+
ctx.beginPath();
|
|
204
|
+
this.roundRect(ctx, this.x, this.y, this.width, this.height, 8);
|
|
205
|
+
ctx.stroke();
|
|
206
|
+
|
|
207
|
+
// Valeur
|
|
208
|
+
ctx.fillStyle = isPlaceholder ? '#999999' : '#000000';
|
|
209
|
+
ctx.font = `${this.fontSize}px -apple-system, sans-serif`;
|
|
210
|
+
ctx.textAlign = 'left';
|
|
211
|
+
ctx.textBaseline = 'middle';
|
|
212
|
+
ctx.fillText(displayValue, this.x + 10, this.y + this.height / 2);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Curseur
|
|
216
|
+
if (this.focused && this.cursorVisible) {
|
|
217
|
+
const textWidth = ctx.measureText(displayValue).width;
|
|
218
|
+
ctx.fillStyle = this.platform === 'material' ? '#6200EE' : '#007AFF';
|
|
219
|
+
ctx.fillRect(this.x + (this.platform === 'material' ? 2 : 10) + textWidth + 2,
|
|
220
|
+
this.y + 10, 2, this.height - 20);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
ctx.restore();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Dessine un rectangle avec des coins arrondis
|
|
228
|
+
* @param {CanvasRenderingContext2D} ctx - Contexte de dessin
|
|
229
|
+
* @param {number} x - Position X
|
|
230
|
+
* @param {number} y - Position Y
|
|
231
|
+
* @param {number} width - Largeur
|
|
232
|
+
* @param {number} height - Hauteur
|
|
233
|
+
* @param {number} radius - Rayon des coins
|
|
234
|
+
* @private
|
|
235
|
+
*/
|
|
236
|
+
roundRect(ctx, x, y, width, height, radius) {
|
|
237
|
+
ctx.beginPath();
|
|
238
|
+
ctx.moveTo(x + radius, y);
|
|
239
|
+
ctx.lineTo(x + width - radius, y);
|
|
240
|
+
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
|
|
241
|
+
ctx.lineTo(x + width, y + height - radius);
|
|
242
|
+
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
|
|
243
|
+
ctx.lineTo(x + radius, y + height);
|
|
244
|
+
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
|
|
245
|
+
ctx.lineTo(x, y + radius);
|
|
246
|
+
ctx.quadraticCurveTo(x, y, x + radius, y);
|
|
247
|
+
ctx.closePath();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Vérifie si un point est à l'intérieur du composant
|
|
252
|
+
* @param {number} x - Position X
|
|
253
|
+
* @param {number} y - Position Y
|
|
254
|
+
* @returns {boolean} True si le point est à l'intérieur
|
|
255
|
+
*/
|
|
256
|
+
isPointInside(x, y) {
|
|
257
|
+
return x >= this.x && x <= this.x + this.width &&
|
|
258
|
+
y >= this.y && y <= this.y + this.height;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Nettoie les ressources (supprime l'input caché du DOM)
|
|
263
|
+
*/
|
|
264
|
+
destroy() {
|
|
265
|
+
if (this.hiddenInput && this.hiddenInput.parentNode) {
|
|
266
|
+
this.hiddenInput.parentNode.removeChild(this.hiddenInput);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export default NumberInput;
|