svelte-select-5 7.0.2 → 7.0.4
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/Select.svelte +43 -24
- package/no-styles/Select.svelte +43 -24
- package/package.json +1 -1
package/Select.svelte
CHANGED
|
@@ -46,20 +46,25 @@
|
|
|
46
46
|
import LoadingIcon from './LoadingIcon.svelte';
|
|
47
47
|
|
|
48
48
|
// Performance: Polymorphic shallow equality comparison (faster than JSON.stringify)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
// Uses $state.snapshot() at top level to avoid proxy equality mismatch warnings
|
|
50
|
+
function shallowEqual(a, b, _isNested = false) {
|
|
51
|
+
// Only snapshot at top level to avoid repeated snapshot overhead
|
|
52
|
+
const plainA = !_isNested && a && typeof a === 'object' ? $state.snapshot(a) : a;
|
|
53
|
+
const plainB = !_isNested && b && typeof b === 'object' ? $state.snapshot(b) : b;
|
|
54
|
+
|
|
55
|
+
if (plainA === plainB) return true;
|
|
56
|
+
if (!plainA || !plainB || typeof plainA !== 'object' || typeof plainB !== 'object') return false;
|
|
57
|
+
|
|
58
|
+
// Handle arrays - recursive comparison
|
|
59
|
+
if (Array.isArray(plainA) && Array.isArray(plainB)) {
|
|
60
|
+
if (plainA.length !== plainB.length) return false;
|
|
61
|
+
return plainA.every((item, i) => shallowEqual(item, plainB[i], true));
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
// Handle objects
|
|
60
|
-
const keysA = Object.keys(
|
|
61
|
-
if (keysA.length !== Object.keys(
|
|
62
|
-
return keysA.every(key =>
|
|
65
|
+
const keysA = Object.keys(plainA);
|
|
66
|
+
if (keysA.length !== Object.keys(plainB).length) return false;
|
|
67
|
+
return keysA.every(key => plainA[key] === plainB[key]);
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
// Props with $props() rune
|
|
@@ -187,6 +192,7 @@
|
|
|
187
192
|
let isScrollingTimer;
|
|
188
193
|
let itemSelectedTimer;
|
|
189
194
|
let startIdApplied = $state(false);
|
|
195
|
+
let startIdLoadTriggered = false; // Flag to prevent repeated load triggers
|
|
190
196
|
let lastFilterText = ''; // Non-reactive for effect comparison
|
|
191
197
|
|
|
192
198
|
// Validated props using $derived to avoid state_referenced_locally warning
|
|
@@ -856,19 +862,18 @@
|
|
|
856
862
|
// Update value display when items change (untrack previousItemsRef to prevent loop)
|
|
857
863
|
$effect(() => {
|
|
858
864
|
const prevRef = untrack(() => previousItemsRef);
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
}
|
|
870
|
-
previousItemsRef = items;
|
|
865
|
+
const itemsLen = items?.length ?? 0;
|
|
866
|
+
const prevLen = prevRef?.length ?? 0;
|
|
867
|
+
// Compare by content, not reference, to avoid proxy issues
|
|
868
|
+
const hasChanged = itemsLen !== prevLen ||
|
|
869
|
+
(itemsLen > 0 && (
|
|
870
|
+
items[0]?.[validatedItemId] !== prevRef?.[0]?.[validatedItemId] ||
|
|
871
|
+
items[itemsLen - 1]?.[validatedItemId] !== prevRef?.[prevLen - 1]?.[validatedItemId]
|
|
872
|
+
));
|
|
873
|
+
if (hasChanged) {
|
|
874
|
+
updateValueDisplay(items);
|
|
871
875
|
}
|
|
876
|
+
previousItemsRef = items;
|
|
872
877
|
});
|
|
873
878
|
|
|
874
879
|
// Sync selectedValue → selectedId
|
|
@@ -902,7 +907,13 @@
|
|
|
902
907
|
const valuesMatch = Array.isArray(selectedId) && Array.isArray(computed)
|
|
903
908
|
? selectedId.length === computed.length && selectedId.every((v, i) => v === computed[i])
|
|
904
909
|
: selectedId === computed;
|
|
905
|
-
|
|
910
|
+
// Use snapshot comparison to avoid proxy equality mismatch warnings
|
|
911
|
+
const currentIdSnapshot = selectedId !== undefined ? $state.snapshot(selectedId) : selectedId;
|
|
912
|
+
const prevIdSnapshot = untrack(() => previousSelectedId !== undefined ? $state.snapshot(previousSelectedId) : previousSelectedId);
|
|
913
|
+
const referenceDiffers = Array.isArray(currentIdSnapshot) && Array.isArray(prevIdSnapshot)
|
|
914
|
+
? currentIdSnapshot.length !== prevIdSnapshot.length || currentIdSnapshot.some((v, i) => v !== prevIdSnapshot[i])
|
|
915
|
+
: currentIdSnapshot !== prevIdSnapshot;
|
|
916
|
+
const isExternalChange = referenceDiffers && !valuesMatch;
|
|
906
917
|
if (isExternalChange) {
|
|
907
918
|
if (!items) {
|
|
908
919
|
pendingSelectedId = selectedId;
|
|
@@ -926,6 +937,14 @@
|
|
|
926
937
|
}
|
|
927
938
|
});
|
|
928
939
|
|
|
940
|
+
// Trigger initial load when startId is set with loadOptions but no items yet
|
|
941
|
+
$effect(() => {
|
|
942
|
+
if (startId !== undefined && validatedLoadOptions && !items && !startIdLoadTriggered) {
|
|
943
|
+
startIdLoadTriggered = true;
|
|
944
|
+
setupFilterText();
|
|
945
|
+
}
|
|
946
|
+
});
|
|
947
|
+
|
|
929
948
|
$effect.pre(() => {
|
|
930
949
|
readOnlySelectedValue = selectedValue;
|
|
931
950
|
readOnlySelectedId = computeSelectedId();
|
package/no-styles/Select.svelte
CHANGED
|
@@ -46,20 +46,25 @@
|
|
|
46
46
|
import LoadingIcon from './LoadingIcon.svelte';
|
|
47
47
|
|
|
48
48
|
// Performance: Polymorphic shallow equality comparison (faster than JSON.stringify)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
// Uses $state.snapshot() at top level to avoid proxy equality mismatch warnings
|
|
50
|
+
function shallowEqual(a, b, _isNested = false) {
|
|
51
|
+
// Only snapshot at top level to avoid repeated snapshot overhead
|
|
52
|
+
const plainA = !_isNested && a && typeof a === 'object' ? $state.snapshot(a) : a;
|
|
53
|
+
const plainB = !_isNested && b && typeof b === 'object' ? $state.snapshot(b) : b;
|
|
54
|
+
|
|
55
|
+
if (plainA === plainB) return true;
|
|
56
|
+
if (!plainA || !plainB || typeof plainA !== 'object' || typeof plainB !== 'object') return false;
|
|
57
|
+
|
|
58
|
+
// Handle arrays - recursive comparison
|
|
59
|
+
if (Array.isArray(plainA) && Array.isArray(plainB)) {
|
|
60
|
+
if (plainA.length !== plainB.length) return false;
|
|
61
|
+
return plainA.every((item, i) => shallowEqual(item, plainB[i], true));
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
// Handle objects
|
|
60
|
-
const keysA = Object.keys(
|
|
61
|
-
if (keysA.length !== Object.keys(
|
|
62
|
-
return keysA.every(key =>
|
|
65
|
+
const keysA = Object.keys(plainA);
|
|
66
|
+
if (keysA.length !== Object.keys(plainB).length) return false;
|
|
67
|
+
return keysA.every(key => plainA[key] === plainB[key]);
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
// Props with $props() rune
|
|
@@ -187,6 +192,7 @@
|
|
|
187
192
|
let isScrollingTimer;
|
|
188
193
|
let itemSelectedTimer;
|
|
189
194
|
let startIdApplied = $state(false);
|
|
195
|
+
let startIdLoadTriggered = false; // Flag to prevent repeated load triggers
|
|
190
196
|
let lastFilterText = ''; // Non-reactive for effect comparison
|
|
191
197
|
|
|
192
198
|
// Validated props using $derived to avoid state_referenced_locally warning
|
|
@@ -856,19 +862,18 @@
|
|
|
856
862
|
// Update value display when items change (untrack previousItemsRef to prevent loop)
|
|
857
863
|
$effect(() => {
|
|
858
864
|
const prevRef = untrack(() => previousItemsRef);
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
}
|
|
870
|
-
previousItemsRef = items;
|
|
865
|
+
const itemsLen = items?.length ?? 0;
|
|
866
|
+
const prevLen = prevRef?.length ?? 0;
|
|
867
|
+
// Compare by content, not reference, to avoid proxy issues
|
|
868
|
+
const hasChanged = itemsLen !== prevLen ||
|
|
869
|
+
(itemsLen > 0 && (
|
|
870
|
+
items[0]?.[validatedItemId] !== prevRef?.[0]?.[validatedItemId] ||
|
|
871
|
+
items[itemsLen - 1]?.[validatedItemId] !== prevRef?.[prevLen - 1]?.[validatedItemId]
|
|
872
|
+
));
|
|
873
|
+
if (hasChanged) {
|
|
874
|
+
updateValueDisplay(items);
|
|
871
875
|
}
|
|
876
|
+
previousItemsRef = items;
|
|
872
877
|
});
|
|
873
878
|
|
|
874
879
|
// Sync selectedValue → selectedId
|
|
@@ -902,7 +907,13 @@
|
|
|
902
907
|
const valuesMatch = Array.isArray(selectedId) && Array.isArray(computed)
|
|
903
908
|
? selectedId.length === computed.length && selectedId.every((v, i) => v === computed[i])
|
|
904
909
|
: selectedId === computed;
|
|
905
|
-
|
|
910
|
+
// Use snapshot comparison to avoid proxy equality mismatch warnings
|
|
911
|
+
const currentIdSnapshot = selectedId !== undefined ? $state.snapshot(selectedId) : selectedId;
|
|
912
|
+
const prevIdSnapshot = untrack(() => previousSelectedId !== undefined ? $state.snapshot(previousSelectedId) : previousSelectedId);
|
|
913
|
+
const referenceDiffers = Array.isArray(currentIdSnapshot) && Array.isArray(prevIdSnapshot)
|
|
914
|
+
? currentIdSnapshot.length !== prevIdSnapshot.length || currentIdSnapshot.some((v, i) => v !== prevIdSnapshot[i])
|
|
915
|
+
: currentIdSnapshot !== prevIdSnapshot;
|
|
916
|
+
const isExternalChange = referenceDiffers && !valuesMatch;
|
|
906
917
|
if (isExternalChange) {
|
|
907
918
|
if (!items) {
|
|
908
919
|
pendingSelectedId = selectedId;
|
|
@@ -926,6 +937,14 @@
|
|
|
926
937
|
}
|
|
927
938
|
});
|
|
928
939
|
|
|
940
|
+
// Trigger initial load when startId is set with loadOptions but no items yet
|
|
941
|
+
$effect(() => {
|
|
942
|
+
if (startId !== undefined && validatedLoadOptions && !items && !startIdLoadTriggered) {
|
|
943
|
+
startIdLoadTriggered = true;
|
|
944
|
+
setupFilterText();
|
|
945
|
+
}
|
|
946
|
+
});
|
|
947
|
+
|
|
929
948
|
$effect.pre(() => {
|
|
930
949
|
readOnlySelectedValue = selectedValue;
|
|
931
950
|
readOnlySelectedId = computeSelectedId();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelte-select-5",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.4",
|
|
4
4
|
"description": "A <Select> component for Svelte 5 apps (fork of svelte-select)",
|
|
5
5
|
"repository": "https://github.com/Dbone29/svelte-select-5.git",
|
|
6
6
|
"author": "Robert Balfré <rob.balfre@gmail.com> (https://github.com/rob-balfre)",
|