Skip to content
Merged
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
123 changes: 68 additions & 55 deletions resources/js/components/publish/CalendarPostChip.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const props = withDefaults(
post: CalendarPost;
timezone: string;
layout?: 'month' | 'week';
compact?: boolean;
}>(),
{ layout: 'month' },
);
Expand Down Expand Up @@ -137,72 +138,84 @@ const scheduleMode = computed(() =>
class="flex min-w-0 shrink-0 gap-1.5 rounded-lg border border-border-strong bg-card text-start transition-control hover:bg-secondary focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-ring data-[state=open]:bg-secondary"
:class="[
STATUS_CLASSES[post.status] ?? '',
layout === 'week'
? 'items-start p-1.5'
: 'h-7 items-center px-1',
compact
? 'size-7 items-center justify-center'
: layout === 'week'
? 'items-start p-1.5'
: 'h-7 items-center px-1',
]"
:aria-label="`${channels} · ${time} · ${text || $t('calendar.no_content')}`"
:data-testid="`calendar-post-${post.id}`"
>
<span class="flex min-w-0 flex-1 flex-col gap-1">
<span class="flex min-w-0 items-center gap-1">
<PlatformBrandIcon
v-if="post.platform"
:platform="post.platform"
class="size-4 shrink-0"
/>
<template v-if="compact">
<PlatformBrandIcon
v-if="post.platform"
:platform="post.platform"
colored
class="size-4 shrink-0"
/>
</template>
<template v-else>
<span class="flex min-w-0 flex-1 flex-col gap-1">
<span class="flex min-w-0 items-center gap-1">
<PlatformBrandIcon
v-if="post.platform"
:platform="post.platform"
class="size-4 shrink-0"
/>
<span
class="truncate text-xs font-medium text-muted-foreground"
>{{ time }}</span
>
<IconRepeat
v-if="scheduleMode && post.recurrence_frequency"
class="size-3 shrink-0 text-muted-foreground"
:aria-label="$t('posts.recurrence.marker')"
:data-testid="`calendar-post-recurring-${post.id}`"
/>
<PostScheduleModeBadge
v-else-if="scheduleMode"
:post-id="post.id"
:mode="scheduleMode"
compact
plain
/>
</span>
<span
class="truncate text-xs font-medium text-muted-foreground"
>{{ time }}</span
v-if="layout === 'week'"
class="line-clamp-2 text-xs break-words text-foreground"
:class="{ 'text-muted-foreground': !text }"
:data-testid="`calendar-post-text-${post.id}`"
>{{ text || $t('calendar.no_content') }}</span
>
<IconRepeat
v-if="scheduleMode && post.recurrence_frequency"
class="size-3 shrink-0 text-muted-foreground"
:aria-label="$t('posts.recurrence.marker')"
:data-testid="`calendar-post-recurring-${post.id}`"
/>
<PostScheduleModeBadge
v-else-if="scheduleMode"
:post-id="post.id"
:mode="scheduleMode"
compact
plain
/>
</span>
<span
v-if="layout === 'week'"
class="line-clamp-2 text-xs break-words text-foreground"
:class="{ 'text-muted-foreground': !text }"
:data-testid="`calendar-post-text-${post.id}`"
>{{ text || $t('calendar.no_content') }}</span
v-if="thumbnail"
class="relative shrink-0 overflow-hidden rounded bg-secondary"
:class="layout === 'week' ? 'size-9' : 'size-5'"
>
</span>
<span
v-if="thumbnail"
class="relative shrink-0 overflow-hidden rounded bg-secondary"
:class="layout === 'week' ? 'size-9' : 'size-5'"
>
<img
v-if="isImage(thumbnail)"
:src="thumbnail.url"
alt=""
loading="lazy"
class="size-full object-cover"
/>
<template v-else>
<video
:src="videoFrameUrl(thumbnail)"
<img
v-if="isImage(thumbnail)"
:src="thumbnail.url"
alt=""
loading="lazy"
class="size-full object-cover"
muted
playsinline
preload="metadata"
/>
<IconPlayerPlayFilled
aria-hidden="true"
class="absolute top-1/2 left-1/2 size-3.5 -translate-x-1/2 -translate-y-1/2 rounded-full bg-black/60 p-0.5 text-white"
/>
</template>
</span>
<template v-else>
<video
:src="videoFrameUrl(thumbnail)"
class="size-full object-cover"
muted
playsinline
preload="metadata"
/>
<IconPlayerPlayFilled
aria-hidden="true"
class="absolute top-1/2 left-1/2 size-3.5 -translate-x-1/2 -translate-y-1/2 rounded-full bg-black/60 p-0.5 text-white"
/>
</template>
</span>
</template>
</button>
</PopoverTrigger>
<PopoverContent
Expand Down
40 changes: 24 additions & 16 deletions resources/js/components/publish/CalendarSlotChip.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const props = withDefaults(
timezone: string;
canCreatePost: boolean;
layout?: 'chip' | 'agenda';
compact?: boolean;
}>(),
{ layout: 'chip' },
);
Expand Down Expand Up @@ -80,7 +81,12 @@ const newPost = (): void => {
v-else
:is="canCreatePost ? 'button' : 'div'"
:type="canCreatePost ? 'button' : undefined"
class="group/slot flex h-7 w-full min-w-0 shrink-0 items-center gap-1.5 rounded-lg border border-dashed border-border-strong px-1.5 text-xs font-medium text-muted-foreground transition-control enabled:hover:bg-secondary enabled:hover:text-foreground enabled:focus-visible:bg-secondary enabled:focus-visible:text-foreground"
class="group/slot flex shrink-0 items-center rounded-lg border border-dashed border-border-strong text-xs font-medium text-muted-foreground transition-control enabled:hover:bg-secondary enabled:hover:text-foreground enabled:focus-visible:bg-secondary enabled:focus-visible:text-foreground"
:class="
compact
? 'size-7 justify-center'
: 'h-7 w-full min-w-0 gap-1.5 px-1.5'
"
:title="channel?.display_label"
:aria-label="
channel
Expand All @@ -97,22 +103,24 @@ const newPost = (): void => {
<PlatformBrandIcon
v-if="channel"
:platform="channel.platform"
class="shrink-0"
:class="compact ? 'size-3.5' : 'shrink-0'"
:data-testid="`calendar-posting-slot-icon-${testKey}`"
/>
<span
:class="[
'truncate',
canCreatePost
? 'group-hover/slot:hidden group-focus-visible/slot:hidden'
: '',
]"
>{{ time }}</span
>
<span
v-if="canCreatePost"
class="hidden truncate group-hover/slot:inline group-focus-visible/slot:inline"
>{{ $t('posts.publish.add_post_in_slot') }}</span
>
<template v-if="!compact">
<span
:class="[
'truncate',
canCreatePost
? 'group-hover/slot:hidden group-focus-visible/slot:hidden'
: '',
]"
>{{ time }}</span
>
<span
v-if="canCreatePost"
class="hidden truncate group-hover/slot:inline group-focus-visible/slot:inline"
>{{ $t('posts.publish.add_post_in_slot') }}</span
>
</template>
</component>
</template>
27 changes: 17 additions & 10 deletions resources/js/components/publish/CalendarTimeGrid.vue
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,19 @@ const isStacked = (
hour: number,
): boolean => (column.byHour.get(hour)?.length ?? 0) > 1;

const iconOnly = computed(() => compact.value && props.days.length === 7);

const chipClass = (
column: { byHour: Map<number, CalendarItem[]> },
hour: number,
): string => {
if (iconOnly.value) {
return '';
}

return isStacked(column, hour) ? 'w-full shrink-0' : 'flex-1';
};

const visibleItems = (
key: string,
hour: number,
Expand Down Expand Up @@ -201,23 +214,17 @@ watch(
? 'month'
: 'week'
"
:class="
isStacked(column, hour)
? 'w-full shrink-0'
: 'flex-1'
"
:compact="iconOnly"
:class="chipClass(column, hour)"
/>
<CalendarSlotChip
v-else-if="item.slot"
:posting-slot="item.slot"
:channel="channels[item.slot.channel_id] ?? null"
:timezone="timezone"
:can-create-post="canCreatePost"
:class="
isStacked(column, hour)
? 'w-full shrink-0'
: 'flex-1'
"
:compact="iconOnly"
:class="chipClass(column, hour)"
/>
</template>
<button
Expand Down
91 changes: 53 additions & 38 deletions resources/js/pages/posts/Calendar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -432,10 +432,14 @@ const toggleDay = (key: string): void => {
: [...expandedDays.value, key];
};

const isDayExpanded = (day: dayjs.Dayjs): boolean =>
expandedDays.value.includes(dayKey(day));

const hiddenCount = (day: dayjs.Dayjs): number =>
itemsFor(day).length - MONTH_CHIPS;

const visibleMonthItems = (day: dayjs.Dayjs): CalendarItem[] =>
expandedDays.value.includes(dayKey(day))
? itemsFor(day)
: itemsFor(day).slice(0, MONTH_CHIPS);
isDayExpanded(day) ? itemsFor(day) : itemsFor(day).slice(0, MONTH_CHIPS);

const mobileTitle = computed(() => {
if (props.view === 'month') {
Expand Down Expand Up @@ -711,7 +715,7 @@ const goToDay = (key: string): void => {
<div
v-for="(week, weekIndex) in calendarWeeks"
:key="weekIndex"
class="grid min-h-24 flex-1 auto-rows-[minmax(0,1fr)] grid-cols-7 border-border-strong md:min-h-[208px]"
class="grid min-h-40 flex-1 auto-rows-[minmax(0,1fr)] grid-cols-7 border-border-strong md:min-h-[208px]"
:class="{ 'border-t': weekIndex > 0 }"
>
<div
Expand Down Expand Up @@ -757,7 +761,7 @@ const goToDay = (key: string): void => {
class="flex flex-col gap-1"
:class="{
'absolute inset-0 overflow-y-auto overscroll-contain':
expandedDays.includes(dayKey(day)),
isDayExpanded(day),
}"
:data-testid="`calendar-items-${dayKey(day)}`"
>
Expand All @@ -769,6 +773,7 @@ const goToDay = (key: string): void => {
v-if="item.post"
:post="item.post"
:timezone="timezone"
:compact="!isDesktop"
/>
<CalendarSlotChip
v-else-if="item.slot"
Expand All @@ -780,45 +785,55 @@ const goToDay = (key: string): void => {
"
:timezone="timezone"
:can-create-post="canCreatePost"
:compact="!isDesktop"
/>
</template>
</div>
</div>
<button
v-if="
itemsFor(day).length > MONTH_CHIPS
"
type="button"
class="inline-flex h-6 shrink-0 items-center gap-1 self-end rounded-md px-2 text-xs font-medium text-foreground transition-control hover:bg-accent"
:aria-expanded="
expandedDays.includes(dayKey(day))
"
:data-testid="`calendar-more-${dayKey(day)}`"
@click="toggleDay(dayKey(day))"
>
<IconChevronDown
class="size-3.5 transition-transform"
:class="{
'rotate-180':
expandedDays.includes(
dayKey(day),
v-if="hiddenCount(day) > 0"
type="button"
class="inline-flex shrink-0 items-center gap-1 rounded-md text-xs font-medium text-foreground transition-control hover:bg-accent"
:class="
isDesktop
? 'h-6 self-end px-2'
: 'h-7 min-w-7 justify-center self-start px-1'
"
:aria-expanded="isDayExpanded(day)"
:aria-label="
isDesktop
? undefined
: isDayExpanded(day)
? $t('calendar.less')
: $t('calendar.more', {
count: String(
hiddenCount(day),
),
}"
/>
{{
expandedDays.includes(
dayKey(day),
)
? $t('calendar.less')
: $t('calendar.more', {
count: String(
itemsFor(day)
.length -
MONTH_CHIPS,
),
})
}}
</button>
})
"
:data-testid="`calendar-more-${dayKey(day)}`"
@click="toggleDay(dayKey(day))"
>
<IconChevronDown
v-if="isDesktop || isDayExpanded(day)"
class="size-3.5 transition-transform"
:class="{
'rotate-180': isDayExpanded(day),
}"
/>
<template v-if="isDesktop">{{
isDayExpanded(day)
? $t('calendar.less')
: $t('calendar.more', {
count: String(
hiddenCount(day),
),
})
}}</template>
<template v-else-if="!isDayExpanded(day)"
>+{{ hiddenCount(day) }}</template
>
</button>
</div>
</div>
</div>
Expand Down
Loading
Loading