@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.
- package/README.md +58 -58
- package/dist/Components/AccordionGroup.svelte +28 -28
- package/dist/Components/AccordionGroupButton.svelte +60 -60
- package/dist/Components/Action.svelte +48 -48
- package/dist/Components/AlertModal.svelte +102 -102
- package/dist/Components/Base/ContextWrapper.svelte +45 -45
- package/dist/Components/Base/Pagination.svelte +135 -135
- package/dist/Components/Base/Snippets.svelte +25 -25
- package/dist/Components/BigRadioSet.svelte +34 -34
- package/dist/Components/Bubble.svelte +38 -38
- package/dist/Components/Button.svelte +83 -83
- package/dist/Components/Checkbox.svelte +71 -71
- package/dist/Components/CollapsibleCard.svelte +59 -59
- package/dist/Components/CollapsibleMenuGroup.svelte +43 -43
- package/dist/Components/ConditionBuilder/ConditionBuilder.svelte +101 -101
- package/dist/Components/Contexts/ContextMenu.svelte +76 -76
- package/dist/Components/Contexts/ContextMenuDivider.svelte +15 -15
- package/dist/Components/Contexts/ContextMenuOption.svelte +38 -38
- package/dist/Components/Contexts/Toast.svelte +71 -71
- package/dist/Components/Contexts/ToastContainer.svelte +34 -34
- package/dist/Components/Contexts/Tooltip.svelte +32 -32
- package/dist/Components/DataMatrixIcon.svelte +37 -37
- package/dist/Components/DateInput.svelte +80 -80
- package/dist/Components/DatePicker.svelte +86 -86
- package/dist/Components/DesktopModal.svelte +65 -65
- package/dist/Components/Dial.svelte +35 -35
- package/dist/Components/DimensionInput.svelte +95 -95
- package/dist/Components/DragMenu/DragMenuHolder.svelte +53 -53
- package/dist/Components/DragMenu/DraggableCard.svelte +49 -49
- package/dist/Components/FileCard.svelte +18 -18
- package/dist/Components/FileUpload.svelte +115 -115
- package/dist/Components/GridInput.svelte +123 -123
- package/dist/Components/GridInputSet.svelte +21 -21
- package/dist/Components/HorizontalTabGroup.svelte +54 -54
- package/dist/Components/HorizontalTabGroupButton.svelte +32 -32
- package/dist/Components/Icon.svelte +68 -68
- package/dist/Components/InfoAlert.svelte +32 -32
- package/dist/Components/LoadingAnimation.svelte +228 -227
- package/dist/Components/LoadingModal.svelte +19 -19
- package/dist/Components/LoadingOverlay.svelte +20 -20
- package/dist/Components/Modal.svelte +127 -127
- package/dist/Components/MultiSelect.svelte +126 -126
- package/dist/Components/Nav/Nav.svelte +71 -71
- package/dist/Components/Nav/NavServiceMenuList.svelte +37 -37
- package/dist/Components/Nav/NavTab.svelte +46 -46
- package/dist/Components/Nav/ServiceMenuItem.svelte +62 -62
- package/dist/Components/Nav/ServicesNav.svelte +50 -50
- package/dist/Components/NumberInput.svelte +98 -98
- package/dist/Components/OptionCards.svelte +58 -58
- package/dist/Components/PageHeading.svelte +21 -21
- package/dist/Components/PasswordInput.svelte +61 -61
- package/dist/Components/PopoverButton.svelte +53 -53
- package/dist/Components/Portal.svelte +7 -7
- package/dist/Components/ProgressBubble.svelte +42 -42
- package/dist/Components/QuerySelect.svelte +102 -102
- package/dist/Components/ResourceCalendar/ResourceCalendar.svelte +241 -241
- package/dist/Components/SidebarPanel.svelte +32 -32
- package/dist/Components/SingleSelect.svelte +122 -122
- package/dist/Components/StepButton.svelte +48 -48
- package/dist/Components/StepForm.svelte +58 -58
- package/dist/Components/Switch.svelte +43 -43
- package/dist/Components/Table/ARCHITECTURE.md +298 -298
- package/dist/Components/Table/Body/Rows/ColumnHighlight.svelte +30 -30
- package/dist/Components/Table/Body/Rows/ColumnList.svelte +24 -24
- package/dist/Components/Table/Body/Rows/ColumnListRow.svelte +165 -165
- package/dist/Components/Table/Body/Rows/Columns/AccessorComponent.svelte +13 -13
- package/dist/Components/Table/Body/Rows/Columns/ActionsCell.svelte +120 -120
- package/dist/Components/Table/Body/Rows/Columns/BubbleCell.svelte +81 -81
- package/dist/Components/Table/Body/Rows/Columns/ExpandCell.svelte +92 -92
- package/dist/Components/Table/Body/Rows/Columns/FocusableImmutableCell.svelte +110 -110
- package/dist/Components/Table/Body/Rows/Columns/ProgressCell.svelte +54 -54
- package/dist/Components/Table/Body/Rows/Columns/TableAction.svelte +35 -35
- package/dist/Components/Table/Body/Rows/Columns/TableCheckbox.svelte +64 -64
- package/dist/Components/Table/Body/Rows/Columns/TableColumn.svelte +82 -82
- package/dist/Components/Table/Body/Rows/Columns/TableDatePicker.svelte +88 -88
- package/dist/Components/Table/Body/Rows/Columns/TableNumberInput.svelte +90 -90
- package/dist/Components/Table/Body/Rows/Columns/TableQuerySelect.svelte +96 -96
- package/dist/Components/Table/Body/Rows/Columns/TableRowSelectionCheckbox.svelte +71 -71
- package/dist/Components/Table/Body/Rows/Columns/TableSelect.svelte +124 -124
- package/dist/Components/Table/Body/Rows/Columns/TableTextInput.svelte +91 -91
- package/dist/Components/Table/Body/Rows/Columns/ValidityCell.svelte +88 -88
- package/dist/Components/Table/Body/Rows/TableRow.svelte +127 -127
- package/dist/Components/Table/Body/TableBody.svelte +92 -92
- package/dist/Components/Table/Headers/HighlightAllHeader.svelte +25 -25
- package/dist/Components/Table/Headers/SelectAllHeader.svelte +20 -20
- package/dist/Components/Table/Headers/TableHead.svelte +175 -175
- package/dist/Components/Table/Misc/ColumnPanel.svelte +32 -32
- package/dist/Components/Table/Misc/ColumnPanelModal.svelte +29 -29
- package/dist/Components/Table/Misc/FilterInput.svelte +326 -326
- package/dist/Components/Table/Misc/FilterPanel.svelte +79 -79
- package/dist/Components/Table/Misc/FilterPanelModal.svelte +21 -21
- package/dist/Components/Table/Misc/ShareViewModal.svelte +118 -118
- package/dist/Components/Table/Misc/SidePanel.svelte +24 -24
- package/dist/Components/Table/Misc/TableConfigurationModal.svelte +318 -318
- package/dist/Components/Table/Misc/TableFooter.svelte +201 -201
- package/dist/Components/Table/Misc/TableHorizontalBar.svelte +112 -112
- package/dist/Components/Table/Misc/TableSidebar.svelte +116 -116
- package/dist/Components/Table/Misc/TableViewDropdown.svelte +25 -25
- package/dist/Components/Table/Misc/ToolboxPanel.svelte +60 -60
- package/dist/Components/Table/Misc/ToolboxPanelModal.svelte +63 -63
- package/dist/Components/Table/Misc/UpdateViewOptionsModal.svelte +101 -101
- package/dist/Components/Table/NestedTable.svelte +29 -29
- package/dist/Components/Table/Table.svelte +140 -140
- package/dist/Components/Test/TestContextConsumer.svelte +8 -8
- package/dist/Components/TextArea.svelte +92 -90
- package/dist/Components/TextInput.svelte +128 -117
- package/dist/Components/ThreeStateRadio.svelte +101 -101
- package/dist/Components/Touch/BoolCard.svelte +27 -27
- package/dist/Components/Touch/DateModal.svelte +98 -98
- package/dist/Components/Touch/EditCard.svelte +44 -44
- package/dist/Components/Touch/InfoCard.svelte +25 -25
- package/dist/Components/Touch/InfoTextCard.svelte +22 -22
- package/dist/Components/Touch/ModalExplanation.svelte +13 -13
- package/dist/Components/Touch/NumberKeyboard.svelte +81 -81
- package/dist/Components/Touch/NumberModal.svelte +49 -49
- package/dist/Components/Touch/PlusMinusCard.svelte +40 -40
- package/dist/Components/Touch/SelectModal.svelte +86 -86
- package/dist/Components/Touch/SummaryCard.svelte +74 -74
- package/dist/Components/Touch/TextAreaModal.svelte +53 -53
- package/dist/Components/Touch/TextModal.svelte +56 -56
- package/dist/Components/Touch/TouchCard.svelte +28 -28
- package/dist/Components/Touch/TouchModal.svelte +141 -141
- package/dist/Components/Touch/UtilityButton.svelte +63 -63
- package/dist/Components/VerticalTabGroup.svelte +38 -38
- package/dist/Components/VerticalTabGroupButton.svelte +46 -46
- package/dist/Helpers/Datamatrix.d.ts +19 -19
- package/dist/Helpers/Datamatrix.js +291 -291
- package/dist/Types/Examples/ExampleModal.svelte +27 -27
- package/dist/paraglide/README.md +1 -1
- package/dist/scss/_basics.scss +12 -12
- package/dist/scss/_colours.scss +140 -140
- package/dist/scss/_motion.scss +57 -57
- package/dist/scss/_sizing.scss +85 -85
- package/dist/scss/_transitions.scss +8 -8
- 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>
|