renusify 2.5.1 → 3.0.0

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.
Files changed (212) hide show
  1. package/components/app/index.vue +74 -22
  2. package/components/app/toast/index.vue +76 -71
  3. package/components/app/toast/toast.vue +62 -44
  4. package/components/avatar/index.vue +208 -84
  5. package/components/button/buttonConfirm.vue +53 -26
  6. package/components/button/buttonGroup.js +0 -2
  7. package/components/button/buttonGroup.vue +310 -62
  8. package/components/button/index.vue +584 -100
  9. package/components/calendar/index.js +0 -2
  10. package/components/calendar/index.vue +326 -262
  11. package/components/calendar/month.vue +64 -55
  12. package/components/calendar/year.vue +30 -25
  13. package/components/card/index.vue +139 -59
  14. package/components/codeEditor/highlightCss.vue +38 -39
  15. package/components/codeEditor/highlightHtml.vue +64 -64
  16. package/components/codeEditor/highlightJs.vue +37 -38
  17. package/components/codeEditor/index.vue +129 -79
  18. package/components/codeEditor/run.vue +225 -39
  19. package/components/codeEditor/useCodeFormatter.js +150 -0
  20. package/components/confirm/index.vue +140 -81
  21. package/components/container/col.vue +5 -4
  22. package/components/container/divider.vue +28 -19
  23. package/components/container/index.vue +34 -15
  24. package/components/container/row.vue +26 -9
  25. package/components/container/spacer.vue +2 -4
  26. package/components/container/style.scss +3 -0
  27. package/components/content/index.vue +49 -32
  28. package/components/cropper/index.vue +401 -244
  29. package/components/float/index.vue +542 -415
  30. package/components/form/addressInput/index.vue +184 -109
  31. package/components/form/camInput/index.vue +370 -244
  32. package/components/form/checkInput/index.vue +138 -71
  33. package/components/form/checkboxInput/index.vue +87 -47
  34. package/components/form/colorInput/Alpha.vue +81 -83
  35. package/components/form/colorInput/Hue.vue +91 -68
  36. package/components/form/colorInput/Preview.vue +43 -47
  37. package/components/form/colorInput/Saturation.vue +101 -86
  38. package/components/form/colorInput/index.vue +72 -40
  39. package/components/form/colorInput/picker.vue +111 -106
  40. package/components/form/colorInput/useColor.js +153 -0
  41. package/components/form/dateInput/index.vue +691 -356
  42. package/components/form/dateInput/month.vue +63 -54
  43. package/components/form/dateInput/year.vue +35 -25
  44. package/components/form/fileInput/index.js +0 -1
  45. package/components/form/fileInput/index.vue +263 -106
  46. package/components/form/fileInput/single.vue +323 -164
  47. package/components/form/groupInput/index.vue +199 -101
  48. package/components/form/index.vue +189 -83
  49. package/components/form/input/index.vue +416 -377
  50. package/components/form/jsonInput/JsonView.vue +54 -56
  51. package/components/form/jsonInput/index.vue +247 -165
  52. package/components/form/maskInput/index.vue +252 -132
  53. package/components/form/numberInput/index.js +0 -1
  54. package/components/form/numberInput/index.vue +226 -117
  55. package/components/form/passwordInput/index.js +2 -1
  56. package/components/form/passwordInput/index.vue +269 -102
  57. package/components/form/radioInput/index.vue +143 -72
  58. package/components/form/rangeInput/index.vue +280 -167
  59. package/components/form/ratingInput/index.vue +57 -57
  60. package/components/form/selectInput/index.js +1 -3
  61. package/components/form/selectInput/index.vue +584 -296
  62. package/components/form/switchInput/index.vue +73 -59
  63. package/components/form/telInput/index.js +0 -1
  64. package/components/form/telInput/index.vue +238 -135
  65. package/components/form/textArea/index.vue +72 -35
  66. package/components/form/textEditor/index.vue +739 -0
  67. package/components/form/{text-editor → textEditor}/style.scss +8 -16
  68. package/components/form/textInput/index.vue +54 -32
  69. package/components/form/timeInput/index.vue +83 -56
  70. package/components/form/timeInput/range.vue +116 -95
  71. package/components/form/timeInput/timepicker.vue +382 -449
  72. package/components/form/uniqueInput/index.vue +105 -48
  73. package/components/form/unitInput/index.vue +139 -84
  74. package/components/formCreator/index.js +0 -1
  75. package/components/formCreator/index.vue +314 -148
  76. package/components/highlight/index.vue +41 -25
  77. package/components/highlight/style.scss +2 -2
  78. package/components/highlight/{mixin.js → useHighlight.js} +181 -160
  79. package/components/icon/index.vue +79 -33
  80. package/components/img/index.vue +249 -147
  81. package/components/img/preview.vue +180 -198
  82. package/components/img/svgImg.vue +42 -39
  83. package/components/index.js +5 -20
  84. package/components/infinite/index.js +1 -2
  85. package/components/infinite/index.vue +248 -66
  86. package/components/map/index.vue +428 -261
  87. package/components/map/route.vue +794 -487
  88. package/components/map/select.vue +118 -58
  89. package/components/menu/index.vue +201 -91
  90. package/components/meta/meta.js +26 -3
  91. package/components/modal/index.vue +383 -158
  92. package/components/notify/index.vue +204 -86
  93. package/components/notify/notification.vue +38 -55
  94. package/components/progress/circle.vue +189 -70
  95. package/components/progress/line.vue +266 -46
  96. package/components/searchBox/index.js +1 -3
  97. package/components/searchBox/index.vue +194 -101
  98. package/components/skeleton/index.vue +45 -20
  99. package/components/slider/index.vue +318 -156
  100. package/components/swiper/index.vue +254 -106
  101. package/components/table/crud/footer.vue +77 -53
  102. package/components/table/crud/header.vue +71 -72
  103. package/components/table/crud/index.vue +631 -401
  104. package/components/table/index.vue +721 -278
  105. package/components/timeAgo/index.vue +145 -96
  106. package/components/tour/index.vue +338 -235
  107. package/components/tree/index.vue +235 -89
  108. package/components/tree/tree-element.vue +107 -106
  109. package/directive/animate/index.js +77 -0
  110. package/directive/clickOutSide/index.js +98 -0
  111. package/directive/drag/index.js +153 -0
  112. package/directive/index.js +11 -13
  113. package/directive/intersect/index.js +263 -0
  114. package/directive/mask/index.js +67 -0
  115. package/directive/parallax/index.js +78 -0
  116. package/directive/ripple/index.js +14 -0
  117. package/directive/scroll/index.js +244 -0
  118. package/directive/sortable/index.js +274 -0
  119. package/directive/title/index.js +75 -0
  120. package/directive/touch/index.js +268 -0
  121. package/index.js +10 -8
  122. package/package.json +5 -2
  123. package/plugins/validation/Validate.js +88 -79
  124. package/scripts/generate-docs.mjs +226 -0
  125. package/scripts/menu.mjs +240 -0
  126. package/scripts/parser.mjs +1086 -0
  127. package/style/_index.scss +7 -0
  128. package/style/app.scss +13 -65
  129. package/style/colors.scss +5 -22
  130. package/style/functions/index.scss +8 -0
  131. package/style/mixins/index.scss +17 -5
  132. package/style/variables/base.scss +154 -175
  133. package/style/variables/color.scss +0 -12
  134. package/style/variables/utilities.scss +0 -180
  135. package/tools/helper.js +0 -8
  136. package/tools/icons.js +6 -1
  137. package/tools/root.js +71 -0
  138. package/components/app/style.scss +0 -41
  139. package/components/app/toast/style.scss +0 -20
  140. package/components/avatar/style.scss +0 -32
  141. package/components/bar/bottomNav.js +0 -1
  142. package/components/bar/bottomNav.vue +0 -28
  143. package/components/bar/bottomNavigationCircle.js +0 -2
  144. package/components/bar/bottomNavigationCircle.vue +0 -99
  145. package/components/bar/scss/bottomNav.scss +0 -67
  146. package/components/bar/scss/toolbar.scss +0 -174
  147. package/components/bar/toolbar/index.js +0 -8
  148. package/components/bar/toolbar/index.vue +0 -35
  149. package/components/bar/toolbar/laptop.vue +0 -33
  150. package/components/bar/toolbar/menuChilds.vue +0 -41
  151. package/components/bar/toolbar/menuLaptop.vue +0 -41
  152. package/components/bar/toolbar/menuMob.vue +0 -39
  153. package/components/bar/toolbar/mixin.js +0 -43
  154. package/components/bar/toolbar/mobile.vue +0 -34
  155. package/components/breadcrumb/bredcrumbItem.vue +0 -39
  156. package/components/breadcrumb/index.js +0 -3
  157. package/components/breadcrumb/index.vue +0 -71
  158. package/components/breadcrumb/style.scss +0 -51
  159. package/components/button/style.scss +0 -411
  160. package/components/card/style.scss +0 -86
  161. package/components/chart/chart.js +0 -1
  162. package/components/chart/chart.vue +0 -69
  163. package/components/chart/worldMap.js +0 -2
  164. package/components/chart/worldMap.vue +0 -1112
  165. package/components/chat/MessageList.vue +0 -163
  166. package/components/chat/chatInput.vue +0 -150
  167. package/components/chat/chatMsg.vue +0 -276
  168. package/components/chat/index.js +0 -11
  169. package/components/chat/index.vue +0 -113
  170. package/components/chip/index.js +0 -3
  171. package/components/chip/index.vue +0 -77
  172. package/components/chip/style.scss +0 -199
  173. package/components/codeEditor/mixin.js +0 -145
  174. package/components/countdown/index.js +0 -1
  175. package/components/countdown/index.vue +0 -105
  176. package/components/form/colorInput/mixin.js +0 -132
  177. package/components/form/fileInput/file.js +0 -148
  178. package/components/form/telInput/assets/flags.png +0 -0
  179. package/components/form/telInput/assets/flags@2x.png +0 -0
  180. package/components/form/text-editor/index.vue +0 -710
  181. package/components/icon/style.scss +0 -17
  182. package/components/infinite/div.js +0 -6
  183. package/components/infinite/div.vue +0 -193
  184. package/components/infinite/page.js +0 -3
  185. package/components/infinite/page.vue +0 -105
  186. package/components/list/index.js +0 -3
  187. package/components/list/index.vue +0 -122
  188. package/components/list/style.scss +0 -66
  189. package/components/message/index.js +0 -4
  190. package/components/message/index.vue +0 -40
  191. package/components/modal/style.scss +0 -146
  192. package/components/nestable/NestableItem.vue +0 -307
  193. package/components/nestable/editable.js +0 -44
  194. package/components/nestable/index.js +0 -1
  195. package/components/nestable/index.vue +0 -226
  196. package/components/nestable/methods.js +0 -416
  197. package/components/progress/style.scss +0 -229
  198. package/components/table/style.scss +0 -338
  199. package/components/tabs/index.js +0 -3
  200. package/components/tabs/index.vue +0 -151
  201. package/components/timeline/index.js +0 -6
  202. package/components/timeline/index.vue +0 -76
  203. package/directive/resize/index.js +0 -30
  204. package/directive/skeleton/index.js +0 -27
  205. package/directive/skeleton/style.scss +0 -37
  206. package/plugins/request/Request.js +0 -68
  207. package/style/animation.scss +0 -94
  208. package/style/style.scss +0 -8
  209. package/tools/rootable.js +0 -75
  210. /package/components/form/{text-editor → textEditor}/index.js +0 -0
  211. /package/components/form/{text-editor → textEditor}/preview.js +0 -0
  212. /package/components/form/{text-editor → textEditor}/preview.vue +0 -0
@@ -1,356 +1,623 @@
1
1
  <template>
2
- <div :class="`${this.$r.prefix}select-container`" ref="select" v-click-outside="closeList">
3
- <r-input :active="active"
2
+ <div ref="selectRef" v-click-outside="showMode==='modal'?()=>{}:closeList"
3
+ :class="[`${$r.prefix}select-container`,{'open-to-top':openToTop}]">
4
+ <r-input :active="active||openList"
4
5
  v-bind="$attrs"
5
- :readonly="readonly"
6
- :modelValue="$helper.ifHas(chips,null,0,value)"
6
+ :modelValue="modelValue"
7
7
  @click.prevent="handleClick()">
8
- <div class="select-wrap v-center"
9
- :class="{
10
- 'h-center':disableSearch,
8
+ <div :class="{
11
9
  'flex-nowrap':!multiple
12
- }">
13
- <r-chip
10
+ }"
11
+ class="select-wrap v-center">
12
+ <span
14
13
  v-for="(item,key) in chips"
15
14
  :key="key"
16
- :class="{'px-0':!multiple}"
17
- :close="multiple&&!textChip"
18
- :text="textChip || !multiple"
19
- class="my-0 ms-0"
20
- selectable
21
- @update:modelValue="handleChip($event,key)">
15
+ :class="{'px-0':!multiple,'chip body-3':multiple}"
16
+ class=" ms-0 d-flex v-center">
22
17
  {{ item ? item[text] : '' }}
23
- </r-chip>
18
+ <r-icon v-if="multiple" class="chip-icon cursor-pointer ms-1" height="16" width="16"
19
+ @click="handleChip(false,key)"
20
+ v-html="$r.icons.close"></r-icon>
21
+
22
+ </span>
24
23
  <span>
25
- <input :type="type"
26
- v-if="!disableSearch"
27
- @focusin="focusInput(true)"
28
- @focusout="focusInput(false)"
29
- @keydown.enter="add"
24
+ <input v-if="!disableSearch"
25
+ ref="inputRef"
26
+ :autofocus="autofocus"
27
+ :placeholder="modelValue?'':placeholder"
28
+ :type="type"
29
+ :value="inputVal"
30
30
  autocomplete="no"
31
31
  class="select-input"
32
- :readonly="readonly"
33
- ref="input"
34
- :value="inputVal"
32
+ @focusin="focusInput(true)"
33
+ @focusout="focusInput(false)"
35
34
  @input="e => inputVal = e.target.value"
35
+ @keydown.enter="add"
36
36
  />
37
37
  </span>
38
38
  </div>
39
39
  <r-progress-line v-if="loading" color="color-two"></r-progress-line>
40
40
  </r-input>
41
- <r-card class="card-select" :class="{
42
- 'card-select-active':genItems.length>0 && openList,
43
- 'to-top':openToTop
44
- }"
45
- :style="{
46
- width:$refs.select&&$refs.select.getBoundingClientRect().width+'px'
41
+ <div v-if="showMode!=='modal'" :class="{
42
+ 'card-no-float':showMode==='noFloat',
43
+ 'card-select-active':genItems.length>0 && openList
44
+ }" :style="{
45
+ width:width+'px'
47
46
  }"
47
+ class="card-select"
48
48
  >
49
- <r-list :filter="!searchLink?(inputVal&&inputVal.trim()):''"
50
- :items="genItems"
51
- :multiple="multiple"
52
- :modelValue="chips"
53
- :text="text"
54
- :value="value"
55
- @update:modelValue="listInput"
56
- checked>
57
- <template v-slot="props">
58
- <slot :item="props.item">
59
- <div class="list-title">
60
- {{ props.item[text] }}
61
- </div>
62
- </slot>
49
+ <div v-for="(item,i) in genItems"
50
+ :key="i"
51
+ :class="{'card-item-active':$helper.searchArray(chips,text,item[text])!==false}"
52
+ class="card-item"
53
+ @click="handle_item(item)">
54
+ <!-- Default slot for list item. Provide item prop. -->
55
+ <slot :item="item">
56
+ {{ item[text] }}
63
57
  <transition name="fade">
64
- <r-icon class="pe-1" exact
58
+ <r-icon v-if="$helper.searchArray(chips,text,item[text])!==false" class="pe-1"
59
+ exact
65
60
  v-html="$r.icons.check"
66
- v-if="$helper.searchArray(props.list,text,props.item[text])!==false"
67
61
  ></r-icon>
68
62
  </transition>
69
- </template>
70
- </r-list>
71
- </r-card>
63
+ </slot>
64
+ </div>
65
+ </div>
66
+ <r-modal v-else v-model="openList" full-width position="bottom">
67
+ <div :class="`${$r.prefix}select-container`" class="pa-3">
68
+ <r-input v-if="!disableSearch" :active="true"
69
+ :modelValue="$helper.ifHas(chips,null,0,value)"
70
+ v-bind="$attrs"
71
+ @click.prevent="handleClick()">
72
+ <div :class="{
73
+ 'h-center':disableSearch,
74
+ 'flex-nowrap':!multiple
75
+ }"
76
+ class="select-wrap v-center">
77
+ <span
78
+ v-for="(item,key) in chips"
79
+ :key="key"
80
+ :class="{'px-0':!multiple,'chip body-3':multiple}"
81
+ class=" ms-0 d-flex v-center">
82
+ {{ item ? item[text] : '' }}
83
+ <r-icon v-if="multiple" class="chip-icon cursor-pointer ms-1" height="16" width="16"
84
+ @click="handleChip(false,key)"
85
+ v-html="$r.icons.close"></r-icon>
86
+
87
+ </span>
88
+ <span>
89
+ <input :type="type"
90
+ v-if="!disableSearch"
91
+ @focusin="focusInput(true)"
92
+ @focusout="focusInput(false)"
93
+ @keydown.enter="add"
94
+ autocomplete="no"
95
+ class="select-input"
96
+ :value="inputVal"
97
+ @input="e => inputVal = e.target.value"
98
+ />
99
+ </span>
100
+ </div>
101
+ <r-progress-line v-if="loading" color="color-two"></r-progress-line>
102
+ </r-input>
103
+ <div v-for="(item,i) in genItems"
104
+ :key="i"
105
+ :class="{'card-item-active':$helper.searchArray(chips,text,item[text])!==false}"
106
+ class="card-item"
107
+ @click="handle_item(item)">
108
+ <!-- Default slot for list item. Provide item prop. -->
109
+ <slot :item="item">
110
+ {{ item[text] }}
111
+ <transition name="fade">
112
+ <r-icon v-if="$helper.searchArray(chips,text,item[text])!==false" class="pe-1"
113
+ exact
114
+ v-html="$r.icons.check"
115
+ ></r-icon>
116
+ </transition>
117
+ </slot>
118
+ </div>
119
+ </div>
120
+ </r-modal>
72
121
  </div>
73
122
  </template>
74
- <script>
75
-
76
- export default {
77
- name: 'r-select',
78
- inheritAttrs: false,
79
- props: {
80
- searchLink: String,
81
- type: {
82
- type: String,
83
- default: 'text'
84
- },
85
- text: {
86
- type: String,
87
- default: 'name'
88
- },
89
- value: {
90
- type: String,
91
- default: 'value'
92
- },
93
- disableSearch: Boolean,
94
- readonly: Boolean,
95
- textChip: Boolean,
96
- items: Array,
97
- modelValue: [String, Number, Array, Object],
98
- tags: Boolean,
99
- multiple: Boolean,
100
- justValue: Boolean,
101
- openToTop: Boolean,
102
- translate: Boolean,
103
- firstSelect: Boolean,
104
- headers: Object
123
+ <script setup>
124
+ import {ref, computed, watch, onMounted, inject, nextTick, useAttrs} from 'vue'
125
+
126
+ defineOptions({
127
+ inheritAttrs: false
128
+ })
129
+
130
+ const attr = useAttrs()
131
+ const placeholder = attr.placeholder
132
+ const autofocus = attr.autofocus
133
+
134
+ const props = defineProps({
135
+ /**
136
+ * API endpoint URL for fetching search results
137
+ * @type {String}
138
+ */
139
+ searchLink: String,
140
+ /**
141
+ * Input type for the select component
142
+ * @type {String}
143
+ * @default 'text'
144
+ */
145
+ type: {
146
+ type: String,
147
+ default: 'text'
105
148
  },
106
- emits: ['update:modelValue', 'del'],
107
- data() {
108
- return {
109
- apiData: [],
110
- loading: false,
111
- active: false,
112
- openList: false,
113
- inputVal: null,
114
- chips: []
115
- }
149
+ /**
150
+ * Property name for display text in items
151
+ * @type {String}
152
+ * @default 'name'
153
+ */
154
+ text: {
155
+ type: String,
156
+ default: 'name'
116
157
  },
117
- mounted() {
118
- this.chips = this.getValue()
119
- if (this.firstSelect & this.chips.length === 0) {
120
- this.chips.push(this.genItems[0])
121
- this.emitVal()
122
- }
123
- if (this.searchLink) {
124
- this.get()
125
- }
158
+ /**
159
+ * Property name for value in items
160
+ * @type {String}
161
+ * @default 'value'
162
+ */
163
+ value: {
164
+ type: String,
165
+ default: 'value'
126
166
  },
127
- computed: {
128
- genItems() {
129
- let res = []
130
- if (this.apiData.length > 0) {
131
- res = this.apiData
132
- }
167
+ /**
168
+ * Disable search functionality
169
+ * @type {Boolean}
170
+ */
171
+ disableSearch: Boolean,
172
+ /**
173
+ * Static array of select items
174
+ * @type {Array}
175
+ */
176
+ items: Array,
177
+ /**
178
+ * The selected value(s)
179
+ * @type {String|Number|Array|Object}
180
+ */
181
+ modelValue: [String, Number, Array, Object],
182
+ /**
183
+ * Enable tag mode (allow custom values not in items)
184
+ * @type {Boolean}
185
+ */
186
+ tags: Boolean,
187
+ /**
188
+ * Enable multiple selection
189
+ * @type {Boolean}
190
+ */
191
+ multiple: Boolean,
192
+ /**
193
+ * Emit only values instead of full objects
194
+ * @type {Boolean}
195
+ */
196
+ justValue: Boolean,
197
+ /**
198
+ * Open dropdown above the input
199
+ * @type {Boolean}
200
+ */
201
+ openToTop: Boolean,
202
+ /**
203
+ * Enable translation for item texts
204
+ * @type {Boolean}
205
+ */
206
+ translate: Boolean,
207
+ /**
208
+ * Automatically select first item when empty
209
+ * @type {Boolean}
210
+ */
211
+ firstSelect: Boolean,
212
+ /**
213
+ * Disable floating label
214
+ * @type {Boolean}
215
+ */
216
+ noFloat: Boolean,
217
+ /**
218
+ * Force modal display on all screen sizes
219
+ * @type {Boolean}
220
+ */
221
+ modal: Boolean,
222
+ /**
223
+ * Auto display mode based on screen size
224
+ * @type {String}
225
+ * @default 'modal'
226
+ * @validator ['modal', 'float', 'none']
227
+ */
228
+ auto: {type: String, default: 'modal', validator: value => ['modal', 'float', 'none'].includes(value)},
229
+ /**
230
+ * Additional headers for API requests
231
+ * @type {Object}
232
+ */
233
+ headers: Object
234
+ })
133
235
 
134
- if (this.items) {
135
- res = Object.assign([], this.items)
136
- }
236
+ const emit = defineEmits([
237
+ /**
238
+ * Emitted when selected value(s) change
239
+ * @param {String|Number|Array|Object} value - Updated selected value(s)
240
+ */
241
+ 'update:modelValue',
242
+ /**
243
+ * Emitted when a chip/tag is deleted
244
+ * @param {Array} deletedItem - Array containing [index, deletedItem]
245
+ */
246
+ 'del'
247
+ ])
137
248
 
138
- if (typeof res[0] !== 'object') {
139
- for (let i in res) {
140
- if (this.$helper.hasKey(res, i)) {
141
- let v = {}
142
- v[this.text] = res[i].toString()
143
- v[this.value] = res[i]
144
- res[i] = v
145
- }
146
- }
147
- }
148
- if (this.translate) {
149
- for (let i in res) {
150
- if (this.$helper.hasKey(res, i)) {
151
- res[i][this.text] = this.$t(res[i][this.text])
152
- }
153
- }
154
- }
155
- return res
249
+ const {$helper, $t, $r} = inject('renusify')
250
+ const $axios = inject('axios')
251
+
252
+ // Reactive data
253
+ const apiData = ref([])
254
+ const loading = ref(false)
255
+ const active = ref(false)
256
+ const openList = ref(false)
257
+ const inputVal = ref(null)
258
+ const width = ref(100)
259
+ const chips = ref([])
260
+
261
+ // Template refs
262
+ const selectRef = ref(null)
263
+ const inputRef = ref(null)
264
+
265
+ // Computed properties
266
+ /**
267
+ * Determines display mode based on props and screen size
268
+ * @returns {String} Display mode: 'modal', 'noFloat', or 'none'
269
+ */
270
+ const showMode = computed(() => {
271
+ if ($r.breakpoint.mdAndDown) {
272
+ if (props.auto === 'modal') {
273
+ return 'modal'
156
274
  }
157
- },
158
- watch: {
159
- 'inputVal': function (newVal) {
160
- if (this.searchLink) {
161
- if (newVal && newVal.length > 0) {
162
- this.get()
163
- }
164
- }
165
- },
166
- 'modelValue': function (n) {
167
- this.chips = this.getValue()
275
+ if (props.auto === 'float') {
276
+ return 'noFloat'
168
277
  }
169
- },
170
- methods: {
171
- get() {
172
- this.loading = true
173
- return this.$axios.get(this.searchLink, {
174
- params: {
175
- s: (this.inputVal === null ? '' : this.inputVal)
176
- },
177
- headers: this.headers
178
- }).then(({data}) => {
179
- this.apiData = data
180
- this.loading = false
181
- }, () => {
182
- this.loading = false
183
- })
184
- },
185
- getValue() {
186
- if (!this.searchLink && !this.tags && this.modelValue !== undefined && this.modelValue !== null) {
187
- if (typeof this.modelValue === 'string' || typeof this.modelValue === 'number') {
188
- const index = this.$helper.searchArray(this.genItems, this.value, this.modelValue)
278
+ } else {
279
+ if (props.modal) {
280
+ return 'modal'
281
+ }
282
+ if (props.noFloat) {
283
+ return 'noFloat'
284
+ }
285
+ }
286
+ return 'none'
287
+ })
288
+
289
+ /**
290
+ * Generates and filters items for display
291
+ * @returns {Array} Processed and filtered items array
292
+ */
293
+ const genItems = computed(() => {
294
+ let res = []
295
+
296
+ if (apiData.value.length > 0) {
297
+ res = apiData.value
298
+ }
299
+
300
+ if (props.items) {
301
+ res = [...(res || []), ...props.items]
302
+ }
303
+
304
+ // Convert primitive values to objects
305
+ if (res.length > 0 && typeof res[0] !== 'object') {
306
+ res = res.map(item => ({
307
+ [props.text]: item.toString(),
308
+ [props.value]: item
309
+ }))
310
+ }
311
+
312
+ // Apply translation if enabled
313
+ if (props.translate) {
314
+ res = res.map(item => ({
315
+ ...item,
316
+ [props.text]: $t(item[props.text])
317
+ }))
318
+ }
319
+
320
+ // Filter by search term if not using API search
321
+ if (!props.searchLink && inputVal.value) {
322
+ const searchTerm = inputVal.value.trim()
323
+ return res.filter(el => {
324
+ return el[props.text] && el[props.text].toLowerCase().includes(searchTerm.toLowerCase())
325
+ })
326
+ }
327
+
328
+ return res
329
+ })
330
+
331
+ // Methods
332
+ /**
333
+ * Fetches data from API search endpoint
334
+ */
335
+ const get = async () => {
336
+ if (!props.searchLink) return
337
+
338
+ loading.value = true
339
+ try {
340
+ const {data} = await $axios.get(props.searchLink, {
341
+ params: {
342
+ s: inputVal.value || ''
343
+ },
344
+ headers: props.headers
345
+ })
346
+ apiData.value = data
347
+ } catch (error) {
348
+ console.error('Error fetching select data:', error)
349
+ } finally {
350
+ loading.value = false
351
+ }
352
+ }
353
+
354
+ /**
355
+ * Converts modelValue to chip format
356
+ * @returns {Array} Array of chip objects
357
+ */
358
+ const getValue = () => {
359
+ // Validate modelValue against available items if not using tags
360
+ if (!props.searchLink && !props.tags && props.modelValue !== undefined && props.modelValue !== null) {
361
+ if (typeof props.modelValue === 'string' || typeof props.modelValue === 'number') {
362
+ const index = $helper?.searchArray(genItems.value, props.value, props.modelValue)
363
+ if (index === false) {
364
+ emit('update:modelValue', null)
365
+ return []
366
+ }
367
+ } else if (Array.isArray(props.modelValue)) {
368
+ for (const item of props.modelValue) {
369
+ if (typeof item === 'string' || typeof item === 'number') {
370
+ const index = $helper?.searchArray(genItems.value, props.value, item)
189
371
  if (index === false) {
190
- this.$emit('update:modelValue', null)
372
+ emit('update:modelValue', null)
191
373
  return []
192
374
  }
193
- } else if (this.$helper.isArray(this.modelValue)) {
194
- this.modelValue.forEach((item) => {
195
- if (typeof item === 'string' || typeof item === 'number') {
196
- const index = this.$helper.searchArray(this.genItems, this.value, item)
197
- if (index === false) {
198
- this.$emit('update:modelValue', null)
199
- return []
200
- }
201
- } else {
202
- const index = this.$helper.searchArray(this.genItems, this.value, item[this.value])
203
- if (index === false) {
204
- this.$emit('update:modelValue', null)
205
- return []
206
- }
207
- }
208
- })
209
375
  } else {
210
- const index = this.$helper.searchArray(this.genItems, this.value, this.modelValue[this.value])
376
+ const index = $helper?.searchArray(genItems.value, props.value, item[props.value])
211
377
  if (index === false) {
212
- this.$emit('update:modelValue', null)
378
+ emit('update:modelValue', null)
213
379
  return []
214
380
  }
215
381
  }
216
382
  }
217
- if (this.modelValue !== undefined && this.modelValue !== null) {
218
- if (this.$helper.ifHas(this.modelValue, false, this.text)) {
219
- return [this.modelValue]
220
- } else if (typeof this.modelValue === 'string' || typeof this.modelValue === 'number') {
221
- const index = this.$helper.searchArray(this.genItems, this.value, this.modelValue)
222
- if (index !== false) {
223
- return [this.genItems[index]]
224
- }
225
- return [{
226
- [this.text]: this.modelValue.toString(), [this.value]: this.modelValue
227
- }]
228
- } else if (typeof this.modelValue[0] === 'string' || typeof this.modelValue[0] === 'number') {
229
- let res = []
230
- const lng = this.modelValue.length
231
- for (let i = 0; i < lng; i++) {
232
- const index = this.$helper.searchArray(this.genItems, this.value, this.modelValue[i])
233
- if (index !== false) {
234
- res.push(this.genItems[index])
235
- } else {
236
- res.push({[this.text]: this.modelValue[i].toString(), [this.value]: this.modelValue[i]})
237
- }
238
- }
239
- return res
240
- } else {
241
- return this.modelValue
242
- }
383
+ } else {
384
+ const index = $helper?.searchArray(genItems.value, props.value, props.modelValue[props.value])
385
+ if (index === false) {
386
+ emit('update:modelValue', null)
387
+ return []
243
388
  }
244
- this.$emit('update:modelValue', null)
245
- return []
246
- },
247
- handleChip(e, key) {
248
- if (e === false) {
249
- this.$emit('del', [key, this.chips[key]])
250
- this.chips.splice(key, 1)
251
- this.emitVal()
252
- } else {
253
- this.handleClick()
254
- }
255
- },
256
- handleClick() {
257
- if (this.$refs.input) {
258
- this.$refs.input.focus()
259
- this.$refs.select.scrollIntoView({
260
- block: "start",
261
- behavior: "smooth"
262
- })
263
- } else {
264
- this.focusInput(true)
265
- }
266
- },
267
- add() {
268
- if (this.inputVal) {
269
- let val = {[this.text]: this.inputVal.toString(), [this.value]: this.inputVal}
270
- if (!this.multiple) {
271
- this.chips = []
272
- }
273
- if (!this.tags) {
274
- const exist = this.$helper.searchArray(this.genItems, this.text, val[this.text])
275
- if (exist !== false) {
276
- this.chips.push(val)
277
- }
278
- } else {
279
- this.chips.push(val)
280
- }
281
- this.inputVal = null
389
+ }
390
+ }
282
391
 
283
- this.emitVal()
392
+ // Convert modelValue to chip format
393
+ if (props.modelValue !== undefined && props.modelValue !== null) {
394
+ if ($helper?.ifHas(props.modelValue, false, props.text)) {
395
+ return [props.modelValue]
396
+ } else if (typeof props.modelValue === 'string' || typeof props.modelValue === 'number') {
397
+ const index = $helper?.searchArray(genItems.value, props.value, props.modelValue)
398
+ if (index !== false) {
399
+ return [genItems.value[index]]
284
400
  }
285
- },
286
- emitVal() {
287
- this.chips = this.$helper.uniqArray(this.chips)
288
-
289
- let val = this.chips
290
- if (this.justValue) {
291
- val = []
292
- for (let i in this.chips) {
293
- if (this.$helper.hasKey(this.chips, i)) {
294
- val.push(this.chips[i][this.value])
295
- }
401
+ return [{
402
+ [props.text]: props.modelValue.toString(),
403
+ [props.value]: props.modelValue
404
+ }]
405
+ } else if (Array.isArray(props.modelValue) && (typeof props.modelValue[0] === 'string' || typeof props.modelValue[0] === 'number')) {
406
+ return props.modelValue.map(item => {
407
+ const index = $helper?.searchArray(genItems.value, props.value, item)
408
+ if (index !== false) {
409
+ return genItems.value[index]
296
410
  }
297
- }
298
- if (!this.multiple) {
299
- val = val[0]
300
- if (val) {
301
- this.closeList()
411
+ return {
412
+ [props.text]: item.toString(),
413
+ [props.value]: item
302
414
  }
303
- }
304
- if (val === undefined) {
305
- val = null
306
- }
415
+ })
416
+ } else {
417
+ return Array.isArray(props.modelValue) ? props.modelValue : [props.modelValue]
418
+ }
419
+ }
307
420
 
308
- this.$emit('update:modelValue', val)
421
+ emit('update:modelValue', null)
422
+ return []
423
+ }
309
424
 
310
- },
311
- focusInput(val) {
312
- if (this.readonly) {
313
- return
314
- }
315
- this.active = val
425
+ /**
426
+ * Handles chip deletion or click
427
+ * @param {Boolean} e - False if deleting, otherwise clicking
428
+ * @param {Number} key - Chip index
429
+ */
430
+ const handleChip = (e, key) => {
431
+ if (e === false) {
432
+ emit('del', [key, chips.value[key]])
433
+ chips.value.splice(key, 1)
434
+ emitVal()
435
+ } else {
436
+ handleClick()
437
+ }
438
+ }
316
439
 
317
- if (val === true) {
318
- this.openList = true
319
- }
320
- setTimeout(() => {
321
- this.add()
322
- }, 200)
323
- },
324
- listInput(e) {
325
- this.inputVal = null
326
- if (e) {
327
- this.chips = this.multiple ? e : [e]
328
-
329
- } else {
330
- this.chips = []
440
+ /**
441
+ * Handles input click to open dropdown
442
+ */
443
+ const handleClick = () => {
444
+ if (selectRef.value) {
445
+ nextTick(() => {
446
+ width.value = selectRef.value.getBoundingClientRect().width
447
+ })
448
+ }
449
+ if (inputRef.value) {
450
+ inputRef.value.focus()
451
+ } else {
452
+ focusInput(true)
453
+ }
454
+ }
455
+
456
+ /**
457
+ * Adds custom tag from input value
458
+ */
459
+ const add = () => {
460
+ if (inputVal.value) {
461
+ const val = {
462
+ [props.text]: inputVal.value.toString(),
463
+ [props.value]: inputVal.value
464
+ }
465
+
466
+ if (!props.multiple) {
467
+ chips.value = []
468
+ }
469
+
470
+ if (!props.tags) {
471
+ const exist = $helper?.searchArray(genItems.value, props.text, val[props.text])
472
+ if (exist !== false) {
473
+ chips.value.push(val)
331
474
  }
475
+ } else {
476
+ chips.value.push(val)
477
+ }
478
+
479
+ inputVal.value = null
480
+ emitVal()
481
+ }
482
+ }
332
483
 
333
- this.emitVal()
334
- },
335
- closeList() {
336
- this.active = false
337
- this.openList = false
484
+ /**
485
+ * Emits updated value to parent
486
+ */
487
+ const emitVal = () => {
488
+ chips.value = $helper?.uniqArray(chips.value) || chips.value
338
489
 
490
+ let val = chips.value
491
+ if (props.justValue) {
492
+ val = chips.value.map(chip => chip[props.value])
493
+ }
494
+
495
+ if (!props.multiple) {
496
+ val = val[0]
497
+ if (val) {
498
+ closeList()
339
499
  }
340
500
  }
501
+
502
+ if (val === undefined) {
503
+ val = null
504
+ }
505
+
506
+ emit('update:modelValue', val)
341
507
  }
342
508
 
509
+ /**
510
+ * Focuses input and opens dropdown
511
+ * @param {Boolean} val - Whether to focus/open
512
+ */
513
+ const focusInput = (val) => {
514
+ active.value = val
515
+ if (val === true) {
516
+ openList.value = true
517
+ }
518
+
519
+ setTimeout(() => {
520
+ add()
521
+ }, 200)
522
+ }
523
+
524
+ /**
525
+ * Handles item selection from dropdown
526
+ * @param {Object} item - Selected item
527
+ */
528
+ const handle_item = (item) => {
529
+ const index = $helper?.searchArray(chips.value, props.text, item[props.text])
530
+
531
+ if (index !== false) {
532
+ chips.value.splice(index, 1)
533
+ } else {
534
+ if (!props.multiple) {
535
+ chips.value = []
536
+ }
537
+ chips.value.push(item)
538
+ }
539
+
540
+ inputVal.value = null
541
+ if (!props.multiple && chips.value.length > 0) {
542
+ chips.value = [chips.value[0]]
543
+ }
544
+
545
+ emitVal()
546
+ }
547
+
548
+ /**
549
+ * Closes dropdown list
550
+ */
551
+ const closeList = () => {
552
+ active.value = false
553
+ openList.value = false
554
+ }
555
+
556
+ // Watchers
557
+ watch(inputVal, (newVal) => {
558
+ if (props.searchLink && newVal && newVal.length > 0) {
559
+ get()
560
+ }
561
+ })
562
+
563
+ watch(() => props.modelValue, (n) => {
564
+ chips.value = getValue()
565
+ })
566
+
567
+ // Lifecycle hooks
568
+ onMounted(() => {
569
+ chips.value = getValue()
570
+
571
+ if (props.firstSelect && chips.value.length === 0 && genItems.value.length > 0) {
572
+ chips.value.push(genItems.value[0])
573
+ emitVal()
574
+ }
575
+
576
+ if (props.searchLink) {
577
+ get()
578
+ }
579
+
580
+ if (selectRef.value) {
581
+ nextTick(() => {
582
+ width.value = selectRef.value.getBoundingClientRect().width
583
+ })
584
+ }
585
+ })
343
586
  </script>
344
587
  <style lang="scss">
345
588
  @use "sass:map";
346
- @use "../../../style/variables/base";
589
+ @use "../../../style" as *;
347
590
 
348
591
 
349
592
  $min-height: 40px;
350
- .#{base.$prefix}select-container {
593
+ .#{$prefix}select-container {
351
594
  position: relative;
352
595
  scroll-margin: $min-height;
353
596
 
597
+ &.open-to-top {
598
+ display: flex;
599
+ flex-direction: column-reverse;
600
+ }
601
+
602
+ .chip {
603
+ background: var(--color-sheet);
604
+ color: var(--color-on-sheet);
605
+ border-radius: 50px;
606
+ margin: 4px;
607
+ @include rtl() {
608
+ padding: 2px 8px 2px 4px;
609
+ }
610
+ @include ltr() {
611
+ padding: 2px 4px 2px 8px;
612
+ }
613
+
614
+ .chip-icon {
615
+ background: var(--color-sheet-container);
616
+ border-radius: 50%;
617
+ padding: 2px;
618
+ }
619
+ }
620
+
354
621
  .input-control {
355
622
  min-height: $min-height;
356
623
  height: auto;
@@ -378,7 +645,7 @@ $min-height: 40px;
378
645
  position: relative;
379
646
  }
380
647
 
381
- .#{base.$prefix}chip {
648
+ .#{$prefix}chip {
382
649
  flex: 0 1 auto;
383
650
  margin: 4px;
384
651
  }
@@ -387,18 +654,39 @@ $min-height: 40px;
387
654
  max-height: 0;
388
655
  overflow: auto;
389
656
  opacity: .5;
390
- transition: all .3s base.$primary-transition;
391
- position: absolute;
657
+ transition: all .3s $primary-transition;
392
658
  left: 0;
393
- z-index: map.get(base.$z-index, 'default');
659
+ z-index: map.get($z-index, 'default');
660
+ background: var(--color-sheet-container);
661
+ color: var(--color-on-sheet);
662
+
663
+ &:not(.card-no-float) {
664
+ position: absolute;
665
+ }
666
+ }
667
+
668
+ &.open-to-top {
669
+ .card-select {
670
+ bottom: $min-height+2px;
671
+ }
672
+ }
673
+
674
+
675
+ .card-item {
676
+ display: flex;
677
+ justify-content: space-between;
678
+ padding: 12px;
679
+ cursor: pointer;
680
+ border-bottom: 1px solid var(--color-sheet-container-low);
394
681
  }
395
682
 
396
- .to-top {
397
- bottom: $min-height+2px;
683
+ .card-item-active {
684
+ background: var(--color-one-container);
685
+ color: var(--color-on-one-container);
398
686
  }
399
687
 
400
688
  .card-select-active {
401
- transition: all 0.3s base.$primary-transition;
689
+ transition: all 0.3s $primary-transition;
402
690
  max-height: 300px;
403
691
  opacity: 1;
404
692
  }