@flytedan/flytebot-design-system 0.11.6 → 0.12.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.d.cts CHANGED
@@ -457,14 +457,27 @@ interface TopbarProps {
457
457
  href?: string;
458
458
  onClick?: () => void;
459
459
  }>;
460
+ /** Rendered last, after any `overflowActions` — the one control every screen needs to
461
+ * keep directly reachable (typically the account menu). Never collapses, at any width. */
460
462
  actions?: React.ReactNode;
463
+ /** Secondary controls (an assistant launcher, a version badge, appearance/dev-tools
464
+ * toggles, undo — whatever a screen needs beside `actions`). Rendered inline, in the
465
+ * same spot `actions` used to hold everything, on a viewport wider than
466
+ * `overflowBreakpoint`. At or under it, they collapse into a single "More actions"
467
+ * menu instead of pushing the rest of the bar past the screen edge. Decided once
468
+ * here, so every Topbar consumer gets it without re-solving it per screen. */
469
+ overflowActions?: React.ReactNode;
470
+ /** Viewport width (px) at or under which `overflowActions` collapses into the "More
471
+ * actions" menu. Default 640 — comfortably covers 320/375/430 phone widths without
472
+ * touching any tablet or desktop layout above it. */
473
+ overflowBreakpoint?: number;
461
474
  /** Shows the indeterminate route loader on the top edge. */
462
475
  loading?: boolean;
463
476
  /** Mobile menu handler — renders the hamburger. */
464
477
  onMenu?: () => void;
465
478
  className?: string;
466
479
  }
467
- declare function Topbar({ crumbs, actions, loading, onMenu, className, ...rest }: TopbarProps): React.JSX.Element;
480
+ declare function Topbar({ crumbs, actions, overflowActions, overflowBreakpoint, loading, onMenu, className, ...rest }: TopbarProps): React.JSX.Element;
468
481
 
469
482
  /** A person or organisation mark. Initials on a soft brand tint when no image exists. */
470
483
  interface AvatarProps {
package/dist/index.d.ts CHANGED
@@ -457,14 +457,27 @@ interface TopbarProps {
457
457
  href?: string;
458
458
  onClick?: () => void;
459
459
  }>;
460
+ /** Rendered last, after any `overflowActions` — the one control every screen needs to
461
+ * keep directly reachable (typically the account menu). Never collapses, at any width. */
460
462
  actions?: React.ReactNode;
463
+ /** Secondary controls (an assistant launcher, a version badge, appearance/dev-tools
464
+ * toggles, undo — whatever a screen needs beside `actions`). Rendered inline, in the
465
+ * same spot `actions` used to hold everything, on a viewport wider than
466
+ * `overflowBreakpoint`. At or under it, they collapse into a single "More actions"
467
+ * menu instead of pushing the rest of the bar past the screen edge. Decided once
468
+ * here, so every Topbar consumer gets it without re-solving it per screen. */
469
+ overflowActions?: React.ReactNode;
470
+ /** Viewport width (px) at or under which `overflowActions` collapses into the "More
471
+ * actions" menu. Default 640 — comfortably covers 320/375/430 phone widths without
472
+ * touching any tablet or desktop layout above it. */
473
+ overflowBreakpoint?: number;
461
474
  /** Shows the indeterminate route loader on the top edge. */
462
475
  loading?: boolean;
463
476
  /** Mobile menu handler — renders the hamburger. */
464
477
  onMenu?: () => void;
465
478
  className?: string;
466
479
  }
467
- declare function Topbar({ crumbs, actions, loading, onMenu, className, ...rest }: TopbarProps): React.JSX.Element;
480
+ declare function Topbar({ crumbs, actions, overflowActions, overflowBreakpoint, loading, onMenu, className, ...rest }: TopbarProps): React.JSX.Element;
468
481
 
469
482
  /** A person or organisation mark. Initials on a soft brand tint when no image exists. */
470
483
  interface AvatarProps {
package/dist/index.js CHANGED
@@ -888,9 +888,35 @@ function Tabs({ tabs = [], value, onChange, className = "", ...rest }) {
888
888
 
889
889
  // src/components/navigation/Topbar.tsx
890
890
  import * as React5 from "react";
891
- import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
892
- function Topbar({ crumbs = [], actions, loading = false, onMenu, className = "", ...rest }) {
893
- return /* @__PURE__ */ jsxs12("header", { className: ["fd-topbar", className].filter(Boolean).join(" "), ...rest, children: [
891
+ import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
892
+ function useIsNarrow(breakpoint) {
893
+ const [narrow, setNarrow] = React5.useState(
894
+ () => typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia(`(max-width: ${breakpoint}px)`).matches
895
+ );
896
+ React5.useEffect(() => {
897
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
898
+ const mq = window.matchMedia(`(max-width: ${breakpoint}px)`);
899
+ const apply = () => setNarrow(mq.matches);
900
+ apply();
901
+ mq.addEventListener("change", apply);
902
+ return () => mq.removeEventListener("change", apply);
903
+ }, [breakpoint]);
904
+ return narrow;
905
+ }
906
+ function Topbar({
907
+ crumbs = [],
908
+ actions,
909
+ overflowActions,
910
+ overflowBreakpoint = 640,
911
+ loading = false,
912
+ onMenu,
913
+ className = "",
914
+ ...rest
915
+ }) {
916
+ const narrow = useIsNarrow(overflowBreakpoint);
917
+ const [moreOpen, setMoreOpen] = React5.useState(false);
918
+ const moreRef = React5.useRef(null);
919
+ return /* @__PURE__ */ jsxs12("header", { className: ["fd-topbar", narrow ? "is-narrow" : "", className].filter(Boolean).join(" "), ...rest, children: [
894
920
  onMenu ? /* @__PURE__ */ jsx16("button", { type: "button", className: "fd-btn-icon", "aria-label": "Menu", onClick: onMenu, children: /* @__PURE__ */ jsx16("i", { className: "ph ph-list" }) }) : null,
895
921
  /* @__PURE__ */ jsx16("nav", { className: "fd-crumb", "aria-label": "Breadcrumb", children: crumbs.map((c, i) => /* @__PURE__ */ jsxs12(React5.Fragment, { children: [
896
922
  i > 0 ? /* @__PURE__ */ jsx16("i", { className: "ph ph-caret-right", style: { fontSize: 11, opacity: 0.6 }, "aria-hidden": "true" }) : null,
@@ -907,6 +933,34 @@ function Topbar({ crumbs = [], actions, loading = false, onMenu, className = "",
907
933
  )
908
934
  ] }, i)) }),
909
935
  /* @__PURE__ */ jsx16("span", { style: { flex: 1 } }),
936
+ overflowActions != null ? narrow ? /* @__PURE__ */ jsxs12(Fragment4, { children: [
937
+ /* @__PURE__ */ jsx16(
938
+ "button",
939
+ {
940
+ type: "button",
941
+ ref: moreRef,
942
+ className: "fd-btn-icon",
943
+ "aria-label": "More actions",
944
+ "aria-haspopup": "dialog",
945
+ "aria-expanded": moreOpen,
946
+ onClick: () => setMoreOpen((v) => !v),
947
+ children: /* @__PURE__ */ jsx16("i", { className: "ph ph-dots-three-vertical", "aria-hidden": "true" })
948
+ }
949
+ ),
950
+ /* @__PURE__ */ jsx16(
951
+ Popover,
952
+ {
953
+ open: moreOpen,
954
+ anchorRef: moreRef,
955
+ onClose: () => setMoreOpen(false),
956
+ placement: "bottom-end",
957
+ padded: true,
958
+ label: "More actions",
959
+ className: "fd-topbar-overflow",
960
+ children: /* @__PURE__ */ jsx16("div", { className: "fd-stack fd-topbar-overflow-list", children: overflowActions })
961
+ }
962
+ )
963
+ ] }) : overflowActions : null,
910
964
  actions,
911
965
  loading ? /* @__PURE__ */ jsx16("span", { className: "fd-topload", "aria-hidden": "true" }) : null
912
966
  ] });
@@ -958,12 +1012,12 @@ function EmptyState({ icon = "tray", title, children, action, className = "", ..
958
1012
  }
959
1013
 
960
1014
  // src/components/feedback/Flag.tsx
961
- import { Fragment as Fragment4, jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
1015
+ import { Fragment as Fragment5, jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
962
1016
  function Flag({ statement, cost, actions, tone = "warning", className = "", ...rest }) {
963
1017
  return /* @__PURE__ */ jsxs16("div", { className: ["fd-flag", "fd-flag-" + tone, className].filter(Boolean).join(" "), role: "status", ...rest, children: [
964
1018
  /* @__PURE__ */ jsxs16("span", { className: "fd-flag-text", children: [
965
1019
  statement,
966
- cost ? /* @__PURE__ */ jsxs16(Fragment4, { children: [
1020
+ cost ? /* @__PURE__ */ jsxs16(Fragment5, { children: [
967
1021
  " ",
968
1022
  /* @__PURE__ */ jsx20("span", { className: "fd-flag-cost", children: cost })
969
1023
  ] }) : null
@@ -1546,7 +1600,7 @@ function StatTile({ label, value, sub, delta, loading = false, compact: compact3
1546
1600
 
1547
1601
  // src/components/data/CodeBlock.tsx
1548
1602
  import * as React9 from "react";
1549
- import { Fragment as Fragment5, jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
1603
+ import { Fragment as Fragment6, jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
1550
1604
  var JS_KW = /^(?:const|let|var|function|return|if|else|for|while|do|new|class|extends|import|from|export|default|await|async|try|catch|finally|throw|typeof|instanceof|of|in|switch|case|break|continue|delete|void|yield|static|get|set|this|super|interface|type|enum|implements|public|private|protected|readonly|abstract|as|satisfies|declare|namespace)$/;
1551
1605
  var RULES = {
1552
1606
  js: [
@@ -1702,7 +1756,7 @@ function tokenize(src, lang) {
1702
1756
  }
1703
1757
  function Highlighted({ code, lang }) {
1704
1758
  const toks = React9.useMemo(() => tokenize(code, lang), [code, lang]);
1705
- return /* @__PURE__ */ jsx30(Fragment5, { children: toks.map((tk, i) => tk.c ? /* @__PURE__ */ jsx30("span", { className: "tok-" + tk.c, children: tk.t }, i) : tk.t) });
1759
+ return /* @__PURE__ */ jsx30(Fragment6, { children: toks.map((tk, i) => tk.c ? /* @__PURE__ */ jsx30("span", { className: "tok-" + tk.c, children: tk.t }, i) : tk.t) });
1706
1760
  }
1707
1761
  function useCopy() {
1708
1762
  const [done, setDone] = React9.useState(false);
@@ -2066,7 +2120,7 @@ function Meter({ segments = [], unallocated = 0, height = 8, label, legend = fal
2066
2120
  }
2067
2121
 
2068
2122
  // src/components/data/Pagination.tsx
2069
- import { Fragment as Fragment7, jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
2123
+ import { Fragment as Fragment8, jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
2070
2124
  function Pagination({
2071
2125
  page = 1,
2072
2126
  pageSize = 25,
@@ -2092,10 +2146,10 @@ function Pagination({
2092
2146
  else if (nums[nums.length - 1] !== "\u2026") nums.push("\u2026");
2093
2147
  }
2094
2148
  return /* @__PURE__ */ jsxs28("div", { className: ["fd-pag", className].filter(Boolean).join(" "), ...rest, children: [
2095
- /* @__PURE__ */ jsx33("span", { className: "fd-pag-count", children: loading ? /* @__PURE__ */ jsx33("span", { className: "fd-skel fd-skel-text", style: { display: "inline-block", height: 12, width: 108 } }) : total === 0 ? /* @__PURE__ */ jsxs28(Fragment7, { children: [
2149
+ /* @__PURE__ */ jsx33("span", { className: "fd-pag-count", children: loading ? /* @__PURE__ */ jsx33("span", { className: "fd-skel fd-skel-text", style: { display: "inline-block", height: 12, width: 108 } }) : total === 0 ? /* @__PURE__ */ jsxs28(Fragment8, { children: [
2096
2150
  "No ",
2097
2151
  unit
2098
- ] }) : /* @__PURE__ */ jsxs28(Fragment7, { children: [
2152
+ ] }) : /* @__PURE__ */ jsxs28(Fragment8, { children: [
2099
2153
  /* @__PURE__ */ jsxs28("strong", { children: [
2100
2154
  from.toLocaleString(),
2101
2155
  "\u2013",
@@ -6579,7 +6633,7 @@ var SessionKit = {
6579
6633
  };
6580
6634
 
6581
6635
  // src/components/platform/AccountMenu.tsx
6582
- import { Fragment as Fragment14, jsx as jsx57, jsxs as jsxs52 } from "react/jsx-runtime";
6636
+ import { Fragment as Fragment15, jsx as jsx57, jsxs as jsxs52 } from "react/jsx-runtime";
6583
6637
  var DEFAULT_LINKS = [
6584
6638
  { id: "profile", label: "Your profile", icon: "user-circle", href: "../admin/index.html#profile" },
6585
6639
  { id: "preferences", label: "Preferences", icon: "sliders-horizontal", href: "../admin/index.html#preferences" }
@@ -6632,7 +6686,7 @@ function AccountMenu({
6632
6686
  if (onSignOut) return onSignOut();
6633
6687
  window.alert("Signed out. (Simulated \u2014 no auth provider is wired up.)");
6634
6688
  };
6635
- return /* @__PURE__ */ jsxs52(Fragment14, { children: [
6689
+ return /* @__PURE__ */ jsxs52(Fragment15, { children: [
6636
6690
  /* @__PURE__ */ jsxs52(
6637
6691
  "button",
6638
6692
  {
@@ -7469,7 +7523,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
7469
7523
  // src/components/platform/ComingSoon.tsx
7470
7524
  import * as React35 from "react";
7471
7525
  import { createPortal as createPortal5 } from "react-dom";
7472
- import { Fragment as Fragment16, jsx as jsx61, jsxs as jsxs56 } from "react/jsx-runtime";
7526
+ import { Fragment as Fragment17, jsx as jsx61, jsxs as jsxs56 } from "react/jsx-runtime";
7473
7527
  var BYPASS_STORE = "fd.soon.bypass.v1";
7474
7528
  function readBypassed() {
7475
7529
  try {
@@ -7499,7 +7553,7 @@ function useBypass(key) {
7499
7553
  return [on, set];
7500
7554
  }
7501
7555
  function SoonCard({ label, detail, backend, eta, effort, onRoadmap, allowed, onBypass }) {
7502
- return /* @__PURE__ */ jsxs56(Fragment16, { children: [
7556
+ return /* @__PURE__ */ jsxs56(Fragment17, { children: [
7503
7557
  /* @__PURE__ */ jsxs56("span", { className: "fd-soon-badge", children: [
7504
7558
  /* @__PURE__ */ jsx61("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" }),
7505
7559
  label
@@ -7850,7 +7904,7 @@ function PermissionHint({ perm, children }) {
7850
7904
 
7851
7905
  // src/components/platform/ModeSwitch.tsx
7852
7906
  import * as React36 from "react";
7853
- import { Fragment as Fragment17, jsx as jsx63, jsxs as jsxs58 } from "react/jsx-runtime";
7907
+ import { Fragment as Fragment18, jsx as jsx63, jsxs as jsxs58 } from "react/jsx-runtime";
7854
7908
  function ModeSwitch({ canToggle = false, requestCount = 0, onClearLog, renderLog, onOpenSpec, summary }) {
7855
7909
  const mode2 = useRuntimeMode();
7856
7910
  const [open, setOpen] = React36.useState(false);
@@ -7920,7 +7974,7 @@ function ModeSwitch({ canToggle = false, requestCount = 0, onClearLog, renderLog
7920
7974
  s.total,
7921
7975
  " features complete"
7922
7976
  ] }),
7923
- onOpenSpec ? /* @__PURE__ */ jsxs58(Fragment17, { children: [
7977
+ onOpenSpec ? /* @__PURE__ */ jsxs58(Fragment18, { children: [
7924
7978
  /* @__PURE__ */ jsx63("span", { style: { flex: 1 } }),
7925
7979
  /* @__PURE__ */ jsxs58("button", { type: "button", className: "fd-soon-link", onClick: () => {
7926
7980
  setOpen(false);
@@ -8105,7 +8159,7 @@ function SaturationDistribution({ campuses = [], floorBand, floorScore, onSelect
8105
8159
 
8106
8160
  // src/components/planner/MixGap.tsx
8107
8161
  import * as React37 from "react";
8108
- import { Fragment as Fragment18, jsx as jsx66, jsxs as jsxs61 } from "react/jsx-runtime";
8162
+ import { Fragment as Fragment19, jsx as jsx66, jsxs as jsxs61 } from "react/jsx-runtime";
8109
8163
  var MIX_GAP_LAYOUT = {
8110
8164
  trackHeight: 30,
8111
8165
  barHeight: 20,
@@ -8149,7 +8203,7 @@ function MixGap({ rows = [], loading = false, className = "" }) {
8149
8203
  onMouseLeave: () => setHover(null),
8150
8204
  children: [
8151
8205
  /* @__PURE__ */ jsx66("span", { style: { width: 128, flex: "none" }, children: /* @__PURE__ */ jsx66(ChannelTag, { channel: r.channel, size: "sm" }) }),
8152
- /* @__PURE__ */ jsx66("span", { style: { position: "relative", flex: 1, height: trackHeight, minWidth: 120 }, children: loading ? /* @__PURE__ */ jsx66("span", { className: "fd-skel", style: { position: "absolute", left: 0, right: 0, top: barTop, height: barHeight, borderRadius: 3 } }) : /* @__PURE__ */ jsxs61(Fragment18, { children: [
8206
+ /* @__PURE__ */ jsx66("span", { style: { position: "relative", flex: 1, height: trackHeight, minWidth: 120 }, children: loading ? /* @__PURE__ */ jsx66("span", { className: "fd-skel", style: { position: "absolute", left: 0, right: 0, top: barTop, height: barHeight, borderRadius: 3 } }) : /* @__PURE__ */ jsxs61(Fragment19, { children: [
8153
8207
  /* @__PURE__ */ jsx66("span", { "data-mixgap-bar": "", style: { position: "absolute", left: 0, top: barTop, height: barHeight, width: mixGapOffset(r.realized, max), background: TONE[tone], borderRadius: "2px 3px 3px 2px", transition: "width var(--dur-slow) var(--ease), background var(--dur-base) var(--ease)" } }),
8154
8208
  /* @__PURE__ */ jsx66("span", { "data-mixgap-target": "", style: { position: "absolute", left: `calc(${mixGapOffset(r.target, max)} - ${targetWidth / 2}px)`, top: 0, width: targetWidth, height: trackHeight, background: MIX_GAP_TARGET_STRIPE, borderRadius: 1 } }),
8155
8209
  /* @__PURE__ */ jsxs61("span", { "data-mixgap-label": "", className: "fd-num", style: { position: "absolute", left: labelLeft, top: "50%", transform: "translateY(-50%)", fontSize: 12, color: "var(--text-muted)", whiteSpace: "nowrap" }, children: [
@@ -8263,7 +8317,7 @@ function ChannelContribution({
8263
8317
 
8264
8318
  // src/components/planner/BudgetReallocator.tsx
8265
8319
  import * as React38 from "react";
8266
- import { Fragment as Fragment19, jsx as jsx68, jsxs as jsxs63 } from "react/jsx-runtime";
8320
+ import { Fragment as Fragment20, jsx as jsx68, jsxs as jsxs63 } from "react/jsx-runtime";
8267
8321
  var bandFor = (crp) => crp >= 200 ? "dominant" : crp >= 100 ? "strong" : crp >= 50 ? "adequate" : "weak";
8268
8322
  function BudgetReallocator({
8269
8323
  campus,
@@ -8303,7 +8357,7 @@ function BudgetReallocator({
8303
8357
  /* @__PURE__ */ jsxs63("div", { className: "fd-row", style: { gap: 12, flexWrap: "wrap" }, children: [
8304
8358
  /* @__PURE__ */ jsx68("span", { className: "fd-h4", style: { flex: 1, minWidth: 140 }, children: campus }),
8305
8359
  /* @__PURE__ */ jsx68(CsiBadge, { band: nowBand, crp, size: "medium" }),
8306
- dirty ? /* @__PURE__ */ jsxs63(Fragment19, { children: [
8360
+ dirty ? /* @__PURE__ */ jsxs63(Fragment20, { children: [
8307
8361
  /* @__PURE__ */ jsx68("i", { className: "ph ph-arrow-right fd-muted", "aria-hidden": "true" }),
8308
8362
  /* @__PURE__ */ jsx68(CsiBadge, { band: nextBand, crp: nextCrp, size: "medium", preview: true })
8309
8363
  ] }) : null