@arbidocs/blocks 0.3.177 → 0.3.179
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/{chunk-6HGMAQLB.cjs → chunk-DBAUZD7A.cjs} +448 -113
- package/dist/chunk-DBAUZD7A.cjs.map +1 -0
- package/dist/{chunk-27Q4AST5.js → chunk-FZ2S6VAZ.js} +449 -116
- package/dist/chunk-FZ2S6VAZ.js.map +1 -0
- package/dist/{index-er1L8kkn.d.cts → index-DduoEosi.d.cts} +65 -2
- package/dist/{index-er1L8kkn.d.ts → index-DduoEosi.d.ts} +65 -2
- package/dist/index.cjs +258 -178
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +64 -9
- package/dist/index.d.ts +64 -9
- package/dist/index.js +110 -40
- package/dist/index.js.map +1 -1
- package/dist/site/index.cjs +61 -61
- package/dist/site/index.d.cts +1 -1
- package/dist/site/index.d.ts +1 -1
- package/dist/site/index.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-27Q4AST5.js.map +0 -1
- package/dist/chunk-6HGMAQLB.cjs.map +0 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { create } from 'zustand';
|
|
2
2
|
import { persist } from 'zustand/middleware';
|
|
3
|
-
import { createContext, useState, useEffect, useMemo, useContext } from 'react';
|
|
4
|
-
import { RotateCcw, Palette, Calendar, X, CalendarDays, GanttChartSquare, ImageIcon, ArrowRight, Quote, ChevronDown, XCircle, AlertTriangle, CheckCircle2, Info, Check, Save } from 'lucide-react';
|
|
3
|
+
import { createContext, useState, useEffect, useMemo, useRef, useCallback, useContext } from 'react';
|
|
4
|
+
import { RotateCcw, Palette, ChevronLeft, ChevronRight, ZoomOut, ZoomIn, Calendar, X, CalendarDays, GanttChartSquare, ImageIcon, ArrowRight, Quote, ChevronDown, XCircle, AlertTriangle, CheckCircle2, Info, Check, Save } from 'lucide-react';
|
|
5
5
|
import { Input, Card, CardHeader, CardTitle, CardContent, cn, Button, Avatar, AvatarImage, AvatarFallback, AiMarkdown } from '@arbidocs/react/ui';
|
|
6
6
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
7
|
+
import { createPortal } from 'react-dom';
|
|
7
8
|
|
|
8
9
|
// src/theme/tokens.ts
|
|
9
10
|
var THEME_STYLE_ID = "arbi-theme-vars";
|
|
@@ -676,20 +677,62 @@ function formatSpan(startIso, endIso, timezone) {
|
|
|
676
677
|
return `${day(start)} \u2013 ${day(end)}`;
|
|
677
678
|
}
|
|
678
679
|
var DEFAULT_MAX_LANES = 3;
|
|
679
|
-
var MAX_WEEKS =
|
|
680
|
-
function isPrecise(item) {
|
|
681
|
-
if (!item.start || !item.end) return false;
|
|
682
|
-
return item.start.slice(0, 10) === item.end.slice(0, 10);
|
|
683
|
-
}
|
|
680
|
+
var MAX_WEEKS = 2600;
|
|
684
681
|
function TimelineCalendar({
|
|
685
682
|
items,
|
|
686
683
|
onSelect,
|
|
684
|
+
selectedIds,
|
|
687
685
|
maxLanes = DEFAULT_MAX_LANES,
|
|
686
|
+
navigatorContainer,
|
|
687
|
+
navigatorTotal,
|
|
688
688
|
emptyState,
|
|
689
689
|
className,
|
|
690
690
|
testId = "timeline-calendar"
|
|
691
691
|
}) {
|
|
692
692
|
const dated = useMemo(() => items.filter((i) => i.start), [items]);
|
|
693
|
+
const sortedItems = useMemo(
|
|
694
|
+
() => [...dated].sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime()),
|
|
695
|
+
[dated]
|
|
696
|
+
);
|
|
697
|
+
const indexById = useMemo(() => new Map(sortedItems.map((it, i) => [it.id, i])), [sortedItems]);
|
|
698
|
+
const [activeIndex, setActiveIndex] = useState(null);
|
|
699
|
+
const scrollRef = useRef(null);
|
|
700
|
+
const select = useCallback(
|
|
701
|
+
(index) => {
|
|
702
|
+
const clamped = Math.max(0, Math.min(sortedItems.length - 1, index));
|
|
703
|
+
setActiveIndex(clamped);
|
|
704
|
+
onSelect?.(sortedItems[clamped]);
|
|
705
|
+
return clamped;
|
|
706
|
+
},
|
|
707
|
+
[onSelect, sortedItems]
|
|
708
|
+
);
|
|
709
|
+
const scrollToItem = useCallback(
|
|
710
|
+
(id, behavior) => {
|
|
711
|
+
scrollRef.current?.querySelector(`[data-testid="${testId}-entry-${id}"]`)?.scrollIntoView({ block: "center", behavior });
|
|
712
|
+
},
|
|
713
|
+
[testId]
|
|
714
|
+
);
|
|
715
|
+
const lastNavScrollRef = useRef(null);
|
|
716
|
+
const goTo = useCallback(
|
|
717
|
+
(index) => {
|
|
718
|
+
const target = sortedItems[select(index)];
|
|
719
|
+
if (target) {
|
|
720
|
+
lastNavScrollRef.current = target.id;
|
|
721
|
+
scrollToItem(target.id, "smooth");
|
|
722
|
+
}
|
|
723
|
+
},
|
|
724
|
+
[select, sortedItems, scrollToItem]
|
|
725
|
+
);
|
|
726
|
+
const selectedKey = (selectedIds ?? []).join("\0");
|
|
727
|
+
useEffect(() => {
|
|
728
|
+
const id = selectedKey ? selectedKey.split("\0")[0] : null;
|
|
729
|
+
if (!id) return;
|
|
730
|
+
if (lastNavScrollRef.current === id) {
|
|
731
|
+
lastNavScrollRef.current = null;
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
scrollToItem(id, "auto");
|
|
735
|
+
}, [selectedKey, scrollToItem]);
|
|
693
736
|
const weeks = useMemo(() => {
|
|
694
737
|
if (dated.length === 0) return [];
|
|
695
738
|
const times = dated.flatMap((i) => [
|
|
@@ -715,22 +758,32 @@ function TimelineCalendar({
|
|
|
715
758
|
return out;
|
|
716
759
|
}, [dated]);
|
|
717
760
|
const segmentsByWeek = useMemo(() => {
|
|
718
|
-
|
|
761
|
+
if (weeks.length === 0) return [];
|
|
762
|
+
const gridStartMs = weeks[0][0].getTime();
|
|
763
|
+
const WEEK_MS2 = 7 * DAY_MS;
|
|
764
|
+
const ranged = dated.map((item) => ({
|
|
765
|
+
item,
|
|
766
|
+
start: utcDay(new Date(item.start)),
|
|
767
|
+
end: utcDay(new Date(item.end ?? item.start))
|
|
768
|
+
}));
|
|
769
|
+
ranged.sort((a, b) => {
|
|
770
|
+
const lenA = b.end.getTime() - b.start.getTime();
|
|
771
|
+
const lenB = a.end.getTime() - a.start.getTime();
|
|
772
|
+
return lenA - lenB || a.start.getTime() - b.start.getTime();
|
|
773
|
+
});
|
|
774
|
+
const buckets = weeks.map(() => []);
|
|
775
|
+
for (const r of ranged) {
|
|
776
|
+
const from = Math.max(0, Math.floor((r.start.getTime() - gridStartMs) / WEEK_MS2));
|
|
777
|
+
const to = Math.min(weeks.length - 1, Math.floor((r.end.getTime() - gridStartMs) / WEEK_MS2));
|
|
778
|
+
for (let w = from; w <= to; w++) buckets[w].push(r);
|
|
779
|
+
}
|
|
780
|
+
return weeks.map((week, wi) => {
|
|
719
781
|
const weekStart = week[0];
|
|
720
782
|
const weekEnd = week[6];
|
|
721
|
-
const overlapping = dated.map((item) => ({
|
|
722
|
-
item,
|
|
723
|
-
start: utcDay(new Date(item.start)),
|
|
724
|
-
end: utcDay(new Date(item.end ?? item.start))
|
|
725
|
-
})).filter(({ start, end }) => start <= weekEnd && end >= weekStart).sort((a, b) => {
|
|
726
|
-
const lenA = b.end.getTime() - b.start.getTime();
|
|
727
|
-
const lenB = a.end.getTime() - a.start.getTime();
|
|
728
|
-
return lenA - lenB || a.start.getTime() - b.start.getTime();
|
|
729
|
-
});
|
|
730
783
|
const laneEnds = [];
|
|
731
784
|
const segments = [];
|
|
732
785
|
let overflow = 0;
|
|
733
|
-
for (const { item, start, end } of
|
|
786
|
+
for (const { item, start, end } of buckets[wi]) {
|
|
734
787
|
const from = start < weekStart ? weekStart : start;
|
|
735
788
|
const to = end > weekEnd ? weekEnd : end;
|
|
736
789
|
const startCol = Math.round((from.getTime() - weekStart.getTime()) / DAY_MS);
|
|
@@ -757,9 +810,52 @@ function TimelineCalendar({
|
|
|
757
810
|
if (weeks.length === 0 && emptyState) {
|
|
758
811
|
return /* @__PURE__ */ jsx(Fragment, { children: emptyState });
|
|
759
812
|
}
|
|
813
|
+
const selectedSet = new Set(selectedIds ?? []);
|
|
814
|
+
const selectedIndex = sortedItems.findIndex((i) => selectedSet.has(i.id));
|
|
815
|
+
const currentIndex = selectedIndex >= 0 ? selectedIndex : activeIndex;
|
|
816
|
+
const navigator = /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-0.5", "data-testid": `${testId}-nav`, children: [
|
|
817
|
+
/* @__PURE__ */ jsx(
|
|
818
|
+
"button",
|
|
819
|
+
{
|
|
820
|
+
type: "button",
|
|
821
|
+
"aria-label": "Previous event",
|
|
822
|
+
"data-testid": `${testId}-nav-prev`,
|
|
823
|
+
disabled: sortedItems.length === 0 || currentIndex === null || currentIndex <= 0,
|
|
824
|
+
onClick: () => goTo((currentIndex ?? 0) - 1),
|
|
825
|
+
className: "rounded p-0.5 text-muted-foreground enabled:hover:bg-accent enabled:hover:text-foreground disabled:opacity-30",
|
|
826
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
|
|
827
|
+
}
|
|
828
|
+
),
|
|
829
|
+
/* @__PURE__ */ jsxs(
|
|
830
|
+
"span",
|
|
831
|
+
{
|
|
832
|
+
className: "tabular-nums text-sm font-light text-muted-foreground",
|
|
833
|
+
"data-testid": `${testId}-nav-position`,
|
|
834
|
+
children: [
|
|
835
|
+
currentIndex !== null ? currentIndex + 1 : "\u2013",
|
|
836
|
+
" /",
|
|
837
|
+
" ",
|
|
838
|
+
navigatorTotal ?? (sortedItems.length > 0 ? sortedItems.length : "\u2013")
|
|
839
|
+
]
|
|
840
|
+
}
|
|
841
|
+
),
|
|
842
|
+
/* @__PURE__ */ jsx(
|
|
843
|
+
"button",
|
|
844
|
+
{
|
|
845
|
+
type: "button",
|
|
846
|
+
"aria-label": "Next event",
|
|
847
|
+
"data-testid": `${testId}-nav-next`,
|
|
848
|
+
disabled: sortedItems.length === 0 || currentIndex !== null && currentIndex >= sortedItems.length - 1,
|
|
849
|
+
onClick: () => goTo(currentIndex === null ? 0 : currentIndex + 1),
|
|
850
|
+
className: "rounded p-0.5 text-muted-foreground enabled:hover:bg-accent enabled:hover:text-foreground disabled:opacity-30",
|
|
851
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
|
|
852
|
+
}
|
|
853
|
+
)
|
|
854
|
+
] });
|
|
760
855
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col h-full", className), "data-testid": testId, children: [
|
|
856
|
+
navigatorContainer === void 0 ? navigator && /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center justify-end border-b border-border px-2 py-1", children: navigator }) : navigatorContainer && navigator && createPortal(navigator, navigatorContainer),
|
|
761
857
|
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 border-b border-border bg-background", children: WEEKDAY_NAMES.map((d) => /* @__PURE__ */ jsx("div", { className: "px-2 py-1 text-[11px] text-muted-foreground text-center", children: d }, d)) }),
|
|
762
|
-
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-auto", "data-testid": `${testId}-scroll`, children: weeks.map((week, wi) => {
|
|
858
|
+
/* @__PURE__ */ jsx("div", { ref: scrollRef, className: "flex-1 overflow-auto", "data-testid": `${testId}-scroll`, children: weeks.map((week, wi) => {
|
|
763
859
|
const { segments, overflow } = segmentsByWeek[wi];
|
|
764
860
|
const monthStartDay = week.find((d) => d.getUTCDate() === 1);
|
|
765
861
|
const marker = monthStartDay ?? (wi === 0 ? week[0] : void 0);
|
|
@@ -794,13 +890,13 @@ function TimelineCalendar({
|
|
|
794
890
|
"div",
|
|
795
891
|
{
|
|
796
892
|
className: "grid grid-cols-7 gap-y-0.5 px-0.5 pb-1 bg-muted/40",
|
|
797
|
-
style: { gridAutoRows: "
|
|
893
|
+
style: { gridAutoRows: "18px", minHeight: 62 },
|
|
798
894
|
children: [
|
|
799
895
|
segments.map((seg) => /* @__PURE__ */ jsx(
|
|
800
896
|
"button",
|
|
801
897
|
{
|
|
802
898
|
type: "button",
|
|
803
|
-
onClick: () =>
|
|
899
|
+
onClick: () => select(indexById.get(seg.item.id) ?? 0),
|
|
804
900
|
title: `${formatSpan(seg.item.start, seg.item.end)} \u2014 ${seg.item.label}`,
|
|
805
901
|
"data-testid": `${testId}-entry-${seg.item.id}`,
|
|
806
902
|
style: {
|
|
@@ -808,8 +904,13 @@ function TimelineCalendar({
|
|
|
808
904
|
gridRow: seg.lane + 1
|
|
809
905
|
},
|
|
810
906
|
className: cn(
|
|
811
|
-
|
|
812
|
-
|
|
907
|
+
// One line per event (truncated), so several events stack
|
|
908
|
+
// compactly on the same day instead of a few tall bars
|
|
909
|
+
// pushing the rest into a "+n more". Every event is the
|
|
910
|
+
// SAME neutral colour; the one open in the composer is
|
|
911
|
+
// marked by the caller (a ring), not by a different fill.
|
|
912
|
+
"block h-full w-full truncate text-left text-[10px] leading-[14px] py-[1px] px-1",
|
|
913
|
+
"bg-muted text-muted-foreground",
|
|
813
914
|
seg.isStart && "rounded-l",
|
|
814
915
|
seg.isEnd && "rounded-r"
|
|
815
916
|
),
|
|
@@ -837,38 +938,152 @@ function TimelineCalendar({
|
|
|
837
938
|
}) })
|
|
838
939
|
] });
|
|
839
940
|
}
|
|
840
|
-
var
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
941
|
+
var TIMELINE_ZOOM = { MIN: 1, MAX: 10, STEP: 0.25 };
|
|
942
|
+
function TimelineZoomControl({
|
|
943
|
+
zoom,
|
|
944
|
+
onZoomChange,
|
|
945
|
+
className,
|
|
946
|
+
testId = "timeline-gantt"
|
|
947
|
+
}) {
|
|
948
|
+
return /* @__PURE__ */ jsxs(
|
|
949
|
+
"div",
|
|
950
|
+
{
|
|
951
|
+
className: cn(
|
|
952
|
+
"flex items-center gap-1.5 rounded-md border border-border bg-background px-2 py-1",
|
|
953
|
+
className
|
|
954
|
+
),
|
|
955
|
+
"data-testid": `${testId}-zoom`,
|
|
956
|
+
children: [
|
|
957
|
+
/* @__PURE__ */ jsx(ZoomOut, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground", "aria-hidden": true }),
|
|
958
|
+
/* @__PURE__ */ jsx(
|
|
959
|
+
"input",
|
|
960
|
+
{
|
|
961
|
+
type: "range",
|
|
962
|
+
"aria-label": "Timeline zoom",
|
|
963
|
+
"data-testid": `${testId}-zoom-slider`,
|
|
964
|
+
min: TIMELINE_ZOOM.MIN,
|
|
965
|
+
max: TIMELINE_ZOOM.MAX,
|
|
966
|
+
step: TIMELINE_ZOOM.STEP,
|
|
967
|
+
value: zoom,
|
|
968
|
+
onChange: (e) => onZoomChange(Number(e.target.value)),
|
|
969
|
+
className: "h-1 w-24 cursor-pointer accent-primary"
|
|
970
|
+
}
|
|
971
|
+
),
|
|
972
|
+
/* @__PURE__ */ jsx(ZoomIn, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground", "aria-hidden": true }),
|
|
973
|
+
/* @__PURE__ */ jsxs("span", { className: "w-11 shrink-0 text-right text-[11px] tabular-nums text-muted-foreground", children: [
|
|
974
|
+
Math.round(zoom * 100),
|
|
975
|
+
"%"
|
|
976
|
+
] })
|
|
977
|
+
]
|
|
978
|
+
}
|
|
979
|
+
);
|
|
980
|
+
}
|
|
981
|
+
var NEUTRAL_BAR = "#94a3b8";
|
|
982
|
+
function barBackground(colors) {
|
|
983
|
+
const c = (colors ?? []).filter(Boolean).slice(0, 3);
|
|
984
|
+
if (c.length === 0) return NEUTRAL_BAR;
|
|
985
|
+
if (c.length === 1) return c[0];
|
|
986
|
+
const step = 100 / c.length;
|
|
987
|
+
const stops = c.map((col, i) => `${col} ${i * step}% ${(i + 1) * step}%`).join(", ");
|
|
988
|
+
return `linear-gradient(180deg, ${stops})`;
|
|
989
|
+
}
|
|
857
990
|
var WEEK_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
858
|
-
function formatAxisDate(d) {
|
|
859
|
-
|
|
991
|
+
function formatAxisDate(d, intervalDays) {
|
|
992
|
+
if (intervalDays < 45) {
|
|
993
|
+
return d.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
|
|
994
|
+
}
|
|
995
|
+
if (intervalDays < 365 * 2) {
|
|
996
|
+
return d.toLocaleDateString("en-US", { month: "short", year: "numeric" });
|
|
997
|
+
}
|
|
998
|
+
return d.toLocaleDateString("en-US", { year: "numeric" });
|
|
860
999
|
}
|
|
861
1000
|
function TimelineGantt({
|
|
862
1001
|
items,
|
|
863
1002
|
onSelect,
|
|
1003
|
+
selectedIds,
|
|
864
1004
|
renderDetail,
|
|
865
1005
|
labelWidth = 240,
|
|
1006
|
+
onLabelWidthChange,
|
|
1007
|
+
minLabelWidth = 120,
|
|
1008
|
+
maxLabelWidth = 600,
|
|
1009
|
+
zoom: zoomProp,
|
|
1010
|
+
onZoomChange,
|
|
1011
|
+
navigatorContainer,
|
|
1012
|
+
navigatorTotal,
|
|
866
1013
|
emptyMessage = "Nothing dated yet.",
|
|
867
1014
|
className,
|
|
868
1015
|
testId = "timeline-gantt"
|
|
869
1016
|
}) {
|
|
870
1017
|
const [activeIndex, setActiveIndex] = useState(null);
|
|
1018
|
+
const [internalZoom, setInternalZoom] = useState(1);
|
|
1019
|
+
const zoomControlled = zoomProp != null && onZoomChange != null;
|
|
1020
|
+
const zoomValue = zoomControlled ? zoomProp : internalZoom;
|
|
1021
|
+
const setZoomValue = zoomControlled ? onZoomChange : setInternalZoom;
|
|
1022
|
+
const dragRef = useRef(null);
|
|
1023
|
+
const chartRef = useRef(null);
|
|
1024
|
+
const gutterBodyRef = useRef(null);
|
|
1025
|
+
const startResize = (e) => {
|
|
1026
|
+
if (!onLabelWidthChange) return;
|
|
1027
|
+
e.preventDefault();
|
|
1028
|
+
dragRef.current = { startX: e.clientX, startWidth: labelWidth };
|
|
1029
|
+
const onMove = (ev) => {
|
|
1030
|
+
const drag = dragRef.current;
|
|
1031
|
+
if (!drag) return;
|
|
1032
|
+
const next = Math.min(
|
|
1033
|
+
maxLabelWidth,
|
|
1034
|
+
Math.max(minLabelWidth, drag.startWidth + (ev.clientX - drag.startX))
|
|
1035
|
+
);
|
|
1036
|
+
onLabelWidthChange(next);
|
|
1037
|
+
};
|
|
1038
|
+
const onUp = () => {
|
|
1039
|
+
dragRef.current = null;
|
|
1040
|
+
window.removeEventListener("mousemove", onMove);
|
|
1041
|
+
window.removeEventListener("mouseup", onUp);
|
|
1042
|
+
};
|
|
1043
|
+
window.addEventListener("mousemove", onMove);
|
|
1044
|
+
window.addEventListener("mouseup", onUp);
|
|
1045
|
+
};
|
|
871
1046
|
const rows = items.filter((i) => parseIso2(i.start) !== null).slice().sort((a, b) => parseIso2(a.start).getTime() - parseIso2(b.start).getTime());
|
|
1047
|
+
const select = (index) => {
|
|
1048
|
+
setActiveIndex(index);
|
|
1049
|
+
onSelect?.(index === null ? null : rows[index]);
|
|
1050
|
+
};
|
|
1051
|
+
const selectedSet = new Set(selectedIds ?? []);
|
|
1052
|
+
const selectedRowIndex = rows.findIndex((r) => selectedSet.has(r.id));
|
|
1053
|
+
const currentIndex = selectedRowIndex >= 0 ? selectedRowIndex : activeIndex;
|
|
1054
|
+
const goTo = (index) => select(Math.max(0, Math.min(rows.length - 1, index)));
|
|
1055
|
+
const navigator = /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-0.5 text-sm font-light", "data-testid": `${testId}-nav`, children: [
|
|
1056
|
+
/* @__PURE__ */ jsx(
|
|
1057
|
+
"button",
|
|
1058
|
+
{
|
|
1059
|
+
type: "button",
|
|
1060
|
+
"aria-label": "Previous event",
|
|
1061
|
+
"data-testid": `${testId}-nav-prev`,
|
|
1062
|
+
disabled: rows.length === 0 || currentIndex === null || currentIndex <= 0,
|
|
1063
|
+
onClick: () => goTo((currentIndex ?? 0) - 1),
|
|
1064
|
+
className: "rounded p-0.5 text-muted-foreground enabled:hover:bg-accent enabled:hover:text-foreground disabled:opacity-30",
|
|
1065
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
|
|
1066
|
+
}
|
|
1067
|
+
),
|
|
1068
|
+
/* @__PURE__ */ jsxs("span", { className: "tabular-nums text-muted-foreground", "data-testid": `${testId}-nav-position`, children: [
|
|
1069
|
+
currentIndex !== null ? currentIndex + 1 : "\u2013",
|
|
1070
|
+
" /",
|
|
1071
|
+
" ",
|
|
1072
|
+
navigatorTotal ?? (rows.length > 0 ? rows.length : "\u2013")
|
|
1073
|
+
] }),
|
|
1074
|
+
/* @__PURE__ */ jsx(
|
|
1075
|
+
"button",
|
|
1076
|
+
{
|
|
1077
|
+
type: "button",
|
|
1078
|
+
"aria-label": "Next event",
|
|
1079
|
+
"data-testid": `${testId}-nav-next`,
|
|
1080
|
+
disabled: rows.length === 0 || currentIndex !== null && currentIndex >= rows.length - 1,
|
|
1081
|
+
onClick: () => goTo(currentIndex === null ? 0 : currentIndex + 1),
|
|
1082
|
+
className: "rounded p-0.5 text-muted-foreground enabled:hover:bg-accent enabled:hover:text-foreground disabled:opacity-30",
|
|
1083
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
|
|
1084
|
+
}
|
|
1085
|
+
)
|
|
1086
|
+
] });
|
|
872
1087
|
if (rows.length === 0) {
|
|
873
1088
|
return /* @__PURE__ */ jsxs(
|
|
874
1089
|
"div",
|
|
@@ -880,7 +1095,8 @@ function TimelineGantt({
|
|
|
880
1095
|
),
|
|
881
1096
|
children: [
|
|
882
1097
|
/* @__PURE__ */ jsx(Calendar, { className: "w-12 h-12 mb-3 opacity-30" }),
|
|
883
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm", children: emptyMessage })
|
|
1098
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm", children: emptyMessage }),
|
|
1099
|
+
navigatorContainer && createPortal(navigator, navigatorContainer)
|
|
884
1100
|
]
|
|
885
1101
|
}
|
|
886
1102
|
);
|
|
@@ -896,89 +1112,200 @@ function TimelineGantt({
|
|
|
896
1112
|
const axisMax = new Date(maxTime + pad);
|
|
897
1113
|
const axisRange = axisMax.getTime() - axisMin.getTime();
|
|
898
1114
|
const toPercent = (d) => Math.min(100, Math.max(0, (d.getTime() - axisMin.getTime()) / axisRange * 100));
|
|
1115
|
+
const tickCount = Math.max(2, Math.min(50, Math.round(8 * zoomValue) + 1));
|
|
1116
|
+
const tickStep = axisRange / (tickCount - 1);
|
|
1117
|
+
const tickIntervalDays = tickStep / DAY_MS;
|
|
899
1118
|
const ticks = Array.from(
|
|
900
|
-
{ length:
|
|
901
|
-
(_, i) => new Date(axisMin.getTime() +
|
|
1119
|
+
{ length: tickCount },
|
|
1120
|
+
(_, i) => new Date(axisMin.getTime() + tickStep * i)
|
|
902
1121
|
);
|
|
903
1122
|
const today = /* @__PURE__ */ new Date();
|
|
904
1123
|
const todayPct = toPercent(today);
|
|
905
1124
|
const showToday = today > axisMin && today < axisMax;
|
|
906
1125
|
const activeItem = activeIndex !== null ? rows[activeIndex] : null;
|
|
907
1126
|
const detail = activeItem ? renderDetail?.(activeItem) : null;
|
|
908
|
-
const select = (index) => {
|
|
909
|
-
setActiveIndex(index);
|
|
910
|
-
onSelect?.(index === null ? null : rows[index]);
|
|
911
|
-
};
|
|
912
1127
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex h-full min-h-0", className), children: [
|
|
913
|
-
/* @__PURE__ */ jsxs("div", { className: "flex
|
|
914
|
-
/* @__PURE__ */
|
|
1128
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex min-h-0 flex-1 flex-col p-5", children: [
|
|
1129
|
+
/* @__PURE__ */ jsx(
|
|
1130
|
+
"div",
|
|
1131
|
+
{
|
|
1132
|
+
"aria-hidden": true,
|
|
1133
|
+
"data-testid": `${testId}-gutter-divider`,
|
|
1134
|
+
style: { left: `calc(20px + ${labelWidth}px)` },
|
|
1135
|
+
className: "pointer-events-none absolute top-0 bottom-0 z-30 w-[3px] -translate-x-1/2 bg-border"
|
|
1136
|
+
}
|
|
1137
|
+
),
|
|
1138
|
+
onLabelWidthChange && /* @__PURE__ */ jsx(
|
|
1139
|
+
"div",
|
|
1140
|
+
{
|
|
1141
|
+
role: "separator",
|
|
1142
|
+
"aria-orientation": "vertical",
|
|
1143
|
+
"aria-label": "Resize event column",
|
|
1144
|
+
"data-testid": `${testId}-label-resizer`,
|
|
1145
|
+
onMouseDown: startResize,
|
|
1146
|
+
style: { left: `calc(20px + ${labelWidth}px)` },
|
|
1147
|
+
className: "absolute top-0 bottom-0 z-30 w-2 -translate-x-1/2 cursor-col-resize group",
|
|
1148
|
+
children: /* @__PURE__ */ jsx("div", { className: "mx-auto h-full w-[3px] transition-colors group-hover:bg-primary" })
|
|
1149
|
+
}
|
|
1150
|
+
),
|
|
1151
|
+
!zoomControlled && /* @__PURE__ */ jsx("div", { className: "absolute right-2 top-2 z-30 shadow-sm", children: /* @__PURE__ */ jsx(TimelineZoomControl, { zoom: zoomValue, onZoomChange: setZoomValue, testId }) }),
|
|
1152
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1 flex min-h-0 flex-1", children: [
|
|
1153
|
+
/* @__PURE__ */ jsxs("div", { style: { width: labelWidth, flexShrink: 0 }, className: "flex min-h-0 flex-col", children: [
|
|
1154
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-1 flex h-6 shrink-0 items-center gap-1 border-b border-border pr-3 text-sm font-medium text-muted-foreground", children: [
|
|
1155
|
+
/* @__PURE__ */ jsx("span", { children: "Event" }),
|
|
1156
|
+
navigatorContainer === void 0 ? /* @__PURE__ */ jsx("span", { className: "ml-auto", children: navigator }) : navigatorContainer && createPortal(navigator, navigatorContainer)
|
|
1157
|
+
] }),
|
|
1158
|
+
/* @__PURE__ */ jsx(
|
|
1159
|
+
"div",
|
|
1160
|
+
{
|
|
1161
|
+
ref: gutterBodyRef,
|
|
1162
|
+
"data-testid": `${testId}-gutter`,
|
|
1163
|
+
className: "min-h-0 flex-1 overflow-hidden",
|
|
1164
|
+
onWheel: (e) => {
|
|
1165
|
+
if (chartRef.current) chartRef.current.scrollTop += e.deltaY;
|
|
1166
|
+
},
|
|
1167
|
+
children: /* @__PURE__ */ jsx("div", { className: "pt-4 pb-6", children: rows.map((item, i) => {
|
|
1168
|
+
const isActive = activeIndex === i;
|
|
1169
|
+
const isSelected = selectedIds?.includes(item.id) ?? false;
|
|
1170
|
+
const highlighted = isActive || isSelected;
|
|
1171
|
+
const zebra = i % 2 === 1;
|
|
1172
|
+
return (
|
|
1173
|
+
// Fixed h-9 = the track row's h-7 bar + py-1, so the two
|
|
1174
|
+
// columns stay row-for-row aligned. Clicking the label
|
|
1175
|
+
// selects the row exactly like its bar does — the label IS
|
|
1176
|
+
// the event's title, so it should be a target too. Keep the
|
|
1177
|
+
// `break-words leading-snug` tail on THIS element — a scoped
|
|
1178
|
+
// CSS rule (.facts-gantt .break-words.leading-snug) clamps it
|
|
1179
|
+
// to one line. A selected row gets the tint plus a left
|
|
1180
|
+
// accent bar, mirroring the facts table's edited-row mark.
|
|
1181
|
+
/* @__PURE__ */ jsx(
|
|
1182
|
+
"div",
|
|
1183
|
+
{
|
|
1184
|
+
onClick: () => select(isActive ? null : i),
|
|
1185
|
+
className: cn(
|
|
1186
|
+
"flex h-9 cursor-pointer items-center rounded border-l-[3px] pr-3 text-xs font-medium",
|
|
1187
|
+
isSelected ? "border-l-primary bg-primary/10" : zebra ? "border-l-transparent bg-muted" : "border-l-transparent bg-background",
|
|
1188
|
+
highlighted ? "text-foreground" : "text-muted-foreground",
|
|
1189
|
+
"break-words leading-snug"
|
|
1190
|
+
),
|
|
1191
|
+
title: item.label,
|
|
1192
|
+
children: /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1 truncate", children: [
|
|
1193
|
+
/* @__PURE__ */ jsxs("span", { className: "mr-1 tabular-nums text-muted-foreground/60", children: [
|
|
1194
|
+
i + 1,
|
|
1195
|
+
"."
|
|
1196
|
+
] }),
|
|
1197
|
+
item.label
|
|
1198
|
+
] })
|
|
1199
|
+
},
|
|
1200
|
+
item.id
|
|
1201
|
+
)
|
|
1202
|
+
);
|
|
1203
|
+
}) })
|
|
1204
|
+
}
|
|
1205
|
+
)
|
|
1206
|
+
] }),
|
|
915
1207
|
/* @__PURE__ */ jsx(
|
|
916
1208
|
"div",
|
|
917
1209
|
{
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
),
|
|
923
|
-
/* @__PURE__ */ jsx("div", { className: "flex-1 relative h-6 border-b border-border", children: ticks.map((tick, i) => /* @__PURE__ */ jsx(
|
|
924
|
-
"span",
|
|
925
|
-
{
|
|
926
|
-
style: { left: `${toPercent(tick)}%` },
|
|
927
|
-
className: "absolute bottom-1 text-[10px] text-muted-foreground -translate-x-1/2 whitespace-nowrap select-none",
|
|
928
|
-
children: formatAxisDate(tick)
|
|
929
|
-
},
|
|
930
|
-
i
|
|
931
|
-
)) })
|
|
932
|
-
] }),
|
|
933
|
-
/* @__PURE__ */ jsxs("div", { className: "relative mt-2", children: [
|
|
934
|
-
showToday && /* @__PURE__ */ jsxs(
|
|
935
|
-
"div",
|
|
936
|
-
{
|
|
937
|
-
className: "absolute top-0 bottom-0 pointer-events-none z-10",
|
|
938
|
-
style: {
|
|
939
|
-
left: `calc(${labelWidth}px + (100% - ${labelWidth}px) * ${todayPct / 100})`
|
|
1210
|
+
ref: chartRef,
|
|
1211
|
+
"data-testid": `${testId}-scroller`,
|
|
1212
|
+
onScroll: (e) => {
|
|
1213
|
+
if (gutterBodyRef.current) gutterBodyRef.current.scrollTop = e.currentTarget.scrollTop;
|
|
940
1214
|
},
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
]
|
|
945
|
-
}
|
|
946
|
-
),
|
|
947
|
-
rows.map((item, i) => {
|
|
948
|
-
const startDate = parseIso2(item.start);
|
|
949
|
-
const endDate = parseIso2(item.end ?? null) ?? startDate;
|
|
950
|
-
const leftPct = toPercent(startDate);
|
|
951
|
-
const widthPct = Math.max(1, toPercent(endDate) - toPercent(startDate));
|
|
952
|
-
const color = BAR_PALETTE[i % BAR_PALETTE.length];
|
|
953
|
-
const isActive = activeIndex === i;
|
|
954
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex items-center mb-2", children: [
|
|
955
|
-
/* @__PURE__ */ jsx(
|
|
956
|
-
"div",
|
|
957
|
-
{
|
|
958
|
-
style: { width: labelWidth, flexShrink: 0 },
|
|
959
|
-
className: "text-xs font-medium text-muted-foreground pr-3 break-words leading-snug",
|
|
960
|
-
children: item.label
|
|
961
|
-
}
|
|
1215
|
+
className: cn(
|
|
1216
|
+
"scrollbar-subtle relative min-h-0 min-w-0 flex-1 overflow-y-auto",
|
|
1217
|
+
zoomValue > 1 ? "overflow-x-auto" : "overflow-x-hidden"
|
|
962
1218
|
),
|
|
963
|
-
|
|
964
|
-
"
|
|
1219
|
+
children: /* @__PURE__ */ jsxs(
|
|
1220
|
+
"div",
|
|
965
1221
|
{
|
|
966
|
-
"
|
|
967
|
-
"
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1222
|
+
className: "relative min-h-full",
|
|
1223
|
+
style: { width: `${zoomValue * 100}%`, minWidth: "100%" },
|
|
1224
|
+
children: [
|
|
1225
|
+
/* @__PURE__ */ jsx("div", { className: "sticky top-0 z-20 mb-1 h-6 border-b border-border bg-background", children: ticks.map((tick, i) => {
|
|
1226
|
+
const isFirst = i === 0;
|
|
1227
|
+
const isLast = i === ticks.length - 1;
|
|
1228
|
+
const edgeShift = isFirst ? "translate-x-1" : isLast ? "-translate-x-full" : "-translate-x-1/2";
|
|
1229
|
+
return /* @__PURE__ */ jsx(
|
|
1230
|
+
"span",
|
|
1231
|
+
{
|
|
1232
|
+
style: { left: `${toPercent(tick)}%` },
|
|
1233
|
+
className: `absolute bottom-1 text-[10px] text-muted-foreground whitespace-nowrap select-none ${edgeShift}`,
|
|
1234
|
+
children: formatAxisDate(tick, tickIntervalDays)
|
|
1235
|
+
},
|
|
1236
|
+
i
|
|
1237
|
+
);
|
|
1238
|
+
}) }),
|
|
1239
|
+
/* @__PURE__ */ jsxs("div", { className: "relative pt-4", children: [
|
|
1240
|
+
ticks.map((tick, i) => /* @__PURE__ */ jsx(
|
|
1241
|
+
"div",
|
|
1242
|
+
{
|
|
1243
|
+
"aria-hidden": true,
|
|
1244
|
+
className: "pointer-events-none absolute top-0 bottom-0 z-10 border-l-2 border-border/30",
|
|
1245
|
+
style: { left: `${toPercent(tick)}%` }
|
|
1246
|
+
},
|
|
1247
|
+
`grid-${i}`
|
|
1248
|
+
)),
|
|
1249
|
+
showToday && /* @__PURE__ */ jsxs(
|
|
1250
|
+
"div",
|
|
1251
|
+
{
|
|
1252
|
+
className: "pointer-events-none absolute top-0 bottom-0 z-10",
|
|
1253
|
+
style: { left: `${todayPct}%` },
|
|
1254
|
+
children: [
|
|
1255
|
+
/* @__PURE__ */ jsx("div", { className: "h-full border-l-2 border-dashed border-muted-foreground/50" }),
|
|
1256
|
+
/* @__PURE__ */ jsx("span", { className: "absolute top-0 -translate-x-1/2 rounded border bg-background px-1 text-[9px] text-muted-foreground", children: "today" })
|
|
1257
|
+
]
|
|
1258
|
+
}
|
|
1259
|
+
),
|
|
1260
|
+
rows.map((item, i) => {
|
|
1261
|
+
const startDate = parseIso2(item.start);
|
|
1262
|
+
const endDate = parseIso2(item.end ?? null) ?? startDate;
|
|
1263
|
+
const leftPct = toPercent(startDate);
|
|
1264
|
+
const dayPct = DAY_MS / axisRange * 100;
|
|
1265
|
+
const widthPct = Math.max(dayPct, toPercent(endDate) - toPercent(startDate));
|
|
1266
|
+
const isActive = activeIndex === i;
|
|
1267
|
+
const isSelected = selectedIds?.includes(item.id) ?? false;
|
|
1268
|
+
const highlighted = isActive || isSelected;
|
|
1269
|
+
const zebra = i % 2 === 1;
|
|
1270
|
+
return (
|
|
1271
|
+
// Fixed h-9 (h-7 bar + py-1) — the gutter's label rows use
|
|
1272
|
+
// the same height, which is what keeps the columns aligned.
|
|
1273
|
+
/* @__PURE__ */ jsx("div", { className: "h-9 py-1", children: /* @__PURE__ */ jsx(
|
|
1274
|
+
"div",
|
|
1275
|
+
{
|
|
1276
|
+
className: cn(
|
|
1277
|
+
"relative h-7 overflow-visible rounded-md border border-border",
|
|
1278
|
+
isSelected ? "bg-primary/10" : zebra ? "bg-muted" : "bg-background"
|
|
1279
|
+
),
|
|
1280
|
+
children: /* @__PURE__ */ jsx(
|
|
1281
|
+
"button",
|
|
1282
|
+
{
|
|
1283
|
+
"data-testid": `${testId}-bar-${i}`,
|
|
1284
|
+
"aria-label": item.label,
|
|
1285
|
+
"aria-pressed": highlighted,
|
|
1286
|
+
style: {
|
|
1287
|
+
left: `${leftPct}%`,
|
|
1288
|
+
width: `${widthPct}%`,
|
|
1289
|
+
// Colour-coded by the item's labels (up to three), not by
|
|
1290
|
+
// row order — see barBackground / TimelineItem.colors.
|
|
1291
|
+
background: barBackground(item.colors),
|
|
1292
|
+
boxShadow: highlighted ? "0 0 0 2px white, 0 0 0 4px #6366f1" : "0 1px 3px rgba(0,0,0,.2)"
|
|
1293
|
+
},
|
|
1294
|
+
title: formatSpan(item.start, item.end) || void 0,
|
|
1295
|
+
className: "absolute top-0.5 h-6 min-w-[6px] rounded cursor-pointer transition-[filter] hover:brightness-110 focus:outline-none",
|
|
1296
|
+
onClick: () => select(isActive ? null : i)
|
|
1297
|
+
}
|
|
1298
|
+
)
|
|
1299
|
+
}
|
|
1300
|
+
) }, item.id)
|
|
1301
|
+
);
|
|
1302
|
+
})
|
|
1303
|
+
] })
|
|
1304
|
+
]
|
|
978
1305
|
}
|
|
979
|
-
)
|
|
980
|
-
|
|
981
|
-
|
|
1306
|
+
)
|
|
1307
|
+
}
|
|
1308
|
+
)
|
|
982
1309
|
] })
|
|
983
1310
|
] }),
|
|
984
1311
|
/* @__PURE__ */ jsx(
|
|
@@ -1008,12 +1335,18 @@ function TimelineGantt({
|
|
|
1008
1335
|
}
|
|
1009
1336
|
) }),
|
|
1010
1337
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1011
|
-
/* @__PURE__ */
|
|
1338
|
+
/* @__PURE__ */ jsxs(
|
|
1012
1339
|
"div",
|
|
1013
1340
|
{
|
|
1014
1341
|
"data-testid": `${testId}-panel-title`,
|
|
1015
1342
|
className: "text-sm font-semibold text-foreground leading-snug",
|
|
1016
|
-
children:
|
|
1343
|
+
children: [
|
|
1344
|
+
activeIndex !== null && /* @__PURE__ */ jsxs("span", { className: "mr-1 tabular-nums text-muted-foreground", children: [
|
|
1345
|
+
activeIndex + 1,
|
|
1346
|
+
"."
|
|
1347
|
+
] }),
|
|
1348
|
+
activeItem.label
|
|
1349
|
+
]
|
|
1017
1350
|
}
|
|
1018
1351
|
),
|
|
1019
1352
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 mt-1 text-xs text-muted-foreground", children: [
|
|
@@ -2008,6 +2341,6 @@ function TestimonialGrid({ items, columns = "3", testId }) {
|
|
|
2008
2341
|
)) });
|
|
2009
2342
|
}
|
|
2010
2343
|
|
|
2011
|
-
export { ASSET_REF_PREFIX, Accordion, AssetResolverProvider, AvatarBlock, Banner, BlockImage, BlockLink, CTABanner, Callout, Columns, ContactForm, Container, FAQ, FONT_PAIRINGS, FeatureGrid, Footer, Gallery, Hero, ListBlock, LogoCloud, MediaText, Navbar, PricingTable, RichTextBlock, SectionHeading, Spacer, Steps, THEME_STYLE_ID, Tabs, TeamGrid, Testimonial, TestimonialGrid, ThemeEditor, Timeline, TimelineCalendar, TimelineGantt, VideoEmbed, applyFontVars, applyStoredTheme, applyThemeVars, asAssetRef, assetRefId, clearFontVars, clearThemeVars, createThemeStore, dayKey, daysUntil, fmtDate, fmtDateTime, fontPackages, formatPageRefs, formatSpan, fromNow, gbp, getFontPairing, hrs, initials, isAssetRef, matchFontPairing, monthLabel, parseIso2 as parseIso, pct, tid, toEmbedUrl, toHslTriplet, useAssetRenderer, useAssetResolverActive, utcDay };
|
|
2012
|
-
//# sourceMappingURL=chunk-
|
|
2013
|
-
//# sourceMappingURL=chunk-
|
|
2344
|
+
export { ASSET_REF_PREFIX, Accordion, AssetResolverProvider, AvatarBlock, Banner, BlockImage, BlockLink, CTABanner, Callout, Columns, ContactForm, Container, FAQ, FONT_PAIRINGS, FeatureGrid, Footer, Gallery, Hero, ListBlock, LogoCloud, MediaText, Navbar, PricingTable, RichTextBlock, SectionHeading, Spacer, Steps, THEME_STYLE_ID, TIMELINE_ZOOM, Tabs, TeamGrid, Testimonial, TestimonialGrid, ThemeEditor, Timeline, TimelineCalendar, TimelineGantt, TimelineZoomControl, VideoEmbed, applyFontVars, applyStoredTheme, applyThemeVars, asAssetRef, assetRefId, clearFontVars, clearThemeVars, createThemeStore, dayKey, daysUntil, fmtDate, fmtDateTime, fontPackages, formatPageRefs, formatSpan, fromNow, gbp, getFontPairing, hrs, initials, isAssetRef, matchFontPairing, monthLabel, parseIso2 as parseIso, pct, tid, toEmbedUrl, toHslTriplet, useAssetRenderer, useAssetResolverActive, utcDay };
|
|
2345
|
+
//# sourceMappingURL=chunk-FZ2S6VAZ.js.map
|
|
2346
|
+
//# sourceMappingURL=chunk-FZ2S6VAZ.js.map
|