@ceebee/ui 1.8.0 → 1.10.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/THIRD_PARTY_NOTICES.md +19 -1
- package/dist/client.css +281 -0
- package/dist/client.d.ts +447 -1
- package/dist/client.js +878 -51
- package/dist/styles.css +555 -0
- package/package.json +6 -2
package/dist/client.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Modal as Modal$1, Button, theme, ConfigProvider, Progress } from 'antd';
|
|
3
3
|
export * from 'antd';
|
|
4
|
+
import * as React from 'react';
|
|
4
5
|
import { createContext, useId, useRef, useCallback, useInsertionEffect, useLayoutEffect, useState, useEffect, useMemo, useContext, Children } from 'react';
|
|
5
6
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
6
7
|
import { StyleProvider, createCache, extractStyle } from '@ant-design/cssinjs';
|
|
@@ -13,6 +14,9 @@ import { X, Minus, Plus, RotateCcw, Minimize2, Maximize2, Search, Check, Chevron
|
|
|
13
14
|
import { Toast } from '@base-ui/react/toast';
|
|
14
15
|
import { Popover } from '@base-ui/react/popover';
|
|
15
16
|
import { AnimatePresence, motion } from 'motion/react';
|
|
17
|
+
import { useSensors, useSensor, PointerSensor, DndContext, closestCorners, DragOverlay, useDroppable } from '@dnd-kit/core';
|
|
18
|
+
import { SortableContext, verticalListSortingStrategy, useSortable } from '@dnd-kit/sortable';
|
|
19
|
+
import { CSS } from '@dnd-kit/utilities';
|
|
16
20
|
import { ReactFlow, ConnectionMode, Background, Controls, applyNodeChanges, MarkerType, Handle, Position } from '@xyflow/react';
|
|
17
21
|
|
|
18
22
|
// src/lib/cn.ts
|
|
@@ -118,6 +122,66 @@ function ModalRoot({
|
|
|
118
122
|
}
|
|
119
123
|
var Modal = Object.assign(ModalRoot, Modal$1);
|
|
120
124
|
|
|
125
|
+
// src/lib/css-probe.ts
|
|
126
|
+
function createCssProbe(root) {
|
|
127
|
+
const probe = document.createElement("span");
|
|
128
|
+
probe.style.position = "fixed";
|
|
129
|
+
probe.style.pointerEvents = "none";
|
|
130
|
+
probe.style.visibility = "hidden";
|
|
131
|
+
root.append(probe);
|
|
132
|
+
return {
|
|
133
|
+
color: (name) => resolveCssColor(probe, name),
|
|
134
|
+
length: (name) => resolveCssLength(probe, name),
|
|
135
|
+
done: () => probe.remove()
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function resolveCssLength(probe, name) {
|
|
139
|
+
probe.style.width = `var(${name})`;
|
|
140
|
+
const value = Number.parseFloat(getComputedStyle(probe).width);
|
|
141
|
+
probe.style.removeProperty("width");
|
|
142
|
+
return Number.isFinite(value) ? value : void 0;
|
|
143
|
+
}
|
|
144
|
+
function resolveCssColor(probe, name) {
|
|
145
|
+
probe.style.color = `var(${name})`;
|
|
146
|
+
const value = getComputedStyle(probe).color;
|
|
147
|
+
probe.style.removeProperty("color");
|
|
148
|
+
if (!value) return void 0;
|
|
149
|
+
if (value.startsWith("var(")) return void 0;
|
|
150
|
+
if (/^(?:#|rgb|hsl|hsv)/i.test(value)) return value;
|
|
151
|
+
const canvas = document.createElement("canvas");
|
|
152
|
+
canvas.width = 1;
|
|
153
|
+
canvas.height = 1;
|
|
154
|
+
const context = canvas.getContext("2d", { willReadFrequently: true });
|
|
155
|
+
if (!context) return void 0;
|
|
156
|
+
context.clearRect(0, 0, 1, 1);
|
|
157
|
+
context.fillStyle = value;
|
|
158
|
+
context.fillRect(0, 0, 1, 1);
|
|
159
|
+
const [red = 0, green = 0, blue = 0, alpha = 255] = context.getImageData(0, 0, 1, 1).data;
|
|
160
|
+
return `rgba(${red}, ${green}, ${blue}, ${alpha / 255})`;
|
|
161
|
+
}
|
|
162
|
+
var SKIN_LINK_ID = "cb-skin";
|
|
163
|
+
function watchTokens(onChange) {
|
|
164
|
+
const rootObserver = new MutationObserver(onChange);
|
|
165
|
+
rootObserver.observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] });
|
|
166
|
+
const headObserver = new MutationObserver((records) => {
|
|
167
|
+
const skinChanged = records.some((record) => [...record.addedNodes, ...record.removedNodes].some((node) => node instanceof HTMLElement && node.id === SKIN_LINK_ID));
|
|
168
|
+
if (skinChanged) onChange();
|
|
169
|
+
});
|
|
170
|
+
headObserver.observe(document.head, { childList: true });
|
|
171
|
+
const onSkinLoad = (event) => {
|
|
172
|
+
if (event.target instanceof HTMLElement && event.target.id === SKIN_LINK_ID) onChange();
|
|
173
|
+
};
|
|
174
|
+
document.addEventListener("load", onSkinLoad, true);
|
|
175
|
+
const dark = window.matchMedia("(prefers-color-scheme: dark)");
|
|
176
|
+
dark.addEventListener("change", onChange);
|
|
177
|
+
return () => {
|
|
178
|
+
rootObserver.disconnect();
|
|
179
|
+
headObserver.disconnect();
|
|
180
|
+
document.removeEventListener("load", onSkinLoad, true);
|
|
181
|
+
dark.removeEventListener("change", onChange);
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
121
185
|
// src/theme/ant-theme-seeds.generated.ts
|
|
122
186
|
var generatedCeebeeAntSeeds = {
|
|
123
187
|
"ceebee": {
|
|
@@ -788,7 +852,6 @@ var THEME_MODE_COOKIE = "cb-theme-mode";
|
|
|
788
852
|
function serializeThemeModeCookie(mode) {
|
|
789
853
|
return `${THEME_MODE_COOKIE}=${mode}; Path=/; Max-Age=31536000; SameSite=Lax`;
|
|
790
854
|
}
|
|
791
|
-
var SKIN_LINK_ID = "cb-skin";
|
|
792
855
|
function ThemeBridge({
|
|
793
856
|
children,
|
|
794
857
|
mode,
|
|
@@ -802,27 +865,12 @@ function ThemeBridge({
|
|
|
802
865
|
}, []);
|
|
803
866
|
useEffect(() => {
|
|
804
867
|
refresh();
|
|
805
|
-
const
|
|
806
|
-
rootObserver.observe(document.documentElement, {
|
|
807
|
-
attributes: true,
|
|
808
|
-
attributeFilter: ["data-theme"]
|
|
809
|
-
});
|
|
810
|
-
const headObserver = new MutationObserver((records) => {
|
|
811
|
-
const skinChanged = records.some((record) => [...record.addedNodes, ...record.removedNodes].some((node) => node instanceof HTMLElement && node.id === SKIN_LINK_ID));
|
|
812
|
-
if (skinChanged) refresh();
|
|
813
|
-
});
|
|
814
|
-
headObserver.observe(document.head, { childList: true });
|
|
815
|
-
const onSkinLoad = (event) => {
|
|
816
|
-
if (event.target instanceof HTMLElement && event.target.id === SKIN_LINK_ID) refresh();
|
|
817
|
-
};
|
|
818
|
-
document.addEventListener("load", onSkinLoad, true);
|
|
868
|
+
const stopWatchingTokens = watchTokens(refresh);
|
|
819
869
|
const coarsePointer = window.matchMedia("(pointer: coarse)");
|
|
820
870
|
const onPointerChange = () => refresh();
|
|
821
871
|
coarsePointer.addEventListener("change", onPointerChange);
|
|
822
872
|
return () => {
|
|
823
|
-
|
|
824
|
-
headObserver.disconnect();
|
|
825
|
-
document.removeEventListener("load", onSkinLoad, true);
|
|
873
|
+
stopWatchingTokens();
|
|
826
874
|
coarsePointer.removeEventListener("change", onPointerChange);
|
|
827
875
|
};
|
|
828
876
|
}, [refresh]);
|
|
@@ -849,13 +897,9 @@ function mergeComponents(base, override) {
|
|
|
849
897
|
return merged;
|
|
850
898
|
}
|
|
851
899
|
function readCeebeeThemeToken(root) {
|
|
852
|
-
const probe =
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
probe.style.visibility = "hidden";
|
|
856
|
-
root.append(probe);
|
|
857
|
-
const color = (name) => resolveCssColor(probe, name);
|
|
858
|
-
const length = (name) => resolveCssLength(probe, name);
|
|
900
|
+
const probe = createCssProbe(root);
|
|
901
|
+
const color = (name) => probe.color(name);
|
|
902
|
+
const length = (name) => probe.length(name);
|
|
859
903
|
const tokens = {
|
|
860
904
|
colorPrimary: color("--cb-tone-brand"),
|
|
861
905
|
colorInfo: color("--cb-tone-info"),
|
|
@@ -915,7 +959,7 @@ function readCeebeeThemeToken(root) {
|
|
|
915
959
|
dotActiveBorderColor: trackBg
|
|
916
960
|
};
|
|
917
961
|
}
|
|
918
|
-
probe.
|
|
962
|
+
probe.done();
|
|
919
963
|
return {
|
|
920
964
|
token: Object.fromEntries(
|
|
921
965
|
Object.entries(tokens).filter(([, value]) => value !== void 0)
|
|
@@ -923,30 +967,6 @@ function readCeebeeThemeToken(root) {
|
|
|
923
967
|
components
|
|
924
968
|
};
|
|
925
969
|
}
|
|
926
|
-
function resolveCssLength(probe, name) {
|
|
927
|
-
probe.style.width = `var(${name})`;
|
|
928
|
-
const value = Number.parseFloat(getComputedStyle(probe).width);
|
|
929
|
-
probe.style.removeProperty("width");
|
|
930
|
-
return Number.isFinite(value) ? value : void 0;
|
|
931
|
-
}
|
|
932
|
-
function resolveCssColor(probe, name) {
|
|
933
|
-
probe.style.color = `var(${name})`;
|
|
934
|
-
const value = getComputedStyle(probe).color;
|
|
935
|
-
probe.style.removeProperty("color");
|
|
936
|
-
if (!value) return void 0;
|
|
937
|
-
if (value.startsWith("var(")) return void 0;
|
|
938
|
-
if (/^(?:#|rgb|hsl|hsv)/i.test(value)) return value;
|
|
939
|
-
const canvas = document.createElement("canvas");
|
|
940
|
-
canvas.width = 1;
|
|
941
|
-
canvas.height = 1;
|
|
942
|
-
const context = canvas.getContext("2d", { willReadFrequently: true });
|
|
943
|
-
if (!context) return void 0;
|
|
944
|
-
context.clearRect(0, 0, 1, 1);
|
|
945
|
-
context.fillStyle = value;
|
|
946
|
-
context.fillRect(0, 0, 1, 1);
|
|
947
|
-
const [red = 0, green = 0, blue = 0, alpha = 255] = context.getImageData(0, 0, 1, 1).data;
|
|
948
|
-
return `rgba(${red}, ${green}, ${blue}, ${alpha / 255})`;
|
|
949
|
-
}
|
|
950
970
|
function CeebeeAntStyleProvider({ cache, children }) {
|
|
951
971
|
return /* @__PURE__ */ jsx(StyleProvider, { cache, children });
|
|
952
972
|
}
|
|
@@ -1673,6 +1693,813 @@ function PanZoomCanvasRoot({
|
|
|
1673
1693
|
] });
|
|
1674
1694
|
}
|
|
1675
1695
|
var PanZoomCanvas = Object.assign(PanZoomCanvasRoot, { Skeleton: PanZoomCanvasSkeleton });
|
|
1696
|
+
function BoardSkeleton({ columns = 3, cards = 3, label = "Loading board" }) {
|
|
1697
|
+
return /* @__PURE__ */ jsx("div", { className: "cb-board", "aria-busy": "true", "aria-label": label, children: /* @__PURE__ */ jsx("div", { className: "cb-board__surface", children: Array.from({ length: columns }, (_, column) => /* @__PURE__ */ jsxs("section", { className: "cb-board__column", children: [
|
|
1698
|
+
/* @__PURE__ */ jsxs("header", { className: "cb-board__head", children: [
|
|
1699
|
+
/* @__PURE__ */ jsx("span", { className: "cb-board__ghost cb-board__ghost cb-board__ghost--name" }),
|
|
1700
|
+
/* @__PURE__ */ jsx("span", { className: "cb-board__ghost cb-board__ghost cb-board__ghost--count" })
|
|
1701
|
+
] }),
|
|
1702
|
+
/* @__PURE__ */ jsx("ol", { className: "cb-board__list", children: Array.from({ length: cards }, (_2, card) => /* @__PURE__ */ jsx("li", { className: "cb-board__card cb-board__ghost-card" }, card)) })
|
|
1703
|
+
] }, column)) }) });
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
// src/data/board/board.math.ts
|
|
1707
|
+
function locate(columns, cardId) {
|
|
1708
|
+
for (const column of columns) {
|
|
1709
|
+
const index = column.cards.findIndex((card) => card.id === cardId);
|
|
1710
|
+
if (index !== -1) return { columnId: column.id, index };
|
|
1711
|
+
}
|
|
1712
|
+
return null;
|
|
1713
|
+
}
|
|
1714
|
+
var columnOf = (columns, columnId) => columns.find((c) => c.id === columnId);
|
|
1715
|
+
function canPickUp(columns, cardId) {
|
|
1716
|
+
const at = locate(columns, cardId);
|
|
1717
|
+
if (!at) return false;
|
|
1718
|
+
return !columnOf(columns, at.columnId)?.cards.find((c) => c.id === cardId)?.disabled;
|
|
1719
|
+
}
|
|
1720
|
+
function refusalFor(columns, move) {
|
|
1721
|
+
if (!canPickUp(columns, move.cardId)) return "this card cannot be moved";
|
|
1722
|
+
const target = columnOf(columns, move.to.columnId);
|
|
1723
|
+
if (!target) return "that column is not on the board";
|
|
1724
|
+
if (target.accepts === false) return "that column does not take cards";
|
|
1725
|
+
return null;
|
|
1726
|
+
}
|
|
1727
|
+
function isNoop(move) {
|
|
1728
|
+
return move.from.columnId === move.to.columnId && move.from.index === move.to.index;
|
|
1729
|
+
}
|
|
1730
|
+
function applyMove(columns, move) {
|
|
1731
|
+
const card = columnOf(columns, move.from.columnId)?.cards[move.from.index];
|
|
1732
|
+
if (!card || card.id !== move.cardId) return [...columns];
|
|
1733
|
+
return columns.map((column) => {
|
|
1734
|
+
if (column.id === move.from.columnId && column.id === move.to.columnId) {
|
|
1735
|
+
const rest = column.cards.filter((_, i) => i !== move.from.index);
|
|
1736
|
+
rest.splice(clamp(move.to.index, rest.length), 0, card);
|
|
1737
|
+
return { ...column, cards: rest };
|
|
1738
|
+
}
|
|
1739
|
+
if (column.id === move.from.columnId) {
|
|
1740
|
+
return { ...column, cards: column.cards.filter((_, i) => i !== move.from.index) };
|
|
1741
|
+
}
|
|
1742
|
+
if (column.id === move.to.columnId) {
|
|
1743
|
+
const next = [...column.cards];
|
|
1744
|
+
next.splice(clamp(move.to.index, next.length), 0, card);
|
|
1745
|
+
return { ...column, cards: next };
|
|
1746
|
+
}
|
|
1747
|
+
return column;
|
|
1748
|
+
});
|
|
1749
|
+
}
|
|
1750
|
+
var clamp = (value, max) => Math.max(0, Math.min(value, max));
|
|
1751
|
+
function nextTarget(columns, held, direction) {
|
|
1752
|
+
const at = columns.findIndex((c) => c.id === held.columnId);
|
|
1753
|
+
const current = columns[at];
|
|
1754
|
+
if (!current) return null;
|
|
1755
|
+
if (direction === "up" || direction === "down") {
|
|
1756
|
+
const size = current.cards.length;
|
|
1757
|
+
const index = held.index + (direction === "down" ? 1 : -1);
|
|
1758
|
+
return index < 0 || index > size - 1 ? null : { columnId: held.columnId, index };
|
|
1759
|
+
}
|
|
1760
|
+
const step = direction === "right" ? 1 : -1;
|
|
1761
|
+
for (let i = at + step; i >= 0 && i < columns.length; i += step) {
|
|
1762
|
+
const column = columns[i];
|
|
1763
|
+
if (!column || column.accepts === false) continue;
|
|
1764
|
+
return { columnId: column.id, index: clamp(held.index, column.cards.length) };
|
|
1765
|
+
}
|
|
1766
|
+
return null;
|
|
1767
|
+
}
|
|
1768
|
+
function columnLoad(column) {
|
|
1769
|
+
const count = column.cards.length;
|
|
1770
|
+
return { count, over: typeof column.limit === "number" && count > column.limit };
|
|
1771
|
+
}
|
|
1772
|
+
var DEFAULTS = {
|
|
1773
|
+
pickUp: "Picked up. Use the arrow keys to move it, Space to drop, Escape to cancel.",
|
|
1774
|
+
dropped: (card, column) => `${card} moved to ${column}.`,
|
|
1775
|
+
cancelled: "Move cancelled.",
|
|
1776
|
+
refused: (reason) => `Move refused: ${reason}`,
|
|
1777
|
+
undo: "Undo",
|
|
1778
|
+
move: (card) => `Move ${card}`,
|
|
1779
|
+
undone: "Move undone.",
|
|
1780
|
+
lanes: "Column",
|
|
1781
|
+
over: (count, limit) => `${count} of ${limit}, over the limit`
|
|
1782
|
+
};
|
|
1783
|
+
var textOf = (node, fallback) => typeof node === "string" ? node : fallback;
|
|
1784
|
+
function BoardRoot({
|
|
1785
|
+
columns,
|
|
1786
|
+
onMove,
|
|
1787
|
+
layout = "auto",
|
|
1788
|
+
phoneQuery = "(max-width: 640px)",
|
|
1789
|
+
labels,
|
|
1790
|
+
motion: motion3 = true,
|
|
1791
|
+
handle = false,
|
|
1792
|
+
"aria-label": ariaLabel = "Board"
|
|
1793
|
+
}) {
|
|
1794
|
+
const text = { ...DEFAULTS, ...labels };
|
|
1795
|
+
const [optimistic, setOptimistic] = React.useState(null);
|
|
1796
|
+
const [held, setHeld] = React.useState(null);
|
|
1797
|
+
const [dragging, setDragging] = React.useState(null);
|
|
1798
|
+
const [announcement, setAnnouncement] = React.useState("");
|
|
1799
|
+
const [undoable, setUndoable] = React.useState(null);
|
|
1800
|
+
const [lane, setLane] = React.useState(0);
|
|
1801
|
+
const view = optimistic ?? columns;
|
|
1802
|
+
React.useEffect(() => setOptimistic(null), [columns]);
|
|
1803
|
+
const narrow = useNarrow(layout === "auto" ? phoneQuery : null);
|
|
1804
|
+
const lanes = layout === "lanes" || layout === "auto" && narrow;
|
|
1805
|
+
const nameOf = (columnId) => {
|
|
1806
|
+
const column = view.find((c) => c.id === columnId);
|
|
1807
|
+
return column?.label ?? textOf(column?.name, columnId);
|
|
1808
|
+
};
|
|
1809
|
+
const commit = React.useCallback(
|
|
1810
|
+
async (move, announceAs) => {
|
|
1811
|
+
if (isNoop(move)) return;
|
|
1812
|
+
const refusal = refusalFor(view, move);
|
|
1813
|
+
if (refusal) {
|
|
1814
|
+
setAnnouncement(text.refused(refusal));
|
|
1815
|
+
return;
|
|
1816
|
+
}
|
|
1817
|
+
const before = view;
|
|
1818
|
+
setOptimistic(applyMove(view, move));
|
|
1819
|
+
setAnnouncement(announceAs ?? text.dropped(titleOf(view, move.cardId), nameOf(move.to.columnId)));
|
|
1820
|
+
try {
|
|
1821
|
+
const result = await onMove(move);
|
|
1822
|
+
if (result && "refused" in result) {
|
|
1823
|
+
setOptimistic(before);
|
|
1824
|
+
setAnnouncement(text.refused(result.refused));
|
|
1825
|
+
return;
|
|
1826
|
+
}
|
|
1827
|
+
setUndoable({ cardId: move.cardId, from: move.to, to: move.from });
|
|
1828
|
+
} catch (e) {
|
|
1829
|
+
setOptimistic(before);
|
|
1830
|
+
setAnnouncement(text.refused(e instanceof Error ? e.message : String(e)));
|
|
1831
|
+
}
|
|
1832
|
+
},
|
|
1833
|
+
[view, onMove, text]
|
|
1834
|
+
);
|
|
1835
|
+
const onCardKeyDown = (event, cardId) => {
|
|
1836
|
+
const DIRECTIONS = {
|
|
1837
|
+
ArrowLeft: "left",
|
|
1838
|
+
ArrowRight: "right",
|
|
1839
|
+
ArrowUp: "up",
|
|
1840
|
+
ArrowDown: "down"
|
|
1841
|
+
};
|
|
1842
|
+
if (event.key === "Escape" && held) {
|
|
1843
|
+
event.preventDefault();
|
|
1844
|
+
setHeld(null);
|
|
1845
|
+
setAnnouncement(text.cancelled);
|
|
1846
|
+
return;
|
|
1847
|
+
}
|
|
1848
|
+
if (event.key === " " || event.key === "Enter") {
|
|
1849
|
+
event.preventDefault();
|
|
1850
|
+
if (!held) {
|
|
1851
|
+
if (!canPickUp(view, cardId)) {
|
|
1852
|
+
const card = view.flatMap((c) => c.cards).find((c) => c.id === cardId);
|
|
1853
|
+
setAnnouncement(text.refused(card?.disabledReason ?? "this card cannot be moved"));
|
|
1854
|
+
return;
|
|
1855
|
+
}
|
|
1856
|
+
const at = locate(view, cardId);
|
|
1857
|
+
if (at) {
|
|
1858
|
+
setHeld({ cardId, at });
|
|
1859
|
+
setAnnouncement(text.pickUp);
|
|
1860
|
+
}
|
|
1861
|
+
return;
|
|
1862
|
+
}
|
|
1863
|
+
const from = locate(view, held.cardId);
|
|
1864
|
+
if (from) void commit({ cardId: held.cardId, from, to: held.at });
|
|
1865
|
+
setHeld(null);
|
|
1866
|
+
return;
|
|
1867
|
+
}
|
|
1868
|
+
const direction = DIRECTIONS[event.key];
|
|
1869
|
+
if (!direction || !held) return;
|
|
1870
|
+
event.preventDefault();
|
|
1871
|
+
const target = nextTarget(view, held.at, direction);
|
|
1872
|
+
if (!target) return;
|
|
1873
|
+
setHeld({ ...held, at: target });
|
|
1874
|
+
setAnnouncement(`${nameOf(target.columnId)}, ${target.index + 1}`);
|
|
1875
|
+
};
|
|
1876
|
+
const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 6 } }));
|
|
1877
|
+
const onDragEnd = (event) => {
|
|
1878
|
+
setDragging(null);
|
|
1879
|
+
const cardId = String(event.active.id);
|
|
1880
|
+
const over = event.over ? String(event.over.id) : null;
|
|
1881
|
+
if (!over) return;
|
|
1882
|
+
const from = locate(view, cardId);
|
|
1883
|
+
if (!from) return;
|
|
1884
|
+
const onColumn = view.find((c) => c.id === over);
|
|
1885
|
+
const to = onColumn ? { columnId: onColumn.id, index: onColumn.cards.length } : locate(view, over);
|
|
1886
|
+
if (to) void commit({ cardId, from, to });
|
|
1887
|
+
};
|
|
1888
|
+
const laneColumn = view[Math.min(lane, Math.max(view.length - 1, 0))];
|
|
1889
|
+
const shown = lanes ? laneColumn ? [laneColumn] : [] : view;
|
|
1890
|
+
return /* @__PURE__ */ jsxs("div", { className: "cb-board", "data-motion": motion3 ? void 0 : "off", children: [
|
|
1891
|
+
lanes ? /* @__PURE__ */ jsx("div", { className: "cb-board__lanes", role: "tablist", "aria-label": text.lanes, children: view.map((column, i) => /* @__PURE__ */ jsxs(
|
|
1892
|
+
"button",
|
|
1893
|
+
{
|
|
1894
|
+
type: "button",
|
|
1895
|
+
role: "tab",
|
|
1896
|
+
"aria-selected": i === lane,
|
|
1897
|
+
className: "cb-board__lane",
|
|
1898
|
+
onClick: () => setLane(i),
|
|
1899
|
+
children: [
|
|
1900
|
+
column.name,
|
|
1901
|
+
" ",
|
|
1902
|
+
/* @__PURE__ */ jsx("span", { className: "cb-board__count", children: column.cards.length })
|
|
1903
|
+
]
|
|
1904
|
+
},
|
|
1905
|
+
column.id
|
|
1906
|
+
)) }) : null,
|
|
1907
|
+
/* @__PURE__ */ jsxs(
|
|
1908
|
+
DndContext,
|
|
1909
|
+
{
|
|
1910
|
+
sensors,
|
|
1911
|
+
collisionDetection: closestCorners,
|
|
1912
|
+
onDragStart: (e) => setDragging(String(e.active.id)),
|
|
1913
|
+
onDragCancel: () => setDragging(null),
|
|
1914
|
+
onDragEnd,
|
|
1915
|
+
children: [
|
|
1916
|
+
/* @__PURE__ */ jsx("div", { className: "cb-board__surface", role: "group", "aria-label": ariaLabel, "data-lanes": lanes ? "" : void 0, children: shown.map((column) => /* @__PURE__ */ jsx(Column, { column, held, onCardKeyDown, labels: text, handle }, column.id)) }),
|
|
1917
|
+
/* @__PURE__ */ jsx(DragOverlay, { children: dragging ? /* @__PURE__ */ jsx("div", { className: "cb-board__card cb-board__card--lift", children: cardTitle(view, dragging) }) : null })
|
|
1918
|
+
]
|
|
1919
|
+
}
|
|
1920
|
+
),
|
|
1921
|
+
undoable ? /* @__PURE__ */ jsx("div", { className: "cb-board__undo", children: /* @__PURE__ */ jsx(
|
|
1922
|
+
"button",
|
|
1923
|
+
{
|
|
1924
|
+
type: "button",
|
|
1925
|
+
onClick: () => {
|
|
1926
|
+
const move = undoable;
|
|
1927
|
+
setUndoable(null);
|
|
1928
|
+
void commit(move, text.undone);
|
|
1929
|
+
},
|
|
1930
|
+
children: text.undo
|
|
1931
|
+
}
|
|
1932
|
+
) }) : null,
|
|
1933
|
+
/* @__PURE__ */ jsx("div", { className: "cb-board__live", role: "status", "aria-live": "polite", children: announcement })
|
|
1934
|
+
] });
|
|
1935
|
+
}
|
|
1936
|
+
var cardTitle = (columns, cardId) => columns.flatMap((c) => c.cards).find((c) => c.id === cardId)?.title ?? null;
|
|
1937
|
+
var titleOf = (columns, cardId) => {
|
|
1938
|
+
const card = columns.flatMap((c) => c.cards).find((c) => c.id === cardId);
|
|
1939
|
+
return card?.label ?? textOf(card?.title, cardId);
|
|
1940
|
+
};
|
|
1941
|
+
function Column({
|
|
1942
|
+
column,
|
|
1943
|
+
held,
|
|
1944
|
+
onCardKeyDown,
|
|
1945
|
+
labels,
|
|
1946
|
+
handle
|
|
1947
|
+
}) {
|
|
1948
|
+
const { setNodeRef, isOver } = useDroppable({ id: column.id });
|
|
1949
|
+
const load = columnLoad(column);
|
|
1950
|
+
const holding = held?.at.columnId === column.id;
|
|
1951
|
+
return /* @__PURE__ */ jsxs(
|
|
1952
|
+
"section",
|
|
1953
|
+
{
|
|
1954
|
+
ref: setNodeRef,
|
|
1955
|
+
className: "cb-board__column",
|
|
1956
|
+
"data-over": isOver ? "" : void 0,
|
|
1957
|
+
"data-refuses": column.accepts === false ? "" : void 0,
|
|
1958
|
+
"aria-label": column.label ?? (typeof column.name === "string" ? column.name : void 0),
|
|
1959
|
+
children: [
|
|
1960
|
+
/* @__PURE__ */ jsxs("header", { className: "cb-board__head", children: [
|
|
1961
|
+
/* @__PURE__ */ jsx("span", { className: "cb-board__name", children: column.name }),
|
|
1962
|
+
/* @__PURE__ */ jsx("span", { className: "cb-board__count", "data-over-limit": load.over ? "" : void 0, children: typeof column.limit === "number" ? `${load.count}/${column.limit}` : load.count })
|
|
1963
|
+
] }),
|
|
1964
|
+
/* @__PURE__ */ jsx(SortableContext, { items: column.cards.map((c) => c.id), strategy: verticalListSortingStrategy, children: /* @__PURE__ */ jsxs("ol", { className: "cb-board__list", children: [
|
|
1965
|
+
column.cards.map((card, index) => /* @__PURE__ */ jsx(
|
|
1966
|
+
Card,
|
|
1967
|
+
{
|
|
1968
|
+
card,
|
|
1969
|
+
held: held?.cardId === card.id,
|
|
1970
|
+
marker: holding && held?.at.index === index,
|
|
1971
|
+
onKeyDown: onCardKeyDown,
|
|
1972
|
+
handle,
|
|
1973
|
+
labels
|
|
1974
|
+
},
|
|
1975
|
+
card.id
|
|
1976
|
+
)),
|
|
1977
|
+
holding && held.at.index >= column.cards.length ? /* @__PURE__ */ jsx("li", { className: "cb-board__marker", "aria-hidden": true }) : null
|
|
1978
|
+
] }) }),
|
|
1979
|
+
column.cards.length === 0 ? /* @__PURE__ */ jsx("div", { className: "cb-board__empty", children: column.empty ?? null }) : null,
|
|
1980
|
+
column.accepts === false && column.refusal ? /* @__PURE__ */ jsx("p", { className: "cb-board__refusal", children: column.refusal }) : null,
|
|
1981
|
+
load.over && typeof column.limit === "number" ? /* @__PURE__ */ jsx("p", { className: "cb-board__over", children: labels.over(load.count, column.limit) }) : null
|
|
1982
|
+
]
|
|
1983
|
+
}
|
|
1984
|
+
);
|
|
1985
|
+
}
|
|
1986
|
+
function Card({
|
|
1987
|
+
card,
|
|
1988
|
+
held,
|
|
1989
|
+
marker,
|
|
1990
|
+
onKeyDown,
|
|
1991
|
+
handle,
|
|
1992
|
+
labels
|
|
1993
|
+
}) {
|
|
1994
|
+
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
|
|
1995
|
+
id: card.id,
|
|
1996
|
+
disabled: card.disabled
|
|
1997
|
+
});
|
|
1998
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1999
|
+
marker ? /* @__PURE__ */ jsx("li", { className: "cb-board__marker", "aria-hidden": true }) : null,
|
|
2000
|
+
/* @__PURE__ */ jsxs(
|
|
2001
|
+
"li",
|
|
2002
|
+
{
|
|
2003
|
+
ref: setNodeRef,
|
|
2004
|
+
style: { transform: CSS.Transform.toString(transform), transition },
|
|
2005
|
+
className: "cb-board__card",
|
|
2006
|
+
"data-dragging": isDragging ? "" : void 0,
|
|
2007
|
+
"data-held": held ? "" : void 0,
|
|
2008
|
+
"data-disabled": card.disabled ? "" : void 0,
|
|
2009
|
+
"data-handle": handle ? "" : void 0,
|
|
2010
|
+
...handle ? {} : {
|
|
2011
|
+
...attributes,
|
|
2012
|
+
...card.disabled ? {} : listeners,
|
|
2013
|
+
tabIndex: 0,
|
|
2014
|
+
"aria-roledescription": "draggable card",
|
|
2015
|
+
"aria-disabled": card.disabled || void 0,
|
|
2016
|
+
onKeyDown: (event) => onKeyDown(event, card.id)
|
|
2017
|
+
},
|
|
2018
|
+
children: [
|
|
2019
|
+
handle ? /* @__PURE__ */ jsx(
|
|
2020
|
+
"button",
|
|
2021
|
+
{
|
|
2022
|
+
type: "button",
|
|
2023
|
+
className: "cb-board__handle",
|
|
2024
|
+
...attributes,
|
|
2025
|
+
...card.disabled ? {} : listeners,
|
|
2026
|
+
"aria-label": labels.move(card.label ?? (typeof card.title === "string" ? card.title : card.id)),
|
|
2027
|
+
"aria-roledescription": "drag handle",
|
|
2028
|
+
"aria-disabled": card.disabled || void 0,
|
|
2029
|
+
disabled: card.disabled,
|
|
2030
|
+
onKeyDown: (event) => onKeyDown(event, card.id),
|
|
2031
|
+
children: /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "\u283F" })
|
|
2032
|
+
}
|
|
2033
|
+
) : null,
|
|
2034
|
+
/* @__PURE__ */ jsx("div", { className: "cb-board__title", children: card.title }),
|
|
2035
|
+
card.meta ? /* @__PURE__ */ jsx("div", { className: "cb-board__meta", children: card.meta }) : null
|
|
2036
|
+
]
|
|
2037
|
+
}
|
|
2038
|
+
)
|
|
2039
|
+
] });
|
|
2040
|
+
}
|
|
2041
|
+
function useNarrow(query) {
|
|
2042
|
+
const [narrow, setNarrow] = React.useState(false);
|
|
2043
|
+
React.useEffect(() => {
|
|
2044
|
+
if (!query || typeof window === "undefined" || !window.matchMedia) return;
|
|
2045
|
+
const mql = window.matchMedia(query);
|
|
2046
|
+
const read = () => setNarrow(mql.matches);
|
|
2047
|
+
read();
|
|
2048
|
+
mql.addEventListener("change", read);
|
|
2049
|
+
return () => mql.removeEventListener("change", read);
|
|
2050
|
+
}, [query]);
|
|
2051
|
+
return narrow;
|
|
2052
|
+
}
|
|
2053
|
+
var Board = Object.assign(BoardRoot, { Skeleton: BoardSkeleton });
|
|
2054
|
+
|
|
2055
|
+
// src/data/time-series/time-series.chart.ts
|
|
2056
|
+
async function mountTimeSeries(host, shape, palette) {
|
|
2057
|
+
const { BaselineSeries, ColorType, LineSeries, LineStyle, createChart, createSeriesMarkers } = await import('lightweight-charts');
|
|
2058
|
+
const chart = createChart(host, {
|
|
2059
|
+
autoSize: true,
|
|
2060
|
+
/* A dashboard chart is read, not traded. Panning and zooming would let somebody scroll the data
|
|
2061
|
+
off the screen and leave them looking at a fragment with no way back, so the whole span is
|
|
2062
|
+
fitted and the viewport is fixed. */
|
|
2063
|
+
handleScroll: false,
|
|
2064
|
+
handleScale: false
|
|
2065
|
+
});
|
|
2066
|
+
const scale = shape.range ? () => ({ priceRange: { minValue: shape.range?.min ?? 0, maxValue: shape.range?.max ?? 0 } }) : void 0;
|
|
2067
|
+
const common = { priceLineVisible: false, lastValueVisible: false, autoscaleInfoProvider: scale };
|
|
2068
|
+
const drawn = shape.baseline ? [
|
|
2069
|
+
chart.addSeries(BaselineSeries, {
|
|
2070
|
+
...common,
|
|
2071
|
+
baseValue: { type: "price", price: shape.baseline.value },
|
|
2072
|
+
lineWidth: 2
|
|
2073
|
+
})
|
|
2074
|
+
] : shape.series.map(
|
|
2075
|
+
(series) => chart.addSeries(LineSeries, {
|
|
2076
|
+
...common,
|
|
2077
|
+
lineWidth: series.emphasis === "reference" ? 2 : 3,
|
|
2078
|
+
lineStyle: series.emphasis === "reference" ? LineStyle.Dashed : LineStyle.Solid
|
|
2079
|
+
})
|
|
2080
|
+
);
|
|
2081
|
+
const markedSeries = drawn.at(-1);
|
|
2082
|
+
const markers = markedSeries ? createSeriesMarkers(markedSeries, []) : null;
|
|
2083
|
+
let current = palette;
|
|
2084
|
+
const apply = (next) => {
|
|
2085
|
+
current = next;
|
|
2086
|
+
chart.applyOptions({
|
|
2087
|
+
layout: {
|
|
2088
|
+
background: { type: ColorType.Solid, color: next.background },
|
|
2089
|
+
textColor: next.text,
|
|
2090
|
+
fontFamily: next.font,
|
|
2091
|
+
/* Apache-2.0 asks that the NOTICE travel with the distribution, not that a logo be painted
|
|
2092
|
+
into a consumer's dashboard. The attribution lives in THIRD_PARTY_NOTICES.md. */
|
|
2093
|
+
attributionLogo: false
|
|
2094
|
+
},
|
|
2095
|
+
grid: { vertLines: { color: next.grid }, horzLines: { color: next.grid } },
|
|
2096
|
+
rightPriceScale: { borderColor: next.grid, scaleMargins: { top: 0.1, bottom: 0.08 } },
|
|
2097
|
+
timeScale: { borderColor: next.grid, fixLeftEdge: true, fixRightEdge: true },
|
|
2098
|
+
crosshair: { vertLine: { color: next.muted }, horzLine: { color: next.muted } },
|
|
2099
|
+
localization: { priceFormatter: shape.format }
|
|
2100
|
+
});
|
|
2101
|
+
drawn.forEach((series, index) => {
|
|
2102
|
+
if (shape.baseline) {
|
|
2103
|
+
series.applyOptions({
|
|
2104
|
+
topLineColor: next.above,
|
|
2105
|
+
topFillColor1: next.above,
|
|
2106
|
+
topFillColor2: next.background,
|
|
2107
|
+
bottomLineColor: next.below,
|
|
2108
|
+
bottomFillColor1: next.background,
|
|
2109
|
+
bottomFillColor2: next.below,
|
|
2110
|
+
crosshairMarkerBorderColor: next.background
|
|
2111
|
+
});
|
|
2112
|
+
return;
|
|
2113
|
+
}
|
|
2114
|
+
series.applyOptions({
|
|
2115
|
+
color: next.series[index] ?? next.text,
|
|
2116
|
+
crosshairMarkerBorderColor: next.background
|
|
2117
|
+
});
|
|
2118
|
+
});
|
|
2119
|
+
};
|
|
2120
|
+
apply(palette);
|
|
2121
|
+
return {
|
|
2122
|
+
setData(series) {
|
|
2123
|
+
drawn.forEach((drawnSeries, index) => {
|
|
2124
|
+
const points = series[index]?.points ?? [];
|
|
2125
|
+
drawnSeries.setData(points.map((point) => ({ time: point.day, value: point.value })));
|
|
2126
|
+
});
|
|
2127
|
+
chart.timeScale().fitContent();
|
|
2128
|
+
},
|
|
2129
|
+
mark(day, text) {
|
|
2130
|
+
markers?.setMarkers(day === null ? [] : [{
|
|
2131
|
+
time: day,
|
|
2132
|
+
position: "belowBar",
|
|
2133
|
+
shape: "arrowUp",
|
|
2134
|
+
color: current.series[current.series.length - 1] ?? current.text,
|
|
2135
|
+
text
|
|
2136
|
+
}]);
|
|
2137
|
+
},
|
|
2138
|
+
applyPalette: apply,
|
|
2139
|
+
destroy() {
|
|
2140
|
+
chart.remove();
|
|
2141
|
+
}
|
|
2142
|
+
};
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
// src/data/time-series/time-series.math.ts
|
|
2146
|
+
function asDay(value) {
|
|
2147
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) return null;
|
|
2148
|
+
const at = (/* @__PURE__ */ new Date(`${value}T00:00:00Z`)).getTime();
|
|
2149
|
+
if (Number.isNaN(at)) return null;
|
|
2150
|
+
return new Date(at).toISOString().startsWith(value) ? value : null;
|
|
2151
|
+
}
|
|
2152
|
+
function seriesPoints(points) {
|
|
2153
|
+
const byDay = /* @__PURE__ */ new Map();
|
|
2154
|
+
for (const point of points) {
|
|
2155
|
+
if (asDay(point.day) === null) continue;
|
|
2156
|
+
byDay.set(point.day, point.value);
|
|
2157
|
+
}
|
|
2158
|
+
return [...byDay].map(([day, value]) => ({ day, value })).sort((a, b) => a.day.localeCompare(b.day));
|
|
2159
|
+
}
|
|
2160
|
+
function valueOn(points, day) {
|
|
2161
|
+
const reached = seriesPoints(points).filter((point) => point.day <= day);
|
|
2162
|
+
const last = reached[reached.length - 1];
|
|
2163
|
+
return last ? last.value : null;
|
|
2164
|
+
}
|
|
2165
|
+
function alignRows(series) {
|
|
2166
|
+
const days = [...new Set(series.flatMap((one) => seriesPoints(one.points).map((point) => point.day)))].sort();
|
|
2167
|
+
return days.map((day) => {
|
|
2168
|
+
const values = {};
|
|
2169
|
+
for (const one of series) values[one.key] = valueOn(one.points, day);
|
|
2170
|
+
return { day, values };
|
|
2171
|
+
});
|
|
2172
|
+
}
|
|
2173
|
+
function seriesSpan(series) {
|
|
2174
|
+
const days = series.flatMap((one) => seriesPoints(one.points).map((point) => point.day)).sort();
|
|
2175
|
+
const from = days[0];
|
|
2176
|
+
const to = days[days.length - 1];
|
|
2177
|
+
return from && to ? { from, to } : null;
|
|
2178
|
+
}
|
|
2179
|
+
function nearestDay(days, day) {
|
|
2180
|
+
const known = days.filter((candidate) => asDay(candidate) !== null);
|
|
2181
|
+
if (known.length === 0 || asDay(day) === null) return null;
|
|
2182
|
+
const distance2 = (candidate) => Math.abs((/* @__PURE__ */ new Date(`${candidate}T00:00:00Z`)).getTime() - (/* @__PURE__ */ new Date(`${day}T00:00:00Z`)).getTime());
|
|
2183
|
+
return known.reduce((closest, candidate) => distance2(candidate) < distance2(closest) ? candidate : closest);
|
|
2184
|
+
}
|
|
2185
|
+
function round1(value) {
|
|
2186
|
+
return Math.round(value * 10) / 10;
|
|
2187
|
+
}
|
|
2188
|
+
function TimeSeriesChartSkeleton({
|
|
2189
|
+
height = 260,
|
|
2190
|
+
label = "Loading chart",
|
|
2191
|
+
className
|
|
2192
|
+
}) {
|
|
2193
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("cb-chart", "cb-chart--skeleton", className), role: "status", "aria-label": label, children: [
|
|
2194
|
+
/* @__PURE__ */ jsx("span", { className: "cb-chart__skeleton-head", "aria-hidden": "true" }),
|
|
2195
|
+
/* @__PURE__ */ jsx("span", { className: "cb-chart__skeleton-plot", style: { height }, "aria-hidden": "true" })
|
|
2196
|
+
] });
|
|
2197
|
+
}
|
|
2198
|
+
function TimeSeriesChart({
|
|
2199
|
+
label,
|
|
2200
|
+
series,
|
|
2201
|
+
format,
|
|
2202
|
+
range,
|
|
2203
|
+
baseline,
|
|
2204
|
+
mark,
|
|
2205
|
+
height = 260,
|
|
2206
|
+
emptyLabel = "Nothing has been reported yet.",
|
|
2207
|
+
tableLabel = "Readings by day",
|
|
2208
|
+
dayLabel = "Day",
|
|
2209
|
+
loading = false,
|
|
2210
|
+
className
|
|
2211
|
+
}) {
|
|
2212
|
+
const host = useRef(null);
|
|
2213
|
+
const [forcedColors, setForcedColors] = useState(false);
|
|
2214
|
+
const cleaned = useMemo(
|
|
2215
|
+
() => series.map((one) => ({ ...one, points: seriesPoints(one.points) })),
|
|
2216
|
+
[series]
|
|
2217
|
+
);
|
|
2218
|
+
const rows = useMemo(() => alignRows(cleaned), [cleaned]);
|
|
2219
|
+
const empty = rows.length === 0;
|
|
2220
|
+
const markDay = useMemo(
|
|
2221
|
+
() => mark ? nearestDay(rows.map((row) => row.day), mark.day) : null,
|
|
2222
|
+
[rows, mark]
|
|
2223
|
+
);
|
|
2224
|
+
useEffect(() => {
|
|
2225
|
+
const query = window.matchMedia("(forced-colors: active)");
|
|
2226
|
+
const read = () => setForcedColors(query.matches);
|
|
2227
|
+
read();
|
|
2228
|
+
query.addEventListener("change", read);
|
|
2229
|
+
return () => query.removeEventListener("change", read);
|
|
2230
|
+
}, []);
|
|
2231
|
+
const tokens = useMemo(() => cleaned.map((one) => one.colorToken), [cleaned]);
|
|
2232
|
+
useEffect(() => {
|
|
2233
|
+
const element = host.current;
|
|
2234
|
+
if (!element || empty || forcedColors || loading) return;
|
|
2235
|
+
let chart = null;
|
|
2236
|
+
let cancelled = false;
|
|
2237
|
+
let palette = readPalette(element, tokens);
|
|
2238
|
+
if (!palette) return;
|
|
2239
|
+
void mountTimeSeries(element, { series: cleaned, format, range, baseline }, palette).then((mounted) => {
|
|
2240
|
+
if (cancelled) {
|
|
2241
|
+
mounted.destroy();
|
|
2242
|
+
return;
|
|
2243
|
+
}
|
|
2244
|
+
chart = mounted;
|
|
2245
|
+
const current = readPalette(element, tokens) ?? palette;
|
|
2246
|
+
if (current) mounted.applyPalette(current);
|
|
2247
|
+
mounted.setData(cleaned);
|
|
2248
|
+
mounted.mark(markDay, mark?.label ?? "");
|
|
2249
|
+
});
|
|
2250
|
+
const stopWatchingTokens = watchTokens(() => {
|
|
2251
|
+
const next = readPalette(element, tokens);
|
|
2252
|
+
if (!next) return;
|
|
2253
|
+
palette = next;
|
|
2254
|
+
chart?.applyPalette(next);
|
|
2255
|
+
});
|
|
2256
|
+
return () => {
|
|
2257
|
+
cancelled = true;
|
|
2258
|
+
stopWatchingTokens();
|
|
2259
|
+
chart?.destroy();
|
|
2260
|
+
chart = null;
|
|
2261
|
+
};
|
|
2262
|
+
}, [cleaned, tokens, format, range, baseline, markDay, mark, empty, forcedColors, loading]);
|
|
2263
|
+
if (loading) return /* @__PURE__ */ jsx(TimeSeriesChartSkeleton, { height, className });
|
|
2264
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("cb-chart", className), "data-forced-colors": forcedColors || void 0, children: [
|
|
2265
|
+
empty ? /* @__PURE__ */ jsx("p", { className: "cb-chart__empty", style: { minHeight: height }, children: emptyLabel }) : null,
|
|
2266
|
+
empty || forcedColors ? null : /* @__PURE__ */ jsx("div", { ref: host, className: "cb-chart__canvas", style: { height }, role: "img", "aria-label": label }),
|
|
2267
|
+
empty ? null : /* @__PURE__ */ jsx("div", { className: "cb-chart__rows", children: /* @__PURE__ */ jsxs("table", { className: "cb-chart__table", children: [
|
|
2268
|
+
/* @__PURE__ */ jsx("caption", { children: tableLabel }),
|
|
2269
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
2270
|
+
/* @__PURE__ */ jsx("th", { scope: "col", children: dayLabel }),
|
|
2271
|
+
cleaned.map((one) => /* @__PURE__ */ jsx("th", { scope: "col", children: one.label }, one.key))
|
|
2272
|
+
] }) }),
|
|
2273
|
+
/* @__PURE__ */ jsx("tbody", { children: rows.map((row) => /* @__PURE__ */ jsxs("tr", { "data-marked": row.day === markDay || void 0, children: [
|
|
2274
|
+
/* @__PURE__ */ jsx("th", { scope: "row", children: row.day }),
|
|
2275
|
+
cleaned.map((one) => {
|
|
2276
|
+
const value = row.values[one.key];
|
|
2277
|
+
return /* @__PURE__ */ jsx("td", { children: value === null || value === void 0 ? "\u2014" : format(value) }, one.key);
|
|
2278
|
+
})
|
|
2279
|
+
] }, row.day)) })
|
|
2280
|
+
] }) })
|
|
2281
|
+
] });
|
|
2282
|
+
}
|
|
2283
|
+
function readPalette(host, seriesTokens) {
|
|
2284
|
+
const probe = createCssProbe(host);
|
|
2285
|
+
const text = probe.color("--cb-fg-muted");
|
|
2286
|
+
const muted = probe.color("--cb-fg-subtle");
|
|
2287
|
+
const grid = probe.color("--cb-border");
|
|
2288
|
+
const background = probe.color("--cb-surface");
|
|
2289
|
+
const above = probe.color("--cb-tone-success");
|
|
2290
|
+
const below = probe.color("--cb-tone-danger");
|
|
2291
|
+
const seriesColors = seriesTokens.map((token) => probe.color(token));
|
|
2292
|
+
probe.done();
|
|
2293
|
+
const font = getComputedStyle(host).fontFamily;
|
|
2294
|
+
if (!text || !muted || !grid || !background || !above || !below || !font) return null;
|
|
2295
|
+
if (seriesColors.some((colour) => colour === void 0)) return null;
|
|
2296
|
+
return {
|
|
2297
|
+
text,
|
|
2298
|
+
muted,
|
|
2299
|
+
grid,
|
|
2300
|
+
background,
|
|
2301
|
+
font,
|
|
2302
|
+
above,
|
|
2303
|
+
below,
|
|
2304
|
+
series: seriesColors.filter((colour) => colour !== void 0)
|
|
2305
|
+
};
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2308
|
+
// src/data/balance-curve/balance-curve.math.ts
|
|
2309
|
+
function balanceReading(points, threshold) {
|
|
2310
|
+
const ordered = seriesPoints(points);
|
|
2311
|
+
if (ordered.length === 0) {
|
|
2312
|
+
return { lowest: null, lowestDay: null, firstBelowDay: null, daysBelow: 0, closing: null };
|
|
2313
|
+
}
|
|
2314
|
+
let lowestPoint = ordered[0];
|
|
2315
|
+
for (const point of ordered) {
|
|
2316
|
+
if (lowestPoint === void 0 || point.value < lowestPoint.value) lowestPoint = point;
|
|
2317
|
+
}
|
|
2318
|
+
const below = ordered.filter((point) => point.value < threshold);
|
|
2319
|
+
return {
|
|
2320
|
+
lowest: lowestPoint?.value ?? null,
|
|
2321
|
+
lowestDay: lowestPoint?.day ?? null,
|
|
2322
|
+
firstBelowDay: below[0]?.day ?? null,
|
|
2323
|
+
daysBelow: below.length,
|
|
2324
|
+
closing: ordered.at(-1)?.value ?? null
|
|
2325
|
+
};
|
|
2326
|
+
}
|
|
2327
|
+
function BalanceCurveSkeleton({
|
|
2328
|
+
height = 260,
|
|
2329
|
+
label = "Loading balance",
|
|
2330
|
+
className
|
|
2331
|
+
}) {
|
|
2332
|
+
return /* @__PURE__ */ jsx(TimeSeriesChartSkeleton, { height, label, className });
|
|
2333
|
+
}
|
|
2334
|
+
function BalanceCurveRoot({
|
|
2335
|
+
label,
|
|
2336
|
+
balances,
|
|
2337
|
+
format,
|
|
2338
|
+
threshold = { value: 0 },
|
|
2339
|
+
height = 260,
|
|
2340
|
+
seriesLabel = "Balance",
|
|
2341
|
+
emptyLabel = "Nothing to project yet.",
|
|
2342
|
+
tableLabel = "Balance by day",
|
|
2343
|
+
belowLabel = (from, lowest, days) => `Below the line from ${from} \u2014 lowest ${lowest}, ${days} day(s) under.`,
|
|
2344
|
+
clearLabel = (lowest) => `Stays above the line. Lowest point ${lowest}.`,
|
|
2345
|
+
loading = false,
|
|
2346
|
+
className
|
|
2347
|
+
}) {
|
|
2348
|
+
const points = useMemo(() => seriesPoints(balances), [balances]);
|
|
2349
|
+
const reading = useMemo(() => balanceReading(points, threshold.value), [points, threshold.value]);
|
|
2350
|
+
const series = useMemo(
|
|
2351
|
+
() => [{ key: "balance", label: seriesLabel, points, emphasis: "primary", colorToken: "--cb-tone-brand" }],
|
|
2352
|
+
[points, seriesLabel]
|
|
2353
|
+
);
|
|
2354
|
+
if (loading) return /* @__PURE__ */ jsx(BalanceCurveSkeleton, { height, className });
|
|
2355
|
+
const breached = reading.firstBelowDay !== null;
|
|
2356
|
+
return /* @__PURE__ */ jsxs("figure", { className: cn("cb-balance-curve", className), children: [
|
|
2357
|
+
/* @__PURE__ */ jsxs("figcaption", { className: "cb-balance-curve__head", children: [
|
|
2358
|
+
/* @__PURE__ */ jsx("span", { className: "cb-balance-curve__label", children: label }),
|
|
2359
|
+
threshold.label ? /* @__PURE__ */ jsx("span", { className: "cb-balance-curve__threshold", children: threshold.label }) : null
|
|
2360
|
+
] }),
|
|
2361
|
+
reading.lowest === null ? null : /* @__PURE__ */ jsx("p", { className: "cb-balance-curve__reading", "data-state": breached ? "below" : "clear", children: breached && reading.firstBelowDay ? belowLabel(reading.firstBelowDay, format(reading.lowest), reading.daysBelow) : clearLabel(format(reading.lowest)) }),
|
|
2362
|
+
/* @__PURE__ */ jsx(
|
|
2363
|
+
TimeSeriesChart,
|
|
2364
|
+
{
|
|
2365
|
+
label,
|
|
2366
|
+
series,
|
|
2367
|
+
format,
|
|
2368
|
+
baseline: threshold,
|
|
2369
|
+
mark: reading.lowestDay ? { day: reading.lowestDay, label: format(reading.lowest ?? 0) } : void 0,
|
|
2370
|
+
height,
|
|
2371
|
+
emptyLabel,
|
|
2372
|
+
tableLabel
|
|
2373
|
+
}
|
|
2374
|
+
)
|
|
2375
|
+
] });
|
|
2376
|
+
}
|
|
2377
|
+
var BalanceCurve = Object.assign(BalanceCurveRoot, { Skeleton: BalanceCurveSkeleton });
|
|
2378
|
+
|
|
2379
|
+
// src/data/progress-curve/progress-curve.math.ts
|
|
2380
|
+
function toPoints(points) {
|
|
2381
|
+
return seriesPoints(points.map((point) => ({ day: point.day, value: point.percent })));
|
|
2382
|
+
}
|
|
2383
|
+
function readingOn(planned, actual, day) {
|
|
2384
|
+
const plannedPercent = valueOn(toPoints(planned), day);
|
|
2385
|
+
const actualPercent = valueOn(toPoints(actual), day);
|
|
2386
|
+
return {
|
|
2387
|
+
plannedPercent,
|
|
2388
|
+
actualPercent,
|
|
2389
|
+
gap: plannedPercent === null || actualPercent === null ? null : round1(actualPercent - plannedPercent)
|
|
2390
|
+
};
|
|
2391
|
+
}
|
|
2392
|
+
function curveRows(planned, actual) {
|
|
2393
|
+
return alignRows([
|
|
2394
|
+
{ key: "planned", points: toPoints(planned) },
|
|
2395
|
+
{ key: "actual", points: toPoints(actual) }
|
|
2396
|
+
]).map((row) => {
|
|
2397
|
+
const plannedPercent = row.values.planned ?? null;
|
|
2398
|
+
const actualPercent = row.values.actual ?? null;
|
|
2399
|
+
return {
|
|
2400
|
+
day: row.day,
|
|
2401
|
+
plannedPercent,
|
|
2402
|
+
actualPercent,
|
|
2403
|
+
gap: plannedPercent === null || actualPercent === null ? null : round1(actualPercent - plannedPercent)
|
|
2404
|
+
};
|
|
2405
|
+
});
|
|
2406
|
+
}
|
|
2407
|
+
function ProgressCurveSkeleton({
|
|
2408
|
+
height = 260,
|
|
2409
|
+
label = "Loading progress",
|
|
2410
|
+
className
|
|
2411
|
+
}) {
|
|
2412
|
+
return /* @__PURE__ */ jsx(TimeSeriesChartSkeleton, { height, label, className });
|
|
2413
|
+
}
|
|
2414
|
+
function ProgressCurveRoot({
|
|
2415
|
+
planned,
|
|
2416
|
+
actual,
|
|
2417
|
+
label,
|
|
2418
|
+
today,
|
|
2419
|
+
height = 260,
|
|
2420
|
+
plannedLabel = "Planned",
|
|
2421
|
+
actualLabel = "Actual",
|
|
2422
|
+
lastReportLabel = "Last report",
|
|
2423
|
+
emptyLabel = "Nothing has been reported yet.",
|
|
2424
|
+
tableLabel = "Progress by day",
|
|
2425
|
+
aheadLabel = "ahead of plan",
|
|
2426
|
+
behindLabel = "behind plan",
|
|
2427
|
+
onTrackLabel = "on plan",
|
|
2428
|
+
loading = false,
|
|
2429
|
+
className
|
|
2430
|
+
}) {
|
|
2431
|
+
const rows = useMemo(() => curveRows(planned, actual), [planned, actual]);
|
|
2432
|
+
const actualPoints = useMemo(() => toPoints(actual), [actual]);
|
|
2433
|
+
const lastReport = actualPoints.at(-1)?.day ?? null;
|
|
2434
|
+
const latest = useMemo(() => {
|
|
2435
|
+
if (!today) return rows[rows.length - 1];
|
|
2436
|
+
const day = asDay(today);
|
|
2437
|
+
return day === null ? rows[rows.length - 1] : { day, ...readingOn(planned, actual, day) };
|
|
2438
|
+
}, [today, rows, planned, actual]);
|
|
2439
|
+
const series = useMemo(
|
|
2440
|
+
() => [
|
|
2441
|
+
{ key: "planned", label: plannedLabel, points: toPoints(planned), emphasis: "reference", colorToken: "--cb-fg-subtle" },
|
|
2442
|
+
{ key: "actual", label: actualLabel, points: toPoints(actual), emphasis: "primary", colorToken: "--cb-tone-brand" }
|
|
2443
|
+
],
|
|
2444
|
+
[planned, actual, plannedLabel, actualLabel]
|
|
2445
|
+
);
|
|
2446
|
+
if (loading) return /* @__PURE__ */ jsx(ProgressCurveSkeleton, { height, className });
|
|
2447
|
+
return /* @__PURE__ */ jsxs("figure", { className: cn("cb-progress-curve", className), children: [
|
|
2448
|
+
/* @__PURE__ */ jsxs("figcaption", { className: "cb-progress-curve__head", children: [
|
|
2449
|
+
/* @__PURE__ */ jsx("span", { className: "cb-progress-curve__label", children: label }),
|
|
2450
|
+
/* @__PURE__ */ jsx("span", { className: "cb-chart-legend", children: series.map((one) => /* @__PURE__ */ jsx("span", { className: "cb-chart-legend__key", "data-emphasis": one.emphasis, "data-series": one.key, children: one.label }, one.key)) })
|
|
2451
|
+
] }),
|
|
2452
|
+
latest ? /* @__PURE__ */ jsxs("p", { className: "cb-progress-curve__reading", "data-state": stateOf(latest.gap), children: [
|
|
2453
|
+
/* @__PURE__ */ jsx("strong", { children: percent(latest.actualPercent) }),
|
|
2454
|
+
" ",
|
|
2455
|
+
actualLabel.toLowerCase(),
|
|
2456
|
+
" \xB7 ",
|
|
2457
|
+
percent(latest.plannedPercent),
|
|
2458
|
+
" ",
|
|
2459
|
+
plannedLabel.toLowerCase(),
|
|
2460
|
+
latest.gap === null ? null : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2461
|
+
" \u2014 ",
|
|
2462
|
+
Math.abs(latest.gap),
|
|
2463
|
+
" ",
|
|
2464
|
+
gapWord(latest.gap, { aheadLabel, behindLabel, onTrackLabel })
|
|
2465
|
+
] }),
|
|
2466
|
+
/* @__PURE__ */ jsxs("span", { className: "cb-progress-curve__on-day", children: [
|
|
2467
|
+
" (",
|
|
2468
|
+
latest.day,
|
|
2469
|
+
")"
|
|
2470
|
+
] })
|
|
2471
|
+
] }) : null,
|
|
2472
|
+
/* @__PURE__ */ jsx(
|
|
2473
|
+
TimeSeriesChart,
|
|
2474
|
+
{
|
|
2475
|
+
label,
|
|
2476
|
+
series,
|
|
2477
|
+
format: percentOf,
|
|
2478
|
+
range: { min: 0, max: 100 },
|
|
2479
|
+
mark: lastReport ? { day: lastReport, label: lastReportLabel } : void 0,
|
|
2480
|
+
height,
|
|
2481
|
+
emptyLabel,
|
|
2482
|
+
tableLabel
|
|
2483
|
+
}
|
|
2484
|
+
)
|
|
2485
|
+
] });
|
|
2486
|
+
}
|
|
2487
|
+
var ProgressCurve = Object.assign(ProgressCurveRoot, { Skeleton: ProgressCurveSkeleton });
|
|
2488
|
+
var percentOf = (value) => `${Math.round(value)}%`;
|
|
2489
|
+
function percent(value) {
|
|
2490
|
+
return value === null ? "\u2014" : `${round1(value)}%`;
|
|
2491
|
+
}
|
|
2492
|
+
function stateOf(gap) {
|
|
2493
|
+
if (gap === null) return "unknown";
|
|
2494
|
+
if (gap < 0) return "behind";
|
|
2495
|
+
if (gap > 0) return "ahead";
|
|
2496
|
+
return "on-plan";
|
|
2497
|
+
}
|
|
2498
|
+
function gapWord(gap, words) {
|
|
2499
|
+
if (gap < 0) return words.behindLabel;
|
|
2500
|
+
if (gap > 0) return words.aheadLabel;
|
|
2501
|
+
return words.onTrackLabel;
|
|
2502
|
+
}
|
|
1676
2503
|
function DiagramNodeView({ data, selected }) {
|
|
1677
2504
|
return /* @__PURE__ */ jsxs(
|
|
1678
2505
|
"div",
|
|
@@ -1980,4 +2807,4 @@ function DiagramEditorRoot(props) {
|
|
|
1980
2807
|
}
|
|
1981
2808
|
var DiagramEditor = Object.assign(DiagramEditorRoot, { Skeleton: DiagramSkeleton });
|
|
1982
2809
|
|
|
1983
|
-
export { CeebeeAntStyleProvider, Checklist, CommandPalette, DEFAULT_LABELS, Diagram, DiagramEditor, LabelsProvider, Modal, MotionProvider, PanZoomCanvas, Reveal, Sidebar, Stagger, StickerGroup, ThemeBridge, ThemeProvider, ToastProvider, TopBar, createCeebeeAntStyleCache, extractCeebeeAntStyles, filterCommands, groupCommands, rankCommand, useLabels, useMotionSettings, useTheme, useToast };
|
|
2810
|
+
export { BalanceCurve, BalanceCurveSkeleton, Board, CeebeeAntStyleProvider, Checklist, CommandPalette, DEFAULT_LABELS, Diagram, DiagramEditor, LabelsProvider, Modal, MotionProvider, PanZoomCanvas, ProgressCurve, ProgressCurveSkeleton, Reveal, Sidebar, Stagger, StickerGroup, ThemeBridge, ThemeProvider, TimeSeriesChart, TimeSeriesChartSkeleton, ToastProvider, TopBar, alignRows, asDay, balanceReading, createCeebeeAntStyleCache, curveRows, extractCeebeeAntStyles, filterCommands, groupCommands, nearestDay, rankCommand, readingOn, round1, seriesPoints, seriesSpan, toPoints, useLabels, useMotionSettings, useTheme, useToast, valueOn };
|