@runcontext/uxd 0.5.3 → 0.6.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/css/index.css +286 -1
- package/dist/react/index.d.ts +63 -1
- package/dist/react/index.js +310 -4
- package/package.json +1 -1
package/dist/css/index.css
CHANGED
|
@@ -91,7 +91,10 @@
|
|
|
91
91
|
justify-content: center;
|
|
92
92
|
gap: var(--rc-space-2);
|
|
93
93
|
font-family: var(--rc-font-sans);
|
|
94
|
+
font-size: var(--rc-text-sm);
|
|
94
95
|
font-weight: var(--rc-font-weight-semibold);
|
|
96
|
+
padding: var(--rc-space-2) var(--rc-space-4);
|
|
97
|
+
height: 2.5rem;
|
|
95
98
|
border-radius: var(--rc-radius-md);
|
|
96
99
|
border: 1px solid transparent;
|
|
97
100
|
cursor: pointer;
|
|
@@ -238,7 +241,7 @@
|
|
|
238
241
|
gap: var(--rc-space-1);
|
|
239
242
|
font-size: var(--rc-text-xs);
|
|
240
243
|
font-weight: var(--rc-font-weight-medium);
|
|
241
|
-
padding: var(--rc-space-
|
|
244
|
+
padding: var(--rc-space-1) var(--rc-space-3);
|
|
242
245
|
border-radius: var(--rc-radius-full);
|
|
243
246
|
border: 1px solid transparent;
|
|
244
247
|
line-height: var(--rc-leading-normal);
|
|
@@ -618,6 +621,288 @@
|
|
|
618
621
|
font-size: var(--rc-text-sm);
|
|
619
622
|
line-height: var(--rc-leading-normal);
|
|
620
623
|
}
|
|
624
|
+
/* ── Toast ── */
|
|
625
|
+
.rc-toast-container {
|
|
626
|
+
position: fixed;
|
|
627
|
+
bottom: var(--rc-space-4);
|
|
628
|
+
right: var(--rc-space-4);
|
|
629
|
+
z-index: 9999;
|
|
630
|
+
display: flex;
|
|
631
|
+
flex-direction: column;
|
|
632
|
+
gap: var(--rc-space-2);
|
|
633
|
+
pointer-events: none;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
.rc-toast {
|
|
637
|
+
display: flex;
|
|
638
|
+
align-items: flex-start;
|
|
639
|
+
gap: var(--rc-space-3);
|
|
640
|
+
padding: var(--rc-space-3) var(--rc-space-4);
|
|
641
|
+
border-radius: var(--rc-radius-lg);
|
|
642
|
+
background: var(--rc-color-surface-card);
|
|
643
|
+
border: 1px solid var(--rc-color-surface-border);
|
|
644
|
+
color: var(--rc-color-text-primary);
|
|
645
|
+
font-family: var(--rc-font-sans);
|
|
646
|
+
font-size: var(--rc-text-sm);
|
|
647
|
+
line-height: var(--rc-line-height);
|
|
648
|
+
box-shadow: var(--rc-shadow-lg);
|
|
649
|
+
pointer-events: auto;
|
|
650
|
+
min-width: 300px;
|
|
651
|
+
max-width: 420px;
|
|
652
|
+
animation: rc-toast-in 0.2s ease-out;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
.rc-toast--exiting {
|
|
656
|
+
animation: rc-toast-out 0.15s ease-in forwards;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
.rc-toast__icon {
|
|
660
|
+
flex-shrink: 0;
|
|
661
|
+
width: 18px;
|
|
662
|
+
height: 18px;
|
|
663
|
+
margin-top: 1px;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
.rc-toast--success .rc-toast__icon { color: var(--rc-color-status-success); }
|
|
667
|
+
.rc-toast--error .rc-toast__icon { color: var(--rc-color-status-error); }
|
|
668
|
+
.rc-toast--warning .rc-toast__icon { color: var(--rc-color-status-warning); }
|
|
669
|
+
.rc-toast--info .rc-toast__icon { color: var(--rc-color-status-info); }
|
|
670
|
+
|
|
671
|
+
.rc-toast--success { border-left: 3px solid var(--rc-color-status-success); }
|
|
672
|
+
.rc-toast--error { border-left: 3px solid var(--rc-color-status-error); }
|
|
673
|
+
.rc-toast--warning { border-left: 3px solid var(--rc-color-status-warning); }
|
|
674
|
+
.rc-toast--info { border-left: 3px solid var(--rc-color-status-info); }
|
|
675
|
+
|
|
676
|
+
.rc-toast__content {
|
|
677
|
+
flex: 1;
|
|
678
|
+
min-width: 0;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
.rc-toast__dismiss {
|
|
682
|
+
flex-shrink: 0;
|
|
683
|
+
background: none;
|
|
684
|
+
border: none;
|
|
685
|
+
color: var(--rc-color-text-muted);
|
|
686
|
+
cursor: pointer;
|
|
687
|
+
padding: 2px;
|
|
688
|
+
line-height: 1;
|
|
689
|
+
transition: color 0.15s ease;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
.rc-toast__dismiss:hover {
|
|
693
|
+
color: var(--rc-color-text-primary);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
@keyframes rc-toast-in {
|
|
697
|
+
from { opacity: 0; transform: translateX(20px); }
|
|
698
|
+
to { opacity: 1; transform: translateX(0); }
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
@keyframes rc-toast-out {
|
|
702
|
+
from { opacity: 1; transform: translateX(0); }
|
|
703
|
+
to { opacity: 0; transform: translateX(20px); }
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/* ── Tooltip ── */
|
|
707
|
+
.rc-tooltip-wrapper {
|
|
708
|
+
position: relative;
|
|
709
|
+
display: inline-flex;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
.rc-tooltip {
|
|
713
|
+
position: fixed;
|
|
714
|
+
z-index: 9999;
|
|
715
|
+
padding: var(--rc-space-2) var(--rc-space-3);
|
|
716
|
+
border-radius: var(--rc-radius-md);
|
|
717
|
+
background: #f8f6f1;
|
|
718
|
+
border: 1px solid #d4cfc5;
|
|
719
|
+
color: #1e1e1e;
|
|
720
|
+
font-family: var(--rc-font-sans);
|
|
721
|
+
font-size: var(--rc-text-xs);
|
|
722
|
+
line-height: var(--rc-line-height);
|
|
723
|
+
max-width: 280px;
|
|
724
|
+
white-space: normal;
|
|
725
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
|
|
726
|
+
pointer-events: none;
|
|
727
|
+
animation: rc-tooltip-in 0.12s ease-out;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
.rc-tooltip--info {
|
|
731
|
+
border-left: 3px solid var(--rc-color-brand-gold);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
@keyframes rc-tooltip-in {
|
|
735
|
+
from { opacity: 0; }
|
|
736
|
+
to { opacity: 1; }
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/* ── InfoCard ── */
|
|
740
|
+
.rc-info-card {
|
|
741
|
+
display: flex;
|
|
742
|
+
align-items: flex-start;
|
|
743
|
+
gap: var(--rc-space-3);
|
|
744
|
+
padding: var(--rc-space-4);
|
|
745
|
+
border-radius: var(--rc-radius-lg);
|
|
746
|
+
background: var(--rc-color-surface-card);
|
|
747
|
+
border: 1px solid var(--rc-color-surface-border);
|
|
748
|
+
border-left: 3px solid var(--rc-color-brand-gold);
|
|
749
|
+
font-family: var(--rc-font-sans);
|
|
750
|
+
animation: rc-tooltip-in 0.2s ease-out;
|
|
751
|
+
margin-bottom: var(--rc-space-4, 16px);
|
|
752
|
+
overflow: visible;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
.rc-info-card__icon {
|
|
756
|
+
flex-shrink: 0;
|
|
757
|
+
color: var(--rc-color-brand-gold);
|
|
758
|
+
width: 20px;
|
|
759
|
+
height: 20px;
|
|
760
|
+
margin-top: 1px;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
.rc-info-card__content {
|
|
764
|
+
flex: 1;
|
|
765
|
+
min-width: 0;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
.rc-info-card__title {
|
|
769
|
+
font-size: var(--rc-text-sm);
|
|
770
|
+
font-weight: var(--rc-weight-semibold);
|
|
771
|
+
color: var(--rc-color-text-primary);
|
|
772
|
+
margin: 0 0 var(--rc-space-1) 0;
|
|
773
|
+
word-wrap: break-word;
|
|
774
|
+
overflow-wrap: break-word;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
.rc-info-card__body {
|
|
778
|
+
font-size: var(--rc-text-sm);
|
|
779
|
+
color: var(--rc-color-text-secondary);
|
|
780
|
+
line-height: var(--rc-line-height);
|
|
781
|
+
margin: 0;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
.rc-info-card__dismiss {
|
|
785
|
+
flex-shrink: 0;
|
|
786
|
+
background: none;
|
|
787
|
+
border: none;
|
|
788
|
+
color: var(--rc-color-text-muted);
|
|
789
|
+
cursor: pointer;
|
|
790
|
+
padding: 2px;
|
|
791
|
+
transition: color 0.15s ease;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
.rc-info-card__dismiss:hover {
|
|
795
|
+
color: var(--rc-color-text-primary);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
/* ── Skeleton ── */
|
|
799
|
+
.rc-skeleton {
|
|
800
|
+
background: var(--rc-color-surface-card);
|
|
801
|
+
border-radius: var(--rc-radius-md);
|
|
802
|
+
animation: rc-skeleton-pulse 1.5s ease-in-out infinite;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
.rc-skeleton--text {
|
|
806
|
+
height: 14px;
|
|
807
|
+
border-radius: var(--rc-radius-sm);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
.rc-skeleton--card {
|
|
811
|
+
border-radius: var(--rc-radius-lg);
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
.rc-skeleton--circle {
|
|
815
|
+
border-radius: var(--rc-radius-full);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
.rc-skeleton--stat {
|
|
819
|
+
border-radius: var(--rc-radius-lg);
|
|
820
|
+
height: 96px;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
.rc-skeleton--table {
|
|
824
|
+
border-radius: var(--rc-radius-md);
|
|
825
|
+
height: 36px;
|
|
826
|
+
width: 100%;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
@keyframes rc-skeleton-pulse {
|
|
830
|
+
0%, 100% { opacity: 0.4; }
|
|
831
|
+
50% { opacity: 0.8; }
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/* ── ConfirmModal ── */
|
|
835
|
+
.rc-modal-overlay {
|
|
836
|
+
position: fixed;
|
|
837
|
+
inset: 0;
|
|
838
|
+
z-index: 9998;
|
|
839
|
+
background: rgba(0, 0, 0, 0.6);
|
|
840
|
+
display: flex;
|
|
841
|
+
align-items: center;
|
|
842
|
+
justify-content: center;
|
|
843
|
+
animation: rc-tooltip-in 0.12s ease-out;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
.rc-modal {
|
|
847
|
+
background: var(--rc-color-surface-card);
|
|
848
|
+
border: 1px solid var(--rc-color-surface-border);
|
|
849
|
+
border-radius: var(--rc-radius-lg);
|
|
850
|
+
padding: var(--rc-space-6);
|
|
851
|
+
max-width: 440px;
|
|
852
|
+
width: 100%;
|
|
853
|
+
font-family: var(--rc-font-sans);
|
|
854
|
+
box-shadow: var(--rc-shadow-lg);
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
.rc-modal__title {
|
|
858
|
+
font-size: var(--rc-text-lg);
|
|
859
|
+
font-weight: var(--rc-weight-semibold);
|
|
860
|
+
color: var(--rc-color-text-primary);
|
|
861
|
+
margin: 0 0 var(--rc-space-2) 0;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
.rc-modal__description {
|
|
865
|
+
font-size: var(--rc-text-sm);
|
|
866
|
+
color: var(--rc-color-text-secondary);
|
|
867
|
+
line-height: var(--rc-line-height);
|
|
868
|
+
margin: 0 0 var(--rc-space-4) 0;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
.rc-modal__typing-label {
|
|
872
|
+
font-size: var(--rc-text-sm);
|
|
873
|
+
color: var(--rc-color-text-secondary);
|
|
874
|
+
margin: 0 0 var(--rc-space-2) 0;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
.rc-modal__typing-label strong {
|
|
878
|
+
color: var(--rc-color-text-primary);
|
|
879
|
+
font-weight: var(--rc-weight-semibold);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
.rc-modal__actions {
|
|
883
|
+
display: flex;
|
|
884
|
+
justify-content: flex-end;
|
|
885
|
+
gap: var(--rc-space-3);
|
|
886
|
+
margin-top: var(--rc-space-4);
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
/* ── ConceptTerm ── */
|
|
890
|
+
.rc-concept-term {
|
|
891
|
+
text-decoration: underline;
|
|
892
|
+
text-decoration-style: dotted;
|
|
893
|
+
text-underline-offset: 3px;
|
|
894
|
+
text-decoration-color: var(--rc-color-text-muted);
|
|
895
|
+
cursor: help;
|
|
896
|
+
display: inline-flex;
|
|
897
|
+
align-items: center;
|
|
898
|
+
gap: 4px;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
.rc-concept-term__icon {
|
|
902
|
+
width: 14px;
|
|
903
|
+
height: 14px;
|
|
904
|
+
color: var(--rc-color-text-muted);
|
|
905
|
+
}
|
|
621
906
|
/* ============================================================
|
|
622
907
|
RunContext UXD — Utility Classes
|
|
623
908
|
All values reference CSS custom properties from tokens.css.
|
package/dist/react/index.d.ts
CHANGED
|
@@ -79,4 +79,66 @@ interface CodeBlockProps extends HTMLAttributes<HTMLPreElement> {
|
|
|
79
79
|
}
|
|
80
80
|
declare function CodeBlock({ code, className, ...props }: CodeBlockProps): react_jsx_runtime.JSX.Element;
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
type ToastVariant = 'success' | 'error' | 'warning' | 'info';
|
|
83
|
+
interface ToastMessage {
|
|
84
|
+
id: string;
|
|
85
|
+
variant: ToastVariant;
|
|
86
|
+
message: string;
|
|
87
|
+
duration?: number;
|
|
88
|
+
}
|
|
89
|
+
interface ToastContextValue {
|
|
90
|
+
toast: (variant: ToastVariant, message: string, duration?: number) => void;
|
|
91
|
+
}
|
|
92
|
+
declare function useToast(): ToastContextValue;
|
|
93
|
+
interface ToastProviderProps {
|
|
94
|
+
children: ReactNode;
|
|
95
|
+
}
|
|
96
|
+
declare function ToastProvider({ children }: ToastProviderProps): react_jsx_runtime.JSX.Element;
|
|
97
|
+
|
|
98
|
+
type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right';
|
|
99
|
+
interface TooltipProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'content'> {
|
|
100
|
+
content: string;
|
|
101
|
+
placement?: TooltipPlacement;
|
|
102
|
+
variant?: 'default' | 'info';
|
|
103
|
+
children: ReactNode;
|
|
104
|
+
}
|
|
105
|
+
declare function Tooltip({ content, placement, variant, children, className, ...props }: TooltipProps): react_jsx_runtime.JSX.Element;
|
|
106
|
+
|
|
107
|
+
interface InfoCardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
108
|
+
title: string;
|
|
109
|
+
children: ReactNode;
|
|
110
|
+
storageKey: string;
|
|
111
|
+
icon?: ReactNode;
|
|
112
|
+
}
|
|
113
|
+
declare function resetInfoCards(): void;
|
|
114
|
+
declare function InfoCard({ title, children, storageKey, icon, className, ...props }: InfoCardProps): react_jsx_runtime.JSX.Element | null;
|
|
115
|
+
|
|
116
|
+
type SkeletonVariant = 'text' | 'card' | 'circle' | 'stat' | 'table';
|
|
117
|
+
interface SkeletonProps extends HTMLAttributes<HTMLDivElement> {
|
|
118
|
+
variant?: SkeletonVariant;
|
|
119
|
+
width?: string | number;
|
|
120
|
+
height?: string | number;
|
|
121
|
+
}
|
|
122
|
+
declare function Skeleton({ variant, width, height, className, style: styleProp, ...props }: SkeletonProps): react_jsx_runtime.JSX.Element;
|
|
123
|
+
|
|
124
|
+
interface ConfirmModalProps {
|
|
125
|
+
open: boolean;
|
|
126
|
+
title: string;
|
|
127
|
+
description: string | ReactNode;
|
|
128
|
+
confirmLabel?: string;
|
|
129
|
+
cancelLabel?: string;
|
|
130
|
+
variant?: 'default' | 'danger';
|
|
131
|
+
requireTyping?: string;
|
|
132
|
+
onConfirm: () => void;
|
|
133
|
+
onCancel: () => void;
|
|
134
|
+
}
|
|
135
|
+
declare function ConfirmModal({ open, title, description, confirmLabel, cancelLabel, variant, requireTyping, onConfirm, onCancel, }: ConfirmModalProps): react_jsx_runtime.JSX.Element | null;
|
|
136
|
+
|
|
137
|
+
interface ConceptTermProps extends HTMLAttributes<HTMLSpanElement> {
|
|
138
|
+
term: string;
|
|
139
|
+
definition: string;
|
|
140
|
+
children?: ReactNode;
|
|
141
|
+
}
|
|
142
|
+
declare function ConceptTerm({ term, definition, children, className, ...props }: ConceptTermProps): react_jsx_runtime.JSX.Element;
|
|
143
|
+
|
|
144
|
+
export { type ActivityEvent, ActivityFeed, type ActivityFeedProps, Badge, type BadgeProps, type BadgeVariant, Button, type ButtonProps, Card, type CardProps, CodeBlock, type CodeBlockProps, ConceptTerm, type ConceptTermProps, ConfirmModal, type ConfirmModalProps, EmptyState, type EmptyStateProps, ErrorCard, type ErrorCardProps, InfoCard, type InfoCardProps, Input, type InputProps, Select, type SelectProps, Skeleton, type SkeletonProps, type SkeletonVariant, Spinner, StatCard, type StatCardProps, Textarea, type TextareaProps, TierBadge, type TierBadgeProps, type ToastContextValue, type ToastMessage, ToastProvider, type ToastProviderProps, type ToastVariant, Tooltip, type TooltipPlacement, type TooltipProps, resetInfoCards, useToast };
|
package/dist/react/index.js
CHANGED
|
@@ -56,9 +56,9 @@ function Badge({ variant, className, children, ...props }) {
|
|
|
56
56
|
// src/react/TierBadge.tsx
|
|
57
57
|
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
58
58
|
var tierLabels = {
|
|
59
|
-
gold: "
|
|
60
|
-
silver: "
|
|
61
|
-
bronze: "
|
|
59
|
+
gold: "Gold",
|
|
60
|
+
silver: "Silver",
|
|
61
|
+
bronze: "Bronze"
|
|
62
62
|
};
|
|
63
63
|
function TierBadge({ tier, ...props }) {
|
|
64
64
|
return /* @__PURE__ */ jsx5(Badge, { variant: tier, ...props, children: tierLabels[tier] });
|
|
@@ -150,18 +150,324 @@ function CodeBlock({ code, className, ...props }) {
|
|
|
150
150
|
const classes = ["rc-code", className].filter(Boolean).join(" ");
|
|
151
151
|
return /* @__PURE__ */ jsx11("pre", { className: classes, ...props, children: /* @__PURE__ */ jsx11("code", { children: code }) });
|
|
152
152
|
}
|
|
153
|
+
|
|
154
|
+
// src/react/Toast.tsx
|
|
155
|
+
import { createContext, useContext, useCallback, useState, useEffect, useRef } from "react";
|
|
156
|
+
import { jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
157
|
+
var ToastContext = createContext(null);
|
|
158
|
+
function useToast() {
|
|
159
|
+
const ctx = useContext(ToastContext);
|
|
160
|
+
if (!ctx) throw new Error("useToast must be used within a ToastProvider");
|
|
161
|
+
return ctx;
|
|
162
|
+
}
|
|
163
|
+
var ICONS = {
|
|
164
|
+
success: "\u2713",
|
|
165
|
+
error: "\u2715",
|
|
166
|
+
warning: "\u26A0",
|
|
167
|
+
info: "\u2139"
|
|
168
|
+
};
|
|
169
|
+
var DEFAULT_DURATION = {
|
|
170
|
+
success: 4e3,
|
|
171
|
+
error: 0,
|
|
172
|
+
// persist until dismissed
|
|
173
|
+
warning: 4e3,
|
|
174
|
+
info: 4e3
|
|
175
|
+
};
|
|
176
|
+
function ToastItem({ toast: t, onDismiss }) {
|
|
177
|
+
const [exiting, setExiting] = useState(false);
|
|
178
|
+
const timerRef = useRef(null);
|
|
179
|
+
const dismiss = useCallback(() => {
|
|
180
|
+
setExiting(true);
|
|
181
|
+
setTimeout(() => onDismiss(t.id), 150);
|
|
182
|
+
}, [t.id, onDismiss]);
|
|
183
|
+
useEffect(() => {
|
|
184
|
+
const dur = t.duration ?? DEFAULT_DURATION[t.variant];
|
|
185
|
+
if (dur > 0) {
|
|
186
|
+
timerRef.current = setTimeout(dismiss, dur);
|
|
187
|
+
}
|
|
188
|
+
return () => {
|
|
189
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
190
|
+
};
|
|
191
|
+
}, [t, dismiss]);
|
|
192
|
+
return /* @__PURE__ */ jsxs5("div", { className: `rc-toast rc-toast--${t.variant}${exiting ? " rc-toast--exiting" : ""}`, role: "alert", children: [
|
|
193
|
+
/* @__PURE__ */ jsx12("span", { className: "rc-toast__icon", "aria-hidden": "true", children: ICONS[t.variant] }),
|
|
194
|
+
/* @__PURE__ */ jsx12("span", { className: "rc-toast__content", children: t.message }),
|
|
195
|
+
/* @__PURE__ */ jsx12("button", { className: "rc-toast__dismiss", onClick: dismiss, "aria-label": "Dismiss notification", children: "\u2715" })
|
|
196
|
+
] });
|
|
197
|
+
}
|
|
198
|
+
var toastCounter = 0;
|
|
199
|
+
function ToastProvider({ children }) {
|
|
200
|
+
const [toasts, setToasts] = useState([]);
|
|
201
|
+
const toast = useCallback((variant, message, duration) => {
|
|
202
|
+
const id = `toast-${++toastCounter}`;
|
|
203
|
+
setToasts((prev) => [...prev, { id, variant, message, duration }]);
|
|
204
|
+
}, []);
|
|
205
|
+
const dismiss = useCallback((id) => {
|
|
206
|
+
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
207
|
+
}, []);
|
|
208
|
+
return /* @__PURE__ */ jsxs5(ToastContext.Provider, { value: { toast }, children: [
|
|
209
|
+
children,
|
|
210
|
+
/* @__PURE__ */ jsx12("div", { className: "rc-toast-container", "aria-live": "polite", children: toasts.map((t) => /* @__PURE__ */ jsx12(ToastItem, { toast: t, onDismiss: dismiss }, t.id)) })
|
|
211
|
+
] });
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// src/react/Tooltip.tsx
|
|
215
|
+
import { useState as useState2, useRef as useRef2, useCallback as useCallback2, useEffect as useEffect2 } from "react";
|
|
216
|
+
import { jsx as jsx13, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
217
|
+
var GAP = 8;
|
|
218
|
+
function Tooltip({ content, placement = "top", variant = "default", children, className, ...props }) {
|
|
219
|
+
const [visible, setVisible] = useState2(false);
|
|
220
|
+
const [coords, setCoords] = useState2(null);
|
|
221
|
+
const [actualPlacement, setActualPlacement] = useState2(placement);
|
|
222
|
+
const timerRef = useRef2(null);
|
|
223
|
+
const wrapperRef = useRef2(null);
|
|
224
|
+
const tooltipRef = useRef2(null);
|
|
225
|
+
const show = useCallback2(() => {
|
|
226
|
+
timerRef.current = setTimeout(() => setVisible(true), 200);
|
|
227
|
+
}, []);
|
|
228
|
+
const hide = useCallback2(() => {
|
|
229
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
230
|
+
setVisible(false);
|
|
231
|
+
setCoords(null);
|
|
232
|
+
}, []);
|
|
233
|
+
useEffect2(() => {
|
|
234
|
+
if (!visible || !wrapperRef.current || !tooltipRef.current) return;
|
|
235
|
+
const anchor = wrapperRef.current.getBoundingClientRect();
|
|
236
|
+
const tip = tooltipRef.current.getBoundingClientRect();
|
|
237
|
+
let top = 0;
|
|
238
|
+
let left = 0;
|
|
239
|
+
let place = placement;
|
|
240
|
+
if (place === "top") {
|
|
241
|
+
top = anchor.top - tip.height - GAP;
|
|
242
|
+
left = anchor.left + anchor.width / 2 - tip.width / 2;
|
|
243
|
+
if (top < 4) {
|
|
244
|
+
place = "bottom";
|
|
245
|
+
top = anchor.bottom + GAP;
|
|
246
|
+
}
|
|
247
|
+
} else if (place === "bottom") {
|
|
248
|
+
top = anchor.bottom + GAP;
|
|
249
|
+
left = anchor.left + anchor.width / 2 - tip.width / 2;
|
|
250
|
+
if (top + tip.height > window.innerHeight - 4) {
|
|
251
|
+
place = "top";
|
|
252
|
+
top = anchor.top - tip.height - GAP;
|
|
253
|
+
}
|
|
254
|
+
} else if (place === "left") {
|
|
255
|
+
top = anchor.top + anchor.height / 2 - tip.height / 2;
|
|
256
|
+
left = anchor.left - tip.width - GAP;
|
|
257
|
+
} else {
|
|
258
|
+
top = anchor.top + anchor.height / 2 - tip.height / 2;
|
|
259
|
+
left = anchor.right + GAP;
|
|
260
|
+
}
|
|
261
|
+
left = Math.max(4, Math.min(left, window.innerWidth - tip.width - 4));
|
|
262
|
+
top = Math.max(4, Math.min(top, window.innerHeight - tip.height - 4));
|
|
263
|
+
setCoords({ top, left });
|
|
264
|
+
setActualPlacement(place);
|
|
265
|
+
}, [visible, placement]);
|
|
266
|
+
const wrapperClasses = ["rc-tooltip-wrapper", className].filter(Boolean).join(" ");
|
|
267
|
+
const tooltipClasses = [
|
|
268
|
+
"rc-tooltip",
|
|
269
|
+
variant === "info" && "rc-tooltip--info"
|
|
270
|
+
].filter(Boolean).join(" ");
|
|
271
|
+
return /* @__PURE__ */ jsxs6("span", { ref: wrapperRef, className: wrapperClasses, onMouseEnter: show, onMouseLeave: hide, onFocus: show, onBlur: hide, ...props, children: [
|
|
272
|
+
children,
|
|
273
|
+
visible && /* @__PURE__ */ jsx13(
|
|
274
|
+
"span",
|
|
275
|
+
{
|
|
276
|
+
ref: tooltipRef,
|
|
277
|
+
className: tooltipClasses,
|
|
278
|
+
role: "tooltip",
|
|
279
|
+
style: coords ? { top: `${coords.top}px`, left: `${coords.left}px` } : { visibility: "hidden" },
|
|
280
|
+
children: content
|
|
281
|
+
}
|
|
282
|
+
)
|
|
283
|
+
] });
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// src/react/InfoCard.tsx
|
|
287
|
+
import { useState as useState3, useEffect as useEffect3 } from "react";
|
|
288
|
+
import { jsx as jsx14, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
289
|
+
function isDismissed(key) {
|
|
290
|
+
if (typeof window === "undefined") return false;
|
|
291
|
+
try {
|
|
292
|
+
return localStorage.getItem(`rc-info-dismissed:${key}`) === "1";
|
|
293
|
+
} catch {
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
function setDismissed(key) {
|
|
298
|
+
try {
|
|
299
|
+
localStorage.setItem(`rc-info-dismissed:${key}`, "1");
|
|
300
|
+
} catch {
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
function resetInfoCards() {
|
|
304
|
+
if (typeof window === "undefined") return;
|
|
305
|
+
try {
|
|
306
|
+
const keys = Object.keys(localStorage).filter((k) => k.startsWith("rc-info-dismissed:"));
|
|
307
|
+
keys.forEach((k) => localStorage.removeItem(k));
|
|
308
|
+
} catch {
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
function InfoCard({ title, children, storageKey, icon, className, ...props }) {
|
|
312
|
+
const [hidden, setHidden] = useState3(true);
|
|
313
|
+
useEffect3(() => {
|
|
314
|
+
setHidden(isDismissed(storageKey));
|
|
315
|
+
}, [storageKey]);
|
|
316
|
+
if (hidden) return null;
|
|
317
|
+
const dismiss = () => {
|
|
318
|
+
setDismissed(storageKey);
|
|
319
|
+
setHidden(true);
|
|
320
|
+
};
|
|
321
|
+
const classes = ["rc-info-card", className].filter(Boolean).join(" ");
|
|
322
|
+
return /* @__PURE__ */ jsxs7("div", { className: classes, role: "note", ...props, children: [
|
|
323
|
+
icon && /* @__PURE__ */ jsx14("span", { className: "rc-info-card__icon", "aria-hidden": "true", children: icon }),
|
|
324
|
+
/* @__PURE__ */ jsxs7("div", { className: "rc-info-card__content", children: [
|
|
325
|
+
/* @__PURE__ */ jsx14("p", { className: "rc-info-card__title", children: title }),
|
|
326
|
+
/* @__PURE__ */ jsx14("p", { className: "rc-info-card__body", children })
|
|
327
|
+
] }),
|
|
328
|
+
/* @__PURE__ */ jsx14("button", { className: "rc-info-card__dismiss", onClick: dismiss, "aria-label": "Dismiss tip", children: "\u2715" })
|
|
329
|
+
] });
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// src/react/Skeleton.tsx
|
|
333
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
334
|
+
function Skeleton({ variant = "text", width, height, className, style: styleProp, ...props }) {
|
|
335
|
+
const style = { ...styleProp };
|
|
336
|
+
if (width) style.width = typeof width === "number" ? `${width}px` : width;
|
|
337
|
+
if (height) style.height = typeof height === "number" ? `${height}px` : height;
|
|
338
|
+
const classes = [
|
|
339
|
+
"rc-skeleton",
|
|
340
|
+
`rc-skeleton--${variant}`,
|
|
341
|
+
className
|
|
342
|
+
].filter(Boolean).join(" ");
|
|
343
|
+
return /* @__PURE__ */ jsx15(
|
|
344
|
+
"div",
|
|
345
|
+
{
|
|
346
|
+
className: classes,
|
|
347
|
+
style,
|
|
348
|
+
"aria-hidden": "true",
|
|
349
|
+
...props
|
|
350
|
+
}
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// src/react/ConfirmModal.tsx
|
|
355
|
+
import { useState as useState4, useEffect as useEffect4, useRef as useRef3, useCallback as useCallback3, useId } from "react";
|
|
356
|
+
import { jsx as jsx16, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
357
|
+
function ConfirmModal({
|
|
358
|
+
open,
|
|
359
|
+
title,
|
|
360
|
+
description,
|
|
361
|
+
confirmLabel = "Confirm",
|
|
362
|
+
cancelLabel = "Cancel",
|
|
363
|
+
variant = "default",
|
|
364
|
+
requireTyping,
|
|
365
|
+
onConfirm,
|
|
366
|
+
onCancel
|
|
367
|
+
}) {
|
|
368
|
+
const [typed, setTyped] = useState4("");
|
|
369
|
+
const overlayRef = useRef3(null);
|
|
370
|
+
const cancelRef = useRef3(null);
|
|
371
|
+
const titleId = useId();
|
|
372
|
+
const canConfirm = requireTyping ? typed === requireTyping : true;
|
|
373
|
+
useEffect4(() => {
|
|
374
|
+
if (!open) {
|
|
375
|
+
setTyped("");
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
document.body.style.overflow = "hidden";
|
|
379
|
+
const handleKey = (e) => {
|
|
380
|
+
if (e.key === "Escape") onCancel();
|
|
381
|
+
if (e.key === "Tab") {
|
|
382
|
+
const modal = overlayRef.current?.querySelector(".rc-modal");
|
|
383
|
+
if (!modal) return;
|
|
384
|
+
const focusable = modal.querySelectorAll(
|
|
385
|
+
'button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
|
386
|
+
);
|
|
387
|
+
if (focusable.length === 0) return;
|
|
388
|
+
const first = focusable[0];
|
|
389
|
+
const last = focusable[focusable.length - 1];
|
|
390
|
+
if (e.shiftKey && document.activeElement === first) {
|
|
391
|
+
e.preventDefault();
|
|
392
|
+
last.focus();
|
|
393
|
+
} else if (!e.shiftKey && document.activeElement === last) {
|
|
394
|
+
e.preventDefault();
|
|
395
|
+
first.focus();
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
document.addEventListener("keydown", handleKey);
|
|
400
|
+
if (!requireTyping) cancelRef.current?.focus();
|
|
401
|
+
return () => {
|
|
402
|
+
document.removeEventListener("keydown", handleKey);
|
|
403
|
+
document.body.style.overflow = "";
|
|
404
|
+
};
|
|
405
|
+
}, [open, onCancel, requireTyping]);
|
|
406
|
+
const handleOverlayClick = useCallback3((e) => {
|
|
407
|
+
if (e.target === overlayRef.current) onCancel();
|
|
408
|
+
}, [onCancel]);
|
|
409
|
+
if (!open) return null;
|
|
410
|
+
const btnVariant = variant === "danger" ? "rc-btn rc-btn--danger" : "rc-btn rc-btn--primary";
|
|
411
|
+
return /* @__PURE__ */ jsx16("div", { className: "rc-modal-overlay", ref: overlayRef, onClick: handleOverlayClick, children: /* @__PURE__ */ jsxs8("div", { className: "rc-modal", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, children: [
|
|
412
|
+
/* @__PURE__ */ jsx16("h2", { className: "rc-modal__title", id: titleId, children: title }),
|
|
413
|
+
/* @__PURE__ */ jsx16("div", { className: "rc-modal__description", children: description }),
|
|
414
|
+
requireTyping && /* @__PURE__ */ jsxs8("div", { children: [
|
|
415
|
+
/* @__PURE__ */ jsxs8("p", { className: "rc-modal__typing-label", children: [
|
|
416
|
+
"Type ",
|
|
417
|
+
/* @__PURE__ */ jsx16("strong", { children: requireTyping }),
|
|
418
|
+
" to confirm:"
|
|
419
|
+
] }),
|
|
420
|
+
/* @__PURE__ */ jsx16(
|
|
421
|
+
"input",
|
|
422
|
+
{
|
|
423
|
+
className: "rc-input",
|
|
424
|
+
value: typed,
|
|
425
|
+
onChange: (e) => setTyped(e.target.value),
|
|
426
|
+
autoFocus: true,
|
|
427
|
+
spellCheck: false
|
|
428
|
+
}
|
|
429
|
+
)
|
|
430
|
+
] }),
|
|
431
|
+
/* @__PURE__ */ jsxs8("div", { className: "rc-modal__actions", children: [
|
|
432
|
+
/* @__PURE__ */ jsx16("button", { className: "rc-btn rc-btn--secondary", onClick: onCancel, ref: cancelRef, children: cancelLabel }),
|
|
433
|
+
/* @__PURE__ */ jsx16("button", { className: btnVariant, onClick: onConfirm, disabled: !canConfirm, children: confirmLabel })
|
|
434
|
+
] })
|
|
435
|
+
] }) });
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// src/react/ConceptTerm.tsx
|
|
439
|
+
import { jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
440
|
+
function ConceptTerm({ term, definition, children, className, ...props }) {
|
|
441
|
+
const classes = ["rc-concept-term", className].filter(Boolean).join(" ");
|
|
442
|
+
return /* @__PURE__ */ jsx17(Tooltip, { content: definition, variant: "info", placement: "top", children: /* @__PURE__ */ jsxs9("span", { className: classes, ...props, children: [
|
|
443
|
+
children ?? term,
|
|
444
|
+
/* @__PURE__ */ jsxs9("svg", { className: "rc-concept-term__icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
445
|
+
/* @__PURE__ */ jsx17("circle", { cx: "12", cy: "12", r: "10" }),
|
|
446
|
+
/* @__PURE__ */ jsx17("path", { d: "M12 16v-4" }),
|
|
447
|
+
/* @__PURE__ */ jsx17("path", { d: "M12 8h.01" })
|
|
448
|
+
] })
|
|
449
|
+
] }) });
|
|
450
|
+
}
|
|
153
451
|
export {
|
|
154
452
|
ActivityFeed,
|
|
155
453
|
Badge,
|
|
156
454
|
Button,
|
|
157
455
|
Card,
|
|
158
456
|
CodeBlock,
|
|
457
|
+
ConceptTerm,
|
|
458
|
+
ConfirmModal,
|
|
159
459
|
EmptyState,
|
|
160
460
|
ErrorCard,
|
|
461
|
+
InfoCard,
|
|
161
462
|
Input,
|
|
162
463
|
Select,
|
|
464
|
+
Skeleton,
|
|
163
465
|
Spinner,
|
|
164
466
|
StatCard,
|
|
165
467
|
Textarea,
|
|
166
|
-
TierBadge
|
|
468
|
+
TierBadge,
|
|
469
|
+
ToastProvider,
|
|
470
|
+
Tooltip,
|
|
471
|
+
resetInfoCards,
|
|
472
|
+
useToast
|
|
167
473
|
};
|