@usman404/crowjs 1.0.4 → 1.1.0
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/Core/Component.js +207 -1
- package/Core/GUIEvent/KeyboardEvent.js +2 -1
- package/Core/Root.js +40 -30
- package/Frames/DummyFrame.js +14 -12
- package/Frames/Frame.js +117 -57
- package/Frames/FrameComponent.js +9 -2
- package/Frames/GridFrame.js +131 -27
- package/Frames/ScrollFrame.js +154 -40
- package/README.md +8 -8
- package/UIComponents/Button.js +281 -0
- package/UIComponents/Icon.js +374 -0
- package/UIComponents/Input.js +15 -8
- package/UIComponents/Label.js +102 -158
- package/UIComponents/TextComponent.js +838 -0
- package/UIComponents/TextField.js +170 -39
- package/UIComponents/UIComponent.js +68 -35
- package/index.js +5 -1
- package/package.json +9 -2
- package/problems.txt +1 -0
|
@@ -17,38 +17,57 @@ export class TextField extends Input{
|
|
|
17
17
|
* @param {number} options.borderWidth - Border width
|
|
18
18
|
* @param {number} options.cornerRadius - Corner radius
|
|
19
19
|
* @param {boolean} options.enableShadow - Enable shadow
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
* @param {string} options.shadowColor - Shadow color (CSS color string)
|
|
21
|
+
* @param {number} options.shadowBlur - Shadow blur radius
|
|
22
|
+
* @param {number} options.shadowOffsetX - Shadow offset on X axis
|
|
23
|
+
* @param {number} options.shadowOffsetY - Shadow offset on Y axis
|
|
24
24
|
* @param {string} options.placeholder - Placeholder text
|
|
25
|
+
* @param {string} options.placeholderColor - Placeholder text color (muted)
|
|
25
26
|
* @param {string} options.text - Initial text
|
|
26
27
|
* @param {string} options.textAlign - Text alignment
|
|
27
|
-
* @param {number} options.
|
|
28
|
+
* @param {number} options.pad - Internal padding
|
|
29
|
+
* @param {number} options.margin - General margin for all sides
|
|
30
|
+
* @param {number} options.marginx - Horizontal margin (left and right)
|
|
31
|
+
* @param {number} options.marginy - Vertical margin (top and bottom)
|
|
32
|
+
* @param {number} options.marginl - Left margin
|
|
33
|
+
* @param {number} options.marginr - Right margin
|
|
34
|
+
* @param {number} options.margint - Top margin
|
|
35
|
+
* @param {number} options.marginb - Bottom margin
|
|
28
36
|
*/
|
|
29
37
|
constructor(x, y, width, height,
|
|
30
38
|
{
|
|
31
39
|
id=null,
|
|
32
40
|
parent=null,
|
|
33
|
-
backgroundColor='rgb(
|
|
34
|
-
textColor='rgb(
|
|
41
|
+
backgroundColor='rgb(30, 30, 46)',
|
|
42
|
+
textColor='rgb(224, 224, 224)',
|
|
35
43
|
borderFlag = true,
|
|
36
|
-
borderColor = color(
|
|
44
|
+
borderColor = color('#3a3a4d'),
|
|
37
45
|
borderWidth = 1,
|
|
38
|
-
cornerRadius =
|
|
46
|
+
cornerRadius = 8,
|
|
39
47
|
enableShadow=false,
|
|
40
|
-
shadowColor= '
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
shadowColor= 'rgba(0,0,0,0.5)',
|
|
49
|
+
shadowBlur= 12,
|
|
50
|
+
shadowOffsetX= 0,
|
|
51
|
+
shadowOffsetY= 4,
|
|
44
52
|
placeholder="",
|
|
53
|
+
placeholderColor='rgb(120, 120, 140)',
|
|
45
54
|
text="",
|
|
46
55
|
textAlign = "left",
|
|
47
|
-
|
|
56
|
+
pad = 10,
|
|
57
|
+
margin = 0,
|
|
58
|
+
marginx = null,
|
|
59
|
+
marginy = null,
|
|
60
|
+
marginl = null,
|
|
61
|
+
marginr = null,
|
|
62
|
+
margint = null,
|
|
63
|
+
marginb = null,
|
|
64
|
+
minWidth = 0,
|
|
65
|
+
minHeight = 0,
|
|
66
|
+
showDebugOverlay = false,
|
|
48
67
|
}={}) {
|
|
49
68
|
super(x, y, width, height, backgroundColor, borderFlag, borderColor,
|
|
50
|
-
borderWidth, cornerRadius, enableShadow, shadowColor,
|
|
51
|
-
|
|
69
|
+
borderWidth, cornerRadius, enableShadow, shadowColor, shadowBlur,
|
|
70
|
+
shadowOffsetX, shadowOffsetY, {parent: parent, type: "Input", id: id, margin: margin, marginx: marginx, marginy: marginy, marginl: marginl, marginr: marginr, margint: margint, marginb: marginb, minWidth: minWidth, minHeight: minHeight, showDebugOverlay: showDebugOverlay});
|
|
52
71
|
|
|
53
72
|
this.cursorPos = 0;
|
|
54
73
|
this.text = text;
|
|
@@ -56,19 +75,32 @@ export class TextField extends Input{
|
|
|
56
75
|
this.displayXOffset = 0;
|
|
57
76
|
|
|
58
77
|
this.textAlign = textAlign;
|
|
59
|
-
this.
|
|
78
|
+
this.pad = pad;
|
|
79
|
+
this.padl = pad;
|
|
80
|
+
this.padr = pad;
|
|
81
|
+
this.padt = pad;
|
|
82
|
+
this.padb = pad;
|
|
60
83
|
this.textColor = textColor;
|
|
61
84
|
this.placeholder = placeholder;
|
|
85
|
+
this.placeholderColor = placeholderColor;
|
|
62
86
|
|
|
63
87
|
this.cursorVisible = true;
|
|
64
|
-
this.lastCursorToggle =
|
|
88
|
+
this.lastCursorToggle = 0;
|
|
65
89
|
this.cursorBlinkInterval = 500;
|
|
66
90
|
|
|
67
91
|
this.selectionStart = null;
|
|
68
92
|
this.selectionEnd = null;
|
|
69
93
|
this.isSelecting = false;
|
|
70
94
|
|
|
95
|
+
// Key repeat state for long-press behavior
|
|
96
|
+
this._lastKeyCode = null;
|
|
97
|
+
this._keyDownStartTime = 0;
|
|
98
|
+
this._lastRepeatTime = 0;
|
|
99
|
+
this._keyRepeatDelay = 500; // ms before repeat starts
|
|
100
|
+
this._keyRepeatInterval = 30; // ms between repeats
|
|
101
|
+
|
|
71
102
|
this.addEventListener("keyPress", (event)=>this.onKeyPress(event));
|
|
103
|
+
this.addEventListener("keyDown", (event)=>this.onKeyDown(event));
|
|
72
104
|
// this.addEventListener("click", (event)=>this.onMouseClick(event));
|
|
73
105
|
this.addEventListener("press", (event)=>this.onMousePress(event)); // NEW
|
|
74
106
|
this.addEventListener("drag", (event)=>this.onMouseDrag(event));
|
|
@@ -83,7 +115,8 @@ export class TextField extends Input{
|
|
|
83
115
|
* @param {MouseEvent} event - The hover event
|
|
84
116
|
*/
|
|
85
117
|
onMouseHover(event){
|
|
86
|
-
|
|
118
|
+
cursor('text');
|
|
119
|
+
event.stopPropagation();
|
|
87
120
|
}
|
|
88
121
|
|
|
89
122
|
/**
|
|
@@ -108,6 +141,9 @@ export class TextField extends Input{
|
|
|
108
141
|
// Cursor usually hides after blur in textfields
|
|
109
142
|
this.cursorVisible = false;
|
|
110
143
|
|
|
144
|
+
// Reset key repeat state
|
|
145
|
+
this._lastKeyCode = null;
|
|
146
|
+
|
|
111
147
|
console.log("blur called!");
|
|
112
148
|
|
|
113
149
|
event.stopPropagation();
|
|
@@ -161,38 +197,122 @@ export class TextField extends Input{
|
|
|
161
197
|
}
|
|
162
198
|
}
|
|
163
199
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
200
|
+
/**
|
|
201
|
+
* Handles continuous key down for key repeat (long-press arrow keys, backspace, etc.)
|
|
202
|
+
* Called from draw loop while key is held.
|
|
203
|
+
*/
|
|
204
|
+
onKeyDown(event) {
|
|
205
|
+
const now = millis();
|
|
206
|
+
const repeatableKeys = [LEFT_ARROW, RIGHT_ARROW, BACKSPACE];
|
|
207
|
+
|
|
208
|
+
if (!repeatableKeys.includes(keyCode)) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Detect new key press
|
|
213
|
+
if (this._lastKeyCode !== keyCode) {
|
|
214
|
+
this._lastKeyCode = keyCode;
|
|
215
|
+
this._keyDownStartTime = now;
|
|
216
|
+
this._lastRepeatTime = 0;
|
|
217
|
+
return; // first press handled by onKeyPress
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Wait for initial delay before repeating
|
|
221
|
+
if (now - this._keyDownStartTime < this._keyRepeatDelay) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Throttle repeats
|
|
226
|
+
if (now - this._lastRepeatTime < this._keyRepeatInterval) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
this._lastRepeatTime = now;
|
|
231
|
+
this._handleRepeatable();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Executes the repeatable key action (shared by onKeyPress and onKeyDown repeat).
|
|
236
|
+
*/
|
|
237
|
+
_handleRepeatable() {
|
|
167
238
|
const hasSelection = this.selectionStart !== null && this.selectionStart !== this.selectionEnd;
|
|
168
239
|
|
|
169
240
|
if (keyCode === LEFT_ARROW) {
|
|
241
|
+
if (keyIsDown(SHIFT) && (this.selectionStart === null || this.selectionStart === this.selectionEnd)) {
|
|
242
|
+
this.selectionStart = this.cursorPos;
|
|
243
|
+
this.selectionEnd = this.cursorPos;
|
|
244
|
+
}
|
|
245
|
+
|
|
170
246
|
if (keyIsDown(CONTROL)) {
|
|
171
247
|
this.jumpLeftByOneWord();
|
|
172
248
|
} else {
|
|
173
249
|
this.moveCursorLeft(1);
|
|
174
250
|
}
|
|
175
|
-
|
|
176
|
-
|
|
251
|
+
|
|
252
|
+
if (keyIsDown(SHIFT)) {
|
|
253
|
+
this.selectionEnd = this.cursorPos;
|
|
254
|
+
} else {
|
|
255
|
+
this.selectionStart = this.selectionEnd = this.cursorPos;
|
|
256
|
+
}
|
|
177
257
|
} else if (keyCode === RIGHT_ARROW) {
|
|
258
|
+
if (keyIsDown(SHIFT) && (this.selectionStart === null || this.selectionStart === this.selectionEnd)) {
|
|
259
|
+
this.selectionStart = this.cursorPos;
|
|
260
|
+
this.selectionEnd = this.cursorPos;
|
|
261
|
+
}
|
|
262
|
+
|
|
178
263
|
if (keyIsDown(CONTROL)) {
|
|
179
264
|
this.jumpRightByOneWord();
|
|
180
265
|
} else {
|
|
181
266
|
this.moveCursorRight(1);
|
|
182
267
|
}
|
|
183
|
-
|
|
268
|
+
|
|
269
|
+
if (keyIsDown(SHIFT)) {
|
|
270
|
+
this.selectionEnd = this.cursorPos;
|
|
271
|
+
} else {
|
|
272
|
+
this.selectionStart = this.selectionEnd = this.cursorPos;
|
|
273
|
+
}
|
|
184
274
|
} else if (keyCode === BACKSPACE) {
|
|
185
275
|
if (hasSelection) {
|
|
186
|
-
// ADDED: delete selection (Backspace with selection deletes selection)
|
|
187
276
|
this.deleteSelectedText();
|
|
188
277
|
} else if (keyIsDown(CONTROL)) {
|
|
189
278
|
this.deleteOneWord();
|
|
190
279
|
} else {
|
|
191
280
|
this.deleteOneChar();
|
|
192
281
|
}
|
|
193
|
-
// collapse selection after deletion
|
|
194
282
|
this.selectionStart = this.selectionEnd = this.cursorPos;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
this.cursorVisible = true;
|
|
286
|
+
this.lastCursorToggle = millis();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
onKeyPress(event) {
|
|
290
|
+
// Reset key repeat tracking on new press
|
|
291
|
+
this._lastKeyCode = keyCode;
|
|
292
|
+
this._keyDownStartTime = millis();
|
|
293
|
+
this._lastRepeatTime = 0;
|
|
294
|
+
|
|
295
|
+
// ADDED: if there's an active selection and user types or presses backspace/delete,
|
|
296
|
+
// we should remove the selection first (so typed char replaces selection)
|
|
297
|
+
const hasSelection = this.selectionStart !== null && this.selectionStart !== this.selectionEnd;
|
|
298
|
+
|
|
299
|
+
if (keyCode === LEFT_ARROW || keyCode === RIGHT_ARROW || keyCode === BACKSPACE) {
|
|
300
|
+
this._handleRepeatable();
|
|
301
|
+
return; // cursor/selection already updated, skip to end
|
|
302
|
+
} else if (keyIsDown(CONTROL) && (key === 'a' || key === 'A')) {
|
|
303
|
+
// Select all text in the field
|
|
304
|
+
this.selectionStart = 0;
|
|
305
|
+
this.selectionEnd = this.text.length;
|
|
306
|
+
this.cursorPos = this.text.length;
|
|
307
|
+
// Prevent the browser from selecting all page content
|
|
308
|
+
if (event.nativeEvent) {
|
|
309
|
+
event.nativeEvent.preventDefault();
|
|
310
|
+
}
|
|
195
311
|
} else if (key.length === 1) {
|
|
312
|
+
// Skip if Ctrl is held (avoid inserting control characters)
|
|
313
|
+
if (keyIsDown(CONTROL)) {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
196
316
|
// ADDED: if a selection exists, remove it before insertion so typed char replaces selection
|
|
197
317
|
if (hasSelection) {
|
|
198
318
|
this.deleteSelectedText();
|
|
@@ -222,10 +342,10 @@ export class TextField extends Input{
|
|
|
222
342
|
|
|
223
343
|
if (this.textAlign === "left") {
|
|
224
344
|
// left aligned starts at left padding, minus the scroll offset
|
|
225
|
-
return this.x + this.
|
|
345
|
+
return this.x + this.pad - this.displayXOffset;
|
|
226
346
|
} else if (this.textAlign === "right") {
|
|
227
347
|
// right aligned: text ends at width - padding, so start is that minus fullWidth
|
|
228
|
-
return this.x + this.width - this.
|
|
348
|
+
return this.x + this.width - this.pad - fullWidth - this.displayXOffset;
|
|
229
349
|
} else { // center or anything else
|
|
230
350
|
return this.x + this.width / 2 - fullWidth / 2 - this.displayXOffset;
|
|
231
351
|
}
|
|
@@ -278,6 +398,10 @@ export class TextField extends Input{
|
|
|
278
398
|
|
|
279
399
|
// ADDED: clear selection anchors and ensure they reflect cursor
|
|
280
400
|
this.selectionStart = this.selectionEnd = null;
|
|
401
|
+
|
|
402
|
+
// Reset scroll offset and ensure cursor is visible
|
|
403
|
+
this.displayXOffset = 0;
|
|
404
|
+
this.scrollCursorIntoView();
|
|
281
405
|
}
|
|
282
406
|
}
|
|
283
407
|
|
|
@@ -406,8 +530,8 @@ export class TextField extends Input{
|
|
|
406
530
|
cursorX = this.findTextWidth(0, this.cursorPos);
|
|
407
531
|
}
|
|
408
532
|
|
|
409
|
-
if (cursorX - this.displayXOffset > this.width - this.
|
|
410
|
-
this.displayXOffset = cursorX - this.width + 2*this.
|
|
533
|
+
if (cursorX - this.displayXOffset > this.width - this.pad) {
|
|
534
|
+
this.displayXOffset = cursorX - this.width + 2*this.pad;
|
|
411
535
|
}
|
|
412
536
|
|
|
413
537
|
if(!cursorX){
|
|
@@ -425,8 +549,8 @@ export class TextField extends Input{
|
|
|
425
549
|
cursorX = this.findTextWidth(0, this.cursorPos);
|
|
426
550
|
}
|
|
427
551
|
|
|
428
|
-
if(cursorX - this.displayXOffset < this.
|
|
429
|
-
this.displayXOffset = cursorX - this.
|
|
552
|
+
if(cursorX - this.displayXOffset < this.pad){
|
|
553
|
+
this.displayXOffset = cursorX - this.pad;
|
|
430
554
|
}
|
|
431
555
|
|
|
432
556
|
if(!cursorX){
|
|
@@ -480,7 +604,7 @@ export class TextField extends Input{
|
|
|
480
604
|
|
|
481
605
|
let x, y;
|
|
482
606
|
if(this.textAlign==="left"){
|
|
483
|
-
x = this.x - this.displayXOffset + this.
|
|
607
|
+
x = this.x - this.displayXOffset + this.pad;
|
|
484
608
|
} else if(this.textAlign==="right") {
|
|
485
609
|
x = this.x - this.displayXOffset + this.width - this.textSize;
|
|
486
610
|
}
|
|
@@ -492,16 +616,23 @@ export class TextField extends Input{
|
|
|
492
616
|
|
|
493
617
|
let highlightX = x + this.findTextWidth(0, start);
|
|
494
618
|
let highlightWidth = this.findTextWidth(start, end);
|
|
495
|
-
let highlightY = y - this.textSize * 0.7;
|
|
496
619
|
|
|
497
620
|
push();
|
|
498
621
|
fill('rgba(15, 111, 206, 0.7)');
|
|
499
622
|
noStroke();
|
|
500
|
-
rect(highlightX,
|
|
623
|
+
rect(highlightX, this.y + this.pad, highlightWidth, this.height - 2 * this.pad);
|
|
501
624
|
pop();
|
|
502
625
|
}
|
|
503
626
|
|
|
504
|
-
|
|
627
|
+
if (this.text.length === 0 && this.placeholder && !this.isFocused) {
|
|
628
|
+
push();
|
|
629
|
+
fill(this.placeholderColor);
|
|
630
|
+
noStroke();
|
|
631
|
+
text(this.placeholder, x, y);
|
|
632
|
+
pop();
|
|
633
|
+
} else {
|
|
634
|
+
text(this.text, x, y);
|
|
635
|
+
}
|
|
505
636
|
|
|
506
637
|
if (millis() - this.lastCursorToggle > this.cursorBlinkInterval) {
|
|
507
638
|
this.cursorVisible = !this.cursorVisible;
|
|
@@ -513,7 +644,7 @@ export class TextField extends Input{
|
|
|
513
644
|
if (this.cursorVisible) {
|
|
514
645
|
stroke(this.textColor);
|
|
515
646
|
strokeWeight(2);
|
|
516
|
-
line(cursorX, y
|
|
647
|
+
line(cursorX, this.y + this.pad, cursorX, this.y + this.height - this.pad);
|
|
517
648
|
}
|
|
518
649
|
}
|
|
519
650
|
|
|
@@ -531,7 +662,7 @@ export class TextField extends Input{
|
|
|
531
662
|
* Updates text size based on current height
|
|
532
663
|
*/
|
|
533
664
|
updateTextSize(){
|
|
534
|
-
this.textSize = this.height * 0.9;
|
|
665
|
+
this.textSize = (this.height - 2 * this.pad) * 0.9;
|
|
535
666
|
this.scrollCursorIntoView();
|
|
536
667
|
}
|
|
537
668
|
|
|
@@ -13,19 +13,26 @@ export class UIComponent extends Component{
|
|
|
13
13
|
* @param {number} borderWidth - Border width
|
|
14
14
|
* @param {number} cornerRadius - Corner radius for rounded corners
|
|
15
15
|
* @param {boolean} enableShadow - Whether to render shadow
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
* @param {string} shadowColor - Shadow color (CSS color string)
|
|
17
|
+
* @param {number} shadowBlur - Shadow blur radius
|
|
18
|
+
* @param {number} shadowOffsetX - Shadow offset on X axis
|
|
19
|
+
* @param {number} shadowOffsetY - Shadow offset on Y axis
|
|
20
20
|
* @param {Object} options - Additional options
|
|
21
21
|
* @param {Component|null} options.parent - Parent component
|
|
22
22
|
* @param {string} options.type - Component type
|
|
23
23
|
* @param {string|null} options.id - Component ID
|
|
24
|
+
* @param {number} options.margin - General margin for all sides
|
|
25
|
+
* @param {number} options.marginx - Horizontal margin (left and right)
|
|
26
|
+
* @param {number} options.marginy - Vertical margin (top and bottom)
|
|
27
|
+
* @param {number} options.marginl - Left margin
|
|
28
|
+
* @param {number} options.marginr - Right margin
|
|
29
|
+
* @param {number} options.margint - Top margin
|
|
30
|
+
* @param {number} options.marginb - Bottom margin
|
|
24
31
|
*/
|
|
25
32
|
constructor(x, y, width, height, backgroundColor, borderFlag, borderColor,
|
|
26
|
-
borderWidth, cornerRadius, enableShadow, shadowColor,
|
|
27
|
-
|
|
28
|
-
super(x, y, width, height, {parent: parent, type: type, id: id});
|
|
33
|
+
borderWidth, cornerRadius, enableShadow, shadowColor, shadowBlur,
|
|
34
|
+
shadowOffsetX, shadowOffsetY, {parent=null, type="", id=null, margin=0, marginx=null, marginy=null, marginl=null, marginr=null, margint=null, marginb=null, minWidth=0, minHeight=0, showDebugOverlay=false} = {}){
|
|
35
|
+
super(x, y, width, height, {parent: parent, type: type, id: id, margin: margin, marginx: marginx, marginy: marginy, marginl: marginl, marginr: marginr, margint: margint, marginb: marginb, minWidth: minWidth, minHeight: minHeight, showDebugOverlay: showDebugOverlay});
|
|
29
36
|
|
|
30
37
|
this.backgroundColor = backgroundColor;
|
|
31
38
|
|
|
@@ -38,10 +45,10 @@ export class UIComponent extends Component{
|
|
|
38
45
|
this.enableShadow = enableShadow;
|
|
39
46
|
|
|
40
47
|
if(this.enableShadow){
|
|
41
|
-
this.shadowColor = shadowColor
|
|
42
|
-
this.
|
|
43
|
-
this.
|
|
44
|
-
this.
|
|
48
|
+
this.shadowColor = shadowColor;
|
|
49
|
+
this.shadowBlur = shadowBlur;
|
|
50
|
+
this.shadowOffsetX = shadowOffsetX;
|
|
51
|
+
this.shadowOffsetY = shadowOffsetY;
|
|
45
52
|
}
|
|
46
53
|
|
|
47
54
|
this.cornerRadius = cornerRadius;
|
|
@@ -57,41 +64,67 @@ export class UIComponent extends Component{
|
|
|
57
64
|
*/
|
|
58
65
|
updateHeight(){};
|
|
59
66
|
/**
|
|
60
|
-
*
|
|
61
|
-
* @param {string} shadowColor -
|
|
62
|
-
* @returns {number[]|null} Array of [r, g, b] values or null if invalid
|
|
67
|
+
* Updates shadow color
|
|
68
|
+
* @param {string} shadowColor - Shadow color (CSS color string)
|
|
63
69
|
*/
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
70
|
+
setShadowColor(shadowColor){
|
|
71
|
+
this.shadowColor = shadowColor;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Updates shadow blur radius
|
|
76
|
+
* @param {number} shadowBlur - Shadow blur
|
|
77
|
+
*/
|
|
78
|
+
setShadowBlur(shadowBlur){
|
|
79
|
+
this.shadowBlur = shadowBlur;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Updates shadow X offset
|
|
84
|
+
* @param {number} shadowOffsetX - Shadow offset on X axis
|
|
85
|
+
*/
|
|
86
|
+
setShadowOffsetX(shadowOffsetX){
|
|
87
|
+
this.shadowOffsetX = shadowOffsetX;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Updates shadow Y offset
|
|
92
|
+
* @param {number} shadowOffsetY - Shadow offset on Y axis
|
|
93
|
+
*/
|
|
94
|
+
setShadowOffsetY(shadowOffsetY){
|
|
95
|
+
this.shadowOffsetY = shadowOffsetY;
|
|
67
96
|
}
|
|
68
97
|
/**
|
|
69
98
|
* Renders a shadow effect around the component
|
|
70
99
|
* @param {Object} options - Shadow rendering options
|
|
71
100
|
*/
|
|
72
101
|
drawShadow({}={}){
|
|
73
|
-
|
|
74
|
-
if(color==null){
|
|
75
|
-
console.log("shadow color value is not in the correct format: rgb(0,0,0)");
|
|
102
|
+
if(this.width<=0 || this.height<=0){
|
|
76
103
|
return;
|
|
77
104
|
}
|
|
78
105
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
this.
|
|
85
|
-
}
|
|
106
|
+
let blur = Math.max(0, this.shadowBlur ?? 0);
|
|
107
|
+
let offsetX = this.shadowOffsetX ?? 0;
|
|
108
|
+
let offsetY = this.shadowOffsetY ?? 0;
|
|
109
|
+
let resolvedShadowColor = (typeof this.shadowColor === "string")
|
|
110
|
+
? this.shadowColor
|
|
111
|
+
: (this.shadowColor && this.shadowColor.toString ? this.shadowColor.toString() : "rgba(0,0,0,0.35)");
|
|
86
112
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
noFill();
|
|
90
|
-
let alpha = this.shadowIntensity * pow(1 - i / this.shadowDetail, 2);
|
|
91
|
-
stroke(`rgba(${color[0]}, ${color[1]}, ${color[2]}, ${alpha})`);
|
|
92
|
-
strokeWeight(this.shadowSpread);
|
|
93
|
-
rect(this.x-((i*this.shadowSpread)/2), this.y-((i*this.shadowSpread)/2), this.width+(i*this.shadowSpread), this.height+(i*this.shadowSpread), this.cornerRadius);
|
|
94
|
-
pop();
|
|
113
|
+
if(blur===0 && offsetX===0 && offsetY===0){
|
|
114
|
+
return;
|
|
95
115
|
}
|
|
116
|
+
|
|
117
|
+
let baseFill = this.backgroundColor ?? color(0, 0, 0, 0);
|
|
118
|
+
|
|
119
|
+
push();
|
|
120
|
+
let ctx = drawingContext;
|
|
121
|
+
ctx.shadowColor = resolvedShadowColor;
|
|
122
|
+
ctx.shadowBlur = blur;
|
|
123
|
+
ctx.shadowOffsetX = offsetX;
|
|
124
|
+
ctx.shadowOffsetY = offsetY;
|
|
125
|
+
noStroke();
|
|
126
|
+
fill(baseFill);
|
|
127
|
+
rect(this.x, this.y, this.width, this.height, this.cornerRadius);
|
|
128
|
+
pop();
|
|
96
129
|
}
|
|
97
130
|
}
|
package/index.js
CHANGED
|
@@ -15,8 +15,10 @@ const ScrollFrame = require('./Frames/ScrollFrame');
|
|
|
15
15
|
// UIComponents
|
|
16
16
|
const Input = require('./UIComponents/Input');
|
|
17
17
|
const Label = require('./UIComponents/Label');
|
|
18
|
+
const Button = require('./UIComponents/Button');
|
|
18
19
|
const TextField = require('./UIComponents/TextField');
|
|
19
20
|
const UIComponent = require('./UIComponents/UIComponent');
|
|
21
|
+
const Icon = require('./UIComponents/Icon');
|
|
20
22
|
|
|
21
23
|
module.exports = {
|
|
22
24
|
// Core
|
|
@@ -36,6 +38,8 @@ module.exports = {
|
|
|
36
38
|
// UIComponents
|
|
37
39
|
Input,
|
|
38
40
|
Label,
|
|
41
|
+
Button,
|
|
39
42
|
TextField,
|
|
40
|
-
UIComponent
|
|
43
|
+
UIComponent,
|
|
44
|
+
Icon
|
|
41
45
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usman404/crowjs",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "lightweight and extensible GUI library built on top of p5.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"CROWJS"
|
|
@@ -18,6 +18,13 @@
|
|
|
18
18
|
"type": "commonjs",
|
|
19
19
|
"main": "index.js",
|
|
20
20
|
"scripts": {
|
|
21
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
21
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
22
|
+
"docs:dev": "vitepress dev docs --host 0.0.0.0",
|
|
23
|
+
"docs:build": "vitepress build docs",
|
|
24
|
+
"docs:preview": "vitepress preview docs"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@iconify/vue": "^4.1.2",
|
|
28
|
+
"vitepress": "^1.6.4"
|
|
22
29
|
}
|
|
23
30
|
}
|
package/problems.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
- work on default look of all items.
|