canvasframework 0.3.18 → 0.3.19
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/components/Banner.js +342 -0
- package/components/RadioButton.js +17 -9
- package/core/CanvasFramework.js +80 -9
- package/core/UIBuilder.js +2 -0
- package/index.js +2 -1
- package/package.json +1 -1
- package/core/CanvasWork.js +0 -32
- package/core/LogicWorker.js +0 -25
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
// components/Banner.js
|
|
2
|
+
import Component from '../core/Component.js';
|
|
3
|
+
|
|
4
|
+
export default class Banner extends Component {
|
|
5
|
+
constructor(framework, options = {}) {
|
|
6
|
+
super(framework, options);
|
|
7
|
+
|
|
8
|
+
this.text = options.text || '';
|
|
9
|
+
this.type = options.type || 'info';
|
|
10
|
+
this.actions = options.actions || [];
|
|
11
|
+
this.dismissible = options.dismissible === true;
|
|
12
|
+
|
|
13
|
+
this.platform = framework.platform || 'material';
|
|
14
|
+
|
|
15
|
+
this.width = options.width || framework.width || window.innerWidth;
|
|
16
|
+
this.height = options.height || 64;
|
|
17
|
+
this.x = options.x || 0;
|
|
18
|
+
this.y = options.y || 0;
|
|
19
|
+
|
|
20
|
+
this.visible = options.visible !== false;
|
|
21
|
+
this.progress = this.visible ? 1 : 0;
|
|
22
|
+
this.animSpeed = 0.18;
|
|
23
|
+
|
|
24
|
+
this._lastUpdate = performance.now();
|
|
25
|
+
this._colors = this._resolveColors();
|
|
26
|
+
|
|
27
|
+
// Bounds calculées à chaque frame
|
|
28
|
+
this._actionBounds = [];
|
|
29
|
+
this._dismissBounds = null;
|
|
30
|
+
|
|
31
|
+
// Pour indiquer qu'on gère nos propres clics
|
|
32
|
+
this.selfManagedClicks = true;
|
|
33
|
+
|
|
34
|
+
// Écouter les événements directement sur le canvas
|
|
35
|
+
this._setupEventListeners();
|
|
36
|
+
|
|
37
|
+
// Ref si fourni
|
|
38
|
+
if (options.ref) options.ref.current = this;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* ===================== Setup ===================== */
|
|
42
|
+
_setupEventListeners() {
|
|
43
|
+
// Stocker les références pour pouvoir les retirer plus tard
|
|
44
|
+
this._boundHandleClick = this._handleClick.bind(this);
|
|
45
|
+
|
|
46
|
+
// Écouter les événements sur le canvas parent
|
|
47
|
+
if (this.framework && this.framework.canvas) {
|
|
48
|
+
this.framework.canvas.addEventListener('click', this._boundHandleClick);
|
|
49
|
+
this.framework.canvas.addEventListener('touchend', this._boundHandleClick);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
_removeEventListeners() {
|
|
54
|
+
if (this.framework && this.framework.canvas && this._boundHandleClick) {
|
|
55
|
+
this.framework.canvas.removeEventListener('click', this._boundHandleClick);
|
|
56
|
+
this.framework.canvas.removeEventListener('touchend', this._boundHandleClick);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* ===================== Lifecycle ===================== */
|
|
61
|
+
onMount() {
|
|
62
|
+
this._setupEventListeners();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
onUnmount() {
|
|
66
|
+
this._removeEventListeners();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* ===================== Colors ===================== */
|
|
70
|
+
_resolveColors() {
|
|
71
|
+
if (this.platform === 'cupertino') {
|
|
72
|
+
return {
|
|
73
|
+
bg: 'rgba(250,250,250,0.95)',
|
|
74
|
+
fg: '#000',
|
|
75
|
+
accent: '#007AFF',
|
|
76
|
+
divider: 'rgba(60,60,67,0.15)'
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Material v3
|
|
81
|
+
const map = {
|
|
82
|
+
info: '#E8F0FE',
|
|
83
|
+
success: '#E6F4EA',
|
|
84
|
+
warning: '#FEF7E0',
|
|
85
|
+
error: '#FCE8E6'
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
bg: map[this.type] || map.info,
|
|
90
|
+
fg: '#1F1F1F',
|
|
91
|
+
accent: '#1A73E8'
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* ===================== Show/Hide ===================== */
|
|
96
|
+
show() {
|
|
97
|
+
this.visible = true;
|
|
98
|
+
this.markDirty();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
hide() {
|
|
102
|
+
this.visible = false;
|
|
103
|
+
this.markDirty();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* ===================== Update ===================== */
|
|
107
|
+
update() {
|
|
108
|
+
const now = performance.now();
|
|
109
|
+
const dt = Math.min((now - this._lastUpdate) / 16.6, 3);
|
|
110
|
+
|
|
111
|
+
const target = this.visible ? 1 : 0;
|
|
112
|
+
this.progress += (target - this.progress) * this.animSpeed * dt;
|
|
113
|
+
this.progress = Math.max(0, Math.min(1, this.progress));
|
|
114
|
+
|
|
115
|
+
if (Math.abs(target - this.progress) > 0.01) this.markDirty();
|
|
116
|
+
|
|
117
|
+
this._lastUpdate = now;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* ===================== Draw ===================== */
|
|
121
|
+
draw(ctx) {
|
|
122
|
+
this.update();
|
|
123
|
+
if (this.progress <= 0.01) return;
|
|
124
|
+
|
|
125
|
+
const h = this.height * this.progress;
|
|
126
|
+
const visibleHeight = h;
|
|
127
|
+
|
|
128
|
+
ctx.save();
|
|
129
|
+
|
|
130
|
+
// Background
|
|
131
|
+
if (this.platform === 'material') {
|
|
132
|
+
ctx.shadowColor = 'rgba(0,0,0,0.18)';
|
|
133
|
+
ctx.shadowBlur = 8;
|
|
134
|
+
ctx.shadowOffsetY = 2;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
ctx.fillStyle = this._colors.bg;
|
|
138
|
+
ctx.fillRect(this.x, this.y, this.width, visibleHeight);
|
|
139
|
+
ctx.shadowColor = 'transparent';
|
|
140
|
+
|
|
141
|
+
// Divider iOS
|
|
142
|
+
if (this.platform === 'cupertino') {
|
|
143
|
+
ctx.strokeStyle = this._colors.divider;
|
|
144
|
+
ctx.beginPath();
|
|
145
|
+
ctx.moveTo(this.x, this.y + visibleHeight);
|
|
146
|
+
ctx.lineTo(this.x + this.width, this.y + visibleHeight);
|
|
147
|
+
ctx.stroke();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Text
|
|
151
|
+
ctx.fillStyle = this._colors.fg;
|
|
152
|
+
ctx.font =
|
|
153
|
+
this.platform === 'cupertino'
|
|
154
|
+
? '400 15px -apple-system'
|
|
155
|
+
: '400 14px Roboto, sans-serif';
|
|
156
|
+
ctx.textBaseline = 'middle';
|
|
157
|
+
ctx.textAlign = 'left';
|
|
158
|
+
ctx.fillText(this.text, this.x + 16, this.y + visibleHeight / 2);
|
|
159
|
+
|
|
160
|
+
// Actions - calculer et stocker les bounds
|
|
161
|
+
this._actionBounds = [];
|
|
162
|
+
let x = this.width - 16;
|
|
163
|
+
|
|
164
|
+
for (let i = this.actions.length - 1; i >= 0; i--) {
|
|
165
|
+
const action = this.actions[i];
|
|
166
|
+
const textWidth = ctx.measureText(action.label).width + 20;
|
|
167
|
+
x -= textWidth;
|
|
168
|
+
|
|
169
|
+
ctx.fillStyle = this._colors.accent;
|
|
170
|
+
ctx.textAlign = 'center';
|
|
171
|
+
ctx.textBaseline = 'middle';
|
|
172
|
+
ctx.fillText(action.label, this.x + x + textWidth / 2, this.y + visibleHeight / 2);
|
|
173
|
+
|
|
174
|
+
// Stocker la hitbox (en coordonnées écran, pas canvas)
|
|
175
|
+
this._actionBounds.push({
|
|
176
|
+
action: action,
|
|
177
|
+
bounds: {
|
|
178
|
+
x: this.x + x,
|
|
179
|
+
y: this.y + (visibleHeight - 44) / 2,
|
|
180
|
+
w: textWidth,
|
|
181
|
+
h: 44
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
x -= 12;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Dismiss button
|
|
189
|
+
if (this.dismissible) {
|
|
190
|
+
const hitSize = 44;
|
|
191
|
+
const cx = this.width - 28;
|
|
192
|
+
const cy = this.y + visibleHeight / 2;
|
|
193
|
+
|
|
194
|
+
ctx.fillStyle =
|
|
195
|
+
this.platform === 'cupertino'
|
|
196
|
+
? 'rgba(60,60,67,0.6)'
|
|
197
|
+
: this._colors.fg;
|
|
198
|
+
|
|
199
|
+
ctx.font =
|
|
200
|
+
this.platform === 'cupertino'
|
|
201
|
+
? '600 16px -apple-system'
|
|
202
|
+
: '500 16px Roboto';
|
|
203
|
+
ctx.textAlign = 'center';
|
|
204
|
+
ctx.textBaseline = 'middle';
|
|
205
|
+
ctx.fillText('×', cx, cy);
|
|
206
|
+
|
|
207
|
+
this._dismissBounds = {
|
|
208
|
+
x: cx - hitSize / 2,
|
|
209
|
+
y: cy - hitSize / 2,
|
|
210
|
+
w: hitSize,
|
|
211
|
+
h: hitSize
|
|
212
|
+
};
|
|
213
|
+
} else {
|
|
214
|
+
this._dismissBounds = null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
ctx.restore();
|
|
218
|
+
|
|
219
|
+
// DEBUG: Dessiner les hitboxes
|
|
220
|
+
if (this.framework && this.framework.debbug) {
|
|
221
|
+
this._drawDebugHitboxes(ctx);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/* ===================== Debug ===================== */
|
|
226
|
+
_drawDebugHitboxes(ctx) {
|
|
227
|
+
ctx.save();
|
|
228
|
+
ctx.strokeStyle = 'red';
|
|
229
|
+
ctx.lineWidth = 1;
|
|
230
|
+
ctx.fillStyle = 'rgba(255, 0, 0, 0.1)';
|
|
231
|
+
|
|
232
|
+
// Dessiner la hitbox principale du banner
|
|
233
|
+
const h = this.height * this.progress;
|
|
234
|
+
ctx.strokeRect(this.x, this.y, this.width, h);
|
|
235
|
+
|
|
236
|
+
// Dessiner les hitboxes des actions
|
|
237
|
+
if (this._actionBounds && this._actionBounds.length > 0) {
|
|
238
|
+
for (const item of this._actionBounds) {
|
|
239
|
+
const b = item.bounds;
|
|
240
|
+
ctx.fillRect(b.x, b.y, b.w, b.h);
|
|
241
|
+
ctx.strokeRect(b.x, b.y, b.w, b.h);
|
|
242
|
+
|
|
243
|
+
// Texte de debug
|
|
244
|
+
ctx.fillStyle = 'red';
|
|
245
|
+
ctx.font = '10px monospace';
|
|
246
|
+
ctx.fillText(item.action.label, b.x + 5, b.y + 12);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Dessiner la hitbox du dismiss button
|
|
251
|
+
if (this._dismissBounds) {
|
|
252
|
+
const b = this._dismissBounds;
|
|
253
|
+
ctx.fillRect(b.x, b.y, b.w, b.h);
|
|
254
|
+
ctx.strokeRect(b.x, b.y, b.w, b.h);
|
|
255
|
+
ctx.fillText('X', b.x + 5, b.y + 12);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
ctx.restore();
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/* ===================== Click Handling ===================== */
|
|
262
|
+
_handleClick(event) {
|
|
263
|
+
if (this.progress < 0.95) return;
|
|
264
|
+
|
|
265
|
+
// Obtenir les coordonnées du clic/touch
|
|
266
|
+
let clientX, clientY;
|
|
267
|
+
|
|
268
|
+
if (event.type === 'touchend') {
|
|
269
|
+
const touch = event.changedTouches[0];
|
|
270
|
+
clientX = touch.clientX;
|
|
271
|
+
clientY = touch.clientY;
|
|
272
|
+
} else {
|
|
273
|
+
clientX = event.clientX;
|
|
274
|
+
clientY = event.clientY;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Convertir en coordonnées canvas SIMPLIFIÉ
|
|
278
|
+
const canvasRect = this.framework.canvas.getBoundingClientRect();
|
|
279
|
+
|
|
280
|
+
// Coordonnées relatives au canvas (en pixels CSS, pas en pixels canvas)
|
|
281
|
+
const x = clientX - canvasRect.left;
|
|
282
|
+
const y = clientY - canvasRect.top;
|
|
283
|
+
|
|
284
|
+
console.log('Click converted:', {
|
|
285
|
+
clientX, clientY,
|
|
286
|
+
canvasLeft: canvasRect.left,
|
|
287
|
+
canvasTop: canvasRect.top,
|
|
288
|
+
x, y,
|
|
289
|
+
bannerX: this.x,
|
|
290
|
+
bannerY: this.y,
|
|
291
|
+
bannerWidth: this.width,
|
|
292
|
+
bannerHeight: this.height * this.progress
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// Vérifier si on clique sur le banner (en coordonnées CSS)
|
|
296
|
+
const bannerBottom = this.y + (this.height * this.progress);
|
|
297
|
+
if (x < this.x || x > this.x + this.width || y < this.y || y > bannerBottom) {
|
|
298
|
+
console.log('Click outside banner');
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
console.log('Click INSIDE banner!');
|
|
303
|
+
|
|
304
|
+
// Empêcher la propagation
|
|
305
|
+
event.stopPropagation();
|
|
306
|
+
|
|
307
|
+
// 1️⃣ Dismiss button
|
|
308
|
+
if (this.dismissible && this._dismissBounds) {
|
|
309
|
+
const b = this._dismissBounds;
|
|
310
|
+
console.log('Checking dismiss bounds:', b, 'click:', {x, y});
|
|
311
|
+
if (x >= b.x && x <= b.x + b.w &&
|
|
312
|
+
y >= b.y && y <= b.y + b.h) {
|
|
313
|
+
console.log('Dismiss clicked!');
|
|
314
|
+
this.hide();
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// 2️⃣ Actions
|
|
320
|
+
if (this._actionBounds && this._actionBounds.length > 0) {
|
|
321
|
+
console.log('Checking', this._actionBounds.length, 'action bounds');
|
|
322
|
+
for (const item of this._actionBounds) {
|
|
323
|
+
const b = item.bounds;
|
|
324
|
+
console.log('Checking action:', item.action.label, 'bounds:', b);
|
|
325
|
+
if (x >= b.x && x <= b.x + b.w &&
|
|
326
|
+
y >= b.y && y <= b.y + b.h) {
|
|
327
|
+
console.log('Action clicked:', item.action.label);
|
|
328
|
+
item.action.onClick?.();
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
console.log('Click on banner but not on any button');
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/* ===================== Resize ===================== */
|
|
338
|
+
_resize(width) {
|
|
339
|
+
this.width = width;
|
|
340
|
+
this.markDirty();
|
|
341
|
+
}
|
|
342
|
+
}
|
|
@@ -97,19 +97,27 @@ class RadioButton extends Component {
|
|
|
97
97
|
ctx.arc(centerX, centerY, 5, 0, Math.PI * 2);
|
|
98
98
|
ctx.fill();
|
|
99
99
|
}
|
|
100
|
-
|
|
101
|
-
// Cupertino
|
|
102
|
-
ctx.strokeStyle = this.checked ? '#007AFF' : '#C7C7CC';
|
|
103
|
-
ctx.lineWidth = 2;
|
|
104
|
-
ctx.beginPath();
|
|
105
|
-
ctx.arc(centerX, centerY, this.circleRadius, 0, Math.PI * 2);
|
|
106
|
-
ctx.stroke();
|
|
107
|
-
|
|
100
|
+
} else {
|
|
101
|
+
// Cupertino (iOS style)
|
|
108
102
|
if (this.checked) {
|
|
103
|
+
// Cercle bleu rempli
|
|
109
104
|
ctx.fillStyle = '#007AFF';
|
|
110
105
|
ctx.beginPath();
|
|
111
|
-
ctx.arc(centerX, centerY,
|
|
106
|
+
ctx.arc(centerX, centerY, this.circleRadius, 0, Math.PI * 2);
|
|
107
|
+
ctx.fill();
|
|
108
|
+
|
|
109
|
+
// Point blanc au centre
|
|
110
|
+
ctx.fillStyle = '#FFFFFF';
|
|
111
|
+
ctx.beginPath();
|
|
112
|
+
ctx.arc(centerX, centerY, 4, 0, Math.PI * 2);
|
|
112
113
|
ctx.fill();
|
|
114
|
+
} else {
|
|
115
|
+
// Cercle gris clair
|
|
116
|
+
ctx.strokeStyle = '#D1D1D6';
|
|
117
|
+
ctx.lineWidth = 1.5;
|
|
118
|
+
ctx.beginPath();
|
|
119
|
+
ctx.arc(centerX, centerY, this.circleRadius, 0, Math.PI * 2);
|
|
120
|
+
ctx.stroke();
|
|
113
121
|
}
|
|
114
122
|
}
|
|
115
123
|
|
package/core/CanvasFramework.js
CHANGED
|
@@ -50,6 +50,7 @@ import ImageCarousel from '../components/ImageCarousel.js';
|
|
|
50
50
|
import PasswordInput from '../components/PasswordInput.js';
|
|
51
51
|
import InputTags from '../components/InputTags.js';
|
|
52
52
|
import InputDatalist from '../components/InputDatalist.js';
|
|
53
|
+
import Banner from '../components/Banner.js';
|
|
53
54
|
|
|
54
55
|
// Utils
|
|
55
56
|
import SafeArea from '../utils/SafeArea.js';
|
|
@@ -122,6 +123,7 @@ const FIXED_COMPONENT_TYPES = new Set([
|
|
|
122
123
|
Modal,
|
|
123
124
|
FAB,
|
|
124
125
|
Toast,
|
|
126
|
+
Banner,
|
|
125
127
|
BottomSheet,
|
|
126
128
|
ContextMenu,
|
|
127
129
|
OpenStreetMap,
|
|
@@ -187,16 +189,17 @@ class CanvasFramework {
|
|
|
187
189
|
this._lastFpsTime = performance.now();
|
|
188
190
|
this.showFps = options.showFps || false; // false par défaut
|
|
189
191
|
this.debbug = options.debug || false; // false par défaut (et correction de la faute de frappe)
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
this.worker
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
// Worker logique pour calculs séparés
|
|
192
|
+
|
|
193
|
+
// Worker pour multithreading Canvas Worker
|
|
194
|
+
this.worker = this.createCanvasWorker();
|
|
195
|
+
this.worker.onmessage = this.handleWorkerMessage.bind(this);
|
|
196
|
+
this.worker.postMessage({ type: 'INIT', payload: { components: [] } });
|
|
196
197
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
198
|
+
// Logic Worker
|
|
199
|
+
this.logicWorker = this.createLogicWorker();
|
|
200
|
+
this.logicWorker.onmessage = this.handleLogicWorkerMessage.bind(this);
|
|
201
|
+
this.logicWorkerState = {};
|
|
202
|
+
this.logicWorker.postMessage({ type: 'SET_STATE', payload: this.state });
|
|
200
203
|
|
|
201
204
|
// Envoyer l'état initial au worker
|
|
202
205
|
this.logicWorker.postMessage({ type: 'SET_STATE', payload: this.state });
|
|
@@ -254,6 +257,74 @@ class CanvasFramework {
|
|
|
254
257
|
get: () => originalFillStyle.get.call(ctx)
|
|
255
258
|
});
|
|
256
259
|
}
|
|
260
|
+
|
|
261
|
+
createCanvasWorker() {
|
|
262
|
+
const workerCode = `
|
|
263
|
+
let components = [];
|
|
264
|
+
|
|
265
|
+
self.onmessage = function(e) {
|
|
266
|
+
const { type, payload } = e.data;
|
|
267
|
+
|
|
268
|
+
switch(type) {
|
|
269
|
+
case 'INIT':
|
|
270
|
+
components = payload.components;
|
|
271
|
+
self.postMessage({ type: 'READY' });
|
|
272
|
+
break;
|
|
273
|
+
|
|
274
|
+
case 'UPDATE_LAYOUT':
|
|
275
|
+
const updated = components.map(comp => {
|
|
276
|
+
if (comp.dynamicHeight && comp.calculateHeight) {
|
|
277
|
+
comp.height = comp.calculateHeight();
|
|
278
|
+
}
|
|
279
|
+
return { id: comp.id, height: comp.height };
|
|
280
|
+
});
|
|
281
|
+
self.postMessage({ type: 'LAYOUT_DONE', payload: updated });
|
|
282
|
+
break;
|
|
283
|
+
|
|
284
|
+
case 'SCROLL_INERTIA':
|
|
285
|
+
let { offset, velocity, friction, maxScroll } = payload;
|
|
286
|
+
offset += velocity;
|
|
287
|
+
offset = Math.max(Math.min(offset, 0), -maxScroll);
|
|
288
|
+
velocity *= friction;
|
|
289
|
+
self.postMessage({ type: 'SCROLL_UPDATED', payload: { offset, velocity } });
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
`;
|
|
294
|
+
|
|
295
|
+
const blob = new Blob([workerCode], { type: 'application/javascript' });
|
|
296
|
+
return new Worker(URL.createObjectURL(blob));
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
createLogicWorker() {
|
|
300
|
+
const workerCode = `
|
|
301
|
+
let state = {};
|
|
302
|
+
|
|
303
|
+
self.onmessage = async function(e) {
|
|
304
|
+
const { type, payload } = e.data;
|
|
305
|
+
|
|
306
|
+
switch(type) {
|
|
307
|
+
case 'SET_STATE':
|
|
308
|
+
state = payload;
|
|
309
|
+
self.postMessage({ type: 'STATE_UPDATED', payload: state });
|
|
310
|
+
break;
|
|
311
|
+
|
|
312
|
+
case 'EXECUTE':
|
|
313
|
+
try {
|
|
314
|
+
const fn = new Function('state', 'args', payload.fnString);
|
|
315
|
+
const result = await fn(state, payload.args);
|
|
316
|
+
self.postMessage({ type: 'EXECUTION_RESULT', payload: result });
|
|
317
|
+
} catch (err) {
|
|
318
|
+
self.postMessage({ type: 'EXECUTION_ERROR', payload: err.message });
|
|
319
|
+
}
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
`;
|
|
324
|
+
|
|
325
|
+
const blob = new Blob([workerCode], { type: 'application/javascript' });
|
|
326
|
+
return new Worker(URL.createObjectURL(blob));
|
|
327
|
+
}
|
|
257
328
|
|
|
258
329
|
// Set Theme dynamique
|
|
259
330
|
setTheme(theme) {
|
package/core/UIBuilder.js
CHANGED
|
@@ -51,6 +51,7 @@ import ImageCarousel from '../components/ImageCarousel.js';
|
|
|
51
51
|
import PasswordInput from '../components/PasswordInput.js';
|
|
52
52
|
import InputTags from '../components/InputTags.js';
|
|
53
53
|
import InputDatalist from '../components/InputDatalist.js';
|
|
54
|
+
import Banner from '../components/Banner.js';
|
|
54
55
|
|
|
55
56
|
// Features
|
|
56
57
|
import PullToRefresh from '../features/PullToRefresh.js';
|
|
@@ -129,6 +130,7 @@ const Components = {
|
|
|
129
130
|
Row,
|
|
130
131
|
Column,
|
|
131
132
|
Positioned,
|
|
133
|
+
Banner,
|
|
132
134
|
Stack
|
|
133
135
|
};
|
|
134
136
|
|
package/index.js
CHANGED
|
@@ -58,6 +58,7 @@ export { default as ImageCarousel } from './components/ImageCarousel.js';
|
|
|
58
58
|
export { default as PasswordInput } from './components/PasswordInput.js';
|
|
59
59
|
export { default as InputTags } from './components/InputTags.js';
|
|
60
60
|
export { default as InputDatalist } from './components/InputDatalist.js';
|
|
61
|
+
export { default as Banner } from './components/Banner.js';
|
|
61
62
|
|
|
62
63
|
// Utils
|
|
63
64
|
export { default as SafeArea } from './utils/SafeArea.js';
|
|
@@ -99,7 +100,7 @@ export { default as FeatureFlags } from './manager/FeatureFlags.js';
|
|
|
99
100
|
|
|
100
101
|
// Version du framework
|
|
101
102
|
|
|
102
|
-
export const VERSION = '0.3.
|
|
103
|
+
export const VERSION = '0.3.19';
|
|
103
104
|
|
|
104
105
|
|
|
105
106
|
|
package/package.json
CHANGED
package/core/CanvasWork.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
// CanvasWorker.js
|
|
2
|
-
let components = [];
|
|
3
|
-
|
|
4
|
-
self.onmessage = (e) => {
|
|
5
|
-
const { type, payload } = e.data;
|
|
6
|
-
|
|
7
|
-
switch(type) {
|
|
8
|
-
case 'INIT':
|
|
9
|
-
components = payload.components;
|
|
10
|
-
self.postMessage({ type: 'READY' });
|
|
11
|
-
break;
|
|
12
|
-
|
|
13
|
-
case 'UPDATE_LAYOUT':
|
|
14
|
-
// Recalculer la hauteur des composants dynamiques
|
|
15
|
-
const updated = components.map(comp => {
|
|
16
|
-
if (comp.dynamicHeight && comp.calculateHeight) {
|
|
17
|
-
comp.height = comp.calculateHeight();
|
|
18
|
-
}
|
|
19
|
-
return { id: comp.id, height: comp.height };
|
|
20
|
-
});
|
|
21
|
-
self.postMessage({ type: 'LAYOUT_DONE', payload: updated });
|
|
22
|
-
break;
|
|
23
|
-
|
|
24
|
-
case 'SCROLL_INERTIA':
|
|
25
|
-
let { offset, velocity, friction, maxScroll } = payload;
|
|
26
|
-
offset += velocity;
|
|
27
|
-
offset = Math.max(Math.min(offset, 0), -maxScroll);
|
|
28
|
-
velocity *= friction;
|
|
29
|
-
self.postMessage({ type: 'SCROLL_UPDATED', payload: { offset, velocity } });
|
|
30
|
-
break;
|
|
31
|
-
}
|
|
32
|
-
};
|
package/core/LogicWorker.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
// LogicWorker.js
|
|
2
|
-
let state = {};
|
|
3
|
-
|
|
4
|
-
self.onmessage = async (e) => {
|
|
5
|
-
const { type, payload } = e.data;
|
|
6
|
-
|
|
7
|
-
switch(type) {
|
|
8
|
-
case 'SET_STATE':
|
|
9
|
-
state = payload;
|
|
10
|
-
self.postMessage({ type: 'STATE_UPDATED', payload: state });
|
|
11
|
-
break;
|
|
12
|
-
|
|
13
|
-
case 'EXECUTE':
|
|
14
|
-
// payload: { fnString: string, args: array }
|
|
15
|
-
// Attention : on envoie la fonction en string et on l'exécute ici
|
|
16
|
-
try {
|
|
17
|
-
const fn = new Function('state', 'args', payload.fnString);
|
|
18
|
-
const result = await fn(state, payload.args);
|
|
19
|
-
self.postMessage({ type: 'EXECUTION_RESULT', payload: result });
|
|
20
|
-
} catch (err) {
|
|
21
|
-
self.postMessage({ type: 'EXECUTION_ERROR', payload: err.message });
|
|
22
|
-
}
|
|
23
|
-
break;
|
|
24
|
-
}
|
|
25
|
-
};
|