@finsweet/webflow-apps-utils 1.0.52 → 1.0.53
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/dist/ui/components/tags/TagsInput.stories.d.ts +11 -0
- package/dist/ui/components/tags/TagsInput.stories.js +55 -0
- package/dist/ui/components/tags/TagsInput.svelte +91 -3
- package/dist/ui/components/tags/types.d.ts +12 -0
- package/dist/ui/providers/GlobalProvider.svelte +0 -4
- package/package.json +1 -1
|
@@ -60,6 +60,14 @@ declare const meta: {
|
|
|
60
60
|
control: "boolean";
|
|
61
61
|
description: string;
|
|
62
62
|
};
|
|
63
|
+
showRemoveIcon: {
|
|
64
|
+
control: "boolean";
|
|
65
|
+
description: string;
|
|
66
|
+
};
|
|
67
|
+
expandOnClick: {
|
|
68
|
+
control: "boolean";
|
|
69
|
+
description: string;
|
|
70
|
+
};
|
|
63
71
|
width: {
|
|
64
72
|
control: "text";
|
|
65
73
|
description: string;
|
|
@@ -99,5 +107,8 @@ export declare const KeywordsTags: Story;
|
|
|
99
107
|
export declare const FormIntegration: Story;
|
|
100
108
|
export declare const ManyTags: Story;
|
|
101
109
|
export declare const LongTags: Story;
|
|
110
|
+
export declare const ShowRemoveIcon: Story;
|
|
111
|
+
export declare const ExpandOnClick: Story;
|
|
112
|
+
export declare const ShowRemoveIconAndExpandOnClick: Story;
|
|
102
113
|
export declare const SpecialCharacters: Story;
|
|
103
114
|
export declare const UnicodeSupport: Story;
|
|
@@ -60,6 +60,14 @@ const meta = {
|
|
|
60
60
|
control: 'boolean',
|
|
61
61
|
description: 'Whether to trim whitespace from tags'
|
|
62
62
|
},
|
|
63
|
+
showRemoveIcon: {
|
|
64
|
+
control: 'boolean',
|
|
65
|
+
description: 'Whether to always show the remove icon on tags'
|
|
66
|
+
},
|
|
67
|
+
expandOnClick: {
|
|
68
|
+
control: 'boolean',
|
|
69
|
+
description: 'Whether clicking a tag expands it to show full content'
|
|
70
|
+
},
|
|
63
71
|
width: {
|
|
64
72
|
control: 'text',
|
|
65
73
|
description: 'Custom width for the component'
|
|
@@ -407,6 +415,53 @@ export const LongTags = {
|
|
|
407
415
|
}
|
|
408
416
|
}
|
|
409
417
|
};
|
|
418
|
+
export const ShowRemoveIcon = {
|
|
419
|
+
args: {
|
|
420
|
+
value: ['JavaScript', 'TypeScript', 'Svelte'],
|
|
421
|
+
showRemoveIcon: true,
|
|
422
|
+
placeholder: 'Remove icon always visible...'
|
|
423
|
+
},
|
|
424
|
+
parameters: {
|
|
425
|
+
docs: {
|
|
426
|
+
description: {
|
|
427
|
+
story: 'When showRemoveIcon is true, the X button is always visible inline with 4px gap.'
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
export const ExpandOnClick = {
|
|
433
|
+
args: {
|
|
434
|
+
value: [
|
|
435
|
+
'This is a very long tag that will be truncated',
|
|
436
|
+
'Another long tag content here',
|
|
437
|
+
'Short'
|
|
438
|
+
],
|
|
439
|
+
expandOnClick: true,
|
|
440
|
+
placeholder: 'Click tags to expand...'
|
|
441
|
+
},
|
|
442
|
+
parameters: {
|
|
443
|
+
docs: {
|
|
444
|
+
description: {
|
|
445
|
+
story: 'When expandOnClick is true, clicking a tag expands it to show full content.'
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
export const ShowRemoveIconAndExpandOnClick = {
|
|
451
|
+
args: {
|
|
452
|
+
value: ['This is a very long tag name', 'TypeScript', 'Click me to expand'],
|
|
453
|
+
showRemoveIcon: true,
|
|
454
|
+
expandOnClick: true,
|
|
455
|
+
placeholder: 'Both features enabled...'
|
|
456
|
+
},
|
|
457
|
+
parameters: {
|
|
458
|
+
docs: {
|
|
459
|
+
description: {
|
|
460
|
+
story: 'Both showRemoveIcon and expandOnClick enabled together.'
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
};
|
|
410
465
|
export const SpecialCharacters = {
|
|
411
466
|
args: {
|
|
412
467
|
value: ['C++', 'C#', '.NET', '@angular', '#svelte'],
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { SvelteSet } from 'svelte/reactivity';
|
|
3
|
+
|
|
2
4
|
import { TimesIcon } from '../../icons';
|
|
3
5
|
|
|
4
6
|
import Loader from '../Loader.svelte';
|
|
@@ -21,6 +23,8 @@
|
|
|
21
23
|
allowDuplicates = false,
|
|
22
24
|
validateTag,
|
|
23
25
|
trimTags = true,
|
|
26
|
+
showRemoveIcon = false,
|
|
27
|
+
expandOnClick = false,
|
|
24
28
|
width = '100%',
|
|
25
29
|
height = 'auto',
|
|
26
30
|
class: className = '',
|
|
@@ -39,6 +43,7 @@
|
|
|
39
43
|
let inputElement: HTMLInputElement | undefined = $state();
|
|
40
44
|
let inputValue = $state('');
|
|
41
45
|
let isFocused = $state(false);
|
|
46
|
+
let expandedTags = new SvelteSet<number>();
|
|
42
47
|
|
|
43
48
|
// Derived states
|
|
44
49
|
let isDisabled = $derived(disabled || loading);
|
|
@@ -67,12 +72,40 @@
|
|
|
67
72
|
${isSuccessAlert ? 'success' : ''}
|
|
68
73
|
${isFocused ? 'focused' : ''}
|
|
69
74
|
${loading ? 'loading' : ''}
|
|
75
|
+
${showRemoveIcon ? 'show-remove-icon' : ''}
|
|
70
76
|
${className}
|
|
71
77
|
`
|
|
72
78
|
.trim()
|
|
73
79
|
.replace(/\s+/g, ' ')
|
|
74
80
|
);
|
|
75
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Collapses all expanded tags
|
|
84
|
+
*/
|
|
85
|
+
const collapseAllTags = () => {
|
|
86
|
+
expandedTags.clear();
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Toggles the expanded state of a tag
|
|
91
|
+
*/
|
|
92
|
+
const toggleTagExpand = (index: number) => {
|
|
93
|
+
if (!expandOnClick) return;
|
|
94
|
+
|
|
95
|
+
if (expandedTags.has(index)) {
|
|
96
|
+
expandedTags.delete(index);
|
|
97
|
+
} else {
|
|
98
|
+
expandedTags.add(index);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Checks if a tag is expanded
|
|
104
|
+
*/
|
|
105
|
+
const isTagExpanded = (index: number): boolean => {
|
|
106
|
+
return expandedTags.has(index);
|
|
107
|
+
};
|
|
108
|
+
|
|
76
109
|
/**
|
|
77
110
|
* Focus the input element
|
|
78
111
|
*/
|
|
@@ -232,9 +265,13 @@
|
|
|
232
265
|
};
|
|
233
266
|
|
|
234
267
|
/**
|
|
235
|
-
* Handles wrapper click to focus input
|
|
268
|
+
* Handles wrapper click to focus input and collapse expanded tags
|
|
236
269
|
*/
|
|
237
270
|
const handleWrapperClick = () => {
|
|
271
|
+
// Collapse all expanded tags when clicking on the wrapper area
|
|
272
|
+
if (expandOnClick && expandedTags.size > 0) {
|
|
273
|
+
collapseAllTags();
|
|
274
|
+
}
|
|
238
275
|
focusInput();
|
|
239
276
|
};
|
|
240
277
|
|
|
@@ -268,7 +305,24 @@
|
|
|
268
305
|
>
|
|
269
306
|
<div class="tags-input-content">
|
|
270
307
|
{#each value as tag, index (`${index}-${tag}`)}
|
|
271
|
-
|
|
308
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
309
|
+
<span
|
|
310
|
+
class="tag {expandOnClick ? 'expandable' : ''} {isTagExpanded(index) ? 'expanded' : ''}"
|
|
311
|
+
role="listitem"
|
|
312
|
+
onclick={(e) => {
|
|
313
|
+
if (expandOnClick) {
|
|
314
|
+
e.stopPropagation();
|
|
315
|
+
toggleTagExpand(index);
|
|
316
|
+
}
|
|
317
|
+
}}
|
|
318
|
+
onkeydown={(e) => {
|
|
319
|
+
if (expandOnClick && e.key === 'Enter') {
|
|
320
|
+
e.stopPropagation();
|
|
321
|
+
toggleTagExpand(index);
|
|
322
|
+
}
|
|
323
|
+
}}
|
|
324
|
+
tabindex={expandOnClick ? 0 : undefined}
|
|
325
|
+
>
|
|
272
326
|
<span class="tag-text">{tag}</span>
|
|
273
327
|
{#if !readonly && !isDisabled}
|
|
274
328
|
<button
|
|
@@ -391,10 +445,14 @@
|
|
|
391
445
|
flex-wrap: wrap;
|
|
392
446
|
align-items: flex-start;
|
|
393
447
|
align-content: flex-start;
|
|
394
|
-
gap:
|
|
448
|
+
gap: 0;
|
|
395
449
|
width: 100%;
|
|
396
450
|
}
|
|
397
451
|
|
|
452
|
+
.tags-input-wrapper.show-remove-icon .tags-input-content {
|
|
453
|
+
gap: 4px;
|
|
454
|
+
}
|
|
455
|
+
|
|
398
456
|
.tag {
|
|
399
457
|
position: relative;
|
|
400
458
|
display: flex;
|
|
@@ -414,6 +472,22 @@
|
|
|
414
472
|
user-select: none;
|
|
415
473
|
max-width: 100%;
|
|
416
474
|
min-width: 0;
|
|
475
|
+
margin: 2px;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
.tags-input-wrapper.show-remove-icon .tag {
|
|
479
|
+
margin: 0;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.tag.expandable {
|
|
483
|
+
cursor: pointer;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
.tag.expanded .tag-text {
|
|
487
|
+
overflow: visible;
|
|
488
|
+
text-overflow: unset;
|
|
489
|
+
white-space: normal;
|
|
490
|
+
word-break: break-word;
|
|
417
491
|
}
|
|
418
492
|
|
|
419
493
|
.tag-text {
|
|
@@ -447,6 +521,20 @@
|
|
|
447
521
|
pointer-events: auto;
|
|
448
522
|
}
|
|
449
523
|
|
|
524
|
+
/* Show remove icon mode - inline button */
|
|
525
|
+
.tags-input-wrapper.show-remove-icon .tag-remove {
|
|
526
|
+
position: relative;
|
|
527
|
+
top: auto;
|
|
528
|
+
right: auto;
|
|
529
|
+
bottom: auto;
|
|
530
|
+
padding: 0;
|
|
531
|
+
margin-left: 4px;
|
|
532
|
+
background: transparent;
|
|
533
|
+
opacity: 1;
|
|
534
|
+
pointer-events: auto;
|
|
535
|
+
border-radius: 0;
|
|
536
|
+
}
|
|
537
|
+
|
|
450
538
|
.tag-remove:not(:disabled) {
|
|
451
539
|
color: var(--text1);
|
|
452
540
|
}
|
|
@@ -62,6 +62,18 @@ export interface TagsInputProps {
|
|
|
62
62
|
* Whether to trim whitespace from tags (default: true)
|
|
63
63
|
*/
|
|
64
64
|
trimTags?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Whether to always show the remove icon on tags (default: false)
|
|
67
|
+
* When true: remove button is inline, 4px gap, no padding on close button
|
|
68
|
+
* When false: remove button is absolute positioned, appears on hover
|
|
69
|
+
*/
|
|
70
|
+
showRemoveIcon?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Whether clicking a tag expands it to show full content (default: false)
|
|
73
|
+
* When true: clicking a tag removes ellipsis and shows full text
|
|
74
|
+
* When false: long tags are always truncated with ellipsis
|
|
75
|
+
*/
|
|
76
|
+
expandOnClick?: boolean;
|
|
65
77
|
/**
|
|
66
78
|
* Custom width for the component
|
|
67
79
|
*/
|