Skip to content

Commit

Permalink
feat: add journal shelves
Browse files Browse the repository at this point in the history
  • Loading branch information
srg-kostyrko committed Oct 27, 2024
1 parent 2de8845 commit 6419337
Show file tree
Hide file tree
Showing 15 changed files with 192 additions and 89 deletions.
61 changes: 40 additions & 21 deletions src/calendar-view/CalendarView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,40 @@ 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$,
pluginSettings$,
journalsList$,
} from "../stores/settings.store";
import { pluginSettings$, journalsList$ } from "../stores/settings.store";
import { openDate } from "@/journals/open-date";
import CalendarMonthButton from "@/components/calendar/CalendarMonthButton.vue";
import CalendarYearButton from "@/components/calendar/CalendarYearButton.vue";
import CalendarQuarterButton from "@/components/calendar/CalendarQuarterButton.vue";
import { ShelfSuggestModal } from "@/components/suggests/shelf-suggest";
import { app$ } from "@/stores/obsidian.store";
import { useShelfProvider } from "@/composables/use-shelf";
const refDateMoment = ref(today());
const refDate = computed(() => refDateMoment.value.format("YYYY-MM-DD"));
const selectedShelf = ref<string | null>(null);
const selectedShelf = computed({
get() {
return pluginSettings$.value.ui.calendarShelf;
},
set(value) {
pluginSettings$.value.ui.calendarShelf = value;
},
});
const shouldShowShelf = computed(() => {
return (
(pluginSettings$.value.useShelves && Object.values(pluginSettings$.value.shelves).length > 0) ||
journalsList$.value.some((journal) => journal.shelves.length === 0)
);
});
const { journals } = useShelfProvider(selectedShelf);
const daysClickable = computed(() => {
return journalsWithDays$.value.length > 0;
return journals.day.value.length > 0;
});
const weeksClickable = computed(() => {
return journalsWithWeeks$.value.length > 0;
});
const quartersClickable = computed(() => {
return journalsWithQuarters$.value.length > 0;
return journals.week.value.length > 0;
});
function selectShelf() {
Expand Down Expand Up @@ -70,19 +69,39 @@ function pickDate() {
}
function openDay(date: string, event: MouseEvent) {
openDate(date, journalsWithDays$.value, event).catch(console.error);
openDate(
date,
journals.day.value.map((journal) => journal.name),
event,
).catch(console.error);
}
function openWeek(date: string, event: MouseEvent) {
openDate(date, journalsWithWeeks$.value, event).catch(console.error);
openDate(
date,
journals.week.value.map((journal) => journal.name),
event,
).catch(console.error);
}
function openMonth(event: MouseEvent) {
openDate(refDate.value, journalsWithMonths$.value, event).catch(console.error);
openDate(
refDate.value,
journals.month.value.map((journal) => journal.name),
event,
).catch(console.error);
}
function openQuarter(event: MouseEvent) {
openDate(refDate.value, journalsWithQuarters$.value, event).catch(console.error);
openDate(
refDate.value,
journals.quarter.value.map((journal) => journal.name),
event,
).catch(console.error);
}
function openYear(event: MouseEvent) {
openDate(refDate.value, journalsWithYears$.value, event).catch(console.error);
openDate(
refDate.value,
journals.year.value.map((journal) => journal.name),
event,
).catch(console.error);
}
// TODO slim header to avoid scroll
</script>
Expand All @@ -108,7 +127,7 @@ function openYear(event: MouseEvent) {
<ObsidianIconButton icon="chevron-left" tooltip="Previous month" @click="navigate(-1, 'month')" />
<div class="calendar-month-header">
<CalendarMonthButton :date="refDateMoment" @select="openMonth" />
<CalendarQuarterButton v-if="quartersClickable" :date="refDateMoment" @select="openQuarter" />
<CalendarQuarterButton v-if="journals.quarter.value.length > 0" :date="refDateMoment" @select="openQuarter" />
<CalendarYearButton :date="refDateMoment" @select="openYear" />
</div>

Expand Down
14 changes: 10 additions & 4 deletions src/code-blocks/navigation/NavigationBlock.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
<script setup lang="ts">
import { computed } from "vue";
import { journals$, journalsList$ } from "@/stores/settings.store";
import { journals$ } from "@/stores/settings.store";
import NavigationBlockRow from "./NavigationBlockRow.vue";
import { plugin$ } from "@/stores/obsidian.store";
import { openDate, openDateInJournal } from "@/journals/open-date";
import { useShelfProvider } from "@/composables/use-shelf";
import { NavBlockRow } from "@/types/settings.types";

Check failure on line 8 in src/code-blocks/navigation/NavigationBlock.vue

View workflow job for this annotation

GitHub Actions / build

'NavBlockRow' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.
const props = defineProps<{
refDate: string;
Expand All @@ -14,17 +16,21 @@ defineEmits<(event: "move-up" | "move-down" | "edit" | "remove", index: number)
const journalSettings = computed(() => journals$.value[props.journalName]);
const journal = computed(() => plugin$.value.getJournal(props.journalName));
const shelfName = computed(() => journalSettings.value?.shelves[0] ?? null);
async function navigate(type: string, date: string, journalName?: string) {
const { journals } = useShelfProvider(shelfName);
async function navigate(type: NavBlockRow["link"], date: string, journalName?: string) {
if (type === "none") return;
if (type === "self") {
const metadata = journal.value?.get(props.refDate);
if (metadata) await journal.value?.open(metadata);
} else if (type === "journal") {
if (!journalName) return;
await openDateInJournal(date, journalName);
} else {
const journals = journalsList$.value.filter((journal) => journal.write.type === type).map(({ name }) => name);
await openDate(date, journals);
const journalsToUse = journals[type].value.map((journal) => journal.name);
await openDate(date, journalsToUse);
}
}
</script>
Expand Down
14 changes: 12 additions & 2 deletions src/code-blocks/navigation/NavigationBlockRow.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<script setup lang="ts">
import { SHELF_DATA_KEY } from "@/constants";
import type { Journal } from "@/journals/journal";
import type { NavBlockRow } from "@/types/settings.types";
import { replaceTemplateVariables } from "@/utils/template";
import { computed } from "vue";
import { computed, inject } from "vue";
const props = defineProps<{
row: NavBlockRow;
Expand Down Expand Up @@ -51,7 +52,16 @@ const text = computed(() => {
const fontSize = computed(() => `${props.row.fontSize}em`);
const fontWeight = computed(() => (props.row.bold ? "bold" : "normal"));
const fontStyle = computed(() => (props.row.italic ? "italic" : "normal"));
const cursor = computed(() => (props.row.link === "none" ? "default" : "pointer"));
const { journals } = inject(SHELF_DATA_KEY);

Check failure on line 56 in src/code-blocks/navigation/NavigationBlockRow.vue

View workflow job for this annotation

GitHub Actions / build

Property 'journals' does not exist on type 'ProvidedShelfData | undefined'.
const isClickable = computed(() => {
if (props.row.link === "none") return false;
if (props.row.link === "self") return true;
if (props.row.link === "journal") return Boolean(props.row.journal);
return journals[props.row.link].value.length > 0;
});
const cursor = computed(() => (isClickable.value ? "pointer" : "default"));
function onClick() {
if (props.row.link === "none") return;
Expand Down
7 changes: 4 additions & 3 deletions src/components/calendar/CalendarDay.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
<script setup lang="ts">
import { toRefs } from "vue";
import { inject, toRefs } from "vue";
import type { MomentDate } from "../../types/date.types";
import { useDecorations } from "@/composables/use-decorations";
import CalendarDecoration from "./CalendarDecoration.vue";
import { decorationsForDays$ } from "@/stores/settings.store";
import { SHELF_DATA_KEY } from "@/constants";
const props = defineProps<{
date: MomentDate;
}>();
const { date } = toRefs(props);
const decorationsStyles = useDecorations(date, decorationsForDays$);
const { decorations } = inject(SHELF_DATA_KEY);
const decorationsStyles = useDecorations(date, decorations.day);
</script>

<template>
Expand Down
11 changes: 6 additions & 5 deletions src/components/calendar/CalendarMonthButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,28 @@
import { useDecorations } from "@/composables/use-decorations";
import type { MomentDate } from "@/types/date.types";
import CalendarDecoration from "./CalendarDecoration.vue";
import { decorationsForMonths$, journalsWithMonths$ } from "@/stores/settings.store";
import ObsidianButton from "../obsidian/ObsidianButton.vue";
import { toRefs } from "vue";
import { inject, toRefs } from "vue";
import { SHELF_DATA_KEY } from "@/constants";
const props = defineProps<{
date: MomentDate;
}>();
const { date } = toRefs(props);
const emit = defineEmits<(event: "select", nativeEvent: MouseEvent, date: MomentDate) => void>();
const decorationsStyles = useDecorations(date, decorationsForMonths$);
const { journals, decorations } = inject(SHELF_DATA_KEY);
const decorationsStyles = useDecorations(date, decorations.month);
function select(event: MouseEvent) {
if (journalsWithMonths$.value.length === 0) {
if (journals.month.value.length === 0) {
return;
}
emit("select", event, props.date);
}
</script>

<template>
<ObsidianButton class="month-button" flat :disabled="journalsWithMonths$.length === 0" @click="select">
<ObsidianButton class="month-button" flat :disabled="journals.month.value.length === 0" @click="select">
<CalendarDecoration :styles="decorationsStyles">{{ date.format("MMMM") }}</CalendarDecoration>
</ObsidianButton>
</template>
Expand Down
11 changes: 6 additions & 5 deletions src/components/calendar/CalendarQuarterButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,28 @@
import { useDecorations } from "@/composables/use-decorations";
import type { MomentDate } from "@/types/date.types";
import CalendarDecoration from "./CalendarDecoration.vue";
import { decorationsForQuarters$, journalsWithQuarters$ } from "@/stores/settings.store";
import ObsidianButton from "../obsidian/ObsidianButton.vue";
import { toRefs } from "vue";
import { inject, toRefs } from "vue";
import { SHELF_DATA_KEY } from "@/constants";
const props = defineProps<{
date: MomentDate;
}>();
const { date } = toRefs(props);
const emit = defineEmits<(event: "select", nativeEvent: MouseEvent, date: MomentDate) => void>();
const decorationsStyles = useDecorations(date, decorationsForQuarters$);
const { journals, decorations } = inject(SHELF_DATA_KEY);
const decorationsStyles = useDecorations(date, decorations.quarter);
function select(event: MouseEvent) {
if (journalsWithQuarters$.value.length === 0) {
if (journals.quarter.value.length === 0) {
return;
}
emit("select", event, props.date);
}
</script>

<template>
<ObsidianButton class="quarter-button" flat :disabled="journalsWithQuarters$.length === 0" @click="select">
<ObsidianButton class="quarter-button" flat :disabled="journals.quarter.value.length === 0" @click="select">
<CalendarDecoration :styles="decorationsStyles">{{ date.format("[Q]Q") }}</CalendarDecoration>
</ObsidianButton>
</template>
Expand Down
7 changes: 4 additions & 3 deletions src/components/calendar/CalendarWeekNumber.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
import { useDecorations } from "@/composables/use-decorations";
import type { MomentDate } from "@/types/date.types";
import CalendarDecoration from "./CalendarDecoration.vue";
import { decorationsForWeeks$ } from "@/stores/settings.store";
import { toRefs } from "vue";
import { inject, toRefs } from "vue";
import { SHELF_DATA_KEY } from "@/constants";
const props = defineProps<{
date: MomentDate;
}>();
const { date } = toRefs(props);
const decorationsStyles = useDecorations(date, decorationsForWeeks$);
const { decorations } = inject(SHELF_DATA_KEY);
const decorationsStyles = useDecorations(date, decorations.week);
</script>

<template>
Expand Down
13 changes: 7 additions & 6 deletions src/components/calendar/CalendarYearButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,28 @@
import { useDecorations } from "@/composables/use-decorations";
import type { MomentDate } from "@/types/date.types";
import CalendarDecoration from "./CalendarDecoration.vue";
import { decorationsForYears$, journalsWithYears$ } from "@/stores/settings.store";
import ObsidianButton from "../obsidian/ObsidianButton.vue";
import { toRefs } from "vue";
import { inject, toRefs } from "vue";
import { SHELF_DATA_KEY } from "@/constants";
const props = defineProps<{
date: MomentDate;
}>();
const { date } = toRefs(props);
const emit = defineEmits<(event: "select", event: MouseEvent, date: MomentDate) => void>();
const emit = defineEmits<(event_: "select", event: MouseEvent, date: MomentDate) => void>();
const decorationsStyles = useDecorations(date, decorationsForYears$);
const { journals, decorations } = inject(SHELF_DATA_KEY);
const decorationsStyles = useDecorations(date, decorations.year);
function select(event: MouseEvent) {
if (journalsWithYears$.value.length === 0) {
if (journals.year.value.length === 0) {
return;
}
emit("select", event, props.date);
}
</script>

<template>
<ObsidianButton class="year-button" flat :disabled="journalsWithYears$.length === 0" @click="select">
<ObsidianButton class="year-button" flat :disabled="journals.year.value.length === 0" @click="select">
<CalendarDecoration :styles="decorationsStyles">{{ date.format("YYYY") }}</CalendarDecoration>
</ObsidianButton>
</template>
Expand Down
67 changes: 67 additions & 0 deletions src/composables/use-shelf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { SHELF_DATA_KEY } from "@/constants";
import { journalsList$, pluginSettings$ } from "@/stores/settings.store";
import type { ProvidedShelfData } from "@/types/provided-data.types";
import type { JournalSettings } from "@/types/settings.types";
import { computed, type MaybeRef, provide, toRef } from "vue";

export function useShelfProvider(shelfName: MaybeRef<string | null>) {
const _shelfName = toRef(shelfName);

const journals = computed(() => {
const name = _shelfName.value;
if (pluginSettings$.value.useShelves && name) {
return journalsList$.value.filter((journal) => journal.shelves.includes(name));
}
return journalsList$.value;
});
const dailyJournals = computed(() => journals.value.filter((journal) => journal.write.type === "day"));
const weeklyJournals = computed(() => journals.value.filter((journal) => journal.write.type === "week"));
const monthlyJournals = computed(() => journals.value.filter((journal) => journal.write.type === "month"));
const quarterlyJournals = computed(() => journals.value.filter((journal) => journal.write.type === "quarter"));
const yearlyJournals = computed(() => journals.value.filter((journal) => journal.write.type === "year"));
const customJournals = computed(() => journals.value.filter((journal) => journal.write.type === "custom"));
const weekdaysJournals = computed(() => journals.value.filter((journal) => journal.write.type === "weekdays"));

const dailyDecorations = computed(() => prepareDecorations(dailyJournals.value));
const weeklyDecorations = computed(() => prepareDecorations(weeklyJournals.value));
const monthlyDecorations = computed(() => prepareDecorations(monthlyJournals.value));
const quarterlyDecorations = computed(() => prepareDecorations(quarterlyJournals.value));
const yearlyDecorations = computed(() => prepareDecorations(yearlyJournals.value));
const customDecorations = computed(() => prepareDecorations(customJournals.value));
const weekdaysDecorations = computed(() => prepareDecorations(weekdaysJournals.value));

const providedShelfData: ProvidedShelfData = {
journals: {
all: journals,
day: dailyJournals,
week: weeklyJournals,
month: monthlyJournals,
quarter: quarterlyJournals,
year: yearlyJournals,
custom: customJournals,
weekdays: weekdaysJournals,
},
decorations: {
day: dailyDecorations,
week: weeklyDecorations,
month: monthlyDecorations,
quarter: quarterlyDecorations,
year: yearlyDecorations,
custom: customDecorations,
weekdays: weekdaysDecorations,
},
};

provide(SHELF_DATA_KEY, providedShelfData);

return providedShelfData;
}

function prepareDecorations(journals: JournalSettings[]) {
return journals.flatMap((journal) =>
journal.decorations.map((decoration) => ({
journalName: journal.name,
decoration,
})),
);
}
3 changes: 3 additions & 0 deletions src/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import type {

export const defaultPluginSettings: PluginSettings = {
version: 2,
ui: {
calendarShelf: null,
},
useShelves: false,
showReloadHint: false,
journals: {},
Expand Down
Loading

0 comments on commit 6419337

Please sign in to comment.