@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
- const minutes = slot.hour * 60 + slot.minute;
116
- for (const window of windows) {
117
- if (minutes >= window.startMinutes && minutes < window.endMinutes) {
118
- return false;
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 true;
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
- background-color: #e9ecf1;
943
- background-image: repeating-linear-gradient(45deg, transparent, transparent 5px, rgba(128, 150, 165, 0.18) 5px, rgba(128, 150, 165, 0.18) 10px);
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 {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lavalogic/scoria",
3
3
  "description": "Svelte components used for the FloWMS Web Frontend",
4
- "version": "0.40.17",
4
+ "version": "0.40.18",
5
5
  "publishConfig": {
6
6
  "access": "restricted"
7
7
  },