shared-ritm 1.2.52 → 1.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shared-ritm",
3
- "version": "1.2.52",
3
+ "version": "1.2.53",
4
4
  "private": false,
5
5
  "files": [
6
6
  "dist",
@@ -3,6 +3,7 @@
3
3
  {{ label }}
4
4
  <span v-if="rules?.length && isShowRequired" class="required">*</span>
5
5
  </label>
6
+
6
7
  <q-select
7
8
  ref="select"
8
9
  v-model="selected"
@@ -51,7 +52,20 @@
51
52
  </template>
52
53
 
53
54
  <template #no-option>
54
- <q-item>{{ emptyText }}</q-item>
55
+ <div class="q-pa-sm">
56
+ <q-item>
57
+ <q-item-section class="wrapper-empty-text">
58
+ {{ emptyText }}
59
+ <button
60
+ v-if="allowCreate && internalSearch && internalSearch.trim()"
61
+ class="add-new-items"
62
+ @click="handleCreateFromSearch"
63
+ >
64
+ Добавить
65
+ </button>
66
+ </q-item-section>
67
+ </q-item>
68
+ </div>
55
69
  </template>
56
70
 
57
71
  <template #option="scope">
@@ -67,7 +81,6 @@
67
81
 
68
82
  <script setup lang="ts">
69
83
  import { computed, defineEmits, defineProps, ref, Ref } from 'vue'
70
-
71
84
  type Option = Record<string, any>
72
85
 
73
86
  interface AppQSelectProps {
@@ -82,6 +95,7 @@ interface AppQSelectProps {
82
95
  loading?: boolean
83
96
  isShowRequired?: boolean
84
97
  isDisabled?: boolean
98
+ allowCreate?: boolean
85
99
  isSearch?: boolean
86
100
  showChip?: boolean
87
101
  chipColor?: string
@@ -93,10 +107,11 @@ interface AppQSelectProps {
93
107
  }
94
108
 
95
109
  const props = defineProps<AppQSelectProps>()
96
- const emit = defineEmits(['update:modelValue', 'update:scroll', 'update:search', 'clear'])
110
+ const emit = defineEmits(['update:modelValue', 'update:scroll', 'update:search', 'clear', 'create'])
97
111
 
98
112
  const select = ref({})
99
113
  const lcText: Ref<string> = ref('')
114
+ const internalSearch = ref('')
100
115
 
101
116
  const selected = computed({
102
117
  get() {
@@ -115,7 +130,12 @@ function handleClear() {
115
130
  }
116
131
 
117
132
  const filteredOptions = computed(() => {
118
- const baseOptions = props.options.filter(x => x[props?.optionLabel || 'label'].toLowerCase().includes(lcText.value))
133
+ const labelKey = props.optionLabel || 'label'
134
+
135
+ const baseOptions = props.options.filter(x => {
136
+ const label = x[labelKey]
137
+ return typeof label === 'string' && label.toLowerCase().includes(lcText.value)
138
+ })
119
139
 
120
140
  if (props.loading) {
121
141
  return [
@@ -131,11 +151,27 @@ const filteredOptions = computed(() => {
131
151
  return baseOptions
132
152
  })
133
153
 
154
+ function handleCreateFromSearch() {
155
+ const labelKey = props.optionLabel || 'label'
156
+ const valueKey = props.optionValue || 'value'
157
+
158
+ const trimmed = internalSearch.value?.trim()
159
+ if (!trimmed) return
160
+
161
+ const newOption = {
162
+ [labelKey]: trimmed,
163
+ [valueKey]: trimmed,
164
+ }
165
+ emit('create', newOption)
166
+ handleClear()
167
+ }
168
+
134
169
  function filterFn(val: string, update: (cb: () => void) => void) {
135
170
  debouncedFilter(val, update)
136
171
  }
137
172
 
138
173
  const debouncedFilter = debounce((val: string, update: (cb: () => void) => void) => {
174
+ internalSearch.value = val
139
175
  emit('update:search', val)
140
176
  update(() => {
141
177
  lcText.value = val.toLowerCase()
@@ -151,10 +187,8 @@ function onScroll({ to, ref: qSelectRef }) {
151
187
 
152
188
  function debounce<T>(fn: T, ms) {
153
189
  let timeoutId
154
-
155
190
  return function (...args) {
156
191
  clearTimeout(timeoutId)
157
-
158
192
  return new Promise(resolve => {
159
193
  timeoutId = setTimeout(() => {
160
194
  resolve(fn(...args))
@@ -165,6 +199,21 @@ function debounce<T>(fn: T, ms) {
165
199
  </script>
166
200
 
167
201
  <style lang="scss" scoped>
202
+ .wrapper-empty-text {
203
+ display: flex;
204
+ align-items: center;
205
+ justify-content: space-between;
206
+ flex-direction: row;
207
+ .add-new-items {
208
+ background: #3f8cff;
209
+ color: #fff;
210
+ outline: none;
211
+ border: none;
212
+ border-radius: 4px;
213
+ padding: 5px 10px;
214
+ cursor: pointer;
215
+ }
216
+ }
168
217
  .field-label {
169
218
  font-size: 14px;
170
219
  font-weight: 700;