@astralui/core 0.1.1 → 0.1.3

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/README.md CHANGED
@@ -13,6 +13,10 @@ dependency-free UI components. No CSS-in-JS runtime: just CSS variables +
13
13
  - 🌗 **Color scheme** - `ColorSchemeProvider` / `useColorScheme` (persisted, `data-astral-scheme` on `<html>`).
14
14
  - 🖌️ **Per-brand theming** - `AstralThemeProvider` generates a brand palette from a single hex and injects per-scheme overrides at runtime (with live preview).
15
15
  - 🧩 **Components** - `AstralModal`, `AstralDrawer`, `AstralSelect`, `AstralMenu`, `AstralPinInput`, `AstralToaster` + `notifications`, `DateInput`, `Spinner`.
16
+ - 🌌 **Backgrounds** - `SpaceBackground` (animated starfield) and `GridBackground` (masked technical grid) for auth screens, heroes, and splash pages.
17
+ - 📊 **Charts** - dependency-free, prop-driven `Sparkline`, `Delta`, `Donut`, `FunnelBars`, `StackedBars` (pure SVG + CSS, theme-aware).
18
+ - 🔐 **Auth surface** - `AuthShell` / `AuthCard` + `au-auth-*` classes for sign-in / sign-up / verify / onboarding screens.
19
+ - ❓ **ConfirmModal** - a confirmation/alert dialog (with a `danger` variant) built on `AstralModal`.
16
20
 
17
21
  ## Install
18
22
 
@@ -89,6 +93,12 @@ notifications.update({ id: 'x', message: 'Done', color: 'green', loading: false,
89
93
  | `DateInput` | Freeze-proof native date / datetime-local input. |
90
94
  | `Spinner` | Theme-aware loading spinner. |
91
95
  | `notifications` + `AstralToaster` | Toast system. |
96
+ | `SpaceBackground` | Animated cosmic backdrop with a twinkling starfield (wrap your content as children). |
97
+ | `GridBackground` | Subtle technical grid with a brand-tinted glow, edge-masked. |
98
+ | `Sparkline` / `Delta` | Inline KPI sparkline; period-over-period delta badge. |
99
+ | `Donut` / `FunnelBars` / `StackedBars` | Interactive, linked-hover charts (pure SVG + CSS). |
100
+ | `AuthShell` / `AuthCard` | Centered auth/onboarding layout on a cosmic or wash backdrop. |
101
+ | `ConfirmModal` | Confirmation dialog (supports a `danger` variant). |
92
102
  | `generateColors(hex)` | 10-shade palette from one color. |
93
103
 
94
104
  ## Token reference
package/dist/index.cjs CHANGED
@@ -599,6 +599,222 @@ function Spinner2({ size = 24, className, style }) {
599
599
  }
600
600
  );
601
601
  }
602
+ function cls(base, extra) {
603
+ return extra ? `${base} ${extra}` : base;
604
+ }
605
+ function SpaceBackground({ children, className, style, fixed }) {
606
+ const s = fixed ? { position: "fixed", inset: 0, ...style } : style ?? {};
607
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cls("astral-space-bg", className), style: s, children: [
608
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "astral-stars", "aria-hidden": "true" }),
609
+ children
610
+ ] });
611
+ }
612
+ function GridBackground({ children, className, style, fixed }) {
613
+ const s = fixed ? { position: "fixed", inset: 0, ...style } : style ?? {};
614
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cls("astral-grid-bg", className), style: s, children });
615
+ }
616
+ function chartColor(c) {
617
+ if (c.startsWith("var(") || c.startsWith("#") || c.startsWith("rgb") || c.startsWith("hsl")) return c;
618
+ return `var(--astral-color-${c.replace(".", "-")})`;
619
+ }
620
+ function Sparkline({ points, color }) {
621
+ if (!points || points.length < 2 || points.every((p) => p === 0)) return null;
622
+ const w = 100, h = 28;
623
+ const max = Math.max(...points), min = Math.min(...points);
624
+ const range = max - min || 1;
625
+ const step = w / (points.length - 1);
626
+ const d = points.map((p, i) => `${i === 0 ? "M" : "L"}${(i * step).toFixed(1)},${(h - (p - min) / range * h).toFixed(1)}`).join(" ");
627
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "au-spark", viewBox: `0 0 ${w} ${h}`, preserveAspectRatio: "none", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d, fill: "none", stroke: chartColor(color), strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" }) });
628
+ }
629
+ function Delta({ current, previous }) {
630
+ if (previous == null || previous === 0) return null;
631
+ const pct = Math.round((current - previous) / previous * 100);
632
+ if (pct === 0) return null;
633
+ const up = pct > 0;
634
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `au-delta ${up ? "up" : "down"}`, children: [
635
+ up ? "\u25B2" : "\u25BC",
636
+ " ",
637
+ Math.abs(pct),
638
+ "%"
639
+ ] });
640
+ }
641
+ function Donut({ data, centerValue, centerLabel }) {
642
+ const [hovered, setHovered] = react.useState(null);
643
+ const total = data.reduce((s, d) => s + d.value, 0) || 1;
644
+ const c = 75, r = 58, sw = 18, circ = 2 * Math.PI * r;
645
+ let offset = 0;
646
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "au-donut", children: [
647
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "au-donut-ring", children: [
648
+ /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: 150, height: 150, viewBox: "0 0 150 150", children: [
649
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: c, cy: c, r, fill: "none", stroke: "var(--au-surface-2)", strokeWidth: sw }),
650
+ data.map((d, i) => {
651
+ const dash = d.value / total * circ;
652
+ const el = /* @__PURE__ */ jsxRuntime.jsx(
653
+ "circle",
654
+ {
655
+ cx: c,
656
+ cy: c,
657
+ r,
658
+ fill: "none",
659
+ stroke: chartColor(d.color),
660
+ strokeWidth: hovered === i ? sw + 4 : sw,
661
+ strokeDasharray: `${dash} ${circ - dash}`,
662
+ strokeDashoffset: -offset,
663
+ transform: `rotate(-90 ${c} ${c})`,
664
+ style: {
665
+ opacity: hovered == null || hovered === i ? 1 : 0.28,
666
+ transition: "opacity .15s, stroke-width .15s",
667
+ cursor: "pointer"
668
+ },
669
+ onMouseEnter: () => setHovered(i),
670
+ onMouseLeave: () => setHovered(null)
671
+ },
672
+ i
673
+ );
674
+ offset += dash;
675
+ return el;
676
+ })
677
+ ] }),
678
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "au-donut-ctr", children: [
679
+ /* @__PURE__ */ jsxRuntime.jsx("b", { children: centerValue }),
680
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: centerLabel })
681
+ ] })
682
+ ] }),
683
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "au-donut-legend", children: data.map((d, i) => /* @__PURE__ */ jsxRuntime.jsxs(
684
+ "div",
685
+ {
686
+ className: `au-dl ${hovered === i ? "on" : hovered != null ? "off" : ""}`,
687
+ onMouseEnter: () => setHovered(i),
688
+ onMouseLeave: () => setHovered(null),
689
+ children: [
690
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "dot", style: { background: chartColor(d.color) } }),
691
+ /* @__PURE__ */ jsxRuntime.jsx("b", { children: d.value.toLocaleString() }),
692
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "dll", children: d.name })
693
+ ]
694
+ },
695
+ i
696
+ )) })
697
+ ] });
698
+ }
699
+ function FunnelBars({ data }) {
700
+ const [hov, setHov] = react.useState(null);
701
+ const max = Math.max(...data.map((d) => d.value), 1);
702
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "au-funnel", children: data.map((d, i) => /* @__PURE__ */ jsxRuntime.jsxs(
703
+ "div",
704
+ {
705
+ className: `au-fbar ${hov === i ? "on" : hov != null ? "off" : ""}`,
706
+ onMouseEnter: () => setHov(i),
707
+ onMouseLeave: () => setHov(null),
708
+ children: [
709
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "au-fbar-label", children: d.stage }),
710
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "au-fbar-track", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "au-fbar-fill", style: { width: `${d.value / max * 100}%`, background: chartColor(d.color) } }) }),
711
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "au-fbar-val", children: d.value.toLocaleString() })
712
+ ]
713
+ },
714
+ d.stage
715
+ )) });
716
+ }
717
+ function StackedBars({ rows, series }) {
718
+ const [hovSeries, setHovSeries] = react.useState(null);
719
+ const [hovSeg, setHovSeg] = react.useState(null);
720
+ const max = Math.max(...rows.map((r) => r.values.reduce((a, b) => a + b, 0)), 1);
721
+ const anyHover = hovSeries != null || hovSeg != null;
722
+ const active = (row, s) => hovSeries != null ? s === hovSeries : hovSeg != null ? hovSeg.row === row && hovSeg.s === s : true;
723
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "au-usage", children: [
724
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "au-usage-legend", children: series.map((s, si) => /* @__PURE__ */ jsxRuntime.jsxs(
725
+ "div",
726
+ {
727
+ className: `au-ulg ${hovSeries === si ? "on" : hovSeries != null ? "off" : ""}`,
728
+ onMouseEnter: () => setHovSeries(si),
729
+ onMouseLeave: () => setHovSeries(null),
730
+ children: [
731
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "dot", style: { background: chartColor(s.color) } }),
732
+ s.label
733
+ ]
734
+ },
735
+ si
736
+ )) }),
737
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "au-usage-rows", children: rows.map((r, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `au-urow ${hovSeg?.row === i ? "on" : ""}`, children: [
738
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "au-urow-label", title: r.label, children: r.label }),
739
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "au-utrack", children: r.values.map((v, si) => /* @__PURE__ */ jsxRuntime.jsx(
740
+ "div",
741
+ {
742
+ className: "au-useg",
743
+ onMouseEnter: () => setHovSeg({ row: i, s: si }),
744
+ onMouseLeave: () => setHovSeg(null),
745
+ style: {
746
+ width: `${v / max * 100}%`,
747
+ background: chartColor(series[si].color),
748
+ opacity: anyHover && !active(i, si) ? 0.2 : 1,
749
+ filter: anyHover && active(i, si) ? "brightness(1.2)" : void 0
750
+ }
751
+ },
752
+ si
753
+ )) }),
754
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "au-unums", children: r.values.map((v, si) => /* @__PURE__ */ jsxRuntime.jsx(
755
+ "b",
756
+ {
757
+ style: {
758
+ color: chartColor(series[si].color),
759
+ opacity: anyHover && !active(i, si) ? 0.28 : 1,
760
+ textShadow: anyHover && active(i, si) ? `0 0 10px ${chartColor(series[si].color)}` : void 0
761
+ },
762
+ children: v.toLocaleString()
763
+ },
764
+ si
765
+ )) })
766
+ ] }, i)) })
767
+ ] });
768
+ }
769
+ function AuthShell({ children, backdrop = "space", topLeft, topRight, className, style }) {
770
+ const cls2 = `au-auth${backdrop === "space" ? " astral-space-bg" : ""}${className ? " " + className : ""}`;
771
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cls2, style, children: [
772
+ backdrop === "space" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "astral-stars", "aria-hidden": "true" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "au-auth-bg", "aria-hidden": "true" }),
773
+ topLeft,
774
+ topRight ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "au-auth-lang", children: topRight }) : null,
775
+ children
776
+ ] });
777
+ }
778
+ function AuthCard({ children, banner, className, style }) {
779
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `au-auth-card${className ? " " + className : ""}`, style, children: [
780
+ banner ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "au-auth-banner", children: banner }) : null,
781
+ children
782
+ ] });
783
+ }
784
+ function ConfirmModal({
785
+ opened,
786
+ onClose,
787
+ onConfirm,
788
+ title,
789
+ message,
790
+ confirmLabel = "Confirm",
791
+ cancelLabel = "Cancel",
792
+ danger,
793
+ loading,
794
+ width = 420,
795
+ children
796
+ }) {
797
+ return /* @__PURE__ */ jsxRuntime.jsx(AstralModal, { opened, onClose, title, width, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "clay-form", children: [
798
+ message != null && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 13, lineHeight: 1.55, color: "var(--au-dim)" }, children: message }),
799
+ children,
800
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "clay-actions", children: [
801
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "au-btn", onClick: onClose, disabled: loading, children: cancelLabel }),
802
+ /* @__PURE__ */ jsxRuntime.jsxs(
803
+ "button",
804
+ {
805
+ type: "button",
806
+ className: `au-btn ${danger ? "solidred" : "primary"}`,
807
+ onClick: onConfirm,
808
+ disabled: loading,
809
+ children: [
810
+ loading ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "au-spinner", style: { width: 14, height: 14 } }) : null,
811
+ confirmLabel
812
+ ]
813
+ }
814
+ )
815
+ ] })
816
+ ] }) });
817
+ }
602
818
 
603
819
  exports.AstralDrawer = AstralDrawer;
604
820
  exports.AstralMenu = AstralMenu;
@@ -607,10 +823,21 @@ exports.AstralPinInput = AstralPinInput;
607
823
  exports.AstralSelect = AstralSelect;
608
824
  exports.AstralThemeProvider = AstralThemeProvider;
609
825
  exports.AstralToaster = AstralToaster;
826
+ exports.AuthCard = AuthCard;
827
+ exports.AuthShell = AuthShell;
610
828
  exports.ColorSchemeProvider = ColorSchemeProvider;
829
+ exports.ConfirmModal = ConfirmModal;
611
830
  exports.DateInput = DateInput;
831
+ exports.Delta = Delta;
832
+ exports.Donut = Donut;
833
+ exports.FunnelBars = FunnelBars;
834
+ exports.GridBackground = GridBackground;
835
+ exports.SpaceBackground = SpaceBackground;
836
+ exports.Sparkline = Sparkline;
612
837
  exports.Spinner = Spinner2;
838
+ exports.StackedBars = StackedBars;
613
839
  exports.buildOrgCss = buildOrgCss;
840
+ exports.chartColor = chartColor;
614
841
  exports.dateTimeToInputStr = dateTimeToInputStr;
615
842
  exports.dateToInputStr = dateToInputStr;
616
843
  exports.generateColors = generateColors;