@flytedan/flytebot-design-system 0.11.5 → 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.cjs CHANGED
@@ -1092,8 +1092,34 @@ function Tabs({ tabs = [], value, onChange, className = "", ...rest }) {
1092
1092
  // src/components/navigation/Topbar.tsx
1093
1093
  var React5 = __toESM(require("react"), 1);
1094
1094
  var import_jsx_runtime16 = require("react/jsx-runtime");
1095
- function Topbar({ crumbs = [], actions, loading = false, onMenu, className = "", ...rest }) {
1096
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("header", { className: ["fd-topbar", className].filter(Boolean).join(" "), ...rest, children: [
1095
+ function useIsNarrow(breakpoint) {
1096
+ const [narrow, setNarrow] = React5.useState(
1097
+ () => typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia(`(max-width: ${breakpoint}px)`).matches
1098
+ );
1099
+ React5.useEffect(() => {
1100
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
1101
+ const mq = window.matchMedia(`(max-width: ${breakpoint}px)`);
1102
+ const apply = () => setNarrow(mq.matches);
1103
+ apply();
1104
+ mq.addEventListener("change", apply);
1105
+ return () => mq.removeEventListener("change", apply);
1106
+ }, [breakpoint]);
1107
+ return narrow;
1108
+ }
1109
+ function Topbar({
1110
+ crumbs = [],
1111
+ actions,
1112
+ overflowActions,
1113
+ overflowBreakpoint = 640,
1114
+ loading = false,
1115
+ onMenu,
1116
+ className = "",
1117
+ ...rest
1118
+ }) {
1119
+ const narrow = useIsNarrow(overflowBreakpoint);
1120
+ const [moreOpen, setMoreOpen] = React5.useState(false);
1121
+ const moreRef = React5.useRef(null);
1122
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("header", { className: ["fd-topbar", narrow ? "is-narrow" : "", className].filter(Boolean).join(" "), ...rest, children: [
1097
1123
  onMenu ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", className: "fd-btn-icon", "aria-label": "Menu", onClick: onMenu, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("i", { className: "ph ph-list" }) }) : null,
1098
1124
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("nav", { className: "fd-crumb", "aria-label": "Breadcrumb", children: crumbs.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(React5.Fragment, { children: [
1099
1125
  i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("i", { className: "ph ph-caret-right", style: { fontSize: 11, opacity: 0.6 }, "aria-hidden": "true" }) : null,
@@ -1110,6 +1136,34 @@ function Topbar({ crumbs = [], actions, loading = false, onMenu, className = "",
1110
1136
  )
1111
1137
  ] }, i)) }),
1112
1138
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { flex: 1 } }),
1139
+ overflowActions != null ? narrow ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
1140
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1141
+ "button",
1142
+ {
1143
+ type: "button",
1144
+ ref: moreRef,
1145
+ className: "fd-btn-icon",
1146
+ "aria-label": "More actions",
1147
+ "aria-haspopup": "dialog",
1148
+ "aria-expanded": moreOpen,
1149
+ onClick: () => setMoreOpen((v) => !v),
1150
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("i", { className: "ph ph-dots-three-vertical", "aria-hidden": "true" })
1151
+ }
1152
+ ),
1153
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1154
+ Popover,
1155
+ {
1156
+ open: moreOpen,
1157
+ anchorRef: moreRef,
1158
+ onClose: () => setMoreOpen(false),
1159
+ placement: "bottom-end",
1160
+ padded: true,
1161
+ label: "More actions",
1162
+ className: "fd-topbar-overflow",
1163
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "fd-stack fd-topbar-overflow-list", children: overflowActions })
1164
+ }
1165
+ )
1166
+ ] }) : overflowActions : null,
1113
1167
  actions,
1114
1168
  loading ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "fd-topload", "aria-hidden": "true" }) : null
1115
1169
  ] });
@@ -8309,11 +8363,24 @@ function SaturationDistribution({ campuses = [], floorBand, floorScore, onSelect
8309
8363
  // src/components/planner/MixGap.tsx
8310
8364
  var React37 = __toESM(require("react"), 1);
8311
8365
  var import_jsx_runtime67 = require("react/jsx-runtime");
8366
+ var MIX_GAP_LAYOUT = {
8367
+ trackHeight: 30,
8368
+ barHeight: 20,
8369
+ targetWidth: 6,
8370
+ labelGap: 6,
8371
+ labelGutter: 50
8372
+ };
8373
+ var MIX_GAP_TARGET_STRIPE = "repeating-linear-gradient(135deg, var(--n-600) 0 2px, var(--n-300) 2px 4px)";
8374
+ function mixGapOffset(value, max) {
8375
+ return `calc((100% - ${MIX_GAP_LAYOUT.labelGutter}px) * ${value / max})`;
8376
+ }
8312
8377
  function MixGap({ rows = [], loading = false, className = "" }) {
8313
8378
  const max = Math.max(1, ...rows.flatMap((r) => [r.target, r.realized]));
8314
8379
  const [hover, setHover] = React37.useState(null);
8315
8380
  const toneOf = (gap) => gap >= -1 ? "ok" : gap >= -4 ? "warn" : "danger";
8316
8381
  const TONE = { ok: "var(--ok-solid)", warn: "var(--warn-solid)", danger: "var(--danger-solid)" };
8382
+ const { trackHeight, barHeight, targetWidth, labelGap } = MIX_GAP_LAYOUT;
8383
+ const barTop = (trackHeight - barHeight) / 2;
8317
8384
  return /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("div", { className: ["fd-stack", className].filter(Boolean).join(" "), style: { gap: 4 }, children: [
8318
8385
  /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("div", { className: "fd-row", style: { gap: 16, justifyContent: "flex-end", paddingBottom: 6, flexWrap: "wrap" }, children: [
8319
8386
  [["On target", TONE.ok], ["Close", TONE.warn], ["Short", TONE.danger]].map(([l, c]) => /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("span", { className: "fd-row fd-body-sm fd-muted", style: { gap: 6 }, children: [
@@ -8321,7 +8388,7 @@ function MixGap({ rows = [], loading = false, className = "" }) {
8321
8388
  l
8322
8389
  ] }, l)),
8323
8390
  /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("span", { className: "fd-row fd-body-sm fd-muted", style: { gap: 6 }, children: [
8324
- /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { style: { width: 3, height: 14, background: "var(--n-500)" } }),
8391
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { "data-mixgap-legend-target": "", style: { width: targetWidth, height: 16, borderRadius: 1, background: MIX_GAP_TARGET_STRIPE } }),
8325
8392
  "Target"
8326
8393
  ] })
8327
8394
  ] }),
@@ -8329,6 +8396,7 @@ function MixGap({ rows = [], loading = false, className = "" }) {
8329
8396
  const gap = r.realized - r.target;
8330
8397
  const tone = toneOf(gap);
8331
8398
  const tip = "Realized " + r.realized + "% vs " + r.target + "% target" + (gap < -1 ? " \xB7 " + Math.abs(gap).toFixed(0) + " pts short" : gap > 1 ? " \xB7 " + gap.toFixed(0) + " pts over" : " \xB7 on target") + (r.note ? " \u2014 " + r.note : "");
8399
+ const labelLeft = r.realized > r.target ? `calc(${mixGapOffset(r.realized, max)} + ${labelGap}px)` : `calc(${mixGapOffset(r.target, max)} + ${targetWidth / 2 + labelGap}px)`;
8332
8400
  return /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)(
8333
8401
  "div",
8334
8402
  {
@@ -8338,14 +8406,14 @@ function MixGap({ rows = [], loading = false, className = "" }) {
8338
8406
  onMouseLeave: () => setHover(null),
8339
8407
  children: [
8340
8408
  /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { style: { width: 128, flex: "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime67.jsx)(ChannelTag, { channel: r.channel, size: "sm" }) }),
8341
- /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { style: { position: "relative", flex: 1, height: 22, minWidth: 120 }, children: loading ? /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "fd-skel", style: { position: "absolute", inset: "5px 0", borderRadius: 3 } }) : /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)(import_jsx_runtime67.Fragment, { children: [
8342
- /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { style: { position: "absolute", left: 0, top: 5, height: 12, width: r.realized / max * 100 + "%", background: TONE[tone], borderRadius: "2px 3px 3px 2px", transition: "width var(--dur-slow) var(--ease), background var(--dur-base) var(--ease)" } }),
8343
- /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { style: { position: "absolute", left: r.target / max * 100 + "%", top: 0, width: 3, height: 22, background: "var(--n-500)", borderRadius: 1 } }),
8344
- /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("span", { className: "fd-num", style: { position: "absolute", right: Math.max(r.realized, r.target) / max > 0.82 ? 0 : "auto", left: Math.max(r.realized, r.target) / max > 0.82 ? "auto" : "calc(" + Math.max(r.realized, r.target) / max * 100 + "% + 10px)", top: 2, fontSize: 12, color: "var(--text-muted)", whiteSpace: "nowrap", background: Math.max(r.realized, r.target) / max > 0.82 ? "var(--surface)" : "none", paddingLeft: 3 }, children: [
8409
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { style: { position: "relative", flex: 1, height: trackHeight, minWidth: 120 }, children: loading ? /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "fd-skel", style: { position: "absolute", left: 0, right: 0, top: barTop, height: barHeight, borderRadius: 3 } }) : /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)(import_jsx_runtime67.Fragment, { children: [
8410
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("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)" } }),
8411
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("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 } }),
8412
+ /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)("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: [
8345
8413
  r.realized,
8346
8414
  "%"
8347
8415
  ] }),
8348
- hover === r.channel ? /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "fd-tooltip", role: "tooltip", style: { position: "absolute", left: 0, right: "auto", bottom: 26, maxWidth: "min(340px, 100%)", whiteSpace: "normal", textAlign: "left", width: "max-content" }, children: tip }) : null
8416
+ hover === r.channel ? /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "fd-tooltip", role: "tooltip", style: { position: "absolute", left: 0, right: "auto", bottom: trackHeight + 4, maxWidth: "min(340px, 100%)", whiteSpace: "normal", textAlign: "left", width: "max-content" }, children: tip }) : null
8349
8417
  ] }) })
8350
8418
  ]
8351
8419
  },