@juspay/svelte-ui-components 2.80.10 → 2.81.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.
- package/README.md +1 -1
- package/dist/Table/BuiltinCell.svelte +608 -0
- package/dist/Table/BuiltinCell.svelte.d.ts +9 -0
- package/dist/Table/Table.svelte +466 -39
- package/dist/Table/cellData.d.ts +48 -0
- package/dist/Table/cellData.js +295 -0
- package/dist/Table/normalizeColumns.d.ts +24 -0
- package/dist/Table/normalizeColumns.js +23 -0
- package/dist/Table/properties.d.ts +284 -0
- package/dist/Table/sortEngine.d.ts +29 -0
- package/dist/Table/sortEngine.js +71 -0
- package/dist/assets/dots-vertical.svg +5 -0
- package/dist/assets/trend-down.svg +4 -0
- package/dist/assets/trend-up.svg +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -91,7 +91,7 @@ This library takes a different approach: **components are unstyled by default**
|
|
|
91
91
|
| **ListItem** | Multi-section list row with images, labels, and accordion expansion. | [docs](docs/ListItem.md) |
|
|
92
92
|
| **Pill** | Compact label/tag for status or categories, optionally clickable with a11y. | [docs](docs/Pill.md) |
|
|
93
93
|
| **Status** | Full-screen status display for success/failure screens. | [docs](docs/Status.md) |
|
|
94
|
-
| **Table** |
|
|
94
|
+
| **Table** | Data table with keyed columns, built-in cell renderers, sorting, pagination and selection. | [docs](docs/Table.md) |
|
|
95
95
|
| **RelativeTime** | Auto-updating relative time display ("5 minutes ago") with locale support and optional tooltip. | [docs](docs/RelativeTime.md) |
|
|
96
96
|
|
|
97
97
|
### Feedback & Loading
|
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onDestroy } from 'svelte';
|
|
3
|
+
import type { TableCellValue, TableColumn } from './properties';
|
|
4
|
+
import {
|
|
5
|
+
asActionGroupCellData,
|
|
6
|
+
asAvatarStackData,
|
|
7
|
+
asButtonCellData,
|
|
8
|
+
asCompareCellData,
|
|
9
|
+
asInputCellData,
|
|
10
|
+
asInputDataType,
|
|
11
|
+
asJsonObject,
|
|
12
|
+
asLinkCellData,
|
|
13
|
+
asPopupMenuCellData,
|
|
14
|
+
asSelectCellData,
|
|
15
|
+
asTagArrayItems,
|
|
16
|
+
asTagCellData,
|
|
17
|
+
buildAvatarStack,
|
|
18
|
+
cellValueToText
|
|
19
|
+
} from './cellData';
|
|
20
|
+
import Pill from '../Pill/Pill.svelte';
|
|
21
|
+
import Img from '../Img/Img.svelte';
|
|
22
|
+
import IconStack from '../IconStack/IconStack.svelte';
|
|
23
|
+
import Toggle from '../Toggle/Toggle.svelte';
|
|
24
|
+
import Button from '../Button/Button.svelte';
|
|
25
|
+
import Select from '../Select/Select.svelte';
|
|
26
|
+
import Input from '../Input/Input.svelte';
|
|
27
|
+
import Menu from '../Menu/Menu.svelte';
|
|
28
|
+
import trendUpSvg from '../assets/trend-up.svg?raw';
|
|
29
|
+
import trendDownSvg from '../assets/trend-down.svg?raw';
|
|
30
|
+
import copySvg from '../assets/copy.svg?raw';
|
|
31
|
+
import dotsSvg from '../assets/dots-vertical.svg?raw';
|
|
32
|
+
|
|
33
|
+
let {
|
|
34
|
+
column,
|
|
35
|
+
value,
|
|
36
|
+
rowIndex
|
|
37
|
+
}: {
|
|
38
|
+
column: TableColumn;
|
|
39
|
+
value: TableCellValue;
|
|
40
|
+
rowIndex: number;
|
|
41
|
+
} = $props();
|
|
42
|
+
|
|
43
|
+
let copied = $state(false);
|
|
44
|
+
let copyResetTimer: ReturnType<typeof setTimeout> | null = null;
|
|
45
|
+
|
|
46
|
+
onDestroy(() => {
|
|
47
|
+
if (copyResetTimer) {
|
|
48
|
+
clearTimeout(copyResetTimer);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Interactive cells stop click/keydown propagation so a clickable row's
|
|
53
|
+
// onRowClick never mis-fires from within a built-in control — the same
|
|
54
|
+
// pattern Table's own checkbox column uses. Scoped to built-in renderers
|
|
55
|
+
// only; bare cell-snippet consumers keep owning their own propagation.
|
|
56
|
+
const stopClickPropagation = (event: MouseEvent): void => {
|
|
57
|
+
event.stopPropagation();
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const stopKeydownPropagation = (event: KeyboardEvent): void => {
|
|
61
|
+
event.stopPropagation();
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const handleCopy = async (url: string): Promise<void> => {
|
|
65
|
+
// SSR / non-secure-context safety: the library renders server-side and as
|
|
66
|
+
// web components, where the clipboard API may be absent.
|
|
67
|
+
if (typeof navigator === 'undefined' || !navigator.clipboard) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
await navigator.clipboard.writeText(url);
|
|
72
|
+
copied = true;
|
|
73
|
+
if (copyResetTimer) {
|
|
74
|
+
clearTimeout(copyResetTimer);
|
|
75
|
+
}
|
|
76
|
+
copyResetTimer = setTimeout(() => {
|
|
77
|
+
copied = false;
|
|
78
|
+
}, 2000);
|
|
79
|
+
} catch {
|
|
80
|
+
copied = false;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
</script>
|
|
84
|
+
|
|
85
|
+
{#if column.type === 'tag'}
|
|
86
|
+
{@const tag = asTagCellData(value)}
|
|
87
|
+
{#if tag}
|
|
88
|
+
<Pill
|
|
89
|
+
text={tag.text}
|
|
90
|
+
classes={tag.classes ?? ''}
|
|
91
|
+
dismissible={tag.dismissible ?? false}
|
|
92
|
+
testId={tag.testId}
|
|
93
|
+
/>
|
|
94
|
+
{:else}
|
|
95
|
+
{cellValueToText(value)}
|
|
96
|
+
{/if}
|
|
97
|
+
{:else if column.type === 'text-tag'}
|
|
98
|
+
{@const data = asJsonObject(value) ?? {}}
|
|
99
|
+
{@const tag = asTagCellData(data.tag ?? null)}
|
|
100
|
+
<div class="builtin-text-tag">
|
|
101
|
+
<span class="builtin-primary-text"
|
|
102
|
+
>{typeof data.text === 'string' ? data.text : cellValueToText(value)}</span
|
|
103
|
+
>
|
|
104
|
+
{#if tag}
|
|
105
|
+
<Pill
|
|
106
|
+
text={tag.text}
|
|
107
|
+
classes={tag.classes ?? ''}
|
|
108
|
+
dismissible={tag.dismissible ?? false}
|
|
109
|
+
testId={tag.testId}
|
|
110
|
+
/>
|
|
111
|
+
{/if}
|
|
112
|
+
</div>
|
|
113
|
+
{:else if column.type === 'two-line-text'}
|
|
114
|
+
{@const data = asJsonObject(value) ?? {}}
|
|
115
|
+
<div class="builtin-two-line">
|
|
116
|
+
<span class="builtin-primary-text">{typeof data.text1 === 'string' ? data.text1 : '-'}</span>
|
|
117
|
+
<span class="builtin-secondary-text">{typeof data.text2 === 'string' ? data.text2 : '-'}</span>
|
|
118
|
+
</div>
|
|
119
|
+
{:else if column.type === 'icon-label'}
|
|
120
|
+
{@const data = asJsonObject(value) ?? {}}
|
|
121
|
+
{@const icons = Array.isArray(data.icons)
|
|
122
|
+
? data.icons.filter((iconSrc) => typeof iconSrc === 'string')
|
|
123
|
+
: []}
|
|
124
|
+
<div class="builtin-icon-label">
|
|
125
|
+
{#each icons as iconSrc, iconIndex (`${iconIndex}-${iconSrc}`)}
|
|
126
|
+
<span class="builtin-icon-label-icon">
|
|
127
|
+
<Img src={String(iconSrc)} alt="" fallback="" />
|
|
128
|
+
</span>
|
|
129
|
+
{/each}
|
|
130
|
+
<span>{typeof data.label === 'string' ? data.label : '-'}</span>
|
|
131
|
+
</div>
|
|
132
|
+
{:else if column.type === 'image-two-line-text'}
|
|
133
|
+
{@const data = asJsonObject(value) ?? {}}
|
|
134
|
+
<div class="builtin-image-two-line">
|
|
135
|
+
{#if typeof data.imageUrl === 'string' && data.imageUrl}
|
|
136
|
+
<span class="builtin-thumb">
|
|
137
|
+
<Img
|
|
138
|
+
src={data.imageUrl}
|
|
139
|
+
alt={typeof data.text1 === 'string' ? data.text1 : ''}
|
|
140
|
+
fallback=""
|
|
141
|
+
/>
|
|
142
|
+
</span>
|
|
143
|
+
{:else}
|
|
144
|
+
<span class="builtin-thumb builtin-thumb-placeholder"></span>
|
|
145
|
+
{/if}
|
|
146
|
+
<div class="builtin-two-line">
|
|
147
|
+
<span class="builtin-primary-text">{typeof data.text1 === 'string' ? data.text1 : '-'}</span>
|
|
148
|
+
<span class="builtin-secondary-text">{typeof data.text2 === 'string' ? data.text2 : '-'}</span
|
|
149
|
+
>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
{:else if column.type === 'tag-array'}
|
|
153
|
+
{@const tags = asTagArrayItems(value)}
|
|
154
|
+
{#if tags}
|
|
155
|
+
<div class="builtin-tag-array">
|
|
156
|
+
{#each tags as tag (tag.text)}
|
|
157
|
+
<Pill text={tag.text} classes={tag.classes ?? ''} />
|
|
158
|
+
{/each}
|
|
159
|
+
</div>
|
|
160
|
+
{:else}
|
|
161
|
+
{cellValueToText(value)}
|
|
162
|
+
{/if}
|
|
163
|
+
{:else if column.type === 'avatar-stack'}
|
|
164
|
+
{@const stackData = asAvatarStackData(value)}
|
|
165
|
+
{#if stackData}
|
|
166
|
+
{@const stack = buildAvatarStack(stackData)}
|
|
167
|
+
<div class="builtin-avatar-stack">
|
|
168
|
+
<IconStack icons={stack.icons} />
|
|
169
|
+
{#if stack.rest > 0}
|
|
170
|
+
<span class="builtin-avatar-rest">+{stack.rest}</span>
|
|
171
|
+
{/if}
|
|
172
|
+
</div>
|
|
173
|
+
{:else}
|
|
174
|
+
{cellValueToText(value)}
|
|
175
|
+
{/if}
|
|
176
|
+
{:else if column.type === 'compare'}
|
|
177
|
+
{#if typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' || value === null}
|
|
178
|
+
{cellValueToText(value)}
|
|
179
|
+
{:else}
|
|
180
|
+
{@const compare = asCompareCellData(value)}
|
|
181
|
+
{#if compare}
|
|
182
|
+
<div class="builtin-compare">
|
|
183
|
+
{#if typeof compare.primary === 'string'}
|
|
184
|
+
<span class="builtin-primary-text">{compare.primary}</span>
|
|
185
|
+
{/if}
|
|
186
|
+
{#if typeof compare.comparison === 'string'}
|
|
187
|
+
<span class="builtin-secondary-text">{compare.comparison}</span>
|
|
188
|
+
{/if}
|
|
189
|
+
{#if typeof compare.trendPercent === 'number'}
|
|
190
|
+
{#if compare.trendPercent > 0}
|
|
191
|
+
<span class="builtin-trend builtin-trend-up">
|
|
192
|
+
<!-- eslint-disable svelte/no-at-html-tags -->
|
|
193
|
+
<span class="builtin-trend-icon">{@html trendUpSvg}</span>
|
|
194
|
+
{compare.trendPercent}%
|
|
195
|
+
</span>
|
|
196
|
+
{:else if compare.trendPercent < 0}
|
|
197
|
+
<span class="builtin-trend builtin-trend-down">
|
|
198
|
+
<!-- eslint-disable svelte/no-at-html-tags -->
|
|
199
|
+
<span class="builtin-trend-icon">{@html trendDownSvg}</span>
|
|
200
|
+
{compare.trendPercent}%
|
|
201
|
+
</span>
|
|
202
|
+
{:else}
|
|
203
|
+
<span class="builtin-trend builtin-trend-flat">↔ 0%</span>
|
|
204
|
+
{/if}
|
|
205
|
+
{:else if typeof compare.trendLabel === 'string'}
|
|
206
|
+
<span class="builtin-trend builtin-trend-flat">{compare.trendLabel}</span>
|
|
207
|
+
{/if}
|
|
208
|
+
</div>
|
|
209
|
+
{:else}
|
|
210
|
+
{cellValueToText(value)}
|
|
211
|
+
{/if}
|
|
212
|
+
{/if}
|
|
213
|
+
{:else if column.type === 'toggle'}
|
|
214
|
+
{@const data = asJsonObject(value) ?? {}}
|
|
215
|
+
{@const isChecked = data.checked === true}
|
|
216
|
+
<span
|
|
217
|
+
class="builtin-toggle"
|
|
218
|
+
role="switch"
|
|
219
|
+
aria-checked={isChecked ? 'true' : 'false'}
|
|
220
|
+
aria-label={typeof data.ariaLabel === 'string' ? data.ariaLabel : null}
|
|
221
|
+
data-pw={typeof data.testId === 'string' ? data.testId : null}
|
|
222
|
+
onclick={stopClickPropagation}
|
|
223
|
+
onkeydown={stopKeydownPropagation}
|
|
224
|
+
tabindex={-1}
|
|
225
|
+
>
|
|
226
|
+
<Toggle
|
|
227
|
+
checked={isChecked}
|
|
228
|
+
text=""
|
|
229
|
+
onclick={(newChecked) => column.onToggle?.(rowIndex, newChecked)}
|
|
230
|
+
/>
|
|
231
|
+
</span>
|
|
232
|
+
{:else if column.type === 'select'}
|
|
233
|
+
{@const selectData = asSelectCellData(value)}
|
|
234
|
+
{#if selectData}
|
|
235
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
236
|
+
<span
|
|
237
|
+
class="builtin-interactive"
|
|
238
|
+
onclick={stopClickPropagation}
|
|
239
|
+
onkeydown={stopKeydownPropagation}
|
|
240
|
+
>
|
|
241
|
+
<Select
|
|
242
|
+
items={selectData.options}
|
|
243
|
+
value={selectData.selectedId ? [selectData.selectedId] : []}
|
|
244
|
+
placeholder={selectData.placeholder ?? ''}
|
|
245
|
+
disabled={selectData.disabled ?? false}
|
|
246
|
+
testId={selectData.testId}
|
|
247
|
+
itemTestId={selectData.itemTestId}
|
|
248
|
+
onchange={(selectedIds) => {
|
|
249
|
+
if (selectedIds.length > 0) {
|
|
250
|
+
column.onSelect?.(rowIndex, selectedIds[0]);
|
|
251
|
+
}
|
|
252
|
+
}}
|
|
253
|
+
/>
|
|
254
|
+
</span>
|
|
255
|
+
{:else}
|
|
256
|
+
{cellValueToText(value)}
|
|
257
|
+
{/if}
|
|
258
|
+
{:else if column.type === 'input'}
|
|
259
|
+
{@const inputData = asInputCellData(value)}
|
|
260
|
+
{#if inputData}
|
|
261
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
262
|
+
<span
|
|
263
|
+
class="builtin-interactive"
|
|
264
|
+
onclick={stopClickPropagation}
|
|
265
|
+
onkeydown={stopKeydownPropagation}
|
|
266
|
+
>
|
|
267
|
+
<Input
|
|
268
|
+
value={inputData.value ?? ''}
|
|
269
|
+
placeholder={inputData.placeholder ?? ''}
|
|
270
|
+
disable={inputData.disabled ?? false}
|
|
271
|
+
testId={inputData.testId ?? ''}
|
|
272
|
+
dataType={asInputDataType(inputData.dataType ?? null)}
|
|
273
|
+
validationPattern={inputData.validationPattern
|
|
274
|
+
? new RegExp(inputData.validationPattern)
|
|
275
|
+
: null}
|
|
276
|
+
onErrorMessage={inputData.onErrorMessage ?? null}
|
|
277
|
+
actionInput={false}
|
|
278
|
+
onInput={(newValue) => column.onInput?.(rowIndex, newValue)}
|
|
279
|
+
/>
|
|
280
|
+
</span>
|
|
281
|
+
{:else}
|
|
282
|
+
{cellValueToText(value)}
|
|
283
|
+
{/if}
|
|
284
|
+
{:else if column.type === 'button'}
|
|
285
|
+
{@const buttonData = asButtonCellData(value)}
|
|
286
|
+
{#if buttonData}
|
|
287
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
288
|
+
<span
|
|
289
|
+
class="builtin-interactive"
|
|
290
|
+
onclick={stopClickPropagation}
|
|
291
|
+
onkeydown={stopKeydownPropagation}
|
|
292
|
+
>
|
|
293
|
+
<Button
|
|
294
|
+
text={buttonData.text}
|
|
295
|
+
disabled={buttonData.disabled ?? false}
|
|
296
|
+
classes={buttonData.classes ?? ''}
|
|
297
|
+
testId={buttonData.testId}
|
|
298
|
+
onclick={() => column.onButtonClick?.(rowIndex)}
|
|
299
|
+
/>
|
|
300
|
+
</span>
|
|
301
|
+
{:else}
|
|
302
|
+
{cellValueToText(value)}
|
|
303
|
+
{/if}
|
|
304
|
+
{:else if column.type === 'action-group'}
|
|
305
|
+
{@const actionData = asActionGroupCellData(value)}
|
|
306
|
+
{#if actionData}
|
|
307
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
308
|
+
<span
|
|
309
|
+
class="builtin-interactive builtin-action-group"
|
|
310
|
+
onclick={stopClickPropagation}
|
|
311
|
+
onkeydown={stopKeydownPropagation}
|
|
312
|
+
>
|
|
313
|
+
{#if actionData.primaryButton}
|
|
314
|
+
{@const primary = actionData.primaryButton}
|
|
315
|
+
<Button
|
|
316
|
+
text={primary.text}
|
|
317
|
+
disabled={primary.disabled ?? false}
|
|
318
|
+
classes={primary.classes ?? ''}
|
|
319
|
+
testId={primary.testId}
|
|
320
|
+
onclick={() => column.onPrimaryAction?.(rowIndex)}
|
|
321
|
+
/>
|
|
322
|
+
{/if}
|
|
323
|
+
{#if actionData.menuItems && actionData.menuItems.length > 0}
|
|
324
|
+
<Menu
|
|
325
|
+
items={actionData.menuItems.map((item) => ({
|
|
326
|
+
value: item.id,
|
|
327
|
+
label: item.label ?? item.id,
|
|
328
|
+
danger: item.danger,
|
|
329
|
+
separator: item.separator
|
|
330
|
+
}))}
|
|
331
|
+
testId={column.testId && `${column.testId}-menu-${rowIndex}`}
|
|
332
|
+
onselect={(menuItem) => column.onMenuAction?.(rowIndex, menuItem.value)}
|
|
333
|
+
>
|
|
334
|
+
{#snippet trigger()}
|
|
335
|
+
<span class="builtin-icon-button">
|
|
336
|
+
<Button
|
|
337
|
+
ariaLabel="More actions"
|
|
338
|
+
testId={column.testId && `${column.testId}-menu-trigger-${rowIndex}`}
|
|
339
|
+
>
|
|
340
|
+
{#snippet icon()}
|
|
341
|
+
<!-- eslint-disable svelte/no-at-html-tags -->
|
|
342
|
+
<span class="builtin-menu-dots">{@html dotsSvg}</span>
|
|
343
|
+
{/snippet}
|
|
344
|
+
</Button>
|
|
345
|
+
</span>
|
|
346
|
+
{/snippet}
|
|
347
|
+
</Menu>
|
|
348
|
+
{/if}
|
|
349
|
+
</span>
|
|
350
|
+
{:else}
|
|
351
|
+
{cellValueToText(value)}
|
|
352
|
+
{/if}
|
|
353
|
+
{:else if column.type === 'popup-menu'}
|
|
354
|
+
{@const popupData = asPopupMenuCellData(value)}
|
|
355
|
+
{#if popupData}
|
|
356
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
357
|
+
<span
|
|
358
|
+
class="builtin-interactive"
|
|
359
|
+
onclick={stopClickPropagation}
|
|
360
|
+
onkeydown={stopKeydownPropagation}
|
|
361
|
+
>
|
|
362
|
+
<Menu
|
|
363
|
+
items={popupData.items.map((item) => ({
|
|
364
|
+
value: item.id,
|
|
365
|
+
label: item.label ?? item.id,
|
|
366
|
+
danger: item.danger,
|
|
367
|
+
separator: item.separator
|
|
368
|
+
}))}
|
|
369
|
+
testId={column.testId && `${column.testId}-popup-${rowIndex}`}
|
|
370
|
+
onselect={(menuItem) => column.onMenuAction?.(rowIndex, menuItem.value)}
|
|
371
|
+
>
|
|
372
|
+
{#snippet trigger()}
|
|
373
|
+
<span class="builtin-icon-button">
|
|
374
|
+
<Button
|
|
375
|
+
ariaLabel={popupData.ariaLabel ?? 'More actions'}
|
|
376
|
+
testId={column.testId && `${column.testId}-popup-trigger-${rowIndex}`}
|
|
377
|
+
>
|
|
378
|
+
{#snippet icon()}
|
|
379
|
+
<!-- eslint-disable svelte/no-at-html-tags -->
|
|
380
|
+
<span class="builtin-menu-dots">{@html dotsSvg}</span>
|
|
381
|
+
{/snippet}
|
|
382
|
+
</Button>
|
|
383
|
+
</span>
|
|
384
|
+
{/snippet}
|
|
385
|
+
</Menu>
|
|
386
|
+
</span>
|
|
387
|
+
{:else}
|
|
388
|
+
{cellValueToText(value)}
|
|
389
|
+
{/if}
|
|
390
|
+
{:else if column.type === 'link'}
|
|
391
|
+
{@const link = asLinkCellData(value)}
|
|
392
|
+
{#if link}
|
|
393
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
394
|
+
<div class="builtin-link" onclick={stopClickPropagation} onkeydown={stopKeydownPropagation}>
|
|
395
|
+
<a
|
|
396
|
+
href={link.url}
|
|
397
|
+
target="_blank"
|
|
398
|
+
rel="noopener noreferrer"
|
|
399
|
+
class="builtin-link-anchor"
|
|
400
|
+
data-pw={column.testId ? `${column.testId}-link-${rowIndex}` : null}
|
|
401
|
+
>
|
|
402
|
+
{link.label ?? link.url}
|
|
403
|
+
</a>
|
|
404
|
+
{#if link.copyable !== false}
|
|
405
|
+
<span class="builtin-link-copy">
|
|
406
|
+
<Button
|
|
407
|
+
ariaLabel={copied ? 'Link copied' : 'Copy link'}
|
|
408
|
+
testId={column.testId && `${column.testId}-copy-${rowIndex}`}
|
|
409
|
+
onclick={() => handleCopy(link.url)}
|
|
410
|
+
>
|
|
411
|
+
{#snippet icon()}
|
|
412
|
+
<!-- eslint-disable svelte/no-at-html-tags -->
|
|
413
|
+
<span class="builtin-copy-icon">{@html copySvg}</span>
|
|
414
|
+
{/snippet}
|
|
415
|
+
</Button>
|
|
416
|
+
</span>
|
|
417
|
+
{#if copied}
|
|
418
|
+
<span class="builtin-link-copied">Copied</span>
|
|
419
|
+
{/if}
|
|
420
|
+
{/if}
|
|
421
|
+
</div>
|
|
422
|
+
{:else}
|
|
423
|
+
{cellValueToText(value)}
|
|
424
|
+
{/if}
|
|
425
|
+
{:else}
|
|
426
|
+
{cellValueToText(value)}
|
|
427
|
+
{/if}
|
|
428
|
+
|
|
429
|
+
<style>
|
|
430
|
+
.builtin-primary-text {
|
|
431
|
+
display: block;
|
|
432
|
+
font-size: var(--table-cell-primary-font-size, var(--table-content-font-size, 14px));
|
|
433
|
+
color: var(--table-cell-primary-color, var(--table-content-color, #111827));
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.builtin-secondary-text {
|
|
437
|
+
display: block;
|
|
438
|
+
font-size: var(--table-cell-secondary-font-size, 12px);
|
|
439
|
+
color: var(--table-cell-secondary-color, #6b7280);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.builtin-two-line,
|
|
443
|
+
.builtin-compare {
|
|
444
|
+
display: flex;
|
|
445
|
+
flex-direction: column;
|
|
446
|
+
gap: var(--table-cell-line-gap, 2px);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.builtin-text-tag {
|
|
450
|
+
display: flex;
|
|
451
|
+
align-items: center;
|
|
452
|
+
gap: var(--table-cell-inline-gap, 8px);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.builtin-icon-label {
|
|
456
|
+
display: flex;
|
|
457
|
+
align-items: center;
|
|
458
|
+
gap: var(--table-cell-inline-gap, 8px);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.builtin-icon-label-icon :global(img),
|
|
462
|
+
.builtin-icon-label-icon :global(svg) {
|
|
463
|
+
width: var(--table-cell-icon-size, 16px);
|
|
464
|
+
height: var(--table-cell-icon-size, 16px);
|
|
465
|
+
display: block;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.builtin-image-two-line {
|
|
469
|
+
display: flex;
|
|
470
|
+
align-items: center;
|
|
471
|
+
gap: var(--table-cell-inline-gap, 8px);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.builtin-thumb {
|
|
475
|
+
display: inline-flex;
|
|
476
|
+
width: var(--table-cell-thumb-size, 32px);
|
|
477
|
+
height: var(--table-cell-thumb-size, 32px);
|
|
478
|
+
border-radius: var(--table-cell-thumb-radius, var(--radius, 4px));
|
|
479
|
+
overflow: hidden;
|
|
480
|
+
flex-shrink: 0;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
.builtin-thumb :global(img) {
|
|
484
|
+
width: 100%;
|
|
485
|
+
height: 100%;
|
|
486
|
+
object-fit: cover;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.builtin-thumb-placeholder {
|
|
490
|
+
background-color: var(--table-cell-thumb-placeholder-background, #f3f4f6);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
.builtin-tag-array {
|
|
494
|
+
display: flex;
|
|
495
|
+
flex-wrap: wrap;
|
|
496
|
+
gap: var(--table-tag-array-gap, 4px);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.builtin-avatar-stack {
|
|
500
|
+
display: flex;
|
|
501
|
+
align-items: center;
|
|
502
|
+
gap: var(--table-cell-inline-gap, 8px);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.builtin-avatar-rest {
|
|
506
|
+
font-size: var(--table-cell-secondary-font-size, 12px);
|
|
507
|
+
color: var(--table-cell-secondary-color, #6b7280);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
.builtin-trend {
|
|
511
|
+
display: inline-flex;
|
|
512
|
+
align-items: center;
|
|
513
|
+
gap: var(--table-trend-gap, 4px);
|
|
514
|
+
font-size: var(--table-cell-secondary-font-size, 12px);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.builtin-trend-up {
|
|
518
|
+
color: var(--table-trend-up-color, #16a34a);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
.builtin-trend-down {
|
|
522
|
+
color: var(--table-trend-down-color, #dc2626);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.builtin-trend-flat {
|
|
526
|
+
color: var(--table-trend-flat-color, #6b7280);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.builtin-trend-icon :global(svg) {
|
|
530
|
+
width: var(--table-trend-icon-size, 12px);
|
|
531
|
+
height: var(--table-trend-icon-size, 12px);
|
|
532
|
+
display: block;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
.builtin-toggle {
|
|
536
|
+
display: inline-flex;
|
|
537
|
+
align-items: center;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
.builtin-interactive {
|
|
541
|
+
display: inline-flex;
|
|
542
|
+
align-items: center;
|
|
543
|
+
min-width: 0;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
.builtin-action-group {
|
|
547
|
+
gap: var(--table-cell-inline-gap, 8px);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.builtin-icon-button {
|
|
551
|
+
--button-color: transparent;
|
|
552
|
+
--button-border: none;
|
|
553
|
+
--button-padding: 2px;
|
|
554
|
+
--button-margin: 0;
|
|
555
|
+
--button-width: fit-content;
|
|
556
|
+
--button-height: fit-content;
|
|
557
|
+
--button-text-color: var(--table-cell-icon-button-color, #6b7280);
|
|
558
|
+
--button-hover-color: var(--table-cell-icon-button-hover-background, rgba(0, 0, 0, 0.05));
|
|
559
|
+
display: inline-flex;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
.builtin-menu-dots :global(svg) {
|
|
563
|
+
width: var(--table-cell-icon-size, 16px);
|
|
564
|
+
height: var(--table-cell-icon-size, 16px);
|
|
565
|
+
display: block;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
.builtin-link {
|
|
569
|
+
display: flex;
|
|
570
|
+
align-items: center;
|
|
571
|
+
gap: var(--table-cell-inline-gap, 8px);
|
|
572
|
+
min-width: 0;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
.builtin-link-anchor {
|
|
576
|
+
color: var(--table-link-color, #2563eb);
|
|
577
|
+
text-decoration: var(--table-link-decoration, underline);
|
|
578
|
+
overflow: hidden;
|
|
579
|
+
text-overflow: ellipsis;
|
|
580
|
+
white-space: nowrap;
|
|
581
|
+
min-width: 0;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
.builtin-link-copy {
|
|
585
|
+
--button-color: transparent;
|
|
586
|
+
--button-border: none;
|
|
587
|
+
--button-padding: 2px;
|
|
588
|
+
--button-margin: 0;
|
|
589
|
+
--button-width: fit-content;
|
|
590
|
+
--button-height: fit-content;
|
|
591
|
+
--button-text-color: var(--table-link-copy-color, #6b7280);
|
|
592
|
+
--button-hover-color: var(--table-link-copy-hover-background, rgba(0, 0, 0, 0.05));
|
|
593
|
+
display: inline-flex;
|
|
594
|
+
flex-shrink: 0;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
.builtin-copy-icon :global(svg) {
|
|
598
|
+
width: var(--table-cell-icon-size, 16px);
|
|
599
|
+
height: var(--table-cell-icon-size, 16px);
|
|
600
|
+
display: block;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
.builtin-link-copied {
|
|
604
|
+
font-size: var(--table-cell-secondary-font-size, 12px);
|
|
605
|
+
color: var(--table-cell-secondary-color, #6b7280);
|
|
606
|
+
flex-shrink: 0;
|
|
607
|
+
}
|
|
608
|
+
</style>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { TableCellValue, TableColumn } from './properties';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
column: TableColumn;
|
|
4
|
+
value: TableCellValue;
|
|
5
|
+
rowIndex: number;
|
|
6
|
+
};
|
|
7
|
+
declare const BuiltinCell: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
8
|
+
type BuiltinCell = ReturnType<typeof BuiltinCell>;
|
|
9
|
+
export default BuiltinCell;
|