@poirazis/supercomponents-shared 1.2.18 → 1.2.19

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.
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@poirazis/supercomponents-shared",
3
- "version": "1.2.18",
3
+ "version": "1.2.19",
4
4
  "description": "Shared Svelte components library",
5
5
  "type": "module",
6
- "packageManager": "bun@1.0.0",
6
+ "packageManager": "bun@1.3.11",
7
7
  "main": "dist/index.js",
8
8
  "module": "dist/index.js",
9
9
  "svelte": "src/index.js",
@@ -21,7 +21,7 @@
21
21
  "license": "MIT",
22
22
  "repository": {
23
23
  "type": "git",
24
- "url": "https://github.com/poirazis/supercomponents-shared.git"
24
+ "url": "https://github.com/poirazis/supercomponents_shared.git"
25
25
  },
26
26
  "keywords": [
27
27
  "svelte",
package/src/index.js CHANGED
@@ -16,6 +16,7 @@ export { default as SuperPopover } from "./lib/SuperPopover/SuperPopover.svelte"
16
16
  // UI Elements
17
17
  export { default as Tooltip } from "./lib/UI/elements/Tooltip.svelte";
18
18
  export { default as IconButton } from "./lib/UI/elements/IconButton.svelte";
19
+ export { default as Textbox } from "./lib/UI/elements/Textbox.svelte";
19
20
 
20
21
  // Table components
21
22
  export { default as SuperTable } from "./lib/SuperTable/SuperTable.svelte";
package/src/index.ts CHANGED
@@ -15,6 +15,7 @@ export { default as SuperPopover } from "./lib/SuperPopover/SuperPopover.svelte"
15
15
 
16
16
  // UI Elements
17
17
  export { default as Tooltip } from "./lib/UI/elements/Tooltip.svelte";
18
+ export { default as Textbox } from "./lib/UI/elements/Textbox.svelte";
18
19
 
19
20
  // Table components
20
21
  export { default as SuperTable } from "./lib/SuperTable/SuperTable.svelte";
@@ -20,12 +20,12 @@
20
20
  export let buttonClass = "actionButton";
21
21
  export let type = "primary";
22
22
  export let tooltip;
23
-
23
+ export let confirm = false;
24
+ export let condition = undefined;
24
25
  // Visibility Conditions
25
26
  export const conditions = []; // Array of condition objects {field, operator, value}
26
27
 
27
28
  export let actionsMode = "normal";
28
- export let condition = undefined; // For backward compatibility
29
29
  export let loopSource = undefined;
30
30
  export let loopDelay = 0;
31
31
  export let loopEvent = undefined;
@@ -40,22 +40,31 @@
40
40
  export let onLoopEnd = undefined;
41
41
  export let onTrueCondition = undefined;
42
42
  export let onFalseCondition = undefined;
43
+ export let workingState;
44
+
45
+ $: enrichedAction = Array.isArray(onClick)
46
+ ? enrichButtonActions(onClick, $context)
47
+ : onClick;
43
48
 
44
49
  $: loop = safeParse(loopSource);
45
- $: buttonText = text || (actionsMode == "timer" ? timerDuration : "");
46
- $: icon_class = working
47
- ? "ph ph-spinner-gap ph-spin"
48
- : icon && !icon.startsWith("ri-")
49
- ? "ph ph-" + icon
50
- : icon
51
- ? icon
52
- : actionsMode == "timer"
53
- ? "ph ph-timer"
54
- : undefined;
50
+ $: buttonText = confirmMode
51
+ ? "Confirm"
52
+ : text || (actionsMode == "timer" ? timerDuration : "");
53
+ $: icon_class =
54
+ working || workingState
55
+ ? "ph ph-spinner-gap ph-spin"
56
+ : icon && !icon.startsWith("ri-")
57
+ ? "ph ph-" + icon
58
+ : icon
59
+ ? icon
60
+ : actionsMode == "timer"
61
+ ? "ph ph-timer"
62
+ : undefined;
55
63
 
56
64
  let working = false;
57
65
  let ui_timer = undefined;
58
66
  let elapsed = 0;
67
+ let confirmMode = false;
59
68
 
60
69
  let buttonElement;
61
70
  let tooltipShow = false;
@@ -74,17 +83,20 @@
74
83
  clearTimeout(tooltipTimer);
75
84
  }
76
85
  tooltipShow = false;
86
+ confirmMode = false;
77
87
  };
78
88
 
79
89
  async function handleClick(e) {
80
- let enrichedAction;
81
- if (Array.isArray(onClick)) {
82
- enrichedAction = enrichButtonActions(onClick, $context);
83
- } else {
84
- enrichedAction = onClick;
90
+ if (disabled || actionsMode == "timer") return;
91
+
92
+ // Handle confirmation flow
93
+ if (confirm && !confirmMode) {
94
+ confirmMode = true;
95
+ return;
85
96
  }
86
97
 
87
- if (disabled || working || actionsMode == "timer") return;
98
+ // Reset confirm mode after executing action
99
+ confirmMode = false;
88
100
  working = true;
89
101
  tooltipShow = false;
90
102
  if (actionsMode == "loop") {
@@ -145,9 +157,14 @@
145
157
 
146
158
  <button
147
159
  bind:this={buttonElement}
148
- on:click={handleClick}
160
+ on:click={(e) => {
161
+ handleClick(e);
162
+ }}
149
163
  on:mouseenter={showTooltip}
150
164
  on:mouseleave={hideTooltip}
165
+ on:blur={() => {
166
+ confirmMode = false;
167
+ }}
151
168
  tabindex={disabled ? -1 : 0}
152
169
  class:super-button={true}
153
170
  class:xsmall={size == "XS"}
@@ -171,10 +188,10 @@
171
188
  ? "spectrum-ActionButton spectrum-ActionButton--size" + size
172
189
  : "spectrum-Button spectrum-Button--size" + size}
173
190
  class:disabled
174
- class:working
191
+ class:working={working || workingState}
175
192
  >
176
193
  <i
177
- class={icon_class}
194
+ class={confirmMode ? "ph ph-check" : icon_class}
178
195
  class:ph-fill={filledIcon}
179
196
  style:order={iconAfterText ? 1 : 0}
180
197
  style:color={disabled ? "var(--spectrum-global-color-gray-400)" : iconColor}
@@ -199,6 +216,7 @@
199
216
  min-width: 4rem;
200
217
  gap: 0.75rem;
201
218
  height: 2rem;
219
+ transition: all 150ms ease-in-out;
202
220
 
203
221
  &.spectrum-ActionButton {
204
222
  padding: 0rem 0.75rem !important;
@@ -258,8 +276,6 @@
258
276
  & > i {
259
277
  display: none;
260
278
  opacity: 0.9;
261
- font-weight: 400;
262
- font-size: 15px;
263
279
  }
264
280
  }
265
281
 
@@ -286,7 +302,6 @@
286
302
  i {
287
303
  color: var(--iconColor);
288
304
  transition: all 230ms ease-in-out;
289
- font-size: 16px;
290
305
  }
291
306
 
292
307
  .cta {
@@ -333,13 +348,9 @@
333
348
  }
334
349
  }
335
350
  &:hover,
336
- &:focus {
351
+ &:focus:not(.working) {
337
352
  background-color: var(--spectrum-global-color-gray-900);
338
353
  }
339
-
340
- &:active {
341
- border: 1px solid var(--spectrum-global-color-blue-700);
342
- }
343
354
  }
344
355
  .primary {
345
356
  &:hover,
@@ -446,14 +457,15 @@
446
457
 
447
458
  .working {
448
459
  cursor: progress;
449
- border: 1px solid var(--spectrum-global-color-gray-300) !important;
460
+ border: 1px solid var(--spectrum-global-color-gray-400) !important;
461
+ background-color: var(--spectrum-global-color-gray-300) !important;
450
462
  & > span {
451
- color: var(--spectrum-global-color-gray-600);
463
+ color: var(--spectrum-global-color-gray-600) !important;
452
464
  }
453
465
  & > i {
454
466
  display: block;
455
467
  animation: spin 1s linear infinite;
456
- color: var(--spectrum-global-color-blue-700);
468
+ color: var(--spectrum-global-color-gray-700) !important;
457
469
  }
458
470
  }
459
471
  </style>
@@ -198,7 +198,8 @@
198
198
 
199
199
  & > .error-message {
200
200
  color: var(--spectrum-global-color-red-400);
201
- font-size: 11px;
201
+ font-size: 10px;
202
+ white-space: nowrap;
202
203
  }
203
204
  }
204
205
  </style>
@@ -29,7 +29,6 @@
29
29
  import ControlSection from "./controls/ControlSection.svelte";
30
30
  import ColumnsSection from "./controls/ColumnsSection.svelte";
31
31
  import PaginationLimitOffset from "./controls/PaginationLimitOffset.svelte";
32
- import { ta } from "zod/v4/locales";
33
32
 
34
33
  const {
35
34
  API,
@@ -1641,30 +1640,35 @@
1641
1640
  >
1642
1641
  <Provider {actions} data={dataContext} />
1643
1642
 
1644
- <ControlSection>
1645
- <SelectionColumn
1646
- hideSelectionColumn={hideSelectionColumn || $superColumns.length === 0}
1647
- />
1648
-
1649
- {#if showButtonColumnLeft && $superColumns.length > 0 && $stbData.loaded}
1650
- <RowButtonsColumn {rowMenuItems} {menuItemsVisible} {rowMenu} />
1651
- {/if}
1652
-
1653
- {#if stickFirstColumn && $superColumns.length > 1}
1654
- <SuperTableColumn
1655
- sticky={true}
1656
- scrollPos={$stbHorizontalScrollPos}
1657
- columnOptions={{
1658
- ...$superColumns[0],
1659
- ...$commonColumnOptions,
1660
- overflow,
1661
- isFirst: true,
1662
- isLast:
1663
- $superColumns?.length == 1 && !showButtonColumnRight && canScroll,
1664
- }}
1643
+ {#if !isEmpty}
1644
+ <ControlSection>
1645
+ <SelectionColumn
1646
+ hideSelectionColumn={hideSelectionColumn ||
1647
+ $superColumns.length === 0}
1665
1648
  />
1666
- {/if}
1667
- </ControlSection>
1649
+
1650
+ {#if showButtonColumnLeft && $superColumns.length > 0 && $stbData.loaded}
1651
+ <RowButtonsColumn {rowMenuItems} {menuItemsVisible} {rowMenu} />
1652
+ {/if}
1653
+
1654
+ {#if stickFirstColumn && $superColumns.length > 1}
1655
+ <SuperTableColumn
1656
+ sticky={true}
1657
+ scrollPos={$stbHorizontalScrollPos}
1658
+ columnOptions={{
1659
+ ...$superColumns[0],
1660
+ ...$commonColumnOptions,
1661
+ overflow,
1662
+ isFirst: true,
1663
+ isLast:
1664
+ $superColumns?.length == 1 &&
1665
+ !showButtonColumnRight &&
1666
+ canScroll,
1667
+ }}
1668
+ />
1669
+ {/if}
1670
+ </ControlSection>
1671
+ {/if}
1668
1672
 
1669
1673
  <ColumnsSection
1670
1674
  {stbSettings}
@@ -1678,7 +1682,7 @@
1678
1682
  {/key}
1679
1683
  </ColumnsSection>
1680
1684
 
1681
- {#if showButtonColumnRight && $superColumns.length > 0}
1685
+ {#if showButtonColumnRight && $superColumns.length > 0 && !isEmpty}
1682
1686
  <ControlSection>
1683
1687
  <RowButtonsColumn
1684
1688
  {rowMenuItems}
@@ -77,7 +77,7 @@
77
77
  class:hovered={$stbHovered == visibleRow || $stbMenuID == visibleRow}
78
78
  class:is-editing={$stbEditing == visibleRow}
79
79
  class:disabled={$rowMetadata[visibleRow].disabled}
80
- style:min-height={$rowMetadata[visibleRow].height + "px"}
80
+ style:height={$rowMetadata[visibleRow].height + "px"}
81
81
  style:padding-right={canScroll && right ? "1.5rem" : "0.5rem"}
82
82
  >
83
83
  <!-- svelte-ignore a11y-click-events-have-key-events -->
@@ -128,11 +128,12 @@
128
128
  <style>
129
129
  .selection {
130
130
  flex: auto;
131
- padding-left: 0.75rem;
132
- padding-right: 0.75rem;
133
- gap: 1rem;
131
+ padding-left: 0.5rem;
132
+ padding-right: 0.5rem;
133
+ gap: 0.5rem;
134
134
  font-size: 13px;
135
135
  font-weight: 500;
136
136
  align-items: center;
137
+ justify-content: center;
137
138
  }
138
139
  </style>
@@ -362,11 +362,6 @@
362
362
  .superCell.formInput.has-popup {
363
363
  background-color: var(--spectrum-global-color-gray-50);
364
364
 
365
- &:hover:not(.disabled):not(.readonly):not(.inEdit) {
366
- border-color: var(--spectrum-global-color-gray-100) !important;
367
- cursor: pointer;
368
- }
369
-
370
365
  &.inEdit {
371
366
  border: 1px solid var(--spectrum-global-color-gray-200) !important;
372
367
  background-color: var(--spectrum-global-color-gray-200) !important;
@@ -807,7 +807,7 @@
807
807
  {/if}
808
808
  {/if}
809
809
 
810
- {#if filteredOptions?.length === 0 && $fetch.loaded}
810
+ {#if filteredOptions?.length === 0}
811
811
  <div class="option">
812
812
  <span>
813
813
  <i class="ri-close-line"></i>
@@ -313,7 +313,9 @@
313
313
  bind:api={pickerApi}
314
314
  on:change={handleChange}
315
315
  on:focusout={cellState.popupfocusout}
316
- />
316
+ >
317
+ <slot />
318
+ </CellSQLLinkPicker>
317
319
  {/if}
318
320
  </SuperPopover>
319
321
  {/if}
@@ -343,6 +343,7 @@
343
343
  on:mouseleave={() => (focusIdx = -1)}
344
344
  on:mousedown|preventDefault|stopPropagation={() => selectRow(row)}
345
345
  >
346
+ <slot />
346
347
  <span>{row.primaryDisplay || row[primaryDisplay]}</span>
347
348
  <i class="ri-check-line"></i>
348
349
  </div>
@@ -361,6 +362,7 @@
361
362
  on:mouseleave={() => (focusIdx = -1)}
362
363
  on:mousedown|preventDefault={() => selectRow(row)}
363
364
  >
365
+ <slot />
364
366
  <span>{row.primaryDisplay || row[primaryDisplay]}</span>
365
367
  <i class="ri-check-line"></i>
366
368
  </div>
@@ -21,8 +21,6 @@
21
21
  $columnState == "EditingCell" &&
22
22
  ($columnSettings.highlighters == "vertical" ||
23
23
  $columnSettings.highlighters == "both");
24
-
25
- $: console.log(isLast);
26
24
  </script>
27
25
 
28
26
  <div
@@ -27,7 +27,7 @@
27
27
  on:click={toggle}
28
28
  >
29
29
  {#if checked}
30
- <i class="ph ph-check"></i>
30
+ <i class="ph ph-bold ph-check"></i>
31
31
  {:else if partial}
32
32
  <i class="ph ph-minus"></i>
33
33
  {:else if locked}
@@ -0,0 +1,210 @@
1
+ <script lang="ts">
2
+ import { createEventDispatcher } from "svelte";
3
+
4
+ const dispatch = createEventDispatcher();
5
+
6
+ interface TextboxProps {
7
+ value?: string | number;
8
+ disabled?: boolean;
9
+ copyLabel?: string;
10
+ copiedLabel?: string;
11
+ title?: string;
12
+ wrap?: boolean;
13
+ monospace?: boolean;
14
+ oncopy?: (detail: { value: string }) => void;
15
+ }
16
+
17
+ let {
18
+ value = "",
19
+ disabled = false,
20
+ copyLabel = "Copy to clipboard",
21
+ copiedLabel = "Copied !",
22
+ title,
23
+ wrap = false,
24
+ monospace = true,
25
+ oncopy,
26
+ }: TextboxProps = $props();
27
+
28
+ let justCopied = $state(false);
29
+ let copyTimeout: ReturnType<typeof setTimeout> | undefined;
30
+
31
+ let stringValue = $derived(String(value ?? "").trim());
32
+
33
+ let displayValue = $derived(
34
+ justCopied && copiedLabel?.trim() ? copiedLabel : stringValue || "",
35
+ );
36
+
37
+ function copyToClipboard() {
38
+ if (disabled || !stringValue || justCopied) return;
39
+
40
+ navigator.clipboard
41
+ .writeText(stringValue)
42
+ .then(() => {
43
+ justCopied = true;
44
+ const detail = { value: stringValue };
45
+
46
+ dispatch("copy", detail);
47
+ oncopy?.(detail);
48
+
49
+ if (copyTimeout) clearTimeout(copyTimeout);
50
+ copyTimeout = setTimeout(() => {
51
+ justCopied = false;
52
+ copyTimeout = undefined;
53
+ }, 800);
54
+ })
55
+ .catch((err) => {
56
+ console.error("Failed to copy to clipboard:", err);
57
+ });
58
+ }
59
+
60
+ function handleKeydown(e: KeyboardEvent) {
61
+ if (disabled) return;
62
+ if ((e.key === "Enter" || e.key === " ") && !justCopied) {
63
+ e.preventDefault();
64
+ copyToClipboard();
65
+ }
66
+ }
67
+
68
+ $effect(() => {
69
+ // Cleanup any pending timeout when the component is destroyed
70
+ return () => {
71
+ if (copyTimeout) clearTimeout(copyTimeout);
72
+ };
73
+ });
74
+ </script>
75
+
76
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
77
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
78
+ <span
79
+ class="textbox value copyable"
80
+ class:copied={justCopied}
81
+ class:disabled
82
+ class:monospace
83
+ role="button"
84
+ tabindex={disabled ? -1 : 0}
85
+ title={justCopied ? "" : (title ?? (stringValue ? copyLabel : ""))}
86
+ onclick={copyToClipboard}
87
+ onkeydown={handleKeydown}
88
+ >
89
+ <span class="textbox-text" class:wrap>
90
+ {displayValue}
91
+ </span>
92
+
93
+ {#if !justCopied && stringValue}
94
+ <span class="copy-icon">
95
+ <svg
96
+ xmlns="http://www.w3.org/2000/svg"
97
+ width="13"
98
+ height="13"
99
+ viewBox="0 0 24 24"
100
+ fill="none"
101
+ stroke="currentColor"
102
+ stroke-width="2.25"
103
+ stroke-linecap="round"
104
+ stroke-linejoin="round"
105
+ >
106
+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
107
+ <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"
108
+ ></path>
109
+ </svg>
110
+ </span>
111
+ {/if}
112
+ </span>
113
+
114
+ <style>
115
+ .textbox.value {
116
+ color: var(--spectrum-global-color-gray-700);
117
+ border: 1px solid var(--spectrum-global-color-gray-300);
118
+ border-radius: 0.25rem;
119
+ padding: 0.5rem 0.75rem;
120
+ background: rgb(from var(--spectrum-global-color-gray-50) r g b / 0.5);
121
+ font-family: monospace;
122
+ font-size: 12px;
123
+ display: flex;
124
+ align-items: center;
125
+ gap: 0.5rem;
126
+ }
127
+
128
+ .textbox.value:not(.monospace) {
129
+ font-family: var(
130
+ --spectrum-global-font-family,
131
+ system-ui,
132
+ -apple-system,
133
+ sans-serif
134
+ );
135
+ font-size: 13px;
136
+ }
137
+
138
+ .textbox.value.copyable {
139
+ cursor: pointer;
140
+ transition:
141
+ background-color 0.1s ease,
142
+ border-color 0.1s ease;
143
+ min-width: 0;
144
+ justify-content: space-between;
145
+ }
146
+
147
+ .textbox.value.copyable:hover:not(.disabled) {
148
+ background-color: var(--spectrum-global-color-gray-100);
149
+ }
150
+
151
+ .textbox.value.copyable:focus:not(.disabled) {
152
+ outline: 1px solid var(--spectrum-global-color-gray-500);
153
+ background-color: var(--spectrum-global-color-gray-100);
154
+ outline-offset: 1px;
155
+ }
156
+
157
+ .textbox.value.copyable.copied {
158
+ border-color: var(--spectrum-global-color-gray-500);
159
+ background-color: var(--spectrum-global-color-gray-100);
160
+ color: var(--spectrum-global-color-gray-600);
161
+ font-weight: 600;
162
+ }
163
+
164
+ .textbox.value.copyable.disabled {
165
+ cursor: not-allowed;
166
+ opacity: 0.6;
167
+ pointer-events: none;
168
+ }
169
+
170
+ .textbox.value .copy-icon {
171
+ opacity: 0;
172
+ transition: opacity 0.15s ease;
173
+ flex-shrink: 0;
174
+ color: var(--spectrum-global-color-gray-600);
175
+ display: flex;
176
+ align-items: center;
177
+ }
178
+
179
+ .textbox.value.copyable:hover:not(.disabled) .copy-icon {
180
+ opacity: 0.65;
181
+ }
182
+
183
+ .textbox.value.copyable:focus:not(.disabled) .copy-icon {
184
+ opacity: 0.65;
185
+ }
186
+
187
+ .textbox.value.copyable:hover:not(.disabled) .copy-icon:hover {
188
+ opacity: 1;
189
+ }
190
+
191
+ .textbox-text {
192
+ flex: 1 1 auto;
193
+ min-width: 0;
194
+ overflow: hidden;
195
+ text-overflow: ellipsis;
196
+ white-space: nowrap;
197
+ }
198
+
199
+ /* Ensure the icon never shrinks and always has space when text is truncated */
200
+ .textbox.value .copy-icon {
201
+ flex-shrink: 0;
202
+ }
203
+
204
+ .textbox-text.wrap {
205
+ white-space: normal;
206
+ overflow: visible;
207
+ text-overflow: unset;
208
+ word-break: break-word;
209
+ }
210
+ </style>