@snksergio/design-system 0.58.0 → 0.60.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-lib/index.cjs +4461 -16
- package/dist-lib/index.cjs.map +1 -1
- package/dist-lib/index.mjs +4477 -32
- package/dist-lib/index.mjs.map +1 -1
- package/dist-lib/src/components/index.d.ts +1 -0
- package/dist-lib/src/components/index.d.ts.map +1 -1
- package/dist-lib/src/components/ui/Gantt/gantt.d.ts +21 -0
- package/dist-lib/src/components/ui/Gantt/gantt.d.ts.map +1 -0
- package/dist-lib/src/components/ui/Gantt/gantt.styles.d.ts +2985 -0
- package/dist-lib/src/components/ui/Gantt/gantt.styles.d.ts.map +1 -0
- package/dist-lib/src/components/ui/Gantt/gantt.types.d.ts +445 -0
- package/dist-lib/src/components/ui/Gantt/gantt.types.d.ts.map +1 -0
- package/dist-lib/src/components/ui/Gantt/hooks/drag.d.ts +71 -0
- package/dist-lib/src/components/ui/Gantt/hooks/drag.d.ts.map +1 -0
- package/dist-lib/src/components/ui/Gantt/hooks/filters.d.ts +79 -0
- package/dist-lib/src/components/ui/Gantt/hooks/filters.d.ts.map +1 -0
- package/dist-lib/src/components/ui/Gantt/hooks/layout.d.ts +232 -0
- package/dist-lib/src/components/ui/Gantt/hooks/layout.d.ts.map +1 -0
- package/dist-lib/src/components/ui/Gantt/hooks/links.d.ts +120 -0
- package/dist-lib/src/components/ui/Gantt/hooks/links.d.ts.map +1 -0
- package/dist-lib/src/components/ui/Gantt/hooks/week-segments.d.ts +69 -0
- package/dist-lib/src/components/ui/Gantt/hooks/week-segments.d.ts.map +1 -0
- package/dist-lib/src/components/ui/Gantt/index.d.ts +46 -0
- package/dist-lib/src/components/ui/Gantt/index.d.ts.map +1 -0
- package/dist-lib/src/components/ui/Gantt/parts/gantt-applied-filters.d.ts +20 -0
- package/dist-lib/src/components/ui/Gantt/parts/gantt-applied-filters.d.ts.map +1 -0
- package/dist-lib/src/components/ui/Gantt/parts/gantt-bar.d.ts +68 -0
- package/dist-lib/src/components/ui/Gantt/parts/gantt-bar.d.ts.map +1 -0
- package/dist-lib/src/components/ui/Gantt/parts/gantt-filter-panel.d.ts +16 -0
- package/dist-lib/src/components/ui/Gantt/parts/gantt-filter-panel.d.ts.map +1 -0
- package/dist-lib/src/components/ui/Gantt/parts/gantt-grid.d.ts +55 -0
- package/dist-lib/src/components/ui/Gantt/parts/gantt-grid.d.ts.map +1 -0
- package/dist-lib/src/components/ui/Gantt/parts/gantt-links-layer.d.ts +46 -0
- package/dist-lib/src/components/ui/Gantt/parts/gantt-links-layer.d.ts.map +1 -0
- package/dist-lib/src/components/ui/Gantt/parts/gantt-skeleton.d.ts +36 -0
- package/dist-lib/src/components/ui/Gantt/parts/gantt-skeleton.d.ts.map +1 -0
- package/dist-lib/src/components/ui/Gantt/parts/gantt-toolbar.d.ts +46 -0
- package/dist-lib/src/components/ui/Gantt/parts/gantt-toolbar.d.ts.map +1 -0
- package/dist-lib/src/components/ui/Gantt/views/calendar.d.ts +64 -0
- package/dist-lib/src/components/ui/Gantt/views/calendar.d.ts.map +1 -0
- package/dist-lib/src/components/ui/Gantt/views/list.d.ts +53 -0
- package/dist-lib/src/components/ui/Gantt/views/list.d.ts.map +1 -0
- package/dist-lib/src/components/ui/Gantt/views/timeline.d.ts +85 -0
- package/dist-lib/src/components/ui/Gantt/views/timeline.d.ts.map +1 -0
- package/dist-lib/src/components/ui/MessageBubble/index.d.ts +1 -1
- package/dist-lib/src/components/ui/MessageBubble/index.d.ts.map +1 -1
- package/dist-lib/src/components/ui/MessageBubble/message-bubble.d.ts +1 -1
- package/dist-lib/src/components/ui/MessageBubble/message-bubble.d.ts.map +1 -1
- package/dist-lib/src/components/ui/MessageBubble/message-bubble.styles.d.ts +22 -2
- package/dist-lib/src/components/ui/MessageBubble/message-bubble.styles.d.ts.map +1 -1
- package/dist-lib/src/components/ui/MessageBubble/message-bubble.types.d.ts +8 -0
- package/dist-lib/src/components/ui/MessageBubble/message-bubble.types.d.ts.map +1 -1
- package/dist-lib/theme/brand-pay.css +205 -189
- package/dist-lib/theme/brand-vibrant.css +162 -148
- package/dist-lib/theme.css +19 -0
- package/dist-lib/tokens/brands/blue/primitives/color-palette.d.ts +28 -0
- package/dist-lib/tokens/brands/blue/primitives/color-palette.d.ts.map +1 -1
- package/dist-lib/tokens/brands/blue/semantic/color-dark.d.ts +16 -0
- package/dist-lib/tokens/brands/blue/semantic/color-dark.d.ts.map +1 -1
- package/dist-lib/tokens/brands/blue/semantic/color-light.d.ts +16 -0
- package/dist-lib/tokens/brands/blue/semantic/color-light.d.ts.map +1 -1
- package/dist-lib/tokens/brands/default/primitives/color-palette.d.ts +29 -0
- package/dist-lib/tokens/brands/default/primitives/color-palette.d.ts.map +1 -1
- package/dist-lib/tokens/brands/default/semantic/color-dark.d.ts +16 -0
- package/dist-lib/tokens/brands/default/semantic/color-dark.d.ts.map +1 -1
- package/dist-lib/tokens/brands/default/semantic/color-light.d.ts +16 -0
- package/dist-lib/tokens/brands/default/semantic/color-light.d.ts.map +1 -1
- package/dist-lib/tokens/brands/default/semantic/elevation.d.ts +4 -0
- package/dist-lib/tokens/brands/default/semantic/elevation.d.ts.map +1 -1
- package/dist-lib/tokens/brands/green/primitives/color-palette.d.ts +28 -0
- package/dist-lib/tokens/brands/green/primitives/color-palette.d.ts.map +1 -1
- package/dist-lib/tokens/brands/green/semantic/color-dark.d.ts +16 -0
- package/dist-lib/tokens/brands/green/semantic/color-dark.d.ts.map +1 -1
- package/dist-lib/tokens/brands/green/semantic/color-light.d.ts +16 -0
- package/dist-lib/tokens/brands/green/semantic/color-light.d.ts.map +1 -1
- package/dist-lib/tokens/brands/pay/primitives/color-palette.d.ts +6 -0
- package/dist-lib/tokens/brands/pay/primitives/color-palette.d.ts.map +1 -1
- package/dist-lib/tokens/brands/pay/semantic/color-dark.d.ts +16 -0
- package/dist-lib/tokens/brands/pay/semantic/color-dark.d.ts.map +1 -1
- package/dist-lib/tokens/brands/pay/semantic/color-light.d.ts +16 -0
- package/dist-lib/tokens/brands/pay/semantic/color-light.d.ts.map +1 -1
- package/dist-lib/tokens/brands/vibrant/primitives/color-palette.d.ts +29 -1
- package/dist-lib/tokens/brands/vibrant/primitives/color-palette.d.ts.map +1 -1
- package/dist-lib/tokens/brands/vibrant/semantic/color-dark.d.ts +16 -0
- package/dist-lib/tokens/brands/vibrant/semantic/color-dark.d.ts.map +1 -1
- package/dist-lib/tokens/brands/vibrant/semantic/color-light.d.ts +16 -0
- package/dist-lib/tokens/brands/vibrant/semantic/color-light.d.ts.map +1 -1
- package/dist-lib/tokens.cjs +23 -2
- package/dist-lib/tokens.cjs.map +1 -1
- package/dist-lib/tokens.mjs +23 -2
- package/dist-lib/tokens.mjs.map +1 -1
- package/package.json +1 -1
package/dist-lib/index.mjs
CHANGED
|
@@ -11,14 +11,14 @@ import { Q as Switch, l as Checkbox, C as Calendar, T as Textarea } from "./chun
|
|
|
11
11
|
import { G as G2, H as H3, F as F2, O as O2 } from "./chunks/sheet-Bjjo4ZaT.mjs";
|
|
12
12
|
import { ao as RadioGroup, ap as RadioGroupItem, as as Separator, at as Skeleton, T as HoverCard, V as HoverCardTrigger, U as HoverCardContent } from "./chunks/toggle-group-XMgZgL-H.mjs";
|
|
13
13
|
import { B as B3, aC } from "./chunks/toggle-group-XMgZgL-H.mjs";
|
|
14
|
-
import { Check, MoreHorizontal, SlidersHorizontal, Calendar as Calendar$1, AlertCircle, CheckCheck, Clock, MoreVertical, Ban, X as X3, CalendarDays, ChevronDown, ChevronLeft, ChevronRight, Search, ListFilter, Columns3, Square, List, Plus, CalendarSearch, Circle, RefreshCw, OctagonX, TriangleAlert, CircleCheck, Info, Bell } from "lucide-react";
|
|
14
|
+
import { Check, MoreHorizontal, SlidersHorizontal, Calendar as Calendar$1, AlertCircle, CheckCheck, Clock, MoreVertical, Ban, X as X3, CalendarDays, ChevronDown, ChevronLeft, ChevronRight, Search, ListFilter, Columns3, Square, List, Plus, CalendarSearch, CircleX, Zap, Filter, ChartNoAxesGantt, Circle, RefreshCw, OctagonX, TriangleAlert, CircleCheck, Info, Bell } from "lucide-react";
|
|
15
15
|
import { Reply } from "lucide-react";
|
|
16
16
|
import { I as Input } from "./chunks/input-BQABJR2N.mjs";
|
|
17
17
|
import { i as i3 } from "./chunks/input-BQABJR2N.mjs";
|
|
18
18
|
import { a as a10, b as b3, K as K2 } from "./chunks/kpi-delta-Df6C70_5.mjs";
|
|
19
19
|
import { P as P2, p as p3 } from "./chunks/page-header-t5hFIp7o.mjs";
|
|
20
20
|
import { useSensors, useSensor, PointerSensor, useDroppable, useDraggable, DndContext, DragOverlay } from "@dnd-kit/core";
|
|
21
|
-
import { addMinutes, differenceInCalendarDays, startOfDay, addDays, startOfMonth, startOfWeek, addHours, endOfDay, addMonths, format, isSameDay, isSameMonth, endOfMonth, addWeeks, endOfWeek } from "date-fns";
|
|
21
|
+
import { addMinutes, differenceInCalendarDays, startOfDay, addDays, startOfMonth, startOfWeek, addHours, endOfDay, addMonths, format, isSameDay, isSameMonth, endOfMonth, addWeeks, endOfWeek, max, min, eachDayOfInterval, startOfQuarter } from "date-fns";
|
|
22
22
|
import * as RechartsPrimitive from "recharts";
|
|
23
23
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
24
24
|
import "@hello-pangea/dnd";
|
|
@@ -4277,6 +4277,7 @@ const messageBubbleStyles = tv({
|
|
|
4277
4277
|
/* ações (hover) */
|
|
4278
4278
|
actionsTrigger: [
|
|
4279
4279
|
"absolute right-pad-md top-pad-md",
|
|
4280
|
+
"bg-bg-emphasis shadow-sh-sm hover:bg-bg-accent-hover",
|
|
4280
4281
|
"opacity-0 transition-opacity",
|
|
4281
4282
|
"group-hover/bubble:opacity-100 focus-visible:opacity-100"
|
|
4282
4283
|
],
|
|
@@ -4292,12 +4293,16 @@ const messageBubbleStyles = tv({
|
|
|
4292
4293
|
received: {
|
|
4293
4294
|
row: "flex-row",
|
|
4294
4295
|
column: "items-start",
|
|
4295
|
-
bubble: "bg-bg-surface"
|
|
4296
|
+
bubble: "bg-bg-surface border-border-subtle"
|
|
4296
4297
|
}
|
|
4297
4298
|
},
|
|
4298
4299
|
tail: {
|
|
4299
4300
|
true: {},
|
|
4300
4301
|
false: {}
|
|
4302
|
+
},
|
|
4303
|
+
origin: {
|
|
4304
|
+
human: {},
|
|
4305
|
+
ai: { bubble: "border-border-brand-subtle" }
|
|
4301
4306
|
}
|
|
4302
4307
|
},
|
|
4303
4308
|
compoundVariants: [
|
|
@@ -4307,7 +4312,8 @@ const messageBubbleStyles = tv({
|
|
|
4307
4312
|
],
|
|
4308
4313
|
defaultVariants: {
|
|
4309
4314
|
side: "received",
|
|
4310
|
-
tail: true
|
|
4315
|
+
tail: true,
|
|
4316
|
+
origin: "human"
|
|
4311
4317
|
}
|
|
4312
4318
|
});
|
|
4313
4319
|
const styles = messageBubbleStyles();
|
|
@@ -4481,6 +4487,7 @@ function MessageBubble({
|
|
|
4481
4487
|
isEdited = false,
|
|
4482
4488
|
isDeleted = false,
|
|
4483
4489
|
tail = true,
|
|
4490
|
+
origin,
|
|
4484
4491
|
authorName,
|
|
4485
4492
|
avatar,
|
|
4486
4493
|
actions,
|
|
@@ -4488,7 +4495,7 @@ function MessageBubble({
|
|
|
4488
4495
|
className
|
|
4489
4496
|
}) {
|
|
4490
4497
|
const [actionsOpen, setActionsOpen] = useState(false);
|
|
4491
|
-
const s2 = messageBubbleStyles({ side, tail });
|
|
4498
|
+
const s2 = messageBubbleStyles({ side, tail, origin });
|
|
4492
4499
|
const time = formatTime(createdAt);
|
|
4493
4500
|
const hasMedia = !isDeleted && mediaType !== "text";
|
|
4494
4501
|
const showAck = side === "sent" && ack !== void 0 && !isDeleted;
|
|
@@ -4506,7 +4513,8 @@ function MessageBubble({
|
|
|
4506
4513
|
Button,
|
|
4507
4514
|
{
|
|
4508
4515
|
type: "button",
|
|
4509
|
-
size: "
|
|
4516
|
+
size: "icon-xs",
|
|
4517
|
+
shape: "pill",
|
|
4510
4518
|
variant: "ghost",
|
|
4511
4519
|
color: "secondary",
|
|
4512
4520
|
"aria-label": "Ações da mensagem",
|
|
@@ -5120,8 +5128,8 @@ const MonthYearPicker = forwardRef(function MonthYearPicker2({
|
|
|
5120
5128
|
value,
|
|
5121
5129
|
onValueChange,
|
|
5122
5130
|
placeholder = "Selecione o mês",
|
|
5123
|
-
min,
|
|
5124
|
-
max,
|
|
5131
|
+
min: min2,
|
|
5132
|
+
max: max2,
|
|
5125
5133
|
locale = "pt-BR",
|
|
5126
5134
|
disabled,
|
|
5127
5135
|
open: openProp,
|
|
@@ -5160,8 +5168,8 @@ const MonthYearPicker = forwardRef(function MonthYearPicker2({
|
|
|
5160
5168
|
return capitalizeFirst(fmt.format(new Date(parsed.year, parsed.month - 1, 1)));
|
|
5161
5169
|
}, [parsed, locale]);
|
|
5162
5170
|
const styles2 = monthYearStyles();
|
|
5163
|
-
const minParsed = parseValue(
|
|
5164
|
-
const maxParsed = parseValue(
|
|
5171
|
+
const minParsed = parseValue(min2);
|
|
5172
|
+
const maxParsed = parseValue(max2);
|
|
5165
5173
|
const minOrdinal = minParsed ? toOrdinal(minParsed.year, minParsed.month - 1) : -Infinity;
|
|
5166
5174
|
const maxOrdinal = maxParsed ? toOrdinal(maxParsed.year, maxParsed.month - 1) : Infinity;
|
|
5167
5175
|
const isMonthDisabled = (monthIndex) => {
|
|
@@ -5865,7 +5873,7 @@ const schedulerPlaceholder = tv({
|
|
|
5865
5873
|
"p-sp-3xl text-center"
|
|
5866
5874
|
]
|
|
5867
5875
|
});
|
|
5868
|
-
const VIEW_ITEMS = [
|
|
5876
|
+
const VIEW_ITEMS$1 = [
|
|
5869
5877
|
{ value: "month", label: "Mês", icon: /* @__PURE__ */ jsx(CalendarDays, {}) },
|
|
5870
5878
|
{ value: "week", label: "Semana", icon: /* @__PURE__ */ jsx(Columns3, {}) },
|
|
5871
5879
|
{ value: "day", label: "Dia", icon: /* @__PURE__ */ jsx(Square, {}) },
|
|
@@ -5892,7 +5900,7 @@ function SchedulerToolbar({
|
|
|
5892
5900
|
primaryAction
|
|
5893
5901
|
}) {
|
|
5894
5902
|
const hasFilters = (filterFields?.length ?? 0) > 0;
|
|
5895
|
-
const activeView = VIEW_ITEMS.find((v3) => v3.value === view) ?? VIEW_ITEMS[0];
|
|
5903
|
+
const activeView = VIEW_ITEMS$1.find((v3) => v3.value === view) ?? VIEW_ITEMS$1[0];
|
|
5896
5904
|
const filterEngaged = filterPanelOpen || appliedCount > 0;
|
|
5897
5905
|
const appliedFields = (filterFields ?? []).filter(
|
|
5898
5906
|
(field) => (filterModel[field.id] ?? []).length > 0
|
|
@@ -5962,7 +5970,7 @@ function SchedulerToolbar({
|
|
|
5962
5970
|
{
|
|
5963
5971
|
value: view,
|
|
5964
5972
|
onValueChange: (v3) => onViewChange(v3),
|
|
5965
|
-
children: VIEW_ITEMS.map((item) => /* @__PURE__ */ jsx(DropdownMenuRadioItem, { value: item.value, children: /* @__PURE__ */ jsx("span", { className: schedulerViewMenuItem(), children: item.label }) }, item.value))
|
|
5973
|
+
children: VIEW_ITEMS$1.map((item) => /* @__PURE__ */ jsx(DropdownMenuRadioItem, { value: item.value, children: /* @__PURE__ */ jsx("span", { className: schedulerViewMenuItem(), children: item.label }) }, item.value))
|
|
5966
5974
|
}
|
|
5967
5975
|
) })
|
|
5968
5976
|
] }),
|
|
@@ -6024,7 +6032,7 @@ function SchedulerToolbar({
|
|
|
6024
6032
|
] }) : null
|
|
6025
6033
|
] });
|
|
6026
6034
|
}
|
|
6027
|
-
function buildMonthMatrix(date, weekStartsOn, locale) {
|
|
6035
|
+
function buildMonthMatrix$1(date, weekStartsOn, locale) {
|
|
6028
6036
|
const monthStart = startOfMonth(date);
|
|
6029
6037
|
const gridStart = startOfWeek(monthStart, { weekStartsOn, locale });
|
|
6030
6038
|
const days = [];
|
|
@@ -6068,7 +6076,7 @@ function segmentMultiDay(events, weekRows) {
|
|
|
6068
6076
|
function effectiveEndMs(event, snapMinutes) {
|
|
6069
6077
|
return event.start.getTime() === event.end.getTime() ? event.end.getTime() + snapMinutes * 6e4 : event.end.getTime();
|
|
6070
6078
|
}
|
|
6071
|
-
function packLanes(events, snapMinutes) {
|
|
6079
|
+
function packLanes$1(events, snapMinutes) {
|
|
6072
6080
|
if (events.length === 0) return [];
|
|
6073
6081
|
const sorted = [...events].sort((a11, b4) => {
|
|
6074
6082
|
const startDiff = a11.start.getTime() - b4.start.getTime();
|
|
@@ -6093,9 +6101,9 @@ function packLanes(events, snapMinutes) {
|
|
|
6093
6101
|
laneEnds[freeLane] = evEnd;
|
|
6094
6102
|
}
|
|
6095
6103
|
}
|
|
6096
|
-
const
|
|
6104
|
+
const laneCount2 = laneEnds.length;
|
|
6097
6105
|
for (const ev of group) {
|
|
6098
|
-
result.push({ event: ev, laneIndex: laneOf.get(ev), laneCount });
|
|
6106
|
+
result.push({ event: ev, laneIndex: laneOf.get(ev), laneCount: laneCount2 });
|
|
6099
6107
|
}
|
|
6100
6108
|
group = [];
|
|
6101
6109
|
groupMaxEnd = -Infinity;
|
|
@@ -6113,7 +6121,7 @@ function packLanes(events, snapMinutes) {
|
|
|
6113
6121
|
}
|
|
6114
6122
|
const MONTH_PILL_HEIGHT_PX = 20;
|
|
6115
6123
|
const MONTH_PILL_GAP_PX = 2;
|
|
6116
|
-
function computeOverflow(dayEvents, availableHeight) {
|
|
6124
|
+
function computeOverflow$1(dayEvents, availableHeight) {
|
|
6117
6125
|
if (dayEvents.length === 0) {
|
|
6118
6126
|
return { visible: [], overflowCount: 0 };
|
|
6119
6127
|
}
|
|
@@ -6201,7 +6209,7 @@ function MiniMonth({
|
|
|
6201
6209
|
[selected, offset]
|
|
6202
6210
|
);
|
|
6203
6211
|
const weeks = useMemo(
|
|
6204
|
-
() => buildMonthMatrix(cursor, weekStartsOn, locale),
|
|
6212
|
+
() => buildMonthMatrix$1(cursor, weekStartsOn, locale),
|
|
6205
6213
|
[cursor, weekStartsOn, locale]
|
|
6206
6214
|
);
|
|
6207
6215
|
const daysWithEvents = useMemo(() => {
|
|
@@ -6820,7 +6828,7 @@ function SchedulerMonthView({
|
|
|
6820
6828
|
podeMover
|
|
6821
6829
|
}) {
|
|
6822
6830
|
const weeks = useMemo(
|
|
6823
|
-
() => buildMonthMatrix(date, weekStartsOn, locale),
|
|
6831
|
+
() => buildMonthMatrix$1(date, weekStartsOn, locale),
|
|
6824
6832
|
[date, weekStartsOn, locale]
|
|
6825
6833
|
);
|
|
6826
6834
|
const teclado = useSchedulerKeyboard({
|
|
@@ -6900,7 +6908,7 @@ function SchedulerMonthView({
|
|
|
6900
6908
|
todos.length <= maxPerCell ? maxPerCell : Math.max(0, maxPerCell - 1)
|
|
6901
6909
|
),
|
|
6902
6910
|
overflowCount: todos.length <= maxPerCell ? 0 : todos.length - Math.max(0, maxPerCell - 1)
|
|
6903
|
-
} : computeOverflow(todos, alturaDisponivel);
|
|
6911
|
+
} : computeOverflow$1(todos, alturaDisponivel);
|
|
6904
6912
|
const outside = !isSameMonth(day, date);
|
|
6905
6913
|
const today = isSameDay(day, now);
|
|
6906
6914
|
const indiceDaCelula = weekIndex * 7 + week.indexOf(day);
|
|
@@ -7085,7 +7093,7 @@ function SchedulerTimeGrid({
|
|
|
7085
7093
|
end: ev.end.getTime() > gridBottom.getTime() ? gridBottom : ev.end
|
|
7086
7094
|
}
|
|
7087
7095
|
}));
|
|
7088
|
-
const lanes = packLanes(
|
|
7096
|
+
const lanes = packLanes$1(
|
|
7089
7097
|
recortados.map((r2) => r2.clamped),
|
|
7090
7098
|
snapMinutes
|
|
7091
7099
|
);
|
|
@@ -7846,6 +7854,4407 @@ const Scheduler = forwardRef(
|
|
|
7846
7854
|
);
|
|
7847
7855
|
}
|
|
7848
7856
|
);
|
|
7857
|
+
const GANTT_ROW_HEIGHT_PX = 48;
|
|
7858
|
+
const GANTT_BAR_HEIGHT_PX = 26;
|
|
7859
|
+
const GANTT_SUMMARY_BAR_HEIGHT_PX = 12;
|
|
7860
|
+
const GANTT_LANE_HEIGHT_PX = 30;
|
|
7861
|
+
const GANTT_HEAD_HEIGHT_PX = 84;
|
|
7862
|
+
const GANTT_HEAD_GROUP_PX = 42;
|
|
7863
|
+
const GANTT_HEAD_UNIT_PX = 42;
|
|
7864
|
+
const GANTT_GROUP_LABEL_MIN_PX = 96;
|
|
7865
|
+
const GANTT_PX_PER_DAY = {
|
|
7866
|
+
day: 46,
|
|
7867
|
+
week: 100 / 7,
|
|
7868
|
+
month: 240 / 30.4,
|
|
7869
|
+
quarter: 320 / 91
|
|
7870
|
+
};
|
|
7871
|
+
const GANTT_WINDOW_DAYS = {
|
|
7872
|
+
day: 60,
|
|
7873
|
+
week: 180,
|
|
7874
|
+
month: 730,
|
|
7875
|
+
quarter: 1825
|
|
7876
|
+
};
|
|
7877
|
+
const ganttRoot = tv({
|
|
7878
|
+
base: [
|
|
7879
|
+
"flex h-full min-h-0 w-full flex-col gap-gp-3xl",
|
|
7880
|
+
"text-fg-default"
|
|
7881
|
+
]
|
|
7882
|
+
});
|
|
7883
|
+
const ganttToolbar = tv({
|
|
7884
|
+
base: [
|
|
7885
|
+
"flex flex-col gap-gp-xl",
|
|
7886
|
+
"lg:flex-row lg:flex-wrap lg:items-center lg:justify-between"
|
|
7887
|
+
]
|
|
7888
|
+
});
|
|
7889
|
+
const ganttToolbarSide = tv({
|
|
7890
|
+
base: "flex min-w-0 items-center",
|
|
7891
|
+
variants: {
|
|
7892
|
+
slot: {
|
|
7893
|
+
leading: [
|
|
7894
|
+
"w-full justify-between gap-gp-sm md:gap-gp-lg",
|
|
7895
|
+
"lg:w-auto lg:flex-wrap lg:justify-start"
|
|
7896
|
+
],
|
|
7897
|
+
trailing: "w-full gap-gp-md lg:w-auto lg:flex-wrap"
|
|
7898
|
+
}
|
|
7899
|
+
},
|
|
7900
|
+
defaultVariants: { slot: "trailing" }
|
|
7901
|
+
});
|
|
7902
|
+
const ganttTitle = tv({
|
|
7903
|
+
base: [
|
|
7904
|
+
// O ícone e o texto são um par; o `gap` aqui é DENTRO do par.
|
|
7905
|
+
"flex min-w-0 items-center gap-gp-md",
|
|
7906
|
+
// O vizinho da ESQUERDA é o `‹ Hoje ›`, e o título é TEXTO — sem borda nem
|
|
7907
|
+
// fundo pra criar respiro do lado dele. 6px somam com o gap do grupo e dão
|
|
7908
|
+
// 16px no desktop / 12px no mobile. (Enquanto o `‹ Hoje ›` vinha depois do
|
|
7909
|
+
// título, esta margem morava lá, pelo mesmo motivo espelhado.)
|
|
7910
|
+
"ml-sp-sm",
|
|
7911
|
+
"text-title-sm font-semibold text-fg-default",
|
|
7912
|
+
"[&>svg]:size-icon-md [&>svg]:shrink-0 [&>svg]:text-fg-muted"
|
|
7913
|
+
]
|
|
7914
|
+
});
|
|
7915
|
+
const ganttTitleText = tv({
|
|
7916
|
+
base: "min-w-0 truncate first-letter:uppercase"
|
|
7917
|
+
});
|
|
7918
|
+
const ganttViewSwitch = tv({
|
|
7919
|
+
base: [
|
|
7920
|
+
// `p-[3px]` fica literal: a escala `sp` vai 2 → 4px e não tem 3.
|
|
7921
|
+
// `max-md:hidden` — em <md este controle mora no menu de opções, como o
|
|
7922
|
+
// toggle Kanban/Lista da tabela mora no menu de Configurações.
|
|
7923
|
+
"max-md:hidden inline-flex shrink-0 items-center gap-gp-2xs p-[3px]",
|
|
7924
|
+
"h-form-lg rounded-radius-lg bg-bg-muted"
|
|
7925
|
+
]
|
|
7926
|
+
});
|
|
7927
|
+
const ganttViewSwitchButton = tv({
|
|
7928
|
+
base: [
|
|
7929
|
+
"grid h-[34px] min-w-[36px] place-items-center px-pad-md",
|
|
7930
|
+
"cursor-pointer rounded-radius-md border-0 bg-transparent outline-none",
|
|
7931
|
+
"text-body-sm font-normal text-fg-muted",
|
|
7932
|
+
"transition-[background-color,color,box-shadow] duration-150",
|
|
7933
|
+
"hover:text-fg-default",
|
|
7934
|
+
"focus-visible:shadow-sh-ring",
|
|
7935
|
+
"[&_svg]:size-[14px]"
|
|
7936
|
+
],
|
|
7937
|
+
variants: {
|
|
7938
|
+
isActive: {
|
|
7939
|
+
true: "bg-bg-accent font-semibold text-fg-default shadow-sh-sm",
|
|
7940
|
+
false: ""
|
|
7941
|
+
}
|
|
7942
|
+
},
|
|
7943
|
+
defaultVariants: { isActive: false }
|
|
7944
|
+
});
|
|
7945
|
+
const ganttToolbarDivider = tv({
|
|
7946
|
+
// `mx-sp-sm` = 6px e `w-sp-px` = 1px, os mesmos valores do `toolbarDivider`
|
|
7947
|
+
// da tabela — que os escreve literais. `h-[24px]` continua literal: a escala
|
|
7948
|
+
// `form` vai 28 → 32 e não tem 24.
|
|
7949
|
+
// `max-md:hidden`: em <md os dois grupos que ele separa não estão na barra
|
|
7950
|
+
// (foram pro menu), e um divisor sem nada de um dos lados é um risco solto.
|
|
7951
|
+
base: "max-md:hidden mx-sp-sm h-[24px] w-sp-px shrink-0 self-center bg-border-default"
|
|
7952
|
+
});
|
|
7953
|
+
const ganttNavGroup = tv({
|
|
7954
|
+
base: [
|
|
7955
|
+
// `max-md:hidden` — 142px de `‹ Hoje ›` numa barra de 375px. Em <md ele
|
|
7956
|
+
// aparece dentro do menu de opções, em largura cheia.
|
|
7957
|
+
"max-md:hidden inline-flex shrink-0 items-center",
|
|
7958
|
+
"[&>*:focus-visible]:relative [&>*:focus-visible]:z-[1]",
|
|
7959
|
+
"[&>*:first-child]:!rounded-r-none",
|
|
7960
|
+
"[&>*:last-child]:!rounded-l-none",
|
|
7961
|
+
"[&>*:not(:first-child):not(:last-child)]:!rounded-none",
|
|
7962
|
+
"[&>*]:border-y-border-subtle dark:[&>*]:border-y-border-input",
|
|
7963
|
+
"[&>*]:border-l-border-subtle dark:[&>*]:border-l-border-input",
|
|
7964
|
+
"[&>*:last-child]:border-r-border-subtle dark:[&>*:last-child]:border-r-border-input"
|
|
7965
|
+
]
|
|
7966
|
+
});
|
|
7967
|
+
const ganttMobileMenu = tv({
|
|
7968
|
+
slots: {
|
|
7969
|
+
trigger: "shrink-0 md:hidden",
|
|
7970
|
+
/*
|
|
7971
|
+
Sem largura de propósito. O `PopoverContent` do DS já carrega
|
|
7972
|
+
`data-mobile-sheet` + `max-md:w-full max-md:max-w-none`, e a regra do tema
|
|
7973
|
+
(`@media (width < 768px)`) prende o wrapper do Popper no rodapé com
|
|
7974
|
+
`width: 100% !important` — este menu vira bottom-sheet sozinho.
|
|
7975
|
+
|
|
7976
|
+
MEDIDO: um `w-[280px] max-w-[calc(100vw-32px)]` que eu havia escrito aqui
|
|
7977
|
+
resolvia 375px na tela, ou seja, era declaração morta. E não teria como
|
|
7978
|
+
valer acima de `md`: o `trigger` é `md:hidden`, então o popover nunca
|
|
7979
|
+
abre onde a largura importaria.
|
|
7980
|
+
*/
|
|
7981
|
+
content: "flex flex-col gap-px p-pad-sm",
|
|
7982
|
+
label: "px-pad-lg py-pad-sm text-caption-sm font-semibold uppercase tracking-wider text-fg-subtle",
|
|
7983
|
+
separator: "mx-pad-xs my-pad-xs h-px bg-border-default",
|
|
7984
|
+
field: "px-pad-lg pb-pad-sm",
|
|
7985
|
+
item: [
|
|
7986
|
+
"group/row relative flex w-full items-center gap-pad-lg",
|
|
7987
|
+
"rounded-radius-sm px-pad-lg py-pad-md",
|
|
7988
|
+
"cursor-pointer border-0 bg-transparent text-left outline-none",
|
|
7989
|
+
"text-body-sm font-medium text-fg-muted [&_svg]:text-fg-muted",
|
|
7990
|
+
"transition-colors duration-150",
|
|
7991
|
+
"hover:bg-bg-muted hover:text-fg-default hover:[&_svg]:text-fg-default",
|
|
7992
|
+
"focus-visible:bg-bg-muted focus-visible:text-fg-default",
|
|
7993
|
+
"[&_svg]:size-icon-sm [&_svg]:shrink-0"
|
|
7994
|
+
]
|
|
7995
|
+
},
|
|
7996
|
+
variants: {
|
|
7997
|
+
active: {
|
|
7998
|
+
true: { item: "bg-bg-brand-subtle text-fg-brand [&_svg]:text-fg-brand" },
|
|
7999
|
+
false: {}
|
|
8000
|
+
}
|
|
8001
|
+
},
|
|
8002
|
+
defaultVariants: { active: false }
|
|
8003
|
+
});
|
|
8004
|
+
const ganttMenuGroupFluid = tv({
|
|
8005
|
+
base: "flex w-full [&>*]:flex-1"
|
|
8006
|
+
});
|
|
8007
|
+
const ganttSkeleton = tv({
|
|
8008
|
+
slots: {
|
|
8009
|
+
root: "flex min-h-0 flex-1 animate-pulse flex-col overflow-hidden",
|
|
8010
|
+
/** A faixa do cabeçalho do eixo — mesma altura do `ganttHead`. */
|
|
8011
|
+
head: "flex shrink-0 items-center gap-gp-2xl border-b border-border-default bg-bg-table-head px-pad-2xl",
|
|
8012
|
+
/** Linha do corpo — a MESMA altura de `GANTT_ROW_HEIGHT_PX`. */
|
|
8013
|
+
row: "flex shrink-0 items-center gap-gp-2xl border-b border-border-subtle px-pad-2xl",
|
|
8014
|
+
/** O retângulo cinza que faz as vezes de texto ou de barra. */
|
|
8015
|
+
bar: "rounded-radius-sm bg-bg-muted",
|
|
8016
|
+
/** Painel esquerdo da timeline — só ele tem borda de separação. */
|
|
8017
|
+
pane: "flex shrink-0 flex-col overflow-hidden border-r border-border-default",
|
|
8018
|
+
canvas: "flex min-w-0 flex-1 flex-col overflow-hidden",
|
|
8019
|
+
/** Célula da grade de mês. */
|
|
8020
|
+
cell: "flex flex-col gap-gp-xs border-b border-r border-border-subtle p-pad-md",
|
|
8021
|
+
grid: "grid min-h-0 flex-1 grid-cols-7"
|
|
8022
|
+
}
|
|
8023
|
+
});
|
|
8024
|
+
const ganttSearch = tv({
|
|
8025
|
+
base: [
|
|
8026
|
+
"relative flex cursor-text items-center gap-gp-md",
|
|
8027
|
+
"h-form-lg rounded-radius-lg px-pad-lg",
|
|
8028
|
+
"bg-bg-surface dark:bg-bg-muted",
|
|
8029
|
+
"border border-border-subtle dark:border-border-input",
|
|
8030
|
+
"shadow-sh-sm dark:shadow-sh-none",
|
|
8031
|
+
// #8 — expande no foco, igual à busca do `TableToolbar`: 200 → 300px.
|
|
8032
|
+
// A animação usa a mesma curva e duração de lá (220ms).
|
|
8033
|
+
"min-w-0 flex-1 lg:flex-initial lg:w-[200px] lg:focus-within:w-[300px]",
|
|
8034
|
+
"transition-[width,border-color,background-color,box-shadow] duration-[220ms] ease-[cubic-bezier(0.4,0,0.2,1)]",
|
|
8035
|
+
"focus-within:border-border-brand focus-within:ring-4 focus-within:ring-ring-brand",
|
|
8036
|
+
"[&_svg]:size-icon-sm [&_svg]:shrink-0 [&_svg]:text-fg-subtle"
|
|
8037
|
+
]
|
|
8038
|
+
});
|
|
8039
|
+
const ganttSearchInput = tv({
|
|
8040
|
+
base: [
|
|
8041
|
+
"min-w-0 flex-1 border-0 bg-transparent outline-none",
|
|
8042
|
+
"text-body-sm font-normal text-fg-default",
|
|
8043
|
+
"placeholder:text-fg-muted"
|
|
8044
|
+
]
|
|
8045
|
+
});
|
|
8046
|
+
const ganttAppliedRow = tv({
|
|
8047
|
+
base: [
|
|
8048
|
+
"flex items-center gap-gp-md",
|
|
8049
|
+
"mt-pad-2xl pt-pad-2xl border-t border-border-default",
|
|
8050
|
+
"flex-nowrap overflow-x-auto sm:flex-wrap sm:overflow-x-visible",
|
|
8051
|
+
"[scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
|
8052
|
+
]
|
|
8053
|
+
});
|
|
8054
|
+
const ganttAppliedChip = tv({
|
|
8055
|
+
base: [
|
|
8056
|
+
"inline-flex shrink-0 items-center gap-gp-md",
|
|
8057
|
+
"h-form-md pl-pad-sm pr-pad-lg rounded-radius-lg",
|
|
8058
|
+
"bg-bg-surface dark:bg-bg-muted",
|
|
8059
|
+
"border border-dashed border-border-input",
|
|
8060
|
+
"text-body-xs font-normal text-fg-default",
|
|
8061
|
+
"transition-[background-color,border-color] duration-150"
|
|
8062
|
+
],
|
|
8063
|
+
variants: {
|
|
8064
|
+
/** Clicável (abre as opções do campo). Sempre `true` hoje; ver o part. */
|
|
8065
|
+
interactive: {
|
|
8066
|
+
true: [
|
|
8067
|
+
"cursor-pointer",
|
|
8068
|
+
"hover:bg-bg-muted-hover hover:border-border-default",
|
|
8069
|
+
"dark:hover:bg-bg-accent",
|
|
8070
|
+
"focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand"
|
|
8071
|
+
],
|
|
8072
|
+
false: ""
|
|
8073
|
+
}
|
|
8074
|
+
},
|
|
8075
|
+
defaultVariants: { interactive: false }
|
|
8076
|
+
});
|
|
8077
|
+
const ganttAppliedChipRemove = tv({
|
|
8078
|
+
base: [
|
|
8079
|
+
"grid size-[18px] shrink-0 place-items-center rounded-radius-full",
|
|
8080
|
+
"cursor-pointer border-0 bg-transparent p-0 text-fg-subtle outline-none",
|
|
8081
|
+
"transition-colors duration-150 hover:text-fg-default",
|
|
8082
|
+
"focus-visible:shadow-sh-ring",
|
|
8083
|
+
"[&_svg]:size-[14px]"
|
|
8084
|
+
]
|
|
8085
|
+
});
|
|
8086
|
+
const ganttAppliedChipName = tv({
|
|
8087
|
+
base: "font-semibold text-fg-default"
|
|
8088
|
+
});
|
|
8089
|
+
const ganttAppliedChipOp = tv({
|
|
8090
|
+
base: "text-caption-sm text-fg-muted"
|
|
8091
|
+
});
|
|
8092
|
+
const ganttAppliedChipValue = tv({
|
|
8093
|
+
base: [
|
|
8094
|
+
"inline-flex h-[22px] items-center px-pad-md",
|
|
8095
|
+
"rounded-radius-sm bg-bg-muted dark:bg-bg-accent",
|
|
8096
|
+
"text-body-xs font-medium text-fg-default"
|
|
8097
|
+
]
|
|
8098
|
+
});
|
|
8099
|
+
const ganttClearLink = tv({
|
|
8100
|
+
base: [
|
|
8101
|
+
"ml-pad-sm shrink-0 cursor-pointer border-0 bg-transparent p-0",
|
|
8102
|
+
"text-body-xs font-medium text-fg-brand outline-none",
|
|
8103
|
+
"underline-offset-2 transition-opacity duration-150",
|
|
8104
|
+
"hover:underline focus-visible:underline"
|
|
8105
|
+
]
|
|
8106
|
+
});
|
|
8107
|
+
const ganttFilterDot = tv({
|
|
8108
|
+
base: [
|
|
8109
|
+
"pointer-events-none absolute -right-px -top-px",
|
|
8110
|
+
"size-[7px] rounded-radius-full",
|
|
8111
|
+
"bg-bg-brand ring-2 ring-bg-canvas"
|
|
8112
|
+
]
|
|
8113
|
+
});
|
|
8114
|
+
const ganttFilterGroup = tv({
|
|
8115
|
+
base: [
|
|
8116
|
+
"flex flex-col py-pad-lg",
|
|
8117
|
+
"border-b border-border-subtle last:border-b-0"
|
|
8118
|
+
]
|
|
8119
|
+
});
|
|
8120
|
+
const ganttFilterGroupHead = tv({
|
|
8121
|
+
base: [
|
|
8122
|
+
"flex w-full items-center justify-between gap-gp-md",
|
|
8123
|
+
"px-pad-xl py-pad-md text-left",
|
|
8124
|
+
"transition-colors duration-150 hover:bg-bg-muted",
|
|
8125
|
+
"focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand"
|
|
8126
|
+
]
|
|
8127
|
+
});
|
|
8128
|
+
const ganttFilterGroupTitle = tv({
|
|
8129
|
+
base: [
|
|
8130
|
+
"flex min-w-0 items-baseline gap-gp-sm",
|
|
8131
|
+
"text-caption-md font-semibold text-fg-default"
|
|
8132
|
+
]
|
|
8133
|
+
});
|
|
8134
|
+
const ganttFilterGroupChevron = tv({
|
|
8135
|
+
base: "size-icon-sm shrink-0 text-fg-subtle transition-transform duration-200",
|
|
8136
|
+
variants: {
|
|
8137
|
+
open: { true: "rotate-0", false: "-rotate-90" }
|
|
8138
|
+
},
|
|
8139
|
+
defaultVariants: { open: true }
|
|
8140
|
+
});
|
|
8141
|
+
const ganttFilterGroupActions = tv({
|
|
8142
|
+
base: [
|
|
8143
|
+
"flex items-center gap-gp-lg px-pad-xl pb-pad-sm pt-pad-2xs",
|
|
8144
|
+
"[&>button]:text-caption-sm [&>button]:font-medium [&>button]:text-fg-brand",
|
|
8145
|
+
"[&>button]:transition-colors [&>button]:duration-150",
|
|
8146
|
+
"[&>button:hover]:text-fg-default",
|
|
8147
|
+
"[&>button:disabled]:cursor-not-allowed [&>button:disabled]:text-fg-subtle",
|
|
8148
|
+
"[&>button:focus-visible]:outline-none [&>button:focus-visible]:ring-4",
|
|
8149
|
+
"[&>button:focus-visible]:ring-ring-brand [&>button:focus-visible]:rounded-radius-xs"
|
|
8150
|
+
]
|
|
8151
|
+
});
|
|
8152
|
+
const ganttFilterSearch = tv({
|
|
8153
|
+
base: [
|
|
8154
|
+
"mx-pad-xl mb-pad-sm flex cursor-text items-center gap-gp-sm",
|
|
8155
|
+
"h-form-md rounded-radius-md px-pad-md",
|
|
8156
|
+
"bg-bg-surface dark:bg-bg-muted",
|
|
8157
|
+
"border border-border-subtle dark:border-border-input",
|
|
8158
|
+
"transition-[border-color,box-shadow] duration-150",
|
|
8159
|
+
"focus-within:border-border-brand focus-within:ring-4 focus-within:ring-ring-brand",
|
|
8160
|
+
"[&_svg]:size-icon-sm [&_svg]:shrink-0 [&_svg]:text-fg-subtle"
|
|
8161
|
+
]
|
|
8162
|
+
});
|
|
8163
|
+
const ganttFilterSearchInput = tv({
|
|
8164
|
+
base: [
|
|
8165
|
+
"min-w-0 flex-1 border-0 bg-transparent outline-none",
|
|
8166
|
+
"text-body-sm font-normal text-fg-default",
|
|
8167
|
+
"placeholder:text-fg-muted"
|
|
8168
|
+
]
|
|
8169
|
+
});
|
|
8170
|
+
const ganttFilterInput = tv({
|
|
8171
|
+
base: [
|
|
8172
|
+
"min-w-0 flex-1 h-form-md rounded-radius-md px-pad-md",
|
|
8173
|
+
"bg-bg-surface dark:bg-bg-muted",
|
|
8174
|
+
"border border-border-subtle dark:border-border-input",
|
|
8175
|
+
"text-body-sm font-normal text-fg-default outline-none",
|
|
8176
|
+
"placeholder:text-fg-muted",
|
|
8177
|
+
"transition-[border-color,box-shadow] duration-150",
|
|
8178
|
+
"focus-visible:border-border-brand focus-visible:ring-4 focus-visible:ring-ring-brand",
|
|
8179
|
+
// O seletor nativo de data vem escuro no dark sem isto.
|
|
8180
|
+
"dark:[color-scheme:dark]"
|
|
8181
|
+
]
|
|
8182
|
+
});
|
|
8183
|
+
const ganttFilterRange = tv({
|
|
8184
|
+
base: "flex items-center gap-gp-sm px-pad-xl pb-pad-sm"
|
|
8185
|
+
});
|
|
8186
|
+
const ganttFilterRangeSep = tv({
|
|
8187
|
+
base: "shrink-0 text-caption-sm text-fg-muted"
|
|
8188
|
+
});
|
|
8189
|
+
const ganttFilterEmpty = tv({
|
|
8190
|
+
base: "px-pad-xl py-pad-md text-body-sm text-fg-muted"
|
|
8191
|
+
});
|
|
8192
|
+
const ganttFilterOption = tv({
|
|
8193
|
+
base: [
|
|
8194
|
+
"flex w-full min-w-0 cursor-pointer items-center gap-gp-md",
|
|
8195
|
+
"px-pad-xl py-pad-md",
|
|
8196
|
+
"transition-colors duration-150 hover:bg-bg-muted"
|
|
8197
|
+
]
|
|
8198
|
+
});
|
|
8199
|
+
const ganttFilterOptionBox = tv({
|
|
8200
|
+
base: "shrink-0 transition-colors duration-150",
|
|
8201
|
+
variants: {
|
|
8202
|
+
colorKey: {
|
|
8203
|
+
"chart-1": "border-chart-1/60 data-[state=checked]:!bg-chart-1 data-[state=checked]:!border-chart-1",
|
|
8204
|
+
"chart-2": "border-chart-2/60 data-[state=checked]:!bg-chart-2 data-[state=checked]:!border-chart-2",
|
|
8205
|
+
"chart-3": "border-chart-3/60 data-[state=checked]:!bg-chart-3 data-[state=checked]:!border-chart-3",
|
|
8206
|
+
"chart-4": "border-chart-4/60 data-[state=checked]:!bg-chart-4 data-[state=checked]:!border-chart-4",
|
|
8207
|
+
"chart-5": "border-chart-5/60 data-[state=checked]:!bg-chart-5 data-[state=checked]:!border-chart-5",
|
|
8208
|
+
brand: "border-border-brand data-[state=checked]:!bg-bg-brand",
|
|
8209
|
+
success: "border-border-success-muted data-[state=checked]:!bg-bg-success data-[state=checked]:!border-bg-success",
|
|
8210
|
+
warning: "border-border-warning-muted data-[state=checked]:!bg-bg-warning data-[state=checked]:!border-bg-warning",
|
|
8211
|
+
danger: "border-border-danger-muted data-[state=checked]:!bg-bg-danger data-[state=checked]:!border-bg-danger",
|
|
8212
|
+
info: "border-border-info-muted data-[state=checked]:!bg-bg-info data-[state=checked]:!border-bg-info",
|
|
8213
|
+
neutral: "border-border-default"
|
|
8214
|
+
}
|
|
8215
|
+
},
|
|
8216
|
+
defaultVariants: { colorKey: "neutral" }
|
|
8217
|
+
});
|
|
8218
|
+
const ganttFilterOptionLabel = tv({
|
|
8219
|
+
base: "min-w-0 flex-1 truncate text-body-sm text-fg-default"
|
|
8220
|
+
});
|
|
8221
|
+
const ganttFilterOptionCount = tv({
|
|
8222
|
+
base: "shrink-0 text-caption-sm tabular-nums text-fg-subtle"
|
|
8223
|
+
});
|
|
8224
|
+
const ganttBody = tv({
|
|
8225
|
+
base: "flex min-h-0 flex-1 overflow-hidden",
|
|
8226
|
+
variants: {
|
|
8227
|
+
framed: {
|
|
8228
|
+
true: "rounded-radius-xl border border-border-default bg-bg-surface",
|
|
8229
|
+
false: ""
|
|
8230
|
+
}
|
|
8231
|
+
},
|
|
8232
|
+
defaultVariants: { framed: true }
|
|
8233
|
+
});
|
|
8234
|
+
const ganttGridPane = tv({
|
|
8235
|
+
base: [
|
|
8236
|
+
"flex min-h-0 shrink-0 flex-col",
|
|
8237
|
+
/**
|
|
8238
|
+
* ⛔ SEM borda direita — e isso é a correção de um defeito que eu criei.
|
|
8239
|
+
*
|
|
8240
|
+
* A grade precisa de uma linha grossa separando os dois painéis (é a âncora
|
|
8241
|
+
* de leitura: o olho volta pra ela a cada linha). Mas essa linha JÁ EXISTE:
|
|
8242
|
+
* é o `ganttSplitter`, que é `w-[3px] bg-bg-scrollbar-thumb/70` e vive logo depois
|
|
8243
|
+
* deste painel, sem vão nenhum. Pra mudar a espessura da separação, mexa
|
|
8244
|
+
* NELE — não acrescente borda aqui.
|
|
8245
|
+
*
|
|
8246
|
+
* Pôr `border-r-[3px] border-r-border-default` aqui somava **6px contíguos
|
|
8247
|
+
* da cor idêntica** — medido: borda de 478→481 e divisor de 481→484, ambos
|
|
8248
|
+
* `oklch(0.2645 0 0)`, vão de 0. Lia como uma borda com um segundo tracinho
|
|
8249
|
+
* grudado nela.
|
|
8250
|
+
*
|
|
8251
|
+
* A lição: antes de adicionar uma divisória, procure a que já está ali. O
|
|
8252
|
+
* divisor não é decoração — é o alvo de arraste, e ele vira verde no hover.
|
|
8253
|
+
* Duplicá-lo não engrossa a linha, cria duas.
|
|
8254
|
+
*/
|
|
8255
|
+
// ⚠️ `overflow-hidden` e NÃO `overflow-y-auto`: o scroll vertical é do
|
|
8256
|
+
// painel direito, e os dois rolam juntos por `scrollTop` espelhado. Dois
|
|
8257
|
+
// scrollbars independentes dessincronizam as linhas na primeira rolagem.
|
|
8258
|
+
"overflow-hidden",
|
|
8259
|
+
/**
|
|
8260
|
+
* #6 — sombra pra a direita e `z-[2]`: o painel passa POR CIMA do canvas.
|
|
8261
|
+
*
|
|
8262
|
+
* Sem isso, a barra que começa no primeiro dia da janela encostava na
|
|
8263
|
+
* divisória e parecia continuar por baixo da grade. Com a sombra, a grade
|
|
8264
|
+
* lê como uma camada acima — e o corte da barra na borda fica intencional.
|
|
8265
|
+
*
|
|
8266
|
+
* ## Por que `10px 0 36px -18px` e não `4px 0 12px -4px`
|
|
8267
|
+
*
|
|
8268
|
+
* O que decide se a sombra lê como GRADIENTE ou como uma segunda linha é a
|
|
8269
|
+
* posição da borda do retângulo dela, que é `offsetX + spread` relativo à
|
|
8270
|
+
* borda do elemento — não o blur.
|
|
8271
|
+
*
|
|
8272
|
+
* antes: 4 + (-4) = 0 → retângulo termina EXATO na borda do painel.
|
|
8273
|
+
* No blur, a borda do retângulo é o ponto de ~50%
|
|
8274
|
+
* da opacidade: a sombra nascia forte encostada na
|
|
8275
|
+
* divisória e morria em 6px. É a faixa escura que
|
|
8276
|
+
* se lia como "tem mais um pixel depois da borda".
|
|
8277
|
+
* agora: 10 + (-18) = -8 → retângulo termina 8px DENTRO do painel, onde
|
|
8278
|
+
* ninguém vê. O que escapa pra fora é só a cauda
|
|
8279
|
+
* do blur: começa em ~28% na divisória e decai por
|
|
8280
|
+
* 10px (era 6).
|
|
8281
|
+
*
|
|
8282
|
+
* Mais larga e mais suave ao mesmo tempo, que era o pedido — e são coisas
|
|
8283
|
+
* independentes: aumentar o blur sozinho alarga a faixa forte junto.
|
|
8284
|
+
*
|
|
8285
|
+
* Dark em 0.55 contra 0.14 do light = 3.9×, acima do mínimo de 2× da L-011.
|
|
8286
|
+
*/
|
|
8287
|
+
"relative z-[2] shadow-[10px_0_36px_-18px_oklch(0_0_0_/_0.14)]",
|
|
8288
|
+
"dark:shadow-[10px_0_36px_-18px_oklch(0_0_0_/_0.55)]"
|
|
8289
|
+
]
|
|
8290
|
+
});
|
|
8291
|
+
const ganttSplitter = tv({
|
|
8292
|
+
base: [
|
|
8293
|
+
"group/split relative w-[3px] shrink-0 cursor-col-resize",
|
|
8294
|
+
"bg-bg-scrollbar-thumb/70 transition-colors duration-150",
|
|
8295
|
+
"hover:bg-border-brand",
|
|
8296
|
+
"after:absolute after:inset-y-0 after:-left-1 after:-right-1 after:content-['']",
|
|
8297
|
+
"focus-visible:outline-none focus-visible:bg-border-brand"
|
|
8298
|
+
],
|
|
8299
|
+
variants: {
|
|
8300
|
+
active: { true: "bg-border-brand", false: "" }
|
|
8301
|
+
},
|
|
8302
|
+
defaultVariants: { active: false }
|
|
8303
|
+
});
|
|
8304
|
+
const ganttTimelinePane = tv({
|
|
8305
|
+
base: "flex min-w-0 min-h-0 flex-1 flex-col"
|
|
8306
|
+
});
|
|
8307
|
+
const ganttHead = tv({
|
|
8308
|
+
base: [
|
|
8309
|
+
/**
|
|
8310
|
+
* ⚠️ `z-[5]`, e o número não é folga — é a correção de um defeito.
|
|
8311
|
+
*
|
|
8312
|
+
* Era `z-[3]`, **o MESMO** do `ganttLinksLayer`. Empate de z-index no mesmo
|
|
8313
|
+
* contexto de empilhamento é resolvido por ORDEM NO DOM, e o canvas vem
|
|
8314
|
+
* depois do cabeçalho: ao rolar na vertical, as setas de vínculo passavam
|
|
8315
|
+
* POR CIMA dos rótulos do eixo.
|
|
8316
|
+
*
|
|
8317
|
+
* 5 e não 4 porque a barra arrastada é `z-[4]` (`ganttBar` variante
|
|
8318
|
+
* `dragging`) e o conector de vínculo é `z-[3]`: o cabeçalho tem que vencer
|
|
8319
|
+
* TODO o conteúdo do canvas, não só as setas.
|
|
8320
|
+
*
|
|
8321
|
+
* ⛔ Não baixe o `ganttLinksLayer` em vez disto: ele precisa ficar acima das
|
|
8322
|
+
* barras (`z-[1]` do rótulo) pra a seta não desaparecer atrás delas.
|
|
8323
|
+
*/
|
|
8324
|
+
"sticky top-0 z-[5] shrink-0",
|
|
8325
|
+
"border-b border-border-default bg-bg-table-head"
|
|
8326
|
+
]
|
|
8327
|
+
});
|
|
8328
|
+
const ganttHeadRow = tv({
|
|
8329
|
+
base: "relative",
|
|
8330
|
+
variants: {
|
|
8331
|
+
level: {
|
|
8332
|
+
/** Nível de cima: mês/ano. Divisória mais forte entre grupos. */
|
|
8333
|
+
group: "border-b border-border-subtle",
|
|
8334
|
+
/** Nível de baixo: a unidade da grade. */
|
|
8335
|
+
unit: ""
|
|
8336
|
+
}
|
|
8337
|
+
},
|
|
8338
|
+
defaultVariants: { level: "unit" }
|
|
8339
|
+
});
|
|
8340
|
+
const ganttHeadCell = tv({
|
|
8341
|
+
base: [
|
|
8342
|
+
// `absolute top-0` — a posição vem de `dateToX`, ver `ganttHeadRow`.
|
|
8343
|
+
"absolute top-0 flex items-center justify-center overflow-hidden",
|
|
8344
|
+
"border-r border-border-subtle last:border-r-0",
|
|
8345
|
+
"px-pad-xs text-center"
|
|
8346
|
+
],
|
|
8347
|
+
variants: {
|
|
8348
|
+
level: {
|
|
8349
|
+
group: "h-[42px] text-caption-md font-semibold text-fg-default first-letter:uppercase",
|
|
8350
|
+
/**
|
|
8351
|
+
* #10 — a unidade EMPILHA: abreviação do dia em cima, número embaixo e
|
|
8352
|
+
* maior. Lado a lado (`S 21`), o par competia por largura com a
|
|
8353
|
+
* divisória; empilhado, a data ganha peso e a abreviação vira legenda.
|
|
8354
|
+
*/
|
|
8355
|
+
unit: "h-[42px] flex-col justify-center gap-0 font-normal text-fg-muted"
|
|
8356
|
+
},
|
|
8357
|
+
/** Fim de semana: coluna sombreada, e o rótulo desce um tom. */
|
|
8358
|
+
weekend: { true: "bg-bg-subtle text-fg-subtle", false: "" },
|
|
8359
|
+
today: { true: "bg-bg-brand-subtle font-semibold text-fg-brand", false: "" },
|
|
8360
|
+
/** #2 — coluna selecionada por clique: cabeçalho no verde da marca. */
|
|
8361
|
+
selected: { true: "bg-bg-brand-subtle", false: "" },
|
|
8362
|
+
/**
|
|
8363
|
+
* #1 — última unidade de um grupo (o último dia do mês, em `day`).
|
|
8364
|
+
*
|
|
8365
|
+
* Divisória mais grossa e mais clara, atravessando o cabeçalho inteiro e o
|
|
8366
|
+
* conteúdo: é o que faz o mês ler como um BLOCO em vez de uma fileira
|
|
8367
|
+
* contínua de dias. O limite vem de `axis.groups`, então acompanha a escala
|
|
8368
|
+
* — vira mês em `day`/`week` e ano em `month`/`quarter`.
|
|
8369
|
+
*
|
|
8370
|
+
* ⚠️ `border-input` foi ESCOLHIDO por medição, não por nome. ΔL de cada
|
|
8371
|
+
* candidato contra a superfície onde a linha vive:
|
|
8372
|
+
*
|
|
8373
|
+
* token dark (bg 0.225) light (bg 1.0) × a linha comum
|
|
8374
|
+
* border-subtle (comum) 0.031 0.069 1.0×
|
|
8375
|
+
* border-default 0.040 0.092 1.3× / 1.3×
|
|
8376
|
+
* border-input 0.062 0.124 2.0× / 1.8×
|
|
8377
|
+
* chart-grid 0.093 0.092 3.0× / 1.3×
|
|
8378
|
+
*
|
|
8379
|
+
* Três voltas até aqui, e cada uma ensinou algo:
|
|
8380
|
+
*
|
|
8381
|
+
* 1. `border-default` a 2px ficou **invisível** — 0.040 contra 0.031 é a MESMA
|
|
8382
|
+
* linha. Eu tinha medido que o valor do token mudou, não que a mudança se
|
|
8383
|
+
* vê; espessura não compra o que falta em contraste.
|
|
8384
|
+
* 2. `chart-grid` a 3px resolveu o contraste e passou do ponto nos dois
|
|
8385
|
+
* eixos: 3px vira moldura, e 3× a linha comum vira estrutura competindo
|
|
8386
|
+
* com a barra.
|
|
8387
|
+
* 3. `border-input` a 2px é o degrau do meio, e é o único candidato que fica
|
|
8388
|
+
* em ~2× nos DOIS modos — por isso não precisa de par `dark:`. Menos
|
|
8389
|
+
* classe e sem risco de os dois modos divergirem na próxima edição.
|
|
8390
|
+
*
|
|
8391
|
+
* ⛔ Não meça isto durante a transição: a coluna tem `transition-colors`, e
|
|
8392
|
+
* ler no meio dela devolve o valor interpolado (serializado em `oklab`, que é
|
|
8393
|
+
* a pista). Já me enganou uma vez nesta mesma linha.
|
|
8394
|
+
*
|
|
8395
|
+
* ⛔ Não troque por um token de marca: o verde já significa "hoje" e
|
|
8396
|
+
* "selecionado" aqui, e a virada de mês não é nem uma nem outra.
|
|
8397
|
+
*/
|
|
8398
|
+
boundary: { true: "border-r-[2px] border-r-border-input", false: "" }
|
|
8399
|
+
},
|
|
8400
|
+
defaultVariants: {
|
|
8401
|
+
level: "unit",
|
|
8402
|
+
weekend: false,
|
|
8403
|
+
today: false,
|
|
8404
|
+
selected: false,
|
|
8405
|
+
boundary: false
|
|
8406
|
+
}
|
|
8407
|
+
});
|
|
8408
|
+
const ganttHeadWeekday = tv({
|
|
8409
|
+
base: "text-caption-xs leading-none text-fg-subtle",
|
|
8410
|
+
variants: {
|
|
8411
|
+
weekend: { true: "text-fg-subtle/70", false: "" }
|
|
8412
|
+
},
|
|
8413
|
+
defaultVariants: { weekend: false }
|
|
8414
|
+
});
|
|
8415
|
+
const ganttHeadDayNumber = tv({
|
|
8416
|
+
base: "mt-[2px] text-body-sm leading-none tabular-nums",
|
|
8417
|
+
variants: {
|
|
8418
|
+
today: { true: "font-semibold text-fg-brand", false: "text-fg-default" }
|
|
8419
|
+
},
|
|
8420
|
+
defaultVariants: { today: false }
|
|
8421
|
+
});
|
|
8422
|
+
const ganttGridHead = tv({
|
|
8423
|
+
base: [
|
|
8424
|
+
"flex shrink-0 items-center",
|
|
8425
|
+
"border-b border-border-default bg-bg-table-head"
|
|
8426
|
+
]
|
|
8427
|
+
});
|
|
8428
|
+
const ganttGridHeadCell = tv({
|
|
8429
|
+
base: [
|
|
8430
|
+
"flex shrink-0 items-center gap-gp-2xs overflow-hidden",
|
|
8431
|
+
// #6: `px-pad-xl` (14px) e não `pad-lg` — o conteúdo encostava nas duas
|
|
8432
|
+
// bordas da coluna, e a primeira célula ainda soma o recuo da hierarquia.
|
|
8433
|
+
"px-pad-2xl text-caption-sm font-semibold text-fg-muted",
|
|
8434
|
+
// Mesmo respiro da célula, senão o rótulo da coluna e o dado dela ficam com
|
|
8435
|
+
// margens diferentes na borda do painel.
|
|
8436
|
+
"last:pr-pad-4xl"
|
|
8437
|
+
],
|
|
8438
|
+
variants: {
|
|
8439
|
+
align: {
|
|
8440
|
+
left: "justify-start text-left",
|
|
8441
|
+
center: "justify-center text-center",
|
|
8442
|
+
right: "justify-end text-right"
|
|
8443
|
+
}
|
|
8444
|
+
},
|
|
8445
|
+
defaultVariants: { align: "left" }
|
|
8446
|
+
});
|
|
8447
|
+
const ganttGridRow = tv({
|
|
8448
|
+
base: [
|
|
8449
|
+
"group/row flex shrink-0 items-center",
|
|
8450
|
+
"border-b border-border-subtle",
|
|
8451
|
+
"transition-colors duration-150",
|
|
8452
|
+
"hover:bg-bg-subtle"
|
|
8453
|
+
],
|
|
8454
|
+
variants: {
|
|
8455
|
+
type: {
|
|
8456
|
+
task: "",
|
|
8457
|
+
/** `summary` é agrupador: peso maior e fundo levemente destacado. */
|
|
8458
|
+
summary: "bg-bg-subtle/60 font-semibold",
|
|
8459
|
+
milestone: ""
|
|
8460
|
+
},
|
|
8461
|
+
/**
|
|
8462
|
+
* #7 — linha selecionada, no verde da marca.
|
|
8463
|
+
*
|
|
8464
|
+
* Mesma leitura da linha selecionada do `DataTable`: a cor da marca diz "é
|
|
8465
|
+
* esta que estou olhando". Vence o hover no `compoundVariant` abaixo —
|
|
8466
|
+
* senão passar o mouse sobre a linha selecionada a apagava.
|
|
8467
|
+
*/
|
|
8468
|
+
selected: { true: "", false: "" },
|
|
8469
|
+
/**
|
|
8470
|
+
* #3 — hover CRUZADO, controlado por estado e não por `:hover` do CSS.
|
|
8471
|
+
*
|
|
8472
|
+
* `:hover` só acende o elemento sob o cursor, e aqui a linha vive em DOIS
|
|
8473
|
+
* painéis irmãos: passar o mouse no nome tem que acender a faixa da barra e
|
|
8474
|
+
* vice-versa. Nenhum seletor CSS alcança um irmão de outro contêiner, então
|
|
8475
|
+
* o índice da linha sob o cursor é estado do React.
|
|
8476
|
+
*
|
|
8477
|
+
* ⚠️ `bg-bg-muted` e não `bg-bg-subtle`: no dark, `subtle` é
|
|
8478
|
+
* `oklch(1 0 0 / 0.01)` — 1% de branco, que é **invisível** na prática.
|
|
8479
|
+
* Medi o hover e o antes/depois diferiam em 1 centésimo de alfa. `muted` é
|
|
8480
|
+
* 3%, e ainda foi preciso somar a borda pra o realce se ler.
|
|
8481
|
+
*/
|
|
8482
|
+
hovered: { true: "bg-bg-muted", false: "" }
|
|
8483
|
+
},
|
|
8484
|
+
compoundVariants: [
|
|
8485
|
+
// Selecionada vence hover — passar o mouse não pode apagar a seleção.
|
|
8486
|
+
{ selected: true, class: "bg-bg-brand-subtle" },
|
|
8487
|
+
{ selected: true, hovered: true, class: "bg-bg-brand-subtle-hover" }
|
|
8488
|
+
],
|
|
8489
|
+
defaultVariants: { type: "task", selected: false, hovered: false }
|
|
8490
|
+
});
|
|
8491
|
+
const ganttGridCell = tv({
|
|
8492
|
+
base: [
|
|
8493
|
+
/**
|
|
8494
|
+
* `relative` é a origem dos conectores de árvore; `self-stretch` é o que os
|
|
8495
|
+
* faz FUNCIONAR.
|
|
8496
|
+
*
|
|
8497
|
+
* ⚠️ Sem `self-stretch`, a célula encolhe pro conteúdo: medido, **32px numa
|
|
8498
|
+
* linha de 48**, porque a linha é `flex items-center`. O conector vivia num
|
|
8499
|
+
* `inset-y-0` de 32px, sobrava 8px em cima e 8 embaixo, e as verticais de
|
|
8500
|
+
* linhas consecutivas NÃO SE TOCAVAM — a árvore virava uma fileira de
|
|
8501
|
+
* tracinhos soltos em vez de um eixo contínuo.
|
|
8502
|
+
*
|
|
8503
|
+
* O `items-center` interno continua centrando o conteúdo; só a caixa cresce.
|
|
8504
|
+
*/
|
|
8505
|
+
"relative self-stretch flex min-w-0 shrink-0 items-center gap-gp-sm overflow-hidden px-pad-2xl",
|
|
8506
|
+
/**
|
|
8507
|
+
* A ÚLTIMA célula respira 24px à direita, não 16.
|
|
8508
|
+
*
|
|
8509
|
+
* Medido: o conteúdo da última coluna terminava a ~16px do divisor de 3px, e
|
|
8510
|
+
* as duas linhas verticais (a borda da célula e o divisor) somadas ao texto
|
|
8511
|
+
* apertado ali faziam a coluna parecer cortada. 24px separa "fim do dado" de
|
|
8512
|
+
* "fim do painel".
|
|
8513
|
+
*/
|
|
8514
|
+
"last:pr-pad-4xl",
|
|
8515
|
+
// ⚠️ Sem cor na base: quem decide é a variante `tone`. Com `text-fg-default`
|
|
8516
|
+
// aqui, as duas classes empatavam e a ordem no CSS gerado decidia.
|
|
8517
|
+
"text-body-sm"
|
|
8518
|
+
],
|
|
8519
|
+
variants: {
|
|
8520
|
+
align: {
|
|
8521
|
+
left: "justify-start",
|
|
8522
|
+
center: "justify-center",
|
|
8523
|
+
right: "justify-end"
|
|
8524
|
+
},
|
|
8525
|
+
/** Coluna de número/data: `tabular-nums` pra alinhar dígito com dígito. */
|
|
8526
|
+
numeric: { true: "tabular-nums", false: "" },
|
|
8527
|
+
/**
|
|
8528
|
+
* #6 — hierarquia dentro da linha.
|
|
8529
|
+
*
|
|
8530
|
+
* O nome da tarefa é a informação; data, duração e responsável são apoio.
|
|
8531
|
+
* Com todos em `fg-default` e o mesmo peso, as quatro colunas disputavam a
|
|
8532
|
+
* atenção e a grade lia como um bloco de texto. `muted` + `font-normal`
|
|
8533
|
+
* recua o apoio sem escondê-lo.
|
|
8534
|
+
*/
|
|
8535
|
+
tone: {
|
|
8536
|
+
default: "text-fg-default",
|
|
8537
|
+
muted: "font-normal text-fg-muted"
|
|
8538
|
+
}
|
|
8539
|
+
},
|
|
8540
|
+
defaultVariants: { align: "left", numeric: false, tone: "default" }
|
|
8541
|
+
});
|
|
8542
|
+
const ganttGridGroupCell = tv({
|
|
8543
|
+
base: [
|
|
8544
|
+
// `self-stretch` pelo mesmo motivo da célula — ver a nota do `ganttGridCell`.
|
|
8545
|
+
"relative self-stretch flex min-w-0 flex-1 items-center gap-gp-sm overflow-hidden",
|
|
8546
|
+
// `pr-pad-4xl` porque é ele que carrega o `trailing` (o chip de status) — e
|
|
8547
|
+
// era o chip que encostava no divisor.
|
|
8548
|
+
"pl-pad-2xl pr-pad-4xl text-body-sm font-semibold text-fg-default"
|
|
8549
|
+
]
|
|
8550
|
+
});
|
|
8551
|
+
const ganttTreeRail = tv({
|
|
8552
|
+
base: "pointer-events-none absolute inset-y-0 left-pad-2xl"
|
|
8553
|
+
});
|
|
8554
|
+
const ganttTreeColumn = tv({
|
|
8555
|
+
base: "absolute inset-y-0 w-[16px]"
|
|
8556
|
+
});
|
|
8557
|
+
const ganttTreeSegment = tv({
|
|
8558
|
+
base: "absolute bg-border-input"
|
|
8559
|
+
});
|
|
8560
|
+
const ganttGridLabel = tv({
|
|
8561
|
+
base: "flex min-w-0 flex-col justify-center"
|
|
8562
|
+
});
|
|
8563
|
+
const ganttGridLabelText = tv({
|
|
8564
|
+
base: "truncate text-body-sm text-fg-default"
|
|
8565
|
+
});
|
|
8566
|
+
const ganttGridSublabel = tv({
|
|
8567
|
+
base: "truncate text-caption-sm font-normal text-fg-muted"
|
|
8568
|
+
});
|
|
8569
|
+
const ganttChevron = tv({
|
|
8570
|
+
base: [
|
|
8571
|
+
"grid size-icon-md shrink-0 place-items-center rounded-radius-sm",
|
|
8572
|
+
"text-fg-subtle transition-transform duration-200",
|
|
8573
|
+
"hover:text-fg-default",
|
|
8574
|
+
"focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand",
|
|
8575
|
+
"[&_svg]:size-icon-sm"
|
|
8576
|
+
],
|
|
8577
|
+
variants: {
|
|
8578
|
+
open: { true: "rotate-0", false: "-rotate-90" },
|
|
8579
|
+
/** Folha: reserva o espaço sem desenhar o chevron, pra o rótulo alinhar. */
|
|
8580
|
+
hidden: { true: "invisible", false: "" }
|
|
8581
|
+
},
|
|
8582
|
+
defaultVariants: { open: true, hidden: false }
|
|
8583
|
+
});
|
|
8584
|
+
const ganttCanvasScroll = tv({
|
|
8585
|
+
base: [
|
|
8586
|
+
// `overflow-auto` nos dois eixos: é ele que rola o cabeçalho junto (#5).
|
|
8587
|
+
"min-h-0 flex-1 overflow-auto",
|
|
8588
|
+
// `scrollbar-thin` é a barra do DS — `@utility` do tema GERADO, existe nos
|
|
8589
|
+
// 4 canais (não é do globals.css).
|
|
8590
|
+
"scrollbar-thin"
|
|
8591
|
+
]
|
|
8592
|
+
});
|
|
8593
|
+
const ganttCanvas = tv({
|
|
8594
|
+
base: "relative"
|
|
8595
|
+
});
|
|
8596
|
+
const ganttCanvasRow = tv({
|
|
8597
|
+
base: [
|
|
8598
|
+
"absolute inset-x-0 border-b border-border-subtle",
|
|
8599
|
+
"transition-colors duration-150"
|
|
8600
|
+
],
|
|
8601
|
+
variants: {
|
|
8602
|
+
type: {
|
|
8603
|
+
task: "",
|
|
8604
|
+
summary: "bg-bg-subtle/60",
|
|
8605
|
+
milestone: ""
|
|
8606
|
+
},
|
|
8607
|
+
hovered: { true: "bg-bg-muted", false: "" },
|
|
8608
|
+
selected: { true: "", false: "" }
|
|
8609
|
+
},
|
|
8610
|
+
compoundVariants: [
|
|
8611
|
+
{ selected: true, class: "bg-bg-brand-subtle" },
|
|
8612
|
+
{ selected: true, hovered: true, class: "bg-bg-brand-subtle-hover" }
|
|
8613
|
+
],
|
|
8614
|
+
defaultVariants: { type: "task", hovered: false, selected: false }
|
|
8615
|
+
});
|
|
8616
|
+
const ganttGridColumn = tv({
|
|
8617
|
+
base: [
|
|
8618
|
+
/**
|
|
8619
|
+
* #1 — `border-border-subtle`, a MESMA cor das divisórias de linha.
|
|
8620
|
+
*
|
|
8621
|
+
* Era `border-chart-grid`, que é o token da grade de GRÁFICO e vem mais
|
|
8622
|
+
* claro de propósito (num chart a grade é fundo, não estrutura). Aqui as
|
|
8623
|
+
* duas famílias de linha formam uma malha: com tons diferentes, a vertical
|
|
8624
|
+
* lia como sombra da horizontal.
|
|
8625
|
+
*/
|
|
8626
|
+
"absolute inset-y-0 border-r border-border-subtle",
|
|
8627
|
+
"transition-colors duration-150"
|
|
8628
|
+
],
|
|
8629
|
+
variants: {
|
|
8630
|
+
weekend: { true: "bg-bg-subtle/50", false: "" },
|
|
8631
|
+
/** Coluna sob o cursor — a outra metade da mira. */
|
|
8632
|
+
hovered: { true: "bg-bg-muted", false: "" },
|
|
8633
|
+
/** #7 — coluna da linha selecionada, no verde da marca. */
|
|
8634
|
+
selected: { true: "bg-bg-brand-subtle", false: "" },
|
|
8635
|
+
/**
|
|
8636
|
+
* #1 — virada de mês/ano: o par da divisória do cabeçalho, MESMAS classes.
|
|
8637
|
+
* A tabela de ΔL que justifica a escolha do token está no `ganttHeadCell`.
|
|
8638
|
+
*/
|
|
8639
|
+
boundary: { true: "border-r-[2px] border-r-border-input", false: "" }
|
|
8640
|
+
},
|
|
8641
|
+
defaultVariants: {
|
|
8642
|
+
weekend: false,
|
|
8643
|
+
hovered: false,
|
|
8644
|
+
selected: false,
|
|
8645
|
+
boundary: false
|
|
8646
|
+
}
|
|
8647
|
+
});
|
|
8648
|
+
const ganttBar = tv({
|
|
8649
|
+
slots: {
|
|
8650
|
+
root: [
|
|
8651
|
+
"group/bar absolute flex min-w-0 items-center gap-gp-2xs",
|
|
8652
|
+
"overflow-hidden border text-left outline-none",
|
|
8653
|
+
"rounded-radius-sm",
|
|
8654
|
+
"transition-[background-color,border-color,box-shadow] duration-150",
|
|
8655
|
+
"focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand"
|
|
8656
|
+
],
|
|
8657
|
+
/** Acento sólido na borda esquerda — é AQUI que a cor viva mora. */
|
|
8658
|
+
accent: "pointer-events-none absolute inset-y-0 left-0 w-[3px]",
|
|
8659
|
+
/** Preenchimento de progresso, sobre o tingido. */
|
|
8660
|
+
progress: "pointer-events-none absolute inset-y-0 left-0",
|
|
8661
|
+
label: "relative z-[1] min-w-0 truncate pl-pad-md pr-pad-md text-caption-sm text-fg-default"
|
|
8662
|
+
},
|
|
8663
|
+
variants: {
|
|
8664
|
+
/**
|
|
8665
|
+
* ⚠️ A cor entra no fundo TINGIDO e no acento, nunca no texto — ver a nota
|
|
8666
|
+
* do topo. As chaves de chart usam `/14`, que é a mesma proporção do
|
|
8667
|
+
* `color-mix(… 14%, transparent)` com que o DS deriva os `-subtle`.
|
|
8668
|
+
*/
|
|
8669
|
+
colorKey: {
|
|
8670
|
+
"chart-1": {
|
|
8671
|
+
root: "border-chart-1/40 bg-chart-1/14 hover:bg-chart-1/20",
|
|
8672
|
+
accent: "bg-chart-1",
|
|
8673
|
+
progress: "bg-chart-1/35"
|
|
8674
|
+
},
|
|
8675
|
+
"chart-2": {
|
|
8676
|
+
root: "border-chart-2/40 bg-chart-2/14 hover:bg-chart-2/20",
|
|
8677
|
+
accent: "bg-chart-2",
|
|
8678
|
+
progress: "bg-chart-2/35"
|
|
8679
|
+
},
|
|
8680
|
+
"chart-3": {
|
|
8681
|
+
root: "border-chart-3/40 bg-chart-3/14 hover:bg-chart-3/20",
|
|
8682
|
+
accent: "bg-chart-3",
|
|
8683
|
+
progress: "bg-chart-3/35"
|
|
8684
|
+
},
|
|
8685
|
+
"chart-4": {
|
|
8686
|
+
root: "border-chart-4/40 bg-chart-4/14 hover:bg-chart-4/20",
|
|
8687
|
+
accent: "bg-chart-4",
|
|
8688
|
+
progress: "bg-chart-4/35"
|
|
8689
|
+
},
|
|
8690
|
+
"chart-5": {
|
|
8691
|
+
root: "border-chart-5/40 bg-chart-5/14 hover:bg-chart-5/20",
|
|
8692
|
+
accent: "bg-chart-5",
|
|
8693
|
+
progress: "bg-chart-5/35"
|
|
8694
|
+
},
|
|
8695
|
+
brand: {
|
|
8696
|
+
root: "border-border-brand-subtle bg-bg-brand-subtle hover:bg-bg-brand-subtle-hover",
|
|
8697
|
+
accent: "bg-bg-brand",
|
|
8698
|
+
progress: "bg-bg-brand/30"
|
|
8699
|
+
},
|
|
8700
|
+
success: {
|
|
8701
|
+
root: "border-border-success-muted bg-bg-success-muted hover:bg-bg-success-muted-hover",
|
|
8702
|
+
accent: "bg-bg-success",
|
|
8703
|
+
progress: "bg-bg-success/30"
|
|
8704
|
+
},
|
|
8705
|
+
warning: {
|
|
8706
|
+
root: "border-border-warning-muted bg-bg-warning-muted hover:bg-bg-warning-muted-hover",
|
|
8707
|
+
accent: "bg-bg-warning",
|
|
8708
|
+
progress: "bg-bg-warning/30"
|
|
8709
|
+
},
|
|
8710
|
+
danger: {
|
|
8711
|
+
root: "border-border-danger-muted bg-bg-danger-muted hover:bg-bg-danger-muted-hover",
|
|
8712
|
+
accent: "bg-bg-danger",
|
|
8713
|
+
progress: "bg-bg-danger/30"
|
|
8714
|
+
},
|
|
8715
|
+
info: {
|
|
8716
|
+
root: "border-border-info-muted bg-bg-info-muted hover:bg-bg-info-muted-hover",
|
|
8717
|
+
accent: "bg-bg-info",
|
|
8718
|
+
progress: "bg-bg-info/30"
|
|
8719
|
+
},
|
|
8720
|
+
neutral: {
|
|
8721
|
+
root: "border-border-default bg-bg-muted hover:bg-bg-subtle",
|
|
8722
|
+
accent: "bg-fg-subtle",
|
|
8723
|
+
progress: "bg-fg-subtle/25"
|
|
8724
|
+
}
|
|
8725
|
+
},
|
|
8726
|
+
/**
|
|
8727
|
+
* `summary` é mais baixa e sem acento: ela agrega, não é trabalho. As
|
|
8728
|
+
* referências desenham as pontas viradas pra baixo; aqui o sinal é a altura
|
|
8729
|
+
* reduzida e o radius menor, que sobrevive em 4px de largura por dia.
|
|
8730
|
+
*/
|
|
8731
|
+
type: {
|
|
8732
|
+
task: { root: "" },
|
|
8733
|
+
summary: { root: "rounded-radius-xs", accent: "hidden" },
|
|
8734
|
+
milestone: { root: "!border-0 !bg-transparent", accent: "hidden", label: "hidden" }
|
|
8735
|
+
},
|
|
8736
|
+
/** Barra que atravessa a janela: perde o canto e a borda daquele lado. */
|
|
8737
|
+
continuesBefore: { true: { root: "rounded-l-none border-l-0" }, false: {} },
|
|
8738
|
+
continuesAfter: { true: { root: "rounded-r-none border-r-0" }, false: {} },
|
|
8739
|
+
/** Vínculo violado — marcação não-cromática somada à cor. */
|
|
8740
|
+
conflict: {
|
|
8741
|
+
true: { root: "!border-border-danger-muted border-dashed" },
|
|
8742
|
+
false: {}
|
|
8743
|
+
},
|
|
8744
|
+
/** No caminho crítico: anel externo, sem trocar a cor da categoria. */
|
|
8745
|
+
critical: {
|
|
8746
|
+
true: { root: "ring-2 ring-bg-danger/50 ring-offset-0" },
|
|
8747
|
+
false: {}
|
|
8748
|
+
},
|
|
8749
|
+
dragging: {
|
|
8750
|
+
// ⚠️ Sem `transition`: o transform do dnd-kit muda a cada movimento do
|
|
8751
|
+
// ponteiro, e transição em cima disso faz a barra perseguir o cursor com
|
|
8752
|
+
// atraso. A transição vive no estado em repouso.
|
|
8753
|
+
true: { root: "z-[4] cursor-grabbing opacity-90 shadow-sh-lg transition-none" },
|
|
8754
|
+
false: {}
|
|
8755
|
+
},
|
|
8756
|
+
movable: { true: { root: "cursor-grab" }, false: { root: "cursor-pointer" } }
|
|
8757
|
+
},
|
|
8758
|
+
defaultVariants: {
|
|
8759
|
+
colorKey: "chart-1",
|
|
8760
|
+
type: "task",
|
|
8761
|
+
continuesBefore: false,
|
|
8762
|
+
continuesAfter: false,
|
|
8763
|
+
conflict: false,
|
|
8764
|
+
critical: false,
|
|
8765
|
+
dragging: false,
|
|
8766
|
+
movable: false
|
|
8767
|
+
}
|
|
8768
|
+
});
|
|
8769
|
+
const ganttMilestone = tv({
|
|
8770
|
+
base: [
|
|
8771
|
+
"absolute grid place-items-center outline-none",
|
|
8772
|
+
"focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand",
|
|
8773
|
+
"[&>span]:block [&>span]:size-[12px] [&>span]:rotate-45 [&>span]:rounded-[2px]"
|
|
8774
|
+
],
|
|
8775
|
+
variants: {
|
|
8776
|
+
colorKey: {
|
|
8777
|
+
"chart-1": "[&>span]:bg-chart-1",
|
|
8778
|
+
"chart-2": "[&>span]:bg-chart-2",
|
|
8779
|
+
"chart-3": "[&>span]:bg-chart-3",
|
|
8780
|
+
"chart-4": "[&>span]:bg-chart-4",
|
|
8781
|
+
"chart-5": "[&>span]:bg-chart-5",
|
|
8782
|
+
brand: "[&>span]:bg-bg-brand",
|
|
8783
|
+
success: "[&>span]:bg-bg-success",
|
|
8784
|
+
warning: "[&>span]:bg-bg-warning",
|
|
8785
|
+
danger: "[&>span]:bg-bg-danger",
|
|
8786
|
+
info: "[&>span]:bg-bg-info",
|
|
8787
|
+
neutral: "[&>span]:bg-fg-subtle"
|
|
8788
|
+
}
|
|
8789
|
+
},
|
|
8790
|
+
defaultVariants: { colorKey: "brand" }
|
|
8791
|
+
});
|
|
8792
|
+
const ganttResizeHandle = tv({
|
|
8793
|
+
base: [
|
|
8794
|
+
"absolute inset-y-0 z-[2] w-[8px] cursor-col-resize",
|
|
8795
|
+
"opacity-0 transition-opacity duration-150",
|
|
8796
|
+
"group-hover/bar:opacity-100 focus-visible:opacity-100",
|
|
8797
|
+
"focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand",
|
|
8798
|
+
"before:absolute before:inset-y-[6px] before:left-1/2 before:w-[2px]",
|
|
8799
|
+
"before:-translate-x-1/2 before:rounded-radius-full before:bg-fg-default/40",
|
|
8800
|
+
"before:content-['']"
|
|
8801
|
+
],
|
|
8802
|
+
variants: {
|
|
8803
|
+
side: { start: "left-0", end: "right-0" }
|
|
8804
|
+
}
|
|
8805
|
+
});
|
|
8806
|
+
const ganttLinkPort = tv({
|
|
8807
|
+
base: [
|
|
8808
|
+
"absolute top-1/2 z-[3] size-[9px] -translate-y-1/2 cursor-crosshair",
|
|
8809
|
+
"rounded-radius-full border-2 border-bg-surface bg-fg-subtle",
|
|
8810
|
+
"opacity-0 transition-opacity duration-150",
|
|
8811
|
+
"group-hover/bar:opacity-100 focus-visible:opacity-100",
|
|
8812
|
+
"hover:bg-bg-brand focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand"
|
|
8813
|
+
],
|
|
8814
|
+
variants: {
|
|
8815
|
+
side: { start: "-left-[13px]", end: "-right-[13px]" }
|
|
8816
|
+
}
|
|
8817
|
+
});
|
|
8818
|
+
const ganttLinksLayer = tv({
|
|
8819
|
+
base: "pointer-events-none absolute inset-0 z-[3] overflow-visible"
|
|
8820
|
+
});
|
|
8821
|
+
const ganttLinkGhost = tv({
|
|
8822
|
+
base: [
|
|
8823
|
+
"pointer-events-none fill-none",
|
|
8824
|
+
"stroke-bg-brand stroke-2 [stroke-dasharray:5_4]"
|
|
8825
|
+
]
|
|
8826
|
+
});
|
|
8827
|
+
const ganttLinkPath = tv({
|
|
8828
|
+
base: [
|
|
8829
|
+
"pointer-events-auto fill-none cursor-pointer",
|
|
8830
|
+
"transition-[stroke,stroke-width] duration-150"
|
|
8831
|
+
],
|
|
8832
|
+
variants: {
|
|
8833
|
+
state: {
|
|
8834
|
+
default: "stroke-fg-subtle stroke-[1.5] hover:stroke-fg-default hover:stroke-2",
|
|
8835
|
+
/** Violado: tracejado + cor de perigo. Duplo canal, não só cor. */
|
|
8836
|
+
conflict: "stroke-bg-danger stroke-2 [stroke-dasharray:4_3]",
|
|
8837
|
+
critical: "stroke-bg-danger stroke-2"
|
|
8838
|
+
}
|
|
8839
|
+
},
|
|
8840
|
+
defaultVariants: { state: "default" }
|
|
8841
|
+
});
|
|
8842
|
+
tv({
|
|
8843
|
+
base: "fill-[context-stroke]"
|
|
8844
|
+
});
|
|
8845
|
+
const ganttNowLine = tv({
|
|
8846
|
+
base: "pointer-events-none absolute inset-y-0 z-[2] w-0"
|
|
8847
|
+
});
|
|
8848
|
+
const ganttNowStroke = tv({
|
|
8849
|
+
base: "absolute inset-y-0 left-0 w-px -translate-x-1/2 bg-bg-danger"
|
|
8850
|
+
});
|
|
8851
|
+
const ganttNowDot = tv({
|
|
8852
|
+
base: [
|
|
8853
|
+
"absolute left-0 top-0 size-[7px] -translate-x-1/2",
|
|
8854
|
+
"rounded-radius-full bg-bg-danger"
|
|
8855
|
+
]
|
|
8856
|
+
});
|
|
8857
|
+
const ganttMonthFrame = tv({
|
|
8858
|
+
base: [
|
|
8859
|
+
"flex min-h-0 flex-1 flex-col overflow-hidden",
|
|
8860
|
+
"rounded-radius-xl border border-border-default bg-bg-surface"
|
|
8861
|
+
]
|
|
8862
|
+
});
|
|
8863
|
+
const ganttWeekdayRow = tv({
|
|
8864
|
+
base: "grid shrink-0 grid-cols-7 border-b border-border-default bg-bg-table-head"
|
|
8865
|
+
});
|
|
8866
|
+
const ganttWeekdayCell = tv({
|
|
8867
|
+
base: [
|
|
8868
|
+
"px-pad-md py-pad-md text-center",
|
|
8869
|
+
"text-caption-sm font-semibold uppercase text-fg-muted"
|
|
8870
|
+
]
|
|
8871
|
+
});
|
|
8872
|
+
const ganttMonthGrid = tv({
|
|
8873
|
+
base: [
|
|
8874
|
+
"flex min-h-0 flex-1 flex-col",
|
|
8875
|
+
"overflow-y-auto scrollbar-thin"
|
|
8876
|
+
]
|
|
8877
|
+
});
|
|
8878
|
+
const ganttWeekRow = tv({
|
|
8879
|
+
base: "relative flex min-h-[88px] shrink-0 flex-1 flex-col"
|
|
8880
|
+
});
|
|
8881
|
+
const ganttWeekDays = tv({
|
|
8882
|
+
base: "grid min-h-0 flex-1 grid-cols-7"
|
|
8883
|
+
});
|
|
8884
|
+
const ganttWeekSegments = tv({
|
|
8885
|
+
base: "pointer-events-none absolute inset-x-0"
|
|
8886
|
+
});
|
|
8887
|
+
const ganttDaySegment = tv({
|
|
8888
|
+
slots: {
|
|
8889
|
+
root: [
|
|
8890
|
+
"pointer-events-auto absolute flex min-w-0 items-center gap-gp-2xs",
|
|
8891
|
+
"h-[18px] rounded-radius-sm border px-pad-sm",
|
|
8892
|
+
"text-left outline-none transition-colors duration-150",
|
|
8893
|
+
"focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand"
|
|
8894
|
+
],
|
|
8895
|
+
dot: "size-[7px] shrink-0 rounded-radius-full",
|
|
8896
|
+
label: "min-w-0 flex-1 truncate text-caption-sm text-fg-default"
|
|
8897
|
+
},
|
|
8898
|
+
variants: {
|
|
8899
|
+
colorKey: {
|
|
8900
|
+
"chart-1": { root: "border-chart-1/40 bg-chart-1/14 hover:bg-chart-1/24", dot: "bg-chart-1" },
|
|
8901
|
+
"chart-2": { root: "border-chart-2/40 bg-chart-2/14 hover:bg-chart-2/24", dot: "bg-chart-2" },
|
|
8902
|
+
"chart-3": { root: "border-chart-3/40 bg-chart-3/14 hover:bg-chart-3/24", dot: "bg-chart-3" },
|
|
8903
|
+
"chart-4": { root: "border-chart-4/40 bg-chart-4/14 hover:bg-chart-4/24", dot: "bg-chart-4" },
|
|
8904
|
+
"chart-5": { root: "border-chart-5/40 bg-chart-5/14 hover:bg-chart-5/24", dot: "bg-chart-5" },
|
|
8905
|
+
brand: { root: "border-border-brand-subtle bg-bg-brand-subtle hover:bg-bg-brand-subtle-hover", dot: "bg-bg-brand" },
|
|
8906
|
+
success: { root: "border-border-success-muted bg-bg-success-muted", dot: "bg-bg-success" },
|
|
8907
|
+
warning: { root: "border-border-warning-muted bg-bg-warning-muted", dot: "bg-bg-warning" },
|
|
8908
|
+
danger: { root: "border-border-danger-muted bg-bg-danger-muted", dot: "bg-bg-danger" },
|
|
8909
|
+
info: { root: "border-border-info-muted bg-bg-info-muted", dot: "bg-bg-info" },
|
|
8910
|
+
neutral: { root: "border-border-default bg-bg-muted", dot: "bg-fg-subtle" }
|
|
8911
|
+
},
|
|
8912
|
+
continuesBefore: { true: { root: "rounded-l-none border-l-0" }, false: {} },
|
|
8913
|
+
continuesAfter: { true: { root: "rounded-r-none border-r-0" }, false: {} },
|
|
8914
|
+
conflict: { true: { root: "border-dashed !border-border-danger-muted" }, false: {} },
|
|
8915
|
+
critical: { true: { root: "ring-2 ring-bg-danger/50" }, false: {} }
|
|
8916
|
+
},
|
|
8917
|
+
defaultVariants: {
|
|
8918
|
+
colorKey: "chart-1",
|
|
8919
|
+
continuesBefore: false,
|
|
8920
|
+
continuesAfter: false,
|
|
8921
|
+
conflict: false,
|
|
8922
|
+
critical: false
|
|
8923
|
+
}
|
|
8924
|
+
});
|
|
8925
|
+
const ganttDayAdd = tv({
|
|
8926
|
+
base: [
|
|
8927
|
+
"absolute bottom-pad-sm right-pad-sm z-[2]",
|
|
8928
|
+
"grid size-icon-lg place-items-center rounded-radius-md",
|
|
8929
|
+
"border border-border-subtle bg-bg-surface text-fg-muted",
|
|
8930
|
+
"opacity-0 transition-opacity duration-150",
|
|
8931
|
+
"group-hover/cell:opacity-100 focus-visible:opacity-100",
|
|
8932
|
+
"hover:border-border-brand hover:text-fg-brand",
|
|
8933
|
+
"focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand",
|
|
8934
|
+
"[&_svg]:size-icon-sm"
|
|
8935
|
+
]
|
|
8936
|
+
});
|
|
8937
|
+
const ganttMonthCell = tv({
|
|
8938
|
+
base: [
|
|
8939
|
+
// `group/cell` é o gatilho do hover do `ganttDayAdd`. Nome com barra pra
|
|
8940
|
+
// não colidir com o `group/row` da grade esquerda.
|
|
8941
|
+
"group/cell relative flex min-h-0 min-w-0 flex-col overflow-hidden",
|
|
8942
|
+
"border-b border-r border-border-subtle p-pad-sm",
|
|
8943
|
+
"[&:nth-child(7n)]:border-r-0",
|
|
8944
|
+
"transition-colors duration-150"
|
|
8945
|
+
],
|
|
8946
|
+
variants: {
|
|
8947
|
+
/** Dia de fora do mês âncora: presente, mas recuado. */
|
|
8948
|
+
/**
|
|
8949
|
+
* ## O mês é UNIFORME. O que se destaca é o que não é dele.
|
|
8950
|
+
*
|
|
8951
|
+
* Todo dia do mês âncora tem o mesmo fundo — útil ou fim de semana, tanto
|
|
8952
|
+
* faz. Só as células de OUTRO mês recebem `bg-canvas`, e a grade passa a
|
|
8953
|
+
* responder uma pergunta só: onde este mês começa e onde acaba.
|
|
8954
|
+
*
|
|
8955
|
+
* ⛔ **Não existe variante de fim de semana aqui, e isso é decisão.**
|
|
8956
|
+
* Sombrear sábado e domingo divide o mês em blocos verticais e compete com
|
|
8957
|
+
* a única distinção que a grade precisa fazer. Num calendário de agenda o
|
|
8958
|
+
* fim de semana importa (não se marca reunião); num CRONOGRAMA a barra
|
|
8959
|
+
* atravessa o fim de semana de qualquer jeito, e listrar as colunas só
|
|
8960
|
+
* atrapalha a leitura do segmento contínuo que passa por cima.
|
|
8961
|
+
*
|
|
8962
|
+
* ## Por que `bg-canvas` e não escurecer com `/NN`
|
|
8963
|
+
*
|
|
8964
|
+
* O dia de outro mês não é uma SOBREPOSIÇÃO sobre a grade: é o buraco por
|
|
8965
|
+
* onde se vê o fundo da página. O token que significa isso é `bg-canvas`
|
|
8966
|
+
* (0.205), e contra a superfície da grade (`bg-surface`, 0.225) ele dá
|
|
8967
|
+
* ΔL 0.020.
|
|
8968
|
+
*
|
|
8969
|
+
* ⚠️ 0.020 é pouco pra uma LINHA e suficiente pra uma ÁREA — contraste de
|
|
8970
|
+
* área grande é muito mais perceptível que de traço fino. É por isso que
|
|
8971
|
+
* aqui 0.020 lê enquanto na virada de mês do eixo 0.040 não lia.
|
|
8972
|
+
*
|
|
8973
|
+
* ## O histórico, porque ele explica as duas variantes que sumiram
|
|
8974
|
+
*
|
|
8975
|
+
* outside `bg-bg-subtle/40` → branco 1% × 40% = 0.004 de alfa
|
|
8976
|
+
* weekend `bg-bg-subtle/30` → branco 1% × 30% = 0.003 de alfa
|
|
8977
|
+
*
|
|
8978
|
+
* As duas abaixo do limiar de percepção: existiam no CSS e não pintavam
|
|
8979
|
+
* nada — 20 das 42 células com estilo que ninguém via. Ao consertar, eu
|
|
8980
|
+
* tornei o fim de semana visível TAMBÉM, e aí ele virou o defeito oposto:
|
|
8981
|
+
* as colunas de sábado e domingo escuras de ponta a ponta, com o mês
|
|
8982
|
+
* parecendo quatro blocos. O conserto certo era **remover**, não calibrar.
|
|
8983
|
+
*
|
|
8984
|
+
* ⛔ E o `text-fg-subtle` que morava na variante `outside` era CÓDIGO MORTO:
|
|
8985
|
+
* medido, o `ganttDayNumber` põe a própria cor no `<span>` e vence a
|
|
8986
|
+
* herança. Quem dim o número do dia de fora é a variante `outside` DELE.
|
|
8987
|
+
*/
|
|
8988
|
+
outside: { true: "bg-bg-canvas", false: "" },
|
|
8989
|
+
today: { true: "bg-bg-brand-subtle", false: "" }
|
|
8990
|
+
},
|
|
8991
|
+
compoundVariants: [
|
|
8992
|
+
/**
|
|
8993
|
+
* `today` vence `outside`.
|
|
8994
|
+
*
|
|
8995
|
+
* Se hoje cai num dia de outro mês visível na grade, marcar HOJE é mais
|
|
8996
|
+
* importante que marcar não-é-deste-mês: é a única célula que o usuário
|
|
8997
|
+
* procura sem saber a data.
|
|
8998
|
+
*/
|
|
8999
|
+
{ outside: true, today: true, class: "bg-bg-brand-subtle" }
|
|
9000
|
+
],
|
|
9001
|
+
defaultVariants: { outside: false, today: false }
|
|
9002
|
+
});
|
|
9003
|
+
const ganttDayNumber = tv({
|
|
9004
|
+
base: "text-caption-sm tabular-nums text-fg-muted",
|
|
9005
|
+
variants: {
|
|
9006
|
+
outside: { true: "text-fg-disabled", false: "" },
|
|
9007
|
+
today: {
|
|
9008
|
+
true: [
|
|
9009
|
+
"grid size-icon-lg place-items-center rounded-radius-full",
|
|
9010
|
+
"bg-bg-brand font-semibold text-fg-on-brand"
|
|
9011
|
+
],
|
|
9012
|
+
false: ""
|
|
9013
|
+
}
|
|
9014
|
+
},
|
|
9015
|
+
compoundVariants: [
|
|
9016
|
+
// `today` vence `outside`: hoje num dia de outro mês ainda é hoje.
|
|
9017
|
+
{ outside: true, today: true, class: "font-semibold text-fg-on-brand" }
|
|
9018
|
+
],
|
|
9019
|
+
defaultVariants: { today: false, outside: false }
|
|
9020
|
+
});
|
|
9021
|
+
tv({
|
|
9022
|
+
slots: {
|
|
9023
|
+
root: [
|
|
9024
|
+
"flex w-full min-w-0 items-center gap-gp-2xs",
|
|
9025
|
+
"min-h-comp-2xs rounded-radius-sm border px-pad-sm",
|
|
9026
|
+
"text-left outline-none transition-colors duration-150",
|
|
9027
|
+
"focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand"
|
|
9028
|
+
],
|
|
9029
|
+
dot: "size-[7px] shrink-0 rounded-radius-full",
|
|
9030
|
+
label: "min-w-0 flex-1 truncate text-caption-sm text-fg-default"
|
|
9031
|
+
},
|
|
9032
|
+
variants: {
|
|
9033
|
+
colorKey: {
|
|
9034
|
+
"chart-1": { root: "border-chart-1/40 bg-chart-1/14", dot: "bg-chart-1" },
|
|
9035
|
+
"chart-2": { root: "border-chart-2/40 bg-chart-2/14", dot: "bg-chart-2" },
|
|
9036
|
+
"chart-3": { root: "border-chart-3/40 bg-chart-3/14", dot: "bg-chart-3" },
|
|
9037
|
+
"chart-4": { root: "border-chart-4/40 bg-chart-4/14", dot: "bg-chart-4" },
|
|
9038
|
+
"chart-5": { root: "border-chart-5/40 bg-chart-5/14", dot: "bg-chart-5" },
|
|
9039
|
+
brand: { root: "border-border-brand-subtle bg-bg-brand-subtle", dot: "bg-bg-brand" },
|
|
9040
|
+
success: { root: "border-border-success-muted bg-bg-success-muted", dot: "bg-bg-success" },
|
|
9041
|
+
warning: { root: "border-border-warning-muted bg-bg-warning-muted", dot: "bg-bg-warning" },
|
|
9042
|
+
danger: { root: "border-border-danger-muted bg-bg-danger-muted", dot: "bg-bg-danger" },
|
|
9043
|
+
info: { root: "border-border-info-muted bg-bg-info-muted", dot: "bg-bg-info" },
|
|
9044
|
+
neutral: { root: "border-border-default bg-bg-muted", dot: "bg-fg-subtle" }
|
|
9045
|
+
}
|
|
9046
|
+
},
|
|
9047
|
+
defaultVariants: { colorKey: "chart-1" }
|
|
9048
|
+
});
|
|
9049
|
+
const ganttOverflowButton = tv({
|
|
9050
|
+
base: [
|
|
9051
|
+
"absolute inset-x-pad-sm z-[2] truncate rounded-radius-sm px-pad-2xs text-left",
|
|
9052
|
+
"text-caption-sm font-medium text-fg-muted",
|
|
9053
|
+
"transition-colors duration-150",
|
|
9054
|
+
"hover:bg-bg-muted hover:text-fg-default",
|
|
9055
|
+
"focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand"
|
|
9056
|
+
]
|
|
9057
|
+
});
|
|
9058
|
+
const ganttListFrame = tv({
|
|
9059
|
+
base: [
|
|
9060
|
+
"flex min-h-0 flex-1 flex-col overflow-y-auto overscroll-contain scrollbar-thin",
|
|
9061
|
+
"rounded-radius-xl border border-border-default bg-bg-surface"
|
|
9062
|
+
]
|
|
9063
|
+
});
|
|
9064
|
+
const ganttListDay = tv({
|
|
9065
|
+
base: "flex flex-col gap-gp-md border-b border-border-subtle p-sp-xl last:border-b-0"
|
|
9066
|
+
});
|
|
9067
|
+
const ganttListDayHead = tv({
|
|
9068
|
+
base: [
|
|
9069
|
+
"sticky top-0 z-[1] -mx-sp-xl -mt-sp-xl px-sp-xl pb-pad-md pt-sp-xl",
|
|
9070
|
+
"flex items-baseline gap-gp-md bg-bg-surface"
|
|
9071
|
+
]
|
|
9072
|
+
});
|
|
9073
|
+
const ganttListDayNumber = tv({
|
|
9074
|
+
base: "text-title-sm font-semibold tabular-nums",
|
|
9075
|
+
variants: {
|
|
9076
|
+
today: { true: "text-fg-brand", false: "text-fg-default" }
|
|
9077
|
+
},
|
|
9078
|
+
defaultVariants: { today: false }
|
|
9079
|
+
});
|
|
9080
|
+
const ganttListDayName = tv({
|
|
9081
|
+
base: "text-body-sm text-fg-muted first-letter:uppercase"
|
|
9082
|
+
});
|
|
9083
|
+
const ganttListItems = tv({
|
|
9084
|
+
base: "flex flex-col gap-gp-sm"
|
|
9085
|
+
});
|
|
9086
|
+
const ganttListItem = tv({
|
|
9087
|
+
slots: {
|
|
9088
|
+
root: [
|
|
9089
|
+
"group/item flex min-h-form-lg w-full min-w-0 cursor-pointer items-center gap-gp-md",
|
|
9090
|
+
"rounded-radius-lg border px-pad-xl py-pad-md text-left outline-none",
|
|
9091
|
+
"transition-[background-color,border-color] duration-150",
|
|
9092
|
+
"focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand"
|
|
9093
|
+
],
|
|
9094
|
+
dot: "size-[9px] shrink-0 rounded-radius-full",
|
|
9095
|
+
body: "flex min-w-0 flex-1 flex-col",
|
|
9096
|
+
title: "truncate text-body-sm font-medium text-fg-default",
|
|
9097
|
+
sub: "truncate text-caption-sm text-fg-muted",
|
|
9098
|
+
meta: "shrink-0 text-caption-sm tabular-nums text-fg-muted"
|
|
9099
|
+
},
|
|
9100
|
+
variants: {
|
|
9101
|
+
colorKey: {
|
|
9102
|
+
"chart-1": { root: "border-chart-1/40 bg-chart-1/14 hover:bg-chart-1/24", dot: "bg-chart-1" },
|
|
9103
|
+
"chart-2": { root: "border-chart-2/40 bg-chart-2/14 hover:bg-chart-2/24", dot: "bg-chart-2" },
|
|
9104
|
+
"chart-3": { root: "border-chart-3/40 bg-chart-3/14 hover:bg-chart-3/24", dot: "bg-chart-3" },
|
|
9105
|
+
"chart-4": { root: "border-chart-4/40 bg-chart-4/14 hover:bg-chart-4/24", dot: "bg-chart-4" },
|
|
9106
|
+
"chart-5": { root: "border-chart-5/40 bg-chart-5/14 hover:bg-chart-5/24", dot: "bg-chart-5" },
|
|
9107
|
+
brand: { root: "border-border-brand-subtle bg-bg-brand-subtle hover:bg-bg-brand-subtle-hover", dot: "bg-bg-brand" },
|
|
9108
|
+
success: { root: "border-border-success-muted bg-bg-success-muted", dot: "bg-bg-success" },
|
|
9109
|
+
warning: { root: "border-border-warning-muted bg-bg-warning-muted", dot: "bg-bg-warning" },
|
|
9110
|
+
danger: { root: "border-border-danger-muted bg-bg-danger-muted", dot: "bg-bg-danger" },
|
|
9111
|
+
info: { root: "border-border-info-muted bg-bg-info-muted", dot: "bg-bg-info" },
|
|
9112
|
+
neutral: { root: "border-border-default bg-bg-muted", dot: "bg-fg-subtle" }
|
|
9113
|
+
},
|
|
9114
|
+
conflict: { true: { root: "border-dashed !border-border-danger-muted" }, false: {} },
|
|
9115
|
+
critical: { true: { root: "ring-2 ring-bg-danger/50" }, false: {} }
|
|
9116
|
+
},
|
|
9117
|
+
defaultVariants: { colorKey: "chart-1", conflict: false, critical: false }
|
|
9118
|
+
});
|
|
9119
|
+
const ganttEmpty = tv({
|
|
9120
|
+
base: [
|
|
9121
|
+
"flex min-h-[200px] flex-1 flex-col items-center justify-center gap-gp-md",
|
|
9122
|
+
"px-pad-3xl py-pad-3xl text-center"
|
|
9123
|
+
]
|
|
9124
|
+
});
|
|
9125
|
+
const ganttEmptyText = tv({
|
|
9126
|
+
base: "text-body-sm text-fg-muted"
|
|
9127
|
+
});
|
|
9128
|
+
tv({
|
|
9129
|
+
base: [
|
|
9130
|
+
"flex flex-col items-center justify-center gap-gp-md",
|
|
9131
|
+
"rounded-radius-xl border border-border-default bg-bg-subtle",
|
|
9132
|
+
"px-pad-3xl py-pad-3xl text-center"
|
|
9133
|
+
]
|
|
9134
|
+
});
|
|
9135
|
+
function normalizarTexto(s2) {
|
|
9136
|
+
return s2.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase().trim();
|
|
9137
|
+
}
|
|
9138
|
+
function comoLista(v3) {
|
|
9139
|
+
if (v3 === void 0 || v3 === null) return [];
|
|
9140
|
+
if (Array.isArray(v3)) return v3.map((x3) => String(x3));
|
|
9141
|
+
if (v3 instanceof Date) return [v3.toISOString()];
|
|
9142
|
+
return [String(v3)];
|
|
9143
|
+
}
|
|
9144
|
+
function comoNumero(v3) {
|
|
9145
|
+
if (typeof v3 === "number") return Number.isFinite(v3) ? v3 : null;
|
|
9146
|
+
if (typeof v3 === "string" && v3.trim() !== "") {
|
|
9147
|
+
const n2 = Number(v3.replace(",", "."));
|
|
9148
|
+
return Number.isFinite(n2) ? n2 : null;
|
|
9149
|
+
}
|
|
9150
|
+
return null;
|
|
9151
|
+
}
|
|
9152
|
+
const SO_DIA = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
9153
|
+
function parseDiaISO(s2) {
|
|
9154
|
+
const m2 = SO_DIA.exec(s2.trim());
|
|
9155
|
+
if (m2) {
|
|
9156
|
+
const d22 = new Date(Number(m2[1]), Number(m2[2]) - 1, Number(m2[3]));
|
|
9157
|
+
return Number.isNaN(d22.getTime()) ? null : d22;
|
|
9158
|
+
}
|
|
9159
|
+
const d3 = new Date(s2);
|
|
9160
|
+
return Number.isNaN(d3.getTime()) ? null : startOfDay(d3);
|
|
9161
|
+
}
|
|
9162
|
+
function comoData(v3) {
|
|
9163
|
+
if (v3 instanceof Date) return Number.isNaN(v3.getTime()) ? null : startOfDay(v3);
|
|
9164
|
+
if (typeof v3 === "string" && v3.trim() !== "") return parseDiaISO(v3);
|
|
9165
|
+
return null;
|
|
9166
|
+
}
|
|
9167
|
+
function operadorDoCampo(kind, values) {
|
|
9168
|
+
const [a11 = "", b4 = ""] = values;
|
|
9169
|
+
const temA = a11.trim() !== "";
|
|
9170
|
+
const temB = b4.trim() !== "";
|
|
9171
|
+
switch (kind) {
|
|
9172
|
+
case "text":
|
|
9173
|
+
return "contém";
|
|
9174
|
+
case "number":
|
|
9175
|
+
if (temA && temB) return "entre";
|
|
9176
|
+
if (temA) return "≥";
|
|
9177
|
+
return "≤";
|
|
9178
|
+
case "date":
|
|
9179
|
+
if (temA && temB) return "entre";
|
|
9180
|
+
if (temA) return "a partir de";
|
|
9181
|
+
return "até";
|
|
9182
|
+
case "multi":
|
|
9183
|
+
case "single":
|
|
9184
|
+
case "boolean":
|
|
9185
|
+
default:
|
|
9186
|
+
return "é";
|
|
9187
|
+
}
|
|
9188
|
+
}
|
|
9189
|
+
function campoVazio(kind, values) {
|
|
9190
|
+
if (!values || values.length === 0) return true;
|
|
9191
|
+
if (kind === "number" || kind === "date" || kind === "text") {
|
|
9192
|
+
return values.every((v3) => v3.trim() === "");
|
|
9193
|
+
}
|
|
9194
|
+
return false;
|
|
9195
|
+
}
|
|
9196
|
+
function linhaPassaNoCampo(campo, values, row) {
|
|
9197
|
+
const kind = campo.kind ?? "multi";
|
|
9198
|
+
if (campoVazio(kind, values)) return true;
|
|
9199
|
+
const vs = values ?? [];
|
|
9200
|
+
const bruto = campo.accessor(row);
|
|
9201
|
+
switch (kind) {
|
|
9202
|
+
case "multi":
|
|
9203
|
+
case "single": {
|
|
9204
|
+
const lista = comoLista(bruto);
|
|
9205
|
+
if (lista.length === 0) return false;
|
|
9206
|
+
return lista.some((x3) => vs.includes(x3));
|
|
9207
|
+
}
|
|
9208
|
+
case "boolean": {
|
|
9209
|
+
const lista = comoLista(bruto);
|
|
9210
|
+
if (lista.length === 0) return false;
|
|
9211
|
+
const normal = lista.map((x3) => {
|
|
9212
|
+
const n2 = normalizarTexto(x3);
|
|
9213
|
+
if (n2 === "1" || n2 === "sim" || n2 === "true" || n2 === "yes") return "true";
|
|
9214
|
+
if (n2 === "0" || n2 === "nao" || n2 === "false" || n2 === "no") return "false";
|
|
9215
|
+
return n2;
|
|
9216
|
+
});
|
|
9217
|
+
return normal.some((x3) => vs.includes(x3));
|
|
9218
|
+
}
|
|
9219
|
+
case "text": {
|
|
9220
|
+
const termo = normalizarTexto(vs[0] ?? "");
|
|
9221
|
+
if (termo === "") return true;
|
|
9222
|
+
const lista = comoLista(bruto);
|
|
9223
|
+
if (lista.length === 0) return false;
|
|
9224
|
+
return lista.some((x3) => normalizarTexto(x3).includes(termo));
|
|
9225
|
+
}
|
|
9226
|
+
case "number": {
|
|
9227
|
+
const n2 = comoNumero(bruto);
|
|
9228
|
+
if (n2 === null) return false;
|
|
9229
|
+
const min2 = comoNumero(vs[0]);
|
|
9230
|
+
const max2 = comoNumero(vs[1]);
|
|
9231
|
+
if (min2 !== null && n2 < min2) return false;
|
|
9232
|
+
if (max2 !== null && n2 > max2) return false;
|
|
9233
|
+
return true;
|
|
9234
|
+
}
|
|
9235
|
+
case "date": {
|
|
9236
|
+
const d3 = comoData(bruto);
|
|
9237
|
+
if (d3 === null) return false;
|
|
9238
|
+
const de = comoData(vs[0]);
|
|
9239
|
+
const ate = comoData(vs[1]);
|
|
9240
|
+
if (de !== null && differenceInCalendarDays(d3, de) < 0) return false;
|
|
9241
|
+
if (ate !== null && differenceInCalendarDays(d3, ate) > 0) return false;
|
|
9242
|
+
return true;
|
|
9243
|
+
}
|
|
9244
|
+
default:
|
|
9245
|
+
return true;
|
|
9246
|
+
}
|
|
9247
|
+
}
|
|
9248
|
+
function aplicarFiltros(rows, fields, model) {
|
|
9249
|
+
if (!fields || fields.length === 0) return [...rows];
|
|
9250
|
+
let saida = [...rows];
|
|
9251
|
+
for (const campo of fields) {
|
|
9252
|
+
const values = model[campo.id];
|
|
9253
|
+
if (campoVazio(campo.kind ?? "multi", values)) continue;
|
|
9254
|
+
saida = saida.filter((r2) => linhaPassaNoCampo(campo, values, r2));
|
|
9255
|
+
}
|
|
9256
|
+
return saida;
|
|
9257
|
+
}
|
|
9258
|
+
function contarAplicados(fields, model) {
|
|
9259
|
+
if (!fields) return 0;
|
|
9260
|
+
return fields.filter(
|
|
9261
|
+
(c3) => !campoVazio(c3.kind ?? "multi", model[c3.id])
|
|
9262
|
+
).length;
|
|
9263
|
+
}
|
|
9264
|
+
function valoresDoChip(campo, values, formatarData) {
|
|
9265
|
+
const kind = campo.kind ?? "multi";
|
|
9266
|
+
const vs = values ?? [];
|
|
9267
|
+
const dia = (iso) => formatarData ? formatarData(iso) : iso;
|
|
9268
|
+
switch (kind) {
|
|
9269
|
+
case "multi":
|
|
9270
|
+
case "single":
|
|
9271
|
+
return (campo.options ?? []).filter((o2) => vs.includes(o2.value)).map((o2) => o2.label);
|
|
9272
|
+
case "boolean": {
|
|
9273
|
+
const achou = (campo.options ?? []).find((o2) => vs.includes(o2.value));
|
|
9274
|
+
return [achou?.label ?? (vs[0] === "true" ? "Sim" : "Não")];
|
|
9275
|
+
}
|
|
9276
|
+
case "text":
|
|
9277
|
+
return vs[0] ? [vs[0]] : [];
|
|
9278
|
+
case "number": {
|
|
9279
|
+
const [a11 = "", b4 = ""] = vs;
|
|
9280
|
+
if (a11.trim() && b4.trim()) return [`${a11} e ${b4}`];
|
|
9281
|
+
return [a11.trim() || b4.trim()];
|
|
9282
|
+
}
|
|
9283
|
+
case "date": {
|
|
9284
|
+
const [a11 = "", b4 = ""] = vs;
|
|
9285
|
+
if (a11.trim() && b4.trim()) return [`${dia(a11)} e ${dia(b4)}`];
|
|
9286
|
+
return [a11.trim() ? dia(a11) : dia(b4)];
|
|
9287
|
+
}
|
|
9288
|
+
default:
|
|
9289
|
+
return [];
|
|
9290
|
+
}
|
|
9291
|
+
}
|
|
9292
|
+
const MAX_VALORES_VISIVEIS = 3;
|
|
9293
|
+
function GanttAppliedFilters({
|
|
9294
|
+
fields,
|
|
9295
|
+
model,
|
|
9296
|
+
counts,
|
|
9297
|
+
onToggleValue,
|
|
9298
|
+
onClearField,
|
|
9299
|
+
onClearAll,
|
|
9300
|
+
formatDate
|
|
9301
|
+
}) {
|
|
9302
|
+
const [aberto, setAberto] = useState(null);
|
|
9303
|
+
const aplicados = fields.filter(
|
|
9304
|
+
(campo) => !campoVazio(campo.kind ?? "multi", model[campo.id])
|
|
9305
|
+
);
|
|
9306
|
+
if (aplicados.length === 0) return null;
|
|
9307
|
+
return /* @__PURE__ */ jsxs("div", { className: ganttAppliedRow(), children: [
|
|
9308
|
+
aplicados.map((campo) => {
|
|
9309
|
+
const kind = campo.kind ?? "multi";
|
|
9310
|
+
const marcados = model[campo.id] ?? [];
|
|
9311
|
+
const operador = operadorDoCampo(kind, marcados);
|
|
9312
|
+
const rotulos = valoresDoChip(campo, marcados, formatDate);
|
|
9313
|
+
const colapsa = rotulos.length > MAX_VALORES_VISIVEIS;
|
|
9314
|
+
const editavel = kind === "multi" || kind === "single" || kind === "boolean";
|
|
9315
|
+
const conteudoDoChip = /* @__PURE__ */ jsxs(
|
|
9316
|
+
"span",
|
|
9317
|
+
{
|
|
9318
|
+
role: editavel ? "button" : void 0,
|
|
9319
|
+
tabIndex: editavel ? 0 : void 0,
|
|
9320
|
+
"aria-label": editavel ? `Editar filtro ${campo.label}` : void 0,
|
|
9321
|
+
className: ganttAppliedChip({ interactive: editavel }),
|
|
9322
|
+
onKeyDown: (e3) => {
|
|
9323
|
+
if (editavel && (e3.key === "Enter" || e3.key === " ")) {
|
|
9324
|
+
e3.preventDefault();
|
|
9325
|
+
setAberto((a11) => a11 === campo.id ? null : campo.id);
|
|
9326
|
+
}
|
|
9327
|
+
},
|
|
9328
|
+
children: [
|
|
9329
|
+
/* @__PURE__ */ jsx(
|
|
9330
|
+
"button",
|
|
9331
|
+
{
|
|
9332
|
+
type: "button",
|
|
9333
|
+
"aria-label": `Remover filtro ${campo.label}`,
|
|
9334
|
+
className: ganttAppliedChipRemove(),
|
|
9335
|
+
onClick: (e3) => {
|
|
9336
|
+
e3.stopPropagation();
|
|
9337
|
+
onClearField(campo.id);
|
|
9338
|
+
},
|
|
9339
|
+
onKeyDown: (e3) => e3.stopPropagation(),
|
|
9340
|
+
children: /* @__PURE__ */ jsx(CircleX, { strokeWidth: 2, "aria-hidden": true })
|
|
9341
|
+
}
|
|
9342
|
+
),
|
|
9343
|
+
/* @__PURE__ */ jsx("span", { className: ganttAppliedChipName(), children: campo.label }),
|
|
9344
|
+
/* @__PURE__ */ jsx("span", { className: ganttAppliedChipOp(), children: operador }),
|
|
9345
|
+
colapsa ? /* @__PURE__ */ jsx("span", { className: ganttAppliedChipValue(), children: `${rotulos.length} selecionados` }) : /* @__PURE__ */ jsx("span", { className: "inline-flex items-center gap-gp-2xs", children: rotulos.map((r2) => /* @__PURE__ */ jsx("span", { className: ganttAppliedChipValue(), children: r2 }, r2)) })
|
|
9346
|
+
]
|
|
9347
|
+
}
|
|
9348
|
+
);
|
|
9349
|
+
if (!editavel) {
|
|
9350
|
+
return /* @__PURE__ */ jsx("span", { children: conteudoDoChip }, campo.id);
|
|
9351
|
+
}
|
|
9352
|
+
return /* @__PURE__ */ jsxs(
|
|
9353
|
+
Popover,
|
|
9354
|
+
{
|
|
9355
|
+
open: aberto === campo.id,
|
|
9356
|
+
onOpenChange: (v3) => setAberto(v3 ? campo.id : null),
|
|
9357
|
+
children: [
|
|
9358
|
+
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: conteudoDoChip }),
|
|
9359
|
+
/* @__PURE__ */ jsx(PopoverContent, { align: "start", className: "w-[260px] p-0", children: /* @__PURE__ */ jsx("div", { className: "flex max-h-[320px] flex-col overflow-y-auto py-pad-sm", children: (campo.options ?? []).map((opcao) => {
|
|
9360
|
+
const id = `gantt-chip-${campo.id}-${opcao.value}`;
|
|
9361
|
+
const n2 = counts[campo.id]?.[opcao.value] ?? 0;
|
|
9362
|
+
return /* @__PURE__ */ jsxs("label", { htmlFor: id, className: ganttFilterOption(), children: [
|
|
9363
|
+
/* @__PURE__ */ jsx(
|
|
9364
|
+
Checkbox,
|
|
9365
|
+
{
|
|
9366
|
+
id,
|
|
9367
|
+
checked: marcados.includes(opcao.value),
|
|
9368
|
+
onCheckedChange: () => onToggleValue(campo.id, opcao.value),
|
|
9369
|
+
className: ganttFilterOptionBox({
|
|
9370
|
+
colorKey: opcao.colorKey ?? "neutral"
|
|
9371
|
+
})
|
|
9372
|
+
}
|
|
9373
|
+
),
|
|
9374
|
+
/* @__PURE__ */ jsx("span", { className: ganttFilterOptionLabel(), children: opcao.label }),
|
|
9375
|
+
/* @__PURE__ */ jsx("span", { className: ganttFilterOptionCount(), children: n2 })
|
|
9376
|
+
] }, opcao.value);
|
|
9377
|
+
}) }) })
|
|
9378
|
+
]
|
|
9379
|
+
},
|
|
9380
|
+
campo.id
|
|
9381
|
+
);
|
|
9382
|
+
}),
|
|
9383
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: onClearAll, className: ganttClearLink(), children: "Limpar todos" })
|
|
9384
|
+
] });
|
|
9385
|
+
}
|
|
9386
|
+
const VIEW_ITEMS = [
|
|
9387
|
+
{ value: "timeline", label: "Cronograma", icon: /* @__PURE__ */ jsx(ChartNoAxesGantt, {}) },
|
|
9388
|
+
{ value: "calendar", label: "Calendário", icon: /* @__PURE__ */ jsx(CalendarDays, {}) },
|
|
9389
|
+
{ value: "list", label: "Lista", icon: /* @__PURE__ */ jsx(List, {}) }
|
|
9390
|
+
];
|
|
9391
|
+
const ZOOM_ITEMS = [
|
|
9392
|
+
{ value: "day", label: "Dia" },
|
|
9393
|
+
{ value: "week", label: "Semana" },
|
|
9394
|
+
{ value: "month", label: "Mês" },
|
|
9395
|
+
{ value: "quarter", label: "Trimestre" }
|
|
9396
|
+
];
|
|
9397
|
+
function GanttToolbar({
|
|
9398
|
+
title: title2,
|
|
9399
|
+
onPrev,
|
|
9400
|
+
onNext,
|
|
9401
|
+
onToday,
|
|
9402
|
+
view,
|
|
9403
|
+
views,
|
|
9404
|
+
onViewChange,
|
|
9405
|
+
granularity,
|
|
9406
|
+
onGranularityChange,
|
|
9407
|
+
searchable,
|
|
9408
|
+
search,
|
|
9409
|
+
onSearchChange,
|
|
9410
|
+
filterFields,
|
|
9411
|
+
onOpenFilterPanel,
|
|
9412
|
+
appliedCount,
|
|
9413
|
+
filterModel,
|
|
9414
|
+
onClearField,
|
|
9415
|
+
onClearAll,
|
|
9416
|
+
onToggleFilterValue,
|
|
9417
|
+
filterCounts,
|
|
9418
|
+
formatFilterDate,
|
|
9419
|
+
hasLinks,
|
|
9420
|
+
showCriticalToggle,
|
|
9421
|
+
criticalPath,
|
|
9422
|
+
onToggleCriticalPath,
|
|
9423
|
+
toolbarActions,
|
|
9424
|
+
primaryAction
|
|
9425
|
+
}) {
|
|
9426
|
+
const zoomAtivo = ZOOM_ITEMS.find((z3) => z3.value === granularity) ?? ZOOM_ITEMS[0];
|
|
9427
|
+
const itensDeVisao = VIEW_ITEMS.filter((v3) => views.includes(v3.value));
|
|
9428
|
+
const mostraSeletorDeVisao = itensDeVisao.length > 1;
|
|
9429
|
+
const temFiltros = (filterFields?.length ?? 0) > 0;
|
|
9430
|
+
const filtroEngajado = appliedCount > 0;
|
|
9431
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
|
9432
|
+
/* @__PURE__ */ jsxs("div", { className: ganttToolbar(), children: [
|
|
9433
|
+
/* @__PURE__ */ jsxs("div", { className: ganttToolbarSide({ slot: "leading" }), children: [
|
|
9434
|
+
mostraSeletorDeVisao ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9435
|
+
/* @__PURE__ */ jsx(
|
|
9436
|
+
"div",
|
|
9437
|
+
{
|
|
9438
|
+
role: "radiogroup",
|
|
9439
|
+
"aria-label": "Visualização",
|
|
9440
|
+
className: ganttViewSwitch(),
|
|
9441
|
+
children: itensDeVisao.map((v3) => {
|
|
9442
|
+
const ativa = v3.value === view;
|
|
9443
|
+
return /* @__PURE__ */ jsx(
|
|
9444
|
+
"button",
|
|
9445
|
+
{
|
|
9446
|
+
type: "button",
|
|
9447
|
+
role: "radio",
|
|
9448
|
+
"aria-checked": ativa,
|
|
9449
|
+
"aria-label": v3.label,
|
|
9450
|
+
title: v3.label,
|
|
9451
|
+
onClick: () => onViewChange(v3.value),
|
|
9452
|
+
className: ganttViewSwitchButton({ isActive: ativa }),
|
|
9453
|
+
children: v3.icon
|
|
9454
|
+
},
|
|
9455
|
+
v3.value
|
|
9456
|
+
);
|
|
9457
|
+
})
|
|
9458
|
+
}
|
|
9459
|
+
),
|
|
9460
|
+
/* @__PURE__ */ jsx("span", { className: ganttToolbarDivider(), "aria-hidden": true })
|
|
9461
|
+
] }) : null,
|
|
9462
|
+
/* @__PURE__ */ jsxs(
|
|
9463
|
+
"div",
|
|
9464
|
+
{
|
|
9465
|
+
role: "group",
|
|
9466
|
+
"aria-label": "Navegação de período",
|
|
9467
|
+
className: ganttNavGroup(),
|
|
9468
|
+
children: [
|
|
9469
|
+
/* @__PURE__ */ jsx(
|
|
9470
|
+
Button,
|
|
9471
|
+
{
|
|
9472
|
+
variant: "soft",
|
|
9473
|
+
color: "secondary",
|
|
9474
|
+
size: "icon-md",
|
|
9475
|
+
onClick: onPrev,
|
|
9476
|
+
"aria-label": "Período anterior",
|
|
9477
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, {})
|
|
9478
|
+
}
|
|
9479
|
+
),
|
|
9480
|
+
/* @__PURE__ */ jsx(Button, { variant: "soft", color: "secondary", size: "md", onClick: onToday, children: "Hoje" }),
|
|
9481
|
+
/* @__PURE__ */ jsx(
|
|
9482
|
+
Button,
|
|
9483
|
+
{
|
|
9484
|
+
variant: "soft",
|
|
9485
|
+
color: "secondary",
|
|
9486
|
+
size: "icon-md",
|
|
9487
|
+
onClick: onNext,
|
|
9488
|
+
"aria-label": "Próximo período",
|
|
9489
|
+
children: /* @__PURE__ */ jsx(ChevronRight, {})
|
|
9490
|
+
}
|
|
9491
|
+
)
|
|
9492
|
+
]
|
|
9493
|
+
}
|
|
9494
|
+
),
|
|
9495
|
+
/* @__PURE__ */ jsxs("span", { className: ganttTitle(), children: [
|
|
9496
|
+
/* @__PURE__ */ jsx(CalendarDays, { "aria-hidden": true }),
|
|
9497
|
+
/* @__PURE__ */ jsx("span", { className: ganttTitleText(), children: title2 })
|
|
9498
|
+
] }),
|
|
9499
|
+
/* @__PURE__ */ jsx(
|
|
9500
|
+
GanttMobileMenu,
|
|
9501
|
+
{
|
|
9502
|
+
view,
|
|
9503
|
+
views,
|
|
9504
|
+
onViewChange,
|
|
9505
|
+
granularity,
|
|
9506
|
+
onGranularityChange,
|
|
9507
|
+
onPrev,
|
|
9508
|
+
onToday,
|
|
9509
|
+
onNext,
|
|
9510
|
+
hasLinks,
|
|
9511
|
+
showCriticalToggle,
|
|
9512
|
+
criticalPath,
|
|
9513
|
+
onToggleCriticalPath
|
|
9514
|
+
}
|
|
9515
|
+
)
|
|
9516
|
+
] }),
|
|
9517
|
+
/* @__PURE__ */ jsxs("div", { className: ganttToolbarSide({ slot: "trailing" }), children: [
|
|
9518
|
+
searchable ? /* @__PURE__ */ jsxs("label", { className: ganttSearch(), children: [
|
|
9519
|
+
/* @__PURE__ */ jsx(Search, { "aria-hidden": true }),
|
|
9520
|
+
/* @__PURE__ */ jsx(
|
|
9521
|
+
"input",
|
|
9522
|
+
{
|
|
9523
|
+
type: "search",
|
|
9524
|
+
value: search,
|
|
9525
|
+
onChange: (e3) => onSearchChange(e3.target.value),
|
|
9526
|
+
placeholder: "Buscar tarefa…",
|
|
9527
|
+
"aria-label": "Buscar tarefa",
|
|
9528
|
+
className: ganttSearchInput()
|
|
9529
|
+
}
|
|
9530
|
+
)
|
|
9531
|
+
] }) : null,
|
|
9532
|
+
toolbarActions,
|
|
9533
|
+
hasLinks && showCriticalToggle ? /* @__PURE__ */ jsx(
|
|
9534
|
+
Button,
|
|
9535
|
+
{
|
|
9536
|
+
variant: criticalPath ? "soft" : "outline",
|
|
9537
|
+
color: criticalPath ? "primary" : "secondary",
|
|
9538
|
+
size: "md",
|
|
9539
|
+
iconLeft: /* @__PURE__ */ jsx(Zap, {}),
|
|
9540
|
+
onClick: onToggleCriticalPath,
|
|
9541
|
+
"aria-pressed": criticalPath,
|
|
9542
|
+
"aria-label": "Caminho crítico",
|
|
9543
|
+
className: cn(
|
|
9544
|
+
// `max-md:hidden` — em <md ele vira item do menu de opções.
|
|
9545
|
+
"shrink-0 max-md:hidden max-lg:size-form-lg max-lg:p-0",
|
|
9546
|
+
criticalPath && "border-border-brand hover:border-border-brand"
|
|
9547
|
+
),
|
|
9548
|
+
children: /* @__PURE__ */ jsx("span", { className: "hidden lg:inline", children: "Crítico" })
|
|
9549
|
+
}
|
|
9550
|
+
) : null,
|
|
9551
|
+
view === "timeline" ? /* @__PURE__ */ jsxs(DropdownMenu, { children: [
|
|
9552
|
+
/* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
9553
|
+
Button,
|
|
9554
|
+
{
|
|
9555
|
+
variant: "outline",
|
|
9556
|
+
color: "secondary",
|
|
9557
|
+
size: "md",
|
|
9558
|
+
iconLeft: /* @__PURE__ */ jsx(Columns3, {}),
|
|
9559
|
+
iconRight: /* @__PURE__ */ jsx(ChevronDown, {}),
|
|
9560
|
+
"aria-label": `Escala: ${zoomAtivo.label}`,
|
|
9561
|
+
className: "shrink-0 max-md:hidden",
|
|
9562
|
+
children: zoomAtivo.label
|
|
9563
|
+
}
|
|
9564
|
+
) }),
|
|
9565
|
+
/* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", className: "min-w-[168px]", children: /* @__PURE__ */ jsx(
|
|
9566
|
+
DropdownMenuRadioGroup,
|
|
9567
|
+
{
|
|
9568
|
+
value: granularity,
|
|
9569
|
+
onValueChange: (v3) => onGranularityChange(v3),
|
|
9570
|
+
children: ZOOM_ITEMS.map((z3) => /* @__PURE__ */ jsx(DropdownMenuRadioItem, { value: z3.value, children: z3.label }, z3.value))
|
|
9571
|
+
}
|
|
9572
|
+
) })
|
|
9573
|
+
] }) : null,
|
|
9574
|
+
temFiltros ? /* @__PURE__ */ jsxs("span", { className: "relative shrink-0", children: [
|
|
9575
|
+
/* @__PURE__ */ jsx(
|
|
9576
|
+
Button,
|
|
9577
|
+
{
|
|
9578
|
+
variant: filtroEngajado ? "soft" : "outline",
|
|
9579
|
+
color: filtroEngajado ? "primary" : "secondary",
|
|
9580
|
+
size: "icon-md",
|
|
9581
|
+
onClick: onOpenFilterPanel,
|
|
9582
|
+
"aria-label": "Filtro",
|
|
9583
|
+
title: "Filtro",
|
|
9584
|
+
className: cn(
|
|
9585
|
+
filtroEngajado && "border-border-brand hover:border-border-brand"
|
|
9586
|
+
),
|
|
9587
|
+
children: /* @__PURE__ */ jsx(Filter, {})
|
|
9588
|
+
}
|
|
9589
|
+
),
|
|
9590
|
+
appliedCount > 0 ? /* @__PURE__ */ jsx("span", { className: ganttFilterDot(), "aria-hidden": true }) : null
|
|
9591
|
+
] }) : null,
|
|
9592
|
+
primaryAction
|
|
9593
|
+
] })
|
|
9594
|
+
] }),
|
|
9595
|
+
temFiltros ? /* @__PURE__ */ jsx(
|
|
9596
|
+
GanttAppliedFilters,
|
|
9597
|
+
{
|
|
9598
|
+
fields: filterFields ?? [],
|
|
9599
|
+
model: filterModel,
|
|
9600
|
+
counts: filterCounts,
|
|
9601
|
+
onToggleValue: onToggleFilterValue,
|
|
9602
|
+
onClearField,
|
|
9603
|
+
onClearAll,
|
|
9604
|
+
formatDate: formatFilterDate
|
|
9605
|
+
}
|
|
9606
|
+
) : null
|
|
9607
|
+
] });
|
|
9608
|
+
}
|
|
9609
|
+
function GanttMobileMenu({
|
|
9610
|
+
view,
|
|
9611
|
+
views,
|
|
9612
|
+
onViewChange,
|
|
9613
|
+
granularity,
|
|
9614
|
+
onGranularityChange,
|
|
9615
|
+
onPrev,
|
|
9616
|
+
onToday,
|
|
9617
|
+
onNext,
|
|
9618
|
+
hasLinks,
|
|
9619
|
+
showCriticalToggle,
|
|
9620
|
+
criticalPath,
|
|
9621
|
+
onToggleCriticalPath
|
|
9622
|
+
}) {
|
|
9623
|
+
const estilos = ganttMobileMenu();
|
|
9624
|
+
const mostraCritico = hasLinks && showCriticalToggle;
|
|
9625
|
+
const itensDeVisao = VIEW_ITEMS.filter((v3) => views.includes(v3.value));
|
|
9626
|
+
return /* @__PURE__ */ jsxs(Popover, { children: [
|
|
9627
|
+
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
9628
|
+
Button,
|
|
9629
|
+
{
|
|
9630
|
+
variant: "outline",
|
|
9631
|
+
color: "secondary",
|
|
9632
|
+
size: "icon-md",
|
|
9633
|
+
"aria-label": "Opções do cronograma",
|
|
9634
|
+
title: "Opções do cronograma",
|
|
9635
|
+
className: estilos.trigger(),
|
|
9636
|
+
children: /* @__PURE__ */ jsx(SlidersHorizontal, {})
|
|
9637
|
+
}
|
|
9638
|
+
) }),
|
|
9639
|
+
/* @__PURE__ */ jsxs(PopoverContent, { align: "end", className: estilos.content(), children: [
|
|
9640
|
+
/* @__PURE__ */ jsx("div", { className: estilos.label(), children: "Período" }),
|
|
9641
|
+
/* @__PURE__ */ jsx("div", { className: estilos.field(), children: /* @__PURE__ */ jsx("div", { className: ganttMenuGroupFluid(), children: /* @__PURE__ */ jsxs(
|
|
9642
|
+
"div",
|
|
9643
|
+
{
|
|
9644
|
+
role: "group",
|
|
9645
|
+
"aria-label": "Navegação de período",
|
|
9646
|
+
className: cn(ganttNavGroup(), "!flex w-full"),
|
|
9647
|
+
children: [
|
|
9648
|
+
/* @__PURE__ */ jsx(
|
|
9649
|
+
Button,
|
|
9650
|
+
{
|
|
9651
|
+
variant: "soft",
|
|
9652
|
+
color: "secondary",
|
|
9653
|
+
size: "icon-md",
|
|
9654
|
+
onClick: onPrev,
|
|
9655
|
+
"aria-label": "Período anterior",
|
|
9656
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, {})
|
|
9657
|
+
}
|
|
9658
|
+
),
|
|
9659
|
+
/* @__PURE__ */ jsx(
|
|
9660
|
+
Button,
|
|
9661
|
+
{
|
|
9662
|
+
variant: "soft",
|
|
9663
|
+
color: "secondary",
|
|
9664
|
+
size: "md",
|
|
9665
|
+
onClick: onToday,
|
|
9666
|
+
className: "flex-1",
|
|
9667
|
+
children: "Hoje"
|
|
9668
|
+
}
|
|
9669
|
+
),
|
|
9670
|
+
/* @__PURE__ */ jsx(
|
|
9671
|
+
Button,
|
|
9672
|
+
{
|
|
9673
|
+
variant: "soft",
|
|
9674
|
+
color: "secondary",
|
|
9675
|
+
size: "icon-md",
|
|
9676
|
+
onClick: onNext,
|
|
9677
|
+
"aria-label": "Próximo período",
|
|
9678
|
+
children: /* @__PURE__ */ jsx(ChevronRight, {})
|
|
9679
|
+
}
|
|
9680
|
+
)
|
|
9681
|
+
]
|
|
9682
|
+
}
|
|
9683
|
+
) }) }),
|
|
9684
|
+
itensDeVisao.length > 1 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9685
|
+
/* @__PURE__ */ jsx("div", { className: estilos.separator() }),
|
|
9686
|
+
/* @__PURE__ */ jsx("div", { className: estilos.label(), children: "Visualização" }),
|
|
9687
|
+
itensDeVisao.map((v3) => /* @__PURE__ */ jsxs(
|
|
9688
|
+
"button",
|
|
9689
|
+
{
|
|
9690
|
+
type: "button",
|
|
9691
|
+
role: "radio",
|
|
9692
|
+
"aria-checked": v3.value === view,
|
|
9693
|
+
onClick: () => onViewChange(v3.value),
|
|
9694
|
+
className: estilos.item({ active: v3.value === view }),
|
|
9695
|
+
children: [
|
|
9696
|
+
v3.icon,
|
|
9697
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: v3.label }),
|
|
9698
|
+
v3.value === view ? /* @__PURE__ */ jsx(Check, { strokeWidth: 2.2 }) : null
|
|
9699
|
+
]
|
|
9700
|
+
},
|
|
9701
|
+
v3.value
|
|
9702
|
+
))
|
|
9703
|
+
] }) : null,
|
|
9704
|
+
view === "timeline" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9705
|
+
/* @__PURE__ */ jsx("div", { className: estilos.separator() }),
|
|
9706
|
+
/* @__PURE__ */ jsx("div", { className: estilos.label(), children: "Escala" }),
|
|
9707
|
+
ZOOM_ITEMS.map((z3) => /* @__PURE__ */ jsxs(
|
|
9708
|
+
"button",
|
|
9709
|
+
{
|
|
9710
|
+
type: "button",
|
|
9711
|
+
role: "radio",
|
|
9712
|
+
"aria-checked": z3.value === granularity,
|
|
9713
|
+
onClick: () => onGranularityChange(z3.value),
|
|
9714
|
+
className: estilos.item({ active: z3.value === granularity }),
|
|
9715
|
+
children: [
|
|
9716
|
+
/* @__PURE__ */ jsx(Columns3, {}),
|
|
9717
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: z3.label }),
|
|
9718
|
+
z3.value === granularity ? /* @__PURE__ */ jsx(Check, { strokeWidth: 2.2 }) : null
|
|
9719
|
+
]
|
|
9720
|
+
},
|
|
9721
|
+
z3.value
|
|
9722
|
+
))
|
|
9723
|
+
] }) : null,
|
|
9724
|
+
mostraCritico ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9725
|
+
/* @__PURE__ */ jsx("div", { className: estilos.separator() }),
|
|
9726
|
+
/* @__PURE__ */ jsxs(
|
|
9727
|
+
"button",
|
|
9728
|
+
{
|
|
9729
|
+
type: "button",
|
|
9730
|
+
"aria-pressed": criticalPath,
|
|
9731
|
+
onClick: onToggleCriticalPath,
|
|
9732
|
+
className: estilos.item({ active: criticalPath }),
|
|
9733
|
+
children: [
|
|
9734
|
+
/* @__PURE__ */ jsx(Zap, {}),
|
|
9735
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: "Caminho crítico" }),
|
|
9736
|
+
criticalPath ? /* @__PURE__ */ jsx(Check, { strokeWidth: 2.2 }) : null
|
|
9737
|
+
]
|
|
9738
|
+
}
|
|
9739
|
+
)
|
|
9740
|
+
] }) : null
|
|
9741
|
+
] })
|
|
9742
|
+
] });
|
|
9743
|
+
}
|
|
9744
|
+
const LIMIAR_DE_BUSCA = 7;
|
|
9745
|
+
function GanttFilterPanel({
|
|
9746
|
+
open,
|
|
9747
|
+
onOpenChange,
|
|
9748
|
+
fields,
|
|
9749
|
+
model,
|
|
9750
|
+
onToggleValue,
|
|
9751
|
+
onSetFieldValues,
|
|
9752
|
+
onClearAll,
|
|
9753
|
+
appliedCount,
|
|
9754
|
+
counts
|
|
9755
|
+
}) {
|
|
9756
|
+
const [recolhidos, setRecolhidos] = useState(() => /* @__PURE__ */ new Set());
|
|
9757
|
+
const [buscas, setBuscas] = useState({});
|
|
9758
|
+
const alternarGrupo = (id) => setRecolhidos((prev) => {
|
|
9759
|
+
const proximo = new Set(prev);
|
|
9760
|
+
if (proximo.has(id)) proximo.delete(id);
|
|
9761
|
+
else proximo.add(id);
|
|
9762
|
+
return proximo;
|
|
9763
|
+
});
|
|
9764
|
+
return /* @__PURE__ */ jsx(
|
|
9765
|
+
FloatingPanel,
|
|
9766
|
+
{
|
|
9767
|
+
open,
|
|
9768
|
+
onOpenChange,
|
|
9769
|
+
side: "right",
|
|
9770
|
+
size: "md",
|
|
9771
|
+
title: "Filtros",
|
|
9772
|
+
titleIcon: SlidersHorizontal,
|
|
9773
|
+
description: appliedCount === 1 ? "1 filtro ativo" : `${appliedCount} filtros ativos`,
|
|
9774
|
+
bodyPadded: false,
|
|
9775
|
+
footer: /* @__PURE__ */ jsx(
|
|
9776
|
+
Button,
|
|
9777
|
+
{
|
|
9778
|
+
variant: "ghost",
|
|
9779
|
+
color: "secondary",
|
|
9780
|
+
size: "sm",
|
|
9781
|
+
onClick: onClearAll,
|
|
9782
|
+
disabled: appliedCount === 0,
|
|
9783
|
+
children: "Limpar tudo"
|
|
9784
|
+
}
|
|
9785
|
+
),
|
|
9786
|
+
children: /* @__PURE__ */ jsx("div", { className: "flex flex-col", children: fields.map((campo) => /* @__PURE__ */ jsx(
|
|
9787
|
+
GrupoDeFiltro,
|
|
9788
|
+
{
|
|
9789
|
+
campo,
|
|
9790
|
+
marcadas: model[campo.id] ?? [],
|
|
9791
|
+
aberto: !recolhidos.has(campo.id),
|
|
9792
|
+
busca: buscas[campo.id] ?? "",
|
|
9793
|
+
contagens: counts?.[campo.id],
|
|
9794
|
+
onAlternarGrupo: () => alternarGrupo(campo.id),
|
|
9795
|
+
onBuscaChange: (v3) => setBuscas((prev) => ({ ...prev, [campo.id]: v3 })),
|
|
9796
|
+
onToggleValue: (v3) => onToggleValue(campo.id, v3),
|
|
9797
|
+
onSetValues: (vs) => onSetFieldValues(campo.id, vs)
|
|
9798
|
+
},
|
|
9799
|
+
campo.id
|
|
9800
|
+
)) })
|
|
9801
|
+
}
|
|
9802
|
+
);
|
|
9803
|
+
}
|
|
9804
|
+
function GrupoDeFiltro({
|
|
9805
|
+
campo,
|
|
9806
|
+
marcadas,
|
|
9807
|
+
aberto,
|
|
9808
|
+
busca,
|
|
9809
|
+
contagens,
|
|
9810
|
+
onAlternarGrupo,
|
|
9811
|
+
onBuscaChange,
|
|
9812
|
+
onToggleValue,
|
|
9813
|
+
onSetValues
|
|
9814
|
+
}) {
|
|
9815
|
+
const kind = campo.kind ?? "multi";
|
|
9816
|
+
const temLista = kind === "multi" || kind === "single";
|
|
9817
|
+
const opcoes = campo.options ?? [];
|
|
9818
|
+
const temBusca = temLista && (campo.searchable ?? opcoes.length >= LIMIAR_DE_BUSCA);
|
|
9819
|
+
const visiveis = useMemo(() => {
|
|
9820
|
+
const termo = busca.trim().toLowerCase();
|
|
9821
|
+
if (!termo) return opcoes;
|
|
9822
|
+
return opcoes.filter((o2) => o2.label.toLowerCase().includes(termo));
|
|
9823
|
+
}, [opcoes, busca]);
|
|
9824
|
+
const idsVisiveis = visiveis.map((o2) => o2.value);
|
|
9825
|
+
const todasVisiveisMarcadas = idsVisiveis.length > 0 && idsVisiveis.every((v3) => marcadas.includes(v3));
|
|
9826
|
+
return /* @__PURE__ */ jsxs("div", { className: ganttFilterGroup(), children: [
|
|
9827
|
+
/* @__PURE__ */ jsxs(
|
|
9828
|
+
"button",
|
|
9829
|
+
{
|
|
9830
|
+
type: "button",
|
|
9831
|
+
"aria-expanded": aberto,
|
|
9832
|
+
onClick: onAlternarGrupo,
|
|
9833
|
+
className: ganttFilterGroupHead(),
|
|
9834
|
+
children: [
|
|
9835
|
+
/* @__PURE__ */ jsxs("span", { className: ganttFilterGroupTitle(), children: [
|
|
9836
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: campo.label }),
|
|
9837
|
+
/* @__PURE__ */ jsx("span", { className: ganttFilterOptionCount(), children: temLista ? marcadas.length === 0 ? "Todas" : marcadas.length : marcadas.some((v3) => v3.trim() !== "") ? "1" : "—" })
|
|
9838
|
+
] }),
|
|
9839
|
+
/* @__PURE__ */ jsx(
|
|
9840
|
+
ChevronDown,
|
|
9841
|
+
{
|
|
9842
|
+
className: ganttFilterGroupChevron({ open: aberto }),
|
|
9843
|
+
"aria-hidden": true
|
|
9844
|
+
}
|
|
9845
|
+
)
|
|
9846
|
+
]
|
|
9847
|
+
}
|
|
9848
|
+
),
|
|
9849
|
+
aberto ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9850
|
+
kind === "text" ? /* @__PURE__ */ jsx("div", { className: ganttFilterRange(), children: /* @__PURE__ */ jsx(
|
|
9851
|
+
"input",
|
|
9852
|
+
{
|
|
9853
|
+
type: "text",
|
|
9854
|
+
value: marcadas[0] ?? "",
|
|
9855
|
+
onChange: (e3) => onSetValues(e3.target.value ? [e3.target.value] : []),
|
|
9856
|
+
placeholder: campo.placeholder ?? `Contém…`,
|
|
9857
|
+
"aria-label": campo.label,
|
|
9858
|
+
className: ganttFilterInput()
|
|
9859
|
+
}
|
|
9860
|
+
) }) : null,
|
|
9861
|
+
kind === "number" || kind === "date" ? /* @__PURE__ */ jsxs("div", { className: ganttFilterRange(), children: [
|
|
9862
|
+
/* @__PURE__ */ jsx(
|
|
9863
|
+
"input",
|
|
9864
|
+
{
|
|
9865
|
+
type: kind === "date" ? "date" : "number",
|
|
9866
|
+
value: marcadas[0] ?? "",
|
|
9867
|
+
onChange: (e3) => onSetValues([e3.target.value, marcadas[1] ?? ""]),
|
|
9868
|
+
placeholder: kind === "date" ? void 0 : "mín",
|
|
9869
|
+
"aria-label": `${campo.label} — de`,
|
|
9870
|
+
className: ganttFilterInput()
|
|
9871
|
+
}
|
|
9872
|
+
),
|
|
9873
|
+
/* @__PURE__ */ jsx("span", { className: ganttFilterRangeSep(), children: "e" }),
|
|
9874
|
+
/* @__PURE__ */ jsx(
|
|
9875
|
+
"input",
|
|
9876
|
+
{
|
|
9877
|
+
type: kind === "date" ? "date" : "number",
|
|
9878
|
+
value: marcadas[1] ?? "",
|
|
9879
|
+
onChange: (e3) => onSetValues([marcadas[0] ?? "", e3.target.value]),
|
|
9880
|
+
placeholder: kind === "date" ? void 0 : "máx",
|
|
9881
|
+
"aria-label": `${campo.label} — até`,
|
|
9882
|
+
className: ganttFilterInput()
|
|
9883
|
+
}
|
|
9884
|
+
)
|
|
9885
|
+
] }) : null,
|
|
9886
|
+
kind === "boolean" ? /* @__PURE__ */ jsx(
|
|
9887
|
+
RadioGroup,
|
|
9888
|
+
{
|
|
9889
|
+
value: marcadas[0] ?? "",
|
|
9890
|
+
onValueChange: (v3) => onSetValues(v3 ? [v3] : []),
|
|
9891
|
+
className: "flex flex-col gap-0",
|
|
9892
|
+
children: (campo.options ?? [
|
|
9893
|
+
{ value: "true", label: "Sim" },
|
|
9894
|
+
{ value: "false", label: "Não" }
|
|
9895
|
+
]).map((opt) => {
|
|
9896
|
+
const id = `gantt-filtro-${campo.id}-${opt.value}`;
|
|
9897
|
+
return /* @__PURE__ */ jsxs("label", { htmlFor: id, className: ganttFilterOption(), children: [
|
|
9898
|
+
/* @__PURE__ */ jsx(RadioGroupItem, { id, value: opt.value, className: "shrink-0" }),
|
|
9899
|
+
/* @__PURE__ */ jsx("span", { className: ganttFilterOptionLabel(), children: opt.label }),
|
|
9900
|
+
contagens?.[opt.value] !== void 0 ? /* @__PURE__ */ jsx("span", { className: ganttFilterOptionCount(), children: contagens[opt.value] }) : null
|
|
9901
|
+
] }, opt.value);
|
|
9902
|
+
})
|
|
9903
|
+
}
|
|
9904
|
+
) : null,
|
|
9905
|
+
temBusca ? /* @__PURE__ */ jsxs("label", { className: ganttFilterSearch(), children: [
|
|
9906
|
+
/* @__PURE__ */ jsx(Search, { "aria-hidden": true }),
|
|
9907
|
+
/* @__PURE__ */ jsx(
|
|
9908
|
+
"input",
|
|
9909
|
+
{
|
|
9910
|
+
type: "search",
|
|
9911
|
+
value: busca,
|
|
9912
|
+
onChange: (e3) => onBuscaChange(e3.target.value),
|
|
9913
|
+
placeholder: `Buscar em ${campo.label.toLowerCase()}…`,
|
|
9914
|
+
"aria-label": `Buscar em ${campo.label}`,
|
|
9915
|
+
className: ganttFilterSearchInput()
|
|
9916
|
+
}
|
|
9917
|
+
)
|
|
9918
|
+
] }) : null,
|
|
9919
|
+
kind === "multi" ? /* @__PURE__ */ jsxs("div", { className: ganttFilterGroupActions(), children: [
|
|
9920
|
+
/* @__PURE__ */ jsx(
|
|
9921
|
+
"button",
|
|
9922
|
+
{
|
|
9923
|
+
type: "button",
|
|
9924
|
+
onClick: () => onSetValues(
|
|
9925
|
+
todasVisiveisMarcadas ? marcadas.filter((v3) => !idsVisiveis.includes(v3)) : [.../* @__PURE__ */ new Set([...marcadas, ...idsVisiveis])]
|
|
9926
|
+
),
|
|
9927
|
+
disabled: idsVisiveis.length === 0,
|
|
9928
|
+
children: todasVisiveisMarcadas ? "Desmarcar todas" : "Selecionar todas"
|
|
9929
|
+
}
|
|
9930
|
+
),
|
|
9931
|
+
marcadas.length > 0 ? /* @__PURE__ */ jsx("button", { type: "button", onClick: () => onSetValues([]), children: "Limpar" }) : null
|
|
9932
|
+
] }) : null,
|
|
9933
|
+
kind !== "multi" && marcadas.some((v3) => v3.trim() !== "") ? /* @__PURE__ */ jsx("div", { className: ganttFilterGroupActions(), children: /* @__PURE__ */ jsx("button", { type: "button", onClick: () => onSetValues([]), children: "Limpar" }) }) : null,
|
|
9934
|
+
!temLista ? null : visiveis.length === 0 ? /* @__PURE__ */ jsx("p", { className: ganttFilterEmpty(), children: "Nada encontrado." }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
|
9935
|
+
kind === "single" ? (
|
|
9936
|
+
/*
|
|
9937
|
+
`single` = radio. Mesma anatomia de linha do `multi` (label
|
|
9938
|
+
nativo embrulhando o controle real — L-025), com o controle
|
|
9939
|
+
que expressa exclusividade.
|
|
9940
|
+
*/
|
|
9941
|
+
/* @__PURE__ */ jsx(
|
|
9942
|
+
RadioGroup,
|
|
9943
|
+
{
|
|
9944
|
+
value: marcadas[0] ?? "",
|
|
9945
|
+
onValueChange: (v3) => onSetValues(v3 ? [v3] : []),
|
|
9946
|
+
className: "flex flex-col gap-0",
|
|
9947
|
+
children: visiveis.map((opt) => {
|
|
9948
|
+
const id = `gantt-filtro-${campo.id}-${opt.value}`;
|
|
9949
|
+
const n2 = contagens?.[opt.value];
|
|
9950
|
+
return /* @__PURE__ */ jsxs("label", { htmlFor: id, className: ganttFilterOption(), children: [
|
|
9951
|
+
/* @__PURE__ */ jsx(RadioGroupItem, { id, value: opt.value, className: "shrink-0" }),
|
|
9952
|
+
/* @__PURE__ */ jsx("span", { className: ganttFilterOptionLabel(), children: opt.label }),
|
|
9953
|
+
n2 !== void 0 ? /* @__PURE__ */ jsx("span", { className: ganttFilterOptionCount(), children: n2 }) : null
|
|
9954
|
+
] }, opt.value);
|
|
9955
|
+
})
|
|
9956
|
+
}
|
|
9957
|
+
)
|
|
9958
|
+
) : null,
|
|
9959
|
+
kind === "multi" ? visiveis.map((opt) => {
|
|
9960
|
+
const id = `gantt-filtro-${campo.id}-${opt.value}`;
|
|
9961
|
+
const marcada = marcadas.includes(opt.value);
|
|
9962
|
+
const n2 = contagens?.[opt.value];
|
|
9963
|
+
return (
|
|
9964
|
+
/*
|
|
9965
|
+
`<label htmlFor>` nativo embrulhando o `Checkbox` — L-025.
|
|
9966
|
+
Com `<button onClick>` o leitor de tela anuncia "button" em
|
|
9967
|
+
vez de "checkbox", e o clique no rótulo não propaga pro
|
|
9968
|
+
controle real.
|
|
9969
|
+
*/
|
|
9970
|
+
/* @__PURE__ */ jsxs("label", { htmlFor: id, className: ganttFilterOption(), children: [
|
|
9971
|
+
/* @__PURE__ */ jsx(
|
|
9972
|
+
Checkbox,
|
|
9973
|
+
{
|
|
9974
|
+
id,
|
|
9975
|
+
checked: marcada,
|
|
9976
|
+
onCheckedChange: () => onToggleValue(opt.value),
|
|
9977
|
+
className: ganttFilterOptionBox({
|
|
9978
|
+
colorKey: opt.colorKey ?? "neutral"
|
|
9979
|
+
})
|
|
9980
|
+
}
|
|
9981
|
+
),
|
|
9982
|
+
/* @__PURE__ */ jsx("span", { className: ganttFilterOptionLabel(), children: opt.label }),
|
|
9983
|
+
n2 !== void 0 ? /* @__PURE__ */ jsx("span", { className: ganttFilterOptionCount(), children: n2 }) : null
|
|
9984
|
+
] }, opt.value)
|
|
9985
|
+
);
|
|
9986
|
+
}) : null
|
|
9987
|
+
] })
|
|
9988
|
+
] }) : null
|
|
9989
|
+
] });
|
|
9990
|
+
}
|
|
9991
|
+
const LARGURA_DO_NOME = [72, 58, 84, 64, 78, 52, 70, 60];
|
|
9992
|
+
const BARRAS = [
|
|
9993
|
+
{ left: 4, width: 26 },
|
|
9994
|
+
{ left: 18, width: 34 },
|
|
9995
|
+
{ left: 30, width: 20 },
|
|
9996
|
+
{ left: 44, width: 30 },
|
|
9997
|
+
{ left: 38, width: 42 },
|
|
9998
|
+
{ left: 60, width: 24 },
|
|
9999
|
+
{ left: 55, width: 33 },
|
|
10000
|
+
{ left: 70, width: 22 }
|
|
10001
|
+
];
|
|
10002
|
+
function GanttSkeleton({
|
|
10003
|
+
view,
|
|
10004
|
+
gridWidth,
|
|
10005
|
+
rows = 8
|
|
10006
|
+
}) {
|
|
10007
|
+
const e3 = ganttSkeleton();
|
|
10008
|
+
const indices = Array.from({ length: rows }, (_3, i4) => i4);
|
|
10009
|
+
const comum = {
|
|
10010
|
+
role: "status",
|
|
10011
|
+
"aria-busy": true,
|
|
10012
|
+
"aria-label": "Carregando cronograma"
|
|
10013
|
+
};
|
|
10014
|
+
if (view === "timeline") {
|
|
10015
|
+
return /* @__PURE__ */ jsx("div", { className: e3.root(), ...comum, children: /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1", children: [
|
|
10016
|
+
/* @__PURE__ */ jsxs("div", { className: e3.pane(), style: { width: gridWidth }, children: [
|
|
10017
|
+
/* @__PURE__ */ jsxs(
|
|
10018
|
+
"div",
|
|
10019
|
+
{
|
|
10020
|
+
className: e3.head(),
|
|
10021
|
+
style: { height: GANTT_HEAD_HEIGHT_PX },
|
|
10022
|
+
"aria-hidden": true,
|
|
10023
|
+
children: [
|
|
10024
|
+
/* @__PURE__ */ jsx("div", { className: e3.bar(), style: { width: 64, height: 10 } }),
|
|
10025
|
+
/* @__PURE__ */ jsx("div", { className: e3.bar(), style: { width: 44, height: 10 } })
|
|
10026
|
+
]
|
|
10027
|
+
}
|
|
10028
|
+
),
|
|
10029
|
+
indices.map((i4) => /* @__PURE__ */ jsx(
|
|
10030
|
+
"div",
|
|
10031
|
+
{
|
|
10032
|
+
className: e3.row(),
|
|
10033
|
+
style: { height: GANTT_ROW_HEIGHT_PX },
|
|
10034
|
+
"aria-hidden": true,
|
|
10035
|
+
children: /* @__PURE__ */ jsx(
|
|
10036
|
+
"div",
|
|
10037
|
+
{
|
|
10038
|
+
className: e3.bar(),
|
|
10039
|
+
style: {
|
|
10040
|
+
width: `${LARGURA_DO_NOME[i4 % LARGURA_DO_NOME.length]}%`,
|
|
10041
|
+
height: 10
|
|
10042
|
+
}
|
|
10043
|
+
}
|
|
10044
|
+
)
|
|
10045
|
+
},
|
|
10046
|
+
i4
|
|
10047
|
+
))
|
|
10048
|
+
] }),
|
|
10049
|
+
/* @__PURE__ */ jsxs("div", { className: e3.canvas(), children: [
|
|
10050
|
+
/* @__PURE__ */ jsx(
|
|
10051
|
+
"div",
|
|
10052
|
+
{
|
|
10053
|
+
className: e3.head(),
|
|
10054
|
+
style: { height: GANTT_HEAD_HEIGHT_PX },
|
|
10055
|
+
"aria-hidden": true
|
|
10056
|
+
}
|
|
10057
|
+
),
|
|
10058
|
+
indices.map((i4) => {
|
|
10059
|
+
const b4 = BARRAS[i4 % BARRAS.length];
|
|
10060
|
+
return /* @__PURE__ */ jsx(
|
|
10061
|
+
"div",
|
|
10062
|
+
{
|
|
10063
|
+
className: e3.row(),
|
|
10064
|
+
style: { height: GANTT_ROW_HEIGHT_PX },
|
|
10065
|
+
"aria-hidden": true,
|
|
10066
|
+
children: /* @__PURE__ */ jsx("div", { className: "relative h-[22px] w-full", children: /* @__PURE__ */ jsx(
|
|
10067
|
+
"div",
|
|
10068
|
+
{
|
|
10069
|
+
className: e3.bar(),
|
|
10070
|
+
style: {
|
|
10071
|
+
position: "absolute",
|
|
10072
|
+
left: `${b4.left}%`,
|
|
10073
|
+
width: `${b4.width}%`,
|
|
10074
|
+
height: 22
|
|
10075
|
+
}
|
|
10076
|
+
}
|
|
10077
|
+
) })
|
|
10078
|
+
},
|
|
10079
|
+
i4
|
|
10080
|
+
);
|
|
10081
|
+
})
|
|
10082
|
+
] })
|
|
10083
|
+
] }) });
|
|
10084
|
+
}
|
|
10085
|
+
if (view === "calendar") {
|
|
10086
|
+
return /* @__PURE__ */ jsx("div", { className: e3.root(), ...comum, children: /* @__PURE__ */ jsx("div", { className: e3.grid(), "aria-hidden": true, children: Array.from({ length: 42 }, (_3, i4) => /* @__PURE__ */ jsxs("div", { className: e3.cell(), children: [
|
|
10087
|
+
/* @__PURE__ */ jsx("div", { className: e3.bar(), style: { width: 16, height: 10 } }),
|
|
10088
|
+
i4 % 3 === 0 ? /* @__PURE__ */ jsx("div", { className: e3.bar(), style: { width: "80%", height: 14 } }) : null
|
|
10089
|
+
] }, i4)) }) });
|
|
10090
|
+
}
|
|
10091
|
+
return /* @__PURE__ */ jsx("div", { className: e3.root(), ...comum, children: /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-gp-2xl p-pad-xl", "aria-hidden": true, children: indices.slice(0, 4).map((i4) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-gp-md", children: [
|
|
10092
|
+
/* @__PURE__ */ jsx("div", { className: e3.bar(), style: { width: 140, height: 12 } }),
|
|
10093
|
+
Array.from({ length: i4 % 2 + 1 }, (_3, j2) => /* @__PURE__ */ jsx("div", { className: e3.bar(), style: { height: 44 } }, j2))
|
|
10094
|
+
] }, i4)) }) });
|
|
10095
|
+
}
|
|
10096
|
+
const GANTT_DEFAULT_COLUMNS = [
|
|
10097
|
+
{ id: "label", header: "Tarefa" },
|
|
10098
|
+
{ id: "start", header: "Início", width: 92, align: "left" },
|
|
10099
|
+
{ id: "end", header: "Fim", width: 92, align: "left" }
|
|
10100
|
+
];
|
|
10101
|
+
const RECUO_POR_NIVEL_PX = 16;
|
|
10102
|
+
const MEIO_DO_CHEVRON_PX = 10;
|
|
10103
|
+
function ConectoresDeArvore({
|
|
10104
|
+
depth,
|
|
10105
|
+
isLast,
|
|
10106
|
+
ancestorHasNext
|
|
10107
|
+
}) {
|
|
10108
|
+
if (depth <= 0) return null;
|
|
10109
|
+
return /* @__PURE__ */ jsx("span", { className: ganttTreeRail(), "aria-hidden": true, children: Array.from({ length: depth }, (_3, i4) => {
|
|
10110
|
+
const esquerda = i4 * RECUO_POR_NIVEL_PX;
|
|
10111
|
+
if (i4 === depth - 1) {
|
|
10112
|
+
return /* @__PURE__ */ jsxs("span", { className: ganttTreeColumn(), style: { left: esquerda }, children: [
|
|
10113
|
+
/* @__PURE__ */ jsx(
|
|
10114
|
+
"span",
|
|
10115
|
+
{
|
|
10116
|
+
className: ganttTreeSegment(),
|
|
10117
|
+
style: { left: MEIO_DO_CHEVRON_PX, width: 1, top: 0, height: "50%" }
|
|
10118
|
+
}
|
|
10119
|
+
),
|
|
10120
|
+
!isLast ? /* @__PURE__ */ jsx(
|
|
10121
|
+
"span",
|
|
10122
|
+
{
|
|
10123
|
+
className: ganttTreeSegment(),
|
|
10124
|
+
style: { left: MEIO_DO_CHEVRON_PX, width: 1, top: "50%", bottom: 0 }
|
|
10125
|
+
}
|
|
10126
|
+
) : null,
|
|
10127
|
+
/* @__PURE__ */ jsx(
|
|
10128
|
+
"span",
|
|
10129
|
+
{
|
|
10130
|
+
className: ganttTreeSegment(),
|
|
10131
|
+
style: { left: MEIO_DO_CHEVRON_PX, right: 0, top: "50%", height: 1 }
|
|
10132
|
+
}
|
|
10133
|
+
)
|
|
10134
|
+
] }, i4);
|
|
10135
|
+
}
|
|
10136
|
+
if (!ancestorHasNext[i4 + 1]) return null;
|
|
10137
|
+
return /* @__PURE__ */ jsx("span", { className: ganttTreeColumn(), style: { left: esquerda }, children: /* @__PURE__ */ jsx(
|
|
10138
|
+
"span",
|
|
10139
|
+
{
|
|
10140
|
+
className: ganttTreeSegment(),
|
|
10141
|
+
style: { left: MEIO_DO_CHEVRON_PX, width: 1, top: 0, bottom: 0 }
|
|
10142
|
+
}
|
|
10143
|
+
) }, i4);
|
|
10144
|
+
}) });
|
|
10145
|
+
}
|
|
10146
|
+
function GanttGrid({
|
|
10147
|
+
rows,
|
|
10148
|
+
columns,
|
|
10149
|
+
heights,
|
|
10150
|
+
scrollTop,
|
|
10151
|
+
locale,
|
|
10152
|
+
selectedRow,
|
|
10153
|
+
hoveredRow,
|
|
10154
|
+
onHoverRow,
|
|
10155
|
+
onRowToggle,
|
|
10156
|
+
onRowClick
|
|
10157
|
+
}) {
|
|
10158
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10159
|
+
/* @__PURE__ */ jsx("div", { className: ganttGridHead(), style: { height: GANTT_HEAD_HEIGHT_PX }, children: columns.map((col) => /* @__PURE__ */ jsx(
|
|
10160
|
+
"div",
|
|
10161
|
+
{
|
|
10162
|
+
className: ganttGridHeadCell({ align: col.align ?? "left" }),
|
|
10163
|
+
style: col.width ? { width: col.width } : { flex: 1, minWidth: 0 },
|
|
10164
|
+
children: col.header
|
|
10165
|
+
},
|
|
10166
|
+
col.id
|
|
10167
|
+
)) }),
|
|
10168
|
+
/* @__PURE__ */ jsx(
|
|
10169
|
+
"div",
|
|
10170
|
+
{
|
|
10171
|
+
className: "min-h-0 flex-1 overflow-hidden",
|
|
10172
|
+
"aria-hidden": false,
|
|
10173
|
+
role: "rowgroup",
|
|
10174
|
+
children: /* @__PURE__ */ jsx(
|
|
10175
|
+
"div",
|
|
10176
|
+
{
|
|
10177
|
+
style: { transform: `translateY(${-scrollTop}px)` },
|
|
10178
|
+
onMouseLeave: () => onHoverRow?.(null),
|
|
10179
|
+
children: rows.map(
|
|
10180
|
+
({ row, depth, hasChildren, collapsed, isLast, ancestorHasNext }, i4) => /* @__PURE__ */ jsx(
|
|
10181
|
+
"div",
|
|
10182
|
+
{
|
|
10183
|
+
role: "row",
|
|
10184
|
+
className: ganttGridRow({
|
|
10185
|
+
type: row.type ?? "task",
|
|
10186
|
+
selected: selectedRow === i4,
|
|
10187
|
+
hovered: hoveredRow === i4
|
|
10188
|
+
}),
|
|
10189
|
+
style: { height: heights[i4] },
|
|
10190
|
+
onMouseEnter: () => onHoverRow?.(i4),
|
|
10191
|
+
onClick: (e3) => onRowClick?.(row, e3),
|
|
10192
|
+
children: (row.type ?? "task") === "summary" ? /* @__PURE__ */ jsxs("div", { role: "cell", className: ganttGridGroupCell(), children: [
|
|
10193
|
+
/* @__PURE__ */ jsx(
|
|
10194
|
+
ConectoresDeArvore,
|
|
10195
|
+
{
|
|
10196
|
+
depth,
|
|
10197
|
+
isLast,
|
|
10198
|
+
ancestorHasNext
|
|
10199
|
+
}
|
|
10200
|
+
),
|
|
10201
|
+
/* @__PURE__ */ jsxs(
|
|
10202
|
+
"span",
|
|
10203
|
+
{
|
|
10204
|
+
className: "flex min-w-0 items-center gap-gp-sm",
|
|
10205
|
+
style: { paddingLeft: depth * 16 },
|
|
10206
|
+
children: [
|
|
10207
|
+
/* @__PURE__ */ jsx(
|
|
10208
|
+
"button",
|
|
10209
|
+
{
|
|
10210
|
+
type: "button",
|
|
10211
|
+
className: ganttChevron({
|
|
10212
|
+
open: !collapsed,
|
|
10213
|
+
hidden: !hasChildren
|
|
10214
|
+
}),
|
|
10215
|
+
"aria-label": collapsed ? "Expandir" : "Recolher",
|
|
10216
|
+
"aria-expanded": hasChildren ? !collapsed : void 0,
|
|
10217
|
+
tabIndex: hasChildren ? 0 : -1,
|
|
10218
|
+
onClick: (e3) => {
|
|
10219
|
+
e3.stopPropagation();
|
|
10220
|
+
onRowToggle?.(row.id, !collapsed);
|
|
10221
|
+
},
|
|
10222
|
+
children: /* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": true })
|
|
10223
|
+
}
|
|
10224
|
+
),
|
|
10225
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: row.label })
|
|
10226
|
+
]
|
|
10227
|
+
}
|
|
10228
|
+
),
|
|
10229
|
+
row.trailing ? /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center", children: row.trailing }) : null
|
|
10230
|
+
] }) : columns.map((col, i22) => {
|
|
10231
|
+
const primeira = i22 === 0;
|
|
10232
|
+
return /* @__PURE__ */ jsx(
|
|
10233
|
+
"div",
|
|
10234
|
+
{
|
|
10235
|
+
role: "cell",
|
|
10236
|
+
className: ganttGridCell({
|
|
10237
|
+
align: col.align ?? "left",
|
|
10238
|
+
numeric: col.id === "start" || col.id === "end",
|
|
10239
|
+
/**
|
|
10240
|
+
* #6 — só a PRIMEIRA coluna é informação.
|
|
10241
|
+
*
|
|
10242
|
+
* Data, duração e responsável são apoio: ficam em
|
|
10243
|
+
* `fg-muted` e peso normal. Com as quatro em
|
|
10244
|
+
* `fg-default`, a grade lia como um bloco de texto e o
|
|
10245
|
+
* nome da tarefa — que é o que se procura — não
|
|
10246
|
+
* ganhava nada por ser o primeiro.
|
|
10247
|
+
*/
|
|
10248
|
+
tone: primeira ? "default" : "muted"
|
|
10249
|
+
}),
|
|
10250
|
+
style: col.width ? { width: col.width } : { flex: 1, minWidth: 0 },
|
|
10251
|
+
children: primeira ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10252
|
+
/* @__PURE__ */ jsx(
|
|
10253
|
+
ConectoresDeArvore,
|
|
10254
|
+
{
|
|
10255
|
+
depth,
|
|
10256
|
+
isLast,
|
|
10257
|
+
ancestorHasNext
|
|
10258
|
+
}
|
|
10259
|
+
),
|
|
10260
|
+
/* @__PURE__ */ jsxs(
|
|
10261
|
+
"span",
|
|
10262
|
+
{
|
|
10263
|
+
className: "flex min-w-0 items-center gap-gp-sm",
|
|
10264
|
+
style: { paddingLeft: depth * 16 },
|
|
10265
|
+
children: [
|
|
10266
|
+
/* @__PURE__ */ jsx(
|
|
10267
|
+
"button",
|
|
10268
|
+
{
|
|
10269
|
+
type: "button",
|
|
10270
|
+
className: ganttChevron({
|
|
10271
|
+
open: !collapsed,
|
|
10272
|
+
hidden: !hasChildren
|
|
10273
|
+
}),
|
|
10274
|
+
"aria-label": collapsed ? "Expandir" : "Recolher",
|
|
10275
|
+
"aria-expanded": hasChildren ? !collapsed : void 0,
|
|
10276
|
+
tabIndex: hasChildren ? 0 : -1,
|
|
10277
|
+
onClick: (e3) => {
|
|
10278
|
+
e3.stopPropagation();
|
|
10279
|
+
onRowToggle?.(row.id, !collapsed);
|
|
10280
|
+
},
|
|
10281
|
+
children: /* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": true })
|
|
10282
|
+
}
|
|
10283
|
+
),
|
|
10284
|
+
col.render ? col.render(row) : /* @__PURE__ */ jsxs("span", { className: ganttGridLabel(), children: [
|
|
10285
|
+
/* @__PURE__ */ jsx("span", { className: ganttGridLabelText(), children: row.label }),
|
|
10286
|
+
row.sublabel ? /* @__PURE__ */ jsx("span", { className: ganttGridSublabel(), children: row.sublabel }) : null
|
|
10287
|
+
] })
|
|
10288
|
+
]
|
|
10289
|
+
}
|
|
10290
|
+
),
|
|
10291
|
+
row.trailing ? /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center pl-gp-2xs", children: row.trailing }) : null
|
|
10292
|
+
] }) : col.render?.(row) ?? celulaDefault(col.id, row, locale)
|
|
10293
|
+
},
|
|
10294
|
+
col.id
|
|
10295
|
+
);
|
|
10296
|
+
})
|
|
10297
|
+
},
|
|
10298
|
+
row.id
|
|
10299
|
+
)
|
|
10300
|
+
)
|
|
10301
|
+
}
|
|
10302
|
+
)
|
|
10303
|
+
}
|
|
10304
|
+
)
|
|
10305
|
+
] });
|
|
10306
|
+
}
|
|
10307
|
+
function celulaDefault(colId, row, locale) {
|
|
10308
|
+
const primeira = row.bars[0];
|
|
10309
|
+
if (!primeira) return null;
|
|
10310
|
+
if (colId === "start") return format(primeira.start, "dd/MM/yy", { locale });
|
|
10311
|
+
if (colId === "end") return format(primeira.end, "dd/MM/yy", { locale });
|
|
10312
|
+
if (colId === "duration") {
|
|
10313
|
+
const dias = Math.round(
|
|
10314
|
+
(primeira.end.getTime() - primeira.start.getTime()) / 864e5
|
|
10315
|
+
) + 1;
|
|
10316
|
+
return `${dias}d`;
|
|
10317
|
+
}
|
|
10318
|
+
if (colId === "progress") {
|
|
10319
|
+
return primeira.progress === void 0 ? null : `${primeira.progress}%`;
|
|
10320
|
+
}
|
|
10321
|
+
return null;
|
|
10322
|
+
}
|
|
10323
|
+
function buildTimeAxis(windowStart, windowEnd, granularity, locale, weekStartsOn = 0) {
|
|
10324
|
+
const inicio = startOfDay(windowStart);
|
|
10325
|
+
const fim = startOfDay(windowEnd);
|
|
10326
|
+
const totalDays = Math.max(1, differenceInCalendarDays(fim, inicio) + 1);
|
|
10327
|
+
const units = [];
|
|
10328
|
+
const groups = [];
|
|
10329
|
+
if (granularity === "day") {
|
|
10330
|
+
for (const dia of eachDayOfInterval({ start: inicio, end: fim })) {
|
|
10331
|
+
const dow = dia.getDay();
|
|
10332
|
+
units.push({
|
|
10333
|
+
date: dia,
|
|
10334
|
+
spanDays: 1,
|
|
10335
|
+
label: format(dia, "d", { locale }),
|
|
10336
|
+
isWeekend: dow === 0 || dow === 6
|
|
10337
|
+
});
|
|
10338
|
+
}
|
|
10339
|
+
empilharGrupos(groups, inicio, fim, "MMMM yyyy", startOfMonth, locale);
|
|
10340
|
+
} else if (granularity === "week") {
|
|
10341
|
+
let cursor = startOfWeek(inicio, { weekStartsOn });
|
|
10342
|
+
while (cursor <= fim) {
|
|
10343
|
+
const semanaFim = startOfDay(endOfWeek(cursor, { weekStartsOn }));
|
|
10344
|
+
const de = max([startOfDay(cursor), inicio]);
|
|
10345
|
+
const ate = min([semanaFim, fim]);
|
|
10346
|
+
units.push({
|
|
10347
|
+
date: de,
|
|
10348
|
+
spanDays: differenceInCalendarDays(ate, de) + 1,
|
|
10349
|
+
label: `${format(de, "dd/MM", { locale })}`,
|
|
10350
|
+
isWeekend: false
|
|
10351
|
+
});
|
|
10352
|
+
cursor = addDays(semanaFim, 1);
|
|
10353
|
+
}
|
|
10354
|
+
empilharGrupos(groups, inicio, fim, "MMMM yyyy", startOfMonth, locale);
|
|
10355
|
+
} else if (granularity === "month") {
|
|
10356
|
+
empilharGrupos(units, inicio, fim, "MMM", startOfMonth, locale);
|
|
10357
|
+
empilharGrupos(groups, inicio, fim, "yyyy", (d3) => new Date(d3.getFullYear(), 0, 1), locale);
|
|
10358
|
+
} else {
|
|
10359
|
+
empilharGrupos(units, inicio, fim, "QQQ", startOfQuarter, locale);
|
|
10360
|
+
empilharGrupos(groups, inicio, fim, "yyyy", (d3) => new Date(d3.getFullYear(), 0, 1), locale);
|
|
10361
|
+
}
|
|
10362
|
+
return { groups, units, totalDays };
|
|
10363
|
+
}
|
|
10364
|
+
function empilharGrupos(saida, inicio, fim, formato, inicioDoPeriodo, locale) {
|
|
10365
|
+
let cursor = inicio;
|
|
10366
|
+
let guarda = 0;
|
|
10367
|
+
while (cursor <= fim && guarda < 600) {
|
|
10368
|
+
const dentroDoPeriodo = inicioDoPeriodo(cursor);
|
|
10369
|
+
const proximo = inicioDoPeriodo(addDays(dentroDoPeriodo, diasDoPeriodo(formato)));
|
|
10370
|
+
const periodoFim = addDays(proximo, -1);
|
|
10371
|
+
const ate = min([periodoFim, fim]);
|
|
10372
|
+
saida.push({
|
|
10373
|
+
date: cursor,
|
|
10374
|
+
spanDays: differenceInCalendarDays(ate, cursor) + 1,
|
|
10375
|
+
label: format(cursor, formato, { locale }),
|
|
10376
|
+
isWeekend: false
|
|
10377
|
+
});
|
|
10378
|
+
cursor = addDays(ate, 1);
|
|
10379
|
+
guarda++;
|
|
10380
|
+
}
|
|
10381
|
+
}
|
|
10382
|
+
function diasDoPeriodo(formato) {
|
|
10383
|
+
if (formato === "yyyy") return 366;
|
|
10384
|
+
if (formato === "QQQ") return 93;
|
|
10385
|
+
return 32;
|
|
10386
|
+
}
|
|
10387
|
+
function dateToDayOffset(date, windowStart) {
|
|
10388
|
+
const base = startOfDay(windowStart);
|
|
10389
|
+
const dias = differenceInCalendarDays(startOfDay(date), base);
|
|
10390
|
+
const fracao = (date.getTime() - startOfDay(date).getTime()) / 864e5;
|
|
10391
|
+
return dias + fracao;
|
|
10392
|
+
}
|
|
10393
|
+
function dateToX(date, windowStart, pxPerDay) {
|
|
10394
|
+
return dateToDayOffset(date, windowStart) * pxPerDay;
|
|
10395
|
+
}
|
|
10396
|
+
function xToDate(x3, windowStart, pxPerDay) {
|
|
10397
|
+
if (pxPerDay <= 0) return startOfDay(windowStart);
|
|
10398
|
+
const dias = x3 / pxPerDay;
|
|
10399
|
+
const inteiros = Math.floor(dias);
|
|
10400
|
+
const resto = dias - inteiros;
|
|
10401
|
+
const base = addDays(startOfDay(windowStart), inteiros);
|
|
10402
|
+
return new Date(base.getTime() + Math.round(resto * 864e5));
|
|
10403
|
+
}
|
|
10404
|
+
function snapDate(date, granularity, weekStartsOn = 0) {
|
|
10405
|
+
if (granularity === "day") return startOfDay(date);
|
|
10406
|
+
if (granularity === "week") return startOfWeek(date, { weekStartsOn });
|
|
10407
|
+
if (granularity === "month") return startOfMonth(date);
|
|
10408
|
+
return startOfQuarter(date);
|
|
10409
|
+
}
|
|
10410
|
+
function clipToWindow(bar, windowStart, windowEnd) {
|
|
10411
|
+
const jIni = startOfDay(windowStart);
|
|
10412
|
+
const jFim = startOfDay(windowEnd);
|
|
10413
|
+
const bIni = startOfDay(bar.start);
|
|
10414
|
+
const bFim = startOfDay(bar.end);
|
|
10415
|
+
if (bFim < jIni || bIni > jFim) return null;
|
|
10416
|
+
const antes = bIni < jIni;
|
|
10417
|
+
const depois = bFim > jFim;
|
|
10418
|
+
return {
|
|
10419
|
+
bar,
|
|
10420
|
+
start: antes ? jIni : bar.start,
|
|
10421
|
+
end: depois ? addDays(jFim, 1) : bar.end,
|
|
10422
|
+
// O flag informado pelo consumidor VENCE o derivado: quem pagina no
|
|
10423
|
+
// servidor sabe que continua fora, e o componente não tem como saber.
|
|
10424
|
+
continuesBefore: bar.continuesBefore ?? antes,
|
|
10425
|
+
continuesAfter: bar.continuesAfter ?? depois
|
|
10426
|
+
};
|
|
10427
|
+
}
|
|
10428
|
+
function packLanes(bars, mode = "stack") {
|
|
10429
|
+
if (mode === "stack") return bars.map((b4) => [b4]);
|
|
10430
|
+
const lanes = [];
|
|
10431
|
+
const ordenadas = [...bars].sort((a11, b4) => a11.start.getTime() - b4.start.getTime());
|
|
10432
|
+
for (const barra of ordenadas) {
|
|
10433
|
+
const lane = lanes.find((l2) => {
|
|
10434
|
+
const ultima = l2[l2.length - 1];
|
|
10435
|
+
return ultima.end.getTime() <= barra.start.getTime();
|
|
10436
|
+
});
|
|
10437
|
+
if (lane) lane.push(barra);
|
|
10438
|
+
else lanes.push([barra]);
|
|
10439
|
+
}
|
|
10440
|
+
return lanes;
|
|
10441
|
+
}
|
|
10442
|
+
function flattenRows(rows, collapsedIds) {
|
|
10443
|
+
const porPai = /* @__PURE__ */ new Map();
|
|
10444
|
+
const ids = new Set(rows.map((r2) => r2.id));
|
|
10445
|
+
for (const row of rows) {
|
|
10446
|
+
const pai = row.parent && ids.has(row.parent) ? row.parent : null;
|
|
10447
|
+
const lista = porPai.get(pai);
|
|
10448
|
+
if (lista) lista.push(row);
|
|
10449
|
+
else porPai.set(pai, [row]);
|
|
10450
|
+
}
|
|
10451
|
+
const saida = [];
|
|
10452
|
+
const visitados = /* @__PURE__ */ new Set();
|
|
10453
|
+
const visitarIrmaos = (irmaos, depth, ancestorHasNext) => {
|
|
10454
|
+
irmaos.forEach((row, idx) => {
|
|
10455
|
+
if (visitados.has(row.id)) return;
|
|
10456
|
+
visitados.add(row.id);
|
|
10457
|
+
const filhos = porPai.get(row.id) ?? [];
|
|
10458
|
+
const colapsada = collapsedIds.has(row.id) || (row.collapsed ?? false);
|
|
10459
|
+
const isLast = idx === irmaos.length - 1;
|
|
10460
|
+
saida.push({
|
|
10461
|
+
row,
|
|
10462
|
+
depth,
|
|
10463
|
+
hasChildren: filhos.length > 0,
|
|
10464
|
+
collapsed: colapsada,
|
|
10465
|
+
isLast,
|
|
10466
|
+
ancestorHasNext
|
|
10467
|
+
});
|
|
10468
|
+
if (!colapsada) {
|
|
10469
|
+
visitarIrmaos(porPai.get(row.id) ?? [], depth + 1, [
|
|
10470
|
+
...ancestorHasNext,
|
|
10471
|
+
!isLast
|
|
10472
|
+
]);
|
|
10473
|
+
}
|
|
10474
|
+
});
|
|
10475
|
+
};
|
|
10476
|
+
visitarIrmaos(porPai.get(null) ?? [], 0, []);
|
|
10477
|
+
const alcancaveis = /* @__PURE__ */ new Set();
|
|
10478
|
+
const alcancar = (paiId) => {
|
|
10479
|
+
for (const row of porPai.get(paiId) ?? []) {
|
|
10480
|
+
if (alcancaveis.has(row.id)) continue;
|
|
10481
|
+
alcancaveis.add(row.id);
|
|
10482
|
+
alcancar(row.id);
|
|
10483
|
+
}
|
|
10484
|
+
};
|
|
10485
|
+
alcancar(null);
|
|
10486
|
+
const orfaos = rows.filter((r2) => !alcancaveis.has(r2.id));
|
|
10487
|
+
if (orfaos.length > 0) visitarIrmaos(orfaos, 0, []);
|
|
10488
|
+
return saida;
|
|
10489
|
+
}
|
|
10490
|
+
function deriveSummaryRange(rowId, rows) {
|
|
10491
|
+
const porPai = /* @__PURE__ */ new Map();
|
|
10492
|
+
for (const r2 of rows) {
|
|
10493
|
+
if (!r2.parent) continue;
|
|
10494
|
+
const l2 = porPai.get(r2.parent);
|
|
10495
|
+
if (l2) l2.push(r2);
|
|
10496
|
+
else porPai.set(r2.parent, [r2]);
|
|
10497
|
+
}
|
|
10498
|
+
const datas = [];
|
|
10499
|
+
const vistos = /* @__PURE__ */ new Set();
|
|
10500
|
+
const coletar = (id) => {
|
|
10501
|
+
if (vistos.has(id)) return;
|
|
10502
|
+
vistos.add(id);
|
|
10503
|
+
for (const filho of porPai.get(id) ?? []) {
|
|
10504
|
+
for (const b4 of filho.bars) {
|
|
10505
|
+
datas.push(b4.start, b4.end);
|
|
10506
|
+
}
|
|
10507
|
+
coletar(filho.id);
|
|
10508
|
+
}
|
|
10509
|
+
};
|
|
10510
|
+
coletar(rowId);
|
|
10511
|
+
if (datas.length === 0) return null;
|
|
10512
|
+
return { start: min(datas), end: max(datas) };
|
|
10513
|
+
}
|
|
10514
|
+
function deriveWindow(rows, granularity = "day") {
|
|
10515
|
+
const datas = [];
|
|
10516
|
+
for (const r2 of rows) for (const b4 of r2.bars) datas.push(b4.start, b4.end);
|
|
10517
|
+
if (datas.length === 0) {
|
|
10518
|
+
const hoje = startOfDay(/* @__PURE__ */ new Date());
|
|
10519
|
+
return { start: hoje, end: addDays(hoje, 29) };
|
|
10520
|
+
}
|
|
10521
|
+
const folga = granularity === "day" ? 1 : granularity === "week" ? 7 : 31;
|
|
10522
|
+
return {
|
|
10523
|
+
start: addDays(startOfDay(min(datas)), -folga),
|
|
10524
|
+
end: addDays(startOfDay(max(datas)), folga)
|
|
10525
|
+
};
|
|
10526
|
+
}
|
|
10527
|
+
function rowHeights(rows, baseHeight, laneHeight) {
|
|
10528
|
+
return rows.map(({ row }) => {
|
|
10529
|
+
const modo = row.lanePacking ?? "stack";
|
|
10530
|
+
const n2 = modo === "stack" ? Math.max(1, row.bars.length) : 1;
|
|
10531
|
+
return Math.max(baseHeight, n2 * laneHeight + 8);
|
|
10532
|
+
});
|
|
10533
|
+
}
|
|
10534
|
+
function rowOffsets(heights) {
|
|
10535
|
+
const saida = [];
|
|
10536
|
+
let acc = 0;
|
|
10537
|
+
for (const h3 of heights) {
|
|
10538
|
+
saida.push(acc);
|
|
10539
|
+
acc += h3;
|
|
10540
|
+
}
|
|
10541
|
+
return saida;
|
|
10542
|
+
}
|
|
10543
|
+
function buildMonthMatrix(anchor, weekStartsOn = 0) {
|
|
10544
|
+
const primeiro = startOfMonth(anchor);
|
|
10545
|
+
const inicio = startOfWeek(primeiro, { weekStartsOn });
|
|
10546
|
+
const semanas = [];
|
|
10547
|
+
for (let s2 = 0; s2 < 6; s2++) {
|
|
10548
|
+
const semana = [];
|
|
10549
|
+
for (let d3 = 0; d3 < 7; d3++) semana.push(addDays(inicio, s2 * 7 + d3));
|
|
10550
|
+
semanas.push(semana);
|
|
10551
|
+
}
|
|
10552
|
+
return semanas;
|
|
10553
|
+
}
|
|
10554
|
+
function computeOverflow(total, max2) {
|
|
10555
|
+
if (max2 <= 0) return { visible: 0, overflowCount: total };
|
|
10556
|
+
if (total <= max2) return { visible: total, overflowCount: 0 };
|
|
10557
|
+
const visible = Math.max(0, max2 - 1);
|
|
10558
|
+
return { visible, overflowCount: total - visible };
|
|
10559
|
+
}
|
|
10560
|
+
function daysOfBar(bar) {
|
|
10561
|
+
const saida = [];
|
|
10562
|
+
let cursor = startOfDay(bar.start);
|
|
10563
|
+
const fim = startOfDay(bar.end);
|
|
10564
|
+
if (fim < cursor) return saida;
|
|
10565
|
+
let guarda = 0;
|
|
10566
|
+
while (cursor.getTime() <= fim.getTime() && guarda < 400) {
|
|
10567
|
+
saida.push(cursor);
|
|
10568
|
+
cursor = addDays(cursor, 1);
|
|
10569
|
+
guarda++;
|
|
10570
|
+
}
|
|
10571
|
+
return saida;
|
|
10572
|
+
}
|
|
10573
|
+
function monthMatrixRange(weeks) {
|
|
10574
|
+
return { start: weeks[0][0], end: endOfMonthMatrix(weeks) };
|
|
10575
|
+
}
|
|
10576
|
+
function endOfMonthMatrix(weeks) {
|
|
10577
|
+
const ultima = weeks[weeks.length - 1];
|
|
10578
|
+
return ultima[ultima.length - 1];
|
|
10579
|
+
}
|
|
10580
|
+
function monthBounds(anchor) {
|
|
10581
|
+
return { start: startOfMonth(anchor), end: endOfMonth(anchor) };
|
|
10582
|
+
}
|
|
10583
|
+
function buildWeekSegments(week, barras) {
|
|
10584
|
+
if (week.length === 0) return [];
|
|
10585
|
+
const primeiro = startOfDay(week[0]);
|
|
10586
|
+
const ultimo = startOfDay(week[week.length - 1]);
|
|
10587
|
+
const brutos = [];
|
|
10588
|
+
for (const b4 of barras) {
|
|
10589
|
+
const ini = startOfDay(b4.start);
|
|
10590
|
+
const fim = startOfDay(b4.end);
|
|
10591
|
+
if (differenceInCalendarDays(fim, ini) < 0) continue;
|
|
10592
|
+
if (differenceInCalendarDays(fim, primeiro) < 0 || differenceInCalendarDays(ini, ultimo) > 0) {
|
|
10593
|
+
continue;
|
|
10594
|
+
}
|
|
10595
|
+
const colStart = Math.max(0, differenceInCalendarDays(ini, primeiro));
|
|
10596
|
+
const colEnd = Math.min(
|
|
10597
|
+
week.length - 1,
|
|
10598
|
+
differenceInCalendarDays(fim, primeiro)
|
|
10599
|
+
);
|
|
10600
|
+
brutos.push({
|
|
10601
|
+
barId: b4.id,
|
|
10602
|
+
colStart,
|
|
10603
|
+
colSpan: colEnd - colStart + 1,
|
|
10604
|
+
// ⚠️ Comparado contra a BORDA da semana, não contra `colStart`: uma barra
|
|
10605
|
+
// que começa exatamente no domingo tem `colStart` 0 e NÃO continua antes.
|
|
10606
|
+
continuesBefore: differenceInCalendarDays(ini, primeiro) < 0,
|
|
10607
|
+
continuesAfter: differenceInCalendarDays(fim, ultimo) > 0
|
|
10608
|
+
});
|
|
10609
|
+
}
|
|
10610
|
+
brutos.sort(
|
|
10611
|
+
(a11, b4) => a11.colStart !== b4.colStart ? a11.colStart - b4.colStart : b4.colSpan - a11.colSpan || a11.barId.localeCompare(b4.barId)
|
|
10612
|
+
);
|
|
10613
|
+
const ocupacao = [];
|
|
10614
|
+
const saida = [];
|
|
10615
|
+
for (const s2 of brutos) {
|
|
10616
|
+
let lane = ocupacao.findIndex((ate) => ate < s2.colStart);
|
|
10617
|
+
if (lane === -1) {
|
|
10618
|
+
lane = ocupacao.length;
|
|
10619
|
+
ocupacao.push(-1);
|
|
10620
|
+
}
|
|
10621
|
+
ocupacao[lane] = s2.colStart + s2.colSpan - 1;
|
|
10622
|
+
saida.push({ ...s2, lane });
|
|
10623
|
+
}
|
|
10624
|
+
return saida;
|
|
10625
|
+
}
|
|
10626
|
+
function hiddenPerColumn(segmentos, maxLanes, colunas = 7) {
|
|
10627
|
+
const saida = new Array(colunas).fill(0);
|
|
10628
|
+
if (maxLanes < 0) return saida;
|
|
10629
|
+
for (const s2 of segmentos) {
|
|
10630
|
+
if (s2.lane < maxLanes) continue;
|
|
10631
|
+
for (let c3 = s2.colStart; c3 < s2.colStart + s2.colSpan && c3 < colunas; c3++) {
|
|
10632
|
+
saida[c3] = (saida[c3] ?? 0) + 1;
|
|
10633
|
+
}
|
|
10634
|
+
}
|
|
10635
|
+
return saida;
|
|
10636
|
+
}
|
|
10637
|
+
function laneCount(segmentos) {
|
|
10638
|
+
let max2 = -1;
|
|
10639
|
+
for (const s2 of segmentos) if (s2.lane > max2) max2 = s2.lane;
|
|
10640
|
+
return max2 + 1;
|
|
10641
|
+
}
|
|
10642
|
+
const ALTURA_DA_FAIXA_PX = 20;
|
|
10643
|
+
const ALTURA_DO_CABECALHO_PX = 26;
|
|
10644
|
+
const MIN_FAIXAS = 1;
|
|
10645
|
+
function GanttCalendarView({
|
|
10646
|
+
rows,
|
|
10647
|
+
anchor,
|
|
10648
|
+
weekStartsOn,
|
|
10649
|
+
now,
|
|
10650
|
+
locale,
|
|
10651
|
+
conflictBarIds,
|
|
10652
|
+
criticalBarIds,
|
|
10653
|
+
onBarClick,
|
|
10654
|
+
onDayAdd
|
|
10655
|
+
}) {
|
|
10656
|
+
const semanas = useMemo(
|
|
10657
|
+
() => buildMonthMatrix(anchor, weekStartsOn),
|
|
10658
|
+
[anchor, weekStartsOn]
|
|
10659
|
+
);
|
|
10660
|
+
const entradas = useMemo(() => {
|
|
10661
|
+
const mapa = /* @__PURE__ */ new Map();
|
|
10662
|
+
for (const { row } of rows) {
|
|
10663
|
+
if ((row.type ?? "task") === "summary") continue;
|
|
10664
|
+
for (const bar of row.bars) {
|
|
10665
|
+
mapa.set(bar.id, { bar, row, colorKey: bar.colorKey ?? "chart-1" });
|
|
10666
|
+
}
|
|
10667
|
+
}
|
|
10668
|
+
return mapa;
|
|
10669
|
+
}, [rows]);
|
|
10670
|
+
const barrasParaSegmentar = useMemo(
|
|
10671
|
+
() => [...entradas.values()].map(({ bar }) => ({
|
|
10672
|
+
id: bar.id,
|
|
10673
|
+
start: startOfDay(bar.start),
|
|
10674
|
+
end: startOfDay(bar.end)
|
|
10675
|
+
})),
|
|
10676
|
+
[entradas]
|
|
10677
|
+
);
|
|
10678
|
+
const porSemana = useMemo(
|
|
10679
|
+
() => semanas.map((semana) => buildWeekSegments(semana, barrasParaSegmentar)),
|
|
10680
|
+
[semanas, barrasParaSegmentar]
|
|
10681
|
+
);
|
|
10682
|
+
const [expandidas, setExpandidas] = useState(() => /* @__PURE__ */ new Set());
|
|
10683
|
+
const gradeRef = useRef(null);
|
|
10684
|
+
const [maxFaixas, setMaxFaixas] = useState(3);
|
|
10685
|
+
useEffect(() => {
|
|
10686
|
+
const el = gradeRef.current;
|
|
10687
|
+
if (!el) return;
|
|
10688
|
+
const medir = () => {
|
|
10689
|
+
const primeira = el.firstElementChild;
|
|
10690
|
+
const altura = primeira ? primeira.getBoundingClientRect().height : el.getBoundingClientRect().height / 6;
|
|
10691
|
+
const util = altura - ALTURA_DO_CABECALHO_PX;
|
|
10692
|
+
setMaxFaixas(Math.max(MIN_FAIXAS, Math.floor(util / ALTURA_DA_FAIXA_PX)));
|
|
10693
|
+
};
|
|
10694
|
+
medir();
|
|
10695
|
+
const ro = new ResizeObserver(medir);
|
|
10696
|
+
ro.observe(el);
|
|
10697
|
+
return () => ro.disconnect();
|
|
10698
|
+
}, []);
|
|
10699
|
+
const rotulosDaSemana = useMemo(
|
|
10700
|
+
() => (semanas[0] ?? []).map((d3) => format(d3, "EEEEEE", { locale })),
|
|
10701
|
+
[semanas, locale]
|
|
10702
|
+
);
|
|
10703
|
+
return /* @__PURE__ */ jsxs("div", { className: ganttMonthFrame(), children: [
|
|
10704
|
+
/* @__PURE__ */ jsx("div", { className: ganttWeekdayRow(), children: rotulosDaSemana.map((r2, i4) => /* @__PURE__ */ jsx("div", { className: ganttWeekdayCell(), children: r2 }, i4)) }),
|
|
10705
|
+
/* @__PURE__ */ jsx("div", { className: ganttMonthGrid(), ref: gradeRef, children: semanas.map((semana, iSemana) => {
|
|
10706
|
+
const segmentos = porSemana[iSemana] ?? [];
|
|
10707
|
+
const aberta = expandidas.has(iSemana);
|
|
10708
|
+
const total = laneCount(segmentos);
|
|
10709
|
+
const visiveis = aberta ? total : total > maxFaixas ? Math.max(1, maxFaixas - 1) : total;
|
|
10710
|
+
const escondidosPorColuna = hiddenPerColumn(segmentos, visiveis);
|
|
10711
|
+
return /* @__PURE__ */ jsxs(
|
|
10712
|
+
"div",
|
|
10713
|
+
{
|
|
10714
|
+
className: ganttWeekRow(),
|
|
10715
|
+
style: aberta ? {
|
|
10716
|
+
minHeight: ALTURA_DO_CABECALHO_PX + total * ALTURA_DA_FAIXA_PX + 24
|
|
10717
|
+
} : void 0,
|
|
10718
|
+
children: [
|
|
10719
|
+
/* @__PURE__ */ jsx("div", { className: ganttWeekDays(), children: semana.map((dia, iCol) => {
|
|
10720
|
+
const escondidos = escondidosPorColuna[iCol] ?? 0;
|
|
10721
|
+
return /* @__PURE__ */ jsxs(
|
|
10722
|
+
"div",
|
|
10723
|
+
{
|
|
10724
|
+
className: ganttMonthCell({
|
|
10725
|
+
outside: !isSameMonth(dia, anchor),
|
|
10726
|
+
today: isSameDay(dia, now)
|
|
10727
|
+
}),
|
|
10728
|
+
children: [
|
|
10729
|
+
/* @__PURE__ */ jsx(
|
|
10730
|
+
"span",
|
|
10731
|
+
{
|
|
10732
|
+
className: ganttDayNumber({
|
|
10733
|
+
today: isSameDay(dia, now),
|
|
10734
|
+
// O fundo não distingue mais outro-mês de fim de
|
|
10735
|
+
// semana (os dois são `bg-canvas`) — quem distingue é
|
|
10736
|
+
// o número. Ver a nota do `ganttDayNumber`.
|
|
10737
|
+
outside: !isSameMonth(dia, anchor)
|
|
10738
|
+
}),
|
|
10739
|
+
children: format(dia, "d", { locale })
|
|
10740
|
+
}
|
|
10741
|
+
),
|
|
10742
|
+
escondidos > 0 && !aberta ? /* @__PURE__ */ jsx(
|
|
10743
|
+
"button",
|
|
10744
|
+
{
|
|
10745
|
+
type: "button",
|
|
10746
|
+
className: ganttOverflowButton(),
|
|
10747
|
+
style: {
|
|
10748
|
+
top: ALTURA_DO_CABECALHO_PX + visiveis * ALTURA_DA_FAIXA_PX
|
|
10749
|
+
},
|
|
10750
|
+
onClick: () => setExpandidas((prev) => new Set(prev).add(iSemana)),
|
|
10751
|
+
children: `+${escondidos} mais`
|
|
10752
|
+
}
|
|
10753
|
+
) : null,
|
|
10754
|
+
onDayAdd ? /* @__PURE__ */ jsx(
|
|
10755
|
+
"button",
|
|
10756
|
+
{
|
|
10757
|
+
type: "button",
|
|
10758
|
+
className: ganttDayAdd(),
|
|
10759
|
+
"aria-label": `Adicionar em ${format(dia, "d 'de' MMMM", { locale })}`,
|
|
10760
|
+
title: "Adicionar",
|
|
10761
|
+
onClick: () => onDayAdd(dia),
|
|
10762
|
+
children: /* @__PURE__ */ jsx(Plus, { "aria-hidden": true })
|
|
10763
|
+
}
|
|
10764
|
+
) : null
|
|
10765
|
+
]
|
|
10766
|
+
},
|
|
10767
|
+
dia.getTime()
|
|
10768
|
+
);
|
|
10769
|
+
}) }),
|
|
10770
|
+
/* @__PURE__ */ jsx(
|
|
10771
|
+
"div",
|
|
10772
|
+
{
|
|
10773
|
+
className: ganttWeekSegments(),
|
|
10774
|
+
style: { top: ALTURA_DO_CABECALHO_PX },
|
|
10775
|
+
children: segmentos.filter((seg) => seg.lane < visiveis).map((seg) => {
|
|
10776
|
+
const entrada = entradas.get(seg.barId);
|
|
10777
|
+
if (!entrada) return null;
|
|
10778
|
+
const { bar, row, colorKey } = entrada;
|
|
10779
|
+
const estilos = ganttDaySegment({
|
|
10780
|
+
colorKey,
|
|
10781
|
+
continuesBefore: seg.continuesBefore,
|
|
10782
|
+
continuesAfter: seg.continuesAfter,
|
|
10783
|
+
conflict: conflictBarIds.has(bar.id),
|
|
10784
|
+
critical: criticalBarIds.has(bar.id)
|
|
10785
|
+
});
|
|
10786
|
+
return /* @__PURE__ */ jsxs(
|
|
10787
|
+
"button",
|
|
10788
|
+
{
|
|
10789
|
+
type: "button",
|
|
10790
|
+
className: estilos.root(),
|
|
10791
|
+
style: {
|
|
10792
|
+
left: `calc(${seg.colStart / 7 * 100}% + 2px)`,
|
|
10793
|
+
width: `calc(${seg.colSpan / 7 * 100}% - 4px)`,
|
|
10794
|
+
top: seg.lane * ALTURA_DA_FAIXA_PX
|
|
10795
|
+
},
|
|
10796
|
+
onClick: (e3) => onBarClick?.(bar, row, e3),
|
|
10797
|
+
title: bar.searchText ?? void 0,
|
|
10798
|
+
children: [
|
|
10799
|
+
/* @__PURE__ */ jsx("span", { className: estilos.dot(), "aria-hidden": true }),
|
|
10800
|
+
/* @__PURE__ */ jsx("span", { className: estilos.label(), children: bar.label ?? (typeof row.label === "string" ? row.label : bar.id) })
|
|
10801
|
+
]
|
|
10802
|
+
},
|
|
10803
|
+
`${iSemana}-${seg.barId}`
|
|
10804
|
+
);
|
|
10805
|
+
})
|
|
10806
|
+
}
|
|
10807
|
+
),
|
|
10808
|
+
aberta && total > maxFaixas ? /* @__PURE__ */ jsx(
|
|
10809
|
+
"button",
|
|
10810
|
+
{
|
|
10811
|
+
type: "button",
|
|
10812
|
+
className: `${ganttOverflowButton()} bottom-pad-2xs top-auto z-[3]`,
|
|
10813
|
+
onClick: () => setExpandidas((prev) => {
|
|
10814
|
+
const proximo = new Set(prev);
|
|
10815
|
+
proximo.delete(iSemana);
|
|
10816
|
+
return proximo;
|
|
10817
|
+
}),
|
|
10818
|
+
children: "Recolher"
|
|
10819
|
+
}
|
|
10820
|
+
) : null
|
|
10821
|
+
]
|
|
10822
|
+
},
|
|
10823
|
+
iSemana
|
|
10824
|
+
);
|
|
10825
|
+
}) })
|
|
10826
|
+
] });
|
|
10827
|
+
}
|
|
10828
|
+
function GanttListView({
|
|
10829
|
+
rows,
|
|
10830
|
+
windowStart,
|
|
10831
|
+
windowEnd,
|
|
10832
|
+
now,
|
|
10833
|
+
locale,
|
|
10834
|
+
conflictBarIds,
|
|
10835
|
+
criticalBarIds,
|
|
10836
|
+
onBarClick,
|
|
10837
|
+
emptyState
|
|
10838
|
+
}) {
|
|
10839
|
+
const dias = useMemo(() => {
|
|
10840
|
+
const jIni = startOfDay(windowStart);
|
|
10841
|
+
const jFim = startOfDay(windowEnd);
|
|
10842
|
+
if (differenceInCalendarDays(jFim, jIni) < 0) return [];
|
|
10843
|
+
const mapa = /* @__PURE__ */ new Map();
|
|
10844
|
+
for (const { row } of rows) {
|
|
10845
|
+
if ((row.type ?? "task") === "summary") continue;
|
|
10846
|
+
for (const bar of row.bars) {
|
|
10847
|
+
const bIni = startOfDay(bar.start);
|
|
10848
|
+
const bFim = startOfDay(bar.end);
|
|
10849
|
+
if (differenceInCalendarDays(bFim, bIni) < 0) continue;
|
|
10850
|
+
const totalDeDias = differenceInCalendarDays(bFim, bIni) + 1;
|
|
10851
|
+
const de = differenceInCalendarDays(bIni, jIni) < 0 ? jIni : bIni;
|
|
10852
|
+
const ate = differenceInCalendarDays(bFim, jFim) > 0 ? jFim : bFim;
|
|
10853
|
+
if (differenceInCalendarDays(ate, de) < 0) continue;
|
|
10854
|
+
for (const dia of eachDayOfInterval({ start: de, end: ate })) {
|
|
10855
|
+
const chave = format(dia, "yyyy-MM-dd");
|
|
10856
|
+
const balde = mapa.get(chave) ?? { dia, itens: [] };
|
|
10857
|
+
balde.itens.push({
|
|
10858
|
+
bar,
|
|
10859
|
+
row,
|
|
10860
|
+
colorKey: bar.colorKey ?? "chart-1",
|
|
10861
|
+
diaDoIntervalo: differenceInCalendarDays(dia, bIni) + 1,
|
|
10862
|
+
totalDeDias
|
|
10863
|
+
});
|
|
10864
|
+
mapa.set(chave, balde);
|
|
10865
|
+
}
|
|
10866
|
+
}
|
|
10867
|
+
}
|
|
10868
|
+
return [...mapa.entries()].sort(([a11], [b4]) => a11.localeCompare(b4)).map(([, v3]) => v3);
|
|
10869
|
+
}, [rows, windowStart, windowEnd]);
|
|
10870
|
+
if (dias.length === 0) {
|
|
10871
|
+
return /* @__PURE__ */ jsx("div", { className: ganttListFrame(), children: /* @__PURE__ */ jsx("div", { className: ganttEmpty(), children: emptyState ?? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10872
|
+
/* @__PURE__ */ jsx(CalendarSearch, { className: "size-icon-xl text-fg-subtle", "aria-hidden": true }),
|
|
10873
|
+
/* @__PURE__ */ jsx("span", { className: ganttEmptyText(), children: "Nenhuma tarefa neste período." })
|
|
10874
|
+
] }) }) });
|
|
10875
|
+
}
|
|
10876
|
+
return /* @__PURE__ */ jsx("div", { className: ganttListFrame(), children: dias.map(({ dia, itens }) => {
|
|
10877
|
+
const hoje = isSameDay(dia, now);
|
|
10878
|
+
return /* @__PURE__ */ jsxs("div", { className: ganttListDay(), children: [
|
|
10879
|
+
/* @__PURE__ */ jsxs("div", { className: ganttListDayHead(), children: [
|
|
10880
|
+
/* @__PURE__ */ jsx("span", { className: ganttListDayNumber({ today: hoje }), children: format(dia, "d", { locale }) }),
|
|
10881
|
+
/* @__PURE__ */ jsx("span", { className: ganttListDayName(), children: format(dia, "EEEE, MMMM", { locale }) })
|
|
10882
|
+
] }),
|
|
10883
|
+
/* @__PURE__ */ jsx("div", { className: ganttListItems(), children: itens.map(({ bar, row, colorKey, diaDoIntervalo, totalDeDias }) => {
|
|
10884
|
+
const estilos = ganttListItem({
|
|
10885
|
+
colorKey,
|
|
10886
|
+
conflict: conflictBarIds.has(bar.id),
|
|
10887
|
+
critical: criticalBarIds.has(bar.id)
|
|
10888
|
+
});
|
|
10889
|
+
return /* @__PURE__ */ jsxs(
|
|
10890
|
+
"button",
|
|
10891
|
+
{
|
|
10892
|
+
type: "button",
|
|
10893
|
+
className: estilos.root(),
|
|
10894
|
+
onClick: (e3) => onBarClick?.(bar, row, e3),
|
|
10895
|
+
children: [
|
|
10896
|
+
/* @__PURE__ */ jsx("span", { className: estilos.dot(), "aria-hidden": true }),
|
|
10897
|
+
/* @__PURE__ */ jsxs("span", { className: estilos.body(), children: [
|
|
10898
|
+
/* @__PURE__ */ jsx("span", { className: estilos.title(), children: typeof row.label === "string" ? row.label : bar.id }),
|
|
10899
|
+
row.bars.length > 1 && bar.label ? /* @__PURE__ */ jsx("span", { className: estilos.sub(), children: bar.label }) : row.sublabel ? /* @__PURE__ */ jsx("span", { className: estilos.sub(), children: row.sublabel }) : null
|
|
10900
|
+
] }),
|
|
10901
|
+
/* @__PURE__ */ jsx("span", { className: estilos.meta(), children: totalDeDias === 1 ? "dia único" : `dia ${diaDoIntervalo} de ${totalDeDias}` })
|
|
10902
|
+
]
|
|
10903
|
+
},
|
|
10904
|
+
`${format(dia, "yyyy-MM-dd")}-${bar.id}`
|
|
10905
|
+
);
|
|
10906
|
+
}) })
|
|
10907
|
+
] }, format(dia, "yyyy-MM-dd"));
|
|
10908
|
+
}) });
|
|
10909
|
+
}
|
|
10910
|
+
function dragToDates(bar, deltaPx, pxPerDay, mode) {
|
|
10911
|
+
const inicio = startOfDay(bar.start);
|
|
10912
|
+
const fim = startOfDay(bar.end);
|
|
10913
|
+
if (!Number.isFinite(pxPerDay) || pxPerDay <= 0) {
|
|
10914
|
+
return { start: inicio, end: fim };
|
|
10915
|
+
}
|
|
10916
|
+
const dias = Math.round(deltaPx / pxPerDay);
|
|
10917
|
+
if (dias === 0) return { start: inicio, end: fim };
|
|
10918
|
+
if (mode === "move") {
|
|
10919
|
+
return { start: addDays(inicio, dias), end: addDays(fim, dias) };
|
|
10920
|
+
}
|
|
10921
|
+
if (mode === "resize-start") {
|
|
10922
|
+
const novo2 = addDays(inicio, dias);
|
|
10923
|
+
return {
|
|
10924
|
+
start: differenceInCalendarDays(novo2, fim) > 0 ? fim : novo2,
|
|
10925
|
+
end: fim
|
|
10926
|
+
};
|
|
10927
|
+
}
|
|
10928
|
+
const novo = addDays(fim, dias);
|
|
10929
|
+
return {
|
|
10930
|
+
start: inicio,
|
|
10931
|
+
// Mesmo clamp, pela mesma razão, na outra ponta.
|
|
10932
|
+
end: differenceInCalendarDays(novo, inicio) < 0 ? inicio : novo
|
|
10933
|
+
};
|
|
10934
|
+
}
|
|
10935
|
+
function linkTypeFromSides(de, para) {
|
|
10936
|
+
if (de === "end") return para === "start" ? "FS" : "FF";
|
|
10937
|
+
return para === "start" ? "SS" : "SF";
|
|
10938
|
+
}
|
|
10939
|
+
function sideFromPointer(x3, left, width) {
|
|
10940
|
+
if (width <= 0) return "start";
|
|
10941
|
+
return x3 < left + width / 2 ? "start" : "end";
|
|
10942
|
+
}
|
|
10943
|
+
const GANTT_BAR_MIN_WIDTH_PX = 6;
|
|
10944
|
+
const GanttBarView = forwardRef(
|
|
10945
|
+
function GanttBarView2({
|
|
10946
|
+
bar,
|
|
10947
|
+
left,
|
|
10948
|
+
width,
|
|
10949
|
+
lane,
|
|
10950
|
+
rowType,
|
|
10951
|
+
colorKey,
|
|
10952
|
+
continuesBefore,
|
|
10953
|
+
continuesAfter,
|
|
10954
|
+
height,
|
|
10955
|
+
conflict = false,
|
|
10956
|
+
critical = false,
|
|
10957
|
+
movable = false,
|
|
10958
|
+
resizable = false,
|
|
10959
|
+
linkable = false,
|
|
10960
|
+
dragging = false,
|
|
10961
|
+
onClick,
|
|
10962
|
+
onMoveStart,
|
|
10963
|
+
onResizeStart,
|
|
10964
|
+
onLinkStart,
|
|
10965
|
+
children,
|
|
10966
|
+
style
|
|
10967
|
+
}, ref) {
|
|
10968
|
+
if (rowType === "milestone") {
|
|
10969
|
+
return /* @__PURE__ */ jsx(
|
|
10970
|
+
"button",
|
|
10971
|
+
{
|
|
10972
|
+
type: "button",
|
|
10973
|
+
className: ganttMilestone({ colorKey }),
|
|
10974
|
+
style: { left: left - 7, width: 14, height: 14, ...style },
|
|
10975
|
+
onClick,
|
|
10976
|
+
"aria-label": bar.searchText ?? String(bar.id),
|
|
10977
|
+
children: /* @__PURE__ */ jsx("span", {})
|
|
10978
|
+
}
|
|
10979
|
+
);
|
|
10980
|
+
}
|
|
10981
|
+
const estilos = ganttBar({
|
|
10982
|
+
colorKey,
|
|
10983
|
+
type: rowType,
|
|
10984
|
+
continuesBefore,
|
|
10985
|
+
continuesAfter,
|
|
10986
|
+
conflict,
|
|
10987
|
+
critical,
|
|
10988
|
+
dragging,
|
|
10989
|
+
movable
|
|
10990
|
+
});
|
|
10991
|
+
const larguraFinal = Math.max(GANTT_BAR_MIN_WIDTH_PX, width);
|
|
10992
|
+
return /* @__PURE__ */ jsxs(
|
|
10993
|
+
"div",
|
|
10994
|
+
{
|
|
10995
|
+
ref,
|
|
10996
|
+
role: "button",
|
|
10997
|
+
tabIndex: 0,
|
|
10998
|
+
"aria-label": bar.searchText ?? void 0,
|
|
10999
|
+
"data-gantt-bar": bar.id,
|
|
11000
|
+
className: estilos.root(),
|
|
11001
|
+
style: {
|
|
11002
|
+
left,
|
|
11003
|
+
width: larguraFinal,
|
|
11004
|
+
height,
|
|
11005
|
+
...style
|
|
11006
|
+
},
|
|
11007
|
+
onPointerDown: (e3) => onMoveStart?.(e3),
|
|
11008
|
+
onClick,
|
|
11009
|
+
onKeyDown: (e3) => {
|
|
11010
|
+
if (e3.key === "Enter" || e3.key === " ") {
|
|
11011
|
+
e3.preventDefault();
|
|
11012
|
+
onClick?.(e3);
|
|
11013
|
+
}
|
|
11014
|
+
},
|
|
11015
|
+
children: [
|
|
11016
|
+
bar.progress !== void 0 ? /* @__PURE__ */ jsx(
|
|
11017
|
+
"span",
|
|
11018
|
+
{
|
|
11019
|
+
className: estilos.progress(),
|
|
11020
|
+
style: { width: `${Math.min(100, Math.max(0, bar.progress))}%` },
|
|
11021
|
+
"aria-hidden": true
|
|
11022
|
+
}
|
|
11023
|
+
) : null,
|
|
11024
|
+
/* @__PURE__ */ jsx("span", { className: estilos.accent(), "aria-hidden": true }),
|
|
11025
|
+
children ?? (bar.label ? /* @__PURE__ */ jsx("span", { className: estilos.label(), children: bar.label }) : null),
|
|
11026
|
+
resizable ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
11027
|
+
/* @__PURE__ */ jsx(
|
|
11028
|
+
"span",
|
|
11029
|
+
{
|
|
11030
|
+
role: "separator",
|
|
11031
|
+
"aria-label": "Mudar início",
|
|
11032
|
+
tabIndex: -1,
|
|
11033
|
+
className: ganttResizeHandle({ side: "start" }),
|
|
11034
|
+
onPointerDown: (e3) => {
|
|
11035
|
+
e3.stopPropagation();
|
|
11036
|
+
onResizeStart?.("start", e3);
|
|
11037
|
+
}
|
|
11038
|
+
}
|
|
11039
|
+
),
|
|
11040
|
+
/* @__PURE__ */ jsx(
|
|
11041
|
+
"span",
|
|
11042
|
+
{
|
|
11043
|
+
role: "separator",
|
|
11044
|
+
"aria-label": "Mudar fim",
|
|
11045
|
+
tabIndex: -1,
|
|
11046
|
+
className: ganttResizeHandle({ side: "end" }),
|
|
11047
|
+
onPointerDown: (e3) => {
|
|
11048
|
+
e3.stopPropagation();
|
|
11049
|
+
onResizeStart?.("end", e3);
|
|
11050
|
+
}
|
|
11051
|
+
}
|
|
11052
|
+
)
|
|
11053
|
+
] }) : null,
|
|
11054
|
+
linkable ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
11055
|
+
/* @__PURE__ */ jsx(
|
|
11056
|
+
"span",
|
|
11057
|
+
{
|
|
11058
|
+
role: "button",
|
|
11059
|
+
"aria-label": "Criar vínculo a partir do início",
|
|
11060
|
+
tabIndex: -1,
|
|
11061
|
+
className: ganttLinkPort({ side: "start" }),
|
|
11062
|
+
onPointerDown: (e3) => {
|
|
11063
|
+
e3.stopPropagation();
|
|
11064
|
+
onLinkStart?.("start", e3);
|
|
11065
|
+
}
|
|
11066
|
+
}
|
|
11067
|
+
),
|
|
11068
|
+
/* @__PURE__ */ jsx(
|
|
11069
|
+
"span",
|
|
11070
|
+
{
|
|
11071
|
+
role: "button",
|
|
11072
|
+
"aria-label": "Criar vínculo a partir do fim",
|
|
11073
|
+
tabIndex: -1,
|
|
11074
|
+
className: ganttLinkPort({ side: "end" }),
|
|
11075
|
+
onPointerDown: (e3) => {
|
|
11076
|
+
e3.stopPropagation();
|
|
11077
|
+
onLinkStart?.("end", e3);
|
|
11078
|
+
}
|
|
11079
|
+
}
|
|
11080
|
+
)
|
|
11081
|
+
] }) : null
|
|
11082
|
+
]
|
|
11083
|
+
}
|
|
11084
|
+
);
|
|
11085
|
+
}
|
|
11086
|
+
);
|
|
11087
|
+
function constraintDates(tipo, source, target, lag = 0) {
|
|
11088
|
+
const de = tipo === "FS" || tipo === "FF" ? source.end : source.start;
|
|
11089
|
+
const pontaAlvo = tipo === "FS" || tipo === "SS" ? target.start : target.end;
|
|
11090
|
+
return { limite: addDays(startOfDay(de), lag), pontaAlvo: startOfDay(pontaAlvo) };
|
|
11091
|
+
}
|
|
11092
|
+
function checkLink(link2, bars) {
|
|
11093
|
+
const source = bars.get(link2.source);
|
|
11094
|
+
const target = bars.get(link2.target);
|
|
11095
|
+
if (!source || !target) return null;
|
|
11096
|
+
const { limite, pontaAlvo } = constraintDates(
|
|
11097
|
+
link2.type ?? "FS",
|
|
11098
|
+
source,
|
|
11099
|
+
target,
|
|
11100
|
+
link2.lag ?? 0
|
|
11101
|
+
);
|
|
11102
|
+
const deficitDays = differenceInCalendarDays(limite, pontaAlvo);
|
|
11103
|
+
if (deficitDays <= 0) return null;
|
|
11104
|
+
return { link: link2, deficitDays };
|
|
11105
|
+
}
|
|
11106
|
+
function checkAllLinks(links, bars) {
|
|
11107
|
+
const saida = [];
|
|
11108
|
+
for (const l2 of links) {
|
|
11109
|
+
const v3 = checkLink(l2, bars);
|
|
11110
|
+
if (v3) saida.push(v3);
|
|
11111
|
+
}
|
|
11112
|
+
return saida;
|
|
11113
|
+
}
|
|
11114
|
+
function topoSort(barIds, links) {
|
|
11115
|
+
const existe = new Set(barIds);
|
|
11116
|
+
const grauEntrada = /* @__PURE__ */ new Map();
|
|
11117
|
+
const saintes = /* @__PURE__ */ new Map();
|
|
11118
|
+
for (const id of barIds) {
|
|
11119
|
+
grauEntrada.set(id, 0);
|
|
11120
|
+
saintes.set(id, []);
|
|
11121
|
+
}
|
|
11122
|
+
for (const l2 of links) {
|
|
11123
|
+
if (!existe.has(l2.source) || !existe.has(l2.target)) continue;
|
|
11124
|
+
saintes.get(l2.source).push(l2.target);
|
|
11125
|
+
grauEntrada.set(l2.target, (grauEntrada.get(l2.target) ?? 0) + 1);
|
|
11126
|
+
}
|
|
11127
|
+
const fila = barIds.filter((id) => (grauEntrada.get(id) ?? 0) === 0);
|
|
11128
|
+
const order = [];
|
|
11129
|
+
while (fila.length > 0) {
|
|
11130
|
+
const atual = fila.shift();
|
|
11131
|
+
order.push(atual);
|
|
11132
|
+
for (const proximo of saintes.get(atual) ?? []) {
|
|
11133
|
+
const g3 = (grauEntrada.get(proximo) ?? 0) - 1;
|
|
11134
|
+
grauEntrada.set(proximo, g3);
|
|
11135
|
+
if (g3 === 0) fila.push(proximo);
|
|
11136
|
+
}
|
|
11137
|
+
}
|
|
11138
|
+
if (order.length !== barIds.length) {
|
|
11139
|
+
return { ok: false, cycle: barIds.filter((id) => !order.includes(id)) };
|
|
11140
|
+
}
|
|
11141
|
+
return { ok: true, order };
|
|
11142
|
+
}
|
|
11143
|
+
function computeCriticalPath(bars, links) {
|
|
11144
|
+
const ids = bars.map((b4) => b4.id);
|
|
11145
|
+
const topo = topoSort(ids, links);
|
|
11146
|
+
if (!topo.ok) return { cycle: topo.cycle };
|
|
11147
|
+
const porId = new Map(bars.map((b4) => [b4.id, b4]));
|
|
11148
|
+
const fsEntrando = /* @__PURE__ */ new Map();
|
|
11149
|
+
const fsSaindo = /* @__PURE__ */ new Map();
|
|
11150
|
+
for (const l2 of links) {
|
|
11151
|
+
if ((l2.type ?? "FS") !== "FS") continue;
|
|
11152
|
+
if (!porId.has(l2.source) || !porId.has(l2.target)) continue;
|
|
11153
|
+
empurrar(fsEntrando, l2.target, l2);
|
|
11154
|
+
empurrar(fsSaindo, l2.source, l2);
|
|
11155
|
+
}
|
|
11156
|
+
const duracao = (id) => {
|
|
11157
|
+
const b4 = porId.get(id);
|
|
11158
|
+
return Math.max(0, differenceInCalendarDays(startOfDay(b4.end), startOfDay(b4.start)));
|
|
11159
|
+
};
|
|
11160
|
+
const base = startOfDay(
|
|
11161
|
+
bars.reduce((min2, b4) => b4.start < min2 ? b4.start : min2, bars[0]?.start ?? /* @__PURE__ */ new Date())
|
|
11162
|
+
);
|
|
11163
|
+
const inicioRelativo = (id) => differenceInCalendarDays(startOfDay(porId.get(id).start), base);
|
|
11164
|
+
const early = /* @__PURE__ */ new Map();
|
|
11165
|
+
for (const id of topo.order) {
|
|
11166
|
+
const entradas = fsEntrando.get(id) ?? [];
|
|
11167
|
+
const porVinculo = entradas.map(
|
|
11168
|
+
(l2) => (early.get(l2.source) ?? 0) + duracao(l2.source) + (l2.lag ?? 0)
|
|
11169
|
+
);
|
|
11170
|
+
early.set(id, Math.max(inicioRelativo(id), ...porVinculo));
|
|
11171
|
+
}
|
|
11172
|
+
const fimProjeto = Math.max(
|
|
11173
|
+
0,
|
|
11174
|
+
...topo.order.map((id) => (early.get(id) ?? 0) + duracao(id))
|
|
11175
|
+
);
|
|
11176
|
+
const late = /* @__PURE__ */ new Map();
|
|
11177
|
+
for (const id of [...topo.order].reverse()) {
|
|
11178
|
+
const saidas = fsSaindo.get(id) ?? [];
|
|
11179
|
+
if (saidas.length === 0) {
|
|
11180
|
+
late.set(id, fimProjeto - duracao(id));
|
|
11181
|
+
} else {
|
|
11182
|
+
late.set(
|
|
11183
|
+
id,
|
|
11184
|
+
Math.min(
|
|
11185
|
+
...saidas.map((l2) => (late.get(l2.target) ?? fimProjeto) - duracao(id) - (l2.lag ?? 0))
|
|
11186
|
+
)
|
|
11187
|
+
);
|
|
11188
|
+
}
|
|
11189
|
+
}
|
|
11190
|
+
const critical = /* @__PURE__ */ new Set();
|
|
11191
|
+
for (const id of ids) {
|
|
11192
|
+
const folga = (late.get(id) ?? 0) - (early.get(id) ?? 0);
|
|
11193
|
+
if (folga <= 0) critical.add(id);
|
|
11194
|
+
}
|
|
11195
|
+
return { critical };
|
|
11196
|
+
}
|
|
11197
|
+
function empurrar(map, chave, valor) {
|
|
11198
|
+
const lista = map.get(chave);
|
|
11199
|
+
if (lista) lista.push(valor);
|
|
11200
|
+
else map.set(chave, [valor]);
|
|
11201
|
+
}
|
|
11202
|
+
function linkPath(de, para, opts) {
|
|
11203
|
+
const recuo = opts.recuo ?? 12;
|
|
11204
|
+
const x12 = de.x + (opts.saiDireita ? recuo : -recuo);
|
|
11205
|
+
const x22 = para.x + (opts.entraEsquerda ? -recuo : recuo);
|
|
11206
|
+
const meioY = de.y === para.y ? de.y : (de.y + para.y) / 2;
|
|
11207
|
+
return [
|
|
11208
|
+
`M ${de.x} ${de.y}`,
|
|
11209
|
+
`L ${x12} ${de.y}`,
|
|
11210
|
+
`L ${x12} ${meioY}`,
|
|
11211
|
+
`L ${x22} ${meioY}`,
|
|
11212
|
+
`L ${x22} ${para.y}`,
|
|
11213
|
+
`L ${para.x} ${para.y}`
|
|
11214
|
+
].join(" ");
|
|
11215
|
+
}
|
|
11216
|
+
function linkSides(tipo) {
|
|
11217
|
+
return {
|
|
11218
|
+
saiDireita: tipo === "FS" || tipo === "FF",
|
|
11219
|
+
entraEsquerda: tipo === "FS" || tipo === "SS"
|
|
11220
|
+
};
|
|
11221
|
+
}
|
|
11222
|
+
function GanttLinksLayer({
|
|
11223
|
+
links,
|
|
11224
|
+
width,
|
|
11225
|
+
height,
|
|
11226
|
+
onLinkClick
|
|
11227
|
+
}) {
|
|
11228
|
+
const uid = useId().replace(/:/g, "");
|
|
11229
|
+
const seta = `gantt-arrow-${uid}`;
|
|
11230
|
+
const setaConflito = `gantt-arrow-conflict-${uid}`;
|
|
11231
|
+
if (links.length === 0) return null;
|
|
11232
|
+
return /* @__PURE__ */ jsxs(
|
|
11233
|
+
"svg",
|
|
11234
|
+
{
|
|
11235
|
+
className: ganttLinksLayer(),
|
|
11236
|
+
width,
|
|
11237
|
+
height,
|
|
11238
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
11239
|
+
"aria-hidden": true,
|
|
11240
|
+
focusable: "false",
|
|
11241
|
+
children: [
|
|
11242
|
+
/* @__PURE__ */ jsxs("defs", { children: [
|
|
11243
|
+
/* @__PURE__ */ jsx(
|
|
11244
|
+
"marker",
|
|
11245
|
+
{
|
|
11246
|
+
id: seta,
|
|
11247
|
+
viewBox: "0 0 8 8",
|
|
11248
|
+
refX: "6",
|
|
11249
|
+
refY: "4",
|
|
11250
|
+
markerWidth: "6",
|
|
11251
|
+
markerHeight: "6",
|
|
11252
|
+
orient: "auto-start-reverse",
|
|
11253
|
+
children: /* @__PURE__ */ jsx("path", { d: "M 0 1 L 6 4 L 0 7 z", fill: "context-stroke" })
|
|
11254
|
+
}
|
|
11255
|
+
),
|
|
11256
|
+
/* @__PURE__ */ jsx(
|
|
11257
|
+
"marker",
|
|
11258
|
+
{
|
|
11259
|
+
id: setaConflito,
|
|
11260
|
+
viewBox: "0 0 8 8",
|
|
11261
|
+
refX: "6",
|
|
11262
|
+
refY: "4",
|
|
11263
|
+
markerWidth: "6",
|
|
11264
|
+
markerHeight: "6",
|
|
11265
|
+
orient: "auto-start-reverse",
|
|
11266
|
+
children: /* @__PURE__ */ jsx("path", { d: "M 0 1 L 6 4 L 0 7 z", fill: "context-stroke" })
|
|
11267
|
+
}
|
|
11268
|
+
)
|
|
11269
|
+
] }),
|
|
11270
|
+
links.map((g3) => {
|
|
11271
|
+
const tipo = g3.link.type ?? "FS";
|
|
11272
|
+
const lados = linkSides(tipo);
|
|
11273
|
+
const d3 = linkPath(g3.from, g3.to, lados);
|
|
11274
|
+
const state = g3.conflict ? "conflict" : g3.critical ? "critical" : "default";
|
|
11275
|
+
return /* @__PURE__ */ jsx(
|
|
11276
|
+
"path",
|
|
11277
|
+
{
|
|
11278
|
+
d: d3,
|
|
11279
|
+
className: ganttLinkPath({ state }),
|
|
11280
|
+
markerEnd: `url(#${g3.conflict ? setaConflito : seta})`,
|
|
11281
|
+
onClick: () => onLinkClick?.(g3.link),
|
|
11282
|
+
children: /* @__PURE__ */ jsxs("title", { children: [
|
|
11283
|
+
`${tipo}${g3.link.lag ? ` ${g3.link.lag > 0 ? "+" : ""}${g3.link.lag}d` : ""}`,
|
|
11284
|
+
g3.conflict ? " — conflito" : ""
|
|
11285
|
+
] })
|
|
11286
|
+
},
|
|
11287
|
+
g3.link.id
|
|
11288
|
+
);
|
|
11289
|
+
})
|
|
11290
|
+
]
|
|
11291
|
+
}
|
|
11292
|
+
);
|
|
11293
|
+
}
|
|
11294
|
+
function GanttTimelineView({
|
|
11295
|
+
rows,
|
|
11296
|
+
allRows,
|
|
11297
|
+
links,
|
|
11298
|
+
windowStart,
|
|
11299
|
+
windowEnd,
|
|
11300
|
+
granularity,
|
|
11301
|
+
pxPerDay,
|
|
11302
|
+
heights,
|
|
11303
|
+
offsets,
|
|
11304
|
+
now,
|
|
11305
|
+
locale,
|
|
11306
|
+
weekStartsOn,
|
|
11307
|
+
conflictBarIds,
|
|
11308
|
+
criticalBarIds,
|
|
11309
|
+
draggable,
|
|
11310
|
+
resizable,
|
|
11311
|
+
linkable,
|
|
11312
|
+
hoveredRow,
|
|
11313
|
+
onHoverRow,
|
|
11314
|
+
hoveredDay,
|
|
11315
|
+
onHoverDay,
|
|
11316
|
+
selectedRow,
|
|
11317
|
+
selectedDay,
|
|
11318
|
+
onSelectDay,
|
|
11319
|
+
onBarMove,
|
|
11320
|
+
onBarResize,
|
|
11321
|
+
onLinkCreate,
|
|
11322
|
+
onScroll,
|
|
11323
|
+
scrollRef,
|
|
11324
|
+
onBarClick,
|
|
11325
|
+
onLinkClick
|
|
11326
|
+
}) {
|
|
11327
|
+
const [gesto, setGesto] = useState(null);
|
|
11328
|
+
const [deltaPx, setDeltaPx] = useState(0);
|
|
11329
|
+
const deltaRef = useRef(0);
|
|
11330
|
+
const [vinculo, setVinculo] = useState(null);
|
|
11331
|
+
const canvasRef = useRef(null);
|
|
11332
|
+
const axis = useMemo(
|
|
11333
|
+
() => buildTimeAxis(windowStart, windowEnd, granularity, locale, weekStartsOn),
|
|
11334
|
+
[windowStart, windowEnd, granularity, locale, weekStartsOn]
|
|
11335
|
+
);
|
|
11336
|
+
const larguraTotal = axis.totalDays * pxPerDay;
|
|
11337
|
+
const alturas = heights;
|
|
11338
|
+
const tops = offsets;
|
|
11339
|
+
const alturaTotal = alturas.reduce((s2, h3) => s2 + h3, 0);
|
|
11340
|
+
const geos = useMemo(() => {
|
|
11341
|
+
const mapa = /* @__PURE__ */ new Map();
|
|
11342
|
+
const lista = [];
|
|
11343
|
+
rows.forEach(({ row }, i4) => {
|
|
11344
|
+
const tipo = row.type ?? "task";
|
|
11345
|
+
const barras = tipo === "summary" && row.bars.length === 0 ? (() => {
|
|
11346
|
+
const r2 = deriveSummaryRange(row.id, allRows);
|
|
11347
|
+
return r2 ? [{ id: `${row.id}__summary`, start: r2.start, end: r2.end }] : [];
|
|
11348
|
+
})() : row.bars;
|
|
11349
|
+
const comPrevia = barras.map(
|
|
11350
|
+
(b4) => gesto?.barId === b4.id ? { ...b4, ...dragToDates(b4, deltaPx, pxPerDay, gesto.mode) } : b4
|
|
11351
|
+
);
|
|
11352
|
+
const recortadas = comPrevia.map((b4) => clipToWindow(b4, windowStart, windowEnd)).filter((x3) => x3 !== null);
|
|
11353
|
+
const lanes = packLanes(recortadas, row.lanePacking ?? "stack");
|
|
11354
|
+
lanes.forEach((lane, laneIndex) => {
|
|
11355
|
+
for (const c3 of lane) {
|
|
11356
|
+
const x12 = dateToX(c3.start, windowStart, pxPerDay);
|
|
11357
|
+
const x22 = dateToX(c3.end, windowStart, pxPerDay);
|
|
11358
|
+
const geo = {
|
|
11359
|
+
bar: c3.bar,
|
|
11360
|
+
row,
|
|
11361
|
+
left: x12,
|
|
11362
|
+
width: Math.max(0, x22 - x12),
|
|
11363
|
+
lane: laneIndex,
|
|
11364
|
+
top: tops[i4] ?? 0,
|
|
11365
|
+
continuesBefore: c3.continuesBefore,
|
|
11366
|
+
continuesAfter: c3.continuesAfter,
|
|
11367
|
+
colorKey: c3.bar.colorKey ?? "chart-1"
|
|
11368
|
+
};
|
|
11369
|
+
mapa.set(c3.bar.id, geo);
|
|
11370
|
+
lista.push(geo);
|
|
11371
|
+
}
|
|
11372
|
+
});
|
|
11373
|
+
});
|
|
11374
|
+
return { mapa, lista };
|
|
11375
|
+
}, [rows, allRows, windowStart, windowEnd, pxPerDay, tops, gesto, deltaPx]);
|
|
11376
|
+
const topDaBarra = (g3, tipo) => {
|
|
11377
|
+
const altura = tipo === "summary" ? GANTT_SUMMARY_BAR_HEIGHT_PX : GANTT_BAR_HEIGHT_PX;
|
|
11378
|
+
const alturaDaFaixa = alturas[rows.findIndex((r2) => r2.row.id === g3.row.id)] ?? GANTT_ROW_HEIGHT_PX;
|
|
11379
|
+
const umaLaneSo = (g3.row.lanePacking ?? "stack") === "stack" && g3.row.bars.length <= 1;
|
|
11380
|
+
const faixa = umaLaneSo ? alturaDaFaixa : GANTT_LANE_HEIGHT_PX;
|
|
11381
|
+
const offsetLane = umaLaneSo ? 0 : g3.lane * GANTT_LANE_HEIGHT_PX;
|
|
11382
|
+
return g3.top + offsetLane + Math.max(0, (faixa - altura) / 2);
|
|
11383
|
+
};
|
|
11384
|
+
const linkGeos = useMemo(() => {
|
|
11385
|
+
const saida = [];
|
|
11386
|
+
for (const link2 of links) {
|
|
11387
|
+
const de = geos.mapa.get(link2.source);
|
|
11388
|
+
const para = geos.mapa.get(link2.target);
|
|
11389
|
+
if (!de || !para) continue;
|
|
11390
|
+
const tipo = link2.type ?? "FS";
|
|
11391
|
+
const saiDireita = tipo === "FS" || tipo === "FF";
|
|
11392
|
+
const entraEsquerda = tipo === "FS" || tipo === "SS";
|
|
11393
|
+
const alturaDe = (de.row.type ?? "task") === "summary" ? GANTT_SUMMARY_BAR_HEIGHT_PX : GANTT_BAR_HEIGHT_PX;
|
|
11394
|
+
const alturaPara = (para.row.type ?? "task") === "summary" ? GANTT_SUMMARY_BAR_HEIGHT_PX : GANTT_BAR_HEIGHT_PX;
|
|
11395
|
+
const yDe = topDaBarra(de, de.row.type ?? "task") + alturaDe / 2;
|
|
11396
|
+
const yPara = topDaBarra(para, para.row.type ?? "task") + alturaPara / 2;
|
|
11397
|
+
saida.push({
|
|
11398
|
+
link: link2,
|
|
11399
|
+
from: { x: saiDireita ? de.left + de.width : de.left, y: yDe },
|
|
11400
|
+
to: { x: entraEsquerda ? para.left : para.left + para.width, y: yPara },
|
|
11401
|
+
conflict: conflictBarIds.has(link2.target),
|
|
11402
|
+
critical: criticalBarIds.has(link2.source) && criticalBarIds.has(link2.target)
|
|
11403
|
+
});
|
|
11404
|
+
}
|
|
11405
|
+
return saida;
|
|
11406
|
+
}, [links, geos, conflictBarIds, criticalBarIds, alturas, rows]);
|
|
11407
|
+
const iniciarGesto = (barId, mode, evt) => {
|
|
11408
|
+
if (evt.button !== 0) return;
|
|
11409
|
+
if (mode === "move" ? !draggable : !resizable) return;
|
|
11410
|
+
deltaRef.current = 0;
|
|
11411
|
+
setDeltaPx(0);
|
|
11412
|
+
setGesto({ barId, mode, xInicial: evt.clientX });
|
|
11413
|
+
};
|
|
11414
|
+
useEffect(() => {
|
|
11415
|
+
if (!gesto) return;
|
|
11416
|
+
const mover = (e3) => {
|
|
11417
|
+
const dx = e3.clientX - gesto.xInicial;
|
|
11418
|
+
deltaRef.current = dx;
|
|
11419
|
+
setDeltaPx(dx);
|
|
11420
|
+
};
|
|
11421
|
+
const soltar = () => {
|
|
11422
|
+
const geo = geos.mapa.get(gesto.barId);
|
|
11423
|
+
const dx = deltaRef.current;
|
|
11424
|
+
setGesto(null);
|
|
11425
|
+
setDeltaPx(0);
|
|
11426
|
+
deltaRef.current = 0;
|
|
11427
|
+
if (!geo) return;
|
|
11428
|
+
const destino = dragToDates(geo.bar, dx, pxPerDay, gesto.mode);
|
|
11429
|
+
if (destino.start.getTime() === startOfDay(geo.bar.start).getTime() && destino.end.getTime() === startOfDay(geo.bar.end).getTime()) {
|
|
11430
|
+
return;
|
|
11431
|
+
}
|
|
11432
|
+
const change = {
|
|
11433
|
+
bar: geo.bar,
|
|
11434
|
+
row: geo.row,
|
|
11435
|
+
start: destino.start,
|
|
11436
|
+
end: destino.end
|
|
11437
|
+
};
|
|
11438
|
+
if (gesto.mode === "move") onBarMove?.(change);
|
|
11439
|
+
else onBarResize?.(change);
|
|
11440
|
+
};
|
|
11441
|
+
window.addEventListener("pointermove", mover);
|
|
11442
|
+
window.addEventListener("pointerup", soltar);
|
|
11443
|
+
window.addEventListener("pointercancel", soltar);
|
|
11444
|
+
return () => {
|
|
11445
|
+
window.removeEventListener("pointermove", mover);
|
|
11446
|
+
window.removeEventListener("pointerup", soltar);
|
|
11447
|
+
window.removeEventListener("pointercancel", soltar);
|
|
11448
|
+
};
|
|
11449
|
+
}, [gesto]);
|
|
11450
|
+
const iniciarVinculo = (barId, side, evt) => {
|
|
11451
|
+
if (evt.button !== 0) return;
|
|
11452
|
+
if (!linkable) return;
|
|
11453
|
+
const caixa = canvasRef.current?.getBoundingClientRect();
|
|
11454
|
+
if (!caixa) return;
|
|
11455
|
+
setVinculo({
|
|
11456
|
+
barId,
|
|
11457
|
+
side,
|
|
11458
|
+
x: evt.clientX - caixa.left,
|
|
11459
|
+
y: evt.clientY - caixa.top
|
|
11460
|
+
});
|
|
11461
|
+
};
|
|
11462
|
+
useEffect(() => {
|
|
11463
|
+
if (!vinculo) return;
|
|
11464
|
+
const mover = (e3) => {
|
|
11465
|
+
const caixa = canvasRef.current?.getBoundingClientRect();
|
|
11466
|
+
if (!caixa) return;
|
|
11467
|
+
setVinculo(
|
|
11468
|
+
(v3) => v3 ? { ...v3, x: e3.clientX - caixa.left, y: e3.clientY - caixa.top } : v3
|
|
11469
|
+
);
|
|
11470
|
+
};
|
|
11471
|
+
const soltar = (e3) => {
|
|
11472
|
+
const origem = vinculo;
|
|
11473
|
+
setVinculo(null);
|
|
11474
|
+
const sob = document.elementFromPoint(e3.clientX, e3.clientY);
|
|
11475
|
+
const alvo = sob?.closest("[data-gantt-bar]");
|
|
11476
|
+
const alvoId = alvo?.getAttribute("data-gantt-bar");
|
|
11477
|
+
if (!alvoId) return;
|
|
11478
|
+
if (alvoId === origem.barId) return;
|
|
11479
|
+
const geoAlvo = geos.mapa.get(alvoId);
|
|
11480
|
+
const caixa = canvasRef.current?.getBoundingClientRect();
|
|
11481
|
+
if (!geoAlvo || !caixa) return;
|
|
11482
|
+
const ladoAlvo = sideFromPointer(
|
|
11483
|
+
e3.clientX - caixa.left,
|
|
11484
|
+
geoAlvo.left,
|
|
11485
|
+
geoAlvo.width
|
|
11486
|
+
);
|
|
11487
|
+
onLinkCreate?.({
|
|
11488
|
+
source: origem.barId,
|
|
11489
|
+
target: alvoId,
|
|
11490
|
+
type: linkTypeFromSides(origem.side, ladoAlvo)
|
|
11491
|
+
});
|
|
11492
|
+
};
|
|
11493
|
+
window.addEventListener("pointermove", mover);
|
|
11494
|
+
window.addEventListener("pointerup", soltar);
|
|
11495
|
+
window.addEventListener("pointercancel", () => setVinculo(null));
|
|
11496
|
+
return () => {
|
|
11497
|
+
window.removeEventListener("pointermove", mover);
|
|
11498
|
+
window.removeEventListener("pointerup", soltar);
|
|
11499
|
+
};
|
|
11500
|
+
}, [vinculo?.barId, vinculo?.side]);
|
|
11501
|
+
const diaDaUnidade = (data) => Math.round(dateToX(data, windowStart, pxPerDay) / pxPerDay);
|
|
11502
|
+
const limitesDeGrupo = useMemo(() => {
|
|
11503
|
+
const saida = /* @__PURE__ */ new Set();
|
|
11504
|
+
let acumulado = 0;
|
|
11505
|
+
for (const g3 of axis.groups) {
|
|
11506
|
+
acumulado += g3.spanDays;
|
|
11507
|
+
if (acumulado < axis.totalDays) saida.add(acumulado);
|
|
11508
|
+
}
|
|
11509
|
+
return saida;
|
|
11510
|
+
}, [axis]);
|
|
11511
|
+
const vinculoFantasma = useMemo(() => {
|
|
11512
|
+
if (!vinculo) return null;
|
|
11513
|
+
const de = geos.mapa.get(vinculo.barId);
|
|
11514
|
+
if (!de) return null;
|
|
11515
|
+
const tipo = de.row.type ?? "task";
|
|
11516
|
+
const altura = tipo === "summary" ? GANTT_SUMMARY_BAR_HEIGHT_PX : GANTT_BAR_HEIGHT_PX;
|
|
11517
|
+
return {
|
|
11518
|
+
x1: vinculo.side === "end" ? de.left + de.width : de.left,
|
|
11519
|
+
y1: topDaBarra(de, tipo) + altura / 2,
|
|
11520
|
+
x2: vinculo.x,
|
|
11521
|
+
y2: vinculo.y
|
|
11522
|
+
};
|
|
11523
|
+
}, [vinculo, geos]);
|
|
11524
|
+
const nowX = dateToX(now, windowStart, pxPerDay);
|
|
11525
|
+
const nowVisivel = nowX >= 0 && nowX <= larguraTotal;
|
|
11526
|
+
return /* @__PURE__ */ jsx("div", { className: ganttCanvasScroll(), ref: scrollRef, onScroll, children: /* @__PURE__ */ jsxs("div", { style: { width: larguraTotal, minWidth: "100%" }, children: [
|
|
11527
|
+
/* @__PURE__ */ jsxs("div", { className: ganttHead(), style: { height: GANTT_HEAD_HEIGHT_PX }, children: [
|
|
11528
|
+
/* @__PURE__ */ jsx(
|
|
11529
|
+
"div",
|
|
11530
|
+
{
|
|
11531
|
+
className: ganttHeadRow({ level: "group" }),
|
|
11532
|
+
style: { height: GANTT_HEAD_GROUP_PX },
|
|
11533
|
+
children: axis.groups.map((g3) => {
|
|
11534
|
+
const largura = g3.spanDays * pxPerDay;
|
|
11535
|
+
const cabe = largura >= GANTT_GROUP_LABEL_MIN_PX;
|
|
11536
|
+
return /* @__PURE__ */ jsx(
|
|
11537
|
+
"div",
|
|
11538
|
+
{
|
|
11539
|
+
className: ganttHeadCell({
|
|
11540
|
+
level: "group",
|
|
11541
|
+
boundary: limitesDeGrupo.has(
|
|
11542
|
+
diaDaUnidade(g3.date) + g3.spanDays
|
|
11543
|
+
)
|
|
11544
|
+
}),
|
|
11545
|
+
style: {
|
|
11546
|
+
left: dateToX(g3.date, windowStart, pxPerDay),
|
|
11547
|
+
width: largura
|
|
11548
|
+
},
|
|
11549
|
+
title: cabe ? void 0 : String(g3.label),
|
|
11550
|
+
children: cabe ? g3.label : null
|
|
11551
|
+
},
|
|
11552
|
+
`g-${g3.date.getTime()}`
|
|
11553
|
+
);
|
|
11554
|
+
})
|
|
11555
|
+
}
|
|
11556
|
+
),
|
|
11557
|
+
/* @__PURE__ */ jsx(
|
|
11558
|
+
"div",
|
|
11559
|
+
{
|
|
11560
|
+
className: ganttHeadRow({ level: "unit" }),
|
|
11561
|
+
style: { height: GANTT_HEAD_UNIT_PX },
|
|
11562
|
+
children: axis.units.map((u3) => {
|
|
11563
|
+
const hoje = isSameDay(u3.date, now);
|
|
11564
|
+
const dia = diaDaUnidade(u3.date);
|
|
11565
|
+
return /* @__PURE__ */ jsx(
|
|
11566
|
+
"div",
|
|
11567
|
+
{
|
|
11568
|
+
className: ganttHeadCell({
|
|
11569
|
+
level: "unit",
|
|
11570
|
+
weekend: u3.isWeekend,
|
|
11571
|
+
today: hoje,
|
|
11572
|
+
selected: selectedDay === dia,
|
|
11573
|
+
boundary: limitesDeGrupo.has(dia + u3.spanDays)
|
|
11574
|
+
}),
|
|
11575
|
+
style: {
|
|
11576
|
+
left: dateToX(u3.date, windowStart, pxPerDay),
|
|
11577
|
+
width: u3.spanDays * pxPerDay,
|
|
11578
|
+
cursor: "pointer"
|
|
11579
|
+
},
|
|
11580
|
+
role: "columnheader",
|
|
11581
|
+
"aria-selected": selectedDay === dia,
|
|
11582
|
+
onClick: () => (
|
|
11583
|
+
// Clicar na coluna já selecionada DESSELECIONA — mesma
|
|
11584
|
+
// regra da linha, senão não há volta ao estado neutro.
|
|
11585
|
+
onSelectDay(selectedDay === dia ? null : dia)
|
|
11586
|
+
),
|
|
11587
|
+
children: granularity === "day" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
11588
|
+
/* @__PURE__ */ jsx("span", { className: ganttHeadWeekday({ weekend: u3.isWeekend }), children: format(u3.date, "EEEEEE", { locale }) }),
|
|
11589
|
+
/* @__PURE__ */ jsx("span", { className: ganttHeadDayNumber({ today: hoje }), children: u3.label })
|
|
11590
|
+
] }) : /* @__PURE__ */ jsx("span", { className: ganttHeadDayNumber({ today: hoje }), children: u3.label })
|
|
11591
|
+
},
|
|
11592
|
+
`u-${u3.date.getTime()}`
|
|
11593
|
+
);
|
|
11594
|
+
})
|
|
11595
|
+
}
|
|
11596
|
+
)
|
|
11597
|
+
] }),
|
|
11598
|
+
/* @__PURE__ */ jsxs(
|
|
11599
|
+
"div",
|
|
11600
|
+
{
|
|
11601
|
+
ref: canvasRef,
|
|
11602
|
+
className: ganttCanvas(),
|
|
11603
|
+
style: { width: larguraTotal, height: Math.max(alturaTotal, 1) },
|
|
11604
|
+
onMouseMove: (e3) => {
|
|
11605
|
+
const x3 = e3.clientX - e3.currentTarget.getBoundingClientRect().left;
|
|
11606
|
+
const dia = Math.floor(x3 / pxPerDay);
|
|
11607
|
+
if (dia !== hoveredDay) onHoverDay(dia);
|
|
11608
|
+
},
|
|
11609
|
+
onMouseLeave: () => {
|
|
11610
|
+
onHoverRow(null);
|
|
11611
|
+
onHoverDay(null);
|
|
11612
|
+
},
|
|
11613
|
+
children: [
|
|
11614
|
+
axis.units.map((u3) => {
|
|
11615
|
+
const dia = diaDaUnidade(u3.date);
|
|
11616
|
+
return /* @__PURE__ */ jsx(
|
|
11617
|
+
"div",
|
|
11618
|
+
{
|
|
11619
|
+
className: ganttGridColumn({
|
|
11620
|
+
weekend: u3.isWeekend,
|
|
11621
|
+
hovered: hoveredDay !== null && hoveredDay >= dia && hoveredDay < dia + u3.spanDays,
|
|
11622
|
+
selected: selectedDay !== null && selectedDay >= dia && selectedDay < dia + u3.spanDays,
|
|
11623
|
+
boundary: limitesDeGrupo.has(dia + u3.spanDays)
|
|
11624
|
+
}),
|
|
11625
|
+
style: {
|
|
11626
|
+
left: dateToX(u3.date, windowStart, pxPerDay),
|
|
11627
|
+
width: u3.spanDays * pxPerDay
|
|
11628
|
+
},
|
|
11629
|
+
"aria-hidden": true
|
|
11630
|
+
},
|
|
11631
|
+
`col-${u3.date.getTime()}`
|
|
11632
|
+
);
|
|
11633
|
+
}),
|
|
11634
|
+
rows.map(({ row }, i4) => /* @__PURE__ */ jsx(
|
|
11635
|
+
"div",
|
|
11636
|
+
{
|
|
11637
|
+
className: ganttCanvasRow({
|
|
11638
|
+
type: row.type ?? "task",
|
|
11639
|
+
hovered: hoveredRow === i4,
|
|
11640
|
+
selected: selectedRow === i4
|
|
11641
|
+
}),
|
|
11642
|
+
style: { top: tops[i4], height: alturas[i4] },
|
|
11643
|
+
onMouseEnter: () => onHoverRow(i4),
|
|
11644
|
+
"aria-hidden": true
|
|
11645
|
+
},
|
|
11646
|
+
`row-${row.id}`
|
|
11647
|
+
)),
|
|
11648
|
+
geos.lista.map((g3) => {
|
|
11649
|
+
const tipo = g3.row.type ?? "task";
|
|
11650
|
+
const aceitaGesto = tipo !== "summary";
|
|
11651
|
+
return /* @__PURE__ */ jsx(
|
|
11652
|
+
GanttBarView,
|
|
11653
|
+
{
|
|
11654
|
+
bar: g3.bar,
|
|
11655
|
+
left: g3.left,
|
|
11656
|
+
width: g3.width,
|
|
11657
|
+
lane: g3.lane,
|
|
11658
|
+
rowType: tipo,
|
|
11659
|
+
colorKey: g3.colorKey,
|
|
11660
|
+
continuesBefore: g3.continuesBefore,
|
|
11661
|
+
continuesAfter: g3.continuesAfter,
|
|
11662
|
+
conflict: conflictBarIds.has(g3.bar.id),
|
|
11663
|
+
critical: criticalBarIds.has(g3.bar.id),
|
|
11664
|
+
movable: draggable && aceitaGesto,
|
|
11665
|
+
resizable: resizable && aceitaGesto,
|
|
11666
|
+
linkable: linkable && aceitaGesto,
|
|
11667
|
+
height: tipo === "summary" ? GANTT_SUMMARY_BAR_HEIGHT_PX : GANTT_BAR_HEIGHT_PX,
|
|
11668
|
+
dragging: gesto?.barId === g3.bar.id,
|
|
11669
|
+
style: { top: topDaBarra(g3, tipo) },
|
|
11670
|
+
onClick: (e3) => onBarClick?.(g3.bar, g3.row, e3),
|
|
11671
|
+
onMoveStart: (e3) => iniciarGesto(g3.bar.id, "move", e3),
|
|
11672
|
+
onResizeStart: (lado, e3) => iniciarGesto(
|
|
11673
|
+
g3.bar.id,
|
|
11674
|
+
lado === "start" ? "resize-start" : "resize-end",
|
|
11675
|
+
e3
|
|
11676
|
+
),
|
|
11677
|
+
onLinkStart: (lado, e3) => iniciarVinculo(g3.bar.id, lado, e3)
|
|
11678
|
+
},
|
|
11679
|
+
g3.bar.id
|
|
11680
|
+
);
|
|
11681
|
+
}),
|
|
11682
|
+
/* @__PURE__ */ jsx(
|
|
11683
|
+
GanttLinksLayer,
|
|
11684
|
+
{
|
|
11685
|
+
links: linkGeos,
|
|
11686
|
+
width: larguraTotal,
|
|
11687
|
+
height: Math.max(alturaTotal, 1),
|
|
11688
|
+
onLinkClick
|
|
11689
|
+
}
|
|
11690
|
+
),
|
|
11691
|
+
vinculoFantasma ? /* @__PURE__ */ jsx(
|
|
11692
|
+
"svg",
|
|
11693
|
+
{
|
|
11694
|
+
className: "pointer-events-none absolute inset-0 z-[4] overflow-visible",
|
|
11695
|
+
width: larguraTotal,
|
|
11696
|
+
height: Math.max(alturaTotal, 1),
|
|
11697
|
+
"aria-hidden": true,
|
|
11698
|
+
children: /* @__PURE__ */ jsx(
|
|
11699
|
+
"line",
|
|
11700
|
+
{
|
|
11701
|
+
x1: vinculoFantasma.x1,
|
|
11702
|
+
y1: vinculoFantasma.y1,
|
|
11703
|
+
x2: vinculoFantasma.x2,
|
|
11704
|
+
y2: vinculoFantasma.y2,
|
|
11705
|
+
className: ganttLinkGhost()
|
|
11706
|
+
}
|
|
11707
|
+
)
|
|
11708
|
+
}
|
|
11709
|
+
) : null,
|
|
11710
|
+
nowVisivel ? /* @__PURE__ */ jsxs("div", { className: ganttNowLine(), style: { left: nowX }, "aria-hidden": true, children: [
|
|
11711
|
+
/* @__PURE__ */ jsx("span", { className: ganttNowStroke() }),
|
|
11712
|
+
/* @__PURE__ */ jsx("span", { className: ganttNowDot() })
|
|
11713
|
+
] }) : null
|
|
11714
|
+
]
|
|
11715
|
+
}
|
|
11716
|
+
)
|
|
11717
|
+
] }) });
|
|
11718
|
+
}
|
|
11719
|
+
const Gantt = forwardRef(function Gantt2({
|
|
11720
|
+
rows,
|
|
11721
|
+
links = [],
|
|
11722
|
+
view: viewProp,
|
|
11723
|
+
views,
|
|
11724
|
+
onViewChange,
|
|
11725
|
+
windowStart: wsProp,
|
|
11726
|
+
windowEnd: weProp,
|
|
11727
|
+
granularity: granProp,
|
|
11728
|
+
onGranularityChange,
|
|
11729
|
+
columns,
|
|
11730
|
+
gridWidth: gridWidthProp = 360,
|
|
11731
|
+
draggable = false,
|
|
11732
|
+
resizable = false,
|
|
11733
|
+
linkable = false,
|
|
11734
|
+
// Sem default no destructuring de propósito: `undefined` é o sinal de
|
|
11735
|
+
// "não controlado", e um default aqui o apagaria.
|
|
11736
|
+
criticalPath: criticalProp,
|
|
11737
|
+
onCriticalPathChange,
|
|
11738
|
+
criticalPathToggle = true,
|
|
11739
|
+
onGraphError,
|
|
11740
|
+
onLinkViolations,
|
|
11741
|
+
searchable = false,
|
|
11742
|
+
filterFields,
|
|
11743
|
+
filterModel,
|
|
11744
|
+
onFilterModelChange,
|
|
11745
|
+
onBarClick,
|
|
11746
|
+
onRowClick,
|
|
11747
|
+
/**
|
|
11748
|
+
* ⚠️ Estes quatro estavam DECLARADOS no tipo e nunca desestruturados aqui —
|
|
11749
|
+
* a raiz não os recebia. Era superfície de API sem nada por trás: os punhos
|
|
11750
|
+
* e as portas renderizavam, acessíveis, e o gesto não existia.
|
|
11751
|
+
*/
|
|
11752
|
+
onBarMove,
|
|
11753
|
+
onBarResize,
|
|
11754
|
+
onLinkCreate,
|
|
11755
|
+
onLinkDelete,
|
|
11756
|
+
onDayAdd,
|
|
11757
|
+
onRowToggle,
|
|
11758
|
+
now = /* @__PURE__ */ new Date(),
|
|
11759
|
+
locale,
|
|
11760
|
+
weekStartsOn = 0,
|
|
11761
|
+
toolbarActions,
|
|
11762
|
+
primaryAction,
|
|
11763
|
+
emptyState,
|
|
11764
|
+
loading = false,
|
|
11765
|
+
loadingState,
|
|
11766
|
+
className
|
|
11767
|
+
}, ref) {
|
|
11768
|
+
const [viewLocal, setViewLocal] = useState(viewProp ?? "timeline");
|
|
11769
|
+
const visoesDisponiveis = useMemo(
|
|
11770
|
+
() => views && views.length > 0 ? views : ["timeline", "calendar", "list"],
|
|
11771
|
+
[views]
|
|
11772
|
+
);
|
|
11773
|
+
const viewPedida = viewProp ?? viewLocal;
|
|
11774
|
+
const view = visoesDisponiveis.includes(viewPedida) ? viewPedida : visoesDisponiveis[0];
|
|
11775
|
+
const [granLocal, setGranLocal] = useState(granProp ?? "day");
|
|
11776
|
+
const granularity = granProp ?? granLocal;
|
|
11777
|
+
const [criticalLocal, setCriticalLocal] = useState(criticalProp ?? false);
|
|
11778
|
+
const criticalPath = criticalProp ?? criticalLocal;
|
|
11779
|
+
const [busca, setBusca] = useState("");
|
|
11780
|
+
const [filterLocal, setFilterLocal] = useState({});
|
|
11781
|
+
const [colapsadas, setColapsadas] = useState(() => /* @__PURE__ */ new Set());
|
|
11782
|
+
const [gridWidth, setGridWidth] = useState(gridWidthProp);
|
|
11783
|
+
const [arrastandoDivisor, setArrastandoDivisor] = useState(false);
|
|
11784
|
+
const [janelaLocal, setJanelaLocal] = useState(
|
|
11785
|
+
() => deriveWindow(rows, granProp ?? "day")
|
|
11786
|
+
);
|
|
11787
|
+
const windowStart = wsProp ?? janelaLocal.start;
|
|
11788
|
+
const windowEnd = weProp ?? janelaLocal.end;
|
|
11789
|
+
const pxPerDay = GANTT_PX_PER_DAY[granularity];
|
|
11790
|
+
const modeloEfetivo = filterModel ?? filterLocal;
|
|
11791
|
+
const aplicarModelo = useCallback(
|
|
11792
|
+
(proximo) => {
|
|
11793
|
+
setFilterLocal(proximo);
|
|
11794
|
+
onFilterModelChange?.(proximo);
|
|
11795
|
+
},
|
|
11796
|
+
[onFilterModelChange]
|
|
11797
|
+
);
|
|
11798
|
+
const definirValores = useCallback(
|
|
11799
|
+
(fieldId, values) => {
|
|
11800
|
+
aplicarModelo({ ...modeloEfetivo, [fieldId]: values });
|
|
11801
|
+
},
|
|
11802
|
+
[modeloEfetivo, aplicarModelo]
|
|
11803
|
+
);
|
|
11804
|
+
const alternarValor = useCallback(
|
|
11805
|
+
(fieldId, value) => {
|
|
11806
|
+
const atuais = modeloEfetivo[fieldId] ?? [];
|
|
11807
|
+
const proximas = atuais.includes(value) ? atuais.filter((v3) => v3 !== value) : [...atuais, value];
|
|
11808
|
+
aplicarModelo({ ...modeloEfetivo, [fieldId]: proximas });
|
|
11809
|
+
},
|
|
11810
|
+
[modeloEfetivo, aplicarModelo]
|
|
11811
|
+
);
|
|
11812
|
+
const rowsFiltradas = useMemo(() => {
|
|
11813
|
+
let saida = rows;
|
|
11814
|
+
const termo = busca.trim().toLowerCase();
|
|
11815
|
+
if (termo) {
|
|
11816
|
+
const casa = (r2) => {
|
|
11817
|
+
const alvo = [
|
|
11818
|
+
r2.searchText,
|
|
11819
|
+
typeof r2.label === "string" ? r2.label : "",
|
|
11820
|
+
typeof r2.sublabel === "string" ? r2.sublabel : "",
|
|
11821
|
+
...r2.bars.map((b4) => b4.searchText ?? "")
|
|
11822
|
+
].join(" ").toLowerCase();
|
|
11823
|
+
return alvo.includes(termo);
|
|
11824
|
+
};
|
|
11825
|
+
const porId = new Map(rows.map((r2) => [r2.id, r2]));
|
|
11826
|
+
const manter = /* @__PURE__ */ new Set();
|
|
11827
|
+
for (const r2 of rows) {
|
|
11828
|
+
if (!casa(r2)) continue;
|
|
11829
|
+
manter.add(r2.id);
|
|
11830
|
+
let pai = r2.parent;
|
|
11831
|
+
let guarda = 0;
|
|
11832
|
+
while (pai && guarda < 50) {
|
|
11833
|
+
if (manter.has(pai)) break;
|
|
11834
|
+
manter.add(pai);
|
|
11835
|
+
pai = porId.get(pai)?.parent;
|
|
11836
|
+
guarda++;
|
|
11837
|
+
}
|
|
11838
|
+
}
|
|
11839
|
+
saida = saida.filter((r2) => manter.has(r2.id));
|
|
11840
|
+
}
|
|
11841
|
+
saida = aplicarFiltros(saida, filterFields, modeloEfetivo);
|
|
11842
|
+
return saida;
|
|
11843
|
+
}, [rows, busca, filterFields, modeloEfetivo]);
|
|
11844
|
+
const flat = useMemo(
|
|
11845
|
+
() => flattenRows(rowsFiltradas, colapsadas),
|
|
11846
|
+
[rowsFiltradas, colapsadas]
|
|
11847
|
+
);
|
|
11848
|
+
const alturas = useMemo(
|
|
11849
|
+
() => rowHeights(flat, GANTT_ROW_HEIGHT_PX, GANTT_LANE_HEIGHT_PX),
|
|
11850
|
+
[flat]
|
|
11851
|
+
);
|
|
11852
|
+
const offsets = useMemo(() => rowOffsets(alturas), [alturas]);
|
|
11853
|
+
const contagens = useMemo(() => {
|
|
11854
|
+
const saida = {};
|
|
11855
|
+
for (const campo of filterFields ?? []) {
|
|
11856
|
+
const kind = campo.kind ?? "multi";
|
|
11857
|
+
if (kind !== "multi" && kind !== "single" && kind !== "boolean") continue;
|
|
11858
|
+
const porOpcao = {};
|
|
11859
|
+
for (const r2 of rows) {
|
|
11860
|
+
const v3 = campo.accessor(r2);
|
|
11861
|
+
if (v3 === void 0 || v3 === null) continue;
|
|
11862
|
+
for (const x3 of Array.isArray(v3) ? v3 : [v3]) {
|
|
11863
|
+
const chave = String(x3);
|
|
11864
|
+
porOpcao[chave] = (porOpcao[chave] ?? 0) + 1;
|
|
11865
|
+
}
|
|
11866
|
+
}
|
|
11867
|
+
saida[campo.id] = porOpcao;
|
|
11868
|
+
}
|
|
11869
|
+
return saida;
|
|
11870
|
+
}, [rows, filterFields]);
|
|
11871
|
+
const todasBarras = useMemo(() => {
|
|
11872
|
+
const lista = [];
|
|
11873
|
+
for (const r2 of rows) lista.push(...r2.bars);
|
|
11874
|
+
return lista;
|
|
11875
|
+
}, [rows]);
|
|
11876
|
+
const mapaBarras = useMemo(
|
|
11877
|
+
() => new Map(todasBarras.map((b4) => [b4.id, b4])),
|
|
11878
|
+
[todasBarras]
|
|
11879
|
+
);
|
|
11880
|
+
const violacoes = useMemo(
|
|
11881
|
+
() => checkAllLinks(links, mapaBarras),
|
|
11882
|
+
[links, mapaBarras]
|
|
11883
|
+
);
|
|
11884
|
+
const conflictBarIds = useMemo(
|
|
11885
|
+
() => new Set(violacoes.map((v3) => v3.link.target)),
|
|
11886
|
+
[violacoes]
|
|
11887
|
+
);
|
|
11888
|
+
const violacoesRef = useRef("");
|
|
11889
|
+
useEffect(() => {
|
|
11890
|
+
const assinatura = violacoes.map((v3) => `${v3.link.id}:${v3.deficitDays}`).join("|");
|
|
11891
|
+
if (assinatura === violacoesRef.current) return;
|
|
11892
|
+
violacoesRef.current = assinatura;
|
|
11893
|
+
onLinkViolations?.(violacoes);
|
|
11894
|
+
}, [violacoes, onLinkViolations]);
|
|
11895
|
+
const criticalBarIds = useMemo(() => {
|
|
11896
|
+
if (!criticalPath || links.length === 0) return /* @__PURE__ */ new Set();
|
|
11897
|
+
const r2 = computeCriticalPath(todasBarras, links);
|
|
11898
|
+
if ("cycle" in r2) return /* @__PURE__ */ new Set();
|
|
11899
|
+
return r2.critical;
|
|
11900
|
+
}, [criticalPath, links, todasBarras]);
|
|
11901
|
+
const cicloRef = useRef("");
|
|
11902
|
+
useEffect(() => {
|
|
11903
|
+
if (!criticalPath || links.length === 0) return;
|
|
11904
|
+
const r2 = computeCriticalPath(todasBarras, links);
|
|
11905
|
+
if (!("cycle" in r2)) {
|
|
11906
|
+
cicloRef.current = "";
|
|
11907
|
+
return;
|
|
11908
|
+
}
|
|
11909
|
+
const assinatura = r2.cycle.join("|");
|
|
11910
|
+
if (assinatura === cicloRef.current) return;
|
|
11911
|
+
cicloRef.current = assinatura;
|
|
11912
|
+
onGraphError?.({ kind: "cycle", barIds: r2.cycle });
|
|
11913
|
+
}, [criticalPath, links, todasBarras, onGraphError]);
|
|
11914
|
+
const [hoveredRow, setHoveredRow] = useState(null);
|
|
11915
|
+
const [selectedRow, setSelectedRow] = useState(null);
|
|
11916
|
+
const [selectedDay, setSelectedDay] = useState(null);
|
|
11917
|
+
const [filtroAberto, setFiltroAberto] = useState(false);
|
|
11918
|
+
const [hoveredDay, setHoveredDay] = useState(null);
|
|
11919
|
+
const [scrollTop, setScrollTop] = useState(0);
|
|
11920
|
+
const aoRolar = useCallback((e3) => {
|
|
11921
|
+
setScrollTop(e3.currentTarget.scrollTop);
|
|
11922
|
+
}, []);
|
|
11923
|
+
const bodyRef = useRef(null);
|
|
11924
|
+
const scrollerRef = useRef(null);
|
|
11925
|
+
const [centrarEm, setCentrarEm] = useState(null);
|
|
11926
|
+
useEffect(() => {
|
|
11927
|
+
if (!centrarEm) return;
|
|
11928
|
+
const el = scrollerRef.current;
|
|
11929
|
+
if (!el) return;
|
|
11930
|
+
const x3 = dateToX(centrarEm, windowStart, pxPerDay);
|
|
11931
|
+
el.scrollLeft = Math.max(0, x3 - el.clientWidth / 2);
|
|
11932
|
+
setCentrarEm(null);
|
|
11933
|
+
}, [centrarEm, windowStart, pxPerDay]);
|
|
11934
|
+
useEffect(() => {
|
|
11935
|
+
if (!arrastandoDivisor) return;
|
|
11936
|
+
const mover2 = (e3) => {
|
|
11937
|
+
const caixa = bodyRef.current?.getBoundingClientRect();
|
|
11938
|
+
if (!caixa) return;
|
|
11939
|
+
const bruto = e3.clientX - caixa.left;
|
|
11940
|
+
setGridWidth(Math.min(Math.max(180, bruto), caixa.width * 0.6));
|
|
11941
|
+
};
|
|
11942
|
+
const soltar = () => setArrastandoDivisor(false);
|
|
11943
|
+
window.addEventListener("pointermove", mover2);
|
|
11944
|
+
window.addEventListener("pointerup", soltar);
|
|
11945
|
+
return () => {
|
|
11946
|
+
window.removeEventListener("pointermove", mover2);
|
|
11947
|
+
window.removeEventListener("pointerup", soltar);
|
|
11948
|
+
};
|
|
11949
|
+
}, [arrastandoDivisor]);
|
|
11950
|
+
const larguraJanelaDias = differenceInCalendarDays(startOfDay(windowEnd), startOfDay(windowStart)) + 1;
|
|
11951
|
+
const mover = useCallback(
|
|
11952
|
+
(dias) => {
|
|
11953
|
+
if (wsProp || weProp) return;
|
|
11954
|
+
setJanelaLocal((j2) => ({
|
|
11955
|
+
start: addDays(j2.start, dias),
|
|
11956
|
+
end: addDays(j2.end, dias)
|
|
11957
|
+
}));
|
|
11958
|
+
},
|
|
11959
|
+
[wsProp, weProp]
|
|
11960
|
+
);
|
|
11961
|
+
useImperativeHandle(
|
|
11962
|
+
ref,
|
|
11963
|
+
() => ({
|
|
11964
|
+
goToDate: (date) => {
|
|
11965
|
+
if (wsProp || weProp) return;
|
|
11966
|
+
const meio = Math.floor(larguraJanelaDias / 2);
|
|
11967
|
+
setJanelaLocal({
|
|
11968
|
+
start: addDays(startOfDay(date), -meio),
|
|
11969
|
+
end: addDays(startOfDay(date), larguraJanelaDias - meio - 1)
|
|
11970
|
+
});
|
|
11971
|
+
},
|
|
11972
|
+
goToToday: () => {
|
|
11973
|
+
if (wsProp || weProp) return;
|
|
11974
|
+
const meio = Math.floor(larguraJanelaDias / 2);
|
|
11975
|
+
setJanelaLocal({
|
|
11976
|
+
start: addDays(startOfDay(now), -meio),
|
|
11977
|
+
end: addDays(startOfDay(now), larguraJanelaDias - meio - 1)
|
|
11978
|
+
});
|
|
11979
|
+
},
|
|
11980
|
+
expandAll: () => setColapsadas(/* @__PURE__ */ new Set()),
|
|
11981
|
+
collapseAll: () => setColapsadas(new Set(rows.filter((r2) => r2.parent === void 0).map((r2) => r2.id)))
|
|
11982
|
+
}),
|
|
11983
|
+
[wsProp, weProp, larguraJanelaDias, now, rows]
|
|
11984
|
+
);
|
|
11985
|
+
const ancoraDoMes = useMemo(
|
|
11986
|
+
() => addDays(startOfDay(windowStart), Math.floor(larguraJanelaDias / 2)),
|
|
11987
|
+
[windowStart, larguraJanelaDias]
|
|
11988
|
+
);
|
|
11989
|
+
const mesVisivel = useMemo(
|
|
11990
|
+
() => ({
|
|
11991
|
+
start: startOfMonth(ancoraDoMes),
|
|
11992
|
+
end: endOfMonth(ancoraDoMes)
|
|
11993
|
+
}),
|
|
11994
|
+
[ancoraDoMes]
|
|
11995
|
+
);
|
|
11996
|
+
const visaoMensal = view === "calendar" || view === "list";
|
|
11997
|
+
const titulo = useMemo(() => {
|
|
11998
|
+
if (visaoMensal) {
|
|
11999
|
+
return format(ancoraDoMes, "MMMM yyyy", { locale });
|
|
12000
|
+
}
|
|
12001
|
+
const mesmoAno = windowStart.getFullYear() === windowEnd.getFullYear();
|
|
12002
|
+
const a11 = format(windowStart, mesmoAno ? "d MMM" : "d MMM yyyy", { locale });
|
|
12003
|
+
const b4 = format(windowEnd, "d MMM yyyy", { locale });
|
|
12004
|
+
return `${a11} – ${b4}`;
|
|
12005
|
+
}, [visaoMensal, ancoraDoMes, windowStart, windowEnd, locale]);
|
|
12006
|
+
const passoDeNavegacao = useCallback(
|
|
12007
|
+
(direcao) => {
|
|
12008
|
+
if (visaoMensal) {
|
|
12009
|
+
const destino = addMonths(ancoraDoMes, direcao);
|
|
12010
|
+
return differenceInCalendarDays(destino, ancoraDoMes);
|
|
12011
|
+
}
|
|
12012
|
+
return direcao * Math.max(1, Math.floor(larguraJanelaDias / 2));
|
|
12013
|
+
},
|
|
12014
|
+
[visaoMensal, ancoraDoMes, larguraJanelaDias]
|
|
12015
|
+
);
|
|
12016
|
+
const formatarDataDeFiltro = useCallback(
|
|
12017
|
+
(iso) => {
|
|
12018
|
+
const d3 = parseDiaISO(iso);
|
|
12019
|
+
if (!d3) return iso;
|
|
12020
|
+
return format(d3, "dd/MM/yy", { locale });
|
|
12021
|
+
},
|
|
12022
|
+
[locale]
|
|
12023
|
+
);
|
|
12024
|
+
const colunas = columns ?? GANTT_DEFAULT_COLUMNS;
|
|
12025
|
+
const appliedCount = contarAplicados(filterFields, modeloEfetivo);
|
|
12026
|
+
return /* @__PURE__ */ jsxs("div", { className: cn(ganttRoot(), className), children: [
|
|
12027
|
+
/* @__PURE__ */ jsx(
|
|
12028
|
+
GanttToolbar,
|
|
12029
|
+
{
|
|
12030
|
+
title: titulo,
|
|
12031
|
+
onPrev: () => mover(passoDeNavegacao(-1)),
|
|
12032
|
+
onNext: () => mover(passoDeNavegacao(1)),
|
|
12033
|
+
onToday: () => {
|
|
12034
|
+
if (wsProp || weProp) return;
|
|
12035
|
+
const meio = Math.floor(larguraJanelaDias / 2);
|
|
12036
|
+
setJanelaLocal({
|
|
12037
|
+
start: addDays(startOfDay(now), -meio),
|
|
12038
|
+
end: addDays(startOfDay(now), larguraJanelaDias - meio - 1)
|
|
12039
|
+
});
|
|
12040
|
+
},
|
|
12041
|
+
view,
|
|
12042
|
+
views: visoesDisponiveis,
|
|
12043
|
+
onViewChange: (v3) => {
|
|
12044
|
+
if (viewProp === void 0) setViewLocal(v3);
|
|
12045
|
+
onViewChange?.(v3);
|
|
12046
|
+
},
|
|
12047
|
+
granularity,
|
|
12048
|
+
onGranularityChange: (g3) => {
|
|
12049
|
+
const el = scrollerRef.current;
|
|
12050
|
+
if (el) {
|
|
12051
|
+
setCentrarEm(
|
|
12052
|
+
xToDate(el.scrollLeft + el.clientWidth / 2, windowStart, pxPerDay)
|
|
12053
|
+
);
|
|
12054
|
+
}
|
|
12055
|
+
if (granProp === void 0) setGranLocal(g3);
|
|
12056
|
+
onGranularityChange?.(g3);
|
|
12057
|
+
if (wsProp || weProp) return;
|
|
12058
|
+
const dias = GANTT_WINDOW_DAYS[g3];
|
|
12059
|
+
const extensao = deriveWindow(rows, g3);
|
|
12060
|
+
setJanelaLocal((j2) => {
|
|
12061
|
+
const meio = addDays(
|
|
12062
|
+
startOfDay(j2.start),
|
|
12063
|
+
Math.floor(
|
|
12064
|
+
differenceInCalendarDays(startOfDay(j2.end), startOfDay(j2.start)) / 2
|
|
12065
|
+
)
|
|
12066
|
+
);
|
|
12067
|
+
const start = addDays(meio, -Math.floor(dias / 2));
|
|
12068
|
+
const end = addDays(meio, dias - Math.floor(dias / 2) - 1);
|
|
12069
|
+
return {
|
|
12070
|
+
start: extensao.start < start ? extensao.start : start,
|
|
12071
|
+
end: extensao.end > end ? extensao.end : end
|
|
12072
|
+
};
|
|
12073
|
+
});
|
|
12074
|
+
},
|
|
12075
|
+
searchable,
|
|
12076
|
+
search: busca,
|
|
12077
|
+
onSearchChange: setBusca,
|
|
12078
|
+
filterFields,
|
|
12079
|
+
onOpenFilterPanel: () => setFiltroAberto(true),
|
|
12080
|
+
appliedCount,
|
|
12081
|
+
filterModel: modeloEfetivo,
|
|
12082
|
+
onClearField: (id) => definirValores(id, []),
|
|
12083
|
+
onClearAll: () => aplicarModelo({}),
|
|
12084
|
+
onToggleFilterValue: alternarValor,
|
|
12085
|
+
filterCounts: contagens,
|
|
12086
|
+
formatFilterDate: formatarDataDeFiltro,
|
|
12087
|
+
hasLinks: links.length > 0,
|
|
12088
|
+
showCriticalToggle: criticalPathToggle,
|
|
12089
|
+
criticalPath,
|
|
12090
|
+
onToggleCriticalPath: () => {
|
|
12091
|
+
const proximo = !criticalPath;
|
|
12092
|
+
if (criticalProp === void 0) setCriticalLocal(proximo);
|
|
12093
|
+
onCriticalPathChange?.(proximo);
|
|
12094
|
+
},
|
|
12095
|
+
toolbarActions,
|
|
12096
|
+
primaryAction
|
|
12097
|
+
}
|
|
12098
|
+
),
|
|
12099
|
+
/* @__PURE__ */ jsx(
|
|
12100
|
+
"div",
|
|
12101
|
+
{
|
|
12102
|
+
ref: bodyRef,
|
|
12103
|
+
className: ganttBody({ framed: view === "timeline" }),
|
|
12104
|
+
role: "region",
|
|
12105
|
+
"aria-label": `Cronograma, ${titulo}`,
|
|
12106
|
+
children: loading ? loadingState ?? /* @__PURE__ */ jsx(GanttSkeleton, { view, gridWidth }) : flat.length === 0 ? /* @__PURE__ */ jsx("div", { className: ganttEmpty(), children: emptyState ?? /* @__PURE__ */ jsx("span", { className: ganttEmptyText(), children: "Nenhuma tarefa neste período." }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
12107
|
+
view === "timeline" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
12108
|
+
/* @__PURE__ */ jsx("div", { className: ganttGridPane(), style: { width: gridWidth }, children: /* @__PURE__ */ jsx(
|
|
12109
|
+
GanttGrid,
|
|
12110
|
+
{
|
|
12111
|
+
rows: flat,
|
|
12112
|
+
columns: colunas,
|
|
12113
|
+
heights: alturas,
|
|
12114
|
+
scrollTop,
|
|
12115
|
+
locale,
|
|
12116
|
+
hoveredRow,
|
|
12117
|
+
onHoverRow: setHoveredRow,
|
|
12118
|
+
selectedRow,
|
|
12119
|
+
onRowToggle: (id, colapsar) => {
|
|
12120
|
+
setColapsadas((prev) => {
|
|
12121
|
+
const proximo = new Set(prev);
|
|
12122
|
+
if (colapsar) proximo.add(id);
|
|
12123
|
+
else proximo.delete(id);
|
|
12124
|
+
return proximo;
|
|
12125
|
+
});
|
|
12126
|
+
onRowToggle?.(id, colapsar);
|
|
12127
|
+
},
|
|
12128
|
+
onRowClick: (row, e3) => {
|
|
12129
|
+
const i4 = flat.findIndex((f3) => f3.row.id === row.id);
|
|
12130
|
+
setSelectedRow((atual) => atual === i4 ? null : i4);
|
|
12131
|
+
onRowClick?.(row, e3);
|
|
12132
|
+
}
|
|
12133
|
+
}
|
|
12134
|
+
) }),
|
|
12135
|
+
/* @__PURE__ */ jsx(
|
|
12136
|
+
"div",
|
|
12137
|
+
{
|
|
12138
|
+
role: "separator",
|
|
12139
|
+
"aria-orientation": "vertical",
|
|
12140
|
+
"aria-label": "Redimensionar a grade",
|
|
12141
|
+
tabIndex: 0,
|
|
12142
|
+
className: ganttSplitter({ active: arrastandoDivisor }),
|
|
12143
|
+
onPointerDown: () => setArrastandoDivisor(true),
|
|
12144
|
+
onKeyDown: (e3) => {
|
|
12145
|
+
if (e3.key === "ArrowLeft") setGridWidth((w3) => Math.max(180, w3 - 16));
|
|
12146
|
+
if (e3.key === "ArrowRight") setGridWidth((w3) => w3 + 16);
|
|
12147
|
+
}
|
|
12148
|
+
}
|
|
12149
|
+
)
|
|
12150
|
+
] }) : null,
|
|
12151
|
+
/* @__PURE__ */ jsx("div", { className: ganttTimelinePane(), children: view === "timeline" ? /* @__PURE__ */ jsx(
|
|
12152
|
+
GanttTimelineView,
|
|
12153
|
+
{
|
|
12154
|
+
rows: flat,
|
|
12155
|
+
allRows: rows,
|
|
12156
|
+
links,
|
|
12157
|
+
windowStart,
|
|
12158
|
+
windowEnd,
|
|
12159
|
+
granularity,
|
|
12160
|
+
pxPerDay,
|
|
12161
|
+
heights: alturas,
|
|
12162
|
+
offsets,
|
|
12163
|
+
now,
|
|
12164
|
+
locale,
|
|
12165
|
+
weekStartsOn,
|
|
12166
|
+
conflictBarIds,
|
|
12167
|
+
criticalBarIds,
|
|
12168
|
+
draggable,
|
|
12169
|
+
resizable,
|
|
12170
|
+
linkable,
|
|
12171
|
+
hoveredRow,
|
|
12172
|
+
onHoverRow: setHoveredRow,
|
|
12173
|
+
hoveredDay,
|
|
12174
|
+
onHoverDay: setHoveredDay,
|
|
12175
|
+
selectedRow,
|
|
12176
|
+
selectedDay,
|
|
12177
|
+
onSelectDay: setSelectedDay,
|
|
12178
|
+
onBarMove,
|
|
12179
|
+
onBarResize,
|
|
12180
|
+
onLinkCreate,
|
|
12181
|
+
onLinkClick: onLinkDelete,
|
|
12182
|
+
onScroll: aoRolar,
|
|
12183
|
+
scrollRef: scrollerRef,
|
|
12184
|
+
onBarClick
|
|
12185
|
+
}
|
|
12186
|
+
) : view === "calendar" ? (
|
|
12187
|
+
/*
|
|
12188
|
+
⚠️ O mês âncora é o MEIO da janela, e chegar nisso custou uma
|
|
12189
|
+
medição.
|
|
12190
|
+
|
|
12191
|
+
Não é `now`: aí navegar com `‹ ›` moveria a janela e a grade
|
|
12192
|
+
ficaria parada — as duas visões falariam de períodos
|
|
12193
|
+
diferentes, e trocar de visão teleportaria o usuário.
|
|
12194
|
+
|
|
12195
|
+
E não é `windowStart`, que foi a primeira tentativa. A janela
|
|
12196
|
+
derivada começa UM DIA ANTES da primeira barra, então o mês
|
|
12197
|
+
âncora caía no mês anterior ao trabalho: medido no exemplo,
|
|
12198
|
+
`windowStart` = 31/ago abria agosto com **5 células ocupadas
|
|
12199
|
+
de 42** enquanto os dados viviam em set–nov.
|
|
12200
|
+
|
|
12201
|
+
O meio da janela é onde a massa do cronograma está, acompanha
|
|
12202
|
+
o `‹ ›` e nunca cai num mês de borda.
|
|
12203
|
+
*/
|
|
12204
|
+
/* @__PURE__ */ jsx(
|
|
12205
|
+
GanttCalendarView,
|
|
12206
|
+
{
|
|
12207
|
+
rows: flat,
|
|
12208
|
+
anchor: ancoraDoMes,
|
|
12209
|
+
weekStartsOn,
|
|
12210
|
+
now,
|
|
12211
|
+
locale,
|
|
12212
|
+
conflictBarIds,
|
|
12213
|
+
criticalBarIds,
|
|
12214
|
+
onBarClick,
|
|
12215
|
+
onDayAdd
|
|
12216
|
+
}
|
|
12217
|
+
)
|
|
12218
|
+
) : (
|
|
12219
|
+
/*
|
|
12220
|
+
A agenda recebe o MÊS, não a janela — ver a nota do
|
|
12221
|
+
`mesVisivel`. As setas `‹ ›` andam um mês nesta visão, então o
|
|
12222
|
+
recorte e a navegação falam a mesma unidade.
|
|
12223
|
+
*/
|
|
12224
|
+
/* @__PURE__ */ jsx(
|
|
12225
|
+
GanttListView,
|
|
12226
|
+
{
|
|
12227
|
+
rows: flat,
|
|
12228
|
+
windowStart: mesVisivel.start,
|
|
12229
|
+
windowEnd: mesVisivel.end,
|
|
12230
|
+
now,
|
|
12231
|
+
locale,
|
|
12232
|
+
conflictBarIds,
|
|
12233
|
+
criticalBarIds,
|
|
12234
|
+
onBarClick,
|
|
12235
|
+
emptyState
|
|
12236
|
+
}
|
|
12237
|
+
)
|
|
12238
|
+
) })
|
|
12239
|
+
] })
|
|
12240
|
+
}
|
|
12241
|
+
),
|
|
12242
|
+
filterFields && filterFields.length > 0 ? /* @__PURE__ */ jsx(
|
|
12243
|
+
GanttFilterPanel,
|
|
12244
|
+
{
|
|
12245
|
+
open: filtroAberto,
|
|
12246
|
+
onOpenChange: setFiltroAberto,
|
|
12247
|
+
fields: filterFields,
|
|
12248
|
+
model: modeloEfetivo,
|
|
12249
|
+
onToggleValue: alternarValor,
|
|
12250
|
+
onSetFieldValues: definirValores,
|
|
12251
|
+
onClearAll: () => aplicarModelo({}),
|
|
12252
|
+
appliedCount,
|
|
12253
|
+
counts: contagens
|
|
12254
|
+
}
|
|
12255
|
+
) : null
|
|
12256
|
+
] });
|
|
12257
|
+
});
|
|
7849
12258
|
const spinnerStyles = tv({
|
|
7850
12259
|
base: ["inline-block shrink-0", "animate-spin motion-reduce:animate-none"],
|
|
7851
12260
|
variants: {
|
|
@@ -8444,8 +12853,8 @@ const TabsNavigationRoot = forwardRef(function TabsNavigation2({
|
|
|
8444
12853
|
const medir = useCallback(() => {
|
|
8445
12854
|
const el = trilho.current;
|
|
8446
12855
|
if (!el) return;
|
|
8447
|
-
const
|
|
8448
|
-
setRolagem({ transborda:
|
|
12856
|
+
const max2 = el.scrollWidth - el.clientWidth;
|
|
12857
|
+
setRolagem({ transborda: max2 > 1, esq: el.scrollLeft > 1, dir: el.scrollLeft < max2 - 1 });
|
|
8449
12858
|
}, []);
|
|
8450
12859
|
useEffect(() => {
|
|
8451
12860
|
const el = trilho.current;
|
|
@@ -10224,17 +14633,17 @@ const ChoroplethMap = forwardRef(
|
|
|
10224
14633
|
const numeric = Object.values(values).filter(
|
|
10225
14634
|
(v3) => typeof v3 === "number" && Number.isFinite(v3)
|
|
10226
14635
|
);
|
|
10227
|
-
const
|
|
10228
|
-
const
|
|
14636
|
+
const min2 = domain ? domain[0] : numeric.length ? Math.min(...numeric) : 0;
|
|
14637
|
+
const max2 = domain ? domain[1] : numeric.length ? Math.max(...numeric) : 0;
|
|
10229
14638
|
const scaleVar = `var(--color-bg-${scaleToken})`;
|
|
10230
14639
|
const strokeVar = `var(--color-fg-${scaleToken})`;
|
|
10231
14640
|
const surfaceVar = "var(--color-bg-surface)";
|
|
10232
14641
|
const noDataVar = "var(--color-bg-muted)";
|
|
10233
14642
|
const fillFor = (value) => {
|
|
10234
14643
|
if (value === void 0 || !Number.isFinite(value)) return noDataVar;
|
|
10235
|
-
if (colorScale) return colorScale(value, { min, max });
|
|
10236
|
-
const range =
|
|
10237
|
-
const t3 = range > 0 ? (value -
|
|
14644
|
+
if (colorScale) return colorScale(value, { min: min2, max: max2 });
|
|
14645
|
+
const range = max2 - min2;
|
|
14646
|
+
const t3 = range > 0 ? (value - min2) / range : 1;
|
|
10238
14647
|
const clamped = Math.max(0, Math.min(1, t3));
|
|
10239
14648
|
const pct = 12 + clamped * 88;
|
|
10240
14649
|
return `color-mix(in srgb, ${scaleVar} ${pct}%, ${surfaceVar})`;
|
|
@@ -10342,8 +14751,8 @@ const ChoroplethMap = forwardRef(
|
|
|
10342
14751
|
}
|
|
10343
14752
|
),
|
|
10344
14753
|
/* @__PURE__ */ jsxs("div", { className: styles2.legendScale(), children: [
|
|
10345
|
-
/* @__PURE__ */ jsx("span", { children: fmt(
|
|
10346
|
-
/* @__PURE__ */ jsx("span", { children: fmt(
|
|
14754
|
+
/* @__PURE__ */ jsx("span", { children: fmt(min2) }),
|
|
14755
|
+
/* @__PURE__ */ jsx("span", { children: fmt(max2) })
|
|
10347
14756
|
] })
|
|
10348
14757
|
] })
|
|
10349
14758
|
] });
|
|
@@ -11562,6 +15971,17 @@ export {
|
|
|
11562
15971
|
b as FormFieldSelect,
|
|
11563
15972
|
V as FormFieldSwitch,
|
|
11564
15973
|
c as FormFieldTextarea,
|
|
15974
|
+
GANTT_BAR_MIN_WIDTH_PX,
|
|
15975
|
+
GANTT_DEFAULT_COLUMNS,
|
|
15976
|
+
GANTT_HEAD_HEIGHT_PX,
|
|
15977
|
+
GANTT_LANE_HEIGHT_PX,
|
|
15978
|
+
GANTT_PX_PER_DAY,
|
|
15979
|
+
GANTT_ROW_HEIGHT_PX,
|
|
15980
|
+
Gantt,
|
|
15981
|
+
GanttBarView,
|
|
15982
|
+
GanttFilterPanel,
|
|
15983
|
+
GanttGrid,
|
|
15984
|
+
GanttLinksLayer,
|
|
11565
15985
|
H2 as Header,
|
|
11566
15986
|
d as HeaderBreadcrumb,
|
|
11567
15987
|
e as HeaderMessages,
|
|
@@ -11664,22 +16084,37 @@ export {
|
|
|
11664
16084
|
aC as badgeVariants,
|
|
11665
16085
|
p2 as breadcrumbCaminho,
|
|
11666
16086
|
q as breadcrumbSwitcher,
|
|
16087
|
+
buildMonthMatrix,
|
|
16088
|
+
buildTimeAxis,
|
|
11667
16089
|
ag2 as buttonVariants,
|
|
11668
16090
|
cardCheckbox,
|
|
11669
16091
|
cardOption,
|
|
11670
16092
|
cardOptionGroup,
|
|
16093
|
+
checkAllLinks,
|
|
16094
|
+
checkLink,
|
|
11671
16095
|
r as chipCount,
|
|
11672
16096
|
s as chipVariants,
|
|
16097
|
+
clipToWindow,
|
|
11673
16098
|
colorPickerStyles,
|
|
11674
16099
|
columnTypeRegistry,
|
|
16100
|
+
computeCriticalPath,
|
|
16101
|
+
computeOverflow,
|
|
16102
|
+
constraintDates,
|
|
11675
16103
|
conversationListItemStyles,
|
|
11676
16104
|
currencyColumn,
|
|
11677
16105
|
customColumn,
|
|
11678
16106
|
dateColumn,
|
|
11679
16107
|
dateSeparatorChipStyles,
|
|
16108
|
+
dateToDayOffset,
|
|
16109
|
+
dateToX,
|
|
16110
|
+
daysOfBar,
|
|
16111
|
+
deriveSummaryRange,
|
|
16112
|
+
deriveWindow,
|
|
11680
16113
|
emailColumn,
|
|
11681
16114
|
emptyStateStyles,
|
|
16115
|
+
flattenRows,
|
|
11682
16116
|
aj as floatingPanelStyles,
|
|
16117
|
+
ganttDaySegment,
|
|
11683
16118
|
iconStyles,
|
|
11684
16119
|
icons,
|
|
11685
16120
|
ad2 as inputGroupVariants,
|
|
@@ -11688,18 +16123,26 @@ export {
|
|
|
11688
16123
|
isFilterEntryActive,
|
|
11689
16124
|
v2 as isHashHref,
|
|
11690
16125
|
w2 as isModifiedClick,
|
|
16126
|
+
linkPath,
|
|
16127
|
+
linkSides,
|
|
11691
16128
|
markdownTextStyles,
|
|
11692
16129
|
messageAckStyles,
|
|
11693
16130
|
messageBubbleStyles,
|
|
11694
16131
|
messageComposerStyles,
|
|
11695
16132
|
messageVariablesPickerStyles,
|
|
16133
|
+
monthBounds,
|
|
16134
|
+
monthMatrixRange,
|
|
11696
16135
|
numberColumn,
|
|
16136
|
+
packLanes,
|
|
11697
16137
|
p3 as pageHeaderStyles,
|
|
11698
16138
|
ak as panelContainer,
|
|
11699
16139
|
phoneColumn,
|
|
11700
16140
|
p as presetView,
|
|
16141
|
+
rowHeights,
|
|
16142
|
+
rowOffsets,
|
|
11701
16143
|
al as savedViewsMockService,
|
|
11702
16144
|
x2 as shouldPreventNavigation,
|
|
16145
|
+
snapDate,
|
|
11703
16146
|
statusColumn,
|
|
11704
16147
|
tabsNavigationAcao,
|
|
11705
16148
|
tabsNavigationAcoes,
|
|
@@ -11712,6 +16155,7 @@ export {
|
|
|
11712
16155
|
textColumn,
|
|
11713
16156
|
toast,
|
|
11714
16157
|
toastVariants,
|
|
16158
|
+
topoSort,
|
|
11715
16159
|
useBrand,
|
|
11716
16160
|
useChart,
|
|
11717
16161
|
am as useColumnResize,
|
|
@@ -11723,6 +16167,7 @@ export {
|
|
|
11723
16167
|
u2 as useTheme,
|
|
11724
16168
|
useToolbarFilterControl,
|
|
11725
16169
|
useToolbarFilters,
|
|
11726
|
-
useToolbarSort
|
|
16170
|
+
useToolbarSort,
|
|
16171
|
+
xToDate
|
|
11727
16172
|
};
|
|
11728
16173
|
//# sourceMappingURL=index.mjs.map
|