Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cms/src/components/BasePage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const isMobileScreen = breakpoints.smaller("lg");
class="sticky top-0 z-40 flex h-12 shrink-0 items-center gap-x-4 bg-white py-8 shadow-sm sm:gap-x-3"
:class="[
{ 'border-b border-zinc-200': !$slots.internalPageHeader },
isSmallScreen ? 'sm:pl-5 sm:pr-1' : 'lg:pl-9 lg:pr-5',
isSmallScreen ? 'sm:pl-5 sm:pr-4' : 'lg:pl-9 lg:pr-5',
]"
>
<button
Expand Down
12 changes: 9 additions & 3 deletions cms/src/components/content/EditContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,7 @@ const contentActions = computed(() => {
});

const isLocalChange = db.isLocalChangeAsRef(parentId);
const isLanguageSelectorCollapsed = ref(false);
</script>

<template>
Expand Down Expand Up @@ -595,7 +596,7 @@ const isLocalChange = db.isLocalChangeAsRef(parentId);
<div class="flex flex-col gap-0 lg:h-full lg:flex-row lg:gap-2 lg:overflow-y-hidden">
<!-- sidebar -->
<div class="w-full flex-shrink-0 lg:h-full lg:w-[336px]" v-if="editableParent">
<div class="overflow-y-auto scrollbar-hide lg:h-full lg:pb-2">
<div class="scrollbar-hide lg:overflow-y-auto lg:h-full lg:pb-2">
<div class="flex flex-col gap-2 pb-0 lg:pb-4">
<EditContentParent
v-if="editableParent"
Expand Down Expand Up @@ -626,7 +627,7 @@ const isLocalChange = db.isLocalChangeAsRef(parentId);
v-model:parent="editableParent"
/>

<div class="sticky -top-1 z-10 lg:static">
<div class="sticky top-0 z-10 lg:static">
<EditContentParentValidation
:tag-or-post-type="props.tagOrPostType"
:can-translate="canTranslate"
Expand All @@ -643,6 +644,7 @@ const isLocalChange = db.isLocalChangeAsRef(parentId);
:existingParent="existingParent"
@updateIsValid="(val) => (isValid = val)"
@create-translation="(language) => createTranslation(language)"
@update:selectorCollapsed="(val) => (isLanguageSelectorCollapsed = val)"
/>
</div>
<EditContentVideo
Expand All @@ -659,7 +661,10 @@ const isLocalChange = db.isLocalChangeAsRef(parentId);
</div>
</div>
<!-- main content instance -->
<div class="mt-2 min-h-0 w-full overflow-y-auto scrollbar-hide lg:mt-0 lg:flex-1">
<div
class="min-h-0 w-full scrollbar-hide lg:mt-0 lg:flex-1 lg:overflow-y-auto lg:static"
:class="isLanguageSelectorCollapsed ? 'sticky top-14 z-[5]' : 'mt-2 overflow-y-auto'"
>
<EmptyState
v-if="!selectedContent"
:icon="icon"
Expand Down Expand Up @@ -700,6 +705,7 @@ const isLocalChange = db.isLocalChangeAsRef(parentId);
:selectedLanguage="selectedLanguage!"
:disabled="!canTranslate"
:disablePublish="!canPublish"
:isLanguageSelectorCollapsed="isLanguageSelectorCollapsed"
/>
</div>
</div>
Expand Down
24 changes: 15 additions & 9 deletions cms/src/components/content/EditContentParentValidation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,27 +60,28 @@ const checkTopbarCollision = () => {
const cardTop = languageSelector.value.getBoundingClientRect().top;
const distance = cardTop - topbarBottom;

// Collapse if card is within 25px of topbar (give more buffer)
const shouldCollapse = distance <= 25;
isLanguageSelectorCollapsed.value = shouldCollapse;
// Hysteresis: collapse when close to topbar, uncollapse when sufficiently away
// This prevents jitter from layout reflow triggering rapid collapse/uncollapse
if (!isLanguageSelectorCollapsed.value && distance <= 25) {
isLanguageSelectorCollapsed.value = true;
} else if (isLanguageSelectorCollapsed.value && distance > 60) {
isLanguageSelectorCollapsed.value = false;
}
};

onMounted(() => {
// Run on small screens or when testing
if (!isSmallScreen.value && import.meta.env.MODE !== "test") return;

// Add window scroll listener for topbar collision check
window.addEventListener("scroll", checkTopbarCollision, { passive: true });

// Also listen to any scroll events that might affect positioning
// Listen during capture phase so it fires even when scroll propagation is stopped
document.addEventListener("scroll", checkTopbarCollision, { passive: true, capture: true });

// Initial check
setTimeout(() => checkTopbarCollision(), 100);
});

onUnmounted(() => {
window.removeEventListener("scroll", checkTopbarCollision);
document.removeEventListener("scroll", checkTopbarCollision, { capture: true } as EventListenerOptions);
});

const router = useRouter();
Expand Down Expand Up @@ -110,8 +111,13 @@ const setOverallValidation = (id: Uuid, isValid: boolean) => {
const emit = defineEmits<{
(e: "updateIsValid", value: boolean): void;
(e: "createTranslation", language: LanguageDto): void;
(e: "update:selectorCollapsed", value: boolean): void;
}>();

watch(isLanguageSelectorCollapsed, (val) => {
emit("update:selectorCollapsed", val);
});

watchEffect(() => {
emit("updateIsValid", overallIsValid.value);
});
Expand Down Expand Up @@ -163,7 +169,7 @@ watch(
shadow="small"
title="Translations"
:icon="LanguageIcon"
collapsible
:collapsible="props.languages.length > 1"
v-model:collapsed="isLanguageSelectorCollapsed"
>
<template #actions>
Expand Down
8 changes: 6 additions & 2 deletions cms/src/components/content/EditContentText.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ import RichTextEditor from "../editor/RichTextEditor.vue";
type Props = {
selectedLanguage?: LanguageDto;
disabled: boolean;
isLanguageSelectorCollapsed?: boolean;
};
defineProps<Props>();
const props = defineProps<Props>();
const content = defineModel<ContentDto>("content");
</script>

<template>
<div v-if="content">
<LCard class="h-max bg-white pt-0 sm:h-[calc(100vh-5rem)] lg:pt-2">
<LCard
class="bg-white pt-0 lg:pt-2"
:class="props.isLanguageSelectorCollapsed ? 'h-[calc(100dvh-7rem)] lg:h-[calc(100vh-5rem)]' : 'h-max sm:h-[calc(100vh-5rem)]'"
>
<RichTextEditor
class="mb-0 lg:mb-16"
v-model:text="content.text"
Expand Down
Loading