Skip to content

Commit fe681ab

Browse files
committed
refac: tags
1 parent 43c6846 commit fe681ab

3 files changed

Lines changed: 78 additions & 100 deletions

File tree

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
<script lang="ts">
2-
import TagInput from './Tags/TagInput.svelte';
32
import TagList from './Tags/TagList.svelte';
43
import { getContext, createEventDispatcher } from 'svelte';
54
const dispatch = createEventDispatcher();
@@ -8,21 +7,35 @@
87
98
export let tags = [];
109
export let suggestionTags = [];
10+
11+
let inputValue = '';
12+
13+
const addTag = () => {
14+
const value = inputValue.trim();
15+
if (value !== '') {
16+
dispatch('add', value);
17+
inputValue = '';
18+
}
19+
};
1120
</script>
1221

13-
<ul class="flex flex-row flex-wrap gap-[0.3rem] line-clamp-1">
22+
<div class="flex flex-wrap items-center gap-1.5 w-full">
1423
<TagList
1524
{tags}
1625
on:delete={(e) => {
1726
dispatch('delete', e.detail);
1827
}}
1928
/>
2029

21-
<TagInput
22-
label={tags.length == 0 ? $i18n.t('Add Tags') : ''}
23-
{suggestionTags}
24-
on:add={(e) => {
25-
dispatch('add', e.detail);
30+
<input
31+
bind:value={inputValue}
32+
class="flex-1 min-w-24 px-1 text-xs bg-transparent outline-hidden placeholder:text-gray-400 dark:placeholder:text-gray-500"
33+
placeholder={$i18n.t('Type to add tag...')}
34+
on:keydown={(event) => {
35+
if (event.key === 'Enter' || event.key === ' ') {
36+
event.preventDefault();
37+
addTag();
38+
}
2639
}}
2740
/>
28-
</ul>
41+
</div>
Lines changed: 47 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,72 @@
11
<script lang="ts">
22
import { createEventDispatcher, getContext } from 'svelte';
3-
import { tags } from '$lib/stores';
4-
import { toast } from 'svelte-sonner';
53
const dispatch = createEventDispatcher();
64
75
const i18n = getContext('i18n');
86
9-
export let label = '';
107
export let suggestionTags = [];
118
12-
let showTagInput = false;
139
let tagName = '';
10+
let showInput = false;
11+
let inputElement: HTMLInputElement;
1412
1513
const addTagHandler = async () => {
1614
tagName = tagName.trim();
1715
if (tagName !== '') {
1816
dispatch('add', tagName);
1917
tagName = '';
20-
showTagInput = false;
21-
} else {
22-
toast.error($i18n.t(`Invalid Tag`));
2318
}
2419
};
25-
</script>
2620
27-
<div class="px-0.5 flex {showTagInput ? 'flex-row-reverse' : ''}">
28-
{#if showTagInput}
29-
<div class="flex items-center">
30-
<input
31-
bind:value={tagName}
32-
class=" px-2 cursor-pointer self-center text-xs h-fit bg-transparent outline-hidden line-clamp-1 w-[6.5rem]"
33-
placeholder={$i18n.t('Add a tag')}
34-
aria-label={$i18n.t('Add a tag')}
35-
list="tagOptions"
36-
on:keydown={(event) => {
37-
if (event.key === 'Enter') {
38-
event.preventDefault();
39-
addTagHandler();
40-
}
41-
}}
42-
/>
43-
{#if suggestionTags.length > 0}
44-
<datalist id="tagOptions">
45-
{#each suggestionTags as tag}
46-
<option value={tag.name} />
47-
{/each}
48-
</datalist>
49-
{/if}
21+
const openInput = () => {
22+
showInput = true;
23+
setTimeout(() => inputElement?.focus(), 0);
24+
};
5025
51-
<button type="button" aria-label={$i18n.t('Save Tag')} on:click={addTagHandler}>
52-
<svg
53-
xmlns="http://www.w3.org/2000/svg"
54-
viewBox="0 0 16 16"
55-
fill="currentColor"
56-
stroke-width="2"
57-
aria-hidden="true"
58-
class="w-3 h-3"
59-
>
60-
<path
61-
fill-rule="evenodd"
62-
d="M12.416 3.376a.75.75 0 0 1 .208 1.04l-5 7.5a.75.75 0 0 1-1.154.114l-3-3a.75.75 0 0 1 1.06-1.06l2.353 2.353 4.493-6.74a.75.75 0 0 1 1.04-.207Z"
63-
clip-rule="evenodd"
64-
/>
65-
</svg>
66-
</button>
67-
</div>
68-
{/if}
26+
const closeInput = () => {
27+
if (tagName.trim() === '') {
28+
showInput = false;
29+
}
30+
};
31+
</script>
6932

33+
{#if showInput}
34+
<div class="flex items-center gap-2 px-3 py-1.5 rounded-lg bg-gray-200/80 dark:bg-gray-700">
35+
<span class="text-gray-500 dark:text-blue-400">+</span>
36+
<input
37+
bind:this={inputElement}
38+
bind:value={tagName}
39+
class="w-20 text-sm bg-transparent outline-hidden text-gray-700 dark:text-blue-400 placeholder:text-gray-400 dark:placeholder:text-blue-400/50"
40+
placeholder={$i18n.t('Add tag')}
41+
aria-label={$i18n.t('Add a tag')}
42+
list="tagOptions"
43+
on:keydown={(event) => {
44+
if (event.key === 'Enter' || event.key === ' ') {
45+
event.preventDefault();
46+
addTagHandler();
47+
} else if (event.key === 'Escape') {
48+
tagName = '';
49+
showInput = false;
50+
}
51+
}}
52+
on:blur={closeInput}
53+
/>
54+
</div>
55+
{:else}
7056
<button
71-
class=" cursor-pointer self-center p-0.5 flex h-fit items-center rounded-full transition border dark:border-gray-600 border-dashed"
7257
type="button"
73-
aria-label={$i18n.t('Add Tag')}
74-
on:click={() => {
75-
showTagInput = !showTagInput;
76-
}}
58+
class="flex items-center gap-2 px-3 py-1.5 rounded-lg bg-gray-200/80 dark:bg-gray-700 text-gray-500 dark:text-blue-400 text-sm font-medium hover:bg-gray-300 dark:hover:bg-gray-600 transition-colors"
59+
on:click={openInput}
7760
>
78-
<div class=" m-auto self-center">
79-
<svg
80-
xmlns="http://www.w3.org/2000/svg"
81-
viewBox="0 0 16 16"
82-
aria-hidden="true"
83-
fill="currentColor"
84-
class="size-2.5 {showTagInput ? 'rotate-45' : ''} transition-all transform"
85-
>
86-
<path
87-
d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z"
88-
/>
89-
</svg>
90-
</div>
61+
<span>+</span>
62+
<span>{$i18n.t('Add Tag')}</span>
9163
</button>
64+
{/if}
9265

93-
{#if label && !showTagInput}
94-
<span class="text-xs pl-2 self-center">{label}</span>
95-
{/if}
96-
</div>
66+
{#if suggestionTags.length > 0}
67+
<datalist id="tagOptions">
68+
{#each suggestionTags as tag}
69+
<option value={tag.name} />
70+
{/each}
71+
</datalist>
72+
{/if}

src/lib/components/common/Tags/TagItem.svelte

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,21 @@
33
44
const i18n = getContext('i18n');
55
6-
import Tooltip from '../Tooltip.svelte';
76
import XMark from '$lib/components/icons/XMark.svelte';
87
98
export let tag;
109
export let onDelete = () => {};
1110
</script>
1211

1312
{#if tag}
14-
<Tooltip content={tag.name}>
15-
<button
16-
type="button"
17-
aria-label={$i18n.t('Remove this tag from list')}
18-
class="relative group/tags px-1.5 py-[0.5px] gap-0.5 flex justify-between h-fit max-h-fit w-fit items-center rounded-lg bg-gray-500/20 text-gray-700 dark:text-gray-200 transition cursor-pointer"
19-
on:click={() => {
20-
onDelete();
21-
}}
22-
>
23-
<div class=" text-[0.7rem] font-medium self-center line-clamp-1 w-fit">
24-
{tag.name}
25-
</div>
26-
27-
<div class="hidden group-hover/tags:block transition">
28-
<div class="rounded-full pl-[1px] backdrop-blur-sm h-full flex self-center cursor-pointer">
29-
<XMark className="size-3" strokeWidth="2.5" />
30-
</div>
31-
</div>
32-
</button>
33-
</Tooltip>
13+
<button
14+
type="button"
15+
class="flex items-center gap-0.5 px-1.5 py-[1px] rounded-lg bg-gray-500/20 text-gray-700 dark:text-gray-200 text-xs font-medium hover:bg-gray-500/30 transition-colors"
16+
on:click={() => {
17+
onDelete();
18+
}}
19+
>
20+
<span class="line-clamp-1">{tag.name}</span>
21+
<XMark className="size-3" strokeWidth="2.5" />
22+
</button>
3423
{/if}

0 commit comments

Comments
 (0)