@winchsa/ui 0.1.36 → 0.1.39

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.
@@ -120,7 +120,7 @@ const mergedCloseBtn = computed(() => ({
120
120
  class="d-flex flex-wrap justify-center gap-4 mt-4 "
121
121
  >
122
122
  <BaseButton
123
- v-if="mergedCloseBtn"
123
+ v-if="closeBtn"
124
124
  :color="mergedCloseBtn.color"
125
125
  :disabled="mergedCloseBtn?.disabled"
126
126
  class="flex-1-1"
@@ -131,7 +131,7 @@ const mergedCloseBtn = computed(() => ({
131
131
  </BaseButton>
132
132
 
133
133
  <BaseButton
134
- v-if="mergedConfirmBtn"
134
+ v-if="confirmBtn"
135
135
  type="submit"
136
136
  class="flex-1-1"
137
137
  :color="mergedConfirmBtn.color"
@@ -15,12 +15,12 @@ type __VLS_Slots = {} & {
15
15
  default?: (props: typeof __VLS_76) => any;
16
16
  };
17
17
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
18
- collapsed: (isContentCollapsed: boolean) => any;
19
18
  refresh: (hideOverlay: () => void) => any;
19
+ collapsed: (isContentCollapsed: boolean) => any;
20
20
  trash: () => any;
21
21
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
22
- onCollapsed?: ((isContentCollapsed: boolean) => any) | undefined;
23
22
  onRefresh?: ((hideOverlay: () => void) => any) | undefined;
23
+ onCollapsed?: ((isContentCollapsed: boolean) => any) | undefined;
24
24
  onTrash?: (() => any) | undefined;
25
25
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
26
26
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -15,12 +15,12 @@ type __VLS_Slots = {} & {
15
15
  default?: (props: typeof __VLS_76) => any;
16
16
  };
17
17
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
18
- collapsed: (isContentCollapsed: boolean) => any;
19
18
  refresh: (hideOverlay: () => void) => any;
19
+ collapsed: (isContentCollapsed: boolean) => any;
20
20
  trash: () => any;
21
21
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
22
- onCollapsed?: ((isContentCollapsed: boolean) => any) | undefined;
23
22
  onRefresh?: ((hideOverlay: () => void) => any) | undefined;
23
+ onCollapsed?: ((isContentCollapsed: boolean) => any) | undefined;
24
24
  onTrash?: (() => any) | undefined;
25
25
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
26
26
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -35,12 +35,14 @@ type __VLS_Props = {
35
35
  isLoading?: boolean;
36
36
  disableIfEmpty?: boolean;
37
37
  clearable?: boolean;
38
+ showNoOptions?: boolean;
39
+ showNoResults?: boolean;
38
40
  };
39
- declare var __VLS_71: string | number, __VLS_72: any, __VLS_136: string | number, __VLS_137: any;
41
+ declare var __VLS_69: string | number, __VLS_70: any, __VLS_134: string | number, __VLS_135: any;
40
42
  type __VLS_Slots = {} & {
41
- [K in NonNullable<typeof __VLS_71>]?: (props: typeof __VLS_72) => any;
43
+ [K in NonNullable<typeof __VLS_69>]?: (props: typeof __VLS_70) => any;
42
44
  } & {
43
- [K in NonNullable<typeof __VLS_136>]?: (props: typeof __VLS_137) => any;
45
+ [K in NonNullable<typeof __VLS_134>]?: (props: typeof __VLS_135) => any;
44
46
  };
45
47
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
46
48
  records: Ref<{
@@ -4,7 +4,7 @@ import { ref, computed, watch, useAttrs } from "vue";
4
4
  import { useI18n } from "vue-i18n";
5
5
  import { VLabel, VValidation, VListItem, VMessages, VIcon } from "vuetify/components";
6
6
  import { client } from "../../utils/client";
7
- import { generateUniqueId } from "../../utils/utils";
7
+ import { generateUniqueId, searchArabicText } from "../../utils/utils";
8
8
  import { buildQueryString } from "../../utils/queryParams";
9
9
  import LoadingBar from "../LoadingBar.vue";
10
10
  const { t } = useI18n();
@@ -43,8 +43,11 @@ const props = defineProps({
43
43
  fetchEnabled: { type: Boolean, required: false, default: true },
44
44
  isLoading: { type: Boolean, required: false, default: false },
45
45
  disableIfEmpty: { type: Boolean, required: false, default: false },
46
- clearable: { type: Boolean, required: false, default: true }
46
+ clearable: { type: Boolean, required: false, default: true },
47
+ showNoOptions: { type: Boolean, required: false, default: true },
48
+ showNoResults: { type: Boolean, required: false, default: true }
47
49
  });
50
+ const emits = defineEmits(["update:model-value"]);
48
51
  const defaultOptions = ref(props.options ?? []);
49
52
  const displayedOptions = ref(props.options ?? []);
50
53
  const loadedValuesHaveBeenSet = ref(false);
@@ -52,10 +55,8 @@ const loading = ref(false);
52
55
  const showTeleport = ref(false);
53
56
  const search = ref("");
54
57
  const inputRef = ref({});
55
- const teleportRef = ref(null);
56
58
  const errorState = ref(false);
57
59
  const isOpen = ref(false);
58
- const emits = defineEmits(["update:model-value"]);
59
60
  const value = computed({
60
61
  get() {
61
62
  return props.modelValue;
@@ -64,9 +65,7 @@ const value = computed({
64
65
  emits("update:model-value", value2);
65
66
  }
66
67
  });
67
- const isLoading = computed(() => {
68
- return props.isLoading || loading.value;
69
- });
68
+ const isLoading = computed(() => props.isLoading || loading.value);
70
69
  const params = computed(() => buildQueryString(props.params));
71
70
  const elementId = computed(() => {
72
71
  const attrs = useAttrs();
@@ -74,10 +73,59 @@ const elementId = computed(() => {
74
73
  return _elementIdToken ? `autocomplete-input-${_elementIdToken}-${guid}` : `autocomplete-input-${guid}`;
75
74
  });
76
75
  const internalPlaceholder = computed(() => {
77
- return props.placeholder ? props.placeholder : t("inputs.placeholder_select", {
78
- name: props.label ? t(props.label) : ""
79
- });
76
+ return props.placeholder ?? t("inputs.placeholder_select", { name: props.label ? t(props.label) : "" });
77
+ });
78
+ const hasValue = computed(() => Array.isArray(value.value) ? value.value.length > 0 : !!value.value);
79
+ const hasError = computed(() => (validationIsValid) => {
80
+ return props.error || validationIsValid === false || errorState.value;
80
81
  });
82
+ const filteredOptions = computed(() => {
83
+ if (!search.value) {
84
+ return displayedOptions.value;
85
+ }
86
+ return displayedOptions.value?.map((entry) => {
87
+ if (props.grouped) {
88
+ const matchingChildren = entry?.children?.filter((child) => {
89
+ const childValue = getOptionValue(child);
90
+ return searchArabicText(childValue, search.value);
91
+ });
92
+ return matchingChildren?.length ? { ...entry, children: matchingChildren } : null;
93
+ }
94
+ const entryValue = getOptionValue(entry);
95
+ return searchArabicText(entryValue, search.value) ? entry : null;
96
+ }).filter(Boolean);
97
+ });
98
+ const disableIfNoOptions = computed(() => filteredOptions.value.length === 0 && props.disableIfEmpty);
99
+ const sharedMultiSelectProps = computed(() => ({
100
+ options: filteredOptions.value,
101
+ multiple: props.multiple,
102
+ closeOnSelect: !props.multiple,
103
+ searchable: props.searchable,
104
+ internalSearch: false,
105
+ preselectFirst: props.preselectFirst,
106
+ disabled: props.disabled || disableIfNoOptions.value,
107
+ required: props.required,
108
+ groupSelect: props.grouped,
109
+ groupValues: props.groupValues,
110
+ groupLabel: props.groupLabel,
111
+ label: props.itemLabel,
112
+ trackBy: props.trackBy,
113
+ placeholder: hasValue.value ? "" : internalPlaceholder.value || t("inputs.placeholder_select"),
114
+ showLabels: false,
115
+ clearOnSelect: true,
116
+ showNoOptions: isLoading.value ? false : props.showNoOptions,
117
+ showNoResults: isLoading.value ? false : props.showNoResults,
118
+ deselectLabel: props.deselectLabel,
119
+ hideSelected: props.hideSelected,
120
+ selectLabel: props.selectLabel,
121
+ selectedLabel: props.selectedLabel
122
+ }));
123
+ const getOptionValue = (option) => {
124
+ if (typeof option === "object" && option !== null && "name" in option) {
125
+ return option.name?.toString() ?? "";
126
+ }
127
+ return option?.toString() || "";
128
+ };
81
129
  const customPosition = () => {
82
130
  const autocompleteInputElement = document.querySelector(`#${elementId.value}`);
83
131
  const rect = autocompleteInputElement?.getBoundingClientRect();
@@ -88,26 +136,22 @@ const customPosition = () => {
88
136
  height: rect?.height
89
137
  };
90
138
  };
91
- const normalizeArabic = (text) => {
92
- if (typeof text === "number") {
93
- return text.toString();
94
- }
95
- return text.trim().replace(/أ|ا|إ|آ/g, "\u0627").replace(/ي|ى|ئ/g, "\u064A").replace(/ة/g, "\u0647");
139
+ const customSearch = (query) => {
140
+ search.value = query?.toString().trim() || "";
96
141
  };
97
- const searchArabic = (value2, query) => {
98
- if (!value2 || !query) {
99
- return false;
142
+ const handleOpen = () => {
143
+ if (props.teleported) {
144
+ showTeleport.value = true;
100
145
  }
101
- const normalizedValue = normalizeArabic(value2.toString()).toLowerCase();
102
- const normalizedQuery = normalizeArabic(query).toLowerCase();
103
- return normalizedValue.includes(normalizedQuery);
146
+ isOpen.value = true;
104
147
  };
105
- const customSearch = (query) => {
106
- search.value = query?.toString().trim() || "";
148
+ const handleClose = () => {
149
+ showTeleport.value = false;
150
+ isOpen.value = false;
107
151
  };
108
152
  const resetTeleportState = () => {
109
153
  if (!props.multiple && props.teleported) {
110
- showTeleport.value = false;
154
+ handleClose();
111
155
  inputRef.value[`select-input-${elementId.value}`]?.deactivate();
112
156
  }
113
157
  };
@@ -132,90 +176,62 @@ const getRecords = async (searchQuery) => {
132
176
  const fetchAndUpdateRecords = async (searchQuery) => {
133
177
  displayedOptions.value = await getRecords(searchQuery);
134
178
  };
135
- watch(
136
- [() => props.trackByValue, defaultOptions],
137
- async ([trackByValue, _defaultOptions]) => {
138
- if (!trackByValue || _defaultOptions.length === 0 || loadedValuesHaveBeenSet.value) {
139
- return;
140
- }
141
- loadedValuesHaveBeenSet.value = true;
142
- const loadedValues = Array.isArray(trackByValue) ? trackByValue : [trackByValue];
143
- let items = _defaultOptions.flatMap((option) => props.grouped ? option.children : [option]);
144
- const missingIds = loadedValues.filter((value2) => !items.some((item) => item?.[props.trackBy] == value2));
145
- if (missingIds.length > 0 && props.url) {
146
- const fetchedItems = await Promise.all(missingIds.map((id) => getRecords(id)));
147
- if (fetchedItems.length > 0) {
148
- items = items.concat(fetchedItems.flat());
149
- }
150
- }
151
- const optionsToSelect = loadedValues.map((value2) => items.find((item) => item?.[props.trackBy] == value2)).filter(Boolean);
152
- if (optionsToSelect.length > 0) {
153
- emits("update:model-value", optionsToSelect);
179
+ const updateOptions = async (newOptions) => {
180
+ defaultOptions.value = newOptions;
181
+ displayedOptions.value = newOptions;
182
+ };
183
+ const handleSelect = () => {
184
+ errorState.value = false;
185
+ resetTeleportState();
186
+ };
187
+ const handleSearchChange = (query) => {
188
+ if (props.searchInternally) {
189
+ customSearch(query);
190
+ } else {
191
+ fetchAndUpdateRecords(query?.toString());
192
+ }
193
+ };
194
+ watch([() => props.trackByValue, defaultOptions], async ([trackByValue, _defaultOptions]) => {
195
+ if (!trackByValue || _defaultOptions.length === 0 || loadedValuesHaveBeenSet.value) {
196
+ return;
197
+ }
198
+ loadedValuesHaveBeenSet.value = true;
199
+ const loadedValues = Array.isArray(trackByValue) ? trackByValue : [trackByValue];
200
+ let items = _defaultOptions.flatMap((option) => props.grouped ? option.children : [option]);
201
+ const missingIds = loadedValues.filter(
202
+ (value2) => !items.some((item) => item?.[props.trackBy] == value2)
203
+ );
204
+ if (missingIds.length > 0 && props.url) {
205
+ const fetchedItems = await Promise.all(missingIds.map((id) => getRecords(id)));
206
+ if (fetchedItems.length > 0) {
207
+ items = items.concat(fetchedItems.flat());
154
208
  }
155
- },
156
- {
157
- immediate: true
158
209
  }
159
- );
160
- watch(() => props.errorMessages, () => {
161
- if (props.errorMessages && props.errorMessages.length > 0) {
162
- errorState.value = true;
210
+ const optionsToSelect = loadedValues.map((value2) => items.find((item) => item?.[props.trackBy] == value2)).filter(Boolean);
211
+ if (optionsToSelect.length > 0) {
212
+ emits("update:model-value", optionsToSelect);
163
213
  }
164
- }, {
165
- deep: true,
166
- immediate: true
167
- });
214
+ }, { immediate: true });
215
+ watch(() => props.errorMessages, (newErrorMessages) => {
216
+ errorState.value = !!(newErrorMessages && newErrorMessages.length > 0);
217
+ }, { deep: true, immediate: true });
168
218
  watch(() => params.value, (value2) => {
169
219
  if (value2) {
170
220
  fetchAndUpdateRecords();
171
221
  }
172
222
  });
173
- const filteredOptions = computed(() => {
174
- if (!search.value) {
175
- return displayedOptions.value;
176
- }
177
- return displayedOptions.value?.map((entry) => {
178
- if (props.grouped) {
179
- const matchingChildren = entry?.children?.filter((child) => {
180
- const childValue = typeof child === "object" && child !== null && "name" in child ? child.name : child;
181
- return searchArabic(childValue?.toString() ?? "", search.value);
182
- });
183
- if (matchingChildren && matchingChildren.length > 0) {
184
- return {
185
- ...entry,
186
- children: matchingChildren
187
- };
188
- }
189
- return null;
190
- }
191
- const entryValue = typeof entry === "object" && entry !== null && "name" in entry ? entry.name : entry;
192
- return searchArabic(entryValue?.toString() || "", search.value) ? entry : null;
193
- }).filter(Boolean);
194
- });
195
- const disableIfNoOptions = computed(() => filteredOptions.value.length === 0 && props.disableIfEmpty);
196
- watch(() => props.url, async () => {
197
- if (props.fetchEnabled && props.url) {
198
- const options = await getRecords();
199
- defaultOptions.value = options;
200
- displayedOptions.value = options;
201
- }
202
- }, {
203
- immediate: true
204
- });
205
- watch(() => props.options, () => {
223
+ watch([() => props.url, () => props.options], async () => {
206
224
  if (props.options && props.options.length > 0) {
207
- defaultOptions.value = props.options;
208
- displayedOptions.value = props.options;
225
+ updateOptions(props.options);
226
+ } else if (props.fetchEnabled && props.url) {
227
+ const options = await getRecords();
228
+ updateOptions(options);
209
229
  }
210
- }, {
211
- deep: true,
212
- immediate: true
213
- });
230
+ }, { deep: true, immediate: true });
214
231
  const refetch = async () => {
215
232
  if (props.url) {
216
233
  const options = await getRecords();
217
- defaultOptions.value = options;
218
- displayedOptions.value = options;
234
+ updateOptions(options);
219
235
  return options;
220
236
  }
221
237
  return [];
@@ -244,22 +260,11 @@ defineExpose({
244
260
  validate-on="input lazy"
245
261
  >
246
262
  <MultiSelect
247
- ref="teleportRef"
248
263
  v-model="value"
249
- :options="filteredOptions"
250
- :multiple="multiple"
251
- :close-on-select="multiple ? false : true"
252
- :searchable="searchable"
253
- :internal-search="false"
254
- :preselect-first="preselectFirst"
255
- :disabled="disabled || disableIfNoOptions"
256
- :required="required"
257
- :group-select="grouped"
258
- :group-values="groupValues"
259
- :group-label="groupLabel"
264
+ v-bind="sharedMultiSelectProps"
260
265
  class="app-custom-input position-absolute app-z-7 app-custom-input-teleport"
261
266
  :class="{
262
- 'app-custom-input-error': error || isValid.value === false || errorState
267
+ 'app-custom-input-error': hasError(isValid.value)
263
268
  }"
264
269
  :style="{
265
270
  left: `${customPosition().left}px`,
@@ -267,25 +272,19 @@ defineExpose({
267
272
  width: `${customPosition().width}px`,
268
273
  height: `${customPosition().height}px`
269
274
  }"
270
- :label="itemLabel"
271
- :track-by="trackBy"
272
- :placeholder="value && value.length > 0 ? '' : internalPlaceholder || t('inputs.placeholder_select')"
273
- :show-labels="false"
274
- :clear-on-select="true"
275
- :show-no-options="isLoading ? false : true"
276
- :show-no-results="isLoading ? false : true"
277
- :deselect-label="deselectLabel"
278
- :hide-selected="hideSelected"
279
- :select-label="selectLabel"
280
- :selected-label="selectedLabel"
281
275
  :value="value"
282
- @search-change="searchInternally ? customSearch($event) : fetchAndUpdateRecords($event)"
283
- @select="errorState = false, resetTeleportState()"
284
- @close="showTeleport = false, isOpen = false"
276
+ @search-change="handleSearchChange"
277
+ @select="handleSelect"
278
+ @close="handleClose"
285
279
  >
286
280
  <template #caret>
287
281
  <span class="multiselect-arrow">
288
- <VIcon class="icon" size="19">tabler-chevron-down</VIcon>
282
+ <VIcon
283
+ icon="tabler-chevron-down"
284
+ class="icon"
285
+ size="19"
286
+ :color="hasError(isValid.value) ? 'error' : ''"
287
+ />
289
288
  </span>
290
289
  </template>
291
290
 
@@ -320,13 +319,16 @@ defineExpose({
320
319
 
321
320
  <template #clear>
322
321
  <div
323
- v-if="(Array.isArray(value) ? value.length > 0 : !!value) && !disabled && clearable"
322
+ v-if="hasValue && !disabled && clearable"
324
323
  class="multiselect-clear cursor-pointer"
325
324
  @mousedown.prevent.stop="value = null"
326
325
  >
327
- <VIcon class="icon" size="19">
328
- tabler-x
329
- </VIcon>
326
+ <VIcon
327
+ icon="tabler-x"
328
+ class="icon"
329
+ size="19"
330
+ :color="hasError(isValid.value) ? 'error' : ''"
331
+ />
330
332
  </div>
331
333
  </template>
332
334
 
@@ -340,9 +342,7 @@ defineExpose({
340
342
 
341
343
  <template v-if="isLoading" #beforeList>
342
344
  <div class="app-h-50px w-100">
343
- <LoadingBar
344
- :is-loading="isLoading"
345
- />
345
+ <LoadingBar :is-loading="isLoading" />
346
346
  </div>
347
347
  </template>
348
348
 
@@ -375,46 +375,30 @@ defineExpose({
375
375
  validate-on="input lazy"
376
376
  >
377
377
  <div class="d-flex h-100 position-relative">
378
- <div v-if="isOpen" class="w-100 h-100 position-absolute app-z-11" @click="closeMenu" />
378
+ <div v-if="isOpen && !teleported" class="w-100 h-100 position-absolute app-z-11" @click="closeMenu" />
379
379
  <MultiSelect
380
380
  :ref="el => inputRef[`select-input-${elementId}`] = el"
381
381
  v-model="value"
382
- :options="filteredOptions"
383
- :multiple="multiple"
384
- :close-on-select="multiple ? false : true"
385
- :searchable="searchable"
386
- :internal-search="false"
387
- :preselect-first="preselectFirst"
388
- :disabled="disabled || disableIfNoOptions"
389
- :required="required"
390
- :group-select="grouped"
391
- :group-values="groupValues"
392
- :group-label="groupLabel"
382
+ v-bind="sharedMultiSelectProps"
393
383
  class="app-custom-input"
394
384
  :class="{
395
- 'app-custom-input-error': error || isValid.value === false || errorState,
385
+ 'app-custom-input-error': hasError(isValid.value),
396
386
  'app-teleport-clone': teleported
397
387
  }"
398
388
  style="margin-top: 2px;"
399
- :label="itemLabel"
400
- :track-by="trackBy"
401
- :placeholder="value && value?.length > 0 ? '' : internalPlaceholder || t('inputs.placeholder_select')"
402
- :show-labels="false"
403
- :clear-on-select="true"
404
- :show-no-options="isLoading ? false : true"
405
- :show-no-results="isLoading ? false : true"
406
- :deselect-label="deselectLabel"
407
- :hide-selected="hideSelected"
408
- :select-label="selectLabel"
409
- :selected-label="selectedLabel"
410
- @open="showTeleport = true, isOpen = true"
411
- @close="showTeleport = false, isOpen = false"
412
- @search-change="searchInternally ? customSearch($event) : fetchAndUpdateRecords($event)"
413
- @select="errorState = false, resetTeleportState()"
389
+ @open="handleOpen"
390
+ @close="handleClose"
391
+ @search-change="handleSearchChange"
392
+ @select="handleSelect"
414
393
  >
415
394
  <template #caret>
416
395
  <span class="multiselect-arrow">
417
- <VIcon class="icon" size="19">tabler-chevron-down</VIcon>
396
+ <VIcon
397
+ icon="tabler-chevron-down"
398
+ class="icon"
399
+ size="19"
400
+ :color="hasError(isValid.value) ? 'error' : ''"
401
+ />
418
402
  </span>
419
403
  </template>
420
404
 
@@ -452,13 +436,16 @@ defineExpose({
452
436
 
453
437
  <template #clear>
454
438
  <div
455
- v-if="(Array.isArray(value) ? value.length > 0 : !!value) && !disabled && clearable"
439
+ v-if="hasValue && !disabled && clearable"
456
440
  class="multiselect-clear cursor-pointer"
457
441
  @mousedown.prevent.stop="value = null"
458
442
  >
459
- <VIcon class="icon" size="19">
460
- tabler-x
461
- </VIcon>
443
+ <VIcon
444
+ icon="tabler-x"
445
+ class="icon"
446
+ size="19"
447
+ :color="hasError(isValid.value) ? 'error' : ''"
448
+ />
462
449
  </div>
463
450
  </template>
464
451
 
@@ -472,9 +459,7 @@ defineExpose({
472
459
 
473
460
  <template v-if="isLoading" #beforeList>
474
461
  <div class="app-h-50px w-100">
475
- <LoadingBar
476
- :is-loading="isLoading"
477
- />
462
+ <LoadingBar :is-loading="isLoading" />
478
463
  </div>
479
464
  </template>
480
465
 
@@ -507,16 +492,22 @@ defineExpose({
507
492
  <style>
508
493
  .app-custom-input-teleport .multiselect__tags {
509
494
  display: none !important;
495
+ height: 100%;
496
+ display: flex;
497
+ align-items: center;
498
+ cursor: pointer;
499
+ padding-top: 0;
510
500
  }
511
501
  .app-custom-input-teleport .multiselect-arrow {
512
502
  display: none !important;
513
503
  }
514
504
  .app-custom-input-teleport .multiselect__placeholder {
515
- display: none !important;
505
+ margin: 0;
506
+ padding-top: 0;
516
507
  }
517
508
  .app-custom-input-teleport .multiselect__content-wrapper {
518
509
  margin-top: 53px;
519
- z-index: 9999;
510
+ z-index: 10;
520
511
  display: block !important;
521
512
  }
522
513
 
@@ -531,6 +522,9 @@ defineExpose({
531
522
  margin: 0;
532
523
  padding-top: 0;
533
524
  }
525
+ .app-teleport-clone .multiselect__content-wrapper {
526
+ display: none !important;
527
+ }
534
528
 
535
529
  .app-custom-input .multiselect__placeholder {
536
530
  display: flex;
@@ -550,7 +544,7 @@ defineExpose({
550
544
  font-size: 13px !important;
551
545
  background-color: rgba(var(--v-theme-surface));
552
546
  font-weight: 100 !important;
553
- min-height: 43px;
547
+ min-height: 40px;
554
548
  border-radius: 6px;
555
549
  }
556
550
  .app-custom-input .multiselect__tags:hover {
@@ -573,7 +567,6 @@ defineExpose({
573
567
  border-radius: 6px;
574
568
  background: rgba(var(--v-theme-surface));
575
569
  color: rgba(var(--v-theme-on-surface), var(--v-high-emphasis-opacity));
576
- z-index: 9999 !important;
577
570
  }
578
571
 
579
572
  .multiselect__input {
@@ -35,12 +35,14 @@ type __VLS_Props = {
35
35
  isLoading?: boolean;
36
36
  disableIfEmpty?: boolean;
37
37
  clearable?: boolean;
38
+ showNoOptions?: boolean;
39
+ showNoResults?: boolean;
38
40
  };
39
- declare var __VLS_71: string | number, __VLS_72: any, __VLS_136: string | number, __VLS_137: any;
41
+ declare var __VLS_69: string | number, __VLS_70: any, __VLS_134: string | number, __VLS_135: any;
40
42
  type __VLS_Slots = {} & {
41
- [K in NonNullable<typeof __VLS_71>]?: (props: typeof __VLS_72) => any;
43
+ [K in NonNullable<typeof __VLS_69>]?: (props: typeof __VLS_70) => any;
42
44
  } & {
43
- [K in NonNullable<typeof __VLS_136>]?: (props: typeof __VLS_137) => any;
45
+ [K in NonNullable<typeof __VLS_134>]?: (props: typeof __VLS_135) => any;
44
46
  };
45
47
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
46
48
  records: Ref<{
@@ -63,6 +63,18 @@ const handleInput = (e, index) => {
63
63
  updateModelAt(index, value);
64
64
  }
65
65
  };
66
+ const handleKeydown = (e, index) => {
67
+ if (e.key === "Delete" || e.key === "Del" || e.key === "Backspace") {
68
+ e.preventDefault();
69
+ const currentValue = model.value?.[index] ?? "";
70
+ if (!currentValue && index > 0) {
71
+ const prevInput = document.getElementById(`license-${props.customId || ""}-${index - 1}`);
72
+ prevInput?.focus();
73
+ } else if (currentValue) {
74
+ updateModelAt(index, "");
75
+ }
76
+ }
77
+ };
66
78
  const clear = () => {
67
79
  model.value = null;
68
80
  };
@@ -108,6 +120,7 @@ watch(() => model.value, (newVal) => {
108
120
  :maxlength="index === 3 ? 4 : 1"
109
121
  :placeholder="index === 3 ? t('inputs.numbers') : t('inputs.character')"
110
122
  @input="handleInput($event, index)"
123
+ @keydown="handleKeydown($event, index)"
111
124
  >
112
125
  <a
113
126
  v-if="model?.some(Boolean)"
@@ -127,8 +140,8 @@ watch(() => model.value, (newVal) => {
127
140
  v-if="!hideDetails"
128
141
  class="mt-1"
129
142
  color="error"
130
- :messages="errorMessages || validationErrors.value"
131
- :active="!isValid.value"
143
+ :messages="[...validationErrors.value, errorMessages]"
144
+ :active="isValid.value === false || errorMessages?.length !== 0"
132
145
  />
133
146
  </div>
134
147
  </VValidation>
@@ -246,7 +246,7 @@ defineExpose({
246
246
 
247
247
  <template #item.created_at="{ item }">
248
248
  <div class="d-flex text-pre-line">
249
- {{ formatDate(item.raw?.created_at) }}
249
+ {{ item?.created_at != "-" || item?.created_at ? formatDate(item?.created_at) : "-" }}
250
250
  </div>
251
251
  </template>
252
252
 
@@ -34,7 +34,8 @@ const moduleIdentifier = computed(() => props.module ?? "rows");
34
34
  item-value="id"
35
35
  class="secondary-table"
36
36
  >
37
- <template v-for="header in headers" #[`column.${header.key}`] :key="`header.${header.key}`">
37
+ <!-- headers -->
38
+ <template v-for="header in headers" #[`header.${header.key}`] :key="`header.${header.key}`">
38
39
  <div class="d-flex align-center gap-1">
39
40
  {{ header.title }}
40
41
 
@@ -42,7 +43,7 @@ const moduleIdentifier = computed(() => props.module ?? "rows");
42
43
 
43
44
  <VTooltip
44
45
  v-if="header.tooltip"
45
- content-class="bg-secondary !app-max-w-200px text-center app-font-size-12 text-pre-line"
46
+ :content-class="`text-center text-pre-line ${header.tooltipClass}`"
46
47
  >
47
48
  <template #activator="{ props: tooltipProps }">
48
49
  <div class="position-relative">
@@ -62,28 +63,12 @@ const moduleIdentifier = computed(() => props.module ?? "rows");
62
63
  </div>
63
64
  </template>
64
65
 
66
+ <!-- rows -->
65
67
  <template
66
68
  v-for="(header) in headers"
67
- #[`item.${header.key}`]="{ item, index }"
68
- :key="`row.${index}.${header.key}`"
69
+ #[`item.${header.key}`]="{ index }"
70
+ :key="`row.item.${index}.${header.key}`"
69
71
  >
70
- <span
71
- v-if="header.key === 'index'"
72
- :key="`row.${index}.${header.key}`"
73
- class="px-2 d-flex align-center justify-center h-100 app-border-silver"
74
- style="border-inline-start: 0px !important;"
75
- >
76
- {{ `${index + 1 < 10 ? "0" : ""}${index + 1}` }}
77
- </span>
78
-
79
- <span
80
- v-if="header.type === 'display'"
81
- :key="`row.${index}.${header.key}`"
82
- class="px-2 d-flex align-center justify-center h-100 app-border-silver"
83
- >
84
- {{ item[header.key] }}
85
- </span>
86
-
87
72
  <!-- eslint-disable vue/no-mutating-props -->
88
73
  <template v-if="header?.fields && header?.fields?.length > 0">
89
74
  <div
@@ -120,6 +105,7 @@ const moduleIdentifier = computed(() => props.module ?? "rows");
120
105
  <!-- eslint-enable vue/no-mutating-props -->
121
106
  </template>
122
107
 
108
+ <!-- other slots -->
123
109
  <template v-for="(_, name) in $slots" #[name]="slotProps">
124
110
  <slot :name="name" v-bind="slotProps || {}" />
125
111
  </template>
@@ -150,17 +136,15 @@ table tr th {
150
136
 
151
137
  .secondary-table :deep(table) {
152
138
  width: 100% !important;
153
- border-collapse: collapse !important;
154
139
  border-spacing: 0 !important;
155
140
  border: 0px solid rgb(var(--v-theme-silver)) !important;
156
- border-collapse: collapse !important;
157
141
  font-size: 13px !important;
158
142
  }
159
143
  .secondary-table :deep(table) thead th {
160
144
  border-top: 1px solid rgb(var(--v-theme-silver)) !important;
161
145
  border-left: 1px solid rgb(var(--v-theme-silver)) !important;
162
146
  border-right: 1px solid rgb(var(--v-theme-silver)) !important;
163
- border-bottom: 0px !important;
147
+ border-bottom: 0px solid rgb(var(--v-theme-silver)) !important;
164
148
  }
165
149
  .secondary-table :deep(table) tbody tr:first-child {
166
150
  display: none !important;
@@ -172,15 +156,15 @@ table tr th {
172
156
  padding: 14px 16px !important;
173
157
  height: fit-content !important;
174
158
  top: 80px !important;
175
- border: 0px solid rgb(var(--v-theme-silver)) !important;
159
+ border: 1px solid rgb(var(--v-theme-silver)) !important;
176
160
  background-color: rgb(var(--v-theme-gray-200)) !important;
177
161
  color: rgb(var(--v-theme-on-surface)) !important;
178
162
  }
179
163
  .secondary-table :deep(table) td:first-child {
180
- border-inline-start: 1px solid rgb(var(--v-theme-silver)) !important;
164
+ border: 1px solid rgb(var(--v-theme-silver)) !important;
181
165
  }
182
166
  .secondary-table :deep(table) td:last-child {
183
- border-top: 0px solid rgb(var(--v-theme-silver)) !important;
167
+ border: 1px solid rgb(var(--v-theme-silver)) !important;
184
168
  }
185
169
  .secondary-table :deep(table) td {
186
170
  padding: 0px !important;
@@ -264,7 +248,6 @@ table tr th {
264
248
  height: 100%;
265
249
  border-color: rgba(var(--v-theme-silver));
266
250
  border-radius: 0;
267
- padding-top: 0px !important;
268
251
  }
269
252
  :deep(.app-custom-input) .multiselect__tags .multiselect__single {
270
253
  margin-bottom: 0px;
@@ -312,11 +295,6 @@ table tr th {
312
295
  --v-field-border-opacity: 1 !important;
313
296
  }
314
297
 
315
- .app-input-table {
316
- min-width: 250px !important;
317
- max-width: 250px !important;
318
- }
319
-
320
298
  .app-custom-input-teleport .multiselect__tags {
321
299
  border-color: rgba(var(--v-theme-silver)) !important;
322
300
  border-radius: 0;
@@ -101,6 +101,7 @@ const placeholder = (header) => {
101
101
  flat
102
102
  hide-details
103
103
  />
104
+
104
105
  <AppNumberField
105
106
  v-if="header.type === 'number'"
106
107
  v-model="item[header.key]"
@@ -158,7 +159,6 @@ const placeholder = (header) => {
158
159
  :error-messages="errorMessages"
159
160
  :error="error"
160
161
  :disable-if-empty="header.disableIfEmpty"
161
- teleported
162
162
  hide-details
163
163
  :clearable="header.clearable"
164
164
  />
@@ -185,7 +185,6 @@ const placeholder = (header) => {
185
185
  :error="error"
186
186
  :disable-if-empty="header.disableIfEmpty"
187
187
  grouped
188
- teleported
189
188
  hide-details
190
189
  :clearable="header.clearable"
191
190
  />
@@ -249,4 +248,20 @@ const placeholder = (header) => {
249
248
  :error-messages="errorMessages"
250
249
  hide-details
251
250
  />
251
+
252
+ <span
253
+ v-if="header.key === 'index'"
254
+ :key="`row.${index}.${header.key}`"
255
+ class="px-2 d-flex align-center justify-center h-100"
256
+ >
257
+ {{ `${index + 1 < 10 ? "0" : ""}${index + 1}` }}
258
+ </span>
259
+
260
+ <span
261
+ v-if="header.type === 'display'"
262
+ :key="`row.${index}.${header.key}`"
263
+ class="px-2 d-flex align-center justify-center h-100"
264
+ >
265
+ {{ item[header.key] }}
266
+ </span>
252
267
  </template>
@@ -1040,3 +1040,13 @@ $btn-hover-overlay-opacity: (
1040
1040
  ::-webkit-resizer {
1041
1041
  background: transparent;
1042
1042
  }
1043
+
1044
+
1045
+ .v-tooltip > .v-overlay__content {
1046
+ color: rgb(var(--v-theme-on-secondary)) !important;
1047
+ font-size: 13px !important;
1048
+ }
1049
+
1050
+ .v-icon {
1051
+ outline: none !important;
1052
+ }
@@ -18768,6 +18768,15 @@ body {
18768
18768
  background: transparent;
18769
18769
  }
18770
18770
 
18771
+ .v-tooltip > .v-overlay__content {
18772
+ color: rgb(var(--v-theme-on-secondary)) !important;
18773
+ font-size: 13px !important;
18774
+ }
18775
+
18776
+ .v-icon {
18777
+ outline: none !important;
18778
+ }
18779
+
18771
18780
  .v-timeline-item .app-timeline-title {
18772
18781
  color: rgba(var(--v-theme-on-surface), var(--v-high-emphasis-opacity));
18773
18782
  font-size: 15px;
package/dist/types.d.ts CHANGED
@@ -124,6 +124,7 @@ export type EditableDataTableItem = {
124
124
  renderDataOnBeforeMount?: boolean
125
125
  showDescription?: boolean
126
126
  tooltip?: string
127
+ tooltipClass?: string
127
128
  clearable?: boolean
128
129
  disabled?: boolean
129
130
  maxlength?: number
@@ -4,4 +4,5 @@ export declare function formValidator(errors: ErrorResponseFiled[] | undefined,
4
4
  getMessage: () => string;
5
5
  hasError: () => boolean;
6
6
  };
7
- export declare function getErrorMessage(errors: ErrorResponseFiled[] | undefined, key: string): string;
7
+ export declare function getErrorMessage(errors: ErrorResponseFiled[] | undefined, key: string): string | undefined;
8
+ export declare function getErrorMessages(errors: ErrorResponseFiled[] | undefined, keys: string[]): string | undefined;
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.formValidator = formValidator;
7
7
  exports.getErrorMessage = getErrorMessage;
8
+ exports.getErrorMessages = getErrorMessages;
8
9
  function formValidator(errors = [], key) {
9
10
  const getError = () => errors?.find(e => e.field == key);
10
11
  const getMessage = () => getError()?.message || "";
@@ -16,5 +17,14 @@ function formValidator(errors = [], key) {
16
17
  };
17
18
  }
18
19
  function getErrorMessage(errors = [], key) {
19
- return errors?.find(e => e.field == key)?.message || "";
20
+ return errors?.find(e => e.field == key)?.message || void 0;
21
+ }
22
+ function getErrorMessages(errors = [], keys) {
23
+ for (const key of keys) {
24
+ const error = errors?.find(e => e.field == key);
25
+ if (error?.message) {
26
+ return error.message;
27
+ }
28
+ }
29
+ return void 0;
20
30
  }
@@ -9,5 +9,14 @@ export function formValidator(errors = [], key) {
9
9
  };
10
10
  }
11
11
  export function getErrorMessage(errors = [], key) {
12
- return errors?.find((e) => e.field == key)?.message || "";
12
+ return errors?.find((e) => e.field == key)?.message || void 0;
13
+ }
14
+ export function getErrorMessages(errors = [], keys) {
15
+ for (const key of keys) {
16
+ const error = errors?.find((e) => e.field == key);
17
+ if (error?.message) {
18
+ return error.message;
19
+ }
20
+ }
21
+ return void 0;
13
22
  }
@@ -7,3 +7,5 @@ export declare const buildFormData: <T>(data: T | T[], excludedKeys?: string[],
7
7
  export declare const stripLeadingZero: (mobile: string) => string;
8
8
  export declare const cloneObjectDeep: <T>(obj: T) => T;
9
9
  export declare const mergeDeep: (target: any, source: any) => any;
10
+ export declare const normalizeArabicText: (text: string | number) => string;
11
+ export declare const searchArabicText: (value: string | number, query: string) => boolean;
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.stripLeadingZero = exports.mergeDeep = exports.isNullOrUndefined = exports.isEmptyArray = exports.isEmpty = exports.generateUniqueId = exports.generateRandomId = exports.cloneObjectDeep = exports.buildFormData = void 0;
6
+ exports.stripLeadingZero = exports.searchArabicText = exports.normalizeArabicText = exports.mergeDeep = exports.isNullOrUndefined = exports.isEmptyArray = exports.isEmpty = exports.generateUniqueId = exports.generateRandomId = exports.cloneObjectDeep = exports.buildFormData = void 0;
7
7
  const generateRandomId = length => {
8
8
  const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
9
9
  let result = "";
@@ -81,4 +81,20 @@ const mergeDeep = (target, source) => {
81
81
  }
82
82
  return output;
83
83
  };
84
- exports.mergeDeep = mergeDeep;
84
+ exports.mergeDeep = mergeDeep;
85
+ const normalizeArabicText = text => {
86
+ if (typeof text === "number") {
87
+ return text.toString();
88
+ }
89
+ return text.trim().replace(/أ|ا|إ|آ/g, "\u0627").replace(/ي|ى|ئ/g, "\u064A").replace(/ة/g, "\u0647");
90
+ };
91
+ exports.normalizeArabicText = normalizeArabicText;
92
+ const searchArabicText = (value, query) => {
93
+ if (!value || !query) {
94
+ return false;
95
+ }
96
+ const normalizedValue = normalizeArabicText(value.toString()).toLowerCase();
97
+ const normalizedQuery = normalizeArabicText(query).toLowerCase();
98
+ return normalizedValue.includes(normalizedQuery);
99
+ };
100
+ exports.searchArabicText = searchArabicText;
@@ -65,3 +65,17 @@ export const mergeDeep = (target, source) => {
65
65
  }
66
66
  return output;
67
67
  };
68
+ export const normalizeArabicText = (text) => {
69
+ if (typeof text === "number") {
70
+ return text.toString();
71
+ }
72
+ return text.trim().replace(/أ|ا|إ|آ/g, "\u0627").replace(/ي|ى|ئ/g, "\u064A").replace(/ة/g, "\u0647");
73
+ };
74
+ export const searchArabicText = (value, query) => {
75
+ if (!value || !query) {
76
+ return false;
77
+ }
78
+ const normalizedValue = normalizeArabicText(value.toString()).toLowerCase();
79
+ const normalizedQuery = normalizeArabicText(query).toLowerCase();
80
+ return normalizedValue.includes(normalizedQuery);
81
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@winchsa/ui",
3
- "version": "0.1.36",
3
+ "version": "0.1.39",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "publishConfig": {