mirror of
https://github.com/zadam/trilium.git
synced 2025-10-22 08:08:51 +02:00
feat(collections/calendar): use own UI for header
This commit is contained in:
parent
49c80f0e0b
commit
afc17f41f6
@ -293,6 +293,11 @@ button.close:hover {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-action.btn {
|
||||||
|
padding: 0 8px;
|
||||||
|
min-width: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
.ui-widget-content a:not(.ui-tabs-anchor) {
|
.ui-widget-content a:not(.ui-tabs-anchor) {
|
||||||
color: #337ab7 !important;
|
color: #337ab7 !important;
|
||||||
}
|
}
|
||||||
|
@ -587,7 +587,17 @@
|
|||||||
"september": "September",
|
"september": "September",
|
||||||
"october": "October",
|
"october": "October",
|
||||||
"november": "November",
|
"november": "November",
|
||||||
"december": "December"
|
"december": "December",
|
||||||
|
"week": "Week",
|
||||||
|
"week_previous": "Previous week",
|
||||||
|
"week_next": "Next week",
|
||||||
|
"month": "Month",
|
||||||
|
"month_previous": "Previous month",
|
||||||
|
"month_next": "Next month",
|
||||||
|
"year": "Year",
|
||||||
|
"year_previous": "Previous year",
|
||||||
|
"year_next": "Next year",
|
||||||
|
"list": "List"
|
||||||
},
|
},
|
||||||
"close_pane_button": {
|
"close_pane_button": {
|
||||||
"close_this_pane": "Close this pane"
|
"close_this_pane": "Close this pane"
|
||||||
|
@ -60,3 +60,20 @@ body.desktop:not(.zen) .calendar-container .fc-toolbar.fc-header-toolbar {
|
|||||||
opacity: 0.85;
|
opacity: 0.85;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* #region Header */
|
||||||
|
.calendar-header {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-header .btn {
|
||||||
|
min-width: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-header > .title {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
/* #endregion */
|
@ -16,18 +16,50 @@ import date_notes from "../../../services/date_notes";
|
|||||||
import appContext from "../../../components/app_context";
|
import appContext from "../../../components/app_context";
|
||||||
import { DateClickArg } from "@fullcalendar/interaction";
|
import { DateClickArg } from "@fullcalendar/interaction";
|
||||||
import FNote from "../../../entities/fnote";
|
import FNote from "../../../entities/fnote";
|
||||||
|
import Button, { ButtonGroup } from "../../react/Button";
|
||||||
|
import ActionButton from "../../react/ActionButton";
|
||||||
|
import { RefObject } from "preact";
|
||||||
|
|
||||||
interface CalendarViewData {
|
interface CalendarViewData {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface CalendarViewData {
|
||||||
|
type: string;
|
||||||
|
name: string;
|
||||||
|
previousText: string;
|
||||||
|
nextText: string;
|
||||||
|
}
|
||||||
|
|
||||||
const CALENDAR_VIEWS = [
|
const CALENDAR_VIEWS = [
|
||||||
"timeGridWeek",
|
{
|
||||||
"dayGridMonth",
|
type: "timeGridWeek",
|
||||||
"multiMonthYear",
|
name: t("calendar.week"),
|
||||||
"listMonth"
|
previousText: t("calendar.week_previous"),
|
||||||
|
nextText: t("calendar.week_next")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "dayGridMonth",
|
||||||
|
name: t("calendar.month"),
|
||||||
|
previousText: t("calendar.month_previous"),
|
||||||
|
nextText: t("calendar.month_next")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "multiMonthYear",
|
||||||
|
name: t("calendar.year"),
|
||||||
|
previousText: t("calendar.year_previous"),
|
||||||
|
nextText: t("calendar.year_next")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "listMonth",
|
||||||
|
name: t("calendar.list"),
|
||||||
|
previousText: t("calendar.month_previous"),
|
||||||
|
nextText: t("calendar.month_next")
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const SUPPORTED_CALENDAR_VIEW_TYPE = CALENDAR_VIEWS.map(v => v.type);
|
||||||
|
|
||||||
// Here we hard-code the imports in order to ensure that they are embedded by webpack without having to load all the languages.
|
// Here we hard-code the imports in order to ensure that they are embedded by webpack without having to load all the languages.
|
||||||
export const LOCALE_MAPPINGS: Record<LOCALE_IDS, (() => Promise<{ default: LocaleInput }>) | null> = {
|
export const LOCALE_MAPPINGS: Record<LOCALE_IDS, (() => Promise<{ default: LocaleInput }>) | null> = {
|
||||||
de: () => import("@fullcalendar/core/locales/de"),
|
de: () => import("@fullcalendar/core/locales/de"),
|
||||||
@ -83,20 +115,18 @@ export default function CalendarView({ note, noteIds }: ViewModeProps<CalendarVi
|
|||||||
|
|
||||||
return (plugins &&
|
return (plugins &&
|
||||||
<div className="calendar-view" ref={containerRef}>
|
<div className="calendar-view" ref={containerRef}>
|
||||||
|
<CalendarHeader calendarRef={calendarRef} />
|
||||||
<Calendar
|
<Calendar
|
||||||
events={eventBuilder}
|
events={eventBuilder}
|
||||||
calendarRef={calendarRef}
|
calendarRef={calendarRef}
|
||||||
plugins={plugins}
|
plugins={plugins}
|
||||||
tabIndex={100}
|
tabIndex={100}
|
||||||
initialView={initialView.current && CALENDAR_VIEWS.includes(initialView.current) ? initialView.current : "dayGridMonth"}
|
initialView={initialView.current && SUPPORTED_CALENDAR_VIEW_TYPE.includes(initialView.current) ? initialView.current : "dayGridMonth"}
|
||||||
headerToolbar={{
|
headerToolbar={false}
|
||||||
start: "title",
|
|
||||||
end: `${CALENDAR_VIEWS.join(",")} today prev,next`
|
|
||||||
}}
|
|
||||||
firstDay={firstDayOfWeek ?? 0}
|
firstDay={firstDayOfWeek ?? 0}
|
||||||
weekends={!hideWeekends}
|
weekends={!hideWeekends}
|
||||||
weekNumbers={weekNumbers}
|
weekNumbers={weekNumbers}
|
||||||
height="100%"
|
height="90%"
|
||||||
nowIndicator
|
nowIndicator
|
||||||
handleWindowResize={false}
|
handleWindowResize={false}
|
||||||
locale={locale}
|
locale={locale}
|
||||||
@ -113,6 +143,31 @@ export default function CalendarView({ note, noteIds }: ViewModeProps<CalendarVi
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function CalendarHeader({ calendarRef }: { calendarRef: RefObject<FullCalendar> }) {
|
||||||
|
const currentViewType = calendarRef.current?.view?.type;
|
||||||
|
const currentViewData = CALENDAR_VIEWS.find(v => calendarRef.current && v.type === currentViewType);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="calendar-header">
|
||||||
|
<span className="title">{calendarRef.current?.view.title}</span>
|
||||||
|
<ButtonGroup>
|
||||||
|
{CALENDAR_VIEWS.map(viewData => (
|
||||||
|
<Button
|
||||||
|
text={viewData.name.toLocaleLowerCase()}
|
||||||
|
className={currentViewType === viewData.type ? "active" : ""}
|
||||||
|
onClick={() => calendarRef.current?.changeView(viewData.type)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</ButtonGroup>
|
||||||
|
<Button text="today" onClick={() => calendarRef.current?.today()} />
|
||||||
|
<ButtonGroup>
|
||||||
|
<ActionButton icon="bx bx-chevron-left" text={currentViewData?.previousText ?? ""} frame onClick={() => calendarRef.current?.prev()} />
|
||||||
|
<ActionButton icon="bx bx-chevron-right" text={currentViewData?.nextText ?? ""} frame onClick={() => calendarRef.current?.next()} />
|
||||||
|
</ButtonGroup>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function usePlugins(isEditable: boolean, isCalendarRoot: boolean) {
|
function usePlugins(isEditable: boolean, isCalendarRoot: boolean) {
|
||||||
const [ plugins, setPlugins ] = useState<PluginDef[]>();
|
const [ plugins, setPlugins ] = useState<PluginDef[]>();
|
||||||
|
|
||||||
|
@ -11,9 +11,10 @@ export interface ActionButtonProps {
|
|||||||
onClick?: (e: MouseEvent) => void;
|
onClick?: (e: MouseEvent) => void;
|
||||||
triggerCommand?: CommandNames;
|
triggerCommand?: CommandNames;
|
||||||
noIconActionClass?: boolean;
|
noIconActionClass?: boolean;
|
||||||
|
frame?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ActionButton({ text, icon, className, onClick, triggerCommand, titlePosition, noIconActionClass }: ActionButtonProps) {
|
export default function ActionButton({ text, icon, className, onClick, triggerCommand, titlePosition, noIconActionClass, frame }: ActionButtonProps) {
|
||||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||||
const [ keyboardShortcut, setKeyboardShortcut ] = useState<string[]>();
|
const [ keyboardShortcut, setKeyboardShortcut ] = useState<string[]>();
|
||||||
|
|
||||||
@ -31,7 +32,7 @@ export default function ActionButton({ text, icon, className, onClick, triggerCo
|
|||||||
|
|
||||||
return <button
|
return <button
|
||||||
ref={buttonRef}
|
ref={buttonRef}
|
||||||
class={`${className ?? ""} ${!noIconActionClass ? "icon-action" : "btn"} ${icon}`}
|
class={`${className ?? ""} ${!noIconActionClass ? "icon-action" : "btn"} ${icon} ${frame ? "btn btn-primary" : ""}`}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
data-trigger-command={triggerCommand}
|
data-trigger-command={triggerCommand}
|
||||||
/>;
|
/>;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { RefObject } from "preact";
|
import type { ComponentChildren, RefObject } from "preact";
|
||||||
import type { CSSProperties } from "preact/compat";
|
import type { CSSProperties } from "preact/compat";
|
||||||
import { useMemo } from "preact/hooks";
|
import { useMemo } from "preact/hooks";
|
||||||
import { memo } from "preact/compat";
|
import { memo } from "preact/compat";
|
||||||
@ -72,4 +72,12 @@ const Button = memo(({ name, buttonRef, className, text, onClick, keyboardShortc
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export function ButtonGroup({ children }: { children: ComponentChildren }) {
|
||||||
|
return (
|
||||||
|
<div className="btn-group" role="group">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default Button;
|
export default Button;
|
Loading…
x
Reference in New Issue
Block a user