@fishawack/lab-velocity 2.0.0-beta.6 → 2.0.0-beta.60

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 (111) hide show
  1. package/README.md +467 -36
  2. package/_Build/js/libs/build-id.js +14 -0
  3. package/_Build/js/libs/filters.js +36 -0
  4. package/_Build/js/libs/globals.js +7 -0
  5. package/_Build/js/libs/router.js +22 -0
  6. package/_Build/js/libs/routes.js +29 -0
  7. package/_Build/js/libs/store.js +21 -0
  8. package/_Build/js/libs/utility.js +161 -0
  9. package/_Build/vue/components/basic/Button.vue +1 -1
  10. package/_Build/vue/components/form/Avatar.vue +90 -0
  11. package/_Build/vue/components/form/Checkbox.vue +10 -0
  12. package/_Build/vue/components/form/InputNumber.vue +1 -1
  13. package/_Build/vue/components/form/Select.vue +221 -31
  14. package/_Build/vue/components/form/Wysiwyg2.vue +87 -2
  15. package/_Build/vue/components/form/wysiwyg2-video.js +41 -0
  16. package/_Build/vue/components/layout/Alert.vue +5 -5
  17. package/_Build/vue/components/layout/Audit.vue +158 -0
  18. package/_Build/vue/{modules/AuthModule/components/VBreadcrumbs.vue → components/layout/Breadcrumbs.vue} +4 -4
  19. package/_Build/vue/{modules/AuthModule/components → components/layout}/Chips.vue +2 -2
  20. package/_Build/vue/components/layout/Footer.vue +11 -10
  21. package/_Build/vue/{modules/AuthModule/components/VFormFooter.vue → components/layout/FormFooter.vue} +2 -2
  22. package/_Build/vue/{modules/AuthModule/components → components/layout}/FormRole.vue +8 -8
  23. package/_Build/vue/components/layout/Layout.vue +94 -0
  24. package/_Build/vue/components/layout/Navigation.vue +83 -0
  25. package/_Build/vue/{modules/AuthModule/components/VPageHeader.vue → components/layout/PageHeader.vue} +14 -8
  26. package/_Build/vue/components/layout/SideBar.vue +26 -0
  27. package/_Build/vue/{modules/AuthModule/components/VTable.vue → components/layout/Table.vue} +24 -12
  28. package/_Build/vue/{modules/AuthModule/components/VTableSorter.vue → components/layout/TableSorter.vue} +92 -48
  29. package/_Build/vue/components/layout/TokenDisplay.vue +52 -0
  30. package/_Build/vue/components/layout/pageTitle.vue +1 -1
  31. package/_Build/vue/components/navigation/Menu.vue +6 -1
  32. package/_Build/vue/components/navigation/MenuItem.vue +7 -2
  33. package/_Build/vue/components/navigation/MenuItemGroup.vue +7 -2
  34. package/_Build/vue/modules/AuthModule/js/axios.js +21 -1
  35. package/_Build/vue/modules/AuthModule/js/clear-cookies.js +34 -0
  36. package/_Build/vue/modules/AuthModule/js/guest-request.js +32 -0
  37. package/_Build/vue/modules/AuthModule/js/impersonation-banner.js +102 -0
  38. package/_Build/vue/modules/AuthModule/js/router.js +91 -114
  39. package/_Build/vue/modules/AuthModule/js/store.js +23 -6
  40. package/_Build/vue/modules/AuthModule/routes/PCompanies/columns.js +268 -0
  41. package/_Build/vue/modules/AuthModule/routes/PCompanies/resource.js +213 -0
  42. package/_Build/vue/modules/AuthModule/routes/PIntegrations/columns.js +58 -0
  43. package/_Build/vue/modules/AuthModule/routes/PIntegrations/resource.js +79 -0
  44. package/_Build/vue/modules/AuthModule/routes/PTeams/columns.js +78 -0
  45. package/_Build/vue/modules/AuthModule/routes/PTeams/resource.js +251 -0
  46. package/_Build/vue/modules/AuthModule/routes/PUsers/SetPasswordAction.vue +51 -0
  47. package/_Build/vue/modules/AuthModule/routes/PUsers/SetPasswordDialog.vue +138 -0
  48. package/_Build/vue/modules/AuthModule/routes/PUsers/columns.js +349 -0
  49. package/_Build/vue/modules/AuthModule/routes/PUsers/resource.js +239 -0
  50. package/_Build/vue/modules/AuthModule/routes/account-exists.vue +2 -2
  51. package/_Build/vue/modules/AuthModule/routes/change-password.vue +28 -32
  52. package/_Build/vue/modules/AuthModule/routes/container.vue +2 -11
  53. package/_Build/vue/modules/AuthModule/routes/expired-reset.vue +4 -4
  54. package/_Build/vue/modules/AuthModule/routes/expired-verification.vue +10 -9
  55. package/_Build/vue/modules/AuthModule/routes/force-reset.vue +44 -58
  56. package/_Build/vue/modules/AuthModule/routes/forgot.vue +10 -5
  57. package/_Build/vue/modules/AuthModule/routes/login.vue +12 -19
  58. package/_Build/vue/modules/AuthModule/routes/logincallback.vue +1 -3
  59. package/_Build/vue/modules/AuthModule/routes/loginsso.vue +14 -10
  60. package/_Build/vue/modules/AuthModule/routes/logout.vue +14 -5
  61. package/_Build/vue/modules/AuthModule/routes/logoutheadless.vue +16 -5
  62. package/_Build/vue/modules/AuthModule/routes/register.vue +24 -28
  63. package/_Build/vue/modules/AuthModule/routes/reset.vue +20 -14
  64. package/_Build/vue/modules/AuthModule/routes/success-forgot.vue +14 -8
  65. package/_Build/vue/modules/AuthModule/routes/success-reset.vue +2 -2
  66. package/_Build/vue/modules/AuthModule/routes/success-verify.vue +1 -3
  67. package/_Build/vue/modules/AuthModule/routes/verify.vue +11 -14
  68. package/_Build/vue/modules/resource/Children/create.vue +99 -0
  69. package/_Build/vue/modules/resource/Children/edit.vue +124 -0
  70. package/_Build/vue/modules/resource/Children/index.vue +42 -0
  71. package/_Build/vue/modules/resource/Children/partials/form.vue +111 -0
  72. package/_Build/vue/modules/resource/Children/show.vue +170 -0
  73. package/_Build/vue/modules/resource/index.js +562 -0
  74. package/_Build/vue/modules/resource/parent.vue +63 -0
  75. package/_Build/vue/modules/resource/trashable.js +104 -0
  76. package/_base.scss +0 -1
  77. package/_defaults.scss +2 -2
  78. package/components/_auth.scss +19 -61
  79. package/components/_datepicker.scss +1 -0
  80. package/components/_descriptions.scss +2 -0
  81. package/components/_footer.scss +1 -0
  82. package/components/_form.scss +18 -0
  83. package/components/_header.scss +3 -27
  84. package/components/_layout.scss +56 -0
  85. package/components/_menu.scss +0 -5
  86. package/components/_sidebar.scss +12 -27
  87. package/components/_table.scss +3 -0
  88. package/components/_token-display.scss +41 -0
  89. package/components/_wysiwyg2.scss +10 -0
  90. package/general.scss +1 -0
  91. package/index.js +31 -1
  92. package/package.json +7 -4
  93. package/_Build/vue/components/layout/sideBar.vue +0 -25
  94. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/Upload/upload.vue +0 -259
  95. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/create.vue +0 -62
  96. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/edit.vue +0 -98
  97. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/index.vue +0 -90
  98. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/partials/form.vue +0 -181
  99. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/Children/show.vue +0 -266
  100. package/_Build/vue/modules/AuthModule/adminRoutes/PCompanies/parent.vue +0 -36
  101. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/create.vue +0 -112
  102. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/edit.vue +0 -103
  103. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/index.vue +0 -112
  104. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/partials/form.vue +0 -173
  105. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/Children/show.vue +0 -123
  106. package/_Build/vue/modules/AuthModule/adminRoutes/PUsers/parent.vue +0 -36
  107. package/components/_input.scss +0 -0
  108. /package/_Build/vue/{modules/AuthModule/components → components/layout}/AuthModal.vue +0 -0
  109. /package/_Build/vue/{modules/AuthModule/components → components/layout}/Chip.vue +0 -0
  110. /package/_Build/vue/{modules/AuthModule/components/VPasswordValidation.vue → components/layout/PasswordValidation.vue} +0 -0
  111. /package/_Build/vue/{modules/AuthModule/components/VRoleLegend.vue → components/layout/RoleLegend.vue} +0 -0
@@ -0,0 +1,161 @@
1
+ export function eachNode(nodes, cb) {
2
+ nodes = document.querySelectorAll(nodes);
3
+
4
+ for (var i = 0, len = nodes.length; i < len; i++) {
5
+ cb(nodes[i], i);
6
+ }
7
+ }
8
+
9
+ export function parse_query_string(query) {
10
+ var vars = query.split("&");
11
+ var query_string = {};
12
+ for (var i = 0; i < vars.length; i++) {
13
+ var pair = vars[i].split("=");
14
+ // If first entry with this name
15
+ if (typeof query_string[pair[0]] === "undefined") {
16
+ query_string[pair[0]] = decodeURIComponent(pair[1]);
17
+ // If second entry with this name
18
+ } else if (typeof query_string[pair[0]] === "string") {
19
+ var arr = [query_string[pair[0]], decodeURIComponent(pair[1])];
20
+ query_string[pair[0]] = arr;
21
+ // If third or later entry with this name
22
+ } else {
23
+ query_string[pair[0]].push(decodeURIComponent(pair[1]));
24
+ }
25
+ }
26
+ return query_string;
27
+ }
28
+
29
+ export function load(path, mimetype, pages, index, arr) {
30
+ return new Promise((resolve) => {
31
+ index = index || 1;
32
+
33
+ var xobj = new XMLHttpRequest();
34
+ if (mimetype) {
35
+ xobj.overrideMimeType(mimetype);
36
+ }
37
+
38
+ xobj.open(
39
+ "GET",
40
+ pages ? `${path}?per_page=100&page=${index}` : path,
41
+ true,
42
+ );
43
+ xobj.onreadystatechange = function () {
44
+ if (
45
+ xobj.readyState === 4 &&
46
+ (+xobj.status === 200 || +xobj.status === 0)
47
+ ) {
48
+ var data = JSON.parse(xobj.responseText);
49
+
50
+ if (!pages) {
51
+ resolve(data);
52
+ } else {
53
+ var current = +xobj.getResponseHeader("x-wp-totalpages");
54
+
55
+ if (!arr) {
56
+ arr = data || [];
57
+ } else {
58
+ arr = arr.concat(data);
59
+ }
60
+
61
+ if (current && current !== index) {
62
+ load(path, mimetype, pages, ++index, arr).then((res) =>
63
+ resolve(res),
64
+ );
65
+ } else {
66
+ resolve(arr);
67
+ }
68
+ }
69
+ }
70
+ };
71
+ xobj.send(null);
72
+ });
73
+ }
74
+
75
+ export function classList(el) {
76
+ var list = el.classList;
77
+
78
+ return {
79
+ toggle: function (c) {
80
+ list.toggle(c);
81
+ return this;
82
+ },
83
+ add: function (c) {
84
+ list.add(c);
85
+ return this;
86
+ },
87
+ remove: function (c) {
88
+ list.remove(c);
89
+ return this;
90
+ },
91
+ };
92
+ }
93
+
94
+ var blueprints = {};
95
+ export function blueprint(selector, array, cb, root, node) {
96
+ var nodes;
97
+
98
+ if (!node) {
99
+ nodes = (root || document).querySelectorAll(selector);
100
+ } else {
101
+ nodes = [node];
102
+ }
103
+
104
+ var blueprint;
105
+
106
+ if (blueprints[selector]) {
107
+ blueprint = blueprints[selector];
108
+ } else {
109
+ blueprint = nodes[0].children[0];
110
+ blueprint.classList.remove("ut-hide");
111
+ blueprints[selector] = blueprint;
112
+ }
113
+
114
+ var docFrag = document.createDocumentFragment();
115
+
116
+ array.forEach(function (d, i, arr) {
117
+ var item = blueprint.cloneNode(true);
118
+
119
+ cb(item, d, i, arr);
120
+
121
+ docFrag.appendChild(item);
122
+ });
123
+
124
+ for (var i = nodes.length; i--; ) {
125
+ nodes[i].innerHTML = "";
126
+
127
+ nodes[i].appendChild(docFrag);
128
+ }
129
+ }
130
+
131
+ export function styleguide() {
132
+ let colors = [].slice
133
+ .call(document.styleSheets[0].cssRules)
134
+ .filter((d) => d.selectorText && d.selectorText.includes(".color-"))
135
+ .map((d) => d.selectorText);
136
+
137
+ let length = 0;
138
+ /* jshint ignore:start */
139
+ while (colors.find((d) => d === `.color-${length}`)) {
140
+ length++;
141
+ }
142
+ /* jshint ignore:end */
143
+ eachNode(".js-styleguide-dynamic", (node) => {
144
+ let name = node.dataset.styleguideDynamic;
145
+ blueprint(
146
+ name,
147
+ Array(length).fill(0),
148
+ (item, d, i) => {
149
+ let text = item.querySelector(".js-styleguide-index");
150
+ if (text) {
151
+ text.innerText = i;
152
+ }
153
+ classList(item.querySelector(`.${name}`) || item)
154
+ .remove(name)
155
+ .add(`${name}${i}`);
156
+ },
157
+ null,
158
+ node,
159
+ );
160
+ });
161
+ }
@@ -34,7 +34,7 @@ export default {
34
34
  },
35
35
  type: {
36
36
  type: String,
37
- default: null,
37
+ default: "default",
38
38
  },
39
39
  name: {
40
40
  type: String,
@@ -0,0 +1,90 @@
1
+ <template>
2
+ <XInput v-bind="$props">
3
+ <template #label>
4
+ <slot name="label" />
5
+ </template>
6
+
7
+ <el-upload
8
+ action="#"
9
+ :auto-upload="false"
10
+ :show-file-list="false"
11
+ accept="image/*"
12
+ :on-change="onFileChange"
13
+ >
14
+ <img
15
+ v-if="imgSrc"
16
+ :src="imgSrc"
17
+ style="
18
+ max-width: 150px;
19
+ max-height: 150px;
20
+ display: block;
21
+ cursor: pointer;
22
+ margin-bottom: 0.5rem;
23
+ "
24
+ />
25
+ <div v-else style="margin-bottom: 0.5rem">
26
+ <slot name="placeholder">
27
+ <el-button type="primary" plain> Upload Avatar </el-button>
28
+ </slot>
29
+ </div>
30
+ </el-upload>
31
+ <div v-if="content" style="margin-top: 0.5rem">
32
+ <el-button type="danger" size="small" @click="cancel">
33
+ Clear
34
+ </el-button>
35
+ </div>
36
+ </XInput>
37
+ </template>
38
+
39
+ <script>
40
+ import { ElUpload, ElButton } from "element-plus";
41
+ import input from "./input.js";
42
+ import XInput from "./input.vue";
43
+
44
+ export default {
45
+ mixins: [input],
46
+
47
+ components: { XInput, ElUpload, ElButton },
48
+
49
+ props: {
50
+ ...input.props,
51
+ baseClass: {
52
+ type: String,
53
+ default: "vel-basic",
54
+ },
55
+ preview: {
56
+ type: String,
57
+ default: null,
58
+ },
59
+ },
60
+
61
+ data() {
62
+ return {
63
+ imgSrc: null,
64
+ };
65
+ },
66
+
67
+ mounted() {
68
+ this.imgSrc = this.preview;
69
+ },
70
+
71
+ watch: {
72
+ preview(val) {
73
+ if (!this.content) this.imgSrc = val;
74
+ },
75
+ },
76
+
77
+ methods: {
78
+ onFileChange(uploadFile) {
79
+ this.content = uploadFile.raw;
80
+ this.imgSrc = URL.createObjectURL(uploadFile.raw);
81
+ this.handleInput();
82
+ },
83
+ cancel() {
84
+ this.content = null;
85
+ this.imgSrc = this.preview;
86
+ this.handleInput();
87
+ },
88
+ },
89
+ };
90
+ </script>
@@ -6,6 +6,8 @@
6
6
  :disabled="disabled"
7
7
  v-model="content"
8
8
  :required="required"
9
+ :true-label="trueValue"
10
+ :false-label="falseValue"
9
11
  @change="handleInput"
10
12
  >
11
13
  </el-checkbox>
@@ -29,6 +31,14 @@ export default {
29
31
  type: String,
30
32
  default: "vel-checkbox",
31
33
  },
34
+ trueValue: {
35
+ type: [String, Number, Boolean],
36
+ default: true,
37
+ },
38
+ falseValue: {
39
+ type: [String, Number, Boolean],
40
+ default: false,
41
+ },
32
42
  },
33
43
 
34
44
  components: {
@@ -20,7 +20,7 @@
20
20
  :placeholder="placeholder"
21
21
  v-model="content"
22
22
  :required="required"
23
- @input="handleInput"
23
+ @change="handleInput"
24
24
  >
25
25
  <template #prefix>
26
26
  <slot name="prefix" />
@@ -5,6 +5,7 @@
5
5
  </template>
6
6
 
7
7
  <el-select
8
+ ref="select"
8
9
  v-model="content"
9
10
  :class="baseClass"
10
11
  :multiple="multiple"
@@ -14,32 +15,35 @@
14
15
  :collapse-tags="collapseTags"
15
16
  :filterable="filterable"
16
17
  :value-on-clear="null"
17
- @change="handleInput"
18
- @clear="this.$emit('clear')"
19
- @blur="this.$emit('blur')"
18
+ remote-show-suffix
19
+ :remote="!!endpoint"
20
+ :remote-method="endpoint ? handleSearch : undefined"
21
+ :loading="initialLoading"
20
22
  :empty-values="[null, undefined]"
23
+ :value-key="effectiveValueKey"
24
+ @change="handleInput"
25
+ @clear="$emit('clear')"
26
+ @blur="$emit('blur')"
27
+ @visible-change="handleVisibleChange"
21
28
  >
22
29
  <template #default>
23
30
  <slot name="default">
24
31
  <el-option
25
- v-if="!options[0]?.label"
26
- v-for="(label, value) in options"
27
- :key="value"
28
- :label="label"
29
- :value="castValue(value)"
30
- >
31
- </el-option>
32
- <el-option
33
- v-else
34
- v-for="option in options"
35
- :key="option.value"
36
- :label="option.label"
32
+ v-for="option in currentOptions"
33
+ :key="option[effectiveValueKey]"
34
+ :label="option[labelKey]"
37
35
  :disabled="option.disabled"
38
- :value="castValue(option.value)"
36
+ :value="option"
39
37
  >
40
38
  </el-option>
41
39
  </slot>
42
40
  </template>
41
+ <template v-if="endpoint" #loading>
42
+ <div class="el-select-dropdown__loading">Loading...</div>
43
+ </template>
44
+ <template #label="{ value }">
45
+ {{ value[labelKey] || value }}
46
+ </template>
43
47
  </el-select>
44
48
  </XInput>
45
49
  </template>
@@ -47,12 +51,21 @@
47
51
  <script>
48
52
  import { ElSelect } from "element-plus";
49
53
  import { ElOption } from "element-plus";
54
+ import { ElNotification } from "element-plus";
50
55
  import input from "./input.js";
51
56
  import XInput from "./input.vue";
52
57
  import _ from "lodash";
58
+ import axios from "axios";
53
59
 
54
60
  export default {
61
+ components: {
62
+ XInput,
63
+ ElOption,
64
+ ElSelect,
65
+ },
66
+
55
67
  mixins: [input],
68
+
56
69
  props: {
57
70
  ...input.props,
58
71
  modelValue: {
@@ -77,32 +90,209 @@ export default {
77
90
  },
78
91
  options: {
79
92
  type: Array,
80
- default: [],
93
+ default: () => [],
81
94
  },
82
95
  multiple: {
83
96
  type: Boolean,
84
97
  default: false,
85
98
  },
99
+ endpoint: {
100
+ type: String,
101
+ default: null,
102
+ },
103
+ params: {
104
+ type: Object,
105
+ default: () => ({}),
106
+ },
107
+ labelKey: {
108
+ type: String,
109
+ default: "label",
110
+ },
111
+ valueKey: {
112
+ type: String,
113
+ default: null,
114
+ },
115
+ searchParam: {
116
+ type: String,
117
+ default: "name",
118
+ },
119
+ perPage: {
120
+ type: Number,
121
+ default: 10,
122
+ },
86
123
  },
87
124
 
88
- components: {
89
- XInput,
90
- ElOption,
91
- ElSelect,
125
+ emits: ["change", "clear", "blur"],
126
+
127
+ data() {
128
+ return {
129
+ initialLoading: false,
130
+ loadingMore: false,
131
+ asyncOptions: [],
132
+ currentPage: 1,
133
+ hasMore: false,
134
+ searchQuery: "",
135
+ lastSearchQuery: "",
136
+ initialLoadDone: false,
137
+ scrollListener: null,
138
+ wrapScrollTop: 0,
139
+ wrapScrollLeft: 0,
140
+ };
141
+ },
142
+
143
+ computed: {
144
+ currentOptions() {
145
+ // If endpoint is provided, use async options, otherwise use static options
146
+ if (this.endpoint) {
147
+ return this.asyncOptions;
148
+ }
149
+ return this.options;
150
+ },
151
+ effectiveValueKey() {
152
+ // If valueKey was explicitly provided, use it
153
+ if (this.valueKey !== null) {
154
+ return this.valueKey;
155
+ }
156
+ // Otherwise, use "id" for endpoints, "value" for static options
157
+ return this.endpoint ? "id" : "value";
158
+ },
159
+ },
160
+
161
+ mounted() {
162
+ if (this.endpoint && this.$refs.select?.$refs?.scrollbarRef) {
163
+ const scrollbar = this.$refs.select.$refs.scrollbarRef;
164
+ const wrapRef = scrollbar.wrapRef;
165
+
166
+ this.scrollListener = () => {
167
+ if (!wrapRef) return;
168
+
169
+ const distance = 10;
170
+ const prevTop = this.wrapScrollTop;
171
+ const prevLeft = this.wrapScrollLeft;
172
+
173
+ this.wrapScrollTop = wrapRef.scrollTop;
174
+ this.wrapScrollLeft = wrapRef.scrollLeft;
175
+
176
+ const arrivedStates = {
177
+ bottom:
178
+ this.wrapScrollTop + wrapRef.clientHeight >=
179
+ wrapRef.scrollHeight - distance,
180
+ top: this.wrapScrollTop <= distance && prevTop !== 0,
181
+ right:
182
+ this.wrapScrollLeft + wrapRef.clientWidth >=
183
+ wrapRef.scrollWidth - distance &&
184
+ prevLeft !== this.wrapScrollLeft,
185
+ left: this.wrapScrollLeft <= distance && prevLeft !== 0,
186
+ };
187
+
188
+ let direction = null;
189
+ if (prevTop !== this.wrapScrollTop) {
190
+ direction = this.wrapScrollTop > prevTop ? "bottom" : "top";
191
+ }
192
+ if (prevLeft !== this.wrapScrollLeft) {
193
+ direction =
194
+ this.wrapScrollLeft > prevLeft ? "right" : "left";
195
+ }
196
+
197
+ if (direction && arrivedStates[direction]) {
198
+ this.handleEndReached(direction);
199
+ }
200
+ };
201
+
202
+ wrapRef.addEventListener("scroll", this.scrollListener);
203
+ }
204
+ },
205
+
206
+ beforeUnmount() {
207
+ if (
208
+ this.scrollListener &&
209
+ this.$refs.select?.$refs?.scrollbarRef?.wrapRef
210
+ ) {
211
+ this.$refs.select.$refs.scrollbarRef.wrapRef.removeEventListener(
212
+ "scroll",
213
+ this.scrollListener,
214
+ );
215
+ }
92
216
  },
93
217
 
94
- emits: ["change", "clear", "blur"],
95
218
  methods: {
96
- castValue(value) {
97
- if (
98
- !_.isNull(value) &&
99
- typeof value === "string" &&
100
- value !== "" &&
101
- !isNaN(Number(value))
102
- ) {
103
- return parseInt(value);
219
+ async handleVisibleChange(visible) {
220
+ // Load data when dropdown is opened for the first time
221
+ if (visible && this.endpoint && !this.initialLoadDone) {
222
+ await this.handleSearch("");
223
+ }
224
+ },
225
+
226
+ async handleSearch(query) {
227
+ if (!this.endpoint) return;
228
+
229
+ // For initial load, allow empty query even if it matches lastSearchQuery
230
+ if (!this.initialLoadDone || query !== this.lastSearchQuery) {
231
+ this.searchQuery = query;
232
+ this.lastSearchQuery = query;
233
+ this.currentPage = 1;
234
+ this.asyncOptions = [];
235
+ await this.fetchOptions(1, query);
236
+
237
+ if (!this.initialLoadDone) {
238
+ this.initialLoadDone = true;
239
+ }
240
+ }
241
+ },
242
+
243
+ handleEndReached(direction) {
244
+ if (!this.endpoint) return;
245
+ if (direction !== "bottom") return;
246
+ if (!this.hasMore || this.loadingMore) return;
247
+ this.fetchOptions(this.currentPage + 1, this.searchQuery);
248
+ },
249
+
250
+ async fetchOptions(page, searchQuery = "") {
251
+ const isInitialLoad = page === 1;
252
+ const loadingKey = isInitialLoad ? "initialLoading" : "loadingMore";
253
+
254
+ if (this[loadingKey]) return;
255
+
256
+ this[loadingKey] = true;
257
+
258
+ try {
259
+ const requestParams = {
260
+ ...this.params,
261
+ page,
262
+ per_page: this.perPage,
263
+ };
264
+
265
+ // Add search filter if query is provided
266
+ if (searchQuery) {
267
+ requestParams[`filter[${this.searchParam}]`] = searchQuery;
268
+ }
269
+
270
+ const response = await axios.get(this.endpoint, {
271
+ params: requestParams,
272
+ });
273
+
274
+ const data = response.data.data || [];
275
+ const meta = response.data.meta;
276
+
277
+ // If it's the first page, replace options, otherwise append
278
+ if (page === 1) {
279
+ this.asyncOptions = data;
280
+ } else {
281
+ this.asyncOptions = [...this.asyncOptions, ...data];
282
+ }
283
+
284
+ this.currentPage = page;
285
+ this.hasMore = meta && meta.current_page < meta.last_page;
286
+ } catch (error) {
287
+ console.error("Error fetching select options:", error);
288
+ ElNotification.error({
289
+ title: "Error",
290
+ message: "Failed to load options. Please try again.",
291
+ duration: 5000,
292
+ });
293
+ } finally {
294
+ this[loadingKey] = false;
104
295
  }
105
- return value;
106
296
  },
107
297
  },
108
298
  };