@poirazis/supercomponents-shared 1.2.14 → 1.2.16

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.
@@ -6,7 +6,8 @@
6
6
  import CellSkeleton from "./CellSkeleton.svelte";
7
7
  import "./CellCommon.css";
8
8
  const dispatch = createEventDispatcher();
9
- const { API, QueryUtils, fetchData, memo, derivedMemo } = getContext("sdk");
9
+ const { API, QueryUtils, fetchData, memo, derivedMemo, builderStore } =
10
+ getContext("sdk");
10
11
 
11
12
  export let cellOptions;
12
13
  export let value;
@@ -27,13 +28,14 @@
27
28
  let picker;
28
29
  let inactive = true;
29
30
  let allSelected = false;
31
+ let fetch;
30
32
 
31
33
  const colors = derivedMemo(options, ($options) => {
32
34
  let obj = {};
33
35
  if (cellOptions.optionsSource == "custom") return obj;
34
36
  $options.forEach(
35
37
  (option, index) =>
36
- (obj[option] = optionColors[option] ?? colorsArray[index % 14])
38
+ (obj[option] = optionColors[option] ?? colorsArray[index % 14]),
37
39
  );
38
40
  return obj;
39
41
  });
@@ -57,7 +59,7 @@
57
59
  ];
58
60
 
59
61
  let originalValue = JSON.stringify(
60
- Array.isArray(value) ? value : value ? [value] : []
62
+ Array.isArray(value) ? value : value ? [value] : [],
61
63
  );
62
64
 
63
65
  $: ({
@@ -71,33 +73,34 @@
71
73
  role,
72
74
  readonly,
73
75
  disabled,
76
+ direction,
74
77
  error,
75
78
  color,
76
79
  background,
77
80
  } = cellOptions);
78
81
 
82
+ $: inBuilder = $builderStore.inBuilder;
83
+
79
84
  // Handle Options from Data Source
80
85
  const dataSourceStore = memo(cellOptions?.datasource ?? {});
81
86
  $: dataSourceStore.set(cellOptions.datasource);
82
- $: fetch =
83
- optionsSource == "data"
84
- ? createFetch($dataSourceStore)
85
- : memo({ loaded: true });
86
87
  $: fullSelection =
87
88
  filteredOptions.length == localValue.length && filteredOptions.length > 0;
88
89
 
89
90
  // React to property changes
90
- $: cellState.refresh(
91
- fieldSchema,
92
- optionsSource,
93
- labelColumn,
94
- valueColumn,
95
- iconColumn,
96
- colorColumn,
97
- $dataSourceStore
98
- );
91
+ $: if (inBuilder)
92
+ cellState.refresh(
93
+ fieldSchema,
94
+ optionsSource,
95
+ customOptions,
96
+ labelColumn,
97
+ valueColumn,
98
+ iconColumn,
99
+ colorColumn,
100
+ );
99
101
 
100
102
  $: cellState.syncFetch($fetch);
103
+ $: cellState.refresh($dataSourceStore);
101
104
 
102
105
  // We always keep an internal value as an array
103
106
  $: localValue = Array.isArray(value) ? value : value ? [value] : [];
@@ -106,6 +109,20 @@
106
109
  $: radios = controlType == "radio";
107
110
  $: isButtons = controlType == "buttons";
108
111
  $: allSelected = filteredOptions.length == localValue.length;
112
+ $: loading = $cellState == "Loading";
113
+
114
+ const createFetch = (datasource) => {
115
+ return fetchData({
116
+ API,
117
+ datasource,
118
+ options: {
119
+ query: QueryUtils.buildQuery(cellOptions.filter),
120
+ sortColumn: cellOptions.sortColumn,
121
+ sortOrder: cellOptions.sortOrder,
122
+ limit: cellOptions.limit || 1000,
123
+ },
124
+ });
125
+ };
109
126
 
110
127
  export let cellState = fsm("Loading", {
111
128
  "*": {
@@ -116,9 +133,15 @@
116
133
  $options = [];
117
134
  return "Loading";
118
135
  },
136
+ loadOptions() {
137
+ if (optionsSource == "schema") this.loadSchemaOptions();
138
+ else if (optionsSource == "data") this.loadDataOptions($fetch?.rows);
139
+ else if (optionsSource == "custom") this.loadCustomOptions();
140
+ },
119
141
  loadSchemaOptions() {
120
142
  optionColors = fieldSchema?.optionColors || {};
121
143
  $options = fieldSchema?.constraints?.inclusion || [];
144
+ filteredOptions = $options;
122
145
  },
123
146
  loadDataOptions(rows) {
124
147
  if (rows && rows.length) {
@@ -131,6 +154,7 @@
131
154
  });
132
155
  }
133
156
  $options = $options;
157
+ filteredOptions = $options;
134
158
  },
135
159
  loadCustomOptions() {
136
160
  if (customOptions?.length) {
@@ -147,18 +171,17 @@
147
171
  },
148
172
  Loading: {
149
173
  _enter() {
150
- if (cellOptions.optionsSource != "data" || $fetch?.loaded)
174
+ if (cellOptions.optionsSource != "data")
151
175
  this.goTo.debounce(10, cellOptions.initialState || "View");
176
+ else {
177
+ fetch = createFetch($dataSourceStore);
178
+ }
152
179
  },
153
180
  _exit() {
154
- if (optionsSource == "custom") this.loadCustomOptions();
155
- else if (optionsSource == "data") this.loadDataOptions($fetch?.rows);
156
- else this.loadSchemaOptions();
157
-
158
- filteredOptions = $options;
181
+ this.loadOptions();
159
182
  },
160
- syncFetch() {
161
- if ($fetch?.loaded) {
183
+ syncFetch(fetch) {
184
+ if (fetch?.loaded) {
162
185
  return cellOptions.initialState || "View";
163
186
  }
164
187
  },
@@ -174,7 +197,7 @@
174
197
  Editing: {
175
198
  _enter() {
176
199
  originalValue = JSON.stringify(
177
- Array.isArray(value) ? value : value ? [value] : []
200
+ Array.isArray(value) ? value : value ? [value] : [],
178
201
  );
179
202
 
180
203
  dispatch("enteredit");
@@ -284,7 +307,7 @@
284
307
  filterOptions(e) {
285
308
  if (e && e.target.value != "") {
286
309
  filteredOptions = $options.filter((x) =>
287
- x?.startsWith(e.target.value)
310
+ x?.startsWith(e.target.value),
288
311
  );
289
312
  } else filteredOptions = $options;
290
313
  },
@@ -332,19 +355,6 @@
332
355
  },
333
356
  });
334
357
 
335
- const createFetch = (datasource) => {
336
- return fetchData({
337
- API,
338
- datasource,
339
- options: {
340
- query: QueryUtils.buildQuery(cellOptions.filter),
341
- sortColumn: cellOptions.sortColumn,
342
- sortOrder: cellOptions.sortOrder,
343
- limit: cellOptions.limit || 1000,
344
- },
345
- });
346
- };
347
-
348
358
  onMount(() => {
349
359
  if (autofocus)
350
360
  setTimeout(() => {
@@ -361,142 +371,142 @@
361
371
  bind:this={anchor}
362
372
  class="superCell multirow"
363
373
  tabindex={cellOptions?.disabled ? -1 : 0}
364
- class:inEdit={inEdit && controlType != "buttons"}
374
+ class:inEdit={inEdit && !isButtons}
365
375
  class:isDirty={isDirty && cellOptions.showDirty}
366
376
  class:disabled
367
377
  class:readonly
368
378
  class:error
369
379
  style:color
370
380
  style:background
371
- style:font-weight={cellOptions.fontWeight}
372
381
  class:inline={role == "inlineInput"}
373
382
  class:tableCell={role == "tableCell"}
374
383
  class:formInput={role == "formInput"}
375
- class:naked-field={controlType == "buttons"}
384
+ class:naked-field={isButtons || role == "inlineInput"}
376
385
  on:focusin={cellState.focus}
377
386
  on:focusout={cellState.focusout}
378
387
  on:keydown={editorState.handleKeyboard}
379
388
  >
380
- {#if $cellState == "Loading"}
381
- <CellSkeleton>Initializing ..</CellSkeleton>
382
- {:else if controlType == "list"}
383
- <SuperList
384
- items={localValue}
385
- itemsColors={$colors}
386
- itemsLabels={labels}
387
- showColors={cellOptions.optionsViewMode != "text"}
388
- reorderOnly={cellOptions.reorderOnly}
389
- placeholder={cellOptions.placeholder}
390
- readonly={cellOptions.readonly || cellOptions.disabled}
391
- {editorState}
392
- {cellState}
393
- {fullSelection}
394
- bind:inactive
395
- on:togglePicker={editorState.toggle}
396
- on:clear={() => {
397
- localValue = [];
398
- editorState.close();
399
- anchor.focus();
400
- }}
401
- on:change={(e) => {
402
- localValue = [...e.detail];
403
- anchor.focus();
404
- }}
405
- />
406
- {:else if controlType == "radio" || controlType == "buttons"}
407
- {#if isButtons}
408
- <div class="buttons">
409
- {#each $options as option, idx (idx)}
410
- <div
411
- class="button"
412
- class:selected={localValue?.includes(option)}
413
- style:--option-color={$colors[option]}
414
- on:click={() => editorState.toggleOption(idx)}
415
- >
416
- {labels[option] || option}
417
- </div>
418
- {/each}
419
- </div>
420
- {:else if radios}
421
- <div
422
- class="radios"
423
- class:column={cellOptions.direction == "column"}
424
- on:mouseleave={() => (focusedOptionIdx = -1)}
425
- >
426
- {#each $options as option, idx (idx)}
427
- <div
428
- class="radio"
429
- class:selected={localValue?.includes(option)}
430
- class:focused={focusedOptionIdx === idx}
431
- style:--option-color={$colors[option]}
432
- on:mousedown={(e) => editorState.toggleOption(idx)}
433
- on:mouseenter={() => (focusedOptionIdx = idx)}
434
- >
435
- <i
436
- style:color={$colors[option]}
437
- class={radios && localValue.includes(option)
438
- ? "ph-fill ph-radio-button"
439
- : radios
440
- ? "ph ph-circle"
441
- : localValue.includes(option)
442
- ? "ph-fill ph-check-square"
443
- : "ph ph-square"}
444
- ></i>
445
- {labels[option] || option}
446
- </div>
447
- {/each}
448
- </div>
449
- {/if}
450
- {:else if controlType == "switch"}
451
- <div
452
- class="radios"
453
- class:formInput={role == "formInput"}
454
- class:inlineInput={role == "inlineInput"}
455
- class:column={cellOptions.direction == "column"}
456
- on:mouseleave={() => (focusedOptionIdx = -1)}
457
- >
458
- {#if label || cellOptions.toggleAll}
389
+ {#if loading}
390
+ <CellSkeleton />
391
+ {:else}
392
+ {#key controlType}
393
+ {#if controlType == "list"}
394
+ <SuperList
395
+ items={localValue}
396
+ itemsColors={$colors}
397
+ itemsLabels={labels}
398
+ showColors={cellOptions.optionsViewMode != "text"}
399
+ reorderOnly={cellOptions.reorderOnly}
400
+ placeholder={cellOptions.placeholder}
401
+ readonly={cellOptions.readonly || cellOptions.disabled}
402
+ {editorState}
403
+ {cellState}
404
+ {fullSelection}
405
+ bind:inactive
406
+ on:togglePicker={editorState.toggle}
407
+ on:clear={() => {
408
+ localValue = [];
409
+ editorState.close();
410
+ anchor.focus();
411
+ }}
412
+ on:change={(e) => {
413
+ localValue = [...e.detail];
414
+ anchor.focus();
415
+ }}
416
+ />
417
+ {:else if radios == true}
459
418
  <div
460
- class="switch toggleAll"
461
- on:click={cellOptions.toggleAll ? editorState.toggleAll : undefined}
462
- on:mouseenter
419
+ class="radios"
420
+ class:inline={role == "inlineInput"}
421
+ class:column={direction == "column"}
422
+ on:mouseleave={() => (focusedOptionIdx = -1)}
463
423
  >
464
- <div class="text title">{label ?? "Toggle All"}</div>
465
- {#if cellOptions.toggleAll && !(readonly || disabled)}
466
- <div class="spectrum-Switch spectrum-Switch--emphasized">
467
- <input
468
- checked={allSelected}
469
- type="checkbox"
470
- class="spectrum-Switch-input"
471
- />
472
- <span class="spectrum-Switch-switch"></span>
424
+ {#each $options as option, idx}
425
+ <div
426
+ class="radio"
427
+ class:selected={localValue?.includes(option)}
428
+ class:focused={focusedOptionIdx === idx}
429
+ on:mousedown={(e) => editorState.toggleOption(idx)}
430
+ on:mouseenter={() => (focusedOptionIdx = idx)}
431
+ >
432
+ <i
433
+ style:color={$colors[option]}
434
+ class={radios && localValue.includes(option)
435
+ ? "ph-fill ph-radio-button"
436
+ : radios
437
+ ? "ph ph-circle"
438
+ : localValue.includes(option)
439
+ ? "ph-fill ph-check-square"
440
+ : "ph ph-square"}
441
+ ></i>
442
+ {labels[option] || option}
473
443
  </div>
474
- {/if}
444
+ {/each}
475
445
  </div>
476
- {/if}
477
- {#each $options as option, idx (idx)}
446
+ {:else if isButtons == true}
447
+ <div class="buttons">
448
+ {#each $options as option, idx}
449
+ <div
450
+ class="button"
451
+ class:selected={localValue?.includes(option)}
452
+ style:--option-color={$colors[option]}
453
+ on:click={() => editorState.toggleOption(idx)}
454
+ >
455
+ {labels[option] || option}
456
+ </div>
457
+ {/each}
458
+ </div>
459
+ {:else if controlType == "switch"}
478
460
  <div
479
- class="switch"
480
- class:selected={localValue.includes(option)}
481
- class:focused={focusedOptionIdx === idx}
482
- style:--option-color={$colors[option]}
483
- on:click={(e) => editorState.toggleOption(idx)}
484
- on:mouseenter={() => (focusedOptionIdx = idx)}
461
+ class="switches"
462
+ class:inline={role == "inlineInput"}
463
+ class:column={cellOptions.direction == "column"}
464
+ on:mouseleave={() => (focusedOptionIdx = -1)}
485
465
  >
486
- <i class={optionIcons[option] || "no-icon"}></i>
487
- <div class="text">{labels[option] || option}</div>
488
- <div class="spectrum-Switch spectrum-Switch--emphasized">
489
- <input
490
- checked={localValue.includes(option)}
491
- type="checkbox"
492
- class="spectrum-Switch-input"
493
- id={idx}
494
- />
495
- <span class="spectrum-Switch-switch small"> </span>
496
- </div>
466
+ {#if cellOptions.toggleAll}
467
+ <div
468
+ class="switch toggle-all"
469
+ on:click={editorState.toggleAll}
470
+ on:mouseenter={() => (focusedOptionIdx = -1)}
471
+ >
472
+ <div class="text">All</div>
473
+ {#if !(readonly || disabled)}
474
+ <div class="spectrum-Switch spectrum-Switch--emphasized">
475
+ <input
476
+ checked={allSelected}
477
+ type="checkbox"
478
+ class="spectrum-Switch-input"
479
+ />
480
+ <span class="spectrum-Switch-switch"></span>
481
+ </div>
482
+ {/if}
483
+ </div>
484
+ {/if}
485
+ {#each $options as option, idx (idx)}
486
+ <div
487
+ class="switch"
488
+ class:selected={localValue.includes(option)}
489
+ class:focused={focusedOptionIdx === idx}
490
+ style:--option-color={$colors[option]}
491
+ on:click={(e) => editorState.toggleOption(idx)}
492
+ on:mouseenter={() => (focusedOptionIdx = idx)}
493
+ >
494
+ <i class={optionIcons[option] || "no-icon"}></i>
495
+ <div class="text">{labels[option] || option}</div>
496
+ <div class="spectrum-Switch spectrum-Switch--emphasized">
497
+ <input
498
+ checked={localValue.includes(option)}
499
+ type="checkbox"
500
+ class="spectrum-Switch-input"
501
+ id={idx}
502
+ />
503
+ <span class="spectrum-Switch-switch small"> </span>
504
+ </div>
505
+ </div>
506
+ {/each}
497
507
  </div>
498
- {/each}
499
- </div>
508
+ {/if}
509
+ {/key}
500
510
  {/if}
501
511
  </div>
502
512
 
@@ -645,26 +655,15 @@
645
655
  display: flex;
646
656
  flex-wrap: wrap;
647
657
  justify-items: flex-start;
648
- color: var(--spectrum-global-color-gray-700);
649
- font-size: 13px;
650
-
651
- &.formInput {
652
- & > .switch {
653
- padding: 0 0.5rem;
654
- }
655
- }
658
+ padding: 0.25rem 0.25rem;
659
+ }
656
660
 
657
- &.inlineInput {
658
- & > .switch {
659
- padding: 0rem 0.25rem;
660
- &.toggleAll {
661
- margin-bottom: 0.25rem;
662
- padding-bottom: 0.25rem;
663
- border-bottom: 1px solid var(--spectrum-global-color-gray-300);
664
- }
665
- }
666
- }
661
+ .radios.inline {
662
+ border: 1px solid var(--spectrum-global-color-gray-300);
663
+ border-radius: 4px;
664
+ padding: 0.25rem 0.25rem;
667
665
  }
666
+
668
667
  .radios.column {
669
668
  gap: 0rem;
670
669
  flex-direction: column;
@@ -677,25 +676,52 @@
677
676
  align-items: center;
678
677
  cursor: pointer;
679
678
  padding: 0 0.5rem;
679
+ opacity: 0.75;
680
+ border-radius: 0.25rem;
680
681
  &.focused {
681
682
  background-color: var(--spectrum-global-color-gray-200) !important;
682
683
  color: var(--spectrum-global-color-gray-800);
683
684
  }
684
685
 
686
+ &:hover > i {
687
+ color: var(--option-color, var(--spectrum-global-color-gray-700));
688
+ opacity: 1;
689
+ }
690
+
685
691
  &.selected {
686
692
  color: var(--spectrum-global-color-gray-800);
687
- background-color: var(--spectrum-global-color-gray-100);
688
- font-weight: 600;
693
+ opacity: 1;
689
694
  }
690
695
  }
691
696
 
697
+ .switches {
698
+ flex: 1;
699
+ display: flex;
700
+ flex-direction: column;
701
+ padding: 0.25rem 0.25rem;
702
+ overflow-y: auto;
703
+ }
704
+
705
+ .switches.inline {
706
+ border: 1px solid var(--spectrum-global-color-gray-300);
707
+ border-radius: 4px;
708
+ padding: 0.25rem 0.25rem;
709
+ }
710
+
711
+ .switches.inline > .switch {
712
+ padding: 0.25rem 0.5rem;
713
+ }
714
+
692
715
  .switch {
693
716
  width: 100%;
694
717
  display: flex;
695
718
  gap: 0.35rem;
696
719
  align-items: center;
720
+ justify-content: space-between;
697
721
  cursor: pointer;
698
722
  height: 1.75rem;
723
+ padding: 0.25rem 0.5rem;
724
+ color: var(--spectrum-global-color-gray-700);
699
725
 
700
726
  & > i {
701
727
  color: var(--spectrum-global-color-gray-600);
@@ -711,17 +737,6 @@
711
737
  text-overflow: ellipsis;
712
738
  overflow: hidden;
713
739
  white-space: nowrap;
714
- opacity: 0.95;
715
-
716
- &.title {
717
- font-size: 14px;
718
- font-weight: 600;
719
- }
720
- }
721
-
722
- &.toggleAll {
723
- border-bottom: 1px solid var(--spectrum-global-color-gray-200);
724
- height: 2rem;
725
740
  }
726
741
  &.focused {
727
742
  background-color: var(--spectrum-global-color-gray-200) !important;
@@ -741,8 +756,16 @@
741
756
  }
742
757
  }
743
758
 
759
+ .switch.toggle-all .text {
760
+ font-weight: 600;
761
+ color: var(--spectrum-global-color-gray-600);
762
+ }
763
+
744
764
  .switch > .spectrum-Switch {
745
765
  margin-right: unset !important;
766
+ --spectrum-switch-m-handle-border-color: var(
767
+ --spectrum-global-color-gray-500
768
+ ) !important;
746
769
  }
747
770
 
748
771
  .radio > i {
@@ -3,7 +3,7 @@
3
3
  export let minHeight = "2rem";
4
4
  </script>
5
5
 
6
- {#if active}
6
+ {#if active == true}
7
7
  <div class="skeleton" style:min-height={minHeight}>
8
8
  <div class="children"></div>
9
9
  </div>
@@ -25,7 +25,7 @@
25
25
 
26
26
  // Local state
27
27
  let timer;
28
- let editor;
28
+
29
29
  let lastEdit;
30
30
  let localValue = value;
31
31
  let state =
@@ -90,25 +90,20 @@
90
90
  },
91
91
  Editing: {
92
92
  _enter() {
93
- setTimeout(() => {
94
- editor?.focus();
95
- }, 50);
96
93
  dispatch("enteredit");
97
94
  },
98
95
  _exit() {
99
96
  lastEdit = undefined;
100
97
  dispatch("exitedit");
101
98
  },
102
- focus() {
103
- editor?.focus();
104
- },
99
+ focus() {},
105
100
  clear() {
106
101
  if (debounceDelay) {
107
102
  dispatch("change", null);
108
103
  }
109
104
  lastEdit = new Date();
110
105
  localValue = null;
111
- editor?.focus();
106
+
112
107
  dispatch("clear", null);
113
108
  },
114
109
  focusout(e) {
@@ -212,24 +207,17 @@
212
207
  {#if inEdit}
213
208
  {#if textarea}
214
209
  <textarea
215
- bind:this={editor}
216
210
  tabindex="0"
217
- class="editor textarea"
218
211
  class:placeholder={!value && !formattedValue && !localValue}
219
212
  placeholder={cellOptions?.placeholder ?? ""}
220
- style:text-align={cellOptions.align == "center"
221
- ? "center"
222
- : cellOptions.align == "flex-end"
223
- ? "right"
224
- : "left"}
213
+ value={localValue ?? ""}
225
214
  on:input={cellState.debounce}
226
215
  on:focusout={cellState.focusout}
227
216
  on:keydown={cellState.handleKeyboard}
228
- use:focus>{localValue ?? ""}</textarea
229
- >
217
+ use:focus
218
+ ></textarea>
230
219
  {:else}
231
220
  <input
232
- bind:this={editor}
233
221
  tabindex="0"
234
222
  class="editor"
235
223
  class:placeholder={!value && !formattedValue && !localValue}
@@ -251,39 +239,31 @@
251
239
  on:mousedown|self|preventDefault={cellState.clear}
252
240
  ></i>
253
241
  {/if}
254
- {:else if textarea}
255
- <div
256
- class="value textarea"
257
- class:placeholder={!value && !formattedValue}
258
- style:justify-content={cellOptions.align}
259
- >
260
- {formattedValue || value || placeholder}
261
- </div>
262
242
  {:else}
263
243
  <div
264
244
  class="value"
265
- class:placeholder={!value}
245
+ class:textarea
246
+ class:placeholder={!value && !formattedValue}
266
247
  style:justify-content={cellOptions.align}
267
248
  >
268
- <span>
269
- {formattedValue || value || placeholder}
270
- </span>
249
+ <span>{formattedValue || value || placeholder}</span>
271
250
  </div>
272
251
  {/if}
273
252
  </div>
274
253
 
275
254
  <style>
276
- .value.textarea {
277
- flex: 1 0 auto;
278
- display: flex;
279
- align-items: flex-start;
280
- white-space: pre-wrap;
281
- padding: 0.5rem 0rem;
255
+ textarea {
256
+ all: inherit;
257
+ flex: 1;
258
+ min-height: 100%;
259
+ width: 100%;
260
+ padding: 0.5rem 0.75rem;
261
+ background-color: transparent;
282
262
  overflow-y: auto;
283
263
  }
284
264
 
285
- .textarea.placeholder {
286
- color: var(--spectrum-global-color-gray-500);
287
- font-style: italic;
265
+ textarea:focus {
266
+ outline: none;
267
+ border: none;
288
268
  }
289
269
  </style>