@v1nt1248/3nclient-lib 0.1.80 → 0.1.81

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
@@ -2,7 +2,7 @@
2
2
  "name": "@v1nt1248/3nclient-lib",
3
3
  "license": "AGPL-3.0-or-later",
4
4
  "author": "v1nt1248",
5
- "version": "0.1.80",
5
+ "version": "0.1.81",
6
6
  "description": "Library for 3NWeb clients",
7
7
  "type": "module",
8
8
  "files": [
@@ -20,6 +20,8 @@ export interface Ui3nAutocompleteProps<T extends Ui3nAutocompleteOptionBase> {
20
20
  itemTitle?: keyof T;
21
21
  itemValue?: keyof T;
22
22
  modelValue: T[] | Array<T[keyof T]>;
23
+ addNewValue?: boolean;
24
+ newValueValidator?: (value: string) => boolean;
23
25
  multiple?: boolean;
24
26
  noDataText?: string;
25
27
  placeholder?: string;
@@ -30,6 +32,7 @@ export interface Ui3nAutocompleteEmits<T extends Ui3nAutocompleteOptionBase> {
30
32
  (event: 'update:modelValue', value: T[] | Array<T[keyof T]>): void;
31
33
  (event: 'update:search', value: string): void;
32
34
  (event: 'update:focused', value: boolean): void;
35
+ (event: 'valid:new-value', value: boolean): void;
33
36
  }
34
37
 
35
38
  export interface Ui3nAutocompleteSlots<T extends Ui3nAutocompleteOptionBase> {
@@ -52,6 +52,7 @@
52
52
  const inputEl = ref<Nullable<HTMLInputElement>>(null);
53
53
  const menuBodyEl = ref<Nullable<HTMLDivElement>>(null);
54
54
  const activeItemsIndex = ref<Nullable<number>>(null);
55
+ const isNewValueValid = ref(true);
55
56
 
56
57
  const ids = computed(() => props.returnObject
57
58
  ? (props.modelValue as T[]).map(item => item.id)
@@ -96,7 +97,7 @@
96
97
  }
97
98
 
98
99
  function onBlur() {
99
- emits('update:focused', false)
100
+ emits('update:focused', false);
100
101
  activeItemsIndex.value = null;
101
102
 
102
103
  setTimeout(() => {
@@ -166,7 +167,7 @@
166
167
  }
167
168
 
168
169
  function handlePressingEscOrTabKeys() {
169
- emits('update:focused', false)
170
+ emits('update:focused', false);
170
171
  isMenuOpen.value = false;
171
172
  activeItemsIndex.value = null;
172
173
  query.value = '';
@@ -175,21 +176,35 @@
175
176
  }
176
177
 
177
178
  function handlePressingEnterKey() {
178
- if (activeItemsIndex.value === null) {
179
+ if (activeItemsIndex.value === null && !isMenuOpen.value) {
179
180
  isMenuOpen.value = true;
180
181
  activeItemsIndex.value = 0;
181
182
  menuBodyEl.value && menuBodyEl.value.focus();
182
183
  return;
183
184
  }
184
185
 
185
- const item = filteredItems.value[activeItemsIndex.value!];
186
- onItemClick(item);
187
- activeItemsIndex.value = null;
186
+ if (activeItemsIndex.value === null && isMenuOpen.value && query.value) {
187
+ isNewValueValid.value = !props.newValueValidator ? true : props.newValueValidator(query.value);
188
+ emits('valid:new-value', isNewValueValid.value);
189
+ if (isNewValueValid.value) {
190
+ const item = {
191
+ id: getRandomId(6),
192
+ [props.itemTitle]: query.value,
193
+ [props.itemValue]: query.value,
194
+ };
195
+ onItemClick(item as unknown as T);
196
+ }
197
+ return;
198
+ }
199
+
200
+ if (activeItemsIndex.value !== null) {
201
+ const item = filteredItems.value[activeItemsIndex.value!];
202
+ onItemClick(item);
203
+ activeItemsIndex.value = null;
204
+ }
188
205
  }
189
206
 
190
207
  function onKeydown(event: KeyboardEvent, key: 'down' | 'up' | 'esc' | 'enter' | 'tab') {
191
- if (isEmpty(filteredItems.value)) return;
192
-
193
208
  switch (key) {
194
209
  case 'down':
195
210
  event.preventDefault();
@@ -219,6 +234,10 @@
219
234
  if (val !== oVal) {
220
235
  activeItemsIndex.value = null;
221
236
  }
237
+
238
+ if (val && !isNewValueValid.value) {
239
+ isNewValueValid.value = true;
240
+ }
222
241
  },
223
242
  );
224
243
  </script>
@@ -249,7 +268,8 @@
249
268
  ref="inputEl"
250
269
  v-model="query"
251
270
  type="text"
252
- :class="$style.input"
271
+ :class="[$style.input, !isNewValueValid && $style.inputWithError]"
272
+ :placeholder="isEmpty(modelValue) && placeholder ? placeholder : ''"
253
273
  :disabled="disabled"
254
274
  @input="onInput"
255
275
  @focusin="emits('update:focused', true)"
@@ -343,7 +363,12 @@
343
363
  &:hover,
344
364
  &:focus {
345
365
  height: calc(var(--autocomplete-min-height) - 2px);
346
- border: 1px solid var(--color-border-control-accent-focused);
366
+
367
+ &:not(.inputWithError) {
368
+ border: 1px solid var(--color-border-control-accent-focused);
369
+ }
370
+
371
+ border: 1px solid var(--error-content-default);
347
372
  }
348
373
  }
349
374