@pos-360/horizon 0.1.0 → 0.2.1

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.
@@ -1,7 +1,10 @@
1
- import { motion } from 'framer-motion';
1
+ import { cn } from './chunk-TDRL2RCT.mjs';
2
+ import { motion, AnimatePresence } from 'framer-motion';
2
3
  import { jsxs, jsx } from 'react/jsx-runtime';
4
+ import * as React from 'react';
3
5
  import { forwardRef, useState, useEffect } from 'react';
4
- import { Info, ChevronDown, ChevronUp, Check, AlertTriangle, X } from 'lucide-react';
6
+ import { Info, ChevronDown, ChevronUp, LayoutGrid, List, Check, ExternalLink, AlertTriangle, X, Moon, Rocket, Star, Globe, Orbit, Sparkles } from 'lucide-react';
7
+ export { Globe, Moon, Orbit, Rocket, Sparkles, Star } from 'lucide-react';
5
8
 
6
9
  var AnimatedButton = ({
7
10
  label,
@@ -340,6 +343,479 @@ var Input = forwardRef(
340
343
  }
341
344
  );
342
345
  Input.displayName = "Input";
346
+ var accentColors = {
347
+ blue: {
348
+ selected: "bg-blue-100/80 dark:bg-blue-900/30",
349
+ selectedBorder: "border-blue-500 dark:border-blue-400",
350
+ selectedGlow: "shadow-lg shadow-blue-500/20 dark:shadow-blue-400/20",
351
+ hoverBg: "group-hover:bg-blue-50/60 dark:group-hover:bg-blue-900/20",
352
+ hoverBgDark: "hover:bg-blue-50 dark:hover:bg-blue-900/30",
353
+ iconSelected: "text-blue-600 dark:text-blue-400",
354
+ iconHover: "group-hover:text-blue-500 dark:group-hover:text-blue-400",
355
+ checkBg: "bg-blue-500 border-blue-500 dark:bg-blue-400 dark:border-blue-400",
356
+ inputBorder: "border-blue-500 dark:border-blue-400",
357
+ inputRing: "focus:ring-blue-500/20",
358
+ textHover: "hover:text-blue-600 dark:hover:text-blue-400",
359
+ toggleActive: "text-blue-600 dark:text-blue-400"
360
+ },
361
+ violet: {
362
+ selected: "bg-violet-100/80 dark:bg-violet-900/30",
363
+ selectedBorder: "border-violet-500 dark:border-violet-400",
364
+ selectedGlow: "shadow-lg shadow-violet-500/20 dark:shadow-violet-400/20",
365
+ hoverBg: "group-hover:bg-violet-50/60 dark:group-hover:bg-violet-900/20",
366
+ hoverBgDark: "hover:bg-violet-50 dark:hover:bg-violet-900/30",
367
+ iconSelected: "text-violet-600 dark:text-violet-400",
368
+ iconHover: "group-hover:text-violet-500 dark:group-hover:text-violet-400",
369
+ checkBg: "bg-violet-500 border-violet-500 dark:bg-violet-400 dark:border-violet-400",
370
+ inputBorder: "border-violet-500 dark:border-violet-400",
371
+ inputRing: "focus:ring-violet-500/20",
372
+ textHover: "hover:text-violet-600 dark:hover:text-violet-400",
373
+ toggleActive: "text-violet-600 dark:text-violet-400"
374
+ },
375
+ emerald: {
376
+ selected: "bg-emerald-100/80 dark:bg-emerald-900/30",
377
+ selectedBorder: "border-emerald-500 dark:border-emerald-400",
378
+ selectedGlow: "shadow-lg shadow-emerald-500/20 dark:shadow-emerald-400/20",
379
+ hoverBg: "group-hover:bg-emerald-50/60 dark:group-hover:bg-emerald-900/20",
380
+ hoverBgDark: "hover:bg-emerald-50 dark:hover:bg-emerald-900/30",
381
+ iconSelected: "text-emerald-600 dark:text-emerald-400",
382
+ iconHover: "group-hover:text-emerald-500 dark:group-hover:text-emerald-400",
383
+ checkBg: "bg-emerald-500 border-emerald-500 dark:bg-emerald-400 dark:border-emerald-400",
384
+ inputBorder: "border-emerald-500 dark:border-emerald-400",
385
+ inputRing: "focus:ring-emerald-500/20",
386
+ textHover: "hover:text-emerald-600 dark:hover:text-emerald-400",
387
+ toggleActive: "text-emerald-600 dark:text-emerald-400"
388
+ },
389
+ rose: {
390
+ selected: "bg-rose-100/80 dark:bg-rose-900/30",
391
+ selectedBorder: "border-rose-500 dark:border-rose-400",
392
+ selectedGlow: "shadow-lg shadow-rose-500/20 dark:shadow-rose-400/20",
393
+ hoverBg: "group-hover:bg-rose-50/60 dark:group-hover:bg-rose-900/20",
394
+ hoverBgDark: "hover:bg-rose-50 dark:hover:bg-rose-900/30",
395
+ iconSelected: "text-rose-600 dark:text-rose-400",
396
+ iconHover: "group-hover:text-rose-500 dark:group-hover:text-rose-400",
397
+ checkBg: "bg-rose-500 border-rose-500 dark:bg-rose-400 dark:border-rose-400",
398
+ inputBorder: "border-rose-500 dark:border-rose-400",
399
+ inputRing: "focus:ring-rose-500/20",
400
+ textHover: "hover:text-rose-600 dark:hover:text-rose-400",
401
+ toggleActive: "text-rose-600 dark:text-rose-400"
402
+ },
403
+ amber: {
404
+ selected: "bg-amber-100/80 dark:bg-amber-900/30",
405
+ selectedBorder: "border-amber-500 dark:border-amber-400",
406
+ selectedGlow: "shadow-lg shadow-amber-500/20 dark:shadow-amber-400/20",
407
+ hoverBg: "group-hover:bg-amber-50/60 dark:group-hover:bg-amber-900/20",
408
+ hoverBgDark: "hover:bg-amber-50 dark:hover:bg-amber-900/30",
409
+ iconSelected: "text-amber-600 dark:text-amber-400",
410
+ iconHover: "group-hover:text-amber-500 dark:group-hover:text-amber-400",
411
+ checkBg: "bg-amber-500 border-amber-500 dark:bg-amber-400 dark:border-amber-400",
412
+ inputBorder: "border-amber-500 dark:border-amber-400",
413
+ inputRing: "focus:ring-amber-500/20",
414
+ textHover: "hover:text-amber-600 dark:hover:text-amber-400",
415
+ toggleActive: "text-amber-600 dark:text-amber-400"
416
+ }
417
+ };
418
+ var defaultIconMap = {
419
+ nebula: Sparkles,
420
+ andromeda: Orbit,
421
+ cosmos: Globe,
422
+ stellar: Star,
423
+ voyager: Rocket,
424
+ eclipse: Moon
425
+ };
426
+ var getTemplateIcon = (template) => {
427
+ if (template.icon) return template.icon;
428
+ const key = template.title.toLowerCase();
429
+ return defaultIconMap[key] || Star;
430
+ };
431
+ var TemplateSelector = ({
432
+ templates,
433
+ value,
434
+ onChange,
435
+ onNameChange,
436
+ defaultView = "card",
437
+ showViewToggle = true,
438
+ editable = false,
439
+ className,
440
+ cardClassName,
441
+ columns = 3,
442
+ accentColor = "blue"
443
+ }) => {
444
+ const colors = accentColors[accentColor];
445
+ const [view, setView] = useState(defaultView);
446
+ const [selectedId, setSelectedId] = useState(value);
447
+ React.useEffect(() => {
448
+ setSelectedId(value);
449
+ }, [value]);
450
+ const handleSelect = (templateId) => {
451
+ setSelectedId(templateId);
452
+ onChange?.(templateId);
453
+ };
454
+ const handlePreviewClick = (e, previewUrl) => {
455
+ e.stopPropagation();
456
+ window.open(previewUrl, "_blank", "noopener,noreferrer");
457
+ };
458
+ const handleNameChange = (templateId, newName) => {
459
+ onNameChange?.(templateId, newName);
460
+ };
461
+ const columnClasses = {
462
+ 2: "grid-cols-1 sm:grid-cols-2",
463
+ 3: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
464
+ 4: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"
465
+ };
466
+ return /* @__PURE__ */ jsxs("div", { className: cn("w-full", className), children: [
467
+ showViewToggle && /* @__PURE__ */ jsx("div", { className: "flex justify-end mb-4", children: /* @__PURE__ */ jsxs("div", { className: "inline-flex rounded-lg border border-neutral-200 dark:border-neutral-700 p-1 bg-neutral-100 dark:bg-neutral-800", children: [
468
+ /* @__PURE__ */ jsx(
469
+ "button",
470
+ {
471
+ type: "button",
472
+ onClick: () => setView("card"),
473
+ className: cn(
474
+ "p-2 rounded-md text-sm font-medium transition-all duration-200",
475
+ view === "card" ? `bg-white dark:bg-neutral-700 shadow-sm ${colors.toggleActive}` : "text-neutral-500 dark:text-neutral-400 hover:text-neutral-700 dark:hover:text-neutral-300"
476
+ ),
477
+ "aria-label": "Card view",
478
+ children: /* @__PURE__ */ jsx(LayoutGrid, { size: 16 })
479
+ }
480
+ ),
481
+ /* @__PURE__ */ jsx(
482
+ "button",
483
+ {
484
+ type: "button",
485
+ onClick: () => setView("list"),
486
+ className: cn(
487
+ "p-2 rounded-md text-sm font-medium transition-all duration-200",
488
+ view === "list" ? `bg-white dark:bg-neutral-700 shadow-sm ${colors.toggleActive}` : "text-neutral-500 dark:text-neutral-400 hover:text-neutral-700 dark:hover:text-neutral-300"
489
+ ),
490
+ "aria-label": "List view",
491
+ children: /* @__PURE__ */ jsx(List, { size: 16 })
492
+ }
493
+ )
494
+ ] }) }),
495
+ /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: view === "card" ? /* @__PURE__ */ jsx(
496
+ motion.div,
497
+ {
498
+ initial: { opacity: 0, y: 10 },
499
+ animate: { opacity: 1, y: 0 },
500
+ exit: { opacity: 0, y: -10 },
501
+ transition: { duration: 0.2 },
502
+ className: cn("grid gap-4", columnClasses[columns]),
503
+ children: templates.map((template) => /* @__PURE__ */ jsx(
504
+ TemplateCard,
505
+ {
506
+ template,
507
+ isSelected: selectedId === template.id,
508
+ onSelect: handleSelect,
509
+ onPreviewClick: handlePreviewClick,
510
+ onNameChange: handleNameChange,
511
+ editable,
512
+ className: cardClassName,
513
+ colors
514
+ },
515
+ template.id
516
+ ))
517
+ },
518
+ "card-view"
519
+ ) : /* @__PURE__ */ jsx(
520
+ motion.div,
521
+ {
522
+ initial: { opacity: 0, y: 10 },
523
+ animate: { opacity: 1, y: 0 },
524
+ exit: { opacity: 0, y: -10 },
525
+ transition: { duration: 0.2 },
526
+ className: "flex flex-col gap-2",
527
+ children: templates.map((template) => /* @__PURE__ */ jsx(
528
+ TemplateListItem,
529
+ {
530
+ template,
531
+ isSelected: selectedId === template.id,
532
+ onSelect: handleSelect,
533
+ onPreviewClick: handlePreviewClick,
534
+ onNameChange: handleNameChange,
535
+ editable,
536
+ colors
537
+ },
538
+ template.id
539
+ ))
540
+ },
541
+ "list-view"
542
+ ) })
543
+ ] });
544
+ };
545
+ var TemplateCard = ({
546
+ template,
547
+ isSelected,
548
+ onSelect,
549
+ onPreviewClick,
550
+ onNameChange,
551
+ editable,
552
+ className,
553
+ colors
554
+ }) => {
555
+ const [isEditing, setIsEditing] = useState(false);
556
+ const [editValue, setEditValue] = useState(template.name || template.title);
557
+ const inputRef = React.useRef(null);
558
+ const displayName = template.name || template.title;
559
+ const IconComponent = getTemplateIcon(template);
560
+ const handleDoubleClick = (e) => {
561
+ if (editable) {
562
+ e.stopPropagation();
563
+ setIsEditing(true);
564
+ setEditValue(displayName);
565
+ }
566
+ };
567
+ const handleBlur = () => {
568
+ setIsEditing(false);
569
+ if (editValue.trim() && editValue !== displayName) {
570
+ onNameChange(template.id, editValue.trim());
571
+ }
572
+ };
573
+ const handleKeyDown = (e) => {
574
+ if (e.key === "Enter") {
575
+ handleBlur();
576
+ } else if (e.key === "Escape") {
577
+ setIsEditing(false);
578
+ setEditValue(displayName);
579
+ }
580
+ };
581
+ React.useEffect(() => {
582
+ if (isEditing && inputRef.current) {
583
+ inputRef.current.focus();
584
+ inputRef.current.select();
585
+ }
586
+ }, [isEditing]);
587
+ return /* @__PURE__ */ jsxs(
588
+ motion.div,
589
+ {
590
+ whileHover: { y: -4, scale: 1.02 },
591
+ whileTap: { scale: 0.98 },
592
+ transition: { type: "spring", stiffness: 400, damping: 25 },
593
+ onClick: () => onSelect(template.id),
594
+ className: cn(
595
+ "group relative cursor-pointer rounded-xl overflow-hidden border-2 transition-all duration-300",
596
+ "bg-white dark:bg-neutral-800",
597
+ isSelected ? `${colors.selectedBorder} ${colors.selectedGlow}` : "border-neutral-200 dark:border-neutral-700 hover:border-neutral-300 dark:hover:border-neutral-600 hover:shadow-md",
598
+ className
599
+ ),
600
+ children: [
601
+ /* @__PURE__ */ jsx(
602
+ "div",
603
+ {
604
+ className: cn(
605
+ "absolute top-3 left-3 z-10 w-5 h-5 rounded-full border-2 flex items-center justify-center transition-all duration-200",
606
+ isSelected ? colors.checkBg : "bg-white dark:bg-neutral-800 border-neutral-300 dark:border-neutral-600"
607
+ ),
608
+ children: isSelected && /* @__PURE__ */ jsx(Check, { size: 12, className: "text-white", strokeWidth: 3 })
609
+ }
610
+ ),
611
+ /* @__PURE__ */ jsx(
612
+ "button",
613
+ {
614
+ type: "button",
615
+ onClick: (e) => onPreviewClick(e, template.previewUrl),
616
+ className: cn(
617
+ "absolute top-3 right-3 z-10 p-1.5 rounded-lg transition-all duration-200",
618
+ "bg-white dark:bg-neutral-700",
619
+ "border border-neutral-200 dark:border-neutral-600",
620
+ "text-neutral-500 dark:text-neutral-400",
621
+ "opacity-0 group-hover:opacity-100",
622
+ "hover:bg-blue-50 dark:hover:bg-blue-900/30 hover:text-blue-600 dark:hover:text-blue-400 hover:border-blue-300 dark:hover:border-blue-600"
623
+ ),
624
+ "aria-label": `Preview ${displayName}`,
625
+ children: /* @__PURE__ */ jsx(ExternalLink, { size: 14 })
626
+ }
627
+ ),
628
+ /* @__PURE__ */ jsx("div", { className: "aspect-[4/3] overflow-hidden bg-neutral-50 dark:bg-neutral-800/80 flex items-center justify-center", children: /* @__PURE__ */ jsx(
629
+ motion.div,
630
+ {
631
+ initial: { scale: 0.9, opacity: 0.5 },
632
+ animate: { scale: 1, opacity: 1 },
633
+ className: cn(
634
+ "p-6 rounded-full transition-all duration-300",
635
+ isSelected ? colors.selected : `bg-neutral-100 dark:bg-neutral-700/50 ${colors.hoverBg}`
636
+ ),
637
+ children: /* @__PURE__ */ jsx(
638
+ IconComponent,
639
+ {
640
+ size: 48,
641
+ strokeWidth: 1.5,
642
+ className: cn(
643
+ "transition-colors duration-300",
644
+ isSelected ? colors.iconSelected : `text-neutral-400 dark:text-neutral-500 ${colors.iconHover}`
645
+ )
646
+ }
647
+ )
648
+ }
649
+ ) }),
650
+ /* @__PURE__ */ jsx("div", { className: "p-3 border-t border-neutral-100 dark:border-neutral-700", children: isEditing ? /* @__PURE__ */ jsx(
651
+ "input",
652
+ {
653
+ ref: inputRef,
654
+ type: "text",
655
+ value: editValue,
656
+ onChange: (e) => setEditValue(e.target.value),
657
+ onBlur: handleBlur,
658
+ onKeyDown: handleKeyDown,
659
+ onClick: (e) => e.stopPropagation(),
660
+ className: cn(
661
+ "w-full px-2 py-1 text-sm font-medium rounded",
662
+ "bg-neutral-100 dark:bg-neutral-700",
663
+ `border ${colors.inputBorder}`,
664
+ "text-neutral-900 dark:text-neutral-100",
665
+ `focus:outline-none focus:ring-2 ${colors.inputRing}`
666
+ )
667
+ }
668
+ ) : /* @__PURE__ */ jsx(
669
+ "h3",
670
+ {
671
+ onDoubleClick: handleDoubleClick,
672
+ className: cn(
673
+ "text-sm font-medium text-neutral-900 dark:text-neutral-100 truncate",
674
+ editable && `cursor-text ${colors.textHover}`
675
+ ),
676
+ title: editable ? "Double-click to rename" : displayName,
677
+ children: displayName
678
+ }
679
+ ) })
680
+ ]
681
+ }
682
+ );
683
+ };
684
+ var TemplateListItem = ({
685
+ template,
686
+ isSelected,
687
+ onSelect,
688
+ onPreviewClick,
689
+ onNameChange,
690
+ editable,
691
+ colors
692
+ }) => {
693
+ const [isEditing, setIsEditing] = useState(false);
694
+ const [editValue, setEditValue] = useState(template.name || template.title);
695
+ const inputRef = React.useRef(null);
696
+ const displayName = template.name || template.title;
697
+ const IconComponent = getTemplateIcon(template);
698
+ const handleDoubleClick = (e) => {
699
+ if (editable) {
700
+ e.stopPropagation();
701
+ setIsEditing(true);
702
+ setEditValue(displayName);
703
+ }
704
+ };
705
+ const handleBlur = () => {
706
+ setIsEditing(false);
707
+ if (editValue.trim() && editValue !== displayName) {
708
+ onNameChange(template.id, editValue.trim());
709
+ }
710
+ };
711
+ const handleKeyDown = (e) => {
712
+ if (e.key === "Enter") {
713
+ handleBlur();
714
+ } else if (e.key === "Escape") {
715
+ setIsEditing(false);
716
+ setEditValue(displayName);
717
+ }
718
+ };
719
+ React.useEffect(() => {
720
+ if (isEditing && inputRef.current) {
721
+ inputRef.current.focus();
722
+ inputRef.current.select();
723
+ }
724
+ }, [isEditing]);
725
+ return /* @__PURE__ */ jsxs(
726
+ motion.div,
727
+ {
728
+ whileHover: { x: 4, scale: 1.01 },
729
+ whileTap: { scale: 0.995 },
730
+ transition: { type: "spring", stiffness: 400, damping: 25 },
731
+ onClick: () => onSelect(template.id),
732
+ className: cn(
733
+ "group flex items-center gap-4 p-3 cursor-pointer rounded-xl border-2 transition-all duration-300",
734
+ "bg-white dark:bg-neutral-800",
735
+ isSelected ? `${colors.selectedBorder} shadow-md ${colors.selectedGlow}` : "border-neutral-200 dark:border-neutral-700 hover:border-neutral-300 dark:hover:border-neutral-600"
736
+ ),
737
+ children: [
738
+ /* @__PURE__ */ jsx(
739
+ "div",
740
+ {
741
+ className: cn(
742
+ "w-5 h-5 rounded-full border-2 flex items-center justify-center flex-shrink-0 transition-all duration-200",
743
+ isSelected ? colors.checkBg : "bg-white dark:bg-neutral-800 border-neutral-300 dark:border-neutral-600"
744
+ ),
745
+ children: isSelected && /* @__PURE__ */ jsx(Check, { size: 12, className: "text-white", strokeWidth: 3 })
746
+ }
747
+ ),
748
+ /* @__PURE__ */ jsx(
749
+ "div",
750
+ {
751
+ className: cn(
752
+ "w-12 h-12 rounded-lg flex items-center justify-center flex-shrink-0 transition-colors duration-300",
753
+ isSelected ? colors.selected : `bg-neutral-100 dark:bg-neutral-700/50 ${colors.hoverBg}`
754
+ ),
755
+ children: /* @__PURE__ */ jsx(
756
+ IconComponent,
757
+ {
758
+ size: 24,
759
+ strokeWidth: 1.5,
760
+ className: cn(
761
+ "transition-colors duration-300",
762
+ isSelected ? colors.iconSelected : `text-neutral-400 dark:text-neutral-500 ${colors.iconHover}`
763
+ )
764
+ }
765
+ )
766
+ }
767
+ ),
768
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: isEditing ? /* @__PURE__ */ jsx(
769
+ "input",
770
+ {
771
+ ref: inputRef,
772
+ type: "text",
773
+ value: editValue,
774
+ onChange: (e) => setEditValue(e.target.value),
775
+ onBlur: handleBlur,
776
+ onKeyDown: handleKeyDown,
777
+ onClick: (e) => e.stopPropagation(),
778
+ className: cn(
779
+ "w-full px-2 py-1 text-sm font-medium rounded",
780
+ "bg-neutral-100 dark:bg-neutral-700",
781
+ `border ${colors.inputBorder}`,
782
+ "text-neutral-900 dark:text-neutral-100",
783
+ `focus:outline-none focus:ring-2 ${colors.inputRing}`
784
+ )
785
+ }
786
+ ) : /* @__PURE__ */ jsx(
787
+ "h3",
788
+ {
789
+ onDoubleClick: handleDoubleClick,
790
+ className: cn(
791
+ "text-sm font-medium text-neutral-900 dark:text-neutral-100 truncate",
792
+ editable && `cursor-text ${colors.textHover}`
793
+ ),
794
+ title: editable ? "Double-click to rename" : displayName,
795
+ children: displayName
796
+ }
797
+ ) }),
798
+ /* @__PURE__ */ jsx(
799
+ "button",
800
+ {
801
+ type: "button",
802
+ onClick: (e) => onPreviewClick(e, template.previewUrl),
803
+ className: cn(
804
+ "p-2 rounded-lg transition-all duration-200 flex-shrink-0",
805
+ "text-neutral-400 dark:text-neutral-500",
806
+ "opacity-0 group-hover:opacity-100",
807
+ colors.hoverBgDark,
808
+ colors.textHover
809
+ ),
810
+ "aria-label": `Preview ${displayName}`,
811
+ children: /* @__PURE__ */ jsx(ExternalLink, { size: 16 })
812
+ }
813
+ )
814
+ ]
815
+ }
816
+ );
817
+ };
818
+ TemplateSelector.displayName = "TemplateSelector";
343
819
  var TextButton = ({
344
820
  children,
345
821
  onClick,
@@ -466,6 +942,6 @@ var Toast = ({
466
942
  );
467
943
  };
468
944
 
469
- export { AnimatedButton, Input, TextButton, Toast };
470
- //# sourceMappingURL=chunk-6YAK6HNR.mjs.map
471
- //# sourceMappingURL=chunk-6YAK6HNR.mjs.map
945
+ export { AnimatedButton, Input, TemplateSelector, TextButton, Toast };
946
+ //# sourceMappingURL=chunk-TQPMV72P.mjs.map
947
+ //# sourceMappingURL=chunk-TQPMV72P.mjs.map