@poirazis/supercomponents-shared 1.1.8 → 1.2.1

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 (51) hide show
  1. package/dist/index.js +21180 -40125
  2. package/dist/index.umd.cjs +19 -26
  3. package/dist/style.css +1 -1
  4. package/package.json +5 -5
  5. package/src/index.js +4 -0
  6. package/src/index.ts +3 -0
  7. package/src/lib/Actions/index.js +3 -3
  8. package/src/lib/Actions/position_dropdown.js +14 -7
  9. package/src/lib/SuperButton/SuperButton.svelte +34 -3
  10. package/src/lib/SuperField/SuperField.svelte +0 -11
  11. package/src/lib/SuperForm/InnerForm.svelte +1 -1
  12. package/src/lib/SuperList/SuperList.svelte +2 -2
  13. package/src/lib/SuperList/drag-handle.svelte +8 -8
  14. package/src/lib/SuperPopover/SuperPopover.svelte +2 -2
  15. package/src/lib/SuperTable/SuperTable.css +8 -3
  16. package/src/lib/SuperTable/SuperTable.svelte +3 -3
  17. package/src/lib/SuperTable/controls/PaginationLimitOffset.svelte +2 -0
  18. package/src/lib/SuperTable/controls/SelectionColumn.svelte +18 -49
  19. package/src/lib/SuperTable/controls/SuperTableWelcome.svelte +1 -1
  20. package/src/lib/SuperTable/overlays/AddNewRowOverlay.svelte +3 -3
  21. package/src/lib/SuperTable/overlays/EmptyResultSetOverlay.svelte +1 -1
  22. package/src/lib/SuperTable/overlays/ScrollbarsOverlay.svelte +43 -43
  23. package/src/lib/SuperTableCells/CellAttachmentExpanded.svelte +1 -1
  24. package/src/lib/SuperTableCells/CellAttachmentSlider.svelte +2 -3
  25. package/src/lib/SuperTableCells/CellBoolean.svelte +1 -1
  26. package/src/lib/SuperTableCells/CellColor.svelte +7 -7
  27. package/src/lib/SuperTableCells/CellCommon.css +1 -1
  28. package/src/lib/SuperTableCells/CellIcon.svelte +7 -7
  29. package/src/lib/SuperTableCells/CellJSON.svelte +2 -2
  30. package/src/lib/SuperTableCells/CellLink.svelte +50 -43
  31. package/src/lib/SuperTableCells/CellLinkAdvanced.svelte +2 -16
  32. package/src/lib/SuperTableCells/CellLinkPickerSelect.svelte +10 -11
  33. package/src/lib/SuperTableCells/CellLinkPickerTree.svelte +4 -2
  34. package/src/lib/SuperTableCells/CellNumber.svelte +24 -5
  35. package/src/lib/SuperTableCells/CellOptions.svelte +20 -34
  36. package/src/lib/SuperTableCells/CellOptionsAdvanced.svelte +5 -5
  37. package/src/lib/SuperTableCells/CellSQLLink.svelte +24 -36
  38. package/src/lib/SuperTableCells/CellSQLLinkPicker.svelte +9 -9
  39. package/src/lib/SuperTableCells/CellString.svelte +3 -3
  40. package/src/lib/SuperTableCells/CellStringMask.svelte +1 -1
  41. package/src/lib/SuperTableCells/CellTags.svelte +151 -108
  42. package/src/lib/SuperTableCells/JSDOC_GUIDE.md +163 -3
  43. package/src/lib/SuperTableCells/types.js +141 -192
  44. package/src/lib/SuperTableColumn/SuperTableColumn.svelte +1 -1
  45. package/src/lib/SuperTableColumn/parts/SuperColumnHeader.svelte +2 -2
  46. package/src/lib/SuperTableColumn/parts/SuperColumnRow.svelte +5 -3
  47. package/src/lib/SuperTabs/SuperTabs.svelte +2 -2
  48. package/src/lib/SuperTree/SuperTree.svelte +84 -38
  49. package/src/lib/UI/elements/Checkbox.svelte +36 -6
  50. package/src/lib/UI/elements/IconButton.svelte +115 -0
  51. package/src/lib/UI/elements/Tooltip.svelte +65 -0
@@ -208,7 +208,7 @@
208
208
  style:color={filterTerm
209
209
  ? "var(--spectrum-global-color-blue-400)"
210
210
  : "var(--spectrum-global-color-gray-700)"}
211
- />
211
+ ></i>
212
212
  <input
213
213
  bind:this={control}
214
214
  class="search"
@@ -257,7 +257,7 @@
257
257
  {#each relatedColumns as col}
258
258
  <div class="data-cell">{row[col.name] || ""}</div>
259
259
  {/each}
260
- <div class="data-cell check"><i class="ri-check-line" /></div>
260
+ <div class="data-cell check"><i class="ri-check-line" ></i></div>
261
261
  </div>
262
262
  {/each}
263
263
 
@@ -278,7 +278,7 @@
278
278
  <div class="data-cell">{row[col.name] || ""}</div>
279
279
  {/each}
280
280
  <div class="data-cell check">
281
- <i class="ri-check-line" />
281
+ <i class="ri-check-line" ></i>
282
282
  </div>
283
283
  </div>
284
284
  {/if}
@@ -289,13 +289,13 @@
289
289
  {#if $optionsFetch?.loading}
290
290
  <div class="data-row loading">
291
291
  <div class="data-cell" style="grid-column: 1 / -1;">
292
- <i class="ri-loader-2-line rotating" /> Loading more...
292
+ <i class="ri-loader-2-line rotating" ></i> Loading more...
293
293
  </div>
294
294
  </div>
295
295
  {:else if $optionsFetch?.loading && !$optionsFetch.loaded}
296
296
  <div class="data-row loading">
297
297
  <div class="data-cell" style="grid-column: 1 / -1;">
298
- <i class="ri-loader-2-line rotating" /> Loading...
298
+ <i class="ri-loader-2-line rotating" ></i> Loading...
299
299
  </div>
300
300
  </div>
301
301
  {:else if !$optionsFetch?.loading && $optionsFetch?.loaded && !$optionsFetch.rows?.length}
@@ -319,7 +319,7 @@
319
319
  on:mousedown|preventDefault|stopPropagation={() => selectRow(row)}
320
320
  >
321
321
  {row.primaryDisplay || row[primaryDisplay]}
322
- <i class="ri-check-line" />
322
+ <i class="ri-check-line" ></i>
323
323
  </div>
324
324
  {/each}
325
325
 
@@ -337,20 +337,20 @@
337
337
  on:mousedown|preventDefault={() => selectRow(row)}
338
338
  >
339
339
  {row.primaryDisplay || row[primaryDisplay]}
340
- <i class="ri-check-line" />
340
+ <i class="ri-check-line" ></i>
341
341
  </div>
342
342
  {/if}
343
343
  {/each}
344
344
  {/key}
345
345
  {#if $optionsFetch?.loading}
346
346
  <div class="option loading">
347
- <i class="ri-loader-2-line rotating" />
347
+ <i class="ri-loader-2-line rotating" ></i>
348
348
  Loading more...
349
349
  </div>
350
350
  {/if}
351
351
  {:else if $optionsFetch?.loading && !$optionsFetch.loaded}
352
352
  <div class="option loading">
353
- <i class="ri-loader-2-line rotating" />
353
+ <i class="ri-loader-2-line rotating" ></i>
354
354
  Loading...
355
355
  </div>
356
356
  {:else}
@@ -8,7 +8,7 @@
8
8
  import fsm from "svelte-fsm";
9
9
 
10
10
  /**
11
- * @typedef {import('./types.js').CellStringOptions} CellStringOptions
11
+ * @typedef {import('./types.js').CellOptions} CellOptions
12
12
  * @typedef {import('./types.js').CellApi} CellApi
13
13
  */
14
14
 
@@ -21,7 +21,7 @@
21
21
  /** @type {string | undefined} */
22
22
  export let formattedValue = undefined;
23
23
 
24
- /** @type {CellStringOptions} */
24
+ /** @type {CellOptions} */
25
25
  export let cellOptions = {
26
26
  role: "formInput",
27
27
  initialState: "Editing",
@@ -260,7 +260,7 @@
260
260
  <i
261
261
  class="ri-close-line clearIcon"
262
262
  on:mousedown|self|preventDefault={cellState.clear}
263
- />
263
+ ></i>
264
264
  {/if}
265
265
  {/if}
266
266
  {:else if textarea}
@@ -290,7 +290,7 @@
290
290
  <i
291
291
  class="ri-close-line clearIcon"
292
292
  on:mousedown|self|preventDefault={cellState.clear}
293
- />
293
+ ></i>
294
294
  {/if}
295
295
  {:else}
296
296
  <div
@@ -13,7 +13,7 @@
13
13
 
14
14
  export let cellOptions;
15
15
  export let value;
16
- export let fieldSchema;
16
+
17
17
  export let autofocus;
18
18
 
19
19
  let anchor;
@@ -35,6 +35,33 @@
35
35
 
36
36
  let searchInput;
37
37
 
38
+ // Helper function to add tags while preventing duplicates (case-insensitive comparison)
39
+ const addUniqueTags = (tagsToAdd) => {
40
+ if (!tagsToAdd || !tagsToAdd.length) return;
41
+
42
+ const existingLower = new Set(
43
+ (localValue || []).map((t) =>
44
+ String(t || "")
45
+ .toLowerCase()
46
+ .trim()
47
+ )
48
+ );
49
+
50
+ const newTags = [];
51
+ tagsToAdd.forEach((tag) => {
52
+ const trimmedTag = String(tag || "").trim();
53
+ if (trimmedTag && !existingLower.has(trimmedTag.toLowerCase())) {
54
+ newTags.push(trimmedTag);
55
+ existingLower.add(trimmedTag.toLowerCase());
56
+ }
57
+ });
58
+
59
+ if (newTags.length) {
60
+ localValue = [...(localValue || []), ...newTags];
61
+ cellState.change();
62
+ }
63
+ };
64
+
38
65
  const colors = derivedMemo(options, ($options) => {
39
66
  let obj = {};
40
67
  $options.forEach(
@@ -179,16 +206,25 @@
179
206
  },
180
207
  Editing: {
181
208
  _enter() {
182
- originalValue = JSON.stringify(value);
209
+ originalValue = JSON.stringify(
210
+ Array.isArray(value) ? value : value ? [value] : []
211
+ );
183
212
  this.clearFilters();
213
+ editorState.open();
184
214
  dispatch("enteredit");
185
215
  },
186
216
  _exit() {
187
217
  editorState.close();
188
218
  dispatch("exitedit");
189
219
  },
220
+ focus() {
221
+ anchor?.focus();
222
+ },
190
223
  focusout(e) {
191
- if ($editorState == "Open" || anchor?.contains(e.relatedTarget)) {
224
+ if (
225
+ anchor?.contains(e.relatedTarget) ||
226
+ editor?.contains(e.relatedTarget)
227
+ ) {
192
228
  return;
193
229
  } else {
194
230
  this.submit();
@@ -215,6 +251,8 @@
215
251
  this.submit();
216
252
  },
217
253
  cancel() {
254
+ editorState.close();
255
+ localValue = JSON.parse(originalValue);
218
256
  anchor?.blur();
219
257
  return "View";
220
258
  },
@@ -238,8 +276,8 @@
238
276
  localValue.splice(pos, 1);
239
277
  localValue = [...localValue];
240
278
  } else {
241
- // Add if not selected
242
- localValue = [...localValue, option];
279
+ // Add only if not already in localValue
280
+ addUniqueTags([option]);
243
281
  }
244
282
  cellState.change();
245
283
  // Check if we need to fetch more options
@@ -254,8 +292,8 @@
254
292
  localValue.splice(pos, 1);
255
293
  localValue = [...localValue];
256
294
  } else {
257
- // Add if not selected
258
- localValue = [...localValue, option];
295
+ // Add only if not already in localValue
296
+ addUniqueTags([option]);
259
297
  }
260
298
  cellState.change();
261
299
  // Check if we need to fetch more options
@@ -277,8 +315,7 @@
277
315
  .split(",")
278
316
  .map((tag) => tag.trim())
279
317
  .filter((tag) => tag);
280
- localValue = [...localValue, ...tags];
281
- cellState.change();
318
+ addUniqueTags(tags);
282
319
  }
283
320
  searchTerm = "";
284
321
  newTag = null;
@@ -329,14 +366,11 @@
329
366
  if (e.key == "Enter") {
330
367
  // Add new tag if exists
331
368
  if (newTag?.trim()) {
332
- localValue = [
333
- ...localValue,
334
- ...newTag
335
- .split(",")
336
- .map((tag) => tag.trim())
337
- .filter((tag) => tag),
338
- ];
339
- cellState.change();
369
+ const tags = newTag
370
+ .split(",")
371
+ .map((tag) => tag.trim())
372
+ .filter((tag) => tag);
373
+ addUniqueTags(tags);
340
374
  newTag = null;
341
375
  searchTerm = "";
342
376
  // Refocus input
@@ -353,16 +387,29 @@
353
387
  // If no focused option, allow space to be input into the search box
354
388
  }
355
389
  if (e.key == "Tab") {
356
- if (focusedOptionIdx > -1 && filteredOptions[focusedOptionIdx])
357
- this.toggleOption(focusedOptionIdx);
390
+ anchor?.focus();
391
+ editorState.close();
392
+ e.preventDefault();
393
+ return;
394
+ }
358
395
 
359
- // Keep popup open - no submit/close
396
+ if (e.key == "Escape") {
397
+ newTag = null;
398
+
399
+ editorState.close();
400
+ anchor?.focus();
401
+
402
+ e.preventDefault();
403
+ e.stopPropagation();
404
+ return;
360
405
  }
361
406
 
362
407
  if (e.key == "ArrowDown") this.highlightNext(e.stopPropagation());
363
408
  if (e.key == "ArrowUp")
364
409
  this.highlightPrevious(e.preventDefault(), e.stopPropagation());
365
- if (e.key == "Escape") this.close();
410
+ if (e.key == "Escape") {
411
+ cellState.cancel();
412
+ }
366
413
  },
367
414
 
368
415
  handleKeyboard(e) {
@@ -389,8 +436,6 @@
389
436
  if (e.key == "ArrowDown") this.highlightNext();
390
437
  if (e.key == "ArrowUp") this.highlightPrevious(e.preventDefault());
391
438
 
392
- if (e.key == "Escape") this.close();
393
-
394
439
  if (controlType != "inputSelect") search = true;
395
440
  },
396
441
  highlightNext() {
@@ -526,19 +571,16 @@
526
571
  class:multirow={true}
527
572
  style:color
528
573
  style:background
529
- style:font-weight={cellOptions.fontWeight}
530
574
  class:inline={role == "inlineInput"}
531
575
  class:tableCell={role == "tableCell"}
532
576
  class:formInput={role == "formInput"}
533
577
  on:focusin={cellState.focus}
534
- on:focusout={(e) => {
535
- setTimeout(() => cellState.focusout(e), 50);
536
- }}
578
+ on:focusout={cellState.focusout}
537
579
  on:keydown={editorState.handleKeyboard}
538
580
  >
539
581
  <div class="value" class:placeholder={isEmpty} tabindex="-1">
540
582
  {#if isEmpty && !inEdit}
541
- <span>{cellOptions.placeholder || "No Tags"}</span>
583
+ <span>{cellOptions.placeholder || "Add some Tags"}</span>
542
584
  {/if}
543
585
 
544
586
  <div
@@ -553,7 +595,6 @@
553
595
  class="tag"
554
596
  style:--option-color={$colors[tag] ||
555
597
  colorsArray[idx % colorsArray.length]}
556
- tabindex="-1"
557
598
  >
558
599
  <span class="tag-wrap">
559
600
  <span> {tag} </span>
@@ -565,7 +606,7 @@
565
606
  style:z-index={2}
566
607
  on:mousedown|preventDefault|stopPropagation={() =>
567
608
  editorState.toggleOption(tag)}
568
- />
609
+ ></i>
569
610
  {/if}
570
611
  </div>
571
612
  {/each}
@@ -573,8 +614,8 @@
573
614
 
574
615
  {#if inEdit}
575
616
  <i
576
- class="ph ph-plus-circle actionIcon"
577
- on:mousedown|preventDefault|stopPropagation={editorState.toggle}
617
+ class="ph ph-plus actionIcon"
618
+ on:mouseup|preventDefault|stopPropagation={editorState.toggle}
578
619
  ></i>
579
620
  {/if}
580
621
  </div>
@@ -589,83 +630,78 @@
589
630
  useAnchorWidth
590
631
  maxHeight={250}
591
632
  open={$editorState == "Open"}
592
- on:close={(e) => {
593
- editorState.close();
594
- cellState.focusout({});
595
- }}
596
633
  >
597
- <div class="searchControl" on:keydown={editorState.handleInputKeyboard}>
598
- <i
599
- class={suggestions ? "ph ph-magnifying-glass" : "ph ph-pencil-simple"}
600
- class:actionIcon={true}
601
- ></i>
602
- <input
603
- type="text"
604
- placeholder={suggestions ? "Search or Add" : "Enter tag..."}
605
- class="searchInput"
606
- bind:value={searchTerm}
607
- bind:this={searchInput}
608
- on:input={(e) => editorState.filterOptions(e.target.value)}
609
- on:focusout={(e) => {
610
- editorState.close();
611
- anchor?.focus();
612
- }}
613
- use:focus
614
- />
615
- </div>
634
+ <div bind:this={editor} class="editor" tabindex="-1">
635
+ <div class="searchControl" on:keydown={editorState.handleInputKeyboard}>
636
+ <i
637
+ class={suggestions ? "ph ph-magnifying-glass" : "ph ph-pencil-simple"}
638
+ class:actionIcon={true}
639
+ ></i>
640
+ <input
641
+ type="text"
642
+ placeholder={suggestions ? "Search or Add" : "Enter tag..."}
643
+ class="searchInput"
644
+ bind:value={searchTerm}
645
+ bind:this={searchInput}
646
+ on:input={(e) => editorState.filterOptions(e.target.value)}
647
+ on:focusout={cellState.focusout}
648
+ />
649
+ </div>
616
650
 
617
- {#if suggestions}
618
- <div
619
- bind:this={picker}
620
- class="options"
621
- on:wheel={(e) => e.stopPropagation()}
622
- on:mouseleave={() => (focusedOptionIdx = -1)}
623
- on:scroll={suggestions ? handleScroll : null}
624
- >
625
- {#if suggestions}
626
- {#if $fetch?.loading && !$fetch?.rows?.length}
627
- <div class="option loading">
628
- <i class="ri-loader-2-line rotating" />
629
- Loading...
630
- </div>
631
- {:else if filteredOptions?.length}
632
- {#each filteredOptions as option, idx (idx)}
633
- {#if !localValue?.includes(option)}
634
- <div
635
- class="option"
636
- class:text={optionsViewMode == "text"}
637
- class:focused={focusedOptionIdx === idx}
638
- style:--option-color={$colors[option]}
639
- on:mousedown|preventDefault={(e) =>
640
- editorState.toggleOption(idx)}
641
- on:mouseenter={() => (focusedOptionIdx = idx)}
642
- >
643
- <span>
644
- {#if cellOptions.optionsViewMode !== "text"}
645
- <i class="ri-checkbox-blank-fill" />
646
- {/if}
647
- {option}
648
- </span>
651
+ {#if suggestions}
652
+ <div
653
+ bind:this={picker}
654
+ class="options"
655
+ on:wheel={(e) => e.stopPropagation()}
656
+ on:mouseleave={() => (focusedOptionIdx = -1)}
657
+ on:scroll={suggestions ? handleScroll : null}
658
+ on:mousedown|preventDefault|stopPropagation
659
+ >
660
+ {#if suggestions}
661
+ {#if $fetch?.loading && !$fetch?.rows?.length}
662
+ <div class="option loading">
663
+ <i class="ri-loader-2-line rotating"></i>
664
+ Loading...
665
+ </div>
666
+ {:else if filteredOptions?.length}
667
+ {#each filteredOptions as option, idx (idx)}
668
+ {#if !localValue?.includes(option)}
669
+ <div
670
+ class="option"
671
+ class:text={optionsViewMode == "text"}
672
+ class:focused={focusedOptionIdx === idx}
673
+ style:--option-color={$colors[option]}
674
+ on:mousedown|preventDefault={(e) =>
675
+ editorState.toggleOption(idx)}
676
+ on:mouseenter={() => (focusedOptionIdx = idx)}
677
+ >
678
+ <span>
679
+ {#if cellOptions.optionsViewMode !== "text"}
680
+ <i class="ri-checkbox-blank-fill"></i>
681
+ {/if}
682
+ {option}
683
+ </span>
684
+ </div>
685
+ {/if}
686
+ {/each}
687
+ {#if $fetch?.loading}
688
+ <div class="option loading">
689
+ <i class="ri-loader-2-line rotating"></i>
690
+ Loading more...
649
691
  </div>
650
692
  {/if}
651
- {/each}
652
- {#if $fetch?.loading}
653
- <div class="option loading">
654
- <i class="ri-loader-2-line rotating" />
655
- Loading more...
693
+ {:else}
694
+ <div class="option not-found">
695
+ <span>
696
+ <i class="ri-close-line"></i>
697
+ No matches found, Tag will be added as new
698
+ </span>
656
699
  </div>
657
700
  {/if}
658
- {:else}
659
- <div class="option not-found">
660
- <span>
661
- <i class="ri-close-line" />
662
- No matches found, Tag will be added as new
663
- </span>
664
- </div>
665
701
  {/if}
666
- {/if}
667
- </div>
668
- {/if}
702
+ </div>
703
+ {/if}
704
+ </div>
669
705
  </SuperPopover>
670
706
  {/if}
671
707
 
@@ -697,9 +733,15 @@
697
733
  text-transform: uppercase;
698
734
  outline: none;
699
735
  max-width: 7rem;
736
+ transition: all 0.2s ease-in-out;
700
737
 
701
738
  &:hover {
702
739
  filter: brightness(0.9);
740
+
741
+ & > i {
742
+ display: block;
743
+ cursor: pointer;
744
+ }
703
745
  }
704
746
 
705
747
  & > span {
@@ -707,6 +749,12 @@
707
749
  text-overflow: ellipsis;
708
750
  white-space: nowrap;
709
751
  }
752
+
753
+ & > i {
754
+ display: none;
755
+ cursor: pointer;
756
+ transition: all 0.2s ease-in-out;
757
+ }
710
758
  }
711
759
 
712
760
  .searchControl {
@@ -745,11 +793,6 @@
745
793
  cursor: pointer;
746
794
  }
747
795
 
748
- &.selected {
749
- color: var(--spectrum-global-color-gray-800);
750
- font-weight: 600;
751
- }
752
-
753
796
  &.focused {
754
797
  background-color: var(--spectrum-global-color-gray-75);
755
798
  color: var(--spectrum-global-color-gray-800);
@@ -805,7 +848,7 @@
805
848
  display: flex;
806
849
  justify-content: center;
807
850
  align-items: center;
808
- font-size: 1rem;
851
+ font-size: 0.85rem;
809
852
  color: var(--spectrum-global-color-gray-600);
810
853
  }
811
854
  .actionIcon:hover {