@x33025/sveltely 0.0.28 → 0.0.31

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.
@@ -20,6 +20,9 @@
20
20
  let inputValue = $state('');
21
21
  let showInput = $state(false);
22
22
  let inputEl = $state<HTMLInputElement | null>(null);
23
+ let editingTag = $state<string | null>(null);
24
+ let editingValue = $state('');
25
+ let editingEl = $state<HTMLInputElement | null>(null);
23
26
  const selectionEnabled = $derived(selection !== undefined);
24
27
 
25
28
  const addTag = (rawValue: string) => {
@@ -47,6 +50,12 @@
47
50
  return;
48
51
  }
49
52
 
53
+ if (event.key === 'Enter' && !inputValue.trim()) {
54
+ event.preventDefault();
55
+ showInput = false;
56
+ return;
57
+ }
58
+
50
59
  if (event.key === 'Escape') {
51
60
  inputValue = '';
52
61
  showInput = false;
@@ -54,7 +63,8 @@
54
63
  }
55
64
 
56
65
  if (event.key === 'Backspace' && !inputValue && tags.length > 0) {
57
- tags = tags.slice(0, -1);
66
+ event.preventDefault();
67
+ startEditing(tags[tags.length - 1]);
58
68
  }
59
69
  };
60
70
 
@@ -64,6 +74,74 @@
64
74
  inputEl?.focus();
65
75
  };
66
76
 
77
+ const startEditing = async (tag: string) => {
78
+ editingTag = tag;
79
+ editingValue = tag;
80
+ await tick();
81
+ editingEl?.focus();
82
+ editingEl?.select();
83
+ };
84
+
85
+ const commitEdit = async (source: 'enter' | 'blur' | 'advance', targetTag: string) => {
86
+ if (!editingTag || editingTag !== targetTag) return;
87
+
88
+ const previous = editingTag;
89
+ const next = editingValue.trim();
90
+ editingTag = null;
91
+
92
+ if (!next) {
93
+ if (source === 'enter' || source === 'advance') {
94
+ const previousIndex = tags.indexOf(previous);
95
+ const nextTags = tags.filter((tag) => tag !== previous);
96
+ tags = nextTags;
97
+ if (selection?.includes(previous)) {
98
+ selection = selection.filter((tag) => tag !== previous);
99
+ }
100
+
101
+ if (source === 'advance' && nextTags.length > 0) {
102
+ const nextIndex = Math.min(previousIndex, nextTags.length - 1);
103
+ await startEditing(nextTags[nextIndex]);
104
+ } else if (nextTags.length === 0) {
105
+ inputValue = '';
106
+ await openInput();
107
+ }
108
+ }
109
+ return;
110
+ }
111
+
112
+ if (next === previous) return;
113
+ if (tags.includes(next)) return;
114
+
115
+ tags = tags.map((tag) => (tag === previous ? next : tag));
116
+ if (selection?.includes(previous)) {
117
+ selection = selection.map((tag) => (tag === previous ? next : tag));
118
+ }
119
+ };
120
+
121
+ const cancelEdit = () => {
122
+ editingTag = null;
123
+ editingValue = '';
124
+ };
125
+
126
+ const onEditKeydown = async (event: KeyboardEvent, tag: string) => {
127
+ if (event.key === 'Enter') {
128
+ event.preventDefault();
129
+ await commitEdit('enter', tag);
130
+ return;
131
+ }
132
+
133
+ if ((event.key === 'Backspace' || event.key === 'Delete') && !editingValue.trim()) {
134
+ event.preventDefault();
135
+ await commitEdit('advance', tag);
136
+ return;
137
+ }
138
+
139
+ if (event.key === 'Escape') {
140
+ event.preventDefault();
141
+ cancelEdit();
142
+ }
143
+ };
144
+
67
145
  const onBlur = () => {
68
146
  if (!inputValue.trim()) {
69
147
  showInput = false;
@@ -82,18 +160,31 @@
82
160
  <div class="w-full max-w-lg">
83
161
  <div class="tag-row flex flex-wrap items-center">
84
162
  {#each tags as tag (tag)}
85
- {#if selectionEnabled}
163
+ {#if editingTag === tag}
164
+ <input
165
+ bind:this={editingEl}
166
+ bind:value={editingValue}
167
+ class="tag-surface tag-input-field min-w-20 outline-none"
168
+ onblur={() => commitEdit('blur', tag)}
169
+ onkeydown={(event) => onEditKeydown(event, tag)}
170
+ />
171
+ {:else if selectionEnabled}
86
172
  <button
87
173
  type="button"
88
174
  class="tag-surface inline-flex items-center gap-2"
89
175
  class:tag-selected={selection?.includes(tag)}
90
176
  onclick={() => toggleSelected(tag)}
177
+ ondblclick={() => startEditing(tag)}
91
178
  aria-pressed={selection?.includes(tag)}
92
179
  >
93
180
  {tag}
94
181
  </button>
95
182
  {:else}
96
- <span class="tag-surface inline-flex items-center gap-2">{tag}</span>
183
+ <button
184
+ type="button"
185
+ class="tag-surface inline-flex items-center gap-2"
186
+ ondblclick={() => startEditing(tag)}>{tag}</button
187
+ >
97
188
  {/if}
98
189
  {/each}
99
190
 
package/dist/style.css CHANGED
@@ -272,6 +272,9 @@
272
272
  .max-w-lg {
273
273
  max-width: var(--container-lg);
274
274
  }
275
+ .min-w-20 {
276
+ min-width: calc(var(--spacing) * 20);
277
+ }
275
278
  .min-w-36 {
276
279
  min-width: calc(var(--spacing) * 36);
277
280
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x33025/sveltely",
3
- "version": "0.0.28",
3
+ "version": "0.0.31",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",