mirror of
https://github.com/zadam/trilium.git
synced 2025-12-06 15:34:26 +01:00
chore(react/launch_bar): bring back week highlighting
This commit is contained in:
parent
20f44cc64f
commit
185e5691a4
@ -1,109 +0,0 @@
|
|||||||
import { t } from "../../services/i18n.js";
|
|
||||||
import dateNoteService from "../../services/date_notes.js";
|
|
||||||
import server from "../../services/server.js";
|
|
||||||
import appContext from "../../components/app_context.js";
|
|
||||||
import RightDropdownButtonWidget from "./right_dropdown_button.js";
|
|
||||||
import toastService from "../../services/toast.js";
|
|
||||||
import options from "../../services/options.js";
|
|
||||||
import { Dropdown } from "bootstrap";
|
|
||||||
import type { EventData } from "../../components/app_context.js";
|
|
||||||
import { dayjs, type Dayjs } from "@triliumnext/commons";
|
|
||||||
import type { AttributeRow, OptionDefinitions } from "@triliumnext/commons";
|
|
||||||
|
|
||||||
interface WeekCalculationOptions {
|
|
||||||
firstWeekType: number;
|
|
||||||
minDaysInFirstWeek: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class CalendarWidget extends RightDropdownButtonWidget {
|
|
||||||
private $month!: JQuery<HTMLElement>;
|
|
||||||
private $weekHeader!: JQuery<HTMLElement>;
|
|
||||||
private $monthSelect!: JQuery<HTMLElement>;
|
|
||||||
private $yearSelect!: JQuery<HTMLElement>;
|
|
||||||
private $next!: JQuery<HTMLElement>;
|
|
||||||
private $previous!: JQuery<HTMLElement>;
|
|
||||||
private $nextYear!: JQuery<HTMLElement>;
|
|
||||||
private $previousYear!: JQuery<HTMLElement>;
|
|
||||||
private monthDropdown!: Dropdown;
|
|
||||||
// stored in ISO 1–7
|
|
||||||
private firstDayOfWeekISO!: number;
|
|
||||||
private weekCalculationOptions!: WeekCalculationOptions;
|
|
||||||
private activeDate: Dayjs | null = null;
|
|
||||||
private todaysDate!: Dayjs;
|
|
||||||
private date!: Dayjs;
|
|
||||||
private weekNoteEnable: boolean = false;
|
|
||||||
private weekNotes: string[] = [];
|
|
||||||
|
|
||||||
constructor(title: string = "", icon: string = "") {
|
|
||||||
super(title, icon, DROPDOWN_TPL, "calendar-dropdown-menu");
|
|
||||||
}
|
|
||||||
|
|
||||||
doRender() {
|
|
||||||
super.doRender();
|
|
||||||
|
|
||||||
this.$month = this.$dropdownContent.find('[data-calendar-area="month"]');
|
|
||||||
|
|
||||||
this.manageFirstDayOfWeek();
|
|
||||||
this.initWeekCalculation();
|
|
||||||
|
|
||||||
// Handle click events for the entire calendar widget
|
|
||||||
this.$dropdownContent.on("click", (e) => {
|
|
||||||
const $target = $(e.target);
|
|
||||||
|
|
||||||
// Keep dropdown open when clicking on month select button or year selector area
|
|
||||||
if ($target.closest('.btn.dropdown-toggle.select-button').length) {
|
|
||||||
e.stopPropagation();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hide dropdown for all other cases
|
|
||||||
this.monthDropdown.hide();
|
|
||||||
// Prevent dismissing the calendar popup by clicking on an empty space inside it.
|
|
||||||
e.stopPropagation();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
initWeekCalculation() {
|
|
||||||
this.weekCalculationOptions = {
|
|
||||||
firstWeekType: options.getInt("firstWeekOfYear") || 0,
|
|
||||||
minDaysInFirstWeek: options.getInt("minDaysInFirstWeek") || 4
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async dropdownShown() {
|
|
||||||
await this.getWeekNoteEnable();
|
|
||||||
this.weekNotes = await server.get<string[]>(`attribute-values/weekNote`);
|
|
||||||
this.init( ?? null);
|
|
||||||
}
|
|
||||||
|
|
||||||
createWeekNumber(weekNumber: number) {
|
|
||||||
|
|
||||||
let $newWeekNumber;
|
|
||||||
|
|
||||||
if (this.weekNoteEnable) {
|
|
||||||
if (this.weekNotes.includes(weekNoteId)) {
|
|
||||||
$newWeekNumber.addClass("calendar-date-exists").attr("data-href", `#root/${weekNoteId}`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
$newWeekNumber.addClass("calendar-week-number").attr("data-calendar-week-number", weekNoteId);
|
|
||||||
return $newWeekNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
|
|
||||||
const WEEK_OPTIONS: (keyof OptionDefinitions)[] = [
|
|
||||||
"firstDayOfWeek",
|
|
||||||
"firstWeekOfYear",
|
|
||||||
"minDaysInFirstWeek",
|
|
||||||
];
|
|
||||||
if (!WEEK_OPTIONS.some(opt => loadResults.getOptionNames().includes(opt))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.manageFirstDayOfWeek();
|
|
||||||
this.initWeekCalculation();
|
|
||||||
this.createMonth();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,86 +0,0 @@
|
|||||||
import { handleRightToLeftPlacement } from "../../services/utils.js";
|
|
||||||
import BasicWidget from "../basic_widget.js";
|
|
||||||
import { Tooltip, Dropdown } from "bootstrap";
|
|
||||||
type PopoverPlacement = Tooltip.PopoverPlacement;
|
|
||||||
|
|
||||||
const TPL = /*html*/`
|
|
||||||
<div class="dropdown right-dropdown-widget">
|
|
||||||
<button type="button" data-bs-toggle="dropdown"
|
|
||||||
aria-haspopup="true" aria-expanded="false"
|
|
||||||
class="bx right-dropdown-button launcher-button">
|
|
||||||
<div class="tooltip-trigger"></div>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div class="dropdown-menu"></div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default class RightDropdownButtonWidget extends BasicWidget {
|
|
||||||
protected iconClass: string;
|
|
||||||
protected title: string;
|
|
||||||
protected dropdownTpl: string;
|
|
||||||
protected settings: { titlePlacement: PopoverPlacement };
|
|
||||||
protected $dropdownMenu!: JQuery<HTMLElement>;
|
|
||||||
protected dropdown!: Dropdown;
|
|
||||||
protected $tooltip!: JQuery<HTMLElement>;
|
|
||||||
protected tooltip!: Tooltip;
|
|
||||||
private dropdownClass?: string;
|
|
||||||
public $dropdownContent!: JQuery<HTMLElement>;
|
|
||||||
|
|
||||||
constructor(title: string, iconClass: string, dropdownTpl: string, dropdownClass?: string) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.iconClass = iconClass;
|
|
||||||
this.title = title;
|
|
||||||
this.dropdownTpl = dropdownTpl;
|
|
||||||
this.dropdownClass = dropdownClass;
|
|
||||||
|
|
||||||
this.settings = {
|
|
||||||
titlePlacement: "right"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
doRender() {
|
|
||||||
this.$widget = $(TPL);
|
|
||||||
this.$dropdownMenu = this.$widget.find(".dropdown-menu");
|
|
||||||
if (this.dropdownClass) {
|
|
||||||
this.$dropdownMenu.addClass(this.dropdownClass);
|
|
||||||
}
|
|
||||||
this.dropdown = Dropdown.getOrCreateInstance(this.$widget.find("[data-bs-toggle='dropdown']")[0], {
|
|
||||||
popperConfig: {
|
|
||||||
placement: this.settings.titlePlacement,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$tooltip = this.$widget.find(".tooltip-trigger").attr("title", this.title);
|
|
||||||
this.tooltip = new Tooltip(this.$tooltip[0], {
|
|
||||||
placement: handleRightToLeftPlacement(this.settings.titlePlacement),
|
|
||||||
fallbackPlacements: [ handleRightToLeftPlacement(this.settings.titlePlacement) ]
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$widget
|
|
||||||
.find(".right-dropdown-button")
|
|
||||||
.addClass(this.iconClass)
|
|
||||||
.on("click", () => this.tooltip.hide())
|
|
||||||
.on("mouseenter", () => this.tooltip.show())
|
|
||||||
.on("mouseleave", () => this.tooltip.hide());
|
|
||||||
|
|
||||||
this.$widget.on("show.bs.dropdown", async () => {
|
|
||||||
await this.dropdownShown();
|
|
||||||
|
|
||||||
const rect = this.$dropdownMenu[0].getBoundingClientRect();
|
|
||||||
const windowHeight = $(window).height() || 0;
|
|
||||||
const pixelsToBottom = windowHeight - rect.bottom;
|
|
||||||
|
|
||||||
if (pixelsToBottom < 0) {
|
|
||||||
this.$dropdownMenu.css("top", pixelsToBottom);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$dropdownContent = $(this.dropdownTpl);
|
|
||||||
this.$widget.find(".dropdown-menu").append(this.$dropdownContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// to be overridden
|
|
||||||
async dropdownShown(): Promise<void> {}
|
|
||||||
}
|
|
||||||
@ -31,6 +31,7 @@ export interface CalendarArgs {
|
|||||||
activeDate: Dayjs | null;
|
activeDate: Dayjs | null;
|
||||||
onDateClicked(date: string, e: TargetedMouseEvent<HTMLAnchorElement>): void;
|
onDateClicked(date: string, e: TargetedMouseEvent<HTMLAnchorElement>): void;
|
||||||
onWeekClicked?: (week: string, e: TargetedMouseEvent<HTMLAnchorElement>) => void;
|
onWeekClicked?: (week: string, e: TargetedMouseEvent<HTMLAnchorElement>) => void;
|
||||||
|
weekNotes: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Calendar(args: CalendarArgs) {
|
export default function Calendar(args: CalendarArgs) {
|
||||||
@ -77,7 +78,7 @@ function PreviousMonthDays({ date, info: { dates, weekNumbers }, ...args }: { da
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CalendarWeek date={date} weekNumber={weekNumbers[0]} onWeekClicked={args.onWeekClicked} />
|
<CalendarWeek date={date} weekNumber={weekNumbers[0]} {...args} />
|
||||||
{dates.map(date => <CalendarDay date={date} dateNotesForMonth={dateNotesForPrevMonth} className="calendar-date-prev-month" {...args} />)}
|
{dates.map(date => <CalendarDay date={date} dateNotesForMonth={dateNotesForPrevMonth} className="calendar-date-prev-month" {...args} />)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
@ -97,7 +98,7 @@ function CurrentMonthDays({ date, firstDayOfWeekISO, ...args }: { date: Dayjs, f
|
|||||||
while (dateCursor.month() === currentMonth) {
|
while (dateCursor.month() === currentMonth) {
|
||||||
const weekNumber = getWeekNumber(dateCursor, firstDayOfWeekISO);
|
const weekNumber = getWeekNumber(dateCursor, firstDayOfWeekISO);
|
||||||
if (dateCursor.isoWeekday() === firstDayOfWeekISO) {
|
if (dateCursor.isoWeekday() === firstDayOfWeekISO) {
|
||||||
items.push(<CalendarWeek date={dateCursor} weekNumber={weekNumber} onWeekClicked={args.onWeekClicked} />)
|
items.push(<CalendarWeek date={dateCursor} weekNumber={weekNumber} {...args}/>)
|
||||||
}
|
}
|
||||||
|
|
||||||
items.push(<CalendarDay date={dateCursor} dateNotesForMonth={dateNotesForCurMonth} {...args} />)
|
items.push(<CalendarDay date={dateCursor} dateNotesForMonth={dateNotesForCurMonth} {...args} />)
|
||||||
@ -141,18 +142,25 @@ function CalendarDay({ date, dateNotesForMonth, className, activeDate, todaysDat
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function CalendarWeek({ date, weekNumber, onWeekClicked }: { weekNumber: number } & Pick<CalendarArgs, "date" | "onWeekClicked">) {
|
function CalendarWeek({ date, weekNumber, weekNotes, onWeekClicked }: { weekNumber: number, weekNotes: string[] } & Pick<CalendarArgs, "date" | "onWeekClicked">) {
|
||||||
|
const weekString = date.local().format('YYYY-') + 'W' + String(weekNumber).padStart(2, '0');
|
||||||
|
|
||||||
if (onWeekClicked) {
|
if (onWeekClicked) {
|
||||||
const weekNoteId = date.local().format('YYYY-') + 'W' + String(weekNumber).padStart(2, '0');
|
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
className="calendar-week-number calendar-date"
|
className={clsx("calendar-week-number", "calendar-date",
|
||||||
onClick={(e) => onWeekClicked(weekNoteId, e)}
|
weekNotes.includes(weekString) && "calendar-date-exists")}
|
||||||
|
data-calendar-week-number={weekNumber}
|
||||||
|
onClick={(e) => onWeekClicked(weekString, e)}
|
||||||
>{weekNumber}</a>
|
>{weekNumber}</a>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (<span className="calendar-week-number calendar-week-number-disabled">{weekNumber}</span>);
|
return (
|
||||||
|
<span
|
||||||
|
className="calendar-week-number calendar-week-number-disabled"
|
||||||
|
data-calendar-week-number={weekNumber}
|
||||||
|
>{weekNumber}</span>);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMonthInformation(date: Dayjs, firstDayISO: number, firstDayOfWeekISO: number) {
|
export function getMonthInformation(date: Dayjs, firstDayISO: number, firstDayOfWeekISO: number) {
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import toast from "../../services/toast";
|
|||||||
import date_notes from "../../services/date_notes";
|
import date_notes from "../../services/date_notes";
|
||||||
import { Dropdown } from "bootstrap";
|
import { Dropdown } from "bootstrap";
|
||||||
import search from "../../services/search";
|
import search from "../../services/search";
|
||||||
|
import server from "../../services/server";
|
||||||
|
|
||||||
const MONTHS = [
|
const MONTHS = [
|
||||||
t("calendar.january"),
|
t("calendar.january"),
|
||||||
@ -35,6 +36,7 @@ export default function CalendarWidget({ launcherNote }: { launcherNote: FNote }
|
|||||||
const [ date, setDate ] = useState<Dayjs>();
|
const [ date, setDate ] = useState<Dayjs>();
|
||||||
const dropdownRef = useRef<Dropdown>(null);
|
const dropdownRef = useRef<Dropdown>(null);
|
||||||
const [ enableWeekNotes, setEnableWeekNotes ] = useState(false);
|
const [ enableWeekNotes, setEnableWeekNotes ] = useState(false);
|
||||||
|
const [ weekNotes, setWeekNotes ] = useState<string[]>([]);
|
||||||
const calendarRootRef = useRef<FNote>();
|
const calendarRootRef = useRef<FNote>();
|
||||||
|
|
||||||
async function checkEnableWeekNotes() {
|
async function checkEnableWeekNotes() {
|
||||||
@ -45,7 +47,13 @@ export default function CalendarWidget({ launcherNote }: { launcherNote: FNote }
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!calendarRootRef.current) return;
|
if (!calendarRootRef.current) return;
|
||||||
setEnableWeekNotes(calendarRootRef.current.hasLabel("enableWeekNote"));
|
|
||||||
|
const enableWeekNotes = calendarRootRef.current.hasLabel("enableWeekNote");
|
||||||
|
setEnableWeekNotes(enableWeekNotes);
|
||||||
|
|
||||||
|
if (enableWeekNotes) {
|
||||||
|
server.get<string[]>(`attribute-values/weekNote`).then(setWeekNotes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -91,6 +99,7 @@ export default function CalendarWidget({ launcherNote }: { launcherNote: FNote }
|
|||||||
}
|
}
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
} : undefined}
|
} : undefined}
|
||||||
|
weekNotes={weekNotes}
|
||||||
{...calendarArgs}
|
{...calendarArgs}
|
||||||
/>
|
/>
|
||||||
</div>}
|
</div>}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user