@snksergio/design-system 0.55.0 → 0.57.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.
Files changed (43) hide show
  1. package/dist-lib/index.cjs +2597 -3
  2. package/dist-lib/index.cjs.map +1 -1
  3. package/dist-lib/index.mjs +2631 -37
  4. package/dist-lib/index.mjs.map +1 -1
  5. package/dist-lib/src/components/index.d.ts +1 -0
  6. package/dist-lib/src/components/index.d.ts.map +1 -1
  7. package/dist-lib/src/components/ui/Scheduler/hooks/layout.d.ts +165 -0
  8. package/dist-lib/src/components/ui/Scheduler/hooks/layout.d.ts.map +1 -0
  9. package/dist-lib/src/components/ui/Scheduler/hooks/use-media-query.d.ts +21 -0
  10. package/dist-lib/src/components/ui/Scheduler/hooks/use-media-query.d.ts.map +1 -0
  11. package/dist-lib/src/components/ui/Scheduler/hooks/use-scheduler-dnd.d.ts +41 -0
  12. package/dist-lib/src/components/ui/Scheduler/hooks/use-scheduler-dnd.d.ts.map +1 -0
  13. package/dist-lib/src/components/ui/Scheduler/hooks/use-scheduler-filter.d.ts +35 -0
  14. package/dist-lib/src/components/ui/Scheduler/hooks/use-scheduler-filter.d.ts.map +1 -0
  15. package/dist-lib/src/components/ui/Scheduler/hooks/use-scheduler-keyboard.d.ts +47 -0
  16. package/dist-lib/src/components/ui/Scheduler/hooks/use-scheduler-keyboard.d.ts.map +1 -0
  17. package/dist-lib/src/components/ui/Scheduler/hooks/use-scheduler-state.d.ts +44 -0
  18. package/dist-lib/src/components/ui/Scheduler/hooks/use-scheduler-state.d.ts.map +1 -0
  19. package/dist-lib/src/components/ui/Scheduler/index.d.ts +14 -0
  20. package/dist-lib/src/components/ui/Scheduler/index.d.ts.map +1 -0
  21. package/dist-lib/src/components/ui/Scheduler/parts/draggable-event.d.ts +30 -0
  22. package/dist-lib/src/components/ui/Scheduler/parts/draggable-event.d.ts.map +1 -0
  23. package/dist-lib/src/components/ui/Scheduler/parts/mini-month.d.ts +28 -0
  24. package/dist-lib/src/components/ui/Scheduler/parts/mini-month.d.ts.map +1 -0
  25. package/dist-lib/src/components/ui/Scheduler/parts/scheduler-event.d.ts +53 -0
  26. package/dist-lib/src/components/ui/Scheduler/parts/scheduler-event.d.ts.map +1 -0
  27. package/dist-lib/src/components/ui/Scheduler/parts/scheduler-filter-panel.d.ts +54 -0
  28. package/dist-lib/src/components/ui/Scheduler/parts/scheduler-filter-panel.d.ts.map +1 -0
  29. package/dist-lib/src/components/ui/Scheduler/parts/scheduler-toolbar.d.ts +25 -0
  30. package/dist-lib/src/components/ui/Scheduler/parts/scheduler-toolbar.d.ts.map +1 -0
  31. package/dist-lib/src/components/ui/Scheduler/scheduler.d.ts +44 -0
  32. package/dist-lib/src/components/ui/Scheduler/scheduler.d.ts.map +1 -0
  33. package/dist-lib/src/components/ui/Scheduler/scheduler.styles.d.ts +911 -0
  34. package/dist-lib/src/components/ui/Scheduler/scheduler.styles.d.ts.map +1 -0
  35. package/dist-lib/src/components/ui/Scheduler/scheduler.types.d.ts +285 -0
  36. package/dist-lib/src/components/ui/Scheduler/scheduler.types.d.ts.map +1 -0
  37. package/dist-lib/src/components/ui/Scheduler/views/list.d.ts +33 -0
  38. package/dist-lib/src/components/ui/Scheduler/views/list.d.ts.map +1 -0
  39. package/dist-lib/src/components/ui/Scheduler/views/month.d.ts +47 -0
  40. package/dist-lib/src/components/ui/Scheduler/views/month.d.ts.map +1 -0
  41. package/dist-lib/src/components/ui/Scheduler/views/time-grid.d.ts +48 -0
  42. package/dist-lib/src/components/ui/Scheduler/views/time-grid.d.ts.map +1 -0
  43. package/package.json +1 -1
@@ -1,22 +1,24 @@
1
- import { g as getContrastTextColor, a as FormField, e as Avatar, i as isFilterEntryActive, f as FilterPopover, T as ToolbarToolButton, h as ButtonGroup, j as ToolbarSimpleFilterDrawer, k as TableToolbar, l as ToolbarActions, m as ToolbarFilterButton, n as ToolbarSearch, o as ToolbarApplied, q as ToolbarTabs, r as columnTypeRegistry, L as ListItem, s as List } from "./chunks/panel-C3puLY_V.mjs";
2
- import { t, A, u, B, v, C, w, x, y, D, z, E, G, H, I, J, K, M, N, d, O, Q, R, S, U, F, b, V, c, W, X, P, Y, Z, _, $, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af, ag, ah, ai, aj, ak, p, al, am, an, ao, ap } from "./chunks/panel-C3puLY_V.mjs";
1
+ import { g as getContrastTextColor, a as FormField, e as Avatar, i as isFilterEntryActive, f as FilterPopover, T as ToolbarToolButton, h as ButtonGroup, j as ToolbarSimpleFilterDrawer, d as FloatingPanel, k as TableToolbar, l as ToolbarActions, m as ToolbarFilterButton, n as ToolbarSearch, o as ToolbarApplied, q as ToolbarTabs, r as columnTypeRegistry, L as ListItem, s as List$1 } from "./chunks/panel-C3puLY_V.mjs";
2
+ import { t, A, u, B, v, C, w, x, y, D, z, E, G, H, I, J, K, M, N, O, Q, R, S, U, F, b, V, c, W, X, P, Y, Z, _, $, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af, ag, ah, ai, aj, ak, p, al, am, an, ao, ap } from "./chunks/panel-C3puLY_V.mjs";
3
3
  import { C as Chip } from "./chunks/useTheme-caPM-I73.mjs";
4
- import { A as A2, B as B2, c as c2, a, b as b2, H as H2, d as d2, e, f, g, h, M as M2, S as S2, i, j, k, l, m, n, o, U as U2, p as p2, q, r, s, t as t2, v as v2, w as w2, x as x2, y as y2, z as z2, u as u2 } from "./chunks/useTheme-caPM-I73.mjs";
5
- import { af as tv, c as cn, a0 as Popover, a4 as PopoverTrigger, a3 as PopoverContent, ae as Button, D as Dialog, G as DialogTrigger, v as DialogContent, F as DialogTitle, a1 as PopoverAnchor, u as DialogClose, w as DialogDescription, H as DropdownMenu, V as DropdownMenuTrigger, J as DropdownMenuContent, M as DropdownMenuLabel, Q as DropdownMenuSeparator, L as DropdownMenuItem } from "./chunks/avatar-CZRLkTxt.mjs";
6
- import { d as d3, e as e2, f as f2, g as g2, h as h2, i as i2, W as W2, X as X2, Y as Y2, Z as Z2, _ as _2, $ as $2, I as I2, O as O2, P as P2, ag as ag2, ad as ad2 } from "./chunks/avatar-CZRLkTxt.mjs";
4
+ import { A as A2, B as B2, c as c2, a, b as b2, H as H2, d, e, f, g, h, M as M2, S as S2, i, j, k, l, m, n, o, U as U2, p as p2, q, r, s, t as t2, v as v2, w as w2, x as x2, y as y2, z as z2, u as u2 } from "./chunks/useTheme-caPM-I73.mjs";
5
+ import { af as tv, c as cn, a0 as Popover, a4 as PopoverTrigger, a3 as PopoverContent, ae as Button, D as Dialog, G as DialogTrigger, v as DialogContent, F as DialogTitle, a1 as PopoverAnchor, u as DialogClose, w as DialogDescription, H as DropdownMenu, V as DropdownMenuTrigger, J as DropdownMenuContent, O as DropdownMenuRadioGroup, P as DropdownMenuRadioItem, M as DropdownMenuLabel, Q as DropdownMenuSeparator, L as DropdownMenuItem } from "./chunks/avatar-CZRLkTxt.mjs";
6
+ import { d as d2, e as e2, f as f2, g as g2, h as h2, i as i2, W as W2, X as X2, Y as Y2, Z as Z2, _ as _2, $ as $2, I as I2, ag as ag2, ad as ad2 } from "./chunks/avatar-CZRLkTxt.mjs";
7
7
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
8
8
  import * as React from "react";
9
- import { createContext, forwardRef, useContext, useId, useState, Fragment as Fragment$1, isValidElement, useCallback, useMemo, createElement, useRef, useEffect, useLayoutEffect, Children } from "react";
9
+ import { createContext, forwardRef, useContext, useId, useState, Fragment as Fragment$1, isValidElement, useCallback, useMemo, createElement, useRef, useEffect, useLayoutEffect, useImperativeHandle, Children } from "react";
10
10
  import { Q as Switch, l as Checkbox, C as Calendar, T as Textarea } from "./chunks/sheet-Bjjo4ZaT.mjs";
11
- import { G as G2, H as H3, F as F2, O as O3 } from "./chunks/sheet-Bjjo4ZaT.mjs";
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, Circle, Plus, 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, 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-Bfs_oVpq.mjs";
19
- import { P as P3, p as p3 } from "./chunks/page-header-t5hFIp7o.mjs";
19
+ import { P as P2, p as p3 } from "./chunks/page-header-t5hFIp7o.mjs";
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";
20
22
  import * as RechartsPrimitive from "recharts";
21
23
  import { useVirtualizer } from "@tanstack/react-virtual";
22
24
  import "@hello-pangea/dnd";
@@ -3861,7 +3863,7 @@ const Icon = forwardRef(
3861
3863
  ...props,
3862
3864
  children: [
3863
3865
  title2 ? /* @__PURE__ */ jsx("title", { children: title2 }) : null,
3864
- paths.map((d4, i4) => /* @__PURE__ */ jsx("path", { d: d4 }, i4))
3866
+ paths.map((d3, i4) => /* @__PURE__ */ jsx("path", { d: d3 }, i4))
3865
3867
  ]
3866
3868
  }
3867
3869
  );
@@ -5254,6 +5256,2596 @@ const MonthYearPicker = forwardRef(function MonthYearPicker2({
5254
5256
  ] });
5255
5257
  });
5256
5258
  MonthYearPicker.displayName = "MonthYearPicker";
5259
+ const schedulerRoot = tv({
5260
+ base: [
5261
+ "flex h-full min-h-0 w-full flex-col gap-gp-2xl",
5262
+ "text-fg-default"
5263
+ ]
5264
+ });
5265
+ const schedulerToolbar = tv({
5266
+ base: [
5267
+ "flex flex-col gap-gp-xl",
5268
+ "lg:flex-row lg:flex-wrap lg:items-center lg:justify-between"
5269
+ ]
5270
+ });
5271
+ const schedulerToolbarSide = tv({
5272
+ base: "flex min-w-0 items-center gap-gp-md",
5273
+ variants: {
5274
+ /**
5275
+ * `leading` = título + navegação. Empilhado, ocupa a linha inteira e
5276
+ * separa os dois extremos — é o que joga `‹ Hoje ›` pro fim.
5277
+ * `trailing` = busca + controles. A busca é quem estica (`flex-1` nela),
5278
+ * então aqui basta ocupar a linha.
5279
+ */
5280
+ slot: {
5281
+ leading: "w-full justify-between lg:w-auto lg:flex-wrap lg:justify-start",
5282
+ trailing: "w-full lg:w-auto lg:flex-wrap"
5283
+ }
5284
+ },
5285
+ defaultVariants: { slot: "trailing" }
5286
+ });
5287
+ const schedulerTitle = tv({
5288
+ base: [
5289
+ "min-w-0 truncate text-title-sm font-semibold text-fg-default",
5290
+ "first-letter:uppercase"
5291
+ ]
5292
+ });
5293
+ const schedulerNavGroup = tv({
5294
+ base: [
5295
+ "inline-flex shrink-0 items-center",
5296
+ "[&>*:not(:first-child)]:-ml-px",
5297
+ // O segmento com foco sobe pra que o anel não seja cortado pelo vizinho.
5298
+ "[&>*:focus-visible]:relative [&>*:focus-visible]:z-[1]",
5299
+ "[&>*:first-child]:!rounded-r-none",
5300
+ "[&>*:last-child]:!rounded-l-none",
5301
+ "[&>*:not(:first-child):not(:last-child)]:!rounded-none"
5302
+ ]
5303
+ });
5304
+ tv({
5305
+ base: "tabular-nums"
5306
+ });
5307
+ const schedulerViewMenuItem = tv({
5308
+ base: "flex items-center justify-between gap-gp-xl"
5309
+ });
5310
+ const schedulerFilterDot = tv({
5311
+ base: [
5312
+ "pointer-events-none absolute -right-[4px] -top-[4px] z-[2]",
5313
+ "size-[13px] rounded-radius-full",
5314
+ "bg-bg-brand border-2 border-bg-canvas"
5315
+ ]
5316
+ });
5317
+ const schedulerSearch = tv({
5318
+ base: [
5319
+ "relative flex cursor-text items-center gap-gp-md",
5320
+ "h-form-lg rounded-radius-lg px-pad-lg",
5321
+ "bg-bg-surface dark:bg-bg-muted",
5322
+ "border border-border-subtle dark:border-border-input",
5323
+ "shadow-sh-sm dark:shadow-sh-none",
5324
+ "text-fg-default",
5325
+ "min-w-0 flex-1 lg:flex-initial",
5326
+ "lg:w-[200px] lg:focus-within:w-[300px]",
5327
+ "transition-[width,border-color,background-color,box-shadow] duration-200",
5328
+ "focus-within:border-border-brand focus-within:shadow-sh-ring",
5329
+ "[&_svg]:size-icon-sm [&_svg]:shrink-0 [&_svg]:text-fg-muted"
5330
+ ],
5331
+ variants: {
5332
+ /** Mantém expandido mesmo sem foco quando já há termo digitado. */
5333
+ expanded: { true: "lg:w-[300px]", false: "" }
5334
+ },
5335
+ defaultVariants: { expanded: false }
5336
+ });
5337
+ const schedulerSearchInput = tv({
5338
+ base: [
5339
+ "min-w-0 flex-1 border-0 bg-transparent outline-none",
5340
+ "text-body-sm font-normal text-fg-default",
5341
+ "placeholder:text-fg-muted"
5342
+ ]
5343
+ });
5344
+ const schedulerFilterRow = tv({
5345
+ base: [
5346
+ "flex items-center gap-gp-md",
5347
+ "flex-nowrap overflow-x-auto sm:flex-wrap sm:overflow-x-visible",
5348
+ "[scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
5349
+ ]
5350
+ });
5351
+ const schedulerFilterChip = tv({
5352
+ base: [
5353
+ "inline-flex shrink-0 cursor-pointer items-center gap-gp-sm",
5354
+ "h-form-md rounded-radius-lg pl-pad-lg pr-pad-md",
5355
+ "bg-bg-surface dark:bg-bg-muted",
5356
+ "border text-body-xs font-normal text-fg-default",
5357
+ "transition-[background-color,border-color] duration-150",
5358
+ "hover:bg-bg-muted-hover hover:border-border-default dark:hover:bg-bg-accent",
5359
+ "focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand",
5360
+ "[&_svg]:size-icon-xs [&_svg]:shrink-0 [&_svg]:text-fg-subtle"
5361
+ ],
5362
+ variants: {
5363
+ applied: {
5364
+ true: "border-solid border-border-brand bg-bg-brand-subtle dark:bg-bg-brand-subtle",
5365
+ false: "border-dashed border-border-input"
5366
+ }
5367
+ },
5368
+ defaultVariants: { applied: false }
5369
+ });
5370
+ const schedulerFilterChipName = tv({
5371
+ base: "font-semibold text-fg-default"
5372
+ });
5373
+ const schedulerFilterChipValue = tv({
5374
+ base: [
5375
+ "inline-flex h-comp-2xs items-center px-pad-md",
5376
+ "rounded-radius-sm bg-bg-muted dark:bg-bg-accent",
5377
+ "text-body-xs font-medium text-fg-default"
5378
+ ]
5379
+ });
5380
+ const schedulerClearLink = tv({
5381
+ base: [
5382
+ "ml-pad-sm cursor-pointer border-0 bg-transparent p-0",
5383
+ "text-body-xs font-medium text-fg-brand outline-none",
5384
+ "underline-offset-2 transition-opacity duration-150",
5385
+ "hover:underline focus-visible:underline"
5386
+ ]
5387
+ });
5388
+ const schedulerBody = tv({
5389
+ base: "flex min-h-0 flex-1 gap-gp-2xl"
5390
+ });
5391
+ const schedulerMain = tv({
5392
+ base: "flex min-h-0 min-w-0 flex-1 flex-col"
5393
+ });
5394
+ const schedulerFilterAside = tv({
5395
+ base: [
5396
+ "flex min-h-0 flex-col overflow-y-auto",
5397
+ // `scrollbar-thin` é a barra do DS (26 usos no repo contra 2 do
5398
+ // `scrollbar-default`) — trilho transparente + thumb tokenizado.
5399
+ // ⚠️ É `@utility` do tema GERADO, não do globals.css: existe nos 4 canais.
5400
+ "scrollbar-thin"
5401
+ ],
5402
+ variants: {
5403
+ /**
5404
+ * `embedded` = o painel vive DENTRO de outro contêiner que já é a
5405
+ * superfície — hoje o `FloatingPanel` do modo drawer (<1024px).
5406
+ *
5407
+ * Como coluna (`false`, o default) ele É o card: largura própria, borda,
5408
+ * radius e fundo. Embutido, os quatro viram defeito — largura fixa deixa
5409
+ * uma faixa morta à direita do drawer, e borda+radius+fundo repetem a
5410
+ * superfície do painel que já o embrulha (card dentro de card, a mesma
5411
+ * classe de erro da L-050).
5412
+ */
5413
+ embedded: {
5414
+ false: [
5415
+ "w-[280px] shrink-0",
5416
+ "rounded-radius-xl border border-border-default bg-bg-surface"
5417
+ ],
5418
+ true: "w-full"
5419
+ }
5420
+ },
5421
+ defaultVariants: { embedded: false }
5422
+ });
5423
+ const schedulerAsideSection = tv({
5424
+ base: [
5425
+ "flex shrink-0 flex-col gap-gp-md",
5426
+ "border-b border-border-default p-sp-xl last:border-b-0"
5427
+ ],
5428
+ variants: {
5429
+ /** O cabeçalho é mais raso: só uma linha de título + ações. */
5430
+ compact: { true: "py-pad-lg", false: "" },
5431
+ /**
5432
+ * Cabeçalho fixo no topo enquanto o resto rola.
5433
+ *
5434
+ * `bg-bg-surface` é **obrigatório** e não decoração: `sticky` não cria
5435
+ * fundo, então sem ele o mini-calendário passaria por baixo do título. E
5436
+ * `z-[1]` porque as caixas e o `Checkbox` do Radix criam contexto próprio
5437
+ * de empilhamento — sem o z, o conteúdo que rola sobrepõe o cabeçalho.
5438
+ */
5439
+ sticky: { true: "sticky top-0 z-[1] bg-bg-surface", false: "" }
5440
+ },
5441
+ defaultVariants: { compact: false, sticky: false }
5442
+ });
5443
+ const schedulerAsideHead = tv({
5444
+ base: "flex items-center justify-between gap-gp-md"
5445
+ });
5446
+ const schedulerAsideTitle = tv({
5447
+ base: "text-body-sm font-semibold text-fg-default"
5448
+ });
5449
+ const schedulerMiniHead = tv({
5450
+ base: "flex items-center justify-between gap-gp-sm"
5451
+ });
5452
+ const schedulerMiniTitle = tv({
5453
+ base: "min-w-0 truncate text-body-sm font-semibold text-fg-default first-letter:uppercase"
5454
+ });
5455
+ const schedulerMiniGrid = tv({
5456
+ base: "grid grid-cols-7 gap-gp-2xs"
5457
+ });
5458
+ const schedulerMiniWeekday = tv({
5459
+ base: [
5460
+ "grid size-comp-xs place-items-center",
5461
+ "text-caption-xs font-medium uppercase text-fg-subtle"
5462
+ ]
5463
+ });
5464
+ const schedulerMiniDay = tv({
5465
+ base: [
5466
+ "grid size-comp-xs cursor-pointer place-items-center rounded-radius-full",
5467
+ "border-0 bg-transparent outline-none",
5468
+ "text-caption-md tabular-nums",
5469
+ "transition-[background-color,color,box-shadow] duration-150",
5470
+ "hover:bg-bg-muted",
5471
+ "focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand"
5472
+ ],
5473
+ variants: {
5474
+ outside: { true: "text-fg-subtle", false: "text-fg-default" },
5475
+ hasEvents: { true: "font-semibold", false: "" },
5476
+ today: { true: "ring-1 ring-border-brand", false: "" },
5477
+ selected: {
5478
+ true: "bg-bg-brand font-semibold text-fg-on-brand hover:bg-bg-brand-hover",
5479
+ false: ""
5480
+ }
5481
+ },
5482
+ compoundVariants: [
5483
+ // Selecionado vence "hoje": um anel em volta do disco cheio não informa
5484
+ // nada e adiciona 2px de ruído.
5485
+ { selected: true, today: true, class: "ring-0" },
5486
+ { selected: true, outside: true, class: "text-fg-on-brand" }
5487
+ ],
5488
+ defaultVariants: {
5489
+ outside: false,
5490
+ hasEvents: false,
5491
+ today: false,
5492
+ selected: false
5493
+ }
5494
+ });
5495
+ const schedulerGroup = tv({
5496
+ base: "flex flex-col gap-gp-sm"
5497
+ });
5498
+ const schedulerGroupHead = tv({
5499
+ base: [
5500
+ "flex w-full cursor-pointer items-center justify-between gap-gp-md",
5501
+ "min-h-form-sm rounded-radius-sm border-0 bg-transparent -mx-pad-sm px-pad-sm outline-none",
5502
+ "text-body-sm font-semibold text-fg-default",
5503
+ "transition-colors duration-150",
5504
+ "hover:bg-bg-muted",
5505
+ "focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand",
5506
+ "[&_svg]:size-icon-sm [&_svg]:shrink-0 [&_svg]:text-fg-subtle"
5507
+ ]
5508
+ });
5509
+ const schedulerGroupChevron = tv({
5510
+ base: "transition-transform duration-150",
5511
+ variants: { open: { true: "", false: "-rotate-90" } },
5512
+ defaultVariants: { open: true }
5513
+ });
5514
+ const schedulerOption = tv({
5515
+ base: [
5516
+ "flex cursor-pointer items-center gap-gp-md",
5517
+ "min-h-form-sm rounded-radius-sm -mx-pad-sm px-pad-sm",
5518
+ "text-body-sm text-fg-default",
5519
+ "transition-colors duration-150",
5520
+ "hover:bg-bg-muted",
5521
+ "has-[:focus-visible]:ring-4 has-[:focus-visible]:ring-ring-brand"
5522
+ ]
5523
+ });
5524
+ const schedulerOptionLabel = tv({
5525
+ base: "min-w-0 flex-1 truncate"
5526
+ });
5527
+ const schedulerOptionCount = tv({
5528
+ base: "shrink-0 text-caption-sm tabular-nums text-fg-subtle"
5529
+ });
5530
+ const schedulerOptionBox = tv({
5531
+ base: "shrink-0",
5532
+ variants: {
5533
+ color: {
5534
+ brand: "data-[state=checked]:bg-bg-brand data-[state=checked]:border-border-brand [&_svg]:text-fg-on-brand",
5535
+ info: "data-[state=checked]:bg-bg-info data-[state=checked]:border-bg-info [&_svg]:text-fg-on-info",
5536
+ success: "data-[state=checked]:bg-bg-success data-[state=checked]:border-bg-success [&_svg]:text-fg-on-success",
5537
+ warning: "data-[state=checked]:bg-bg-warning data-[state=checked]:border-bg-warning [&_svg]:text-fg-on-warning",
5538
+ danger: "data-[state=checked]:bg-bg-danger data-[state=checked]:border-bg-danger [&_svg]:text-fg-on-danger",
5539
+ neutral: "data-[state=checked]:bg-fg-subtle data-[state=checked]:border-fg-subtle [&_svg]:text-bg-surface"
5540
+ }
5541
+ },
5542
+ defaultVariants: { color: "brand" }
5543
+ });
5544
+ const schedulerMonthFrame = tv({
5545
+ base: [
5546
+ "flex min-h-0 flex-1 flex-col overflow-hidden",
5547
+ "rounded-radius-xl border border-border-default bg-bg-surface"
5548
+ ]
5549
+ });
5550
+ const schedulerWeekdayRow = tv({
5551
+ base: [
5552
+ "grid grid-cols-7 border-b border-border-default bg-bg-subtle dark:bg-bg-canvas"
5553
+ ]
5554
+ });
5555
+ const schedulerWeekdayCell = tv({
5556
+ base: [
5557
+ "flex min-h-form-md items-center justify-center",
5558
+ "text-caption-md font-medium text-fg-muted",
5559
+ "first-letter:uppercase"
5560
+ ]
5561
+ });
5562
+ const schedulerMonthGrid = tv({
5563
+ base: "grid min-h-0 flex-1 grid-cols-7 grid-rows-6"
5564
+ });
5565
+ const schedulerMonthCell = tv({
5566
+ base: [
5567
+ "group/cell relative flex min-h-comp-4xl min-w-0 flex-col gap-gp-2xs",
5568
+ "border-b border-r border-border-subtle p-sp-2xs",
5569
+ "[&:nth-child(7n)]:border-r-0",
5570
+ "transition-colors duration-150"
5571
+ ],
5572
+ variants: {
5573
+ /** Dia de mês vizinho — recuado, não escondido: a grade é sempre 6×7. */
5574
+ outside: {
5575
+ true: "bg-bg-subtle/40 dark:bg-bg-canvas",
5576
+ false: "bg-bg-surface"
5577
+ },
5578
+ /** Última linha da grade não desenha borda inferior (o frame já fecha). */
5579
+ lastRow: { true: "border-b-0", false: "" },
5580
+ interactive: { true: "hover:bg-bg-table-row-hover", false: "" }
5581
+ },
5582
+ defaultVariants: { outside: false, lastRow: false, interactive: false }
5583
+ });
5584
+ const schedulerDayHead = tv({
5585
+ base: "flex shrink-0 items-center justify-between gap-gp-sm"
5586
+ });
5587
+ const schedulerDayNumber = tv({
5588
+ base: [
5589
+ "grid size-comp-xs shrink-0 place-items-center rounded-radius-full",
5590
+ "text-body-xs font-medium tabular-nums",
5591
+ "transition-colors duration-150"
5592
+ ],
5593
+ variants: {
5594
+ today: {
5595
+ true: "bg-bg-brand font-semibold text-fg-on-brand",
5596
+ false: "text-fg-default"
5597
+ },
5598
+ outside: { true: "text-fg-subtle", false: "" }
5599
+ },
5600
+ compoundVariants: [
5601
+ // `today` vence `outside`: o dia de hoje continua marcado mesmo quando
5602
+ // aparece como dia vizinho na grade de outro mês.
5603
+ { today: true, outside: true, class: "text-fg-on-brand" }
5604
+ ],
5605
+ defaultVariants: { today: false, outside: false }
5606
+ });
5607
+ const schedulerSlotAdd = tv({
5608
+ base: [
5609
+ "grid size-comp-3xs shrink-0 place-items-center rounded-radius-sm",
5610
+ "cursor-pointer border-0 bg-transparent text-fg-subtle outline-none",
5611
+ "opacity-0 transition-[opacity,background-color,color] duration-150",
5612
+ "group-hover/cell:opacity-100 focus-visible:opacity-100",
5613
+ "hover:bg-bg-muted hover:text-fg-default",
5614
+ "focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand",
5615
+ "[&_svg]:size-icon-xs"
5616
+ ]
5617
+ });
5618
+ const schedulerCellEvents = tv({
5619
+ base: "flex min-h-0 flex-col gap-gp-2xs overflow-hidden"
5620
+ });
5621
+ const schedulerOverflowButton = tv({
5622
+ base: [
5623
+ "flex min-h-comp-2xs w-full cursor-pointer items-center",
5624
+ "rounded-radius-sm border-0 bg-transparent px-pad-md",
5625
+ "text-caption-sm font-medium text-fg-muted outline-none",
5626
+ "transition-colors duration-150",
5627
+ "hover:bg-bg-muted hover:text-fg-default",
5628
+ "focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand"
5629
+ ]
5630
+ });
5631
+ const schedulerOverflowPanel = tv({
5632
+ base: "flex max-h-[320px] min-w-[240px] flex-col gap-gp-2xs overflow-y-auto p-sp-xs"
5633
+ });
5634
+ const schedulerOverflowPanelTitle = tv({
5635
+ base: "px-pad-md pb-pad-sm text-caption-sm font-semibold text-fg-muted first-letter:uppercase"
5636
+ });
5637
+ const schedulerTimeFrame = tv({
5638
+ base: [
5639
+ "flex min-h-0 flex-1 flex-col overflow-hidden",
5640
+ "rounded-radius-xl border border-border-default bg-bg-surface"
5641
+ ]
5642
+ });
5643
+ const schedulerTimeHead = tv({
5644
+ base: "flex shrink-0 border-b border-border-default bg-bg-subtle dark:bg-bg-canvas"
5645
+ });
5646
+ const schedulerTimeGutter = tv({
5647
+ base: "w-comp-4xl shrink-0"
5648
+ });
5649
+ const schedulerTimeHeadDay = tv({
5650
+ base: [
5651
+ "flex min-w-0 flex-1 flex-col items-center justify-center gap-gp-2xs",
5652
+ "border-l border-border-subtle py-pad-md"
5653
+ ],
5654
+ variants: {
5655
+ /** Sábado/domingo recuados — mesma leitura da grade do mês. */
5656
+ weekend: { true: "bg-bg-subtle/40 dark:bg-bg-canvas", false: "" }
5657
+ },
5658
+ defaultVariants: { weekend: false }
5659
+ });
5660
+ const schedulerTimeHeadWeekday = tv({
5661
+ base: "text-caption-md font-medium uppercase text-fg-muted"
5662
+ });
5663
+ const schedulerAllDayRow = tv({
5664
+ base: "flex shrink-0 border-b border-border-default"
5665
+ });
5666
+ const schedulerAllDayLabel = tv({
5667
+ base: [
5668
+ "flex w-comp-4xl shrink-0 items-start justify-end",
5669
+ "px-pad-md py-pad-sm text-caption-sm text-fg-subtle"
5670
+ ]
5671
+ });
5672
+ const schedulerAllDayCell = tv({
5673
+ base: [
5674
+ "flex min-w-0 flex-1 flex-col gap-gp-2xs",
5675
+ "border-l border-border-subtle p-sp-2xs"
5676
+ ]
5677
+ });
5678
+ const schedulerTimeBody = tv({
5679
+ base: "min-h-0 flex-1 overflow-y-auto overscroll-contain scrollbar-thin"
5680
+ });
5681
+ const schedulerTimeCanvas = tv({
5682
+ base: "relative flex"
5683
+ });
5684
+ const schedulerHourLabel = tv({
5685
+ base: [
5686
+ "relative flex h-comp-3xl items-start justify-end pr-pad-md",
5687
+ "text-caption-sm tabular-nums text-fg-subtle",
5688
+ "first:opacity-0"
5689
+ ]
5690
+ });
5691
+ const schedulerHourLabelText = tv({
5692
+ base: "-translate-y-1/2"
5693
+ });
5694
+ const schedulerTimeColumn = tv({
5695
+ base: "relative min-w-0 flex-1 border-l border-border-subtle",
5696
+ variants: {
5697
+ weekend: { true: "bg-bg-subtle/40 dark:bg-bg-canvas", false: "" }
5698
+ },
5699
+ defaultVariants: { weekend: false }
5700
+ });
5701
+ const schedulerHourSlot = tv({
5702
+ base: [
5703
+ "block h-comp-3xl w-full cursor-pointer",
5704
+ "border-b border-border-subtle bg-transparent p-0 outline-none",
5705
+ "transition-colors duration-150",
5706
+ "hover:bg-bg-table-row-hover",
5707
+ "focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand",
5708
+ "last:border-b-0"
5709
+ ]
5710
+ });
5711
+ const schedulerNowLine = tv({
5712
+ base: "pointer-events-none absolute left-0 right-0 z-[2] flex items-center"
5713
+ });
5714
+ const schedulerNowDot = tv({
5715
+ base: "-ml-[4px] size-[8px] shrink-0 rounded-radius-full bg-bg-brand"
5716
+ });
5717
+ const schedulerNowStroke = tv({
5718
+ base: "h-px flex-1 bg-bg-brand"
5719
+ });
5720
+ const schedulerNowLabel = tv({
5721
+ base: [
5722
+ "pointer-events-none absolute right-pad-md z-[2] -translate-y-1/2",
5723
+ "rounded-radius-xs bg-bg-brand px-pad-sm",
5724
+ "text-caption-xs font-semibold tabular-nums text-fg-on-brand"
5725
+ ]
5726
+ });
5727
+ const schedulerListFrame = tv({
5728
+ base: [
5729
+ "flex min-h-0 flex-1 flex-col overflow-y-auto overscroll-contain scrollbar-thin",
5730
+ "rounded-radius-xl border border-border-default bg-bg-surface"
5731
+ ]
5732
+ });
5733
+ const schedulerListDay = tv({
5734
+ base: "flex flex-col gap-gp-md border-b border-border-subtle p-sp-xl last:border-b-0"
5735
+ });
5736
+ const schedulerListDayHead = tv({
5737
+ base: [
5738
+ "sticky top-0 z-[1] -mx-sp-xl -mt-sp-xl px-sp-xl pb-pad-md pt-sp-xl",
5739
+ "flex items-baseline gap-gp-md bg-bg-surface"
5740
+ ]
5741
+ });
5742
+ const schedulerListDayNumber = tv({
5743
+ base: "text-title-sm font-semibold tabular-nums",
5744
+ variants: {
5745
+ today: { true: "text-fg-brand", false: "text-fg-default" }
5746
+ },
5747
+ defaultVariants: { today: false }
5748
+ });
5749
+ const schedulerListDayName = tv({
5750
+ base: "text-body-sm text-fg-muted first-letter:uppercase"
5751
+ });
5752
+ const schedulerListEvents = tv({
5753
+ base: "flex flex-col gap-gp-sm"
5754
+ });
5755
+ const schedulerEvent = tv({
5756
+ base: [
5757
+ "group/event relative flex min-w-0 cursor-pointer items-center gap-gp-sm",
5758
+ "border text-left outline-none",
5759
+ "transition-[background-color,border-color,box-shadow] duration-150",
5760
+ "focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand"
5761
+ ],
5762
+ variants: {
5763
+ color: {
5764
+ brand: "border-border-brand-subtle bg-bg-brand-subtle hover:bg-bg-brand-subtle-hover",
5765
+ info: "border-border-info-muted bg-bg-info-muted hover:bg-bg-info-muted-hover",
5766
+ success: "border-border-success-muted bg-bg-success-muted hover:bg-bg-success-muted-hover",
5767
+ warning: "border-border-warning-muted bg-bg-warning-muted hover:bg-bg-warning-muted-hover",
5768
+ danger: "border-border-danger-muted bg-bg-danger-muted hover:bg-bg-danger-muted-hover",
5769
+ neutral: "border-border-default bg-bg-muted hover:bg-bg-muted-hover"
5770
+ },
5771
+ variant: {
5772
+ /** Mês: linha de 20px, uma só, sem quebra. */
5773
+ pill: "min-h-comp-2xs rounded-radius-sm px-pad-md text-caption-sm",
5774
+ /** Week/day: bloco absoluto que ocupa a duração. */
5775
+ block: "absolute min-h-comp-3xs flex-col items-start gap-0 rounded-radius-md px-pad-md py-pad-sm text-caption-sm",
5776
+ /** List/agenda: linha larga com descrição. */
5777
+ row: "min-h-form-lg w-full rounded-radius-lg px-pad-xl py-pad-md text-body-sm"
5778
+ },
5779
+ /** Continuação de evento multi-dia: perde o canto do lado truncado. */
5780
+ truncateStart: { true: "rounded-l-none border-l-0", false: "" },
5781
+ truncateEnd: { true: "rounded-r-none border-r-0", false: "" },
5782
+ /**
5783
+ * Enquanto arrasta: sombra alta pra o bloco "sair da superfície", z acima
5784
+ * dos vizinhos, e `cursor-grabbing`.
5785
+ *
5786
+ * ⚠️ **Sem `transition` neste estado.** O `transform` do dnd-kit é atualizado
5787
+ * a cada movimento do ponteiro; uma transição em cima dele faz o bloco
5788
+ * perseguir o cursor com atraso — o gesto fica elástico e impreciso. A
5789
+ * transição vive no estado NÃO-arrastando (`movable`), que é onde ela serve:
5790
+ * o assentamento depois do soltar.
5791
+ */
5792
+ dragging: {
5793
+ true: "z-[3] cursor-grabbing shadow-sh-lg opacity-90 transition-none",
5794
+ false: ""
5795
+ },
5796
+ /** Arrastável e em repouso: mão aberta + transição de assentamento. */
5797
+ movable: {
5798
+ true: "cursor-grab transition-[top,left,height,width,background-color,border-color,box-shadow] duration-200 ease-out",
5799
+ false: ""
5800
+ },
5801
+ disabled: { true: "pointer-events-none opacity-50", false: "" }
5802
+ },
5803
+ defaultVariants: {
5804
+ color: "brand",
5805
+ variant: "pill",
5806
+ truncateStart: false,
5807
+ truncateEnd: false,
5808
+ dragging: false,
5809
+ movable: false,
5810
+ disabled: false
5811
+ }
5812
+ });
5813
+ const schedulerResizeHandle = tv({
5814
+ base: [
5815
+ "absolute inset-x-0 z-[1] h-[6px] cursor-ns-resize",
5816
+ "opacity-0 transition-opacity duration-150",
5817
+ "group-hover/event:opacity-100 focus-visible:opacity-100",
5818
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring-brand",
5819
+ // A barrinha só aparece no hover; o alvo de 6px existe sempre.
5820
+ "after:absolute after:inset-x-[25%] after:top-[2px] after:h-[2px]",
5821
+ "after:rounded-radius-full after:bg-fg-default/40"
5822
+ ],
5823
+ variants: {
5824
+ edge: {
5825
+ start: "-top-[3px]",
5826
+ end: "-bottom-[3px]"
5827
+ }
5828
+ },
5829
+ defaultVariants: { edge: "end" }
5830
+ });
5831
+ const schedulerDropTarget = tv({
5832
+ base: "bg-bg-brand-subtle shadow-[inset_0_0_0_2px_var(--color-border-brand)]"
5833
+ });
5834
+ const schedulerEventDot = tv({
5835
+ base: "shrink-0 rounded-radius-full",
5836
+ variants: {
5837
+ color: {
5838
+ brand: "bg-bg-brand",
5839
+ info: "bg-bg-info",
5840
+ success: "bg-bg-success",
5841
+ warning: "bg-bg-warning",
5842
+ danger: "bg-bg-danger",
5843
+ neutral: "bg-fg-subtle"
5844
+ },
5845
+ size: {
5846
+ sm: "size-icon-2xs",
5847
+ md: "size-icon-xs"
5848
+ }
5849
+ },
5850
+ defaultVariants: { color: "brand", size: "sm" }
5851
+ });
5852
+ const schedulerEventTitle = tv({
5853
+ base: "min-w-0 flex-1 truncate font-medium text-fg-default"
5854
+ });
5855
+ const schedulerEventTime = tv({
5856
+ base: "shrink-0 tabular-nums text-fg-muted"
5857
+ });
5858
+ const schedulerEventDescription = tv({
5859
+ base: "min-w-0 truncate text-caption-sm text-fg-muted"
5860
+ });
5861
+ const schedulerPlaceholder = tv({
5862
+ base: [
5863
+ "flex min-h-[320px] flex-1 flex-col items-center justify-center gap-gp-md",
5864
+ "rounded-radius-xl border border-dashed border-border-input bg-bg-subtle dark:bg-bg-canvas",
5865
+ "p-sp-3xl text-center"
5866
+ ]
5867
+ });
5868
+ const VIEW_ITEMS = [
5869
+ { value: "month", label: "Mês", icon: /* @__PURE__ */ jsx(CalendarDays, {}) },
5870
+ { value: "week", label: "Semana", icon: /* @__PURE__ */ jsx(Columns3, {}) },
5871
+ { value: "day", label: "Dia", icon: /* @__PURE__ */ jsx(Square, {}) },
5872
+ { value: "list", label: "Lista", icon: /* @__PURE__ */ jsx(List, {}) }
5873
+ ];
5874
+ function SchedulerToolbar({
5875
+ title: title2,
5876
+ view,
5877
+ onViewChange,
5878
+ onPrev,
5879
+ onNext,
5880
+ onToday,
5881
+ searchable,
5882
+ search,
5883
+ onSearchChange,
5884
+ filterFields,
5885
+ filterModel,
5886
+ onClearField,
5887
+ onClearAll,
5888
+ appliedCount,
5889
+ filterPanelOpen,
5890
+ onToggleFilterPanel,
5891
+ toolbarActions,
5892
+ primaryAction
5893
+ }) {
5894
+ const hasFilters = (filterFields?.length ?? 0) > 0;
5895
+ const activeView = VIEW_ITEMS.find((v3) => v3.value === view) ?? VIEW_ITEMS[0];
5896
+ const filterEngaged = filterPanelOpen || appliedCount > 0;
5897
+ const appliedFields = (filterFields ?? []).filter(
5898
+ (field) => (filterModel[field.id] ?? []).length > 0
5899
+ );
5900
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-gp-xl", children: [
5901
+ /* @__PURE__ */ jsxs("div", { className: schedulerToolbar(), children: [
5902
+ /* @__PURE__ */ jsxs("div", { className: schedulerToolbarSide({ slot: "leading" }), children: [
5903
+ /* @__PURE__ */ jsx("span", { className: schedulerTitle(), children: title2 }),
5904
+ /* @__PURE__ */ jsxs("div", { role: "group", "aria-label": "Navegação de período", className: schedulerNavGroup(), children: [
5905
+ /* @__PURE__ */ jsx(
5906
+ Button,
5907
+ {
5908
+ variant: "outline",
5909
+ color: "secondary",
5910
+ size: "icon-sm",
5911
+ onClick: onPrev,
5912
+ "aria-label": "Período anterior",
5913
+ children: /* @__PURE__ */ jsx(ChevronLeft, {})
5914
+ }
5915
+ ),
5916
+ /* @__PURE__ */ jsx(Button, { variant: "outline", color: "secondary", size: "sm", onClick: onToday, children: "Hoje" }),
5917
+ /* @__PURE__ */ jsx(
5918
+ Button,
5919
+ {
5920
+ variant: "outline",
5921
+ color: "secondary",
5922
+ size: "icon-sm",
5923
+ onClick: onNext,
5924
+ "aria-label": "Próximo período",
5925
+ children: /* @__PURE__ */ jsx(ChevronRight, {})
5926
+ }
5927
+ )
5928
+ ] })
5929
+ ] }),
5930
+ /* @__PURE__ */ jsxs("div", { className: schedulerToolbarSide({ slot: "trailing" }), children: [
5931
+ searchable ? /* @__PURE__ */ jsxs("label", { className: schedulerSearch({ expanded: search !== "" }), children: [
5932
+ /* @__PURE__ */ jsx(Search, { "aria-hidden": "true" }),
5933
+ /* @__PURE__ */ jsx(
5934
+ "input",
5935
+ {
5936
+ type: "search",
5937
+ value: search,
5938
+ onChange: (e3) => onSearchChange(e3.target.value),
5939
+ placeholder: "Buscar evento…",
5940
+ "aria-label": "Buscar evento",
5941
+ className: schedulerSearchInput()
5942
+ }
5943
+ )
5944
+ ] }) : null,
5945
+ toolbarActions,
5946
+ /* @__PURE__ */ jsxs(DropdownMenu, { children: [
5947
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
5948
+ Button,
5949
+ {
5950
+ variant: "outline",
5951
+ color: "secondary",
5952
+ size: "sm",
5953
+ iconLeft: activeView.icon,
5954
+ iconRight: /* @__PURE__ */ jsx(ChevronDown, { className: "hidden lg:block" }),
5955
+ "aria-label": `Visualização: ${activeView.label}`,
5956
+ className: "shrink-0 max-lg:size-form-xl max-lg:p-0",
5957
+ children: /* @__PURE__ */ jsx("span", { className: "hidden lg:inline", children: activeView.label })
5958
+ }
5959
+ ) }),
5960
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", className: "min-w-[168px]", children: /* @__PURE__ */ jsx(
5961
+ DropdownMenuRadioGroup,
5962
+ {
5963
+ value: view,
5964
+ 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))
5966
+ }
5967
+ ) })
5968
+ ] }),
5969
+ hasFilters ? (
5970
+ /* `relative` no wrapper e não no Button: o ponto é posicionado
5971
+ contra a caixa do botão, e o Button não expõe `position`. */
5972
+ /* @__PURE__ */ jsxs("span", { className: "relative shrink-0", children: [
5973
+ /* @__PURE__ */ jsx(
5974
+ Button,
5975
+ {
5976
+ variant: filterEngaged ? "soft" : "outline",
5977
+ color: filterEngaged ? "primary" : "secondary",
5978
+ size: "sm",
5979
+ iconLeft: /* @__PURE__ */ jsx(ListFilter, {}),
5980
+ onClick: onToggleFilterPanel,
5981
+ "aria-expanded": filterPanelOpen,
5982
+ className: cn(
5983
+ "max-lg:size-form-xl max-lg:p-0",
5984
+ filterEngaged && "border-border-brand hover:border-border-brand"
5985
+ ),
5986
+ "aria-label": "Filtro",
5987
+ children: /* @__PURE__ */ jsx("span", { className: "hidden lg:inline", children: "Filtro" })
5988
+ }
5989
+ ),
5990
+ appliedCount > 0 ? /* @__PURE__ */ jsx("span", { className: schedulerFilterDot(), "aria-hidden": "true" }) : null
5991
+ ] })
5992
+ ) : null,
5993
+ primaryAction
5994
+ ] })
5995
+ ] }),
5996
+ appliedFields.length > 0 ? /* @__PURE__ */ jsxs("div", { className: schedulerFilterRow(), children: [
5997
+ appliedFields.map((field) => {
5998
+ const selected = filterModel[field.id] ?? [];
5999
+ const selectedLabels = field.options.filter((o2) => selected.includes(o2.value)).map((o2) => o2.label);
6000
+ return /* @__PURE__ */ jsxs("span", { className: schedulerFilterChip({ applied: true }), children: [
6001
+ /* @__PURE__ */ jsx("span", { className: schedulerFilterChipName(), children: field.label }),
6002
+ /* @__PURE__ */ jsx("span", { className: schedulerFilterChipValue(), children: selectedLabels.length === 1 ? selectedLabels[0] : `${selectedLabels.length} selecionados` }),
6003
+ /* @__PURE__ */ jsx(
6004
+ "button",
6005
+ {
6006
+ type: "button",
6007
+ "aria-label": `Limpar filtro ${field.label}`,
6008
+ onClick: () => onClearField(field.id),
6009
+ className: "grid size-icon-sm shrink-0 place-items-center rounded-radius-full text-fg-subtle transition-colors duration-150 hover:text-fg-default focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring-brand",
6010
+ children: /* @__PURE__ */ jsx(X3, { "aria-hidden": "true" })
6011
+ }
6012
+ )
6013
+ ] }, field.id);
6014
+ }),
6015
+ /* @__PURE__ */ jsx(
6016
+ "button",
6017
+ {
6018
+ type: "button",
6019
+ onClick: onClearAll,
6020
+ className: cn(schedulerClearLink(), "shrink-0"),
6021
+ children: "Limpar tudo"
6022
+ }
6023
+ )
6024
+ ] }) : null
6025
+ ] });
6026
+ }
6027
+ function buildMonthMatrix(date, weekStartsOn, locale) {
6028
+ const monthStart = startOfMonth(date);
6029
+ const gridStart = startOfWeek(monthStart, { weekStartsOn, locale });
6030
+ const days = [];
6031
+ for (let i4 = 0; i4 < 42; i4++) {
6032
+ days.push(startOfDay(addDays(gridStart, i4)));
6033
+ }
6034
+ const weeks = [];
6035
+ for (let row = 0; row < 6; row++) {
6036
+ weeks.push(days.slice(row * 7, row * 7 + 7));
6037
+ }
6038
+ return weeks;
6039
+ }
6040
+ function segmentMultiDay(events, weekRows) {
6041
+ const segments = [];
6042
+ for (const event of events) {
6043
+ const dayStart = startOfDay(event.start);
6044
+ const dayEnd = startOfDay(event.end);
6045
+ weekRows.forEach((row, weekIndex) => {
6046
+ if (row.length === 0) return;
6047
+ const rowStart = row[0];
6048
+ const rowEnd = row[row.length - 1];
6049
+ if (dayEnd.getTime() < rowStart.getTime() || dayStart.getTime() > rowEnd.getTime()) {
6050
+ return;
6051
+ }
6052
+ const segStart = dayStart.getTime() > rowStart.getTime() ? dayStart : rowStart;
6053
+ const segEnd = dayEnd.getTime() < rowEnd.getTime() ? dayEnd : rowEnd;
6054
+ const colStart = differenceInCalendarDays(segStart, rowStart);
6055
+ const colSpan = differenceInCalendarDays(segEnd, segStart) + 1;
6056
+ segments.push({
6057
+ event,
6058
+ weekIndex,
6059
+ colStart,
6060
+ colSpan,
6061
+ isStart: segStart.getTime() === dayStart.getTime(),
6062
+ isEnd: segEnd.getTime() === dayEnd.getTime()
6063
+ });
6064
+ });
6065
+ }
6066
+ return segments;
6067
+ }
6068
+ function effectiveEndMs(event, snapMinutes) {
6069
+ return event.start.getTime() === event.end.getTime() ? event.end.getTime() + snapMinutes * 6e4 : event.end.getTime();
6070
+ }
6071
+ function packLanes(events, snapMinutes) {
6072
+ if (events.length === 0) return [];
6073
+ const sorted = [...events].sort((a11, b4) => {
6074
+ const startDiff = a11.start.getTime() - b4.start.getTime();
6075
+ if (startDiff !== 0) return startDiff;
6076
+ return effectiveEndMs(b4, snapMinutes) - effectiveEndMs(a11, snapMinutes);
6077
+ });
6078
+ const result = [];
6079
+ let group = [];
6080
+ let groupMaxEnd = -Infinity;
6081
+ const flushGroup = () => {
6082
+ if (group.length === 0) return;
6083
+ const laneEnds = [];
6084
+ const laneOf = /* @__PURE__ */ new Map();
6085
+ for (const ev of group) {
6086
+ const evEnd = effectiveEndMs(ev, snapMinutes);
6087
+ const freeLane = laneEnds.findIndex((end) => end <= ev.start.getTime());
6088
+ if (freeLane === -1) {
6089
+ laneOf.set(ev, laneEnds.length);
6090
+ laneEnds.push(evEnd);
6091
+ } else {
6092
+ laneOf.set(ev, freeLane);
6093
+ laneEnds[freeLane] = evEnd;
6094
+ }
6095
+ }
6096
+ const laneCount = laneEnds.length;
6097
+ for (const ev of group) {
6098
+ result.push({ event: ev, laneIndex: laneOf.get(ev), laneCount });
6099
+ }
6100
+ group = [];
6101
+ groupMaxEnd = -Infinity;
6102
+ };
6103
+ for (const ev of sorted) {
6104
+ const evStart = ev.start.getTime();
6105
+ if (group.length > 0 && evStart >= groupMaxEnd) {
6106
+ flushGroup();
6107
+ }
6108
+ group.push(ev);
6109
+ groupMaxEnd = Math.max(groupMaxEnd, effectiveEndMs(ev, snapMinutes));
6110
+ }
6111
+ flushGroup();
6112
+ return result;
6113
+ }
6114
+ const MONTH_PILL_HEIGHT_PX = 20;
6115
+ const MONTH_PILL_GAP_PX = 2;
6116
+ function computeOverflow(dayEvents, availableHeight) {
6117
+ if (dayEvents.length === 0) {
6118
+ return { visible: [], overflowCount: 0 };
6119
+ }
6120
+ const rawFit = Math.max(
6121
+ 0,
6122
+ Math.floor(
6123
+ (availableHeight + MONTH_PILL_GAP_PX) / (MONTH_PILL_HEIGHT_PX + MONTH_PILL_GAP_PX)
6124
+ )
6125
+ );
6126
+ if (rawFit >= dayEvents.length) {
6127
+ return { visible: dayEvents, overflowCount: 0 };
6128
+ }
6129
+ const visibleCount = Math.max(0, rawFit - 1);
6130
+ return {
6131
+ visible: dayEvents.slice(0, visibleCount),
6132
+ overflowCount: dayEvents.length - visibleCount
6133
+ };
6134
+ }
6135
+ function snapToGrid(date, snapMinutes, dayRange) {
6136
+ const dayStart = startOfDay(date);
6137
+ const minutesSinceMidnight = (date.getTime() - dayStart.getTime()) / 6e4;
6138
+ const snappedMinutes = Math.round(minutesSinceMidnight / snapMinutes) * snapMinutes;
6139
+ const [rangeStartHour, rangeEndHour] = dayRange;
6140
+ const rangeStartMin = rangeStartHour * 60;
6141
+ const rangeEndMin = Math.min(rangeEndHour * 60, 24 * 60);
6142
+ const clampedMinutes = Math.min(
6143
+ Math.max(snappedMinutes, rangeStartMin),
6144
+ rangeEndMin
6145
+ );
6146
+ const snapped = addMinutes(dayStart, clampedMinutes);
6147
+ const dayEnd = endOfDay(date);
6148
+ return snapped.getTime() > dayEnd.getTime() ? dayEnd : snapped;
6149
+ }
6150
+ function resolveResize(event, edge, deltaMin, snapMinutes) {
6151
+ const snappedDelta = Math.round(deltaMin / snapMinutes) * snapMinutes;
6152
+ if (edge === "start") {
6153
+ const proposedStart = addMinutes(event.start, snappedDelta);
6154
+ const maxStart = addMinutes(event.end, -snapMinutes);
6155
+ const start = proposedStart.getTime() > maxStart.getTime() ? maxStart : proposedStart;
6156
+ return { start, end: event.end };
6157
+ }
6158
+ const proposedEnd = addMinutes(event.end, snappedDelta);
6159
+ const minEnd = addMinutes(event.start, snapMinutes);
6160
+ const end = proposedEnd.getTime() < minEnd.getTime() ? minEnd : proposedEnd;
6161
+ return { start: event.start, end };
6162
+ }
6163
+ function pixelsToMinutes(px, hourHeight) {
6164
+ return px / hourHeight * 60;
6165
+ }
6166
+ function resolveMonthDrop(event, targetDay) {
6167
+ const diasDeslocados = differenceInCalendarDays(
6168
+ startOfDay(targetDay),
6169
+ startOfDay(event.start)
6170
+ );
6171
+ if (diasDeslocados === 0) {
6172
+ return { start: event.start, end: event.end };
6173
+ }
6174
+ return {
6175
+ start: addDays(event.start, diasDeslocados),
6176
+ end: addDays(event.end, diasDeslocados)
6177
+ };
6178
+ }
6179
+ function resolveTimeGridMove(event, dayDelta, minuteDelta, snapMinutes, dayRange) {
6180
+ const duracaoMs = event.end.getTime() - event.start.getTime();
6181
+ const bruto = addMinutes(addDays(event.start, dayDelta), minuteDelta);
6182
+ const start = snapToGrid(bruto, snapMinutes, dayRange);
6183
+ return { start, end: new Date(start.getTime() + duracaoMs) };
6184
+ }
6185
+ function minutesToOffset(date, dayRange, hourHeight) {
6186
+ const gridStart = addHours(startOfDay(date), dayRange[0]);
6187
+ const elapsedMinutes = (date.getTime() - gridStart.getTime()) / 6e4;
6188
+ return elapsedMinutes / 60 * hourHeight;
6189
+ }
6190
+ function MiniMonth({
6191
+ selected,
6192
+ now,
6193
+ locale,
6194
+ weekStartsOn,
6195
+ events,
6196
+ onSelect
6197
+ }) {
6198
+ const [offset, setOffset] = useState(0);
6199
+ const cursor = useMemo(
6200
+ () => addMonths(startOfDay(selected), offset),
6201
+ [selected, offset]
6202
+ );
6203
+ const weeks = useMemo(
6204
+ () => buildMonthMatrix(cursor, weekStartsOn, locale),
6205
+ [cursor, weekStartsOn, locale]
6206
+ );
6207
+ const daysWithEvents = useMemo(() => {
6208
+ const set = /* @__PURE__ */ new Set();
6209
+ for (const ev of events) {
6210
+ let cur = startOfDay(ev.start);
6211
+ const last = startOfDay(ev.end);
6212
+ let guard = 0;
6213
+ while (cur.getTime() <= last.getTime() && guard < 400) {
6214
+ set.add(cur.getTime());
6215
+ cur = new Date(cur.getFullYear(), cur.getMonth(), cur.getDate() + 1);
6216
+ guard++;
6217
+ }
6218
+ }
6219
+ return set;
6220
+ }, [events]);
6221
+ const weekdayLabels = useMemo(
6222
+ () => weeks[0].map((day) => format(day, "EEEEE", { locale })),
6223
+ [weeks, locale]
6224
+ );
6225
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-gp-md", children: [
6226
+ /* @__PURE__ */ jsxs("div", { className: schedulerMiniHead(), children: [
6227
+ /* @__PURE__ */ jsx("span", { className: schedulerMiniTitle(), children: format(cursor, "MMMM yyyy", { locale }) }),
6228
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center", children: [
6229
+ /* @__PURE__ */ jsx(
6230
+ Button,
6231
+ {
6232
+ variant: "ghost",
6233
+ color: "secondary",
6234
+ size: "icon-2xs",
6235
+ "aria-label": "Mês anterior",
6236
+ onClick: () => setOffset((o2) => o2 - 1),
6237
+ children: /* @__PURE__ */ jsx(ChevronLeft, {})
6238
+ }
6239
+ ),
6240
+ /* @__PURE__ */ jsx(
6241
+ Button,
6242
+ {
6243
+ variant: "ghost",
6244
+ color: "secondary",
6245
+ size: "icon-2xs",
6246
+ "aria-label": "Próximo mês",
6247
+ onClick: () => setOffset((o2) => o2 + 1),
6248
+ children: /* @__PURE__ */ jsx(ChevronRight, {})
6249
+ }
6250
+ )
6251
+ ] })
6252
+ ] }),
6253
+ /* @__PURE__ */ jsxs("div", { className: schedulerMiniGrid(), children: [
6254
+ weekdayLabels.map((label, i4) => /* @__PURE__ */ jsx("span", { className: schedulerMiniWeekday(), children: label }, `${label}-${i4}`)),
6255
+ weeks.flat().map((day) => /* @__PURE__ */ jsx(
6256
+ "button",
6257
+ {
6258
+ type: "button",
6259
+ onClick: () => {
6260
+ onSelect(day);
6261
+ setOffset(0);
6262
+ },
6263
+ "aria-label": format(day, "PPPP", { locale }),
6264
+ "aria-current": isSameDay(day, selected) ? "date" : void 0,
6265
+ className: schedulerMiniDay({
6266
+ outside: !isSameMonth(day, cursor),
6267
+ hasEvents: daysWithEvents.has(day.getTime()),
6268
+ today: isSameDay(day, now),
6269
+ selected: isSameDay(day, selected)
6270
+ }),
6271
+ children: format(day, "d", { locale })
6272
+ },
6273
+ day.getTime()
6274
+ ))
6275
+ ] })
6276
+ ] });
6277
+ }
6278
+ function SchedulerFilterPanel({
6279
+ filterFields,
6280
+ filterModel,
6281
+ onToggleValue,
6282
+ onClearAll,
6283
+ appliedCount,
6284
+ onClose,
6285
+ embedded = false,
6286
+ counts,
6287
+ date,
6288
+ now,
6289
+ locale,
6290
+ weekStartsOn,
6291
+ events,
6292
+ onDateSelect
6293
+ }) {
6294
+ const [collapsed, setCollapsed] = useState(() => /* @__PURE__ */ new Set());
6295
+ const toggleGroup = (id) => setCollapsed((prev) => {
6296
+ const next = new Set(prev);
6297
+ if (next.has(id)) next.delete(id);
6298
+ else next.add(id);
6299
+ return next;
6300
+ });
6301
+ return /* @__PURE__ */ jsxs("aside", { className: schedulerFilterAside({ embedded }), "aria-label": "Filtros do calendário", children: [
6302
+ embedded ? null : /* @__PURE__ */ jsx("div", { className: schedulerAsideSection({ compact: true, sticky: true }), children: /* @__PURE__ */ jsxs("div", { className: schedulerAsideHead(), children: [
6303
+ /* @__PURE__ */ jsx("span", { className: schedulerAsideTitle(), children: "Filtros" }),
6304
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-gp-sm", children: [
6305
+ appliedCount > 0 ? /* @__PURE__ */ jsx("button", { type: "button", onClick: onClearAll, className: schedulerClearLink(), children: "Limpar" }) : null,
6306
+ /* @__PURE__ */ jsx(
6307
+ Button,
6308
+ {
6309
+ variant: "ghost",
6310
+ color: "secondary",
6311
+ size: "icon-2xs",
6312
+ "aria-label": "Fechar painel de filtros",
6313
+ onClick: onClose,
6314
+ children: /* @__PURE__ */ jsx(X3, {})
6315
+ }
6316
+ )
6317
+ ] })
6318
+ ] }) }),
6319
+ /* @__PURE__ */ jsx("div", { className: schedulerAsideSection(), children: /* @__PURE__ */ jsx(
6320
+ MiniMonth,
6321
+ {
6322
+ selected: date,
6323
+ now,
6324
+ locale,
6325
+ weekStartsOn,
6326
+ events,
6327
+ onSelect: onDateSelect
6328
+ }
6329
+ ) }),
6330
+ filterFields.map((field) => {
6331
+ const selected = filterModel[field.id] ?? [];
6332
+ const isOpen = !collapsed.has(field.id);
6333
+ const fieldCounts = counts[field.id] ?? {};
6334
+ return /* @__PURE__ */ jsxs(
6335
+ "div",
6336
+ {
6337
+ className: cn(schedulerAsideSection(), schedulerGroup()),
6338
+ children: [
6339
+ /* @__PURE__ */ jsxs(
6340
+ "button",
6341
+ {
6342
+ type: "button",
6343
+ "aria-expanded": isOpen,
6344
+ onClick: () => toggleGroup(field.id),
6345
+ className: schedulerGroupHead(),
6346
+ children: [
6347
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-baseline gap-gp-sm", children: [
6348
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: field.label }),
6349
+ /* @__PURE__ */ jsx("span", { className: schedulerOptionCount(), children: selected.length === 0 ? "Todas" : selected.length })
6350
+ ] }),
6351
+ /* @__PURE__ */ jsx(
6352
+ ChevronDown,
6353
+ {
6354
+ className: schedulerGroupChevron({ open: isOpen }),
6355
+ "aria-hidden": "true"
6356
+ }
6357
+ )
6358
+ ]
6359
+ }
6360
+ ),
6361
+ isOpen ? /* @__PURE__ */ jsx("div", { className: "flex flex-col", children: field.options.map((option) => {
6362
+ const id = `scheduler-filter-${field.id}-${option.value}`;
6363
+ const isOn = selected.includes(option.value);
6364
+ const count = fieldCounts[option.value];
6365
+ return /* @__PURE__ */ jsxs("label", { htmlFor: id, className: schedulerOption(), children: [
6366
+ /* @__PURE__ */ jsx(
6367
+ Checkbox,
6368
+ {
6369
+ id,
6370
+ checked: isOn,
6371
+ onCheckedChange: () => onToggleValue(field.id, option.value),
6372
+ className: schedulerOptionBox({
6373
+ color: option.color ?? "brand"
6374
+ })
6375
+ }
6376
+ ),
6377
+ /* @__PURE__ */ jsx("span", { className: schedulerOptionLabel(), children: option.label }),
6378
+ count !== void 0 ? /* @__PURE__ */ jsx("span", { className: schedulerOptionCount(), children: count }) : null
6379
+ ] }, option.value);
6380
+ }) }) : null
6381
+ ]
6382
+ },
6383
+ field.id
6384
+ );
6385
+ })
6386
+ ] });
6387
+ }
6388
+ const SchedulerEventItem = forwardRef(function SchedulerEventItem2({
6389
+ event,
6390
+ view,
6391
+ variant,
6392
+ truncateStart = false,
6393
+ truncateEnd = false,
6394
+ dragging = false,
6395
+ disabled = false,
6396
+ timeLabel,
6397
+ style,
6398
+ renderContent,
6399
+ onClick,
6400
+ className,
6401
+ dragAttributes,
6402
+ dragListeners,
6403
+ setDragNodeRef,
6404
+ dragTransform,
6405
+ movable = false,
6406
+ resizeHandles
6407
+ }, ref) {
6408
+ const color = event.color ?? "brand";
6409
+ const mergeRefs = (el) => {
6410
+ setDragNodeRef?.(el);
6411
+ if (typeof ref === "function") ref(el);
6412
+ else if (ref) {
6413
+ ref.current = el;
6414
+ }
6415
+ };
6416
+ const estilo = {
6417
+ ...style,
6418
+ ...dragTransform ? { transform: `translate3d(${dragTransform.x}px, ${dragTransform.y}px, 0)` } : null
6419
+ };
6420
+ const ariaLabel = [
6421
+ typeof event.title === "string" ? event.title : event.searchText,
6422
+ timeLabel
6423
+ ].filter(Boolean).join(", ");
6424
+ return /* @__PURE__ */ jsxs(
6425
+ "button",
6426
+ {
6427
+ ref: mergeRefs,
6428
+ type: "button",
6429
+ disabled,
6430
+ style: estilo,
6431
+ "aria-label": ariaLabel || void 0,
6432
+ onClick: (evt) => onClick?.(evt),
6433
+ ...dragAttributes,
6434
+ ...dragListeners,
6435
+ className: cn(
6436
+ schedulerEvent({
6437
+ color,
6438
+ variant,
6439
+ truncateStart,
6440
+ truncateEnd,
6441
+ dragging,
6442
+ // `movable` só pinta em REPOUSO: durante o arraste o `dragging` manda,
6443
+ // e ele desliga a transição de propósito (ver nota no styles).
6444
+ movable: movable && !dragging,
6445
+ disabled
6446
+ }),
6447
+ className
6448
+ ),
6449
+ children: [
6450
+ renderContent ? renderContent() : variant === "row" ? (
6451
+ /* `row` é a única forma com espaço pra 2 linhas — a descrição só
6452
+ aparece aqui. No pill de 20px ela não caberia, e no block ela
6453
+ competiria com o título quando a duração é curta. */
6454
+ /* @__PURE__ */ jsxs(Fragment, { children: [
6455
+ /* @__PURE__ */ jsx(
6456
+ "span",
6457
+ {
6458
+ className: schedulerEventDot({ color, size: "md" }),
6459
+ "aria-hidden": "true"
6460
+ }
6461
+ ),
6462
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 flex-col", children: [
6463
+ /* @__PURE__ */ jsx("span", { className: schedulerEventTitle(), children: event.title }),
6464
+ event.description ? /* @__PURE__ */ jsx("span", { className: schedulerEventDescription(), children: event.description }) : null
6465
+ ] }),
6466
+ timeLabel ? /* @__PURE__ */ jsx("span", { className: schedulerEventTime(), children: timeLabel }) : null
6467
+ ] })
6468
+ ) : variant === "block" ? (
6469
+ /* No block o horário vem ANTES do título e em linha própria: o bloco
6470
+ tem altura proporcional à duração, e num evento longo o título
6471
+ sozinho no topo deixaria o horário fora do campo de leitura. */
6472
+ /* @__PURE__ */ jsxs(Fragment, { children: [
6473
+ timeLabel ? /* @__PURE__ */ jsx("span", { className: cn(schedulerEventTime(), "text-caption-xs"), children: timeLabel }) : null,
6474
+ /* @__PURE__ */ jsx("span", { className: cn(schedulerEventTitle(), "w-full"), children: event.title })
6475
+ ] })
6476
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
6477
+ !truncateStart ? /* @__PURE__ */ jsx(
6478
+ "span",
6479
+ {
6480
+ className: schedulerEventDot({ color, size: "sm" }),
6481
+ "aria-hidden": "true"
6482
+ }
6483
+ ) : null,
6484
+ /* @__PURE__ */ jsx("span", { className: schedulerEventTitle(), children: event.title }),
6485
+ timeLabel && view === "month" ? /* @__PURE__ */ jsx("span", { className: schedulerEventTime(), children: timeLabel }) : null
6486
+ ] }),
6487
+ resizeHandles
6488
+ ]
6489
+ }
6490
+ );
6491
+ });
6492
+ const MOVE_PREFIX = "move:";
6493
+ const RESIZE_PREFIX = "resize:";
6494
+ const DAY_PREFIX = "day:";
6495
+ const draggableIdForMove = (eventId) => `${MOVE_PREFIX}${eventId}`;
6496
+ const draggableIdForResize = (eventId, edge) => `${RESIZE_PREFIX}${eventId}:${edge}`;
6497
+ const droppableIdForDay = (day) => `${DAY_PREFIX}${startOfDay(day).getTime()}`;
6498
+ function parseDayDroppable(overId) {
6499
+ if (!overId || !overId.startsWith(DAY_PREFIX)) return null;
6500
+ const ms = Number(overId.slice(DAY_PREFIX.length));
6501
+ return Number.isFinite(ms) ? new Date(ms) : null;
6502
+ }
6503
+ function parseDraggable(activeId) {
6504
+ if (activeId.startsWith(MOVE_PREFIX)) {
6505
+ return { kind: "move", eventId: activeId.slice(MOVE_PREFIX.length) };
6506
+ }
6507
+ if (activeId.startsWith(RESIZE_PREFIX)) {
6508
+ const resto = activeId.slice(RESIZE_PREFIX.length);
6509
+ const corte = resto.lastIndexOf(":");
6510
+ if (corte < 0) return null;
6511
+ const edge = resto.slice(corte + 1);
6512
+ if (edge !== "start" && edge !== "end") return null;
6513
+ return { kind: "resize", eventId: resto.slice(0, corte), edge };
6514
+ }
6515
+ return null;
6516
+ }
6517
+ function useSchedulerDnd({
6518
+ enabled,
6519
+ view,
6520
+ events,
6521
+ snapMinutes,
6522
+ dayRange,
6523
+ hourHeight,
6524
+ draggable,
6525
+ resizable,
6526
+ onEventMove,
6527
+ onEventResize
6528
+ }) {
6529
+ const [ativo, setAtivo] = useState(null);
6530
+ const [diaSobreposto, setDiaSobreposto] = useState(null);
6531
+ const sensors = useSensors(
6532
+ useSensor(PointerSensor, { activationConstraint: { distance: 6 } })
6533
+ );
6534
+ const porId = useMemo(() => {
6535
+ const m2 = /* @__PURE__ */ new Map();
6536
+ for (const e3 of events) m2.set(e3.id, e3);
6537
+ return m2;
6538
+ }, [events]);
6539
+ const podeMover = useCallback(
6540
+ (e3) => enabled && (e3.draggable ?? draggable),
6541
+ [enabled, draggable]
6542
+ );
6543
+ const podeRedimensionar = useCallback(
6544
+ (e3) => (
6545
+ // Resize só existe onde há eixo de tempo. No mês a altura da pílula não
6546
+ // representa duração, então arrastar a borda não teria o que significar.
6547
+ enabled && view !== "month" && view !== "list" && (e3.resizable ?? resizable) && !e3.allDay
6548
+ ),
6549
+ [enabled, view, resizable]
6550
+ );
6551
+ const handleDragStart = useCallback((evt) => {
6552
+ const parsed = parseDraggable(String(evt.active.id));
6553
+ if (parsed) setAtivo(parsed);
6554
+ }, []);
6555
+ const handleDragOver = useCallback((evt) => {
6556
+ const dia = parseDayDroppable(evt.over ? String(evt.over.id) : null);
6557
+ setDiaSobreposto(dia ? dia.getTime() : null);
6558
+ }, []);
6559
+ const handleDragCancel = useCallback(() => {
6560
+ setAtivo(null);
6561
+ setDiaSobreposto(null);
6562
+ }, []);
6563
+ const handleDragEnd = useCallback(
6564
+ (evt) => {
6565
+ setAtivo(null);
6566
+ setDiaSobreposto(null);
6567
+ const atual = parseDraggable(String(evt.active.id));
6568
+ if (!atual) return;
6569
+ if (!enabled) return;
6570
+ const evento = porId.get(atual.eventId);
6571
+ if (!evento) return;
6572
+ if (atual.kind === "resize") {
6573
+ if (!podeRedimensionar(evento)) return;
6574
+ const minutos = pixelsToMinutes(evt.delta.y, hourHeight);
6575
+ if (Math.abs(minutos) < 1) return;
6576
+ const r2 = resolveResize(evento, atual.edge, minutos, snapMinutes);
6577
+ if (r2.start.getTime() === evento.start.getTime() && r2.end.getTime() === evento.end.getTime()) {
6578
+ return;
6579
+ }
6580
+ onEventResize?.({ id: evento.id, start: r2.start, end: r2.end });
6581
+ return;
6582
+ }
6583
+ if (!podeMover(evento)) return;
6584
+ const diaAlvo = parseDayDroppable(evt.over ? String(evt.over.id) : null);
6585
+ if (view === "month") {
6586
+ if (!diaAlvo) return;
6587
+ const r2 = resolveMonthDrop(evento, diaAlvo);
6588
+ if (r2.start.getTime() === evento.start.getTime()) return;
6589
+ onEventMove?.({ id: evento.id, start: r2.start, end: r2.end });
6590
+ return;
6591
+ }
6592
+ if (view === "week" || view === "day") {
6593
+ const deslocamentoDeDias = diaAlvo ? differenceInCalendarDays(diaAlvo, startOfDay(evento.start)) : 0;
6594
+ const minutos = evento.allDay ? 0 : pixelsToMinutes(evt.delta.y, hourHeight);
6595
+ if (deslocamentoDeDias === 0 && Math.abs(minutos) < 1) return;
6596
+ const r2 = resolveTimeGridMove(
6597
+ evento,
6598
+ deslocamentoDeDias,
6599
+ minutos,
6600
+ snapMinutes,
6601
+ dayRange
6602
+ );
6603
+ if (r2.start.getTime() === evento.start.getTime() && r2.end.getTime() === evento.end.getTime()) {
6604
+ return;
6605
+ }
6606
+ onEventMove?.({ id: evento.id, start: r2.start, end: r2.end });
6607
+ }
6608
+ },
6609
+ [
6610
+ enabled,
6611
+ porId,
6612
+ view,
6613
+ hourHeight,
6614
+ snapMinutes,
6615
+ dayRange,
6616
+ podeMover,
6617
+ podeRedimensionar,
6618
+ onEventMove,
6619
+ onEventResize
6620
+ ]
6621
+ );
6622
+ return {
6623
+ sensors,
6624
+ /** `move:<id>` ou `resize:<id>:<edge>` em andamento, pra pintar o estado. */
6625
+ dragAtivo: ativo,
6626
+ /** `getTime()` do dia sob o cursor — a célula/coluna se pinta com isso. */
6627
+ diaSobreposto,
6628
+ podeMover,
6629
+ podeRedimensionar,
6630
+ handleDragStart,
6631
+ handleDragOver,
6632
+ handleDragEnd,
6633
+ handleDragCancel
6634
+ };
6635
+ }
6636
+ function ResizeHandle({
6637
+ eventId,
6638
+ edge,
6639
+ label
6640
+ }) {
6641
+ const { attributes, listeners, setNodeRef } = useDraggable({
6642
+ id: draggableIdForResize(eventId, edge)
6643
+ });
6644
+ return (
6645
+ /* `<span role="button">` e não `<button>`: a alça vive DENTRO do botão do
6646
+ evento, e botão dentro de botão é HTML inválido — o navegador quebra a
6647
+ árvore e o clique do evento para de funcionar. */
6648
+ /* @__PURE__ */ jsx(
6649
+ "span",
6650
+ {
6651
+ ref: setNodeRef,
6652
+ ...attributes,
6653
+ ...listeners,
6654
+ role: "button",
6655
+ tabIndex: -1,
6656
+ "aria-label": label,
6657
+ className: schedulerResizeHandle({ edge }),
6658
+ onClick: (e3) => e3.stopPropagation(),
6659
+ onPointerDown: (e3) => {
6660
+ e3.stopPropagation();
6661
+ listeners?.onPointerDown?.(e3);
6662
+ }
6663
+ }
6664
+ )
6665
+ );
6666
+ }
6667
+ function DraggableEvent({
6668
+ movable = false,
6669
+ resizable = false,
6670
+ ...rest
6671
+ }) {
6672
+ const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
6673
+ id: draggableIdForMove(rest.event.id),
6674
+ disabled: !movable
6675
+ });
6676
+ return /* @__PURE__ */ jsx(
6677
+ SchedulerEventItem,
6678
+ {
6679
+ ...rest,
6680
+ movable,
6681
+ dragging: isDragging,
6682
+ setDragNodeRef: setNodeRef,
6683
+ dragTransform: null,
6684
+ dragAttributes: movable ? { ...attributes } : void 0,
6685
+ dragListeners: movable ? { ...listeners } : void 0,
6686
+ resizeHandles: resizable && !isDragging ? /* @__PURE__ */ jsxs(Fragment, { children: [
6687
+ /* @__PURE__ */ jsx(
6688
+ ResizeHandle,
6689
+ {
6690
+ eventId: rest.event.id,
6691
+ edge: "start",
6692
+ label: "Ajustar início"
6693
+ }
6694
+ ),
6695
+ /* @__PURE__ */ jsx(
6696
+ ResizeHandle,
6697
+ {
6698
+ eventId: rest.event.id,
6699
+ edge: "end",
6700
+ label: "Ajustar fim"
6701
+ }
6702
+ )
6703
+ ] }) : null
6704
+ }
6705
+ );
6706
+ }
6707
+ function DroppableDay({
6708
+ day,
6709
+ disabled,
6710
+ className,
6711
+ overClassName,
6712
+ children,
6713
+ cellRef,
6714
+ ...rest
6715
+ }) {
6716
+ const { setNodeRef, isOver } = useDroppable({
6717
+ id: droppableIdForDay(day),
6718
+ disabled
6719
+ });
6720
+ return /* @__PURE__ */ jsx(
6721
+ "div",
6722
+ {
6723
+ ref: (el) => {
6724
+ setNodeRef(el);
6725
+ cellRef?.(el);
6726
+ },
6727
+ className: cn(className, isOver && !disabled && overClassName),
6728
+ ...rest,
6729
+ children
6730
+ }
6731
+ );
6732
+ }
6733
+ function useSchedulerKeyboard({
6734
+ count,
6735
+ columns,
6736
+ onActivate,
6737
+ enabled = true
6738
+ }) {
6739
+ const [ativo, setAtivo] = useState(0);
6740
+ const [moveuPorTeclado, setMoveuPorTeclado] = useState(false);
6741
+ const refs = useRef([]);
6742
+ useEffect(() => {
6743
+ if (ativo > count - 1) setAtivo(Math.max(0, count - 1));
6744
+ }, [count, ativo]);
6745
+ useEffect(() => {
6746
+ if (!moveuPorTeclado) return;
6747
+ refs.current[ativo]?.focus();
6748
+ setMoveuPorTeclado(false);
6749
+ }, [ativo, moveuPorTeclado]);
6750
+ const registrarRef = useCallback(
6751
+ (index) => (el) => {
6752
+ refs.current[index] = el;
6753
+ },
6754
+ []
6755
+ );
6756
+ const handleKeyDown = useCallback(
6757
+ (index) => (e3) => {
6758
+ if (!enabled) return;
6759
+ const irPara = (proximo) => {
6760
+ e3.preventDefault();
6761
+ setAtivo(Math.min(count - 1, Math.max(0, proximo)));
6762
+ setMoveuPorTeclado(true);
6763
+ };
6764
+ switch (e3.key) {
6765
+ case "ArrowRight":
6766
+ return irPara(index + 1);
6767
+ case "ArrowLeft":
6768
+ return irPara(index - 1);
6769
+ case "ArrowDown":
6770
+ return irPara(index + columns);
6771
+ case "ArrowUp":
6772
+ return irPara(index - columns);
6773
+ case "Home":
6774
+ return irPara(index - index % columns);
6775
+ case "End":
6776
+ return irPara(index - index % columns + (columns - 1));
6777
+ case "PageUp":
6778
+ return irPara(0);
6779
+ case "PageDown":
6780
+ return irPara(count - 1);
6781
+ case "Enter":
6782
+ case " ":
6783
+ e3.preventDefault();
6784
+ onActivate?.(index);
6785
+ return;
6786
+ default:
6787
+ return;
6788
+ }
6789
+ },
6790
+ [enabled, count, columns, onActivate]
6791
+ );
6792
+ const getCellProps = useCallback(
6793
+ (index) => ({
6794
+ tabIndex: enabled && index === ativo ? 0 : -1,
6795
+ onKeyDown: handleKeyDown(index),
6796
+ onFocus: () => setAtivo(index)
6797
+ }),
6798
+ [enabled, ativo, handleKeyDown]
6799
+ );
6800
+ const getCellRef = useCallback(
6801
+ (index) => registrarRef(index),
6802
+ [registrarRef]
6803
+ );
6804
+ return { activeIndex: ativo, getCellProps, getCellRef };
6805
+ }
6806
+ const CELL_CHROME_PX = 24 + 4 + 2;
6807
+ const FALLBACK_ROW_HEIGHT_PX = 96;
6808
+ function SchedulerMonthView({
6809
+ date,
6810
+ events,
6811
+ locale,
6812
+ weekStartsOn,
6813
+ hourFormat,
6814
+ now,
6815
+ maxPerCell,
6816
+ onEventClick,
6817
+ onSlotClick,
6818
+ renderEvent,
6819
+ dndAtivo = false,
6820
+ podeMover
6821
+ }) {
6822
+ const weeks = useMemo(
6823
+ () => buildMonthMatrix(date, weekStartsOn, locale),
6824
+ [date, weekStartsOn, locale]
6825
+ );
6826
+ const teclado = useSchedulerKeyboard({
6827
+ count: weeks.length * 7,
6828
+ columns: 7,
6829
+ onActivate: (index) => {
6830
+ const dia = weeks[Math.floor(index / 7)]?.[index % 7];
6831
+ if (dia) onSlotClick?.(startOfDay(dia), startOfDay(dia));
6832
+ }
6833
+ });
6834
+ const gridRef = useRef(null);
6835
+ const [rowHeight, setRowHeight] = useState(FALLBACK_ROW_HEIGHT_PX);
6836
+ useLayoutEffect(() => {
6837
+ const el = gridRef.current;
6838
+ if (!el) return;
6839
+ const medir = () => {
6840
+ const altura = el.getBoundingClientRect().height;
6841
+ if (altura > 0) setRowHeight(altura / weeks.length);
6842
+ };
6843
+ medir();
6844
+ if (typeof ResizeObserver === "undefined") return;
6845
+ const ro = new ResizeObserver(medir);
6846
+ ro.observe(el);
6847
+ return () => ro.disconnect();
6848
+ }, [weeks.length]);
6849
+ const alturaDisponivel = Math.max(0, rowHeight - CELL_CHROME_PX);
6850
+ const segments = useMemo(
6851
+ () => segmentMultiDay(events, weeks),
6852
+ [events, weeks]
6853
+ );
6854
+ const byDay = useMemo(() => {
6855
+ const map = /* @__PURE__ */ new Map();
6856
+ for (const seg of segments) {
6857
+ const row = weeks[seg.weekIndex];
6858
+ for (let i4 = 0; i4 < seg.colSpan; i4++) {
6859
+ const day = row[seg.colStart + i4];
6860
+ if (!day) continue;
6861
+ const key = day.getTime();
6862
+ const isFirstOfSegment = i4 === 0;
6863
+ const isLastOfSegment = i4 === seg.colSpan - 1;
6864
+ const list = map.get(key) ?? [];
6865
+ list.push({
6866
+ event: seg.event,
6867
+ // A ponta só é "de verdade" quando é a ponta do segmento E a ponta
6868
+ // do evento: um evento que vem da semana anterior chega com
6869
+ // `isStart: false` e nunca ganha canto arredondado à esquerda.
6870
+ truncateStart: !(isFirstOfSegment && seg.isStart),
6871
+ truncateEnd: !(isLastOfSegment && seg.isEnd)
6872
+ });
6873
+ map.set(key, list);
6874
+ }
6875
+ }
6876
+ for (const list of map.values()) {
6877
+ list.sort((a11, b4) => a11.event.start.getTime() - b4.event.start.getTime());
6878
+ }
6879
+ return map;
6880
+ }, [segments, weeks]);
6881
+ const weekdayLabels = useMemo(
6882
+ () => weeks[0].map((day) => format(day, "EEEEEE", { locale })),
6883
+ [weeks, locale]
6884
+ );
6885
+ const timeLabelOf = (event) => {
6886
+ if (event.allDay) return void 0;
6887
+ return format(event.start, hourFormat === "12h" ? "h:mm a" : "HH:mm", {
6888
+ locale
6889
+ });
6890
+ };
6891
+ return /* @__PURE__ */ jsxs("div", { className: schedulerMonthFrame(), children: [
6892
+ /* @__PURE__ */ jsx("div", { className: schedulerWeekdayRow(), children: weekdayLabels.map((label, i4) => /* @__PURE__ */ jsx("span", { className: schedulerWeekdayCell(), children: label }, `${label}-${i4}`)) }),
6893
+ /* @__PURE__ */ jsx("div", { ref: gridRef, role: "grid", className: schedulerMonthGrid(), children: weeks.map(
6894
+ (week, weekIndex) => week.map((day) => {
6895
+ const dayEvents = byDay.get(day.getTime()) ?? [];
6896
+ const todos = dayEvents.map((d3) => d3.event);
6897
+ const { visible, overflowCount } = maxPerCell !== void 0 ? {
6898
+ visible: todos.slice(
6899
+ 0,
6900
+ todos.length <= maxPerCell ? maxPerCell : Math.max(0, maxPerCell - 1)
6901
+ ),
6902
+ overflowCount: todos.length <= maxPerCell ? 0 : todos.length - Math.max(0, maxPerCell - 1)
6903
+ } : computeOverflow(todos, alturaDisponivel);
6904
+ const outside = !isSameMonth(day, date);
6905
+ const today = isSameDay(day, now);
6906
+ const indiceDaCelula = weekIndex * 7 + week.indexOf(day);
6907
+ return /* @__PURE__ */ jsxs(
6908
+ DroppableDay,
6909
+ {
6910
+ day,
6911
+ disabled: !dndAtivo,
6912
+ className: schedulerMonthCell({
6913
+ outside,
6914
+ lastRow: weekIndex === weeks.length - 1,
6915
+ interactive: Boolean(onSlotClick)
6916
+ }),
6917
+ overClassName: schedulerDropTarget(),
6918
+ role: "gridcell",
6919
+ "aria-label": format(day, "PPPP", { locale }),
6920
+ ...teclado.getCellProps(indiceDaCelula),
6921
+ cellRef: teclado.getCellRef(indiceDaCelula),
6922
+ children: [
6923
+ /* @__PURE__ */ jsxs("div", { className: schedulerDayHead(), children: [
6924
+ /* @__PURE__ */ jsx("span", { className: schedulerDayNumber({ today, outside }), children: format(day, "d", { locale }) }),
6925
+ onSlotClick ? /* @__PURE__ */ jsx(
6926
+ "button",
6927
+ {
6928
+ type: "button",
6929
+ className: schedulerSlotAdd(),
6930
+ "aria-label": `Criar evento em ${format(day, "PPP", { locale })}`,
6931
+ tabIndex: -1,
6932
+ onClick: () => (
6933
+ // Dia inteiro: quem cria decide a hora no formulário.
6934
+ // Emitir 00:00–23:59 seria inventar uma duração que o
6935
+ // usuário não pediu.
6936
+ onSlotClick(startOfDay(day), startOfDay(day))
6937
+ ),
6938
+ children: /* @__PURE__ */ jsx(Plus, { "aria-hidden": "true" })
6939
+ }
6940
+ ) : null
6941
+ ] }),
6942
+ /* @__PURE__ */ jsxs("div", { className: schedulerCellEvents(), children: [
6943
+ visible.map((event) => {
6944
+ const meta = dayEvents.find((d3) => d3.event.id === event.id);
6945
+ return /* @__PURE__ */ jsx(
6946
+ DraggableEvent,
6947
+ {
6948
+ event,
6949
+ view: "month",
6950
+ variant: "pill",
6951
+ movable: podeMover?.(event) ?? false,
6952
+ truncateStart: meta?.truncateStart,
6953
+ truncateEnd: meta?.truncateEnd,
6954
+ timeLabel: timeLabelOf(event),
6955
+ onClick: (evt) => onEventClick?.(event, evt),
6956
+ renderContent: renderEvent ? () => renderEvent({
6957
+ event,
6958
+ view: "month",
6959
+ selected: false
6960
+ }) : void 0
6961
+ },
6962
+ `${event.id}-${day.getTime()}`
6963
+ );
6964
+ }),
6965
+ overflowCount > 0 ? /* @__PURE__ */ jsxs(Popover, { children: [
6966
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs("button", { type: "button", className: schedulerOverflowButton(), children: [
6967
+ "+",
6968
+ overflowCount,
6969
+ " mais"
6970
+ ] }) }),
6971
+ /* @__PURE__ */ jsx(PopoverContent, { align: "start", className: "p-0", children: /* @__PURE__ */ jsxs("div", { className: schedulerOverflowPanel(), children: [
6972
+ /* @__PURE__ */ jsx("span", { className: schedulerOverflowPanelTitle(), children: format(day, "EEEE, d 'de' MMMM", { locale }) }),
6973
+ dayEvents.map((d3) => /* @__PURE__ */ jsx(
6974
+ SchedulerEventItem,
6975
+ {
6976
+ event: d3.event,
6977
+ view: "month",
6978
+ variant: "row",
6979
+ timeLabel: timeLabelOf(d3.event),
6980
+ onClick: (evt) => onEventClick?.(d3.event, evt)
6981
+ },
6982
+ `overflow-${d3.event.id}`
6983
+ ))
6984
+ ] }) })
6985
+ ] }) : null
6986
+ ] })
6987
+ ]
6988
+ },
6989
+ day.getTime()
6990
+ );
6991
+ })
6992
+ ) })
6993
+ ] });
6994
+ }
6995
+ const HOUR_HEIGHT_PX = 48;
6996
+ const MIN_BLOCK_PX = 22;
6997
+ function SchedulerTimeGrid({
6998
+ date,
6999
+ view,
7000
+ events,
7001
+ locale,
7002
+ weekStartsOn,
7003
+ hourFormat,
7004
+ dayRange,
7005
+ scrollToHour,
7006
+ nowIndicator,
7007
+ now,
7008
+ snapMinutes,
7009
+ onEventClick,
7010
+ onSlotClick,
7011
+ renderEvent,
7012
+ dndAtivo = false,
7013
+ podeMover,
7014
+ podeRedimensionar
7015
+ }) {
7016
+ const bodyRef = useRef(null);
7017
+ const days = useMemo(() => {
7018
+ if (view === "day") return [startOfDay(date)];
7019
+ const first = startOfWeek(date, { weekStartsOn, locale });
7020
+ return Array.from({ length: 7 }, (_3, i4) => startOfDay(addDays(first, i4)));
7021
+ }, [date, view, weekStartsOn, locale]);
7022
+ const [rangeStart, rangeEnd] = dayRange;
7023
+ const hours = useMemo(
7024
+ () => Array.from({ length: Math.max(1, rangeEnd - rangeStart) }, (_3, i4) => rangeStart + i4),
7025
+ [rangeStart, rangeEnd]
7026
+ );
7027
+ const teclado = useSchedulerKeyboard({
7028
+ count: hours.length * days.length,
7029
+ columns: days.length,
7030
+ onActivate: (index) => {
7031
+ const hora = hours[Math.floor(index / days.length)];
7032
+ const dia = days[index % days.length];
7033
+ if (hora === void 0 || !dia) return;
7034
+ const start = new Date(dia);
7035
+ start.setHours(hora, 0, 0, 0);
7036
+ const end = new Date(start);
7037
+ end.setHours(hora + 1, 0, 0, 0);
7038
+ onSlotClick?.(start, end);
7039
+ },
7040
+ enabled: Boolean(onSlotClick)
7041
+ });
7042
+ useEffect(() => {
7043
+ const el = bodyRef.current;
7044
+ if (!el) return;
7045
+ const target = (scrollToHour - rangeStart) * HOUR_HEIGHT_PX;
7046
+ el.scrollTop = Math.max(0, target);
7047
+ }, [scrollToHour, rangeStart]);
7048
+ const fmtHour = (hour) => {
7049
+ const d3 = new Date(2e3, 0, 1, hour % 24, 0);
7050
+ return format(d3, hourFormat === "12h" ? "h a" : "HH:mm", { locale });
7051
+ };
7052
+ const fmtTime = (d3) => format(d3, hourFormat === "12h" ? "h:mm a" : "HH:mm", { locale });
7053
+ const allDayByDay = useMemo(() => {
7054
+ const map = /* @__PURE__ */ new Map();
7055
+ for (const day of days) map.set(day.getTime(), []);
7056
+ for (const ev of events) {
7057
+ if (!ev.allDay) continue;
7058
+ for (const day of days) {
7059
+ if (startOfDay(ev.start).getTime() <= day.getTime() && startOfDay(ev.end).getTime() >= day.getTime()) {
7060
+ map.get(day.getTime())?.push(ev);
7061
+ }
7062
+ }
7063
+ }
7064
+ return map;
7065
+ }, [events, days]);
7066
+ const hasAllDay = useMemo(
7067
+ () => [...allDayByDay.values()].some((list) => list.length > 0),
7068
+ [allDayByDay]
7069
+ );
7070
+ const blocksByDay = useMemo(() => {
7071
+ const map = /* @__PURE__ */ new Map();
7072
+ for (const day of days) {
7073
+ const dayStart = startOfDay(day);
7074
+ const dayEnd = endOfDay(day);
7075
+ const gridTop = new Date(dayStart.getTime() + rangeStart * 36e5);
7076
+ const gridBottom = new Date(dayStart.getTime() + rangeEnd * 36e5);
7077
+ const doDia = events.filter(
7078
+ (ev) => !ev.allDay && ev.start.getTime() < gridBottom.getTime() && ev.end.getTime() > gridTop.getTime() && ev.start.getTime() <= dayEnd.getTime() && ev.end.getTime() >= dayStart.getTime()
7079
+ );
7080
+ const recortados = doDia.map((ev) => ({
7081
+ original: ev,
7082
+ clamped: {
7083
+ ...ev,
7084
+ start: ev.start.getTime() < gridTop.getTime() ? gridTop : ev.start,
7085
+ end: ev.end.getTime() > gridBottom.getTime() ? gridBottom : ev.end
7086
+ }
7087
+ }));
7088
+ const lanes = packLanes(
7089
+ recortados.map((r2) => r2.clamped),
7090
+ snapMinutes
7091
+ );
7092
+ map.set(
7093
+ day.getTime(),
7094
+ lanes.map((box) => {
7095
+ const original = recortados.find((r2) => r2.clamped.id === box.event.id)?.original ?? box.event;
7096
+ const top = minutesToOffset(box.event.start, dayRange, HOUR_HEIGHT_PX);
7097
+ const rawHeight = (box.event.end.getTime() - box.event.start.getTime()) / 36e5 * HOUR_HEIGHT_PX;
7098
+ return {
7099
+ event: original,
7100
+ top,
7101
+ height: Math.max(MIN_BLOCK_PX, rawHeight),
7102
+ left: `${box.laneIndex / box.laneCount * 100}%`,
7103
+ width: `${1 / box.laneCount * 100}%`,
7104
+ // O rótulo mostra o horário REAL do evento, não o recortado — quem
7105
+ // vê "22:00" num bloco que começa no topo da grade entende que ele
7106
+ // vem de antes; ver "00:00" seria mentira.
7107
+ label: `${fmtTime(original.start)}`
7108
+ };
7109
+ })
7110
+ );
7111
+ }
7112
+ return map;
7113
+ }, [events, days, dayRange, rangeStart, rangeEnd, snapMinutes, hourFormat, locale]);
7114
+ const nowOffset = useMemo(() => {
7115
+ if (!nowIndicator) return null;
7116
+ const visivel = days.some((d3) => isSameDay(d3, now));
7117
+ if (!visivel) return null;
7118
+ const offset = minutesToOffset(now, dayRange, HOUR_HEIGHT_PX);
7119
+ const total = hours.length * HOUR_HEIGHT_PX;
7120
+ if (offset < 0 || offset > total) return null;
7121
+ return offset;
7122
+ }, [nowIndicator, days, now, dayRange, hours.length]);
7123
+ const isWeekend = (d3) => d3.getDay() === 0 || d3.getDay() === 6;
7124
+ return /* @__PURE__ */ jsxs("div", { className: schedulerTimeFrame(), children: [
7125
+ /* @__PURE__ */ jsxs("div", { className: schedulerTimeHead(), children: [
7126
+ /* @__PURE__ */ jsx("div", { className: schedulerTimeGutter() }),
7127
+ days.map((day) => /* @__PURE__ */ jsxs(
7128
+ "div",
7129
+ {
7130
+ className: schedulerTimeHeadDay({ weekend: isWeekend(day) }),
7131
+ children: [
7132
+ /* @__PURE__ */ jsx("span", { className: schedulerTimeHeadWeekday(), children: format(day, view === "day" ? "EEEE" : "EEEEEE", { locale }) }),
7133
+ /* @__PURE__ */ jsx(
7134
+ "span",
7135
+ {
7136
+ className: schedulerDayNumber({ today: isSameDay(day, now) }),
7137
+ children: format(day, "d", { locale })
7138
+ }
7139
+ )
7140
+ ]
7141
+ },
7142
+ day.getTime()
7143
+ ))
7144
+ ] }),
7145
+ hasAllDay ? /* @__PURE__ */ jsxs("div", { className: schedulerAllDayRow(), children: [
7146
+ /* @__PURE__ */ jsx("span", { className: schedulerAllDayLabel(), children: "Dia inteiro" }),
7147
+ days.map((day) => /* @__PURE__ */ jsx("div", { className: schedulerAllDayCell(), children: (allDayByDay.get(day.getTime()) ?? []).map((ev) => /* @__PURE__ */ jsx(
7148
+ DraggableEvent,
7149
+ {
7150
+ event: ev,
7151
+ view,
7152
+ variant: "pill",
7153
+ movable: podeMover?.(ev) ?? false,
7154
+ truncateStart: startOfDay(ev.start).getTime() < day.getTime(),
7155
+ truncateEnd: startOfDay(ev.end).getTime() > day.getTime(),
7156
+ onClick: (evt) => onEventClick?.(ev, evt),
7157
+ renderContent: renderEvent ? () => renderEvent({ event: ev, view, selected: false }) : void 0
7158
+ },
7159
+ `${ev.id}-${day.getTime()}`
7160
+ )) }, day.getTime()))
7161
+ ] }) : null,
7162
+ /* @__PURE__ */ jsx("div", { ref: bodyRef, className: schedulerTimeBody(), children: /* @__PURE__ */ jsxs("div", { className: schedulerTimeCanvas(), children: [
7163
+ /* @__PURE__ */ jsxs("div", { className: `${schedulerTimeGutter()} relative`, children: [
7164
+ hours.map((h3) => /* @__PURE__ */ jsx("div", { className: schedulerHourLabel(), children: /* @__PURE__ */ jsx("span", { className: schedulerHourLabelText(), children: fmtHour(h3) }) }, h3)),
7165
+ nowOffset !== null ? /* @__PURE__ */ jsx("span", { className: schedulerNowLabel(), style: { top: nowOffset }, children: fmtTime(now) }) : null
7166
+ ] }),
7167
+ days.map((day, indiceDaColuna) => /* @__PURE__ */ jsxs(
7168
+ DroppableDay,
7169
+ {
7170
+ day,
7171
+ disabled: !dndAtivo,
7172
+ className: schedulerTimeColumn({ weekend: isWeekend(day) }),
7173
+ overClassName: schedulerDropTarget(),
7174
+ children: [
7175
+ hours.map((h3, indiceHora) => /* @__PURE__ */ jsx(
7176
+ "button",
7177
+ {
7178
+ type: "button",
7179
+ className: schedulerHourSlot(),
7180
+ "aria-label": `Criar evento em ${format(day, "PPP", { locale })} às ${fmtHour(h3)}`,
7181
+ onClick: () => {
7182
+ const start = new Date(day);
7183
+ start.setHours(h3, 0, 0, 0);
7184
+ const end = new Date(start);
7185
+ end.setHours(h3 + 1, 0, 0, 0);
7186
+ onSlotClick?.(start, end);
7187
+ },
7188
+ disabled: !onSlotClick,
7189
+ ref: teclado.getCellRef(
7190
+ indiceHora * days.length + indiceDaColuna
7191
+ ),
7192
+ ...teclado.getCellProps(
7193
+ indiceHora * days.length + indiceDaColuna
7194
+ )
7195
+ },
7196
+ h3
7197
+ )),
7198
+ (blocksByDay.get(day.getTime()) ?? []).map((b4) => /* @__PURE__ */ jsx(
7199
+ DraggableEvent,
7200
+ {
7201
+ event: b4.event,
7202
+ view,
7203
+ variant: "block",
7204
+ movable: podeMover?.(b4.event) ?? false,
7205
+ resizable: podeRedimensionar?.(b4.event) ?? false,
7206
+ timeLabel: b4.label,
7207
+ style: {
7208
+ top: b4.top,
7209
+ height: b4.height,
7210
+ left: b4.left,
7211
+ width: b4.width,
7212
+ // 2px de folga à direita pra dois blocos vizinhos não
7213
+ // encostarem; `width` já é a fração da lane.
7214
+ paddingRight: void 0
7215
+ },
7216
+ className: "mr-[2px]",
7217
+ onClick: (evt) => onEventClick?.(b4.event, evt),
7218
+ renderContent: renderEvent ? () => renderEvent({ event: b4.event, view, selected: false }) : void 0
7219
+ },
7220
+ b4.event.id
7221
+ )),
7222
+ nowOffset !== null && isSameDay(day, now) ? /* @__PURE__ */ jsxs("span", { className: schedulerNowLine(), style: { top: nowOffset }, children: [
7223
+ /* @__PURE__ */ jsx("span", { className: schedulerNowDot(), "aria-hidden": "true" }),
7224
+ /* @__PURE__ */ jsx("span", { className: schedulerNowStroke(), "aria-hidden": "true" })
7225
+ ] }) : null
7226
+ ]
7227
+ },
7228
+ day.getTime()
7229
+ ))
7230
+ ] }) })
7231
+ ] });
7232
+ }
7233
+ function SchedulerListView({
7234
+ date,
7235
+ events,
7236
+ locale,
7237
+ hourFormat,
7238
+ now,
7239
+ onEventClick,
7240
+ renderEvent,
7241
+ emptyState
7242
+ }) {
7243
+ const grupos = useMemo(() => {
7244
+ const inicio = startOfMonth(date).getTime();
7245
+ const fim = endOfMonth(date).getTime();
7246
+ const map = /* @__PURE__ */ new Map();
7247
+ for (const ev of events) {
7248
+ let cursor = startOfDay(ev.start);
7249
+ const ultimo = startOfDay(ev.end);
7250
+ let guard = 0;
7251
+ while (cursor.getTime() <= ultimo.getTime() && guard < 400) {
7252
+ const t3 = cursor.getTime();
7253
+ if (t3 >= inicio && t3 <= fim) {
7254
+ const lista = map.get(t3) ?? [];
7255
+ lista.push(ev);
7256
+ map.set(t3, lista);
7257
+ }
7258
+ cursor = new Date(
7259
+ cursor.getFullYear(),
7260
+ cursor.getMonth(),
7261
+ cursor.getDate() + 1
7262
+ );
7263
+ guard++;
7264
+ }
7265
+ }
7266
+ return [...map.entries()].sort(([a11], [b4]) => a11 - b4).map(([t3, lista]) => ({
7267
+ dia: new Date(t3),
7268
+ // Dia inteiro primeiro, depois por horário — é a ordem em que o dia
7269
+ // acontece: o que vale pro dia todo emoldura o resto.
7270
+ eventos: lista.sort((x3, y3) => {
7271
+ if (!!x3.allDay !== !!y3.allDay) return x3.allDay ? -1 : 1;
7272
+ return x3.start.getTime() - y3.start.getTime();
7273
+ })
7274
+ }));
7275
+ }, [events, date]);
7276
+ const fmtTime = (d3) => format(d3, hourFormat === "12h" ? "h:mm a" : "HH:mm", { locale });
7277
+ const rotuloHorario = (ev) => {
7278
+ if (ev.allDay) return "Dia inteiro";
7279
+ if (ev.start.getTime() === ev.end.getTime()) return fmtTime(ev.start);
7280
+ return `${fmtTime(ev.start)} – ${fmtTime(ev.end)}`;
7281
+ };
7282
+ if (grupos.length === 0) {
7283
+ return /* @__PURE__ */ jsx("div", { className: schedulerPlaceholder(), children: emptyState ?? /* @__PURE__ */ jsxs(Fragment, { children: [
7284
+ /* @__PURE__ */ jsx(
7285
+ CalendarSearch,
7286
+ {
7287
+ className: "size-icon-xl text-fg-subtle",
7288
+ "aria-hidden": "true"
7289
+ }
7290
+ ),
7291
+ /* @__PURE__ */ jsxs("span", { className: "text-body-md font-semibold text-fg-default", children: [
7292
+ "Nenhum evento em ",
7293
+ format(date, "MMMM 'de' yyyy", { locale })
7294
+ ] }),
7295
+ /* @__PURE__ */ jsx("span", { className: "max-w-[42ch] text-body-sm text-fg-muted", children: "Ajuste a busca ou os filtros, ou navegue pra outro mês." })
7296
+ ] }) });
7297
+ }
7298
+ return /* @__PURE__ */ jsx("div", { className: schedulerListFrame(), children: grupos.map(({ dia, eventos }) => /* @__PURE__ */ jsxs("div", { className: schedulerListDay(), children: [
7299
+ /* @__PURE__ */ jsxs("div", { className: schedulerListDayHead(), children: [
7300
+ /* @__PURE__ */ jsx("span", { className: schedulerListDayNumber({ today: isSameDay(dia, now) }), children: format(dia, "d", { locale }) }),
7301
+ /* @__PURE__ */ jsx("span", { className: schedulerListDayName(), children: format(dia, "EEEE, MMMM", { locale }) })
7302
+ ] }),
7303
+ /* @__PURE__ */ jsx("div", { className: schedulerListEvents(), children: eventos.map((ev) => /* @__PURE__ */ jsx(
7304
+ SchedulerEventItem,
7305
+ {
7306
+ event: ev,
7307
+ view: "list",
7308
+ variant: "row",
7309
+ timeLabel: rotuloHorario(ev),
7310
+ onClick: (evt) => onEventClick?.(ev, evt),
7311
+ renderContent: renderEvent ? () => renderEvent({ event: ev, view: "list", selected: false }) : void 0
7312
+ },
7313
+ `${ev.id}-${dia.getTime()}`
7314
+ )) })
7315
+ ] }, dia.getTime())) });
7316
+ }
7317
+ function matchesField(event, fieldId, values) {
7318
+ if (values.length === 0) return true;
7319
+ switch (fieldId) {
7320
+ case "categoryId":
7321
+ return event.categoryId !== void 0 && values.includes(event.categoryId);
7322
+ case "tagIds":
7323
+ return (event.tagIds ?? []).some((tag) => values.includes(tag));
7324
+ case "color":
7325
+ return values.includes(event.color ?? "brand");
7326
+ default:
7327
+ return true;
7328
+ }
7329
+ }
7330
+ function matchesSearch(event, term) {
7331
+ if (term === "") return true;
7332
+ const haystack = [];
7333
+ if (event.searchText) haystack.push(event.searchText);
7334
+ if (typeof event.title === "string") haystack.push(event.title);
7335
+ if (typeof event.description === "string") haystack.push(event.description);
7336
+ if (event.tagIds?.length) haystack.push(...event.tagIds);
7337
+ return haystack.some((piece) => piece.toLowerCase().includes(term));
7338
+ }
7339
+ function useSchedulerFilter({
7340
+ events,
7341
+ searchable,
7342
+ search: searchProp,
7343
+ onSearchChange,
7344
+ filterFields,
7345
+ filterModel: modelProp,
7346
+ onFilterModelChange,
7347
+ filterMode
7348
+ }) {
7349
+ const isSearchControlled = searchProp !== void 0;
7350
+ const isModelControlled = modelProp !== void 0;
7351
+ const [searchState, setSearchState] = useState("");
7352
+ const [modelState, setModelState] = useState({});
7353
+ const search = isSearchControlled ? searchProp : searchState;
7354
+ const filterModel = isModelControlled ? modelProp : modelState;
7355
+ useRef(false);
7356
+ useEffect(() => {
7357
+ return;
7358
+ }, [filterFields, filterMode]);
7359
+ const commitSearch = useCallback(
7360
+ (next) => {
7361
+ if (!isSearchControlled) setSearchState(next);
7362
+ onSearchChange?.(next);
7363
+ },
7364
+ [isSearchControlled, onSearchChange]
7365
+ );
7366
+ const commitModel = useCallback(
7367
+ (next) => {
7368
+ if (!isModelControlled) setModelState(next);
7369
+ onFilterModelChange?.(next);
7370
+ },
7371
+ [isModelControlled, onFilterModelChange]
7372
+ );
7373
+ const toggleValue = useCallback(
7374
+ (fieldId, value) => {
7375
+ const current = filterModel[fieldId] ?? [];
7376
+ const nextValues = current.includes(value) ? current.filter((v3) => v3 !== value) : [...current, value];
7377
+ const next = { ...filterModel };
7378
+ if (nextValues.length === 0) {
7379
+ delete next[fieldId];
7380
+ } else {
7381
+ next[fieldId] = nextValues;
7382
+ }
7383
+ commitModel(next);
7384
+ },
7385
+ [commitModel, filterModel]
7386
+ );
7387
+ const clearField = useCallback(
7388
+ (fieldId) => {
7389
+ const next = { ...filterModel };
7390
+ delete next[fieldId];
7391
+ commitModel(next);
7392
+ },
7393
+ [commitModel, filterModel]
7394
+ );
7395
+ const clearAll = useCallback(() => {
7396
+ commitModel({});
7397
+ commitSearch("");
7398
+ }, [commitModel, commitSearch]);
7399
+ const appliedCount = useMemo(
7400
+ () => Object.values(filterModel).reduce(
7401
+ (total, values) => total + values.length,
7402
+ 0
7403
+ ),
7404
+ [filterModel]
7405
+ );
7406
+ const optionCounts = useMemo(() => {
7407
+ const term = searchable ? search.trim().toLowerCase() : "";
7408
+ const base = events.filter((e3) => matchesSearch(e3, term));
7409
+ const out = {};
7410
+ for (const field of filterFields ?? []) {
7411
+ const perValue = {};
7412
+ for (const option of field.options) {
7413
+ perValue[option.value] = base.filter(
7414
+ (e3) => matchesField(e3, field.id, [option.value])
7415
+ ).length;
7416
+ }
7417
+ out[field.id] = perValue;
7418
+ }
7419
+ return out;
7420
+ }, [events, filterFields, search, searchable]);
7421
+ const filteredEvents = useMemo(() => {
7422
+ if (filterMode === "server") return events;
7423
+ const term = searchable ? search.trim().toLowerCase() : "";
7424
+ const activeFields = Object.entries(filterModel).filter(
7425
+ ([, values]) => values.length > 0
7426
+ );
7427
+ if (term === "" && activeFields.length === 0) return events;
7428
+ return events.filter((event) => {
7429
+ if (!matchesSearch(event, term)) return false;
7430
+ return activeFields.every(
7431
+ ([fieldId, values]) => matchesField(event, fieldId, values)
7432
+ );
7433
+ });
7434
+ }, [events, filterMode, filterModel, search, searchable]);
7435
+ return {
7436
+ search,
7437
+ setSearch: commitSearch,
7438
+ filterModel,
7439
+ toggleValue,
7440
+ clearField,
7441
+ clearAll,
7442
+ appliedCount,
7443
+ optionCounts,
7444
+ filteredEvents
7445
+ };
7446
+ }
7447
+ function useSchedulerState({
7448
+ defaultDate,
7449
+ date: dateProp,
7450
+ onDateChange,
7451
+ defaultView,
7452
+ view: viewProp,
7453
+ onViewChange,
7454
+ weekStartsOn,
7455
+ locale,
7456
+ now
7457
+ }) {
7458
+ const isDateControlled = dateProp !== void 0;
7459
+ const isViewControlled = viewProp !== void 0;
7460
+ const [dateState, setDateState] = useState(
7461
+ () => defaultDate ?? startOfDay(now)
7462
+ );
7463
+ const [viewState, setViewState] = useState(
7464
+ () => defaultView ?? "month"
7465
+ );
7466
+ const date = isDateControlled ? dateProp : dateState;
7467
+ const view = isViewControlled ? viewProp : viewState;
7468
+ useRef(false);
7469
+ useEffect(() => {
7470
+ return;
7471
+ }, [dateProp, defaultDate, viewProp, defaultView, onDateChange, onViewChange]);
7472
+ const commitDate = useCallback(
7473
+ (next2) => {
7474
+ if (!isDateControlled) setDateState(next2);
7475
+ onDateChange?.(next2);
7476
+ },
7477
+ [isDateControlled, onDateChange]
7478
+ );
7479
+ const commitView = useCallback(
7480
+ (next2) => {
7481
+ if (!isViewControlled) setViewState(next2);
7482
+ onViewChange?.(next2);
7483
+ },
7484
+ [isViewControlled, onViewChange]
7485
+ );
7486
+ const shift = useCallback(
7487
+ (direction) => {
7488
+ const step = view === "month" ? addMonths(date, direction) : view === "week" ? addWeeks(date, direction) : addDays(date, direction);
7489
+ commitDate(startOfDay(step));
7490
+ },
7491
+ [commitDate, date, view]
7492
+ );
7493
+ const next = useCallback(() => shift(1), [shift]);
7494
+ const prev = useCallback(() => shift(-1), [shift]);
7495
+ const goToToday = useCallback(
7496
+ () => commitDate(startOfDay(now)),
7497
+ [commitDate, now]
7498
+ );
7499
+ const goToDate = useCallback(
7500
+ (target) => commitDate(startOfDay(target)),
7501
+ [commitDate]
7502
+ );
7503
+ const visibleRange = useMemo(() => {
7504
+ switch (view) {
7505
+ case "month": {
7506
+ const gridStart = startOfWeek(startOfMonth(date), {
7507
+ weekStartsOn,
7508
+ locale
7509
+ });
7510
+ return { start: gridStart, end: endOfDay(addDays(gridStart, 41)) };
7511
+ }
7512
+ case "week":
7513
+ return {
7514
+ start: startOfWeek(date, { weekStartsOn, locale }),
7515
+ end: endOfWeek(date, { weekStartsOn, locale })
7516
+ };
7517
+ case "day":
7518
+ return { start: startOfDay(date), end: endOfDay(date) };
7519
+ case "list":
7520
+ default:
7521
+ return { start: startOfMonth(date), end: endOfMonth(date) };
7522
+ }
7523
+ }, [date, view, weekStartsOn, locale]);
7524
+ return {
7525
+ date,
7526
+ view,
7527
+ setDate: commitDate,
7528
+ setView: commitView,
7529
+ next,
7530
+ prev,
7531
+ goToToday,
7532
+ goToDate,
7533
+ visibleRange
7534
+ };
7535
+ }
7536
+ function useMediaQuery(query) {
7537
+ const [matches, setMatches] = useState(() => {
7538
+ if (typeof window === "undefined") return false;
7539
+ return window.matchMedia(query).matches;
7540
+ });
7541
+ useEffect(() => {
7542
+ if (typeof window === "undefined") return;
7543
+ const mql = window.matchMedia(query);
7544
+ const handler = (e3) => setMatches(e3.matches);
7545
+ setMatches(mql.matches);
7546
+ mql.addEventListener("change", handler);
7547
+ return () => mql.removeEventListener("change", handler);
7548
+ }, [query]);
7549
+ return matches;
7550
+ }
7551
+ const Scheduler = forwardRef(
7552
+ function Scheduler2({
7553
+ events,
7554
+ locale,
7555
+ weekStartsOn = 0,
7556
+ hourFormat = "24h",
7557
+ nowIndicator = true,
7558
+ dayRange = [0, 24],
7559
+ scrollToHour = 8,
7560
+ snapMinutes = 15,
7561
+ defaultDate,
7562
+ date,
7563
+ onDateChange,
7564
+ defaultView,
7565
+ view,
7566
+ onViewChange,
7567
+ onEventClick,
7568
+ onSlotClick,
7569
+ onEventMove,
7570
+ onEventResize,
7571
+ draggable = false,
7572
+ resizable = false,
7573
+ searchable = true,
7574
+ search,
7575
+ onSearchChange,
7576
+ filterFields,
7577
+ filterModel,
7578
+ onFilterModelChange,
7579
+ filterMode = "client",
7580
+ defaultFilterPanelOpen = false,
7581
+ toolbarActions,
7582
+ primaryAction,
7583
+ title: title2,
7584
+ renderEvent,
7585
+ emptyState,
7586
+ className,
7587
+ ...rest
7588
+ }, ref) {
7589
+ const nowRef = useRef(/* @__PURE__ */ new Date());
7590
+ const now = nowRef.current;
7591
+ const state = useSchedulerState({
7592
+ defaultDate,
7593
+ date,
7594
+ onDateChange,
7595
+ defaultView,
7596
+ view,
7597
+ onViewChange,
7598
+ weekStartsOn,
7599
+ locale,
7600
+ now
7601
+ });
7602
+ const filter = useSchedulerFilter({
7603
+ events,
7604
+ searchable,
7605
+ search,
7606
+ onSearchChange,
7607
+ filterFields,
7608
+ filterModel,
7609
+ onFilterModelChange,
7610
+ filterMode
7611
+ });
7612
+ const dndAtivo = Boolean(
7613
+ (draggable || resizable || events.some((e3) => e3.draggable || e3.resizable)) && (onEventMove || onEventResize)
7614
+ );
7615
+ const dnd = useSchedulerDnd({
7616
+ enabled: dndAtivo,
7617
+ view: state.view,
7618
+ events: filter.filteredEvents,
7619
+ snapMinutes,
7620
+ dayRange,
7621
+ hourHeight: HOUR_HEIGHT_PX,
7622
+ draggable,
7623
+ resizable,
7624
+ onEventMove,
7625
+ onEventResize
7626
+ });
7627
+ useRef(false);
7628
+ useEffect(() => {
7629
+ return;
7630
+ }, [draggable, resizable, onEventMove, onEventResize]);
7631
+ const eventoArrastado = dnd.dragAtivo?.kind === "move" ? filter.filteredEvents.find((e3) => e3.id === dnd.dragAtivo?.eventId) ?? null : null;
7632
+ const wideLayout = useMediaQuery("(min-width: 1024px)");
7633
+ const [filterPanelOpen, setFilterPanelOpen] = useState(defaultFilterPanelOpen);
7634
+ const hasFilterFields = (filterFields?.length ?? 0) > 0;
7635
+ const showFilterPanel = filterPanelOpen && wideLayout && hasFilterFields;
7636
+ const showFilterDrawer = filterPanelOpen && !wideLayout && hasFilterFields;
7637
+ useImperativeHandle(
7638
+ ref,
7639
+ () => ({
7640
+ goToDate: state.goToDate,
7641
+ goToToday: state.goToToday,
7642
+ next: state.next,
7643
+ prev: state.prev,
7644
+ getVisibleRange: () => state.visibleRange
7645
+ }),
7646
+ [state]
7647
+ );
7648
+ const periodTitle = useMemo(() => {
7649
+ if (title2 !== void 0) return title2;
7650
+ const { start, end } = state.visibleRange;
7651
+ switch (state.view) {
7652
+ case "week": {
7653
+ const sameMonth = start.getMonth() === end.getMonth();
7654
+ return sameMonth ? `${format(start, "d", { locale })}–${format(end, "d 'de' MMMM yyyy", { locale })}` : `${format(start, "d MMM", { locale })} – ${format(end, "d MMM yyyy", { locale })}`;
7655
+ }
7656
+ case "day":
7657
+ return format(state.date, "EEEE, d 'de' MMMM yyyy", { locale });
7658
+ case "month":
7659
+ case "list":
7660
+ default:
7661
+ return format(state.date, "MMMM yyyy", { locale });
7662
+ }
7663
+ }, [title2, state.view, state.date, state.visibleRange, locale]);
7664
+ return (
7665
+ /* `role="region"` + `aria-label` com o período: é o que dá ao leitor de
7666
+ tela um ponto de navegação ("Calendário, setembro 2026") sem que o
7667
+ título do período precise se declarar `<h2>` e entrar no outline da
7668
+ página. O rótulo só é montado quando o título é string — com um
7669
+ `ReactNode` custom não dá pra derivar texto sem varrer a árvore. */
7670
+ /* @__PURE__ */ jsx(
7671
+ "div",
7672
+ {
7673
+ role: "region",
7674
+ "aria-label": typeof periodTitle === "string" ? `Calendário, ${periodTitle}` : "Calendário",
7675
+ className: cn(schedulerRoot(), className),
7676
+ ...rest,
7677
+ children: /* @__PURE__ */ jsxs(
7678
+ DndContext,
7679
+ {
7680
+ sensors: dnd.sensors,
7681
+ onDragStart: dnd.handleDragStart,
7682
+ onDragOver: dnd.handleDragOver,
7683
+ onDragEnd: dnd.handleDragEnd,
7684
+ onDragCancel: dnd.handleDragCancel,
7685
+ children: [
7686
+ /* @__PURE__ */ jsx(
7687
+ SchedulerToolbar,
7688
+ {
7689
+ title: periodTitle,
7690
+ view: state.view,
7691
+ onViewChange: state.setView,
7692
+ onPrev: state.prev,
7693
+ onNext: state.next,
7694
+ onToday: state.goToToday,
7695
+ searchable,
7696
+ search: filter.search,
7697
+ onSearchChange: filter.setSearch,
7698
+ filterFields,
7699
+ filterModel: filter.filterModel,
7700
+ onClearField: filter.clearField,
7701
+ onClearAll: filter.clearAll,
7702
+ appliedCount: filter.appliedCount,
7703
+ filterPanelOpen: showFilterPanel || showFilterDrawer,
7704
+ onToggleFilterPanel: () => setFilterPanelOpen((o2) => !o2),
7705
+ toolbarActions,
7706
+ primaryAction
7707
+ }
7708
+ ),
7709
+ /* @__PURE__ */ jsxs("div", { className: schedulerBody(), children: [
7710
+ /* @__PURE__ */ jsx("div", { className: schedulerMain(), children: state.view === "month" ? /* @__PURE__ */ jsx(
7711
+ SchedulerMonthView,
7712
+ {
7713
+ date: state.date,
7714
+ events: filter.filteredEvents,
7715
+ locale,
7716
+ weekStartsOn,
7717
+ hourFormat,
7718
+ now,
7719
+ onEventClick,
7720
+ onSlotClick,
7721
+ renderEvent,
7722
+ dndAtivo,
7723
+ podeMover: dnd.podeMover
7724
+ }
7725
+ ) : state.view === "list" ? /* @__PURE__ */ jsx(
7726
+ SchedulerListView,
7727
+ {
7728
+ date: state.date,
7729
+ events: filter.filteredEvents,
7730
+ locale,
7731
+ hourFormat,
7732
+ now,
7733
+ onEventClick,
7734
+ renderEvent,
7735
+ emptyState
7736
+ }
7737
+ ) : (
7738
+ /* `week` e `day` são a MESMA view, com 7 ou 1 coluna. */
7739
+ /* @__PURE__ */ jsx(
7740
+ SchedulerTimeGrid,
7741
+ {
7742
+ date: state.date,
7743
+ view: state.view,
7744
+ events: filter.filteredEvents,
7745
+ locale,
7746
+ weekStartsOn,
7747
+ hourFormat,
7748
+ dayRange,
7749
+ scrollToHour,
7750
+ nowIndicator,
7751
+ now,
7752
+ snapMinutes,
7753
+ onEventClick,
7754
+ onSlotClick,
7755
+ renderEvent,
7756
+ dndAtivo,
7757
+ podeMover: dnd.podeMover,
7758
+ podeRedimensionar: dnd.podeRedimensionar
7759
+ }
7760
+ )
7761
+ ) }),
7762
+ showFilterPanel && filterFields ? /* @__PURE__ */ jsx(
7763
+ SchedulerFilterPanel,
7764
+ {
7765
+ filterFields,
7766
+ filterModel: filter.filterModel,
7767
+ onToggleValue: filter.toggleValue,
7768
+ onClearAll: filter.clearAll,
7769
+ appliedCount: filter.appliedCount,
7770
+ onClose: () => setFilterPanelOpen(false),
7771
+ counts: filter.optionCounts,
7772
+ date: state.date,
7773
+ now,
7774
+ locale,
7775
+ weekStartsOn,
7776
+ events: filter.filteredEvents,
7777
+ onDateSelect: state.goToDate
7778
+ }
7779
+ ) : null
7780
+ ] }),
7781
+ showFilterDrawer && filterFields ? /* @__PURE__ */ jsx(
7782
+ FloatingPanel,
7783
+ {
7784
+ open: true,
7785
+ onOpenChange: (aberto) => setFilterPanelOpen(aberto),
7786
+ side: "right",
7787
+ size: "sm",
7788
+ bodyPadded: false,
7789
+ title: "Filtros",
7790
+ titleIcon: ListFilter,
7791
+ description: filter.appliedCount === 1 ? "1 filtro ativo" : `${filter.appliedCount} filtros ativos`,
7792
+ footer: /* @__PURE__ */ jsx(
7793
+ Button,
7794
+ {
7795
+ variant: "ghost",
7796
+ color: "secondary",
7797
+ size: "sm",
7798
+ onClick: filter.clearAll,
7799
+ disabled: filter.appliedCount === 0,
7800
+ children: "Limpar tudo"
7801
+ }
7802
+ ),
7803
+ children: /* @__PURE__ */ jsx(
7804
+ SchedulerFilterPanel,
7805
+ {
7806
+ embedded: true,
7807
+ filterFields,
7808
+ filterModel: filter.filterModel,
7809
+ onToggleValue: filter.toggleValue,
7810
+ onClearAll: filter.clearAll,
7811
+ appliedCount: filter.appliedCount,
7812
+ onClose: () => setFilterPanelOpen(false),
7813
+ counts: filter.optionCounts,
7814
+ date: state.date,
7815
+ now,
7816
+ locale,
7817
+ weekStartsOn,
7818
+ events: filter.filteredEvents,
7819
+ onDateSelect: (d3) => {
7820
+ state.goToDate(d3);
7821
+ setFilterPanelOpen(false);
7822
+ }
7823
+ }
7824
+ )
7825
+ }
7826
+ ) : null,
7827
+ /* @__PURE__ */ jsx(DragOverlay, { dropAnimation: null, children: eventoArrastado ? /* @__PURE__ */ jsx(
7828
+ SchedulerEventItem,
7829
+ {
7830
+ event: eventoArrastado,
7831
+ view: state.view,
7832
+ variant: state.view === "month" ? "pill" : "block",
7833
+ dragging: true,
7834
+ timeLabel: eventoArrastado.allDay ? void 0 : format(
7835
+ eventoArrastado.start,
7836
+ hourFormat === "12h" ? "h:mm a" : "HH:mm",
7837
+ { locale }
7838
+ )
7839
+ }
7840
+ ) : null })
7841
+ ]
7842
+ }
7843
+ )
7844
+ }
7845
+ )
7846
+ );
7847
+ }
7848
+ );
5257
7849
  const spinnerStyles = tv({
5258
7850
  base: ["inline-block shrink-0", "animate-spin motion-reduce:animate-none"],
5259
7851
  variants: {
@@ -6168,9 +8760,9 @@ function cartesianAddInPlace(a11, b4) {
6168
8760
  function cartesianScale(vector, k2) {
6169
8761
  return [vector[0] * k2, vector[1] * k2, vector[2] * k2];
6170
8762
  }
6171
- function cartesianNormalizeInPlace(d4) {
6172
- var l2 = sqrt(d4[0] * d4[0] + d4[1] * d4[1] + d4[2] * d4[2]);
6173
- d4[0] /= l2, d4[1] /= l2, d4[2] /= l2;
8763
+ function cartesianNormalizeInPlace(d3) {
8764
+ var l2 = sqrt(d3[0] * d3[0] + d3[1] * d3[1] + d3[2] * d3[2]);
8765
+ d3[0] /= l2, d3[1] /= l2, d3[2] /= l2;
6174
8766
  }
6175
8767
  function compose(a11, b4) {
6176
8768
  function compose2(x3, y3) {
@@ -7069,12 +9661,12 @@ function append(strings) {
7069
9661
  }
7070
9662
  }
7071
9663
  function appendRound(digits) {
7072
- const d4 = Math.floor(digits);
7073
- if (!(d4 >= 0)) throw new RangeError(`invalid digits: ${digits}`);
7074
- if (d4 > 15) return append;
7075
- if (d4 !== cacheDigits) {
7076
- const k2 = 10 ** d4;
7077
- cacheDigits = d4;
9664
+ const d3 = Math.floor(digits);
9665
+ if (!(d3 >= 0)) throw new RangeError(`invalid digits: ${digits}`);
9666
+ if (d3 > 15) return append;
9667
+ if (d3 !== cacheDigits) {
9668
+ const k2 = 10 ** d3;
9669
+ cacheDigits = d3;
7078
9670
  cacheAppend = function append2(strings) {
7079
9671
  let i4 = 1;
7080
9672
  this._ += strings[0];
@@ -7130,9 +9722,9 @@ function geoPath(projection2, context) {
7130
9722
  if (!arguments.length) return digits;
7131
9723
  if (_3 == null) digits = null;
7132
9724
  else {
7133
- const d4 = Math.floor(_3);
7134
- if (!(d4 >= 0)) throw new RangeError(`invalid digits: ${_3}`);
7135
- digits = d4;
9725
+ const d3 = Math.floor(_3);
9726
+ if (!(d3 >= 0)) throw new RangeError(`invalid digits: ${_3}`);
9727
+ digits = d3;
7136
9728
  }
7137
9729
  if (context === null) contextStream = new PathString(digits);
7138
9730
  return path;
@@ -7669,18 +10261,18 @@ const ChoroplethMap = forwardRef(
7669
10261
  });
7670
10262
  },
7671
10263
  children: [
7672
- geometry.map(({ feature: feature2, d: d4 }, index) => {
10264
+ geometry.map(({ feature: feature2, d: d3 }, index) => {
7673
10265
  const id = getId(feature2);
7674
10266
  const value = values[id];
7675
10267
  return /* @__PURE__ */ jsx(
7676
10268
  "path",
7677
10269
  {
7678
- d: d4,
10270
+ d: d3,
7679
10271
  fill: fillFor(value),
7680
10272
  strokeWidth,
7681
10273
  className: styles2.path(),
7682
10274
  style: onFeatureClick ? { cursor: "pointer" } : void 0,
7683
- onMouseEnter: () => setHover({ id, name: getName(feature2), value, feature: feature2, d: d4 }),
10275
+ onMouseEnter: () => setHover({ id, name: getName(feature2), value, feature: feature2, d: d3 }),
7684
10276
  onClick: onFeatureClick ? () => onFeatureClick({
7685
10277
  id,
7686
10278
  name: getName(feature2),
@@ -8148,7 +10740,7 @@ function useDataList({
8148
10740
  for (const f3 of filterFields ?? []) m2.set(f3.id, f3);
8149
10741
  return m2;
8150
10742
  }, [filterFields]);
8151
- const matchesSearch = useCallback(
10743
+ const matchesSearch2 = useCallback(
8152
10744
  (item) => {
8153
10745
  const s2 = query.search.trim().toLowerCase();
8154
10746
  if (!s2) return true;
@@ -8164,9 +10756,9 @@ function useDataList({
8164
10756
  );
8165
10757
  const processedItems = useMemo(() => {
8166
10758
  if (mode === "server") return items;
8167
- const searched = query.search ? items.filter(matchesSearch) : items;
10759
+ const searched = query.search ? items.filter(matchesSearch2) : items;
8168
10760
  return applyFilterModel(searched, query.filterModel, fieldsById);
8169
- }, [mode, items, query.search, query.filterModel, matchesSearch, fieldsById]);
10761
+ }, [mode, items, query.search, query.filterModel, matchesSearch2, fieldsById]);
8170
10762
  return {
8171
10763
  query,
8172
10764
  activeViewId,
@@ -8610,7 +11202,7 @@ function DataList(props) {
8610
11202
  getMenuItems
8611
11203
  }
8612
11204
  ) : /* @__PURE__ */ jsx(
8613
- List,
11205
+ List$1,
8614
11206
  {
8615
11207
  layout,
8616
11208
  items: ctrl.processedItems,
@@ -8911,7 +11503,7 @@ export {
8911
11503
  BRANDS,
8912
11504
  B3 as Badge,
8913
11505
  B2 as Breadcrumb,
8914
- d3 as BreadcrumbEllipsis,
11506
+ d2 as BreadcrumbEllipsis,
8915
11507
  e2 as BreadcrumbItem,
8916
11508
  f2 as BreadcrumbLink,
8917
11509
  g2 as BreadcrumbList,
@@ -8959,7 +11551,7 @@ export {
8959
11551
  FileUploadField,
8960
11552
  N as FilterPanel,
8961
11553
  FilterPopover,
8962
- d as FloatingPanel,
11554
+ FloatingPanel,
8963
11555
  O as FloatingPanelField,
8964
11556
  Q as FloatingPanelSection,
8965
11557
  R as FooterTable,
@@ -8971,7 +11563,7 @@ export {
8971
11563
  V as FormFieldSwitch,
8972
11564
  c as FormFieldTextarea,
8973
11565
  H2 as Header,
8974
- d2 as HeaderBreadcrumb,
11566
+ d as HeaderBreadcrumb,
8975
11567
  e as HeaderMessages,
8976
11568
  f as HeaderNotifications,
8977
11569
  g as HeaderSearch,
@@ -8988,7 +11580,7 @@ export {
8988
11580
  a10 as Kpi,
8989
11581
  b3 as KpiDelta,
8990
11582
  K2 as KpiGroup,
8991
- List,
11583
+ List$1 as List,
8992
11584
  ListItem,
8993
11585
  MarkdownText,
8994
11586
  M2 as MenuSidebar,
@@ -9003,10 +11595,10 @@ export {
9003
11595
  I2 as MoreMenuCheckboxItem,
9004
11596
  DropdownMenuItem as MoreMenuItem,
9005
11597
  DropdownMenuLabel as MoreMenuLabel,
9006
- O2 as MoreMenuRadioGroup,
9007
- P2 as MoreMenuRadioItem,
11598
+ DropdownMenuRadioGroup as MoreMenuRadioGroup,
11599
+ DropdownMenuRadioItem as MoreMenuRadioItem,
9008
11600
  DropdownMenuSeparator as MoreMenuSeparator,
9009
- P3 as PageHeader,
11601
+ P2 as PageHeader,
9010
11602
  P as Panel,
9011
11603
  Y as PanelBody,
9012
11604
  G2 as PanelCloseAction,
@@ -9014,8 +11606,10 @@ export {
9014
11606
  Z as PanelFooter,
9015
11607
  _ as PanelHeader,
9016
11608
  F2 as PanelRoot,
9017
- O3 as PanelTrigger,
11609
+ O2 as PanelTrigger,
9018
11610
  $ as SELECTION_COLUMN_WIDTH,
11611
+ Scheduler,
11612
+ SchedulerFilterPanel,
9019
11613
  ScreenLoader,
9020
11614
  S2 as SidebarBrandIcon,
9021
11615
  i as SidebarItem,