@lavalogic/scoria 0.40.32 → 0.40.33

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 (135) hide show
  1. package/README.md +58 -58
  2. package/dist/Components/AccordionGroup.svelte +28 -28
  3. package/dist/Components/AccordionGroupButton.svelte +60 -60
  4. package/dist/Components/Action.svelte +48 -48
  5. package/dist/Components/AlertModal.svelte +102 -102
  6. package/dist/Components/Base/ContextWrapper.svelte +45 -45
  7. package/dist/Components/Base/Pagination.svelte +135 -135
  8. package/dist/Components/Base/Snippets.svelte +25 -25
  9. package/dist/Components/BigRadioSet.svelte +34 -34
  10. package/dist/Components/Bubble.svelte +38 -38
  11. package/dist/Components/Button.svelte +83 -83
  12. package/dist/Components/Checkbox.svelte +71 -71
  13. package/dist/Components/CollapsibleCard.svelte +59 -59
  14. package/dist/Components/CollapsibleMenuGroup.svelte +43 -43
  15. package/dist/Components/ConditionBuilder/ConditionBuilder.svelte +101 -101
  16. package/dist/Components/Contexts/ContextMenu.svelte +76 -76
  17. package/dist/Components/Contexts/ContextMenuDivider.svelte +15 -15
  18. package/dist/Components/Contexts/ContextMenuOption.svelte +38 -38
  19. package/dist/Components/Contexts/Toast.svelte +71 -71
  20. package/dist/Components/Contexts/ToastContainer.svelte +34 -34
  21. package/dist/Components/Contexts/Tooltip.svelte +32 -32
  22. package/dist/Components/DataMatrixIcon.svelte +37 -37
  23. package/dist/Components/DateInput.svelte +80 -80
  24. package/dist/Components/DatePicker.svelte +86 -86
  25. package/dist/Components/DesktopModal.svelte +65 -65
  26. package/dist/Components/Dial.svelte +35 -35
  27. package/dist/Components/DimensionInput.svelte +95 -95
  28. package/dist/Components/DragMenu/DragMenuHolder.svelte +53 -53
  29. package/dist/Components/DragMenu/DraggableCard.svelte +49 -49
  30. package/dist/Components/FileCard.svelte +18 -18
  31. package/dist/Components/FileUpload.svelte +115 -115
  32. package/dist/Components/GridInput.svelte +123 -123
  33. package/dist/Components/GridInputSet.svelte +21 -21
  34. package/dist/Components/HorizontalTabGroup.svelte +54 -54
  35. package/dist/Components/HorizontalTabGroupButton.svelte +32 -32
  36. package/dist/Components/Icon.svelte +68 -68
  37. package/dist/Components/InfoAlert.svelte +32 -32
  38. package/dist/Components/LoadingAnimation.svelte +228 -227
  39. package/dist/Components/LoadingModal.svelte +19 -19
  40. package/dist/Components/LoadingOverlay.svelte +20 -20
  41. package/dist/Components/Modal.svelte +127 -127
  42. package/dist/Components/MultiSelect.svelte +126 -126
  43. package/dist/Components/Nav/Nav.svelte +71 -71
  44. package/dist/Components/Nav/NavServiceMenuList.svelte +37 -37
  45. package/dist/Components/Nav/NavTab.svelte +46 -46
  46. package/dist/Components/Nav/ServiceMenuItem.svelte +62 -62
  47. package/dist/Components/Nav/ServicesNav.svelte +50 -50
  48. package/dist/Components/NumberInput.svelte +98 -98
  49. package/dist/Components/OptionCards.svelte +58 -58
  50. package/dist/Components/PageHeading.svelte +21 -21
  51. package/dist/Components/PasswordInput.svelte +61 -61
  52. package/dist/Components/PopoverButton.svelte +53 -53
  53. package/dist/Components/Portal.svelte +7 -7
  54. package/dist/Components/ProgressBubble.svelte +42 -42
  55. package/dist/Components/QuerySelect.svelte +102 -102
  56. package/dist/Components/ResourceCalendar/ResourceCalendar.svelte +241 -241
  57. package/dist/Components/SidebarPanel.svelte +32 -32
  58. package/dist/Components/SingleSelect.svelte +122 -122
  59. package/dist/Components/StepButton.svelte +48 -48
  60. package/dist/Components/StepForm.svelte +58 -58
  61. package/dist/Components/Switch.svelte +43 -43
  62. package/dist/Components/Table/ARCHITECTURE.md +298 -298
  63. package/dist/Components/Table/Body/Rows/ColumnHighlight.svelte +30 -30
  64. package/dist/Components/Table/Body/Rows/ColumnList.svelte +24 -24
  65. package/dist/Components/Table/Body/Rows/ColumnListRow.svelte +165 -165
  66. package/dist/Components/Table/Body/Rows/Columns/AccessorComponent.svelte +13 -13
  67. package/dist/Components/Table/Body/Rows/Columns/ActionsCell.svelte +120 -120
  68. package/dist/Components/Table/Body/Rows/Columns/BubbleCell.svelte +81 -81
  69. package/dist/Components/Table/Body/Rows/Columns/ExpandCell.svelte +92 -92
  70. package/dist/Components/Table/Body/Rows/Columns/FocusableImmutableCell.svelte +110 -110
  71. package/dist/Components/Table/Body/Rows/Columns/ProgressCell.svelte +54 -54
  72. package/dist/Components/Table/Body/Rows/Columns/TableAction.svelte +35 -35
  73. package/dist/Components/Table/Body/Rows/Columns/TableCheckbox.svelte +64 -64
  74. package/dist/Components/Table/Body/Rows/Columns/TableColumn.svelte +82 -82
  75. package/dist/Components/Table/Body/Rows/Columns/TableDatePicker.svelte +88 -88
  76. package/dist/Components/Table/Body/Rows/Columns/TableNumberInput.svelte +90 -90
  77. package/dist/Components/Table/Body/Rows/Columns/TableQuerySelect.svelte +96 -96
  78. package/dist/Components/Table/Body/Rows/Columns/TableRowSelectionCheckbox.svelte +71 -71
  79. package/dist/Components/Table/Body/Rows/Columns/TableSelect.svelte +124 -124
  80. package/dist/Components/Table/Body/Rows/Columns/TableTextInput.svelte +91 -91
  81. package/dist/Components/Table/Body/Rows/Columns/ValidityCell.svelte +88 -88
  82. package/dist/Components/Table/Body/Rows/TableRow.svelte +127 -127
  83. package/dist/Components/Table/Body/TableBody.svelte +92 -92
  84. package/dist/Components/Table/Headers/HighlightAllHeader.svelte +25 -25
  85. package/dist/Components/Table/Headers/SelectAllHeader.svelte +20 -20
  86. package/dist/Components/Table/Headers/TableHead.svelte +175 -175
  87. package/dist/Components/Table/Misc/ColumnPanel.svelte +32 -32
  88. package/dist/Components/Table/Misc/ColumnPanelModal.svelte +29 -29
  89. package/dist/Components/Table/Misc/FilterInput.svelte +326 -326
  90. package/dist/Components/Table/Misc/FilterPanel.svelte +79 -79
  91. package/dist/Components/Table/Misc/FilterPanelModal.svelte +21 -21
  92. package/dist/Components/Table/Misc/ShareViewModal.svelte +118 -118
  93. package/dist/Components/Table/Misc/SidePanel.svelte +24 -24
  94. package/dist/Components/Table/Misc/TableConfigurationModal.svelte +318 -318
  95. package/dist/Components/Table/Misc/TableFooter.svelte +201 -201
  96. package/dist/Components/Table/Misc/TableHorizontalBar.svelte +112 -112
  97. package/dist/Components/Table/Misc/TableSidebar.svelte +116 -116
  98. package/dist/Components/Table/Misc/TableViewDropdown.svelte +25 -25
  99. package/dist/Components/Table/Misc/ToolboxPanel.svelte +60 -60
  100. package/dist/Components/Table/Misc/ToolboxPanelModal.svelte +63 -63
  101. package/dist/Components/Table/Misc/UpdateViewOptionsModal.svelte +101 -101
  102. package/dist/Components/Table/NestedTable.svelte +29 -29
  103. package/dist/Components/Table/Table.svelte +140 -140
  104. package/dist/Components/Test/TestContextConsumer.svelte +8 -8
  105. package/dist/Components/TextArea.svelte +92 -90
  106. package/dist/Components/TextInput.svelte +128 -117
  107. package/dist/Components/ThreeStateRadio.svelte +101 -101
  108. package/dist/Components/Touch/BoolCard.svelte +27 -27
  109. package/dist/Components/Touch/DateModal.svelte +98 -98
  110. package/dist/Components/Touch/EditCard.svelte +44 -44
  111. package/dist/Components/Touch/InfoCard.svelte +25 -25
  112. package/dist/Components/Touch/InfoTextCard.svelte +22 -22
  113. package/dist/Components/Touch/ModalExplanation.svelte +13 -13
  114. package/dist/Components/Touch/NumberKeyboard.svelte +81 -81
  115. package/dist/Components/Touch/NumberModal.svelte +49 -49
  116. package/dist/Components/Touch/PlusMinusCard.svelte +40 -40
  117. package/dist/Components/Touch/SelectModal.svelte +86 -86
  118. package/dist/Components/Touch/SummaryCard.svelte +74 -74
  119. package/dist/Components/Touch/TextAreaModal.svelte +53 -53
  120. package/dist/Components/Touch/TextModal.svelte +56 -56
  121. package/dist/Components/Touch/TouchCard.svelte +28 -28
  122. package/dist/Components/Touch/TouchModal.svelte +141 -141
  123. package/dist/Components/Touch/UtilityButton.svelte +63 -63
  124. package/dist/Components/VerticalTabGroup.svelte +38 -38
  125. package/dist/Components/VerticalTabGroupButton.svelte +46 -46
  126. package/dist/Helpers/Datamatrix.d.ts +19 -19
  127. package/dist/Helpers/Datamatrix.js +291 -291
  128. package/dist/Types/Examples/ExampleModal.svelte +27 -27
  129. package/dist/paraglide/README.md +1 -1
  130. package/dist/scss/_basics.scss +12 -12
  131. package/dist/scss/_colours.scss +140 -140
  132. package/dist/scss/_motion.scss +57 -57
  133. package/dist/scss/_sizing.scss +85 -85
  134. package/dist/scss/_transitions.scss +8 -8
  135. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
- <svelte:options runes />
2
-
3
- <script
4
- lang="ts"
5
- generics="E extends CalendarEvent"
1
+ <svelte:options runes />
2
+
3
+ <script
4
+ lang="ts"
5
+ generics="E extends CalendarEvent"
6
6
  >import { m as i18n } from "../../paraglide/messages.js";
7
7
  import { Colour } from "../../scss/colours.js";
8
8
  import { Size } from "../../Types/Internal/Size.js";
@@ -519,241 +519,241 @@ $effect(() => {
519
519
  calendarBody.scrollTop += delta;
520
520
  hasCentred = true;
521
521
  });
522
- </script>
523
-
524
- <!-- A drag can end anywhere on the page; listen on the window so the drag state
525
- is always finalised and never left "stuck" for the next drag. -->
526
- <svelte:window onmouseup={onSlotMouseUp} />
527
-
528
- <!-- svelte-ignore a11y_no_static_element_interactions -->
529
- <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
530
- <div
531
- class="resource-calendar"
532
- class:is-moving-event={isDraggingEvent}
533
- role="application"
534
- >
535
- <!-- Header: navigation + view toggle -->
536
- <div class="calendar-header">
537
- <div class="header-left">
538
- <Button
539
- label={i18n['scoria.label.today']()}
540
- size={Size.Small}
541
- onclick={goToToday}
542
- >
543
- {i18n['scoria.label.today']()}
544
- </Button>
545
- <div class="nav-arrows">
546
- <button
547
- class="nav-btn"
548
- onclick={() => {
549
- navigateDate(-1);
550
- }}
551
- aria-label={i18n['scoria.action.previous']()}
552
- >
553
- <Icon
554
- name="chevron-left"
555
- size={Size.Small}
556
- colour={Colour['$ui-blue-14']}
557
- />
558
- </button>
559
- <button
560
- class="nav-btn"
561
- onclick={() => {
562
- navigateDate(1);
563
- }}
564
- aria-label={i18n['scoria.action.next']()}
565
- >
566
- <Icon
567
- name="chevron-right"
568
- size={Size.Small}
569
- colour={Colour['$ui-blue-14']}
570
- />
571
- </button>
572
- </div>
573
- <h2 class="current-date-label">{formatDate(selected)}</h2>
574
- </div>
575
- <div class="header-right">
576
- {#if headerExtra}
577
- {@render headerExtra()}
578
- {/if}
579
- <div class="view-toggle">
580
- {#each viewModes as { mode, label } (mode)}
581
- <button
582
- class="view-btn"
583
- class:active={viewMode === mode}
584
- onclick={() => {
585
- onviewmodechange(mode);
586
- }}
587
- >
588
- {label}
589
- </button>
590
- {/each}
591
- </div>
592
- </div>
593
- </div>
594
-
595
- <!-- Week day tabs rendered OUTSIDE the scrollable body so they stay frozen -->
596
- {#if viewMode === CalendarViewMode.Week}
597
- <div class="week-day-tabs">
598
- {#each weekDays as day (day.getTime())}
599
- <button
600
- class="week-day-tab"
601
- class:active={sameDay(day, selected)}
602
- class:today={sameDay(day, today)}
603
- onclick={() => {
604
- ondatechange(day);
605
- }}
606
- >
607
- <span class="day-name">{dayNames[day.getDay()].slice(0, 3)}</span>
608
- <span class="day-num">{day.getDate()}</span>
609
- </button>
610
- {/each}
611
- </div>
612
- {/if}
613
-
614
- <!-- Calendar body -->
615
- <div
616
- class="calendar-body"
617
- bind:this={calendarBody}
618
- >
619
- {#if isLoading}
620
- <LoadingOverlay />
621
- {/if}
622
-
623
- {#if viewMode === CalendarViewMode.Day}
624
- <!-- DAY VIEW -->
625
- <!-- svelte-ignore a11y_no_static_element_interactions -->
626
- <div
627
- class="day-view"
628
- onmouseleave={() => {
629
- hoveredSlotIndex = null;
630
- }}
631
- >
632
- <!-- eslint-disable-next-line @typescript-eslint/no-confusing-void-expression -->
633
- {@render resourceGrid(selected)}
634
- </div>
635
- {:else if viewMode === CalendarViewMode.Week}
636
- <!-- WEEK VIEW: day grid only, tabs are above calendar-body -->
637
- <!-- svelte-ignore a11y_no_static_element_interactions -->
638
- <div
639
- class="day-view"
640
- onmouseleave={() => {
641
- hoveredSlotIndex = null;
642
- }}
643
- >
644
- <!-- eslint-disable-next-line @typescript-eslint/no-confusing-void-expression -->
645
- {@render resourceGrid(selected)}
646
- </div>
647
- {:else}
648
- <!-- MONTH VIEW -->
649
- <div class="month-view">
650
- <div class="month-header-row">
651
- {#each ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] as dayLabel (dayLabel)}
652
- <div class="month-day-header">{dayLabel}</div>
653
- {/each}
654
- </div>
655
- <div class="month-grid">
656
- {#each monthDays as { date: dayDate, isCurrentMonth } (dayDate.getTime())}
657
- <button
658
- class="month-day-cell"
659
- class:other-month={!isCurrentMonth}
660
- class:today={sameDay(dayDate, today)}
661
- class:selected={sameDay(dayDate, selected)}
662
- onclick={() => {
663
- ondatechange(dayDate);
664
- onviewmodechange(CalendarViewMode.Day);
665
- }}
666
- >
667
- <span class="day-number">{dayDate.getDate()}</span>
668
- <div class="day-badges">
669
- {#each countsAt(dayDate) as { resourceId, resourceLabel, count } (resourceId)}
670
- <span class="resource-badge">
671
- {resourceLabel}: {count}
672
- </span>
673
- {/each}
674
- </div>
675
- </button>
676
- {/each}
677
- </div>
678
- </div>
679
- {/if}
680
- </div>
681
- </div>
682
-
683
- {#snippet resourceGrid(day: Date)}
684
- <div class="grid-header">
685
- <div class="time-header">{i18n['scoria.label.time']()}</div>
686
- {#each resources as resource (resource.id)}
687
- <div class="resource-header">{resource.label}</div>
688
- {/each}
689
- </div>
690
- <div class="grid-body">
691
- <div class="time-column">
692
- {#each timeSlots as slot, slotIdx (slot.hour * 60 + slot.minute)}
693
- <div
694
- class="time-label"
695
- class:hour-start={slot.minute === 0}
696
- class:row-hover={hoveredSlotIndex === slotIdx}
697
- data-centre-row={slot.hour === centreRowHour && slot.minute === 0 ? '' : undefined}
698
- >
699
- {#if slot.minute === 0}
700
- <span>{slot.label}</span>
701
- {/if}
702
- </div>
703
- {/each}
704
- </div>
705
- {#each resources as resource (resource.id)}
706
- <div class="resource-column">
707
- <!-- Slot cells for drag interaction (rendered first, below events) -->
708
- {#each timeSlots as slot, slotIdx (slot.hour * 60 + slot.minute)}
709
- <!-- svelte-ignore a11y_no_static_element_interactions -->
710
- <div
711
- class="slot-cell"
712
- class:hour-start={slot.minute === 0}
713
- class:drag-selected={isSlotInDragRange(resource.id, slotIdx)}
714
- class:move-target={isSlotInMoveTarget(resource.id, slotIdx)}
715
- class:row-hover={hoveredSlotIndex === slotIdx}
716
- class:unavailable={isSlotUnavailable(resource.id, slotIdx)}
717
- onmousedown={(e) => {
718
- onSlotMouseDown(resource.id, slotIdx, e);
719
- }}
720
- onmouseenter={(e) => {
721
- hoveredSlotIndex = slotIdx;
722
- onSlotMouseEnter(resource.id, slotIdx, e);
723
- }}
724
- ondblclick={() => {
725
- onSlotDoubleClick(resource.id, slotIdx);
726
- }}
727
- ></div>
728
- {/each}
729
- <!-- Unavailable shading: one continuous block per contiguous run -->
730
- {#each unavailableRunsAt(resource.id) as run (run.topPercent)}
731
- <div
732
- class="unavailable-overlay"
733
- style="top: {run.topPercent}%; height: {run.heightPercent}%;"
734
- ></div>
735
- {/each}
736
- <!-- Event blocks positioned absolutely (rendered after, above slots).
737
- The consumer renders the card body via the `event` snippet. -->
738
- {#each eventsAt(resource.id, day) as ev (ev.id)}
739
- <!-- svelte-ignore a11y_no_static_element_interactions -->
740
- <div
741
- class="event-block"
742
- class:dragging={isDraggingEvent && draggedEvent?.id === ev.id}
743
- class:movable={!!oneventmove}
744
- style="top: {ev.topPercent}%; height: {ev.heightPercent}%;"
745
- onmousedown={(e) => {
746
- onEventMouseDown(ev, e);
747
- }}
748
- >
749
- {@render eventContent(ev)}
750
- </div>
751
- {/each}
752
- </div>
753
- {/each}
754
- </div>
755
- {/snippet}
756
-
522
+ </script>
523
+
524
+ <!-- A drag can end anywhere on the page; listen on the window so the drag state
525
+ is always finalised and never left "stuck" for the next drag. -->
526
+ <svelte:window onmouseup={onSlotMouseUp} />
527
+
528
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
529
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
530
+ <div
531
+ class="resource-calendar"
532
+ class:is-moving-event={isDraggingEvent}
533
+ role="application"
534
+ >
535
+ <!-- Header: navigation + view toggle -->
536
+ <div class="calendar-header">
537
+ <div class="header-left">
538
+ <Button
539
+ label={i18n['scoria.label.today']()}
540
+ size={Size.Small}
541
+ onclick={goToToday}
542
+ >
543
+ {i18n['scoria.label.today']()}
544
+ </Button>
545
+ <div class="nav-arrows">
546
+ <button
547
+ class="nav-btn"
548
+ onclick={() => {
549
+ navigateDate(-1);
550
+ }}
551
+ aria-label={i18n['scoria.action.previous']()}
552
+ >
553
+ <Icon
554
+ name="chevron-left"
555
+ size={Size.Small}
556
+ colour={Colour['$ui-blue-14']}
557
+ />
558
+ </button>
559
+ <button
560
+ class="nav-btn"
561
+ onclick={() => {
562
+ navigateDate(1);
563
+ }}
564
+ aria-label={i18n['scoria.action.next']()}
565
+ >
566
+ <Icon
567
+ name="chevron-right"
568
+ size={Size.Small}
569
+ colour={Colour['$ui-blue-14']}
570
+ />
571
+ </button>
572
+ </div>
573
+ <h2 class="current-date-label">{formatDate(selected)}</h2>
574
+ </div>
575
+ <div class="header-right">
576
+ {#if headerExtra}
577
+ {@render headerExtra()}
578
+ {/if}
579
+ <div class="view-toggle">
580
+ {#each viewModes as { mode, label } (mode)}
581
+ <button
582
+ class="view-btn"
583
+ class:active={viewMode === mode}
584
+ onclick={() => {
585
+ onviewmodechange(mode);
586
+ }}
587
+ >
588
+ {label}
589
+ </button>
590
+ {/each}
591
+ </div>
592
+ </div>
593
+ </div>
594
+
595
+ <!-- Week day tabs rendered OUTSIDE the scrollable body so they stay frozen -->
596
+ {#if viewMode === CalendarViewMode.Week}
597
+ <div class="week-day-tabs">
598
+ {#each weekDays as day (day.getTime())}
599
+ <button
600
+ class="week-day-tab"
601
+ class:active={sameDay(day, selected)}
602
+ class:today={sameDay(day, today)}
603
+ onclick={() => {
604
+ ondatechange(day);
605
+ }}
606
+ >
607
+ <span class="day-name">{dayNames[day.getDay()].slice(0, 3)}</span>
608
+ <span class="day-num">{day.getDate()}</span>
609
+ </button>
610
+ {/each}
611
+ </div>
612
+ {/if}
613
+
614
+ <!-- Calendar body -->
615
+ <div
616
+ class="calendar-body"
617
+ bind:this={calendarBody}
618
+ >
619
+ {#if isLoading}
620
+ <LoadingOverlay />
621
+ {/if}
622
+
623
+ {#if viewMode === CalendarViewMode.Day}
624
+ <!-- DAY VIEW -->
625
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
626
+ <div
627
+ class="day-view"
628
+ onmouseleave={() => {
629
+ hoveredSlotIndex = null;
630
+ }}
631
+ >
632
+ <!-- eslint-disable-next-line @typescript-eslint/no-confusing-void-expression -->
633
+ {@render resourceGrid(selected)}
634
+ </div>
635
+ {:else if viewMode === CalendarViewMode.Week}
636
+ <!-- WEEK VIEW: day grid only, tabs are above calendar-body -->
637
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
638
+ <div
639
+ class="day-view"
640
+ onmouseleave={() => {
641
+ hoveredSlotIndex = null;
642
+ }}
643
+ >
644
+ <!-- eslint-disable-next-line @typescript-eslint/no-confusing-void-expression -->
645
+ {@render resourceGrid(selected)}
646
+ </div>
647
+ {:else}
648
+ <!-- MONTH VIEW -->
649
+ <div class="month-view">
650
+ <div class="month-header-row">
651
+ {#each ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] as dayLabel (dayLabel)}
652
+ <div class="month-day-header">{dayLabel}</div>
653
+ {/each}
654
+ </div>
655
+ <div class="month-grid">
656
+ {#each monthDays as { date: dayDate, isCurrentMonth } (dayDate.getTime())}
657
+ <button
658
+ class="month-day-cell"
659
+ class:other-month={!isCurrentMonth}
660
+ class:today={sameDay(dayDate, today)}
661
+ class:selected={sameDay(dayDate, selected)}
662
+ onclick={() => {
663
+ ondatechange(dayDate);
664
+ onviewmodechange(CalendarViewMode.Day);
665
+ }}
666
+ >
667
+ <span class="day-number">{dayDate.getDate()}</span>
668
+ <div class="day-badges">
669
+ {#each countsAt(dayDate) as { resourceId, resourceLabel, count } (resourceId)}
670
+ <span class="resource-badge">
671
+ {resourceLabel}: {count}
672
+ </span>
673
+ {/each}
674
+ </div>
675
+ </button>
676
+ {/each}
677
+ </div>
678
+ </div>
679
+ {/if}
680
+ </div>
681
+ </div>
682
+
683
+ {#snippet resourceGrid(day: Date)}
684
+ <div class="grid-header">
685
+ <div class="time-header">{i18n['scoria.label.time']()}</div>
686
+ {#each resources as resource (resource.id)}
687
+ <div class="resource-header">{resource.label}</div>
688
+ {/each}
689
+ </div>
690
+ <div class="grid-body">
691
+ <div class="time-column">
692
+ {#each timeSlots as slot, slotIdx (slot.hour * 60 + slot.minute)}
693
+ <div
694
+ class="time-label"
695
+ class:hour-start={slot.minute === 0}
696
+ class:row-hover={hoveredSlotIndex === slotIdx}
697
+ data-centre-row={slot.hour === centreRowHour && slot.minute === 0 ? '' : undefined}
698
+ >
699
+ {#if slot.minute === 0}
700
+ <span>{slot.label}</span>
701
+ {/if}
702
+ </div>
703
+ {/each}
704
+ </div>
705
+ {#each resources as resource (resource.id)}
706
+ <div class="resource-column">
707
+ <!-- Slot cells for drag interaction (rendered first, below events) -->
708
+ {#each timeSlots as slot, slotIdx (slot.hour * 60 + slot.minute)}
709
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
710
+ <div
711
+ class="slot-cell"
712
+ class:hour-start={slot.minute === 0}
713
+ class:drag-selected={isSlotInDragRange(resource.id, slotIdx)}
714
+ class:move-target={isSlotInMoveTarget(resource.id, slotIdx)}
715
+ class:row-hover={hoveredSlotIndex === slotIdx}
716
+ class:unavailable={isSlotUnavailable(resource.id, slotIdx)}
717
+ onmousedown={(e) => {
718
+ onSlotMouseDown(resource.id, slotIdx, e);
719
+ }}
720
+ onmouseenter={(e) => {
721
+ hoveredSlotIndex = slotIdx;
722
+ onSlotMouseEnter(resource.id, slotIdx, e);
723
+ }}
724
+ ondblclick={() => {
725
+ onSlotDoubleClick(resource.id, slotIdx);
726
+ }}
727
+ ></div>
728
+ {/each}
729
+ <!-- Unavailable shading: one continuous block per contiguous run -->
730
+ {#each unavailableRunsAt(resource.id) as run (run.topPercent)}
731
+ <div
732
+ class="unavailable-overlay"
733
+ style="top: {run.topPercent}%; height: {run.heightPercent}%;"
734
+ ></div>
735
+ {/each}
736
+ <!-- Event blocks positioned absolutely (rendered after, above slots).
737
+ The consumer renders the card body via the `event` snippet. -->
738
+ {#each eventsAt(resource.id, day) as ev (ev.id)}
739
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
740
+ <div
741
+ class="event-block"
742
+ class:dragging={isDraggingEvent && draggedEvent?.id === ev.id}
743
+ class:movable={!!oneventmove}
744
+ style="top: {ev.topPercent}%; height: {ev.heightPercent}%;"
745
+ onmousedown={(e) => {
746
+ onEventMouseDown(ev, e);
747
+ }}
748
+ >
749
+ {@render eventContent(ev)}
750
+ </div>
751
+ {/each}
752
+ </div>
753
+ {/each}
754
+ </div>
755
+ {/snippet}
756
+
757
757
  <style>.resource-calendar {
758
758
  display: flex;
759
759
  flex-flow: column nowrap;
@@ -1150,4 +1150,4 @@ $effect(() => {
1150
1150
  white-space: nowrap;
1151
1151
  overflow: hidden;
1152
1152
  text-overflow: ellipsis;
1153
- }</style>
1153
+ }</style>
@@ -1,36 +1,36 @@
1
- <svelte:options runes />
2
-
3
- <!--
4
- @component
5
- A titled card laid out as a fixed-width menu column beside a main content
6
- area. Both regions are supplied by the consumer as snippets (`menu` and the
7
- default `children`), so the component only owns the framing and layout.
8
- -->
1
+ <svelte:options runes />
2
+
3
+ <!--
4
+ @component
5
+ A titled card laid out as a fixed-width menu column beside a main content
6
+ area. Both regions are supplied by the consumer as snippets (`menu` and the
7
+ default `children`), so the component only owns the framing and layout.
8
+ -->
9
9
  <script lang="ts">let { title, subtitle, menu, children, menuWidth = "14rem" } = $props();
10
10
  export {};
11
- </script>
12
-
13
- <section class="sidebar-panel">
14
- <header class="heading">
15
- <h2>{title}</h2>
16
- {#if subtitle}
17
- <span class="subtitle">{subtitle}</span>
18
- {/if}
19
- </header>
20
-
21
- <div class="body">
22
- <aside
23
- class="menu"
24
- style="--menu-width: {menuWidth};"
25
- >
26
- {@render menu()}
27
- </aside>
28
- <div class="main">
29
- {@render children()}
30
- </div>
31
- </div>
32
- </section>
33
-
11
+ </script>
12
+
13
+ <section class="sidebar-panel">
14
+ <header class="heading">
15
+ <h2>{title}</h2>
16
+ {#if subtitle}
17
+ <span class="subtitle">{subtitle}</span>
18
+ {/if}
19
+ </header>
20
+
21
+ <div class="body">
22
+ <aside
23
+ class="menu"
24
+ style="--menu-width: {menuWidth};"
25
+ >
26
+ {@render menu()}
27
+ </aside>
28
+ <div class="main">
29
+ {@render children()}
30
+ </div>
31
+ </div>
32
+ </section>
33
+
34
34
  <style>.sidebar-panel {
35
35
  display: flex;
36
36
  flex-flow: column nowrap;
@@ -80,4 +80,4 @@ export {};
80
80
  flex-flow: column nowrap;
81
81
  gap: 1.5rem;
82
82
  padding: 1.5rem;
83
- }</style>
83
+ }</style>