@lavalogic/scoria 0.40.17 → 0.40.18
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.
|
@@ -100,6 +100,15 @@ const timeSlots = $derived.by(() => {
|
|
|
100
100
|
* all) is treated as fully available, so nothing is shaded until the
|
|
101
101
|
* caller supplies windows for that door.
|
|
102
102
|
*/
|
|
103
|
+
function slotUnavailableInWindows(windows, slot) {
|
|
104
|
+
const minutes = slot.hour * 60 + slot.minute;
|
|
105
|
+
for (const window of windows) {
|
|
106
|
+
if (minutes >= window.startMinutes && minutes < window.endMinutes) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
103
112
|
function isSlotUnavailable(doorId, slotIndex) {
|
|
104
113
|
if (!availability) {
|
|
105
114
|
return false;
|
|
@@ -112,13 +121,53 @@ function isSlotUnavailable(doorId, slotIndex) {
|
|
|
112
121
|
if (!slot) {
|
|
113
122
|
return false;
|
|
114
123
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
124
|
+
return slotUnavailableInWindows(windows, slot);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Contiguous unavailable runs per door, expressed as top/height
|
|
128
|
+
* percentages of the day column. Rendered as a single overlay block per
|
|
129
|
+
* run so the shading pattern is continuous across the whole region
|
|
130
|
+
* rather than restarting (and visibly seaming) at every slot cell.
|
|
131
|
+
*/
|
|
132
|
+
const unavailableRunsByDoor = $derived.by(() => {
|
|
133
|
+
// Local index built inside a $derived; outer reactivity covers reads.
|
|
134
|
+
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
|
135
|
+
const byDoor = new Map();
|
|
136
|
+
const slotCount = timeSlots.length;
|
|
137
|
+
if (!availability || slotCount === 0) {
|
|
138
|
+
return byDoor;
|
|
139
|
+
}
|
|
140
|
+
for (const door of doors) {
|
|
141
|
+
const windows = availability.get(door.id);
|
|
142
|
+
if (!windows) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const runs = [];
|
|
146
|
+
let runStart = null;
|
|
147
|
+
// One extra iteration (i === slotCount) flushes a run that reaches
|
|
148
|
+
// the end of the day.
|
|
149
|
+
for (let i = 0; i <= slotCount; i++) {
|
|
150
|
+
const unavailable = i < slotCount && slotUnavailableInWindows(windows, timeSlots[i]);
|
|
151
|
+
if (unavailable) {
|
|
152
|
+
if (runStart === null) {
|
|
153
|
+
runStart = i;
|
|
154
|
+
}
|
|
155
|
+
} else if (runStart !== null) {
|
|
156
|
+
runs.push({
|
|
157
|
+
topPercent: runStart / slotCount * 100,
|
|
158
|
+
heightPercent: (i - runStart) / slotCount * 100
|
|
159
|
+
});
|
|
160
|
+
runStart = null;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (runs.length > 0) {
|
|
164
|
+
byDoor.set(door.id, runs);
|
|
119
165
|
}
|
|
120
166
|
}
|
|
121
|
-
return
|
|
167
|
+
return byDoor;
|
|
168
|
+
});
|
|
169
|
+
function unavailableRunsAt(doorId) {
|
|
170
|
+
return unavailableRunsByDoor.get(doorId) ?? [];
|
|
122
171
|
}
|
|
123
172
|
/**
|
|
124
173
|
* Build a `YYYY-MM-DD` key for date bucketing. Avoids `toISOString`
|
|
@@ -696,6 +745,13 @@ $effect(() => {
|
|
|
696
745
|
}}
|
|
697
746
|
></div>
|
|
698
747
|
{/each}
|
|
748
|
+
<!-- Unavailable shading: one continuous block per contiguous run -->
|
|
749
|
+
{#each unavailableRunsAt(door.id) as run (run.topPercent)}
|
|
750
|
+
<div
|
|
751
|
+
class="unavailable-overlay"
|
|
752
|
+
style="top: {run.topPercent}%; height: {run.heightPercent}%;"
|
|
753
|
+
></div>
|
|
754
|
+
{/each}
|
|
699
755
|
<!-- Appointment blocks positioned absolutely (rendered after, sit above slots) -->
|
|
700
756
|
{#each appointmentsAt(door.id, day) as apt (apt.id)}
|
|
701
757
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
@@ -939,8 +995,16 @@ $effect(() => {
|
|
|
939
995
|
}
|
|
940
996
|
.slot-cell.unavailable {
|
|
941
997
|
cursor: not-allowed;
|
|
942
|
-
|
|
943
|
-
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
.unavailable-overlay {
|
|
1001
|
+
position: absolute;
|
|
1002
|
+
left: 0;
|
|
1003
|
+
right: 0;
|
|
1004
|
+
z-index: 3;
|
|
1005
|
+
pointer-events: none;
|
|
1006
|
+
background-color: rgba(128, 150, 165, 0.06);
|
|
1007
|
+
background-image: repeating-linear-gradient(45deg, transparent, transparent 6px, rgba(128, 150, 165, 0.14) 6px, rgba(128, 150, 165, 0.14) 12px);
|
|
944
1008
|
}
|
|
945
1009
|
|
|
946
1010
|
.appointment-block {
|