chore(react/launch_bar): bring back week highlighting

This commit is contained in:
Elian Doran 2025-12-05 09:47:13 +02:00
parent 20f44cc64f
commit 185e5691a4
No known key found for this signature in database
4 changed files with 25 additions and 203 deletions

View File

@ -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 17
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();
}
}

View File

@ -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> {}
}

View File

@ -31,6 +31,7 @@ export interface CalendarArgs {
activeDate: Dayjs | null;
onDateClicked(date: string, e: TargetedMouseEvent<HTMLAnchorElement>): void;
onWeekClicked?: (week: string, e: TargetedMouseEvent<HTMLAnchorElement>) => void;
weekNotes: string[];
}
export default function Calendar(args: CalendarArgs) {
@ -77,7 +78,7 @@ function PreviousMonthDays({ date, info: { dates, weekNumbers }, ...args }: { da
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} />)}
</>
)
@ -97,7 +98,7 @@ function CurrentMonthDays({ date, firstDayOfWeekISO, ...args }: { date: Dayjs, f
while (dateCursor.month() === currentMonth) {
const weekNumber = getWeekNumber(dateCursor, 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} />)
@ -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) {
const weekNoteId = date.local().format('YYYY-') + 'W' + String(weekNumber).padStart(2, '0');
return (
<a
className="calendar-week-number calendar-date"
onClick={(e) => onWeekClicked(weekNoteId, e)}
className={clsx("calendar-week-number", "calendar-date",
weekNotes.includes(weekString) && "calendar-date-exists")}
data-calendar-week-number={weekNumber}
onClick={(e) => onWeekClicked(weekString, e)}
>{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) {

View File

@ -13,6 +13,7 @@ import toast from "../../services/toast";
import date_notes from "../../services/date_notes";
import { Dropdown } from "bootstrap";
import search from "../../services/search";
import server from "../../services/server";
const MONTHS = [
t("calendar.january"),
@ -35,6 +36,7 @@ export default function CalendarWidget({ launcherNote }: { launcherNote: FNote }
const [ date, setDate ] = useState<Dayjs>();
const dropdownRef = useRef<Dropdown>(null);
const [ enableWeekNotes, setEnableWeekNotes ] = useState(false);
const [ weekNotes, setWeekNotes ] = useState<string[]>([]);
const calendarRootRef = useRef<FNote>();
async function checkEnableWeekNotes() {
@ -45,7 +47,13 @@ export default function CalendarWidget({ launcherNote }: { launcherNote: FNote }
}
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 (
@ -91,6 +99,7 @@ export default function CalendarWidget({ launcherNote }: { launcherNote: FNote }
}
e.stopPropagation();
} : undefined}
weekNotes={weekNotes}
{...calendarArgs}
/>
</div>}