Skip to content

Commit

Permalink
feat: open journal from calendar view click
Browse files Browse the repository at this point in the history
  • Loading branch information
srg-kostyrko committed Sep 10, 2024
1 parent f7e8e74 commit c299cba
Show file tree
Hide file tree
Showing 15 changed files with 219 additions and 59 deletions.
119 changes: 86 additions & 33 deletions src/calendar-view/CalendarView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,39 @@ import CalendarMonth from "../components/calendar/CalendarMonth.vue";
import DatePickerModal from "../components/modals/DatePicker.modal.vue";
import { VueModal } from "../components/modals/vue-modal";
import { today, date_from_string } from "../calendar";
import {
journalsWithDays$,
journalsWithWeeks$,
journalsWithMonths$,
journalsWithQuarters$,
journalsWithYears$,
} from "../stores/settings.store";
import { openDate } from "@/journals/open-date";
const redDateMoment = ref(today());
const refDate = computed(() => redDateMoment.value.format("YYYY-MM-DD"));
const refDateMoment = ref(today());
const refDate = computed(() => refDateMoment.value.format("YYYY-MM-DD"));
const daysClickable = computed(() => {
return journalsWithDays$.value.length > 0;
});
const weeksClickable = computed(() => {
return journalsWithWeeks$.value.length > 0;
});
const monthsClickable = computed(() => {
return journalsWithMonths$.value.length > 0;
});
const quartersClickable = computed(() => {
return journalsWithQuarters$.value.length > 0;
});
const yearsClickable = computed(() => {
return journalsWithYears$.value.length > 0;
});
function navigate(amount: number, step: "month" | "year" = "month") {
redDateMoment.value = redDateMoment.value.clone().add(amount, step);
refDateMoment.value = refDateMoment.value.clone().add(amount, step);
}
function goToday() {
redDateMoment.value = today();
refDateMoment.value = today();
}
function pickDate() {
new VueModal(
Expand All @@ -23,12 +47,29 @@ function pickDate() {
{
selectedDate: refDate.value,
onSelect(date: string) {
redDateMoment.value = date_from_string(date);
refDateMoment.value = date_from_string(date);
},
},
400,
).open();
}
function openDay(date: string, event: MouseEvent) {
openDate(date, journalsWithDays$.value, event);
}
function openWeek(date: string, event: MouseEvent) {
openDate(date, journalsWithWeeks$.value, event);
}
function openMonth(event: MouseEvent) {
openDate(refDate.value, journalsWithMonths$.value, event);
}
function openQuarter(event: MouseEvent) {
openDate(refDate.value, journalsWithQuarters$.value, event);
}
function openYear(event: MouseEvent) {
openDate(refDate.value, journalsWithYears$.value, event);
}
// TODO slim header to avoid scroll
</script>

<template>
Expand All @@ -37,33 +78,36 @@ function pickDate() {
<ObsidianIconButton icon="crosshair" tooltip="Select a date to be displayed" @click="pickDate" />
<ObsidianButton @click="goToday">Today</ObsidianButton>
</div>
<CalendarMonth :ref-date="refDate">
<CalendarMonth
:ref-date="refDate"
:select-days="daysClickable"
:select-weeks="weeksClickable"
@select="openDay"
@select-week="openWeek"
>
<template #header>
<ObsidianIconButton
class="clickable-icon"
icon="chevrons-left"
tooltip="Previous year"
@click="navigate(-1, 'year')"
/>
<ObsidianIconButton
class="clickable-icon"
icon="chevron-left"
tooltip="Previous month"
@click="navigate(-1, 'month')"
/>
{{ redDateMoment.format("MMMM YYYY") }}
<ObsidianIconButton
class="clickable-icon"
icon="chevron-right"
tooltip="Next month"
@click="navigate(1, 'month')"
/>
<ObsidianIconButton
class="clickable-icon"
icon="chevrons-right"
tooltip="Next year"
@click="navigate(1, 'year')"
/>
<ObsidianIconButton icon="chevrons-left" tooltip="Previous year" @click="navigate(-1, 'year')" />
<ObsidianIconButton icon="chevron-left" tooltip="Previous month" @click="navigate(-1, 'month')" />
<div class="calendar-month-header">
<ObsidianButton v-if="monthsClickable" flat @click="openMonth">
{{ refDateMoment.format("MMMM") }}
</ObsidianButton>
<span v-else>
{{ refDateMoment.format("MMMM") }}
</span>
<ObsidianButton v-if="quartersClickable" flat @click="openQuarter">
{{ refDateMoment.format("[Q]Q") }}
</ObsidianButton>
<ObsidianButton v-if="yearsClickable" flat @click="openYear">
{{ refDateMoment.format("YYYY") }}
</ObsidianButton>
<span v-else class="header-non-interactive">
{{ refDateMoment.format("YYYY") }}
</span>
</div>

<ObsidianIconButton icon="chevron-right" tooltip="Next month" @click="navigate(1, 'month')" />
<ObsidianIconButton icon="chevrons-right" tooltip="Next year" @click="navigate(1, 'year')" />
</template>
</CalendarMonth>
</div>
Expand All @@ -74,7 +118,16 @@ function pickDate() {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
gap: 12px;
padding-bottom: 12px;
}
.calendar-month-header {
display: flex;
align-items: center;
gap: 2px;
}
.header-non-interactive {
color: var(--icon-color);
opacity: var(--icon-opacity);
font-size: var(--font-ui-small);
}
</style>
1 change: 0 additions & 1 deletion src/components/calendar/CalendarDay.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ defineProps<{

<style scoped>
button {
cursor: pointer;
background-color: transparent;
box-shadow: none;
}
Expand Down
30 changes: 23 additions & 7 deletions src/components/calendar/CalendarMonth.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,20 @@ import { calendarViewSettings$ } from "../../stores/settings.store";
import CalendarDay from "./CalendarDay.vue";
import CalendarWeekNumber from "./CalendarWeekNumber.vue";
const props = defineProps<{
refDate: string;
selectedDate?: string;
}>();
defineEmits<(e: "select" | "selectWeek", date: string) => void>();
const props = withDefaults(
defineProps<{
refDate: string;
selectedDate?: string | null;
selectDays: boolean;
selectWeeks: boolean;
}>(),
{
selectedDate: null,
selectDays: true,
selectWeeks: false,
},
);
defineEmits<(e: "select" | "selectWeek", date: string, event: MouseEvent) => void>();
const { refDate } = toRefs(props);
const momentDate = computed(() => date_from_string(refDate.value));
Expand All @@ -37,7 +46,10 @@ const { grid } = useMonth(refDate);
v-if="uiDate.isWeekNumber"
:date="uiDate.date"
class="calendar-week-number"
@click="$emit('selectWeek', uiDate.date.format('YYYY-MM-DD'))"
:class="{
'calendar-clickable': selectWeeks,
}"
@click="selectWeeks && $emit('selectWeek', uiDate.date.format('YYYY-MM-DD'), $event)"
/>
<CalendarDay
v-else
Expand All @@ -47,8 +59,9 @@ const { grid } = useMonth(refDate);
'calendar-day--outside': uiDate.outside,
'calendar-day--today': uiDate.today,
'calendar-day--selected': selectedDate === uiDate.key,
'calendar-clickable': selectDays,
}"
@click="$emit('select', uiDate.date.format('YYYY-MM-DD'))"
@click="selectDays && $emit('select', uiDate.date.format('YYYY-MM-DD'), $event)"
/>
</template>
</div>
Expand Down Expand Up @@ -93,4 +106,7 @@ const { grid } = useMonth(refDate);
background-color: var(--interactive-accent);
color: var(--text-on-accent);
}
.calendar-clickable {
cursor: pointer;
}
</style>
1 change: 0 additions & 1 deletion src/components/calendar/CalendarWeekNumber.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ defineProps<{

<style scoped>
button {
cursor: pointer;
background-color: transparent;
box-shadow: none;
}
Expand Down
3 changes: 2 additions & 1 deletion src/components/obsidian/ObsidianButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ defineProps<{
warning?: boolean;
type?: "button" | "submit" | "reset";
tooltip?: string;
flat?: boolean;
}>();
</script>

<template>
<button
:class="{ 'mod-cta': cta, 'mod-warning': warning }"
:class="{ 'mod-cta': cta, 'mod-warning': warning, 'clickable-icon': flat && !cta }"
:disabled="disabled"
:type="type ?? 'button'"
:aria-label="tooltip"
Expand Down
8 changes: 7 additions & 1 deletion src/components/obsidian/ObsidianIconButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ const emit = defineEmits(["click"]);
</script>

<template>
<ObsidianButton :tooltip="tooltip">
<ObsidianButton :tooltip="tooltip" flat class="icon-button">
<ObsidianIcon :name="icon" @click="emit('click')" />
</ObsidianButton>
</template>

<style scoped>
.icon-button {
padding: 4px 2px;
}
</style>
27 changes: 27 additions & 0 deletions src/components/suggests/journal-suggest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { type App, SuggestModal } from "obsidian";

interface JournalOption {
id: string;
name: string;
}

export class JournalSuggestModal extends SuggestModal<JournalOption> {
constructor(
app: App,
private journals: JournalOption[],
private cb: (journalId: string) => void,
) {
super(app);
}

getSuggestions(query: string): JournalOption[] | Promise<JournalOption[]> {
query = query.toLocaleLowerCase();
return this.journals.filter((journal) => journal.name.toLocaleLowerCase().contains(query));
}
renderSuggestion(value: JournalOption, el: HTMLElement) {
el.setText(value.name);
}
onChooseSuggestion(item: JournalOption) {
this.cb(item.id);
}
}
5 changes: 4 additions & 1 deletion src/journals/journal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ export class Journal {
this.#config = computed(() => journals$.value[id]);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
this.#intervalResolver = new FixedInterval(id, () => this.#config.value.write);
this.#intervalResolver = new FixedInterval(
id,
computed(() => this.#config.value.write),
);
}

registerCommands(): void {
Expand Down
38 changes: 38 additions & 0 deletions src/journals/open-date.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Menu } from "obsidian";
import { app$, plugin$ } from "@/stores/obsidian.store";
import { JournalSuggestModal } from "@/components/suggests/journal-suggest";

export async function openDate(
date: string,
journals: { id: string; name: string }[],
event?: MouseEvent,
): Promise<void> {
if (!journals.length) return;
if (journals.length === 1) {
const [{ id }] = journals;
await openDateInJournal(date, id);
return;
}

if (event) {
const menu = new Menu();
for (const journal of journals) {
menu.addItem((item) => {
item.setTitle(journal.name).onClick(() => {
openDateInJournal(date, journal.id);
});
});
}
menu.showAtMouseEvent(event);
} else {
new JournalSuggestModal(app$.value, journals, (journalId) => openDateInJournal(date, journalId)).open();
}
}

export async function openDateInJournal(date: string, journalId: string): Promise<void> {
const journal = plugin$.value.getJournal(journalId);
if (!journal) return;
const metadata = await journal.find(date);
if (!metadata) return;
await journal.open(metadata);
}
4 changes: 4 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ export default class JournalPlugin extends Plugin {
return this.#index;
}

getJournal(id: string): Journal | undefined {
return this.#journals.get(id);
}

createJournal(id: string, name: string, write: JournalSettings["write"]): void {
const settings: JournalSettings = {
...structuredClone(defaultJournalSettings),
Expand Down
4 changes: 2 additions & 2 deletions src/settings/JournalSettingsEdit.vue
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,8 @@ watch(
{{ command.name }}
</template>
<template #description> {{ command.type }} in {{ command.context }} </template>
<ObsidianIconButton class="clickable-icon" icon="pencil" tooltip="Edit" @click="editCommand(command, index)" />
<ObsidianIconButton class="clickable-icon" icon="trash-2" tooltip="Delete" @click="deleteCommand(index)" />
<ObsidianIconButton icon="pencil" tooltip="Edit" @click="editCommand(command, index)" />
<ObsidianIconButton icon="trash-2" tooltip="Delete" @click="deleteCommand(index)" />
</ObsidianSetting>
</template>
</template>
Expand Down
14 changes: 2 additions & 12 deletions src/settings/JournalSettingsList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,8 @@ const journalsList = computed(() => Object.values(journals$.value).toSorted((a,
<span class="flair">{{ journal.write.type }}</span>
</template>
<template #description>ID: {{ journal.id }}</template>
<ObsidianIconButton
class="clickable-icon"
icon="pencil"
:tooltip="'Edit ' + journal.name"
@click="$emit('edit', journal.id)"
/>
<ObsidianIconButton
class="clickable-icon"
icon="trash-2"
:tooltip="'Delete ' + journal.name"
@click="$emit('remove', journal.id)"
/>
<ObsidianIconButton icon="pencil" :tooltip="'Edit ' + journal.name" @click="$emit('edit', journal.id)" />
<ObsidianIconButton icon="trash-2" :tooltip="'Delete ' + journal.name" @click="$emit('remove', journal.id)" />
</ObsidianSetting>
</template>
</template>
Loading

0 comments on commit c299cba

Please sign in to comment.