slim-select 3.1.0 → 3.1.1
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/README.md +4 -3
- package/dist/index.d.ts +2 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.js +358 -324
- package/dist/react/index.umd.js +4 -4
- package/dist/slimselect.cjs.js +1 -1
- package/dist/slimselect.cjs.js.map +1 -1
- package/dist/slimselect.es.js +54 -38
- package/dist/slimselect.es.js.map +1 -1
- package/dist/slimselect.iife.js +1 -1
- package/dist/slimselect.iife.js.map +1 -1
- package/dist/slimselect.js +1 -1
- package/dist/slimselect.js.map +1 -1
- package/dist/vue/index.d.ts +2 -0
- package/dist/vue/index.js +67 -35
- package/dist/vue/index.umd.js +1 -1
- package/package.json +2 -1
- package/src/slim-select/index.test.ts +356 -0
- package/src/slim-select/index.ts +4 -2
- package/src/slim-select/react/react.test.tsx +498 -0
- package/src/slim-select/react/react.tsx +102 -5
- package/src/slim-select/select.test.ts +234 -0
- package/src/slim-select/select.ts +45 -0
- package/src/slim-select/vue/vue.test.ts +259 -0
- package/src/slim-select/vue/vue.vue +85 -25
- package/vitest.config.ts +3 -2
|
@@ -59,32 +59,15 @@ export default defineComponent({
|
|
|
59
59
|
config.events = {
|
|
60
60
|
...this.events,
|
|
61
61
|
afterChange: (newVal: Option[]) => {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
// Update v-model if value changed
|
|
65
|
-
if (this.value !== value) {
|
|
66
|
-
this.value = value
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Call the original afterChange callback if it exists
|
|
70
|
-
if (ogAfterChange) {
|
|
71
|
-
ogAfterChange(newVal)
|
|
72
|
-
}
|
|
62
|
+
this.handleAfterChange(newVal, ogAfterChange)
|
|
73
63
|
}
|
|
74
64
|
}
|
|
75
65
|
|
|
76
66
|
// Initialize SlimSelect
|
|
77
67
|
this.slim = new SlimSelect(config)
|
|
78
68
|
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
// Check if modelValue is the same as the value of the select
|
|
83
|
-
if (this.value !== selected) {
|
|
84
|
-
// If not, set the value of the select to the modelValue
|
|
85
|
-
// Don't trigger afterChange on initial sync
|
|
86
|
-
this.slim.setSelected(this.value, false)
|
|
87
|
-
}
|
|
69
|
+
// Sync initial modelValue to SlimSelect
|
|
70
|
+
this.syncModelValueToSlimSelect(false)
|
|
88
71
|
},
|
|
89
72
|
updated() {
|
|
90
73
|
// After DOM updates (like slot content changes), sync selection from modelValue
|
|
@@ -95,7 +78,8 @@ export default defineComponent({
|
|
|
95
78
|
const needsSync = JSON.stringify(currentSelected.sort()) !== JSON.stringify(modelValues.sort())
|
|
96
79
|
|
|
97
80
|
if (needsSync) {
|
|
98
|
-
|
|
81
|
+
// Use the same sync method to handle invalid values properly
|
|
82
|
+
this.syncModelValueToSlimSelect(false)
|
|
99
83
|
}
|
|
100
84
|
}
|
|
101
85
|
},
|
|
@@ -108,10 +92,8 @@ export default defineComponent({
|
|
|
108
92
|
modelValue: {
|
|
109
93
|
handler: function (newVal: string | string[] | undefined) {
|
|
110
94
|
if (!this.slim) return
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
// Don't trigger afterChange when programmatically updating from parent
|
|
114
|
-
this.slim.setSelected(this.getCleanValue(newVal), false)
|
|
95
|
+
// Sync modelValue to SlimSelect (don't trigger afterChange when programmatically updating from parent)
|
|
96
|
+
this.syncModelValueToSlimSelect(false)
|
|
115
97
|
}
|
|
116
98
|
},
|
|
117
99
|
data: {
|
|
@@ -138,6 +120,43 @@ export default defineComponent({
|
|
|
138
120
|
getSlimSelect() {
|
|
139
121
|
return this.slim
|
|
140
122
|
},
|
|
123
|
+
handleAfterChange(newVal: Option[], ogAfterChange?: (newVal: Option[]) => void): void {
|
|
124
|
+
if (!this.slim) return
|
|
125
|
+
|
|
126
|
+
const value = this.multiple ? newVal.map((option) => option.value) : newVal.length > 0 ? newVal[0].value : ''
|
|
127
|
+
|
|
128
|
+
// Check if the current v-model value exists in options
|
|
129
|
+
// If it doesn't exist, don't update v-model (preserve invalid value like regular HTML select)
|
|
130
|
+
const currentValue = this.getCleanValue(this.$props.modelValue)
|
|
131
|
+
const options = this.slim.store.getDataOptions()
|
|
132
|
+
const currentValueExists = Array.isArray(currentValue)
|
|
133
|
+
? currentValue.length > 0 && currentValue.every((val) => options.some((opt) => opt.value === val))
|
|
134
|
+
: currentValue !== '' && options.some((opt) => opt.value === currentValue)
|
|
135
|
+
|
|
136
|
+
// Check if the new value is valid (exists in options)
|
|
137
|
+
const newValueIsValid = Array.isArray(value)
|
|
138
|
+
? value.length > 0 && value.every((val) => options.some((opt) => opt.value === val))
|
|
139
|
+
: value !== '' && options.some((opt) => opt.value === value)
|
|
140
|
+
|
|
141
|
+
// Check if value actually changed (properly compare arrays)
|
|
142
|
+
const valueChanged =
|
|
143
|
+
Array.isArray(value) && Array.isArray(this.value)
|
|
144
|
+
? JSON.stringify(value.sort()) !== JSON.stringify(this.value.sort())
|
|
145
|
+
: this.value !== value
|
|
146
|
+
|
|
147
|
+
// Only update v-model if:
|
|
148
|
+
// 1. The value actually changed, AND
|
|
149
|
+
// 2. Either the current v-model value exists in options, OR we're setting to a valid non-empty value
|
|
150
|
+
// This prevents clearing invalid v-model values when we show placeholder
|
|
151
|
+
if (valueChanged && (currentValueExists || newValueIsValid)) {
|
|
152
|
+
this.value = value
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Call the original afterChange callback if it exists
|
|
156
|
+
if (ogAfterChange) {
|
|
157
|
+
ogAfterChange(newVal)
|
|
158
|
+
}
|
|
159
|
+
},
|
|
141
160
|
getCleanValue(val: string | string[] | undefined): string | string[] {
|
|
142
161
|
const multi = this.$props.multiple
|
|
143
162
|
|
|
@@ -152,6 +171,47 @@ export default defineComponent({
|
|
|
152
171
|
}
|
|
153
172
|
|
|
154
173
|
return multi ? [] : ''
|
|
174
|
+
},
|
|
175
|
+
syncModelValueToSlimSelect(runAfterChange: boolean = false): void {
|
|
176
|
+
if (!this.slim) return
|
|
177
|
+
|
|
178
|
+
// Only sync if modelValue is explicitly set (not undefined)
|
|
179
|
+
// This prevents adding placeholders when modelValue is not provided
|
|
180
|
+
if (this.$props.modelValue === undefined) {
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const cleanValue = this.getCleanValue(this.$props.modelValue)
|
|
185
|
+
const data = this.slim.getData()
|
|
186
|
+
// Extract options from data (flatten optgroups if any)
|
|
187
|
+
const options = data.flatMap((item: Option | Optgroup) => ('label' in item ? item.options : [item])) as Option[]
|
|
188
|
+
|
|
189
|
+
// Check if the value exists in options
|
|
190
|
+
const valueExists = Array.isArray(cleanValue)
|
|
191
|
+
? cleanValue.length > 0 && cleanValue.every((val) => options.some((opt) => opt.value === val))
|
|
192
|
+
: cleanValue !== '' && options.some((opt) => opt.value === cleanValue)
|
|
193
|
+
|
|
194
|
+
// If value doesn't exist in options and it's not empty, add a placeholder option
|
|
195
|
+
if (!valueExists) {
|
|
196
|
+
// If its not multiple, add a placeholder option
|
|
197
|
+
if (!Array.isArray(cleanValue)) {
|
|
198
|
+
// For single select, check if placeholder already exists
|
|
199
|
+
const hasPlaceholder = options.some((opt) => opt.placeholder)
|
|
200
|
+
if (!hasPlaceholder) {
|
|
201
|
+
// Get current data and prepend placeholder option
|
|
202
|
+
const currentData = this.slim.getData()
|
|
203
|
+
const placeholderOption: Partial<Option> = {
|
|
204
|
+
value: '',
|
|
205
|
+
text: '',
|
|
206
|
+
placeholder: true
|
|
207
|
+
}
|
|
208
|
+
this.slim.setData([placeholderOption].concat(currentData))
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Set the selected value (will select placeholder if it was just added)
|
|
214
|
+
this.slim.setSelected(cleanValue, runAfterChange)
|
|
155
215
|
}
|
|
156
216
|
}
|
|
157
217
|
})
|
package/vitest.config.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { defineConfig } from 'vitest/config'
|
|
2
2
|
import vue from '@vitejs/plugin-vue'
|
|
3
|
+
import react from '@vitejs/plugin-react'
|
|
3
4
|
import path from 'path'
|
|
4
5
|
|
|
5
6
|
export default defineConfig({
|
|
6
|
-
plugins: [vue()],
|
|
7
|
+
plugins: [vue(), react()],
|
|
7
8
|
resolve: {
|
|
8
9
|
alias: {
|
|
9
10
|
'@': path.resolve(__dirname, 'src')
|
|
@@ -12,7 +13,7 @@ export default defineConfig({
|
|
|
12
13
|
test: {
|
|
13
14
|
globals: true,
|
|
14
15
|
environment: 'jsdom',
|
|
15
|
-
include: ['src/**/*.test.ts'],
|
|
16
|
+
include: ['src/**/*.test.{ts,tsx}'],
|
|
16
17
|
coverage: {
|
|
17
18
|
provider: 'v8',
|
|
18
19
|
reporter: ['text', 'json', 'html', 'lcov'],
|