juxscript 1.1.30 → 1.1.32
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/index.d.ts +2 -0
- package/index.d.ts.map +1 -1
- package/index.js +2 -0
- package/lib/components/pen.d.ts +125 -0
- package/lib/components/pen.d.ts.map +1 -0
- package/lib/components/pen.js +443 -0
- package/lib/components/pen.ts +567 -0
- package/lib/components/tabs.d.ts +21 -8
- package/lib/components/tabs.d.ts.map +1 -1
- package/lib/components/tabs.js +151 -116
- package/lib/components/tabs.ts +182 -131
- package/package.json +1 -1
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
import { BaseComponent, BaseState } from './base/BaseComponent.js';
|
|
2
|
+
|
|
3
|
+
// Event definitions
|
|
4
|
+
const TRIGGER_EVENTS = [] as const;
|
|
5
|
+
const CALLBACK_EVENTS = ['draw', 'shapeAdd', 'shapeRemove', 'connectionAdd'] as const;
|
|
6
|
+
|
|
7
|
+
export interface PenShape {
|
|
8
|
+
id: string;
|
|
9
|
+
type: 'line' | 'rectangle' | 'circle' | 'text';
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
width?: number;
|
|
13
|
+
height?: number;
|
|
14
|
+
radius?: number;
|
|
15
|
+
text?: string;
|
|
16
|
+
color?: string;
|
|
17
|
+
fillColor?: string;
|
|
18
|
+
strokeWidth?: number;
|
|
19
|
+
connectedTo?: string[]; // IDs of shapes connected to this one
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface PenConnection {
|
|
23
|
+
from: string;
|
|
24
|
+
to: string;
|
|
25
|
+
color?: string;
|
|
26
|
+
strokeWidth?: number;
|
|
27
|
+
dashed?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface PenOptions {
|
|
31
|
+
width?: number | string;
|
|
32
|
+
height?: number | string;
|
|
33
|
+
backgroundColor?: string;
|
|
34
|
+
backgroundImage?: string;
|
|
35
|
+
border?: string;
|
|
36
|
+
text?: string;
|
|
37
|
+
radius?: number;
|
|
38
|
+
gridEnabled?: boolean;
|
|
39
|
+
gridSize?: number;
|
|
40
|
+
gridColor?: string;
|
|
41
|
+
style?: string;
|
|
42
|
+
class?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type PenState = BaseState & {
|
|
46
|
+
width: number | string;
|
|
47
|
+
height: number | string;
|
|
48
|
+
backgroundColor: string;
|
|
49
|
+
backgroundImage: string;
|
|
50
|
+
border: string;
|
|
51
|
+
text: string;
|
|
52
|
+
radius: number;
|
|
53
|
+
gridEnabled: boolean;
|
|
54
|
+
gridSize: number;
|
|
55
|
+
gridColor: string;
|
|
56
|
+
shapes: PenShape[];
|
|
57
|
+
connections: PenConnection[];
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export class Pen extends BaseComponent<PenState> {
|
|
61
|
+
private _canvas: HTMLCanvasElement | null = null;
|
|
62
|
+
private _ctx: CanvasRenderingContext2D | null = null;
|
|
63
|
+
private _animationFrame?: number;
|
|
64
|
+
|
|
65
|
+
constructor(id: string, options: PenOptions = {}) {
|
|
66
|
+
super(id, {
|
|
67
|
+
width: options.width ?? '100%',
|
|
68
|
+
height: options.height ?? 400,
|
|
69
|
+
backgroundColor: options.backgroundColor ?? '#ffffff',
|
|
70
|
+
backgroundImage: options.backgroundImage ?? '',
|
|
71
|
+
border: options.border ?? '1px solid #e5e7eb',
|
|
72
|
+
text: options.text ?? '',
|
|
73
|
+
radius: options.radius ?? 0,
|
|
74
|
+
gridEnabled: options.gridEnabled ?? false,
|
|
75
|
+
gridSize: options.gridSize ?? 20,
|
|
76
|
+
gridColor: options.gridColor ?? '#f0f0f0',
|
|
77
|
+
shapes: [],
|
|
78
|
+
connections: [],
|
|
79
|
+
style: options.style ?? '',
|
|
80
|
+
class: options.class ?? ''
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
protected getTriggerEvents(): readonly string[] {
|
|
85
|
+
return TRIGGER_EVENTS;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
protected getCallbackEvents(): readonly string[] {
|
|
89
|
+
return CALLBACK_EVENTS;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
93
|
+
* REACTIVE UPDATE
|
|
94
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
95
|
+
|
|
96
|
+
update(prop: string, value: any): void {
|
|
97
|
+
super.update(prop, value);
|
|
98
|
+
|
|
99
|
+
if (!this._canvas || !this._ctx) return;
|
|
100
|
+
|
|
101
|
+
switch (prop) {
|
|
102
|
+
case 'backgroundColor':
|
|
103
|
+
this._redraw();
|
|
104
|
+
break;
|
|
105
|
+
case 'backgroundImage':
|
|
106
|
+
this._redraw();
|
|
107
|
+
break;
|
|
108
|
+
case 'text':
|
|
109
|
+
this._redraw();
|
|
110
|
+
break;
|
|
111
|
+
case 'gridEnabled':
|
|
112
|
+
case 'gridSize':
|
|
113
|
+
case 'gridColor':
|
|
114
|
+
this._redraw();
|
|
115
|
+
break;
|
|
116
|
+
case 'shapes':
|
|
117
|
+
case 'connections':
|
|
118
|
+
this._redraw();
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
124
|
+
* FLUENT API
|
|
125
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
126
|
+
|
|
127
|
+
width(value: number | string): this {
|
|
128
|
+
this.state.width = value;
|
|
129
|
+
if (this._canvas) {
|
|
130
|
+
this._canvas.style.width = typeof value === 'number' ? `${value}px` : value;
|
|
131
|
+
this._updateCanvasSize();
|
|
132
|
+
}
|
|
133
|
+
return this;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
height(value: number | string): this {
|
|
137
|
+
this.state.height = value;
|
|
138
|
+
if (this._canvas) {
|
|
139
|
+
this._canvas.style.height = typeof value === 'number' ? `${value}px` : value;
|
|
140
|
+
this._updateCanvasSize();
|
|
141
|
+
}
|
|
142
|
+
return this;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
backgroundColor(value: string): this {
|
|
146
|
+
this.state.backgroundColor = value;
|
|
147
|
+
return this;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
backgroundImage(value: string): this {
|
|
151
|
+
this.state.backgroundImage = value;
|
|
152
|
+
return this;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
border(value: string): this {
|
|
156
|
+
this.state.border = value;
|
|
157
|
+
if (this._canvas) {
|
|
158
|
+
this._canvas.style.border = value;
|
|
159
|
+
}
|
|
160
|
+
return this;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
text(value: string): this {
|
|
164
|
+
this.state.text = value;
|
|
165
|
+
return this;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
radius(value: number): this {
|
|
169
|
+
this.state.radius = value;
|
|
170
|
+
if (this._canvas) {
|
|
171
|
+
this._canvas.style.borderRadius = `${value}px`;
|
|
172
|
+
}
|
|
173
|
+
return this;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
grid(enabled: boolean, size?: number, color?: string): this {
|
|
177
|
+
this.state.gridEnabled = enabled;
|
|
178
|
+
if (size !== undefined) this.state.gridSize = size;
|
|
179
|
+
if (color !== undefined) this.state.gridColor = color;
|
|
180
|
+
return this;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
184
|
+
* DRAWING API
|
|
185
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Add a line shape
|
|
189
|
+
*/
|
|
190
|
+
drawLine(x1: number, y1: number, x2: number, y2: number, options: Partial<PenShape> = {}): this {
|
|
191
|
+
const shape: PenShape = {
|
|
192
|
+
id: options.id || `line-${Date.now()}-${Math.random()}`,
|
|
193
|
+
type: 'line',
|
|
194
|
+
x: x1,
|
|
195
|
+
y: y1,
|
|
196
|
+
width: x2 - x1,
|
|
197
|
+
height: y2 - y1,
|
|
198
|
+
color: options.color || '#000000',
|
|
199
|
+
strokeWidth: options.strokeWidth || 2,
|
|
200
|
+
connectedTo: options.connectedTo || []
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
this.state.shapes = [...this.state.shapes, shape];
|
|
204
|
+
this._triggerCallback('shapeAdd', shape);
|
|
205
|
+
return this;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Add a rectangle shape
|
|
210
|
+
*/
|
|
211
|
+
drawRect(x: number, y: number, width: number, height: number, options: Partial<PenShape> = {}): this {
|
|
212
|
+
const shape: PenShape = {
|
|
213
|
+
id: options.id || `rect-${Date.now()}-${Math.random()}`,
|
|
214
|
+
type: 'rectangle',
|
|
215
|
+
x,
|
|
216
|
+
y,
|
|
217
|
+
width,
|
|
218
|
+
height,
|
|
219
|
+
color: options.color || '#000000',
|
|
220
|
+
fillColor: options.fillColor,
|
|
221
|
+
strokeWidth: options.strokeWidth || 2,
|
|
222
|
+
connectedTo: options.connectedTo || []
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
this.state.shapes = [...this.state.shapes, shape];
|
|
226
|
+
this._triggerCallback('shapeAdd', shape);
|
|
227
|
+
return this;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Add a circle shape
|
|
232
|
+
*/
|
|
233
|
+
drawCircle(x: number, y: number, radius: number, options: Partial<PenShape> = {}): this {
|
|
234
|
+
const shape: PenShape = {
|
|
235
|
+
id: options.id || `circle-${Date.now()}-${Math.random()}`,
|
|
236
|
+
type: 'circle',
|
|
237
|
+
x,
|
|
238
|
+
y,
|
|
239
|
+
radius,
|
|
240
|
+
color: options.color || '#000000',
|
|
241
|
+
fillColor: options.fillColor,
|
|
242
|
+
strokeWidth: options.strokeWidth || 2,
|
|
243
|
+
connectedTo: options.connectedTo || []
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
this.state.shapes = [...this.state.shapes, shape];
|
|
247
|
+
this._triggerCallback('shapeAdd', shape);
|
|
248
|
+
return this;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Add text to canvas
|
|
253
|
+
*/
|
|
254
|
+
drawText(x: number, y: number, text: string, options: Partial<PenShape> = {}): this {
|
|
255
|
+
const shape: PenShape = {
|
|
256
|
+
id: options.id || `text-${Date.now()}-${Math.random()}`,
|
|
257
|
+
type: 'text',
|
|
258
|
+
x,
|
|
259
|
+
y,
|
|
260
|
+
text,
|
|
261
|
+
color: options.color || '#000000',
|
|
262
|
+
connectedTo: options.connectedTo || []
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
this.state.shapes = [...this.state.shapes, shape];
|
|
266
|
+
this._triggerCallback('shapeAdd', shape);
|
|
267
|
+
return this;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Connect two shapes with a line
|
|
272
|
+
*/
|
|
273
|
+
connect(fromId: string, toId: string, options: Partial<PenConnection> = {}): this {
|
|
274
|
+
const connection: PenConnection = {
|
|
275
|
+
from: fromId,
|
|
276
|
+
to: toId,
|
|
277
|
+
color: options.color || '#666666',
|
|
278
|
+
strokeWidth: options.strokeWidth || 1,
|
|
279
|
+
dashed: options.dashed || false
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
this.state.connections = [...this.state.connections, connection];
|
|
283
|
+
|
|
284
|
+
// Update connected shapes
|
|
285
|
+
const fromShape = this.state.shapes.find(s => s.id === fromId);
|
|
286
|
+
if (fromShape && !fromShape.connectedTo?.includes(toId)) {
|
|
287
|
+
fromShape.connectedTo = [...(fromShape.connectedTo || []), toId];
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
this._triggerCallback('connectionAdd', connection);
|
|
291
|
+
return this;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Remove a shape by ID
|
|
296
|
+
*/
|
|
297
|
+
removeShape(id: string): this {
|
|
298
|
+
this.state.shapes = this.state.shapes.filter(s => s.id !== id);
|
|
299
|
+
this.state.connections = this.state.connections.filter(c => c.from !== id && c.to !== id);
|
|
300
|
+
this._triggerCallback('shapeRemove', id);
|
|
301
|
+
return this;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Clear all shapes and connections
|
|
306
|
+
*/
|
|
307
|
+
clear(): this {
|
|
308
|
+
this.state.shapes = [];
|
|
309
|
+
this.state.connections = [];
|
|
310
|
+
return this;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Get shape by ID
|
|
315
|
+
*/
|
|
316
|
+
getShape(id: string): PenShape | undefined {
|
|
317
|
+
return this.state.shapes.find(s => s.id === id);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Get all shapes
|
|
322
|
+
*/
|
|
323
|
+
getShapes(): PenShape[] {
|
|
324
|
+
return this.state.shapes;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Export canvas as data URL
|
|
329
|
+
*/
|
|
330
|
+
toDataURL(type: string = 'image/png'): string {
|
|
331
|
+
return this._canvas?.toDataURL(type) || '';
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Download canvas as image
|
|
336
|
+
*/
|
|
337
|
+
download(filename: string = 'pen-drawing.png'): this {
|
|
338
|
+
const dataURL = this.toDataURL();
|
|
339
|
+
const link = document.createElement('a');
|
|
340
|
+
link.download = filename;
|
|
341
|
+
link.href = dataURL;
|
|
342
|
+
link.click();
|
|
343
|
+
return this;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
347
|
+
* PRIVATE RENDERING
|
|
348
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
349
|
+
|
|
350
|
+
private _updateCanvasSize(): void {
|
|
351
|
+
if (!this._canvas) return;
|
|
352
|
+
|
|
353
|
+
const rect = this._canvas.getBoundingClientRect();
|
|
354
|
+
this._canvas.width = rect.width;
|
|
355
|
+
this._canvas.height = rect.height;
|
|
356
|
+
this._redraw();
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
private _redraw(): void {
|
|
360
|
+
if (!this._ctx || !this._canvas) return;
|
|
361
|
+
|
|
362
|
+
const { width, height } = this._canvas;
|
|
363
|
+
|
|
364
|
+
// Clear canvas
|
|
365
|
+
this._ctx.clearRect(0, 0, width, height);
|
|
366
|
+
|
|
367
|
+
// Draw background
|
|
368
|
+
if (this.state.backgroundColor) {
|
|
369
|
+
this._ctx.fillStyle = this.state.backgroundColor;
|
|
370
|
+
this._ctx.fillRect(0, 0, width, height);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Draw background image
|
|
374
|
+
if (this.state.backgroundImage) {
|
|
375
|
+
const img = new Image();
|
|
376
|
+
img.src = this.state.backgroundImage;
|
|
377
|
+
img.onload = () => {
|
|
378
|
+
this._ctx?.drawImage(img, 0, 0, width, height);
|
|
379
|
+
this._drawContent();
|
|
380
|
+
};
|
|
381
|
+
} else {
|
|
382
|
+
this._drawContent();
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
private _drawContent(): void {
|
|
387
|
+
if (!this._ctx || !this._canvas) return;
|
|
388
|
+
|
|
389
|
+
// Draw grid
|
|
390
|
+
if (this.state.gridEnabled) {
|
|
391
|
+
this._drawGrid();
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Draw connections first (under shapes)
|
|
395
|
+
this.state.connections.forEach(conn => this._drawConnection(conn));
|
|
396
|
+
|
|
397
|
+
// Draw shapes
|
|
398
|
+
this.state.shapes.forEach(shape => this._drawShape(shape));
|
|
399
|
+
|
|
400
|
+
// Draw text overlay
|
|
401
|
+
if (this.state.text) {
|
|
402
|
+
this._ctx.fillStyle = '#000000';
|
|
403
|
+
this._ctx.font = '16px sans-serif';
|
|
404
|
+
this._ctx.fillText(this.state.text, 10, 30);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
this._triggerCallback('draw', { shapes: this.state.shapes, connections: this.state.connections });
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
private _drawGrid(): void {
|
|
411
|
+
if (!this._ctx || !this._canvas) return;
|
|
412
|
+
|
|
413
|
+
const { width, height } = this._canvas;
|
|
414
|
+
const { gridSize, gridColor } = this.state;
|
|
415
|
+
|
|
416
|
+
this._ctx.strokeStyle = gridColor;
|
|
417
|
+
this._ctx.lineWidth = 1;
|
|
418
|
+
|
|
419
|
+
// Vertical lines
|
|
420
|
+
for (let x = 0; x <= width; x += gridSize) {
|
|
421
|
+
this._ctx.beginPath();
|
|
422
|
+
this._ctx.moveTo(x, 0);
|
|
423
|
+
this._ctx.lineTo(x, height);
|
|
424
|
+
this._ctx.stroke();
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Horizontal lines
|
|
428
|
+
for (let y = 0; y <= height; y += gridSize) {
|
|
429
|
+
this._ctx.beginPath();
|
|
430
|
+
this._ctx.moveTo(0, y);
|
|
431
|
+
this._ctx.lineTo(width, y);
|
|
432
|
+
this._ctx.stroke();
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
private _drawShape(shape: PenShape): void {
|
|
437
|
+
if (!this._ctx) return;
|
|
438
|
+
|
|
439
|
+
this._ctx.strokeStyle = shape.color || '#000000';
|
|
440
|
+
this._ctx.lineWidth = shape.strokeWidth || 2;
|
|
441
|
+
|
|
442
|
+
switch (shape.type) {
|
|
443
|
+
case 'line':
|
|
444
|
+
this._ctx.beginPath();
|
|
445
|
+
this._ctx.moveTo(shape.x, shape.y);
|
|
446
|
+
this._ctx.lineTo(shape.x + (shape.width || 0), shape.y + (shape.height || 0));
|
|
447
|
+
this._ctx.stroke();
|
|
448
|
+
break;
|
|
449
|
+
|
|
450
|
+
case 'rectangle':
|
|
451
|
+
if (shape.fillColor) {
|
|
452
|
+
this._ctx.fillStyle = shape.fillColor;
|
|
453
|
+
this._ctx.fillRect(shape.x, shape.y, shape.width || 0, shape.height || 0);
|
|
454
|
+
}
|
|
455
|
+
this._ctx.strokeRect(shape.x, shape.y, shape.width || 0, shape.height || 0);
|
|
456
|
+
break;
|
|
457
|
+
|
|
458
|
+
case 'circle':
|
|
459
|
+
this._ctx.beginPath();
|
|
460
|
+
this._ctx.arc(shape.x, shape.y, shape.radius || 0, 0, Math.PI * 2);
|
|
461
|
+
if (shape.fillColor) {
|
|
462
|
+
this._ctx.fillStyle = shape.fillColor;
|
|
463
|
+
this._ctx.fill();
|
|
464
|
+
}
|
|
465
|
+
this._ctx.stroke();
|
|
466
|
+
break;
|
|
467
|
+
|
|
468
|
+
case 'text':
|
|
469
|
+
this._ctx.fillStyle = shape.color || '#000000';
|
|
470
|
+
this._ctx.font = '14px sans-serif';
|
|
471
|
+
this._ctx.fillText(shape.text || '', shape.x, shape.y);
|
|
472
|
+
break;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
private _drawConnection(conn: PenConnection): void {
|
|
477
|
+
if (!this._ctx) return;
|
|
478
|
+
|
|
479
|
+
const fromShape = this.state.shapes.find(s => s.id === conn.from);
|
|
480
|
+
const toShape = this.state.shapes.find(s => s.id === conn.to);
|
|
481
|
+
|
|
482
|
+
if (!fromShape || !toShape) return;
|
|
483
|
+
|
|
484
|
+
// Calculate center points
|
|
485
|
+
const fromX = fromShape.x + (fromShape.width || fromShape.radius || 0) / 2;
|
|
486
|
+
const fromY = fromShape.y + (fromShape.height || fromShape.radius || 0) / 2;
|
|
487
|
+
const toX = toShape.x + (toShape.width || toShape.radius || 0) / 2;
|
|
488
|
+
const toY = toShape.y + (toShape.height || toShape.radius || 0) / 2;
|
|
489
|
+
|
|
490
|
+
this._ctx.strokeStyle = conn.color || '#666666';
|
|
491
|
+
this._ctx.lineWidth = conn.strokeWidth || 1;
|
|
492
|
+
|
|
493
|
+
if (conn.dashed) {
|
|
494
|
+
this._ctx.setLineDash([5, 5]);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
this._ctx.beginPath();
|
|
498
|
+
this._ctx.moveTo(fromX, fromY);
|
|
499
|
+
this._ctx.lineTo(toX, toY);
|
|
500
|
+
this._ctx.stroke();
|
|
501
|
+
|
|
502
|
+
if (conn.dashed) {
|
|
503
|
+
this._ctx.setLineDash([]);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/* ═════════════════════════════════════════════════════════════════
|
|
508
|
+
* RENDER
|
|
509
|
+
* ═════════════════════════════════════════════════════════════════ */
|
|
510
|
+
|
|
511
|
+
render(targetId?: string): this {
|
|
512
|
+
const container = this._setupContainer(targetId);
|
|
513
|
+
|
|
514
|
+
const { width, height, backgroundColor, border, radius, style, class: className } = this.state;
|
|
515
|
+
|
|
516
|
+
const wrapper = document.createElement('div');
|
|
517
|
+
wrapper.className = 'jux-pen';
|
|
518
|
+
wrapper.id = `${this._id}-wrapper`;
|
|
519
|
+
if (className) wrapper.className += ` ${className}`;
|
|
520
|
+
if (style) wrapper.setAttribute('style', style);
|
|
521
|
+
|
|
522
|
+
const canvas = document.createElement('canvas');
|
|
523
|
+
canvas.id = this._id;
|
|
524
|
+
canvas.className = 'jux-pen-canvas';
|
|
525
|
+
canvas.style.width = typeof width === 'number' ? `${width}px` : width;
|
|
526
|
+
canvas.style.height = typeof height === 'number' ? `${height}px` : height;
|
|
527
|
+
canvas.style.backgroundColor = backgroundColor;
|
|
528
|
+
canvas.style.border = border;
|
|
529
|
+
canvas.style.borderRadius = `${radius}px`;
|
|
530
|
+
canvas.style.display = 'block';
|
|
531
|
+
|
|
532
|
+
this._canvas = canvas;
|
|
533
|
+
this._ctx = canvas.getContext('2d');
|
|
534
|
+
|
|
535
|
+
// Set initial canvas size
|
|
536
|
+
requestAnimationFrame(() => {
|
|
537
|
+
this._updateCanvasSize();
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
// Handle window resize
|
|
541
|
+
window.addEventListener('resize', () => {
|
|
542
|
+
this._updateCanvasSize();
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
this._wireStandardEvents(wrapper);
|
|
546
|
+
this._wireAllSyncs();
|
|
547
|
+
|
|
548
|
+
wrapper.appendChild(canvas);
|
|
549
|
+
container.appendChild(wrapper);
|
|
550
|
+
|
|
551
|
+
return this;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Cleanup on destroy
|
|
556
|
+
*/
|
|
557
|
+
destroy(): void {
|
|
558
|
+
if (this._animationFrame) {
|
|
559
|
+
cancelAnimationFrame(this._animationFrame);
|
|
560
|
+
}
|
|
561
|
+
this.remove();
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
export function pen(id: string, options: PenOptions = {}): Pen {
|
|
566
|
+
return new Pen(id, options);
|
|
567
|
+
}
|
package/lib/components/tabs.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { BaseComponent } from './base/BaseComponent.js';
|
|
1
|
+
import { BaseComponent, BaseState } from './base/BaseComponent.js';
|
|
2
2
|
export interface Tab {
|
|
3
3
|
id: string;
|
|
4
4
|
label: string;
|
|
5
|
-
content: string | HTMLElement
|
|
5
|
+
content: string | HTMLElement | BaseComponent<any>;
|
|
6
6
|
icon?: string;
|
|
7
7
|
}
|
|
8
8
|
export interface TabsOptions {
|
|
@@ -12,25 +12,38 @@ export interface TabsOptions {
|
|
|
12
12
|
style?: string;
|
|
13
13
|
class?: string;
|
|
14
14
|
}
|
|
15
|
-
type TabsState = {
|
|
15
|
+
type TabsState = BaseState & {
|
|
16
16
|
tabs: Tab[];
|
|
17
17
|
activeTab: string;
|
|
18
18
|
variant: string;
|
|
19
|
-
style: string;
|
|
20
|
-
class: string;
|
|
21
19
|
};
|
|
22
20
|
export declare class Tabs extends BaseComponent<TabsState> {
|
|
21
|
+
private _tabsWrapper;
|
|
23
22
|
constructor(id: string, options?: TabsOptions);
|
|
24
23
|
protected getTriggerEvents(): readonly string[];
|
|
25
24
|
protected getCallbackEvents(): readonly string[];
|
|
25
|
+
update(prop: string, value: any): void;
|
|
26
|
+
private _updateActiveTab;
|
|
27
|
+
private _rebuildTabs;
|
|
28
|
+
/**
|
|
29
|
+
* ✅ NEW: Smart content rendering
|
|
30
|
+
* Handles strings, DOM elements, and component instances
|
|
31
|
+
*/
|
|
32
|
+
private _renderTabContent;
|
|
26
33
|
tabs(value: Tab[]): this;
|
|
27
34
|
addTab(tab: Tab): this;
|
|
35
|
+
removeTab(tabId: string): this;
|
|
28
36
|
activeTab(value: string): this;
|
|
29
37
|
variant(value: 'default' | 'pills' | 'underline'): this;
|
|
30
|
-
|
|
31
|
-
|
|
38
|
+
/**
|
|
39
|
+
* ✅ NEW: Update content of a specific tab
|
|
40
|
+
*/
|
|
41
|
+
updateTabContent(tabId: string, content: string | HTMLElement | BaseComponent<any>): this;
|
|
42
|
+
/**
|
|
43
|
+
* ✅ NEW: Get current tab
|
|
44
|
+
*/
|
|
45
|
+
getCurrentTab(): Tab | undefined;
|
|
32
46
|
render(targetId?: string): this;
|
|
33
|
-
private _switchTab;
|
|
34
47
|
}
|
|
35
48
|
export declare function tabs(id: string, options?: TabsOptions): Tabs;
|
|
36
49
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["tabs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["tabs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAMnE,MAAM,WAAW,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,WAAW,CAAC;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,KAAK,SAAS,GAAG,SAAS,GAAG;IAC3B,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,qBAAa,IAAK,SAAQ,aAAa,CAAC,SAAS,CAAC;IAChD,OAAO,CAAC,YAAY,CAA4B;gBAEpC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB;IAUjD,SAAS,CAAC,gBAAgB,IAAI,SAAS,MAAM,EAAE;IAI/C,SAAS,CAAC,iBAAiB,IAAI,SAAS,MAAM,EAAE;IAQhD,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAuBtC,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,YAAY;IAuDpB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAkBzB,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,IAAI;IAKxB,MAAM,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI;IAKtB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAW9B,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAK9B,OAAO,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,GAAG,WAAW,GAAG,IAAI;IAKvD;;OAEG;IACH,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,IAAI;IAkBzF;;OAEG;IACH,aAAa,IAAI,GAAG,GAAG,SAAS;IAQhC,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;CAqEhC;AAED,wBAAgB,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,IAAI,CAEhE"}
|