pixelforge-ui 1.0.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/dist/index.mjs ADDED
@@ -0,0 +1,1069 @@
1
+ // src/tokens/index.ts
2
+ var colors = {
3
+ primary: {
4
+ lime: "#C4F0C8",
5
+ lavender: "#D4B5E8",
6
+ sky: "#B8E0F0",
7
+ peach: "#F0D9C8",
8
+ mint: "#C8F0E0"
9
+ },
10
+ dark: {
11
+ lime: "#6B9D77",
12
+ lavender: "#8B6BA8",
13
+ sky: "#6BA3C8",
14
+ peach: "#C89968",
15
+ mint: "#6BA89F"
16
+ },
17
+ neutral: {
18
+ black: "#1A1A24",
19
+ dark: "#2D2D3D",
20
+ gray: "#666670",
21
+ light: "#F5F5F0",
22
+ white: "#FFFBF5"
23
+ },
24
+ semantic: {
25
+ success: "#7FD8B8",
26
+ warning: "#FFB347",
27
+ error: "#FF6B6B",
28
+ info: "#B8E0F0"
29
+ },
30
+ accent: "#FF6B9D"
31
+ };
32
+ var typography = {
33
+ font: {
34
+ pixel: '"Press Start 2P", monospace',
35
+ sans: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
36
+ mono: '"Courier New", monospace'
37
+ },
38
+ size: {
39
+ xs: "12px",
40
+ sm: "14px",
41
+ base: "16px",
42
+ lg: "20px",
43
+ xl: "24px",
44
+ "2xl": "32px"
45
+ },
46
+ weight: {
47
+ normal: 400,
48
+ bold: 700
49
+ },
50
+ lineHeight: {
51
+ tight: 1.2,
52
+ normal: 1.5,
53
+ relaxed: 1.75
54
+ }
55
+ };
56
+ var spacing = {
57
+ 0: "0",
58
+ 1: "4px",
59
+ 2: "8px",
60
+ 3: "12px",
61
+ 4: "16px",
62
+ 5: "20px",
63
+ 6: "24px",
64
+ 8: "32px",
65
+ 10: "40px",
66
+ 12: "48px",
67
+ 16: "64px",
68
+ 20: "80px"
69
+ };
70
+ var shadows = {
71
+ sm: "4px 4px 0px rgba(0, 0, 0, 0.2)",
72
+ md: "8px 8px 0px rgba(0, 0, 0, 0.15)",
73
+ lg: "12px 12px 0px rgba(0, 0, 0, 0.1)",
74
+ glow: {
75
+ lime: "0 0 20px rgba(196, 240, 200, 0.4)",
76
+ lavender: "0 0 20px rgba(212, 181, 232, 0.4)",
77
+ sky: "0 0 20px rgba(184, 224, 240, 0.4)",
78
+ peach: "0 0 20px rgba(240, 217, 200, 0.4)"
79
+ },
80
+ press: "2px 2px 0px rgba(0, 0, 0, 0.3), inset 0 2px 0px rgba(0, 0, 0, 0.1)"
81
+ };
82
+ var borderRadius = {
83
+ sm: "4px",
84
+ base: "8px",
85
+ lg: "12px",
86
+ full: "9999px"
87
+ };
88
+ var breakpoints = {
89
+ xs: "320px",
90
+ sm: "640px",
91
+ md: "768px",
92
+ lg: "1024px",
93
+ xl: "1280px",
94
+ "2xl": "1536px"
95
+ };
96
+ var zIndex = {
97
+ hidden: -1,
98
+ base: 0,
99
+ dropdown: 1e3,
100
+ sticky: 1100,
101
+ fixed: 1200,
102
+ modal: 1300,
103
+ popover: 1400,
104
+ tooltip: 1500
105
+ };
106
+ var transitions = {
107
+ fast: "150ms ease-in-out",
108
+ base: "200ms ease-in-out",
109
+ slow: "300ms ease-in-out"
110
+ };
111
+ var gamePalettes = {
112
+ nes: {
113
+ name: "NES Classic",
114
+ colors: ["#FF0000", "#00FF00", "#0000FF", "#FFFF00", "#FF00FF", "#00FFFF"]
115
+ },
116
+ gameboy: {
117
+ name: "Game Boy",
118
+ colors: ["#9BBC0F", "#8BAC0F", "#306230", "#0F380F"]
119
+ },
120
+ atari: {
121
+ name: "Atari 2600",
122
+ colors: ["#000000", "#FFFFFF", "#FF0000", "#FFFF00"]
123
+ },
124
+ snes: {
125
+ name: "SNES",
126
+ colors: ["#8B008B", "#FF1493", "#1E90FF", "#FFD700"]
127
+ }
128
+ };
129
+
130
+ // src/components/Button.tsx
131
+ import React from "react";
132
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
133
+ var Button = React.forwardRef(
134
+ ({
135
+ variant = "primary",
136
+ size = "md",
137
+ disabled = false,
138
+ isLoading = false,
139
+ loadingText,
140
+ children,
141
+ className = "",
142
+ ...props
143
+ }, ref) => {
144
+ const baseStyles = `
145
+ inline-flex items-center justify-center
146
+ font-pixel text-sm
147
+ transition-all duration-200
148
+ cursor-pointer
149
+ active:scale-95
150
+ focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-pixelforge-accent
151
+ disabled:opacity-50 disabled:cursor-not-allowed
152
+ `;
153
+ const sizeStyles = {
154
+ sm: "px-3 py-2 text-xs gap-2",
155
+ md: "px-4 py-3 text-sm gap-2",
156
+ lg: "px-6 py-4 text-base gap-3"
157
+ };
158
+ const variantStyles = {
159
+ primary: `
160
+ bg-pixelforge-lime text-pixelforge-black
161
+ border-2 border-pixelforge-darkLime
162
+ shadow-px
163
+ hover:shadow-px-lg hover:translate-y-[-2px]
164
+ active:shadow-px-press active:translate-y-[2px]
165
+ dark:bg-pixelforge-darkLime dark:text-pixelforge-white dark:border-pixelforge-lime
166
+ `,
167
+ secondary: `
168
+ bg-pixelforge-lavender text-pixelforge-black
169
+ border-2 border-pixelforge-darkLavender
170
+ shadow-px
171
+ hover:shadow-px-lg hover:translate-y-[-2px]
172
+ active:shadow-px-press active:translate-y-[2px]
173
+ dark:bg-pixelforge-darkLavender dark:text-pixelforge-white dark:border-pixelforge-lavender
174
+ `,
175
+ outline: `
176
+ bg-transparent border-2
177
+ text-pixelforge-black border-pixelforge-darkLime
178
+ hover:bg-pixelforge-lime/10 active:bg-pixelforge-lime/20
179
+ dark:text-pixelforge-white dark:border-pixelforge-lime
180
+ dark:hover:bg-pixelforge-lime/30 dark:active:bg-pixelforge-lime/40
181
+ `,
182
+ ghost: `
183
+ bg-transparent text-pixelforge-black border-2 border-transparent
184
+ hover:bg-pixelforge-lime/20 active:bg-pixelforge-lime/30
185
+ dark:text-pixelforge-white dark:hover:bg-pixelforge-lime/30
186
+ dark:active:bg-pixelforge-lime/40
187
+ `
188
+ };
189
+ return /* @__PURE__ */ jsx(
190
+ "button",
191
+ {
192
+ ref,
193
+ disabled: disabled || isLoading,
194
+ className: `${baseStyles} ${sizeStyles[size]} ${variantStyles[variant]} ${className}`,
195
+ ...props,
196
+ children: isLoading ? /* @__PURE__ */ jsxs(Fragment, { children: [
197
+ /* @__PURE__ */ jsx("span", { className: "animate-spin", children: "\u27F3" }),
198
+ loadingText || children
199
+ ] }) : children
200
+ }
201
+ );
202
+ }
203
+ );
204
+ Button.displayName = "Button";
205
+
206
+ // src/components/Input.tsx
207
+ import React2 from "react";
208
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
209
+ var Input = React2.forwardRef(
210
+ ({
211
+ label,
212
+ error,
213
+ helperText,
214
+ required,
215
+ leftIcon,
216
+ rightIcon,
217
+ className = "",
218
+ disabled,
219
+ ...props
220
+ }, ref) => {
221
+ const hasError = !!error;
222
+ return /* @__PURE__ */ jsxs2("div", { className: "w-full", children: [
223
+ label && /* @__PURE__ */ jsx2(
224
+ "label",
225
+ {
226
+ htmlFor: props.id,
227
+ className: `
228
+ block font-pixel text-xs mb-2
229
+ text-pixelforge-black dark:text-pixelforge-white
230
+ ${required ? "after:content-['*'] after:text-pixelforge-error after:ml-1" : ""}
231
+ `,
232
+ children: label
233
+ }
234
+ ),
235
+ /* @__PURE__ */ jsxs2("div", { className: "relative", children: [
236
+ leftIcon && /* @__PURE__ */ jsx2("div", { className: "absolute left-3 top-1/2 -translate-y-1/2 text-pixelforge-black dark:text-pixelforge-white", children: leftIcon }),
237
+ /* @__PURE__ */ jsx2(
238
+ "input",
239
+ {
240
+ ref,
241
+ disabled,
242
+ className: `
243
+ w-full
244
+ px-4 py-3
245
+ font-mono text-sm
246
+ bg-pixelforge-white dark:bg-pixelforge-dark
247
+ text-pixelforge-black dark:text-pixelforge-white
248
+ border-2 border-pixelforge-darkLime dark:border-pixelforge-lime
249
+ rounded-px-sm
250
+ shadow-px
251
+ transition-all duration-200
252
+ placeholder:text-pixelforge-gray/50
253
+ focus:outline-none focus:shadow-px-lg focus:translate-y-[-2px]
254
+ focus:border-pixelforge-accent
255
+ disabled:opacity-50 disabled:cursor-not-allowed
256
+ ${hasError ? "border-pixelforge-error focus:border-pixelforge-error shadow-px focus:shadow-px-glow" : ""}
257
+ ${leftIcon ? "pl-10" : ""}
258
+ ${rightIcon ? "pr-10" : ""}
259
+ ${className}
260
+ `,
261
+ ...props
262
+ }
263
+ ),
264
+ rightIcon && /* @__PURE__ */ jsx2("div", { className: "absolute right-3 top-1/2 -translate-y-1/2 text-pixelforge-black dark:text-pixelforge-white", children: rightIcon })
265
+ ] }),
266
+ error && /* @__PURE__ */ jsxs2("p", { className: "mt-2 font-mono text-xs text-pixelforge-error", children: [
267
+ "\u26A0 ",
268
+ error
269
+ ] }),
270
+ helperText && !error && /* @__PURE__ */ jsx2("p", { className: "mt-2 font-mono text-xs text-pixelforge-gray", children: helperText })
271
+ ] });
272
+ }
273
+ );
274
+ Input.displayName = "Input";
275
+
276
+ // src/components/Card.tsx
277
+ import React3 from "react";
278
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
279
+ var Card = React3.forwardRef(
280
+ ({
281
+ title,
282
+ subtitle,
283
+ interactive = false,
284
+ variant = "default",
285
+ header,
286
+ footer,
287
+ children,
288
+ className = "",
289
+ ...props
290
+ }, ref) => {
291
+ const variantStyles = {
292
+ default: `
293
+ bg-pixelforge-white dark:bg-pixelforge-dark
294
+ border-pixelforge-darkLime dark:border-pixelforge-lime
295
+ `,
296
+ lime: `
297
+ bg-pixelforge-lime/20 dark:bg-pixelforge-darkLime/20
298
+ border-pixelforge-lime dark:border-pixelforge-lime
299
+ `,
300
+ lavender: `
301
+ bg-pixelforge-lavender/20 dark:bg-pixelforge-darkLavender/20
302
+ border-pixelforge-lavender dark:border-pixelforge-lavender
303
+ `,
304
+ sky: `
305
+ bg-pixelforge-sky/20 dark:bg-pixelforge-darkSky/20
306
+ border-pixelforge-sky dark:border-pixelforge-sky
307
+ `,
308
+ peach: `
309
+ bg-pixelforge-peach/20 dark:bg-pixelforge-darkPeach/20
310
+ border-pixelforge-peach dark:border-pixelforge-peach
311
+ `,
312
+ mint: `
313
+ bg-pixelforge-mint/20 dark:bg-pixelforge-darkMint/20
314
+ border-pixelforge-mint dark:border-pixelforge-mint
315
+ `
316
+ };
317
+ return /* @__PURE__ */ jsxs3(
318
+ "div",
319
+ {
320
+ ref,
321
+ className: `
322
+ border-2 border-solid
323
+ rounded-px
324
+ shadow-px
325
+ transition-all duration-200
326
+ ${interactive ? "cursor-pointer hover:shadow-px-lg hover:translate-y-[-2px] active:shadow-px-press active:translate-y-[2px]" : ""}
327
+ ${variantStyles[variant]}
328
+ ${className}
329
+ `,
330
+ ...props,
331
+ children: [
332
+ header && /* @__PURE__ */ jsx3("div", { className: "border-b-2 border-pixelforge-darkLime dark:border-pixelforge-lime/30 px-6 py-4", children: header }),
333
+ /* @__PURE__ */ jsxs3("div", { className: "p-6", children: [
334
+ title && /* @__PURE__ */ jsx3("h3", { className: "font-pixel text-lg text-pixelforge-black dark:text-pixelforge-white mb-1", children: title }),
335
+ subtitle && /* @__PURE__ */ jsx3("p", { className: "font-mono text-xs text-pixelforge-gray mb-4", children: subtitle }),
336
+ children
337
+ ] }),
338
+ footer && /* @__PURE__ */ jsx3("div", { className: "border-t-2 border-pixelforge-darkLime dark:border-pixelforge-lime/30 px-6 py-4 bg-pixelforge-light/50 dark:bg-pixelforge-black/20", children: footer })
339
+ ]
340
+ }
341
+ );
342
+ }
343
+ );
344
+ Card.displayName = "Card";
345
+
346
+ // src/components/Badge.tsx
347
+ import React4 from "react";
348
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
349
+ var Badge = React4.forwardRef(
350
+ ({
351
+ variant = "default",
352
+ size = "md",
353
+ dismissible = false,
354
+ onDismiss,
355
+ children,
356
+ className = "",
357
+ ...props
358
+ }, ref) => {
359
+ const sizeStyles = {
360
+ sm: "px-2 py-1 text-xs",
361
+ md: "px-3 py-1.5 text-sm",
362
+ lg: "px-4 py-2 text-base"
363
+ };
364
+ const variantStyles = {
365
+ default: `
366
+ bg-pixelforge-light dark:bg-pixelforge-dark
367
+ text-pixelforge-black dark:text-pixelforge-white
368
+ border-pixelforge-darkLime dark:border-pixelforge-lime
369
+ `,
370
+ lime: `
371
+ bg-pixelforge-lime text-pixelforge-black
372
+ border-pixelforge-darkLime dark:bg-pixelforge-darkLime dark:text-pixelforge-white dark:border-pixelforge-lime
373
+ `,
374
+ lavender: `
375
+ bg-pixelforge-lavender text-pixelforge-black
376
+ border-pixelforge-darkLavender dark:bg-pixelforge-darkLavender dark:text-pixelforge-white dark:border-pixelforge-lavender
377
+ `,
378
+ sky: `
379
+ bg-pixelforge-sky text-pixelforge-black
380
+ border-pixelforge-darkSky dark:bg-pixelforge-darkSky dark:text-pixelforge-white dark:border-pixelforge-sky
381
+ `,
382
+ peach: `
383
+ bg-pixelforge-peach text-pixelforge-black
384
+ border-pixelforge-darkPeach dark:bg-pixelforge-darkPeach dark:text-pixelforge-white dark:border-pixelforge-peach
385
+ `,
386
+ mint: `
387
+ bg-pixelforge-mint text-pixelforge-black
388
+ border-pixelforge-darkMint dark:bg-pixelforge-darkMint dark:text-pixelforge-white dark:border-pixelforge-mint
389
+ `,
390
+ success: `
391
+ bg-pixelforge-success/20 text-pixelforge-success
392
+ border-pixelforge-success dark:bg-pixelforge-success/10 dark:text-pixelforge-success/80
393
+ `,
394
+ warning: `
395
+ bg-pixelforge-warning/20 text-pixelforge-warning
396
+ border-pixelforge-warning dark:bg-pixelforge-warning/10 dark:text-pixelforge-warning/80
397
+ `,
398
+ error: `
399
+ bg-pixelforge-error/20 text-pixelforge-error
400
+ border-pixelforge-error dark:bg-pixelforge-error/10 dark:text-pixelforge-error/80
401
+ `,
402
+ info: `
403
+ bg-pixelforge-sky/20 text-pixelforge-sky
404
+ border-pixelforge-sky dark:bg-pixelforge-sky/10 dark:text-pixelforge-sky/80
405
+ `
406
+ };
407
+ return /* @__PURE__ */ jsxs4(
408
+ "span",
409
+ {
410
+ ref,
411
+ className: `
412
+ inline-flex items-center gap-2
413
+ font-pixel text-xs
414
+ border-2
415
+ rounded-px-sm
416
+ whitespace-nowrap
417
+ transition-all duration-200
418
+ ${sizeStyles[size]}
419
+ ${variantStyles[variant]}
420
+ ${className}
421
+ `,
422
+ ...props,
423
+ children: [
424
+ children,
425
+ dismissible && /* @__PURE__ */ jsx4(
426
+ "button",
427
+ {
428
+ onClick: (e) => {
429
+ e.stopPropagation();
430
+ onDismiss?.();
431
+ },
432
+ className: "ml-1 font-bold hover:opacity-70 transition-opacity",
433
+ "aria-label": "Dismiss",
434
+ children: "\u2715"
435
+ }
436
+ )
437
+ ]
438
+ }
439
+ );
440
+ }
441
+ );
442
+ Badge.displayName = "Badge";
443
+
444
+ // src/components/Modal.tsx
445
+ import { useEffect } from "react";
446
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
447
+ var Modal = ({
448
+ isOpen,
449
+ onClose,
450
+ title,
451
+ closeOnBackdropClick = true,
452
+ showCloseButton = true,
453
+ footer,
454
+ size = "md",
455
+ children
456
+ }) => {
457
+ useEffect(() => {
458
+ if (!isOpen) return;
459
+ const handleKeyDown = (e) => {
460
+ if (e.key === "Escape") {
461
+ onClose();
462
+ }
463
+ };
464
+ window.addEventListener("keydown", handleKeyDown);
465
+ document.body.style.overflow = "hidden";
466
+ return () => {
467
+ window.removeEventListener("keydown", handleKeyDown);
468
+ document.body.style.overflow = "unset";
469
+ };
470
+ }, [isOpen, onClose]);
471
+ if (!isOpen) return null;
472
+ const sizeStyles = {
473
+ sm: "max-w-sm",
474
+ md: "max-w-md",
475
+ lg: "max-w-lg"
476
+ };
477
+ return /* @__PURE__ */ jsxs5(Fragment2, { children: [
478
+ /* @__PURE__ */ jsx5(
479
+ "div",
480
+ {
481
+ className: `
482
+ fixed inset-0 z-modal
483
+ bg-black/40 backdrop-blur-xs
484
+ transition-opacity duration-200
485
+ ${isOpen ? "opacity-100" : "opacity-0 pointer-events-none"}
486
+ `,
487
+ onClick: () => closeOnBackdropClick && onClose(),
488
+ "aria-hidden": "true"
489
+ }
490
+ ),
491
+ /* @__PURE__ */ jsxs5(
492
+ "div",
493
+ {
494
+ className: `
495
+ fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2
496
+ z-modal w-11/12 ${sizeStyles[size]}
497
+ bg-pixelforge-white dark:bg-pixelforge-dark
498
+ border-2 border-pixelforge-darkLime dark:border-pixelforge-lime
499
+ rounded-px
500
+ shadow-lg
501
+ transition-all duration-200
502
+ ${isOpen ? "scale-100 opacity-100" : "scale-95 opacity-0 pointer-events-none"}
503
+ `,
504
+ role: "dialog",
505
+ "aria-modal": "true",
506
+ onClick: (e) => e.stopPropagation(),
507
+ children: [
508
+ title && /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between border-b-2 border-pixelforge-darkLime dark:border-pixelforge-lime/30 px-6 py-4", children: [
509
+ /* @__PURE__ */ jsx5("h2", { className: "font-pixel text-lg text-pixelforge-black dark:text-pixelforge-white", children: title }),
510
+ showCloseButton && /* @__PURE__ */ jsx5(
511
+ "button",
512
+ {
513
+ onClick: onClose,
514
+ className: `
515
+ text-2xl font-bold
516
+ text-pixelforge-black dark:text-pixelforge-white
517
+ hover:opacity-70
518
+ transition-opacity
519
+ p-1 -mr-2
520
+ `,
521
+ "aria-label": "Close modal",
522
+ children: "\u2715"
523
+ }
524
+ )
525
+ ] }),
526
+ /* @__PURE__ */ jsx5("div", { className: "px-6 py-6 max-h-[60vh] overflow-y-auto", children }),
527
+ footer && /* @__PURE__ */ jsx5("div", { className: "border-t-2 border-pixelforge-darkLime dark:border-pixelforge-lime/30 px-6 py-4 bg-pixelforge-light/50 dark:bg-pixelforge-black/20", children: footer })
528
+ ]
529
+ }
530
+ )
531
+ ] });
532
+ };
533
+ Modal.displayName = "Modal";
534
+
535
+ // src/components/Dropdown.tsx
536
+ import { useEffect as useEffect2, useRef, useState } from "react";
537
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
538
+ var Dropdown = ({
539
+ trigger,
540
+ items,
541
+ isOpen: controlledIsOpen,
542
+ onOpenChange,
543
+ placement = "bottom"
544
+ }) => {
545
+ const [internalIsOpen, setInternalIsOpen] = useState(false);
546
+ const isOpen = controlledIsOpen !== void 0 ? controlledIsOpen : internalIsOpen;
547
+ const triggerRef = useRef(null);
548
+ const dropdownRef = useRef(null);
549
+ useEffect2(() => {
550
+ if (!isOpen) return;
551
+ const handleClickOutside = (e) => {
552
+ if (triggerRef.current?.contains(e.target) || dropdownRef.current?.contains(e.target)) {
553
+ return;
554
+ }
555
+ handleClose();
556
+ };
557
+ window.addEventListener("mousedown", handleClickOutside);
558
+ return () => window.removeEventListener("mousedown", handleClickOutside);
559
+ }, [isOpen]);
560
+ useEffect2(() => {
561
+ if (!isOpen) return;
562
+ const handleKeyDown = (e) => {
563
+ if (e.key === "Escape") {
564
+ handleClose();
565
+ }
566
+ };
567
+ window.addEventListener("keydown", handleKeyDown);
568
+ return () => window.removeEventListener("keydown", handleKeyDown);
569
+ }, [isOpen]);
570
+ const handleOpen = () => {
571
+ const newState = !isOpen;
572
+ if (controlledIsOpen === void 0) {
573
+ setInternalIsOpen(newState);
574
+ }
575
+ onOpenChange?.(newState);
576
+ };
577
+ const handleClose = () => {
578
+ if (controlledIsOpen === void 0) {
579
+ setInternalIsOpen(false);
580
+ }
581
+ onOpenChange?.(false);
582
+ };
583
+ const handleItemClick = (item) => {
584
+ if (item.disabled) return;
585
+ item.onClick?.();
586
+ handleClose();
587
+ };
588
+ const placementStyles = {
589
+ bottom: "top-full mt-2",
590
+ top: "bottom-full mb-2",
591
+ left: "right-full mr-2",
592
+ right: "left-full ml-2"
593
+ };
594
+ return /* @__PURE__ */ jsxs6("div", { className: "relative inline-block", children: [
595
+ /* @__PURE__ */ jsx6(
596
+ "div",
597
+ {
598
+ ref: triggerRef,
599
+ onClick: handleOpen,
600
+ className: "cursor-pointer",
601
+ children: trigger
602
+ }
603
+ ),
604
+ isOpen && /* @__PURE__ */ jsx6(
605
+ "div",
606
+ {
607
+ ref: dropdownRef,
608
+ className: `
609
+ absolute ${placementStyles[placement]}
610
+ min-w-max
611
+ bg-pixelforge-white dark:bg-pixelforge-dark
612
+ border-2 border-pixelforge-darkLime dark:border-pixelforge-lime
613
+ rounded-px-sm
614
+ shadow-px-lg
615
+ z-dropdown
616
+ py-1
617
+ animation-in fade-in zoom-in-95 duration-200
618
+ `,
619
+ role: "menu",
620
+ children: items.map((item, index) => {
621
+ if (item.divider) {
622
+ return /* @__PURE__ */ jsx6(
623
+ "div",
624
+ {
625
+ className: "h-px bg-pixelforge-darkLime/20 dark:bg-pixelforge-lime/20 my-1",
626
+ role: "separator"
627
+ },
628
+ item.id || `divider-${index}`
629
+ );
630
+ }
631
+ return /* @__PURE__ */ jsx6(
632
+ "button",
633
+ {
634
+ onClick: () => handleItemClick(item),
635
+ disabled: item.disabled,
636
+ className: `
637
+ w-full text-left
638
+ px-4 py-2.5
639
+ font-mono text-sm
640
+ transition-colors duration-150
641
+ flex items-center gap-2
642
+ ${item.disabled ? "opacity-50 cursor-not-allowed text-pixelforge-gray dark:text-pixelforge-gray/50" : `
643
+ text-pixelforge-black dark:text-pixelforge-white
644
+ hover:bg-pixelforge-lime/20 dark:hover:bg-pixelforge-lime/40
645
+ active:bg-pixelforge-lime/30 dark:active:bg-pixelforge-lime/50
646
+ `}
647
+ `,
648
+ role: "menuitem",
649
+ children: item.label
650
+ },
651
+ item.id
652
+ );
653
+ })
654
+ }
655
+ )
656
+ ] });
657
+ };
658
+ Dropdown.displayName = "Dropdown";
659
+
660
+ // src/components/Tooltip.tsx
661
+ import { useEffect as useEffect3, useRef as useRef2, useState as useState2 } from "react";
662
+ import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
663
+ var Tooltip = ({
664
+ content,
665
+ children,
666
+ placement = "top",
667
+ delay = 200,
668
+ disabled = false,
669
+ variant = "default"
670
+ }) => {
671
+ const [isVisible, setIsVisible] = useState2(false);
672
+ const [position, setPosition] = useState2({ top: 0, left: 0 });
673
+ const triggerRef = useRef2(null);
674
+ const tooltipRef = useRef2(null);
675
+ const timeoutRef = useRef2();
676
+ const handleMouseEnter = () => {
677
+ if (disabled) return;
678
+ timeoutRef.current = setTimeout(() => {
679
+ setIsVisible(true);
680
+ updatePosition();
681
+ }, delay);
682
+ };
683
+ const handleMouseLeave = () => {
684
+ if (timeoutRef.current) {
685
+ clearTimeout(timeoutRef.current);
686
+ }
687
+ setIsVisible(false);
688
+ };
689
+ const updatePosition = () => {
690
+ if (!triggerRef.current || !tooltipRef.current) return;
691
+ const triggerRect = triggerRef.current.getBoundingClientRect();
692
+ const tooltipRect = tooltipRef.current.getBoundingClientRect();
693
+ const offset = 8;
694
+ let top = 0;
695
+ let left = 0;
696
+ switch (placement) {
697
+ case "top":
698
+ top = triggerRect.top - tooltipRect.height - offset;
699
+ left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;
700
+ break;
701
+ case "bottom":
702
+ top = triggerRect.bottom + offset;
703
+ left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;
704
+ break;
705
+ case "left":
706
+ top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;
707
+ left = triggerRect.left - tooltipRect.width - offset;
708
+ break;
709
+ case "right":
710
+ top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;
711
+ left = triggerRect.right + offset;
712
+ break;
713
+ }
714
+ setPosition({ top, left });
715
+ };
716
+ useEffect3(() => {
717
+ if (isVisible) {
718
+ updatePosition();
719
+ const handleResize = () => updatePosition();
720
+ window.addEventListener("resize", handleResize);
721
+ return () => window.removeEventListener("resize", handleResize);
722
+ }
723
+ }, [isVisible, placement]);
724
+ useEffect3(() => {
725
+ return () => {
726
+ if (timeoutRef.current) {
727
+ clearTimeout(timeoutRef.current);
728
+ }
729
+ };
730
+ }, []);
731
+ const variantStyles = {
732
+ default: `
733
+ bg-pixelforge-black dark:bg-pixelforge-white
734
+ text-pixelforge-white dark:text-pixelforge-black
735
+ border-2 border-pixelforge-darkLime
736
+ `,
737
+ dark: `
738
+ bg-pixelforge-black/90 dark:bg-pixelforge-white/90
739
+ text-pixelforge-white dark:text-pixelforge-black
740
+ border-2 border-pixelforge-darkLime/50
741
+ backdrop-blur-xs
742
+ `
743
+ };
744
+ return /* @__PURE__ */ jsxs7(Fragment3, { children: [
745
+ /* @__PURE__ */ jsx7(
746
+ "div",
747
+ {
748
+ ref: triggerRef,
749
+ onMouseEnter: handleMouseEnter,
750
+ onMouseLeave: handleMouseLeave,
751
+ className: "inline-block",
752
+ children
753
+ }
754
+ ),
755
+ isVisible && /* @__PURE__ */ jsxs7(
756
+ "div",
757
+ {
758
+ ref: tooltipRef,
759
+ style: {
760
+ position: "fixed",
761
+ top: `${position.top}px`,
762
+ left: `${position.left}px`
763
+ },
764
+ className: `
765
+ z-tooltip
766
+ px-3 py-2
767
+ font-mono text-xs
768
+ rounded-px-sm
769
+ shadow-px-lg
770
+ whitespace-nowrap
771
+ pointer-events-none
772
+ transition-opacity duration-200
773
+ ${variantStyles[variant]}
774
+ `,
775
+ role: "tooltip",
776
+ children: [
777
+ content,
778
+ /* @__PURE__ */ jsx7(
779
+ "div",
780
+ {
781
+ className: `
782
+ absolute w-2 h-2 bg-pixelforge-black dark:bg-pixelforge-white
783
+ border-r-2 border-b-2 border-pixelforge-darkLime
784
+ ${placement === "top" ? "bottom-0 left-1/2 -translate-x-1/2 translate-y-1/2 rotate-45" : placement === "bottom" ? "top-0 left-1/2 -translate-x-1/2 -translate-y-1/2 -rotate-45" : placement === "left" ? "right-0 top-1/2 -translate-y-1/2 translate-x-1/2 rotate-[135deg]" : "left-0 top-1/2 -translate-y-1/2 -translate-x-1/2 -rotate-[135deg]"}
785
+ `
786
+ }
787
+ )
788
+ ]
789
+ }
790
+ )
791
+ ] });
792
+ };
793
+ Tooltip.displayName = "Tooltip";
794
+
795
+ // src/components/Navbar.tsx
796
+ import React8 from "react";
797
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
798
+ var Navbar = React8.forwardRef(
799
+ ({
800
+ brand,
801
+ links = [],
802
+ rightElement,
803
+ onBrandClick,
804
+ sticky = false,
805
+ className = "",
806
+ ...props
807
+ }, ref) => {
808
+ return /* @__PURE__ */ jsx8(
809
+ "nav",
810
+ {
811
+ ref,
812
+ className: `
813
+ bg-pixelforge-white dark:bg-pixelforge-dark
814
+ text-pixelforge-black dark:text-pixelforge-white
815
+ border-b-2 border-pixelforge-darkLime dark:border-pixelforge-lime
816
+ shadow-px
817
+ transition-all duration-200
818
+ ${sticky ? "sticky top-0 z-fixed" : ""}
819
+ ${className}
820
+ `,
821
+ ...props,
822
+ children: /* @__PURE__ */ jsx8("div", { className: "max-w-full px-6 py-4", children: /* @__PURE__ */ jsxs8("div", { className: "flex items-center justify-between gap-8", children: [
823
+ brand && /* @__PURE__ */ jsx8(
824
+ "button",
825
+ {
826
+ onClick: onBrandClick,
827
+ className: `
828
+ font-pixel text-lg
829
+ text-pixelforge-black dark:text-pixelforge-white
830
+ hover:text-pixelforge-accent
831
+ transition-colors
832
+ whitespace-nowrap
833
+ ${onBrandClick ? "cursor-pointer" : "cursor-default"}
834
+ `,
835
+ children: brand
836
+ }
837
+ ),
838
+ links.length > 0 && /* @__PURE__ */ jsx8("div", { className: "hidden md:flex items-center gap-1", children: links.map((link, index) => /* @__PURE__ */ jsx8(
839
+ "a",
840
+ {
841
+ href: link.href || "#",
842
+ onClick: (e) => {
843
+ if (link.onClick) {
844
+ e.preventDefault();
845
+ link.onClick();
846
+ }
847
+ },
848
+ className: `
849
+ px-4 py-2
850
+ font-mono text-sm
851
+ border-2 border-transparent
852
+ rounded-px-sm
853
+ transition-all duration-200
854
+ ${link.disabled ? "opacity-50 cursor-not-allowed text-pixelforge-gray dark:text-pixelforge-gray/50" : `
855
+ text-pixelforge-black dark:text-pixelforge-white
856
+ hover:bg-pixelforge-lime/20 dark:hover:bg-pixelforge-lime/40
857
+ ${link.active ? "bg-pixelforge-lime/30 dark:bg-pixelforge-lime/50 border-pixelforge-darkLime dark:border-pixelforge-lime" : ""}
858
+ `}
859
+ `,
860
+ children: link.label
861
+ },
862
+ index
863
+ )) }),
864
+ /* @__PURE__ */ jsx8("div", { className: "flex-1" }),
865
+ rightElement && /* @__PURE__ */ jsx8("div", { className: "flex items-center gap-4", children: rightElement })
866
+ ] }) })
867
+ }
868
+ );
869
+ }
870
+ );
871
+ Navbar.displayName = "Navbar";
872
+
873
+ // src/components/Sidebar.tsx
874
+ import React9, { useState as useState3 } from "react";
875
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
876
+ var Sidebar = React9.forwardRef(
877
+ ({
878
+ brand,
879
+ items,
880
+ collapsible = true,
881
+ defaultCollapsed = false,
882
+ footer,
883
+ className = "",
884
+ ...props
885
+ }, ref) => {
886
+ const [isCollapsed, setIsCollapsed] = useState3(defaultCollapsed);
887
+ const [expandedItems, setExpandedItems] = useState3([]);
888
+ const toggleExpand = (itemId) => {
889
+ setExpandedItems(
890
+ (prev) => prev.includes(itemId) ? prev.filter((id) => id !== itemId) : [...prev, itemId]
891
+ );
892
+ };
893
+ const renderItem = (item, depth = 0) => {
894
+ const isExpanded = expandedItems.includes(item.id);
895
+ const hasChildren = item.children && item.children.length > 0;
896
+ return /* @__PURE__ */ jsxs9("div", { children: [
897
+ /* @__PURE__ */ jsxs9(
898
+ "button",
899
+ {
900
+ onClick: () => {
901
+ if (hasChildren) {
902
+ toggleExpand(item.id);
903
+ }
904
+ item.onClick?.();
905
+ },
906
+ disabled: item.disabled,
907
+ className: `
908
+ w-full text-left
909
+ px-4 py-3
910
+ font-mono text-sm
911
+ border-2 border-transparent
912
+ rounded-px-sm
913
+ transition-all duration-200
914
+ flex items-center gap-3
915
+ ${item.disabled ? "opacity-50 cursor-not-allowed text-pixelforge-gray dark:text-pixelforge-gray/50" : `
916
+ text-pixelforge-black dark:text-pixelforge-white
917
+ hover:bg-pixelforge-lime/20 dark:hover:bg-pixelforge-lime/40
918
+ ${item.active ? "bg-pixelforge-lime/30 dark:bg-pixelforge-lime/50 border-pixelforge-darkLime dark:border-pixelforge-lime" : ""}
919
+ `}
920
+ ml-${depth * 4}
921
+ `,
922
+ style: {
923
+ marginLeft: `${depth * 16}px`
924
+ },
925
+ children: [
926
+ item.icon && /* @__PURE__ */ jsx9("span", { className: "flex-shrink-0 text-lg", children: item.icon }),
927
+ /* @__PURE__ */ jsx9("span", { className: "flex-1", children: item.label }),
928
+ hasChildren && /* @__PURE__ */ jsx9(
929
+ "span",
930
+ {
931
+ className: `
932
+ transition-transform duration-200
933
+ ${isExpanded ? "rotate-180" : ""}
934
+ `,
935
+ children: "\u25BC"
936
+ }
937
+ )
938
+ ]
939
+ }
940
+ ),
941
+ hasChildren && isExpanded && /* @__PURE__ */ jsx9("div", { className: "mt-1", children: item.children?.map((child) => renderItem(child, depth + 1)) })
942
+ ] }, item.id);
943
+ };
944
+ return /* @__PURE__ */ jsxs9(
945
+ "div",
946
+ {
947
+ ref,
948
+ className: `
949
+ bg-pixelforge-white dark:bg-pixelforge-dark
950
+ text-pixelforge-black dark:text-pixelforge-white
951
+ border-r-2 border-pixelforge-darkLime dark:border-pixelforge-lime
952
+ h-screen
953
+ overflow-y-auto
954
+ transition-all duration-300
955
+ flex flex-col
956
+ ${isCollapsed ? "w-20" : "w-64"}
957
+ ${className}
958
+ `,
959
+ ...props,
960
+ children: [
961
+ /* @__PURE__ */ jsx9("div", { className: "border-b-2 border-pixelforge-darkLime dark:border-pixelforge-lime px-4 py-4", children: /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between gap-2", children: [
962
+ !isCollapsed && brand && /* @__PURE__ */ jsx9("h2", { className: "font-pixel text-sm text-pixelforge-black dark:text-pixelforge-white truncate", children: brand }),
963
+ collapsible && /* @__PURE__ */ jsx9(
964
+ "button",
965
+ {
966
+ onClick: () => setIsCollapsed(!isCollapsed),
967
+ className: `
968
+ p-2 -mr-2
969
+ text-pixelforge-black dark:text-pixelforge-white
970
+ hover:bg-pixelforge-lime/20 dark:hover:bg-pixelforge-lime/30
971
+ rounded-px-sm
972
+ transition-all
973
+ flex-shrink-0
974
+ `,
975
+ "aria-label": isCollapsed ? "Expand sidebar" : "Collapse sidebar",
976
+ children: isCollapsed ? "\u2192" : "\u2190"
977
+ }
978
+ )
979
+ ] }) }),
980
+ /* @__PURE__ */ jsx9("div", { className: "flex-1 overflow-y-auto py-4 px-2 space-y-1", children: items.map((item) => renderItem(item)) }),
981
+ footer && /* @__PURE__ */ jsx9("div", { className: "border-t-2 border-pixelforge-darkLime dark:border-pixelforge-lime px-4 py-4", children: footer })
982
+ ]
983
+ }
984
+ );
985
+ }
986
+ );
987
+ Sidebar.displayName = "Sidebar";
988
+
989
+ // src/components/Footer.tsx
990
+ import React10 from "react";
991
+ import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
992
+ var Footer = React10.forwardRef(
993
+ ({
994
+ brand,
995
+ sections = [],
996
+ copyright,
997
+ socialLinks,
998
+ className = "",
999
+ ...props
1000
+ }, ref) => {
1001
+ return /* @__PURE__ */ jsxs10(
1002
+ "footer",
1003
+ {
1004
+ ref,
1005
+ className: `
1006
+ bg-pixelforge-white dark:bg-pixelforge-dark
1007
+ text-pixelforge-black dark:text-pixelforge-white
1008
+ border-t-2 border-pixelforge-darkLime dark:border-pixelforge-lime
1009
+ ${className}
1010
+ `,
1011
+ ...props,
1012
+ children: [
1013
+ (brand || sections.length > 0 || socialLinks) && /* @__PURE__ */ jsx10("div", { className: "max-w-full px-6 py-12", children: /* @__PURE__ */ jsxs10("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8", children: [
1014
+ brand && /* @__PURE__ */ jsxs10("div", { className: "col-span-1", children: [
1015
+ /* @__PURE__ */ jsx10("div", { className: "font-pixel text-lg text-pixelforge-black dark:text-pixelforge-white mb-4", children: brand }),
1016
+ socialLinks && /* @__PURE__ */ jsx10("div", { className: "flex gap-3", children: socialLinks })
1017
+ ] }),
1018
+ sections.map((section, index) => /* @__PURE__ */ jsxs10("div", { children: [
1019
+ /* @__PURE__ */ jsx10("h3", { className: "font-pixel text-sm text-pixelforge-black dark:text-pixelforge-white mb-4 uppercase tracking-wider", children: section.title }),
1020
+ /* @__PURE__ */ jsx10("ul", { className: "space-y-3", children: section.links.map((link, linkIndex) => /* @__PURE__ */ jsx10("li", { children: /* @__PURE__ */ jsx10(
1021
+ "a",
1022
+ {
1023
+ href: link.href || "#",
1024
+ onClick: (e) => {
1025
+ if (link.onClick) {
1026
+ e.preventDefault();
1027
+ link.onClick();
1028
+ }
1029
+ },
1030
+ className: `
1031
+ font-mono text-sm
1032
+ text-pixelforge-gray dark:text-pixelforge-gray/70
1033
+ hover:text-pixelforge-black dark:hover:text-pixelforge-white
1034
+ transition-colors duration-200
1035
+ hover:underline
1036
+ `,
1037
+ children: link.label
1038
+ }
1039
+ ) }, linkIndex)) })
1040
+ ] }, index))
1041
+ ] }) }),
1042
+ copyright && /* @__PURE__ */ jsx10("div", { className: "border-t-2 border-pixelforge-darkLime dark:border-pixelforge-lime/30 px-6 py-6 bg-pixelforge-light/50 dark:bg-pixelforge-black/20", children: /* @__PURE__ */ jsx10("p", { className: "font-mono text-xs text-pixelforge-gray dark:text-pixelforge-gray/70 text-center", children: copyright }) })
1043
+ ]
1044
+ }
1045
+ );
1046
+ }
1047
+ );
1048
+ Footer.displayName = "Footer";
1049
+ export {
1050
+ Badge,
1051
+ Button,
1052
+ Card,
1053
+ Dropdown,
1054
+ Footer,
1055
+ Input,
1056
+ Modal,
1057
+ Navbar,
1058
+ Sidebar,
1059
+ Tooltip,
1060
+ borderRadius,
1061
+ breakpoints,
1062
+ colors,
1063
+ gamePalettes,
1064
+ shadows,
1065
+ spacing,
1066
+ transitions,
1067
+ typography,
1068
+ zIndex
1069
+ };