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