@poirazis/supercomponents-shared 1.2.15 → 1.2.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.
Files changed (37) hide show
  1. package/dist/index.js +17704 -22252
  2. package/dist/index.umd.cjs +19 -18
  3. package/package.json +11 -11
  4. package/src/lib/SuperButton/SuperButton.svelte +65 -35
  5. package/src/lib/SuperField/SuperField.svelte +8 -11
  6. package/src/lib/SuperForm/InnerForm.svelte +14 -8
  7. package/src/lib/SuperForm/SuperForm.svelte +5 -5
  8. package/src/lib/SuperPopover/SuperPopover.svelte +1 -1
  9. package/src/lib/SuperTable/SuperTable.css +13 -6
  10. package/src/lib/SuperTable/SuperTable.svelte +28 -19
  11. package/src/lib/SuperTable/constants.js +1 -1
  12. package/src/lib/SuperTable/controls/RowButtonsColumn.svelte +33 -17
  13. package/src/lib/SuperTable/controls/SelectionColumn.svelte +2 -2
  14. package/src/lib/SuperTableCells/CellBoolean.svelte +52 -45
  15. package/src/lib/SuperTableCells/CellCommon.css +97 -35
  16. package/src/lib/SuperTableCells/CellDatetime.svelte +267 -120
  17. package/src/lib/SuperTableCells/CellLink.svelte +22 -13
  18. package/src/lib/SuperTableCells/CellLinkPickerSelect.svelte +28 -24
  19. package/src/lib/SuperTableCells/CellLinkPickerTree.svelte +22 -20
  20. package/src/lib/SuperTableCells/CellNumber.svelte +55 -53
  21. package/src/lib/SuperTableCells/CellOptions.svelte +356 -246
  22. package/src/lib/SuperTableCells/CellOptionsAdvanced.svelte +199 -180
  23. package/src/lib/SuperTableCells/CellSQLLink.svelte +32 -32
  24. package/src/lib/SuperTableCells/CellSQLLinkPicker.svelte +2 -7
  25. package/src/lib/SuperTableCells/CellSkeleton.svelte +1 -1
  26. package/src/lib/SuperTableCells/CellString.svelte +62 -80
  27. package/src/lib/SuperTableCells/CellStringMask.svelte +41 -40
  28. package/src/lib/SuperTableCells/CellStringSimple.svelte +1 -4
  29. package/src/lib/SuperTableColumn/SuperTableColumn.svelte +3 -1
  30. package/src/lib/SuperTableColumn/parts/SuperColumnBody.svelte +3 -0
  31. package/src/lib/SuperTableColumn/parts/SuperColumnHeader.svelte +44 -42
  32. package/src/lib/SuperTableColumn/parts/SuperColumnRow.svelte +2 -4
  33. package/src/lib/SuperTabs/SuperTabs.svelte +33 -17
  34. package/src/lib/UI/elements/Checkbox.svelte +68 -10
  35. package/src/lib/UI/elements/Switch.svelte +162 -0
  36. package/src/lib/UI/elements/Tooltip.svelte +15 -43
  37. package/src/lib/SuperTableCells/JSDOC_GUIDE.md +0 -869
@@ -12,6 +12,11 @@
12
12
  export let autofocus;
13
13
 
14
14
  let originalValue;
15
+ let anchor;
16
+ let picker;
17
+ let timePicker;
18
+ let open;
19
+ let selection = false;
15
20
 
16
21
  export let cellState = fsm("View", {
17
22
  "*": {
@@ -20,6 +25,10 @@
20
25
  },
21
26
  },
22
27
  View: {
28
+ _enter() {
29
+ open = false;
30
+ selection = false;
31
+ },
23
32
  focus() {
24
33
  if (!cellOptions.readonly) return "Editing";
25
34
  },
@@ -33,125 +42,176 @@
33
42
  _exit() {
34
43
  dispatch("exitedit");
35
44
  },
45
+ toggle(e) {
46
+ open = !open;
47
+ },
36
48
  handleKeyboard(e) {
37
49
  if (e.keyCode == 32) {
38
50
  e.stopPropagation();
39
51
  e.preventDefault();
40
52
  open = !open;
41
53
  }
54
+
55
+ if (e.code == "Delete" || e.code == "Backspace") {
56
+ e.stopPropagation();
57
+ e.preventDefault();
58
+ value = null;
59
+ dispatch("change", value);
60
+ }
42
61
  },
43
62
  focusout(e) {
44
63
  const isInPicker = picker?.contains(e.relatedTarget);
45
64
  const isInTimePicker = timePicker?.contains(e.relatedTarget);
46
65
 
66
+ if (isInPicker || isInTimePicker) return;
67
+
47
68
  if (!isInPicker && !isInTimePicker) {
48
69
  open = false;
49
- if (value != originalValue) {
50
- dispatch("change", value);
70
+ let outputValue = innerDate.toISOString();
71
+ if (!currentShowTime) {
72
+ outputValue = innerDate.toLocaleDateString("en-CA"); // YYYY-MM-DD format without time
51
73
  }
74
+
75
+ if (ignoreTimeZone) {
76
+ outputValue = innerDate.toISOString().slice(0, -1);
77
+ }
78
+
79
+ if (selection) dispatch("change", outputValue);
52
80
  return "View";
53
81
  }
54
82
  },
83
+ submit() {
84
+ dispatch("change", innerDate.toISOString());
85
+ return "View";
86
+ },
55
87
  cancel() {
56
- value = Array.isArray(originalValue)
57
- ? [...originalValue]
58
- : originalValue;
88
+ value = originalValue;
59
89
  return "View";
60
90
  },
61
91
  },
62
92
  });
63
93
 
64
- let anchor;
65
- let picker;
66
- let timePicker;
67
- let open;
68
-
69
94
  // Current date format and showTime - extracted to ensure reactivity
70
95
  $: currentDateFormat = cellOptions?.dateFormat;
71
96
  $: currentShowTime = cellOptions?.showTime;
97
+ $: ignoreTimeZone = cellOptions?.ignoreTimezone;
98
+ $: show24HTime = cellOptions?.show24HTime;
99
+ $: innerDate = parseValueToDate(value);
100
+ $: timeValue = innerDate.toLocaleTimeString("en-US", {
101
+ hour12: !show24HTime,
102
+ hour: "2-digit",
103
+ minute: "2-digit",
104
+ });
72
105
 
73
- // Time value (format: HH:mm)
74
- $: timeValue = currentShowTime && value ? getTimeFromValue() : "00:00";
75
- $: innerDate = value ? new Date(value) : new Date();
106
+ // Helper function to create a Date object that properly handles UTC when ignoreTimezone is true
107
+ function parseValueToDate(valueStr) {
108
+ if (!valueStr) return new Date();
109
+ if (valueStr instanceof Date) {
110
+ valueStr = valueStr;
111
+ }
76
112
 
77
- // Extract time from current value if it exists
78
- function getTimeFromValue() {
79
- if (!value) return "00:00";
80
- const date = new Date(value);
81
- return `${date.getHours().toString().padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}`;
113
+ return new Date(valueStr);
82
114
  }
83
115
 
84
- // Date formatting helper function
85
- function formatDate(date, dateFormat) {
86
- if (!date) return "";
87
-
88
- if (!dateFormat || dateFormat === "default") {
89
- return date?.toDateString();
90
- }
116
+ // Parse 12-hour time format to 24-hour format
117
+ function parse12HourTime(timeStr) {
118
+ const match = timeStr.match(/(\d{1,2}):(\d{2})\s*(AM|PM)/i);
119
+ if (!match) return null;
120
+ let hours = parseInt(match[1]);
121
+ const minutes = parseInt(match[2]);
122
+ const ampm = match[3].toUpperCase();
91
123
 
92
- // Manual formatting for specific formats to ensure exact order
93
- if (dateFormat === "MM/DD/YYYY") {
94
- return `${(date.getMonth() + 1).toString().padStart(2, "0")}/${date.getDate().toString().padStart(2, "0")}/${date.getFullYear()}`;
95
- }
124
+ if (ampm === "PM" && hours !== 12) hours += 12;
125
+ if (ampm === "AM" && hours === 12) hours = 0;
96
126
 
97
- if (dateFormat === "DD/MM/YYYY") {
98
- return `${date.getDate().toString().padStart(2, "0")}/${(date.getMonth() + 1).toString().padStart(2, "0")}/${date.getFullYear()}`;
99
- }
127
+ return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}`;
128
+ }
100
129
 
101
- if (dateFormat === "YYYY-MM-DD") {
102
- return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`;
103
- }
130
+ // Format date/datetime helper function
131
+ // Only applies formatting - timezone handling is done in parseValueToDate
132
+ // If showTime is false (default), returns formatted date only
133
+ // If showTime is true, returns formatted date + time
134
+ function formatDateTime(date, dateFormat, showTime = false) {
135
+ if (!date) return "";
104
136
 
105
- // For shorter formats, still use locale-specific formatting
106
- const options = {
107
- "MMM DD, YYYY": {
108
- month: "short",
109
- day: "numeric",
110
- year: "numeric",
111
- timeZone: "UTC",
112
- },
113
- "DD MMM YYYY": {
114
- day: "numeric",
115
- month: "short",
116
- year: "numeric",
117
- timeZone: "UTC",
118
- },
119
- };
137
+ // Format the date part
138
+ let dateResult = "";
120
139
 
121
- const formatOption = options[dateFormat];
122
- if (formatOption) {
123
- return date.toLocaleDateString("en-US", formatOption);
140
+ if (!dateFormat || dateFormat === "default") {
141
+ dateResult = date?.toDateString();
142
+ } else {
143
+ // Use local methods to format - timezone is already handled by parseValueToDate
144
+ const month = date.getMonth();
145
+ const dayOfMonth = date.getDate();
146
+ const year = date.getFullYear();
147
+
148
+ // Manual formatting for specific formats to ensure exact order
149
+ if (dateFormat === "MM/DD/YYYY") {
150
+ dateResult = `${(month + 1).toString().padStart(2, "0")}/${dayOfMonth.toString().padStart(2, "0")}/${year}`;
151
+ } else if (dateFormat === "DD/MM/YYYY") {
152
+ dateResult = `${dayOfMonth.toString().padStart(2, "0")}/${(month + 1).toString().padStart(2, "0")}/${year}`;
153
+ } else if (dateFormat === "YYYY-MM-DD") {
154
+ dateResult = `${year}-${(month + 1).toString().padStart(2, "0")}-${dayOfMonth.toString().padStart(2, "0")}`;
155
+ } else {
156
+ // For shorter formats, use locale-specific formatting
157
+ const options = {
158
+ "MMM DD, YYYY": {
159
+ month: "short",
160
+ day: "numeric",
161
+ year: "numeric",
162
+ },
163
+ "DD MMM YYYY": {
164
+ day: "numeric",
165
+ month: "short",
166
+ year: "numeric",
167
+ },
168
+ };
169
+
170
+ const formatOption = options[dateFormat];
171
+ if (formatOption) {
172
+ dateResult = date.toLocaleDateString("en-US", formatOption);
173
+ } else {
174
+ // Fallback to default
175
+ dateResult = date?.toDateString();
176
+ }
177
+ }
124
178
  }
125
179
 
126
- // Fallback to default
127
- return date?.toDateString();
128
- }
129
-
130
- // DateTime formatting helper function
131
- function formatDateTime(date, dateFormat, showTime) {
132
- if (!date) return "";
133
-
180
+ // If not showing time, return just the formatted date
134
181
  if (!showTime) {
135
- return formatDate(date, dateFormat);
182
+ return dateResult;
136
183
  }
137
184
 
138
- const formattedDate = formatDate(date, dateFormat);
139
- const timeString = `${date.getHours().toString().padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}`;
185
+ // Format the time part when showTime is true (using local time)
186
+ let hours = date.getHours();
187
+ let minutes = date.getMinutes();
188
+
189
+ // Format based on show24HTime
190
+ let timeString;
191
+ if (show24HTime) {
192
+ timeString = `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}`;
193
+ } else {
194
+ const ampm = hours >= 12 ? "PM" : "AM";
195
+ const display12h = hours % 12 || 12;
196
+ timeString = `${display12h.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")} ${ampm}`;
197
+ }
140
198
 
141
- return `${formattedDate} ${timeString}`;
199
+ return `${dateResult} ${timeString}`;
142
200
  }
143
201
 
144
202
  $: formattedValue =
145
- cellOptions.template && value
146
- ? processStringSync(cellOptions.template, { value })
147
- : value
148
- ? formatDate(innerDate, currentDateFormat)
203
+ cellOptions.template && value && value != "" && value != null
204
+ ? processStringSync(cellOptions.template, {
205
+ value: innerDate,
206
+ })
207
+ : (value && value != "" && value != null) || selection
208
+ ? formatDateTime(innerDate, currentDateFormat, currentShowTime)
149
209
  : "";
150
210
 
151
211
  $: placeholder = cellOptions?.placeholder || "";
152
212
  $: inEdit = $cellState == "Editing";
153
213
  $: inline = cellOptions.role == "inlineInpur";
154
- $: isDirty = inEdit && originalValue != value;
214
+ $: isDirty = inEdit && true;
155
215
 
156
216
  onMount(() => {
157
217
  if (autofocus)
@@ -161,20 +221,72 @@
161
221
  }, 30);
162
222
  });
163
223
 
164
- // Handle time picker changes
224
+ // Handle Start button - set time to beginning of day (00:00 or 12:00 AM)
225
+ const handleStartTime = () => {
226
+ const startTime = show24HTime ? "00:00" : "12:00 AM";
227
+ timeValue = startTime;
228
+
229
+ innerDate.setHours(0, 0, 0, 0);
230
+ innerDate = innerDate;
231
+ selection = true;
232
+ anchor?.focus();
233
+ };
234
+
235
+ // Handle End button - set time to end of day (23:59 or 11:59 PM)
236
+ const handleEndTime = () => {
237
+ const endTime = show24HTime ? "23:59" : "11:59 PM";
238
+ timeValue = endTime;
239
+
240
+ innerDate.setHours(23, 59, 0, 0);
241
+ innerDate = innerDate;
242
+ selection = true;
243
+ anchor?.focus();
244
+ };
245
+
246
+ // Handle Now button - set both date and time to current date/time
247
+ const handleNowTime = () => {
248
+ innerDate = new Date();
249
+ selection = true;
250
+ anchor?.focus();
251
+ };
252
+
253
+ // Handle Clear button - clear the value
254
+ const handleClearValue = () => {
255
+ cellState.submit();
256
+ };
257
+
258
+ // Handle time picker changes - only affects the time, not timezone
165
259
  const handleTimeChange = (e) => {
166
- if (!value || !currentShowTime) return;
260
+ if (!currentShowTime) return;
261
+ selection = true;
167
262
 
168
263
  const newTime = e.target.value;
169
- const currentDate = new Date(value);
170
- const [hours, minutes] = newTime.split(":").map(Number);
264
+ // Convert 12-hour format to 24-hour if needed
265
+ const time24h = show24HTime ? newTime : parse12HourTime(newTime);
266
+ if (!time24h) return;
267
+
268
+ const [hours, minutes] = time24h.split(":").map(Number);
269
+
270
+ innerDate.setHours(hours, minutes, 0, 0);
271
+ };
171
272
 
172
- // Update the date with the new time
173
- currentDate.setHours(hours, minutes, 0, 0);
174
- value = currentDate.toISOString();
273
+ const handleDateChange = (e) => {
274
+ const newDate = e.detail;
275
+ selection = true;
276
+
277
+ if (currentShowTime) {
278
+ // Preserve the time from the current innerDate when appending to the selected date
279
+ newDate.setHours(
280
+ innerDate.getHours(),
281
+ innerDate.getMinutes(),
282
+ innerDate.getSeconds(),
283
+ innerDate.getMilliseconds(),
284
+ );
285
+ }
175
286
 
176
- // Keep the popover open for time changes
177
- dispatch("change", value);
287
+ innerDate = newDate;
288
+ anchor?.focus();
289
+ open = false;
178
290
  };
179
291
  </script>
180
292
 
@@ -196,47 +308,46 @@
196
308
  class:open-popup={open}
197
309
  style:color={cellOptions.color}
198
310
  style:background={cellOptions.background}
199
- style:font-weight={cellOptions.fontWeight}
200
311
  on:focus={cellState.focus}
201
- on:keypress={cellState.handleKeyboard}
312
+ on:keydown={cellState.handleKeyboard}
202
313
  on:focusout={cellState.focusout}
314
+ on:mousedown={cellState.toggle}
203
315
  >
204
- {#if cellOptions.icon}
205
- <i class={cellOptions.icon + " field-icon"}></i>
206
- {/if}
207
-
208
- {#if inEdit}
209
- <div
210
- class="editor"
211
- class:placeholder={!formattedValue}
212
- on:click={() => (open = !open)}
213
- >
214
- {formattedValue || placeholder}
215
- <i
216
- class="ri-calendar-line"
217
- style="font-size: 16px; justify-self: flex-end"
218
- ></i>
219
- </div>
220
- {:else}
221
- <div
222
- class="value"
223
- class:placeholder={!formattedValue}
224
- style:justify-content={cellOptions.align}
225
- >
226
- <span>
227
- {formattedValue || placeholder}
228
- </span>
229
- </div>
230
- {/if}
316
+ {#key $cellState}
317
+ {#if cellOptions.icon}
318
+ <i class={cellOptions.icon + " field-icon"}></i>
319
+ {/if}
320
+
321
+ {#if inEdit}
322
+ <div class="editor" class:placeholder={!formattedValue}>
323
+ <span>{formattedValue || placeholder}</span>
324
+ <i
325
+ class="ri-calendar-line"
326
+ style="font-size: 16px; justify-self: flex-end"
327
+ ></i>
328
+ </div>
329
+ {:else}
330
+ <div
331
+ class="value"
332
+ class:placeholder={!formattedValue}
333
+ style:justify-content={cellOptions.align}
334
+ >
335
+ <span>
336
+ {value ? formattedValue : placeholder}
337
+ </span>
338
+ </div>
339
+ {/if}
340
+ {/key}
231
341
  </div>
232
342
 
233
343
  {#if inEdit}
234
344
  <SuperPopover
235
345
  {anchor}
236
- dismissible={false}
237
346
  {open}
347
+ ignoreAnchor
238
348
  align="right"
239
349
  maxHeight={400}
350
+ on:close={cellState.focusout}
240
351
  >
241
352
  <div
242
353
  bind:this={picker}
@@ -245,26 +356,34 @@
245
356
  style:--date-picker-foreground="var(--spectrum-global-color-gray-800)"
246
357
  style:--date-picker-selected-background="var(--accent-color)"
247
358
  >
248
- <DatePicker
249
- bind:value={innerDate}
250
- on:select={(e) => {
251
- value = e.detail;
252
- anchor?.focus();
253
- open = false;
254
- }}
255
- />
359
+ <DatePicker value={innerDate} on:select={handleDateChange} />
256
360
 
257
361
  {#if currentShowTime}
258
362
  <div class="time-section">
259
363
  <!-- svelte-ignore a11y-label-has-associated-control -->
260
364
  <input
261
365
  bind:this={timePicker}
262
- type="time"
366
+ type="text"
367
+ placeholder={show24HTime ? "HH:MM" : "HH:MM AM/PM"}
263
368
  bind:value={timeValue}
264
369
  on:change={handleTimeChange}
370
+ on:focusout={cellState.focusout}
265
371
  class="time-input"
266
- step="900"
267
372
  />
373
+ <div class="time-buttons">
374
+ <button class="time-button start-button" on:click={handleStartTime}
375
+ >Start</button
376
+ >
377
+ <button class="time-button end-button" on:click={handleEndTime}
378
+ >End</button
379
+ >
380
+ <button class="time-button now-button" on:click={handleNowTime}
381
+ >Now</button
382
+ >
383
+ <button class="time-button clear-button" on:click={handleClearValue}
384
+ >Select</button
385
+ >
386
+ </div>
268
387
  </div>
269
388
  {/if}
270
389
  </div>
@@ -284,7 +403,35 @@
284
403
  .time-section {
285
404
  display: flex;
286
405
  flex-direction: column;
287
- gap: 4px;
406
+ gap: 8px;
407
+ }
408
+
409
+ .time-buttons {
410
+ display: flex;
411
+ gap: 6px;
412
+ }
413
+
414
+ .time-button {
415
+ flex: 1;
416
+ padding: 6px 12px;
417
+ border: 1px solid var(--spectrum-global-color-gray-300);
418
+ border-radius: 4px;
419
+ background: var(--spectrum-global-color-gray-50);
420
+ color: var(--spectrum-global-color-gray-800);
421
+ font-size: 13px;
422
+ font-weight: 500;
423
+ cursor: pointer;
424
+ transition: all 0.15s ease;
425
+ }
426
+
427
+ .time-button:hover {
428
+ background: var(--spectrum-global-color-gray-100);
429
+ border-color: var(--spectrum-global-color-gray-400);
430
+ }
431
+
432
+ .time-button:active {
433
+ background: var(--spectrum-global-color-gray-200);
434
+ border-color: var(--spectrum-global-color-gray-500);
288
435
  }
289
436
 
290
437
  .time-label {
@@ -145,7 +145,13 @@
145
145
 
146
146
  if (singleSelect) {
147
147
  editorState.close();
148
- anchor.focus();
148
+ }
149
+
150
+ if (cellOptions.debounced) {
151
+ dispatch(
152
+ "change",
153
+ returnSingle && localValue ? localValue[0] : localValue,
154
+ );
149
155
  }
150
156
  };
151
157
 
@@ -193,7 +199,7 @@
193
199
  class:open-popup={$editorState == "Open"}
194
200
  style:color={cellOptions.color}
195
201
  style:background={cellOptions.background}
196
- on:focusin={cellState.focus}
202
+ on:focus={cellState.focus}
197
203
  on:keydown|self={handleKeyboard}
198
204
  on:focusout={cellState.focusout}
199
205
  on:mousedown={cellState.toggle}
@@ -203,9 +209,14 @@
203
209
  {/if}
204
210
 
205
211
  <div class="value" class:placeholder={localValue?.length < 1}>
206
- {#if localValue?.length < 1}
207
- <span> {placeholder} </span>
208
- {:else if pills || links}
212
+ {#if simpleView}
213
+ <span>
214
+ {#if cellOptions.role == "formInput" && localValue.length > 1}
215
+ ({localValue.length})
216
+ {/if}
217
+ {localValue.map((v) => v.primaryDisplay).join(", ") || placeholder}
218
+ </span>
219
+ {:else}
209
220
  <div
210
221
  class="items"
211
222
  class:pills
@@ -214,7 +225,7 @@
214
225
  class:withCount={localValue.length > 5}
215
226
  class:inEdit
216
227
  >
217
- {#each localValue as val, idx}
228
+ {#each localValue as val, idx (val)}
218
229
  {#if idx < 5}
219
230
  <div
220
231
  class="item"
@@ -231,19 +242,17 @@
231
242
  </div>
232
243
  {/if}
233
244
  {/each}
245
+
246
+ {#if localValue.length == 0}
247
+ <span>{placeholder}</span>
248
+ {/if}
249
+
234
250
  {#if localValue.length > 5}
235
251
  <span class="count">
236
252
  (+ {localValue.length - 5})
237
253
  </span>
238
254
  {/if}
239
255
  </div>
240
- {:else}
241
- <span>
242
- {#if cellOptions.role == "formInput" && localValue.length > 1}
243
- ({localValue.length})
244
- {/if}
245
- {localValue.map((v) => v.primaryDisplay).join(", ")}
246
- </span>
247
256
  {/if}
248
257
  </div>
249
258
  {#if !readonly && (cellOptions.role == "formInput" || inEdit)}
@@ -265,34 +265,38 @@
265
265
  <div class="list" bind:this={listElement} on:scroll={handleScroll}>
266
266
  <div class="options">
267
267
  {#key localValue}
268
- {#if $fetch?.rows?.length || ($fetch?.loading && !$fetch?.loaded)}
269
- {#each $fetch?.rows || [] as row, idx (idx)}
270
- <div
271
- class="option"
272
- class:selected={rowSelected(row)}
273
- class:highlighted={focusIdx == idx}
274
- on:mouseenter={() => (focusIdx = idx)}
275
- on:mouseleave={() => (focusIdx = -1)}
276
- on:mousedown|preventDefault|stopPropagation={selectRow(row)}
277
- >
278
- {row[primaryDisplay]}
279
- <i class="ri-check-line"></i>
280
- </div>
281
- {/each}
282
- {#if $fetch?.loading && $fetch.loaded}
268
+ {#key $fetch?.rows}
269
+ {#if $fetch?.rows?.length || ($fetch?.loading && !$fetch?.loaded)}
270
+ {#each $fetch?.rows || [] as row, idx (idx)}
271
+ <div
272
+ class="option"
273
+ class:selected={rowSelected(row)}
274
+ class:highlighted={focusIdx == idx}
275
+ on:mouseenter={() => (focusIdx = idx)}
276
+ on:mouseleave={() => (focusIdx = -1)}
277
+ on:mousedown|preventDefault|stopPropagation={selectRow(
278
+ row,
279
+ )}
280
+ >
281
+ {row[primaryDisplay]}
282
+ <i class="ri-check-line"></i>
283
+ </div>
284
+ {/each}
285
+ {#if $fetch?.loading && $fetch.loaded}
286
+ <div class="option loading">
287
+ <i class="ri-loader-2-line rotating"></i>
288
+ Loading more...
289
+ </div>
290
+ {/if}
291
+ {:else if $fetch?.loading}
283
292
  <div class="option loading">
284
293
  <i class="ri-loader-2-line rotating"></i>
285
- Loading more...
294
+ Loading...
286
295
  </div>
296
+ {:else}
297
+ <div class="option">No Results Found</div>
287
298
  {/if}
288
- {:else if $fetch?.loading}
289
- <div class="option loading">
290
- <i class="ri-loader-2-line rotating"></i>
291
- Loading...
292
- </div>
293
- {:else}
294
- <div class="option">No Results Found</div>
295
- {/if}
299
+ {/key}
296
300
  {/key}
297
301
  </div>
298
302
  </div>