@lavalogic/scoria 0.40.31 → 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 (137) 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/Icons.d.ts +3 -3
  38. package/dist/Components/Icons.js +11 -11
  39. package/dist/Components/InfoAlert.svelte +32 -32
  40. package/dist/Components/LoadingAnimation.svelte +228 -227
  41. package/dist/Components/LoadingModal.svelte +19 -19
  42. package/dist/Components/LoadingOverlay.svelte +20 -20
  43. package/dist/Components/Modal.svelte +127 -127
  44. package/dist/Components/MultiSelect.svelte +126 -126
  45. package/dist/Components/Nav/Nav.svelte +71 -71
  46. package/dist/Components/Nav/NavServiceMenuList.svelte +37 -37
  47. package/dist/Components/Nav/NavTab.svelte +46 -46
  48. package/dist/Components/Nav/ServiceMenuItem.svelte +62 -62
  49. package/dist/Components/Nav/ServicesNav.svelte +50 -50
  50. package/dist/Components/NumberInput.svelte +98 -98
  51. package/dist/Components/OptionCards.svelte +58 -58
  52. package/dist/Components/PageHeading.svelte +21 -21
  53. package/dist/Components/PasswordInput.svelte +61 -61
  54. package/dist/Components/PopoverButton.svelte +53 -53
  55. package/dist/Components/Portal.svelte +7 -7
  56. package/dist/Components/ProgressBubble.svelte +42 -42
  57. package/dist/Components/QuerySelect.svelte +102 -102
  58. package/dist/Components/ResourceCalendar/ResourceCalendar.svelte +241 -241
  59. package/dist/Components/SidebarPanel.svelte +32 -32
  60. package/dist/Components/SingleSelect.svelte +122 -122
  61. package/dist/Components/StepButton.svelte +48 -48
  62. package/dist/Components/StepForm.svelte +58 -58
  63. package/dist/Components/Switch.svelte +43 -43
  64. package/dist/Components/Table/ARCHITECTURE.md +298 -298
  65. package/dist/Components/Table/Body/Rows/ColumnHighlight.svelte +30 -30
  66. package/dist/Components/Table/Body/Rows/ColumnList.svelte +24 -24
  67. package/dist/Components/Table/Body/Rows/ColumnListRow.svelte +165 -165
  68. package/dist/Components/Table/Body/Rows/Columns/AccessorComponent.svelte +13 -13
  69. package/dist/Components/Table/Body/Rows/Columns/ActionsCell.svelte +120 -120
  70. package/dist/Components/Table/Body/Rows/Columns/BubbleCell.svelte +81 -81
  71. package/dist/Components/Table/Body/Rows/Columns/ExpandCell.svelte +92 -92
  72. package/dist/Components/Table/Body/Rows/Columns/FocusableImmutableCell.svelte +110 -110
  73. package/dist/Components/Table/Body/Rows/Columns/ProgressCell.svelte +54 -54
  74. package/dist/Components/Table/Body/Rows/Columns/TableAction.svelte +35 -35
  75. package/dist/Components/Table/Body/Rows/Columns/TableCheckbox.svelte +64 -64
  76. package/dist/Components/Table/Body/Rows/Columns/TableColumn.svelte +82 -82
  77. package/dist/Components/Table/Body/Rows/Columns/TableDatePicker.svelte +88 -88
  78. package/dist/Components/Table/Body/Rows/Columns/TableNumberInput.svelte +90 -90
  79. package/dist/Components/Table/Body/Rows/Columns/TableQuerySelect.svelte +96 -96
  80. package/dist/Components/Table/Body/Rows/Columns/TableRowSelectionCheckbox.svelte +71 -71
  81. package/dist/Components/Table/Body/Rows/Columns/TableSelect.svelte +124 -124
  82. package/dist/Components/Table/Body/Rows/Columns/TableTextInput.svelte +91 -91
  83. package/dist/Components/Table/Body/Rows/Columns/ValidityCell.svelte +88 -88
  84. package/dist/Components/Table/Body/Rows/TableRow.svelte +127 -127
  85. package/dist/Components/Table/Body/TableBody.svelte +92 -92
  86. package/dist/Components/Table/Headers/HighlightAllHeader.svelte +25 -25
  87. package/dist/Components/Table/Headers/SelectAllHeader.svelte +20 -20
  88. package/dist/Components/Table/Headers/TableHead.svelte +175 -175
  89. package/dist/Components/Table/Misc/ColumnPanel.svelte +32 -32
  90. package/dist/Components/Table/Misc/ColumnPanelModal.svelte +29 -29
  91. package/dist/Components/Table/Misc/FilterInput.svelte +326 -326
  92. package/dist/Components/Table/Misc/FilterPanel.svelte +79 -79
  93. package/dist/Components/Table/Misc/FilterPanelModal.svelte +21 -21
  94. package/dist/Components/Table/Misc/ShareViewModal.svelte +118 -118
  95. package/dist/Components/Table/Misc/SidePanel.svelte +24 -24
  96. package/dist/Components/Table/Misc/TableConfigurationModal.svelte +318 -318
  97. package/dist/Components/Table/Misc/TableFooter.svelte +201 -201
  98. package/dist/Components/Table/Misc/TableHorizontalBar.svelte +112 -112
  99. package/dist/Components/Table/Misc/TableSidebar.svelte +116 -116
  100. package/dist/Components/Table/Misc/TableViewDropdown.svelte +25 -25
  101. package/dist/Components/Table/Misc/ToolboxPanel.svelte +60 -60
  102. package/dist/Components/Table/Misc/ToolboxPanelModal.svelte +63 -63
  103. package/dist/Components/Table/Misc/UpdateViewOptionsModal.svelte +101 -101
  104. package/dist/Components/Table/NestedTable.svelte +29 -29
  105. package/dist/Components/Table/Table.svelte +140 -140
  106. package/dist/Components/Test/TestContextConsumer.svelte +8 -8
  107. package/dist/Components/TextArea.svelte +92 -90
  108. package/dist/Components/TextInput.svelte +128 -117
  109. package/dist/Components/ThreeStateRadio.svelte +101 -101
  110. package/dist/Components/Touch/BoolCard.svelte +27 -27
  111. package/dist/Components/Touch/DateModal.svelte +98 -98
  112. package/dist/Components/Touch/EditCard.svelte +44 -44
  113. package/dist/Components/Touch/InfoCard.svelte +25 -25
  114. package/dist/Components/Touch/InfoTextCard.svelte +22 -22
  115. package/dist/Components/Touch/ModalExplanation.svelte +13 -13
  116. package/dist/Components/Touch/NumberKeyboard.svelte +81 -81
  117. package/dist/Components/Touch/NumberModal.svelte +49 -49
  118. package/dist/Components/Touch/PlusMinusCard.svelte +40 -40
  119. package/dist/Components/Touch/SelectModal.svelte +86 -86
  120. package/dist/Components/Touch/SummaryCard.svelte +74 -74
  121. package/dist/Components/Touch/TextAreaModal.svelte +53 -53
  122. package/dist/Components/Touch/TextModal.svelte +56 -56
  123. package/dist/Components/Touch/TouchCard.svelte +28 -28
  124. package/dist/Components/Touch/TouchModal.svelte +141 -141
  125. package/dist/Components/Touch/UtilityButton.svelte +63 -63
  126. package/dist/Components/VerticalTabGroup.svelte +38 -38
  127. package/dist/Components/VerticalTabGroupButton.svelte +46 -46
  128. package/dist/Helpers/Datamatrix.d.ts +19 -19
  129. package/dist/Helpers/Datamatrix.js +291 -291
  130. package/dist/Types/Examples/ExampleModal.svelte +27 -27
  131. package/dist/paraglide/README.md +1 -1
  132. package/dist/scss/_basics.scss +12 -12
  133. package/dist/scss/_colours.scss +140 -140
  134. package/dist/scss/_motion.scss +57 -57
  135. package/dist/scss/_sizing.scss +85 -85
  136. package/dist/scss/_transitions.scss +8 -8
  137. 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>