@pattern-stack/frontend-patterns 0.2.0-alpha.16 → 0.2.0-alpha.18

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.js CHANGED
@@ -7793,6 +7793,201 @@ const TruncatedText = ({
7793
7793
  }
7794
7794
  );
7795
7795
  };
7796
+ function formatAuto(ms) {
7797
+ if (ms < 1e3) {
7798
+ return { visible: `${ms}ms`, spoken: `${ms} milliseconds` };
7799
+ }
7800
+ if (ms < 6e4) {
7801
+ const secs = ms / 1e3;
7802
+ return {
7803
+ visible: `${secs.toFixed(1)}s`,
7804
+ spoken: `${secs.toFixed(1)} seconds`
7805
+ };
7806
+ }
7807
+ if (ms < 36e5) {
7808
+ const m = Math.floor(ms / 6e4);
7809
+ const s = Math.round(ms % 6e4 / 1e3);
7810
+ return { visible: `${m}m ${s}s`, spoken: `${m} minutes ${s} seconds` };
7811
+ }
7812
+ if (ms < 864e5) {
7813
+ const h2 = Math.floor(ms / 36e5);
7814
+ const m = Math.round(ms % 36e5 / 6e4);
7815
+ return { visible: `${h2}h ${m}m`, spoken: `${h2} hours ${m} minutes` };
7816
+ }
7817
+ const d = Math.floor(ms / 864e5);
7818
+ const h = Math.round(ms % 864e5 / 36e5);
7819
+ return { visible: `${d}d ${h}h`, spoken: `${d} days ${h} hours` };
7820
+ }
7821
+ function formatS(ms) {
7822
+ const secs = ms / 1e3;
7823
+ return {
7824
+ visible: `${secs.toFixed(1)}s`,
7825
+ spoken: `${secs.toFixed(1)} seconds`
7826
+ };
7827
+ }
7828
+ function formatMs(ms) {
7829
+ return { visible: `${ms}ms`, spoken: `${ms} milliseconds` };
7830
+ }
7831
+ function Duration({ ms, precision = "auto", className }) {
7832
+ const { visible, spoken } = precision === "s" ? formatS(ms) : precision === "ms" ? formatMs(ms) : formatAuto(ms);
7833
+ return /* @__PURE__ */ jsxRuntime.jsx(
7834
+ "span",
7835
+ {
7836
+ "aria-label": spoken,
7837
+ className: cn("font-mono text-xs text-muted-foreground", className),
7838
+ children: visible
7839
+ }
7840
+ );
7841
+ }
7842
+ function toDate(v) {
7843
+ return v instanceof Date ? v : new Date(v);
7844
+ }
7845
+ function formatRelative(date, now) {
7846
+ const diffMs = now.getTime() - date.getTime();
7847
+ const abs = Math.abs(diffMs);
7848
+ const suffix = diffMs >= 0 ? "ago" : "from now";
7849
+ if (abs < 6e4) return "just now";
7850
+ if (abs < 36e5) {
7851
+ const m = Math.floor(abs / 6e4);
7852
+ return `${m}m ${suffix}`;
7853
+ }
7854
+ if (abs < 864e5) {
7855
+ const h = Math.floor(abs / 36e5);
7856
+ return `${h}h ${suffix}`;
7857
+ }
7858
+ if (abs < 6048e5) {
7859
+ const d = Math.floor(abs / 864e5);
7860
+ return d === 1 ? "yesterday" : `${d}d ${suffix}`;
7861
+ }
7862
+ return date.toLocaleDateString(void 0);
7863
+ }
7864
+ function formatAbsolute(date, tz) {
7865
+ return date.toLocaleString(void 0, {
7866
+ dateStyle: "short",
7867
+ timeStyle: "short",
7868
+ timeZone: tz
7869
+ });
7870
+ }
7871
+ function Timestamp({
7872
+ value,
7873
+ format = "relative",
7874
+ tz,
7875
+ className
7876
+ }) {
7877
+ const date = toDate(value);
7878
+ const iso = date.toISOString();
7879
+ const [now, setNow] = React.useState(() => /* @__PURE__ */ new Date());
7880
+ React.useEffect(() => {
7881
+ if (format === "absolute") return;
7882
+ const id = setInterval(() => setNow(/* @__PURE__ */ new Date()), 3e4);
7883
+ return () => clearInterval(id);
7884
+ }, [format]);
7885
+ const absolute = formatAbsolute(date, tz);
7886
+ const relative = formatRelative(date, now);
7887
+ const display = format === "relative" ? relative : absolute;
7888
+ const title = format === "both" ? relative : void 0;
7889
+ return /* @__PURE__ */ jsxRuntime.jsx(
7890
+ "time",
7891
+ {
7892
+ dateTime: iso,
7893
+ title,
7894
+ className: cn("font-mono text-xs text-muted-foreground", className),
7895
+ children: display
7896
+ }
7897
+ );
7898
+ }
7899
+ function Sparkline({
7900
+ data,
7901
+ width = 140,
7902
+ height = 28,
7903
+ color = "hsl(var(--muted-foreground))",
7904
+ fill,
7905
+ strokeWidth = 1.2,
7906
+ markLast = false
7907
+ }) {
7908
+ if (data.length < 2) {
7909
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { width, height, "aria-hidden": true });
7910
+ }
7911
+ const min = Math.min(...data);
7912
+ const max = Math.max(...data);
7913
+ const range = max - min || 1;
7914
+ const pad = strokeWidth;
7915
+ const innerW = width - pad * 2;
7916
+ const innerH = height - pad * 2;
7917
+ const points = data.map((v, i) => {
7918
+ const x = pad + i / (data.length - 1) * innerW;
7919
+ const y = pad + innerH - (v - min) / range * innerH;
7920
+ return [x, y];
7921
+ });
7922
+ const polyline = points.map(([x, y]) => `${x.toFixed(2)},${y.toFixed(2)}`).join(" ");
7923
+ const firstPt = points[0];
7924
+ const lastPt = points[points.length - 1];
7925
+ let fillPath;
7926
+ if (fill) {
7927
+ fillPath = [
7928
+ `${firstPt[0].toFixed(2)},${(pad + innerH).toFixed(2)}`,
7929
+ ...points.map(([x, y]) => `${x.toFixed(2)},${y.toFixed(2)}`),
7930
+ `${lastPt[0].toFixed(2)},${(pad + innerH).toFixed(2)}`
7931
+ ].join(" ");
7932
+ }
7933
+ return /* @__PURE__ */ jsxRuntime.jsxs(
7934
+ "svg",
7935
+ {
7936
+ width,
7937
+ height,
7938
+ viewBox: `0 0 ${width} ${height}`,
7939
+ "aria-hidden": true,
7940
+ style: { display: "block", overflow: "visible" },
7941
+ children: [
7942
+ fillPath && /* @__PURE__ */ jsxRuntime.jsx("polygon", { points: fillPath, fill, stroke: "none" }),
7943
+ /* @__PURE__ */ jsxRuntime.jsx(
7944
+ "polyline",
7945
+ {
7946
+ points: polyline,
7947
+ fill: "none",
7948
+ stroke: color,
7949
+ strokeWidth,
7950
+ strokeLinejoin: "round",
7951
+ strokeLinecap: "round"
7952
+ }
7953
+ ),
7954
+ markLast && /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: lastPt[0], cy: lastPt[1], r: strokeWidth * 2, fill: color })
7955
+ ]
7956
+ }
7957
+ );
7958
+ }
7959
+ function LiveIndicator({
7960
+ active,
7961
+ label = "Live",
7962
+ className
7963
+ }) {
7964
+ const ariaLabel = active ? `${label} — connected` : `${label} — paused`;
7965
+ return /* @__PURE__ */ jsxRuntime.jsxs(
7966
+ "span",
7967
+ {
7968
+ role: "status",
7969
+ "aria-live": "polite",
7970
+ "aria-label": ariaLabel,
7971
+ className: cn(
7972
+ "inline-flex items-center gap-2 text-xs font-mono",
7973
+ active ? "text-primary" : "text-muted-foreground",
7974
+ className
7975
+ ),
7976
+ children: [
7977
+ /* @__PURE__ */ jsxRuntime.jsx(
7978
+ "span",
7979
+ {
7980
+ className: cn(
7981
+ "inline-block w-2 h-2 rounded-full",
7982
+ active ? "bg-primary animate-pulse" : "bg-muted-foreground/40"
7983
+ )
7984
+ }
7985
+ ),
7986
+ label
7987
+ ]
7988
+ }
7989
+ );
7990
+ }
7796
7991
  const defaultActivityIconMap = {
7797
7992
  user: {
7798
7993
  icon: lucideReact.Users,
@@ -10644,7 +10839,7 @@ const SidebarButton = ({
10644
10839
  "span",
10645
10840
  {
10646
10841
  className: cn(
10647
- "text-sm font-medium flex-1 text-left",
10842
+ "text-sm font-medium flex-1 text-left whitespace-nowrap",
10648
10843
  active ? `text-category-${category}` : "text-foreground"
10649
10844
  ),
10650
10845
  children: label
@@ -15740,14 +15935,12 @@ function createReactApp(config) {
15740
15935
  if (enableRouting) {
15741
15936
  tree = /* @__PURE__ */ jsxRuntime.jsx(NavigationProvider, { initialNavigation: navigation, children: /* @__PURE__ */ jsxRuntime.jsx(SidebarProvider, { children: tree }) });
15742
15937
  }
15743
- if (enableAuth) {
15744
- const authMode = (auth == null ? void 0 : auth.mode) || "pattern-stack";
15745
- if (authMode === "none") {
15746
- setGlobalAuthService(null);
15747
- tree = /* @__PURE__ */ jsxRuntime.jsx(NoAuthProvider, { children: tree });
15748
- } else {
15749
- tree = /* @__PURE__ */ jsxRuntime.jsx(AuthProvider, { config: auth, children: tree });
15750
- }
15938
+ const authMode = (auth == null ? void 0 : auth.mode) || "pattern-stack";
15939
+ if (enableAuth && authMode !== "none") {
15940
+ tree = /* @__PURE__ */ jsxRuntime.jsx(AuthProvider, { config: auth, children: tree });
15941
+ } else {
15942
+ setGlobalAuthService(null);
15943
+ tree = /* @__PURE__ */ jsxRuntime.jsx(NoAuthProvider, { children: tree });
15751
15944
  }
15752
15945
  if (enableQuery) {
15753
15946
  tree = /* @__PURE__ */ jsxRuntime.jsxs(reactQuery.QueryClientProvider, { client: queryClient, children: [
@@ -19213,6 +19406,7 @@ exports.DropdownMenuSub = DropdownMenuSub;
19213
19406
  exports.DropdownMenuSubContent = DropdownMenuSubContent;
19214
19407
  exports.DropdownMenuSubTrigger = DropdownMenuSubTrigger;
19215
19408
  exports.DropdownMenuTrigger = DropdownMenuTrigger;
19409
+ exports.Duration = Duration;
19216
19410
  exports.EmptyState = EmptyState;
19217
19411
  exports.EnhancedDataTemplate = EnhancedDataTemplate;
19218
19412
  exports.EntityIcon = EntityIcon;
@@ -19232,6 +19426,7 @@ exports.Label = Label;
19232
19426
  exports.ListCard = ListCard;
19233
19427
  exports.ListPageTemplate = ListPageTemplate;
19234
19428
  exports.ListToolbar = ListToolbar;
19429
+ exports.LiveIndicator = LiveIndicator;
19235
19430
  exports.Loading = Loading;
19236
19431
  exports.LoginForm = LoginForm;
19237
19432
  exports.LogoutButton = LogoutButton;
@@ -19276,6 +19471,7 @@ exports.SkeletonAvatar = SkeletonAvatar;
19276
19471
  exports.SkeletonButton = SkeletonButton;
19277
19472
  exports.SkeletonCard = SkeletonCard;
19278
19473
  exports.SkeletonText = SkeletonText;
19474
+ exports.Sparkline = Sparkline;
19279
19475
  exports.Spinner = Spinner;
19280
19476
  exports.StatCard = StatCard;
19281
19477
  exports.StyleGuide = StyleGuide;
@@ -19293,6 +19489,7 @@ exports.Tabs = Tabs;
19293
19489
  exports.TabsContent = TabsContent;
19294
19490
  exports.TabsList = TabsList;
19295
19491
  exports.TabsTrigger = TabsTrigger;
19492
+ exports.Timestamp = Timestamp;
19296
19493
  exports.Toast = Toast;
19297
19494
  exports.ToastContainer = ToastContainer;
19298
19495
  exports.Tooltip = Tooltip;