multiselect working
This commit is contained in:
parent
d41a05d367
commit
8262466f5a
1 changed files with 113 additions and 63 deletions
|
|
@ -61,7 +61,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onBeforeMount, ref, watch } from 'vue';
|
import { onBeforeMount, ref, watch, nextTick } from 'vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useDialogStore } from '../stores/dialog';
|
import { useDialogStore } from '../stores/dialog';
|
||||||
|
|
||||||
|
|
@ -69,31 +69,54 @@ import { useDialogStore } from '../stores/dialog';
|
||||||
const { items, label, isCompareModeEnabled, index } = defineProps({
|
const { items, label, isCompareModeEnabled, index } = defineProps({
|
||||||
label: String,
|
label: String,
|
||||||
items: Array,
|
items: Array,
|
||||||
isCompareModeEnabled: {
|
isCompareModeEnabled: { type: Boolean, default: false },
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
index: Number,
|
index: Number,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Variables
|
// Local state
|
||||||
const currentValue = ref(null);
|
const currentValue = ref(null);
|
||||||
|
const syncing = ref(false); // empêche les réactions pendant les mises à jour programmatiques
|
||||||
|
|
||||||
// Stores
|
// Store
|
||||||
const { activeTracks } = storeToRefs(useDialogStore());
|
const { activeTracks } = storeToRefs(useDialogStore());
|
||||||
|
|
||||||
// Hooks
|
// Utils
|
||||||
|
function isSame(a, b) {
|
||||||
|
if (!a || !b) return false;
|
||||||
|
if (a.slug && b.slug) return a.slug === b.slug;
|
||||||
|
return a.title === b.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toVariation(v) {
|
||||||
|
if (!v) return null;
|
||||||
|
return Array.isArray(v) ? v[v.length - 1] || null : v;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialisation : remplir le 1er select localement ET initialiser le store
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
|
syncing.value = true;
|
||||||
|
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
currentValue.value = items[0];
|
currentValue.value = items[0] || null;
|
||||||
|
// si le store est vide, initialiser avec la variation du premier sélecteur
|
||||||
|
if (!activeTracks.value || activeTracks.value.length === 0) {
|
||||||
|
const v = toVariation(items[0]);
|
||||||
|
if (v) activeTracks.value = [v];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// les autres ne forcent pas le store ; leur currentValue restera à null
|
||||||
|
currentValue.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nextTick(() => (syncing.value = false));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Watchers
|
// Quand on bascule compare mode (objet <-> tableau)
|
||||||
watch(
|
watch(
|
||||||
() => isCompareModeEnabled,
|
() => isCompareModeEnabled,
|
||||||
(newValue) => {
|
(flag) => {
|
||||||
if (newValue) {
|
syncing.value = true;
|
||||||
|
if (flag) {
|
||||||
if (currentValue.value && !Array.isArray(currentValue.value)) {
|
if (currentValue.value && !Array.isArray(currentValue.value)) {
|
||||||
currentValue.value = [currentValue.value];
|
currentValue.value = [currentValue.value];
|
||||||
}
|
}
|
||||||
|
|
@ -102,80 +125,107 @@ watch(
|
||||||
currentValue.value = currentValue.value[0] || null;
|
currentValue.value = currentValue.value[0] || null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
nextTick(() => (syncing.value = false));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(currentValue, (newValue) => {
|
// Détection ajout / suppression dans le MultiSelect (côté composant)
|
||||||
if (
|
// On n'agit que si l'ajout/suppression concerne une variation appartenant à `items`
|
||||||
newValue !== null &&
|
|
||||||
(Array.isArray(newValue) ? newValue.length > 0 : true)
|
|
||||||
) {
|
|
||||||
selectTrack(newValue);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
activeTracks,
|
currentValue,
|
||||||
(newValue) => {
|
(newVal, oldVal) => {
|
||||||
if (!isCompareModeEnabled || !currentValue.value) return;
|
if (syncing.value) return;
|
||||||
|
|
||||||
currentValue.value.forEach((item) => {
|
const newItems = Array.isArray(newVal) ? newVal : newVal ? [newVal] : [];
|
||||||
if (!newValue.includes(item)) {
|
const oldItems = Array.isArray(oldVal) ? oldVal : oldVal ? [oldVal] : [];
|
||||||
currentValue.value = currentValue.value.filter((el) => el !== item);
|
|
||||||
}
|
const added = newItems.find((n) => !oldItems.some((o) => isSame(o, n)));
|
||||||
});
|
const removed = oldItems.find((o) => !newItems.some((n) => isSame(n, o)));
|
||||||
|
|
||||||
|
if (added && items.some((it) => isSame(it, added))) {
|
||||||
|
selectTrack(added, 'add');
|
||||||
|
} else if (removed && items.some((it) => isSame(it, removed))) {
|
||||||
|
selectTrack(removed, 'remove');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{ deep: true }
|
{ deep: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
function selectTrack(track) {
|
// Quand activeTracks change elsewhere -> synchroniser l'affichage local
|
||||||
if (!isCompareModeEnabled) {
|
// Mais n'adopter que les variations qui appartiennent à ce Selector (`items`)
|
||||||
activeTracks.value = [track];
|
watch(
|
||||||
return;
|
activeTracks,
|
||||||
}
|
(newVal) => {
|
||||||
|
syncing.value = true;
|
||||||
|
|
||||||
const lastVariation = track[track.length - 1];
|
const storeList = Array.isArray(newVal) ? newVal : [];
|
||||||
|
// ne garder que les variations du store qui sont dans `items`
|
||||||
|
const matched = storeList.filter((av) =>
|
||||||
|
items.some((it) => isSame(it, av))
|
||||||
|
);
|
||||||
|
|
||||||
if (
|
if (isCompareModeEnabled) {
|
||||||
activeTracks.value.length === 1 &&
|
currentValue.value = matched.length ? [...matched] : [];
|
||||||
!activeTracks.value.includes(lastVariation)
|
|
||||||
) {
|
|
||||||
activeTracks.value.push(lastVariation);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeTracks.value.length === 2) {
|
|
||||||
if (activeTracks.value.includes(lastVariation)) {
|
|
||||||
removeTrack(track);
|
|
||||||
} else {
|
} else {
|
||||||
activeTracks.value.pop();
|
currentValue.value = matched[0] || null;
|
||||||
activeTracks.value.push(lastVariation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nextTick(() => (syncing.value = false));
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Logique centrale de sélection (ajout / suppression)
|
||||||
|
// Règles :
|
||||||
|
// - mode normal -> activeTracks = [variation]
|
||||||
|
// - mode comparaison -> conserver activeTracks[0] si possible; second élément ajouté/remplacé; suppression gère le cas de la suppression de la première
|
||||||
|
function selectTrack(track, action = 'add') {
|
||||||
|
const variation = toVariation(track);
|
||||||
|
if (!variation) return;
|
||||||
|
|
||||||
|
if (!isCompareModeEnabled) {
|
||||||
|
activeTracks.value = [variation];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action === 'remove') {
|
||||||
|
const wasFirst =
|
||||||
|
activeTracks.value.length && isSame(activeTracks.value[0], variation);
|
||||||
|
activeTracks.value = activeTracks.value.filter(
|
||||||
|
(t) => !isSame(t, variation)
|
||||||
|
);
|
||||||
|
|
||||||
|
// si on a retiré la première et qu'il reste une piste, elle devient naturellement index 0
|
||||||
|
// pas d'action supplémentaire nécessaire ici (déjà assuré par le filter)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// action === 'add'
|
||||||
|
if (activeTracks.value.some((t) => isSame(t, variation))) {
|
||||||
|
// déjà présent -> ignore
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeTracks.value.length === 0) {
|
||||||
|
activeTracks.value = [variation];
|
||||||
|
} else if (activeTracks.value.length === 1) {
|
||||||
|
activeTracks.value = [activeTracks.value[0], variation];
|
||||||
|
} else {
|
||||||
|
// remplacer le 2e
|
||||||
|
activeTracks.value = [activeTracks.value[0], variation];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeTrack(track) {
|
// Helpers pour affichage (inchangés)
|
||||||
activeTracks.value = activeTracks.value.filter(
|
|
||||||
(activeTrack) => activeTrack.title !== track.title
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(activeTracks, (newValue) => {
|
|
||||||
if (newValue[newValue.length - 1] !== currentValue.value) {
|
|
||||||
currentValue.value = isCompareModeEnabled ? [] : null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Functions
|
|
||||||
function getFrontViewUrl(item) {
|
function getFrontViewUrl(item) {
|
||||||
if (!item) return '';
|
if (!item) return '';
|
||||||
if (Array.isArray(item)) {
|
if (Array.isArray(item)) {
|
||||||
return item.length > 0 ? getFrontViewUrl(item[0]) : '';
|
return item.length > 0 ? getFrontViewUrl(item[0]) : '';
|
||||||
}
|
}
|
||||||
if (item.files.length > 1) {
|
if (item.files && item.files.length > 1) {
|
||||||
return item.files[7]?.url || item.files[0].url;
|
return item.files[7]?.url || item.files[0].url;
|
||||||
} else {
|
} else {
|
||||||
return item.files[0].url;
|
return item.files && item.files[0] ? item.files[0].url : '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue