+
+
- {{ __(localization.name) }}
+ {{ __(localization.name) }}
-
-
-
-
-
+
+
+
diff --git a/resources/js/components/ui/Publish/Localizations.vue b/resources/js/components/ui/Publish/Localizations.vue
index 3b2d2f68957..92797fa5f5b 100644
--- a/resources/js/components/ui/Publish/Localizations.vue
+++ b/resources/js/components/ui/Publish/Localizations.vue
@@ -4,9 +4,18 @@ import {
Combobox,
Card,
Panel,
+ Subheading,
+ Icon,
} from '@ui';
-import { computed, useId } from 'vue';
+import { computed, ref, useId, watch, nextTick } from 'vue';
+import fuzzysort from 'fuzzysort';
import Localization from './Localization.vue';
+import {
+ flatOptionsFromSiteGroups,
+ groupItemsBySiteGroup,
+ hasNamedSiteGroups,
+ selectedSiteGroupLabel,
+} from '@/util/site-groups.js';
const props = defineProps({
localizations: {
@@ -17,27 +26,125 @@ const props = defineProps({
type: [Boolean, String],
default: false,
},
+ confirmingSwitch: {
+ type: Boolean,
+ default: false,
+ },
heading: {
type: String,
default: null,
},
});
-defineEmits(['selected']);
+const emit = defineEmits(['selected']);
const comboboxId = useId();
+const searchQuery = ref('');
+const pendingHandle = ref(null);
const panelHeading = computed(() => props.heading || __('Working In'));
const activeLocalization = computed(() => {
return props.localizations.find((localization) => localization.active);
});
+
+function clearPendingIfStale() {
+ if (!pendingHandle.value) return;
+ if (pendingHandle.value === activeLocalization.value?.handle) {
+ pendingHandle.value = null;
+ return;
+ }
+ if (props.localizing || props.confirmingSwitch) return;
+
+ pendingHandle.value = null;
+}
+
+watch(
+ () => activeLocalization.value?.handle,
+ (handle) => {
+ if (handle && handle === pendingHandle.value) {
+ pendingHandle.value = null;
+ }
+ },
+);
+
+watch(
+ () => props.localizing,
+ (localizing, wasLocalizing) => {
+ if (wasLocalizing && !localizing) {
+ clearPendingIfStale();
+ }
+ },
+);
+
+watch(
+ () => props.confirmingSwitch,
+ (confirming, wasConfirming) => {
+ if (wasConfirming && !confirming) {
+ clearPendingIfStale();
+ }
+ },
+);
+
+const selectedLocalization = computed(() => {
+ if (pendingHandle.value) {
+ return props.localizations.find((localization) => localization.handle === pendingHandle.value)
+ ?? activeLocalization.value;
+ }
+
+ return activeLocalization.value;
+});
+
+const localizationGroups = computed(() => groupItemsBySiteGroup(props.localizations));
+
+const hasNamedGroups = computed(() => hasNamedSiteGroups(localizationGroups.value));
+
+const useCombobox = computed(() => hasNamedGroups.value || props.localizations.length > 5);
+
+const comboboxOptions = computed(() => {
+ const query = searchQuery.value;
+
+ return flatOptionsFromSiteGroups(localizationGroups.value, {
+ filterItems: (localizations) => query
+ ? fuzzysort
+ .go(query, localizations, { keys: ['name', 'group', 'handle'] })
+ .map((result) => result.obj)
+ : localizations,
+ });
+});
+
+function selectLocalization(localization) {
+ if (!localization) return;
+
+ searchQuery.value = '';
+ pendingHandle.value = localization.handle;
+ emit('selected', localization);
+
+ // Parent may abort without starting a switch (e.g. can't create missing localization).
+ if (!localization.exists) {
+ nextTick(() => {
+ if (pendingHandle.value !== localization.handle) return;
+ if (props.localizing || props.confirmingSwitch) return;
+ if (activeLocalization.value?.handle !== localization.handle) {
+ pendingHandle.value = null;
+ }
+ });
+ }
+}
+
+function handleSearch(query) {
+ searchQuery.value = query;
+}
+
+function selectedGroupLabel(option) {
+ return selectedSiteGroupLabel(option, hasNamedGroups.value);
+}
-
+