obsidian#FileView TypeScript Examples
The following examples show how to use
obsidian#FileView.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: main.tsx From obsidian-annotator with GNU Affero General Public License v3.0 | 6 votes |
getDropExtension() {
return EditorState.transactionFilter.of(transaction => {
if (transaction.isUserEvent('input.drop')) {
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const droppedText = (transaction.changes as any).inserted.map(x => x.text.join('')).join('');
if (this.dragData !== null && droppedText == 'drag-event::hypothesis-highlight') {
const startPos = transaction.selection.ranges[0].from;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const leaf: FileView = Object.keys((transaction.state as any).config.address)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.map(x => (transaction.state as any).field({ id: x }))
.filter(x => x?.file)[0];
const targetFile = leaf.file;
const annotationFile = this.app.vault.getAbstractFileByPath(this.dragData.annotationFilePath);
if (annotationFile instanceof TFile && targetFile instanceof TFile) {
const linkString = this.app.fileManager.generateMarkdownLink(
annotationFile,
targetFile.path,
`#^${this.dragData.annotationId}`,
this.dragData.annotationText
);
this.dragData = null;
return { changes: { from: startPos, insert: linkString }, selection: { anchor: startPos } };
}
}
} catch (e) {}
}
return transaction;
});
}
Example #2
Source File: main.ts From obsidian-map-view with GNU General Public License v3.0 | 6 votes |
/**
* Get the geolocation on the current editor line
* @param editor obsidian Editor instance
* @param view obsidian FileView instance
* @private
*/
private getLocationOnEditorLine(
editor: Editor,
view: FileView
): leaflet.LatLng {
const line = editor.getLine(editor.getCursor().line);
const match = matchInlineLocation(line)[0];
let selectedLocation = null;
if (match)
selectedLocation = new leaflet.LatLng(
parseFloat(match.groups.lat),
parseFloat(match.groups.lng)
);
else {
const fmLocation = getFrontMatterLocation(view.file, this.app);
if (line.indexOf('location') > -1 && fmLocation)
selectedLocation = fmLocation;
}
if (selectedLocation) {
verifyLocation(selectedLocation);
return selectedLocation;
}
return null;
}
Example #3
Source File: view.ts From obsidian-calendar-plugin with MIT License | 6 votes |
private updateActiveFile(): void {
const { view } = this.app.workspace.activeLeaf;
let file = null;
if (view instanceof FileView) {
file = view.file;
}
activeFile.setFile(file);
if (this.calendar) {
this.calendar.tick();
}
}
Example #4
Source File: view.ts From obsidian-calendar-plugin with MIT License | 6 votes |
public revealActiveNote(): void {
const { moment } = window;
const { activeLeaf } = this.app.workspace;
if (activeLeaf.view instanceof FileView) {
// Check to see if the active note is a daily-note
let date = getDateFromFile(activeLeaf.view.file, "day");
if (date) {
this.calendar.$set({ displayedMonth: date });
return;
}
// Check to see if the active note is a weekly-note
const { format } = getWeeklyNoteSettings();
date = moment(activeLeaf.view.file.basename, format, true);
if (date.isValid()) {
this.calendar.$set({ displayedMonth: date });
return;
}
}
}
Example #5
Source File: main.ts From obsidian-jupyter with MIT License | 5 votes |
class JupterPreview extends FileView {
interpreter: string;
constructor(leaf: WorkspaceLeaf, interpreter: string) {
super(leaf);
// Show a placeholder before we've converted the notebook.
this.contentEl.innerHTML = 'Converting notebook...';
this.interpreter = interpreter;
}
onLoadFile(file: TFile): Promise<void> {
// Get the base path of the vault.
let adapter = file.vault.adapter;
if (!(adapter instanceof FileSystemAdapter)) {
this.contentEl.innerHTML = 'Could not determine notebook path.';
return null;
}
// Convert the file by writing it to a temporary location. Piping unfortunately leads to
// problems for long lines due to buffer overflows.
let basePath = adapter.getBasePath();
let filename = `${basePath}/${file.path}`;
let htmlPath = `${tmpdir()}/${uuid()}.html`;
let args = ['-m', 'nbconvert', `--output=${htmlPath}`, '--to=html', filename];
let child = spawn(this.interpreter, args);
// Process the output and delete the temporary file.
child.on('close', (code: number) => {
if (code) {
this.contentEl.innerHTML = 'Failed to convert notebook to HTML.';
} else {
// Create the frame for rendering.
let frame = document.createElement('iframe');
frame.addClass('notebookPreview')
const html = readFileSync(htmlPath).toString();
const blob = new Blob([html], {type: 'text/html'});
frame.src = window.URL.createObjectURL(blob);
// Insert the frame and hook up to resize events.
this.contentEl.innerHTML = '';
this.contentEl.addClass('notebookPreview');
this.contentEl.appendChild(frame);
new ResizeObserver((entries) => {
for (let entry of entries) {
frame.height = `${entry.contentRect.height - 6}px`;
}
}).observe(this.contentEl);
}
rm(htmlPath, () => null);
})
return null;
}
getViewType(): string {
return 'ipynb';
}
canAcceptExtension(extension: string): boolean {
return extension === 'ipynb';
}
}
Example #6
Source File: annotatorView.tsx From obsidian-annotator with GNU Affero General Public License v3.0 | 4 votes |
export default class AnnotatorView extends FileView {
plugin: AnnotatorPlugin;
iframe: HTMLIFrameElement;
activeG: () => void;
annotationTarget?: string;
darkReaderReferences: Set<WeakRef<DarkReaderType>>;
useDarkMode: boolean;
getViewType(): string {
return VIEW_TYPE_PDF_ANNOTATOR;
}
constructor(leaf: WorkspaceLeaf, plugin: AnnotatorPlugin) {
super(leaf);
this.useDarkMode = plugin.settings.deafultDarkMode;
this.plugin = plugin;
this.plugin.views.add(this);
}
getAnnotationTarget(file: TFile): string {
const annotationTargetPropertyValue = this.plugin.getPropertyValue(ANNOTATION_TARGET_PROPERTY, file);
if (!annotationTargetPropertyValue) {
this.plugin.log('Invalid annotation target!');
return '';
}
for (let target of [
annotationTargetPropertyValue,
`${this.plugin.settings.customDefaultPath}${annotationTargetPropertyValue}`
]) {
//unpack target if it is is an array (For Metaedit compatability)
if (Array.isArray(target)) {
target = target[0];
}
if (isUrl(target)) {
return target;
}
let destFile: TFile;
try {
destFile = this.app.metadataCache.getFirstLinkpathDest(target, file?.path || '');
} finally {
if (destFile) {
return destFile.path;
}
}
}
//unpack target if it is is an array (For Metaedit compatability)
if (Array.isArray(annotationTargetPropertyValue)) {
return annotationTargetPropertyValue[0];
}
return annotationTargetPropertyValue;
}
async onLoadFile(file: TFile) {
// this ensures that the steps below are carried out asynchronously without being awatied.
(async () => {
// Prevent pane from loading too early.
await this.plugin.setupPromise;
await this.plugin.awaitDataViewPage(file.path);
ReactDOM.unmountComponentAtNode(this.contentEl);
this.contentEl.empty();
const annotationTarget = this.getAnnotationTarget(file);
this.contentEl.removeClass('view-content');
this.contentEl.style.height = '100%';
this.annotationTarget = annotationTarget;
if (annotationTarget) {
const annotationTargetType =
this.plugin.getPropertyValue(ANNOTATION_TARGET_TYPE_PROPERTY, file) ||
get_url_extension(annotationTarget);
let component;
switch (annotationTargetType) {
case 'pdf':
component = (
<this.plugin.PdfAnnotation
pdf={annotationTarget}
containerEl={this.contentEl}
annotationFile={file.path}
onload={async iframe => {
this.iframe = iframe;
}}
onDarkReadersUpdated={this.onDarkReadersUpdated.bind(this)}
/>
);
break;
case 'epub':
component = (
<this.plugin.EpubAnnotation
epub={annotationTarget}
containerEl={this.contentEl}
annotationFile={file.path}
onload={async iframe => {
this.iframe = iframe;
}}
onDarkReadersUpdated={this.onDarkReadersUpdated.bind(this)}
/>
);
break;
case 'video':
component = (
<this.plugin.VideoAnnotation
video={annotationTarget}
containerEl={this.contentEl}
annotationFile={file.path}
onload={async iframe => {
this.iframe = iframe;
}}
onDarkReadersUpdated={this.onDarkReadersUpdated.bind(this)}
/>
);
break;
case 'web':
component = (
<this.plugin.WebAnnotation
url={annotationTarget}
containerEl={this.contentEl}
annotationFile={file.path}
onload={async iframe => {
this.iframe = iframe;
}}
onDarkReadersUpdated={this.onDarkReadersUpdated.bind(this)}
/>
);
break;
}
ReactDOM.render(component, this.contentEl);
} else {
ReactDOM.render(
<div>
No <pre>annotation-target</pre> property present in frontmatter.
</div>,
this.contentEl
);
}
})();
}
async onDarkReadersUpdated(darkReaderReferences?: Set<WeakRef<DarkReaderType>>): Promise<void> {
if (darkReaderReferences) {
this.darkReaderReferences = darkReaderReferences;
}
this.darkReaderReferences.forEach(r => {
const darkReader = r.deref();
if (!darkReader) return;
const darkReaderSettings = this.plugin.settings.darkReaderSettings;
const f = () => {
try {
if (this.useDarkMode) {
darkReader.enable(darkReaderSettings, { invert: ['.canvasWrapper'] });
} else {
darkReader.disable();
}
} catch (e) {
console.warn('DarkReader', { r }, 'failed with error', { e });
}
};
f();
setTimeout(f, 1000);
});
}
onunload() {
try {
ReactDOM.unmountComponentAtNode(this.contentEl);
} catch (e) {}
this.plugin.views.delete(this);
this.contentEl.empty();
}
async onUnloadFile(file: TFile) {
try {
ReactDOM.unmountComponentAtNode(this.contentEl);
} catch (e) {}
await super.onUnloadFile(file);
}
onMoreOptionsMenu(menu: Menu) {
menu.addItem(item => {
item.setTitle('Open as MD')
.setIcon('document')
.onClick(async () => {
this.plugin.pdfAnnotatorFileModes[(this.leaf as any).id || this.file.path] = 'markdown'; // eslint-disable-line
this.plugin.setMarkdownView(this.leaf);
});
});
menu.addItem(item => {
item.setTitle('Toggle Dark Mode')
.setIcon('switch')
.onClick(async () => {
this.useDarkMode = !this.useDarkMode;
await this.onDarkReadersUpdated();
});
});
super.onMoreOptionsMenu(menu);
}
async scrollToAnnotation(annotationId) {
const annotation = await getAnnotation(annotationId, this.file, this.app.vault);
if (!annotation) return;
let yoffset = -10000;
let newYOffset;
const isPageNote = !annotation.target?.length;
const selectors = new Set(isPageNote ? [] : annotation.target[0].selector.map(x => JSON.stringify(x)));
const annotationTargetType =
this.plugin.getPropertyValue(ANNOTATION_TARGET_TYPE_PROPERTY, this.file) ||
get_url_extension(this.annotationTarget);
const g = () => {
try {
if (this.activeG != g) return;
const document = this.iframe.contentDocument.getElementsByTagName('iframe')[0].contentDocument;
const sidebarIframe: HTMLIFrameElement =
this.iframe?.contentDocument
?.querySelector('iframe')
?.contentDocument?.querySelector('body > hypothesis-sidebar')
?.shadowRoot?.querySelector('div > iframe') ||
this.iframe?.contentDocument
?.querySelector('body > hypothesis-sidebar')
?.shadowRoot?.querySelector('div > iframe');
const guests: any[] = // eslint-disable-line
(this.iframe.contentWindow as any).guests || // eslint-disable-line
(this.iframe.contentDocument.getElementsByTagName('iframe')[0].contentWindow as any).guests; // eslint-disable-line
if (isPageNote) {
//Open Page Notes
const showAllButton: HTMLElement = sidebarIframe.contentDocument.querySelector(
'body > hypothesis-app > div > div.HypothesisApp__content > main > div > div.FilterStatus > div > div:nth-child(2) > button'
);
showAllButton?.click?.();
const pageNotesButton: HTMLElement = sidebarIframe.contentDocument.querySelector(
'body > hypothesis-app > div > div.HypothesisApp__content > main > div > div.SelectionTabs-container > div > div:nth-child(2) > button'
);
pageNotesButton?.click?.();
guests[0]._sidebarRPC.channelListeners.openSidebar();
return;
}
switch (annotationTargetType) {
case 'pdf':
break;
case 'epub':
const loc = new URL(annotation.uri).searchParams.get('loc');
(this.iframe.contentWindow as any).rendition.display(loc); // eslint-disable-line
break;
}
for (const guest of guests) {
if (!guest) continue;
const matchingAnchors = guest.anchors.filter(x =>
x?.annotation?.target?.[0]?.selector
?.map(x => selectors.has(JSON.stringify(x)))
.reduce((a, b) => a || b)
);
guest._sidebarRPC.call(
'showAnnotations',
matchingAnchors.map(x => x.annotation.$tag)
);
let done = false;
switch (annotationTargetType) {
case 'pdf':
for (const anchor of matchingAnchors) {
if (done) break;
for (const highlight of anchor.highlights) {
if (done) break;
if (highlight.scrollIntoViewIfNeeded) {
highlight.scrollIntoViewIfNeeded();
done = true;
} else if (highlight.scrollIntoView) {
highlight.scrollIntoView();
done = true;
}
}
}
break;
case 'epub':
// Use the "real" hypothes.is code.
(
sidebarIframe.contentDocument.getElementById(annotationId).firstChild as HTMLElement
).click();
break;
}
guest._sidebarRPC.channelListeners.focusAnnotations(matchingAnchors.map(x => x.annotation.$tag));
(
sidebarIframe.contentDocument.getElementById(annotationId).firstChild as HTMLElement
).dispatchEvent(new Event('mouseenter'));
}
newYOffset = document.getElementsByTagName('hypothesis-highlight')[0].getBoundingClientRect().y;
if (newYOffset != yoffset && annotationTargetType == 'pdf') {
yoffset = newYOffset;
setTimeout(g, 100);
}
} catch (e) {
if (annotationTargetType == 'pdf') {
setTimeout(g, 100);
} else if (this.plugin.settings.debugLogging) {
console.error(e);
}
}
};
this.activeG = g;
try {
setTimeout(function () {
g();
}, 1000);
} catch (e) {}
try {
g();
} catch (e) {}
}
}
Example #7
Source File: main.ts From obsidian-map-view with GNU General Public License v3.0 | 4 votes |
async onload() {
addIcon('globe', consts.RIBBON_ICON);
await this.loadSettings();
// Add a new ribbon entry to the left bar
this.addRibbonIcon('globe', 'Open map view', () => {
// When clicked change the active view to the map
this.app.workspace
.getLeaf()
.setViewState({ type: consts.MAP_VIEW_NAME });
});
this.registerView(consts.MAP_VIEW_NAME, (leaf: WorkspaceLeaf) => {
return new MapView(leaf, this.settings, this);
});
this.registerObsidianProtocolHandler(
'mapview',
(params: ObsidianProtocolData) => {
if (params.action == 'mapview') {
const state = stateFromParsedUrl(params);
// If a saved URL is opened in another device on which there aren't the same sources, use
// the default source instead
if (
state.chosenMapSource >= this.settings.mapSources.length
)
state.chosenMapSource =
DEFAULT_SETTINGS.defaultState.chosenMapSource;
this.openMapWithState(state, false, false);
}
}
);
this.suggestor = new LocationSuggest(this.app, this.settings);
this.tagSuggestor = new TagSuggest(this.app, this.settings);
this.urlConvertor = new UrlConvertor(this.app, this.settings);
this.registerEditorSuggest(this.suggestor);
this.registerEditorSuggest(this.tagSuggestor);
// Convert old settings formats that are no longer supported
if (convertLegacyMarkerIcons(this.settings)) {
await this.saveSettings();
new Notice(
'Map View: legacy marker icons were converted to the new format'
);
}
if (convertLegacyTilesUrl(this.settings)) {
await this.saveSettings();
new Notice(
'Map View: legacy tiles URL was converted to the new format'
);
}
if (convertLegacyDefaultState(this.settings)) {
await this.saveSettings();
new Notice(
'Map View: legacy default state was converted to the new format'
);
}
if (removeLegacyPresets1(this.settings)) {
await this.saveSettings();
new Notice(
'Map View: legacy URL parsing rules and/or map sources were converted. See the release notes'
);
}
if (convertTagsToQueries(this.settings)) {
await this.saveSettings();
new Notice(
'Map View: legacy tag queries were converted to the new query format'
);
}
if (convertUrlParsingRules1(this.settings)) {
await this.saveSettings();
new Notice(
'Map View: URL parsing rules were converted to the new format'
);
}
// Register commands to the command palette
// Command that opens the map view (same as clicking the map icon)
this.addCommand({
id: 'open-map-view',
name: 'Open Map View',
callback: () => {
this.app.workspace
.getLeaf()
.setViewState({ type: consts.MAP_VIEW_NAME });
},
});
// Command that looks up the selected text to find the location
this.addCommand({
id: 'convert-selection-to-location',
name: 'Convert Selection to Geolocation',
editorCheckCallback: (checking, editor, view) => {
if (checking) return editor.getSelection().length > 0;
this.suggestor.selectionToLink(editor);
},
});
// Command that adds a blank inline location at the cursor location
this.addCommand({
id: 'insert-geolink',
name: 'Add inline geolocation link',
editorCallback: (editor, view) => {
const positionBeforeInsert = editor.getCursor();
editor.replaceSelection('[](geo:)');
editor.setCursor({
line: positionBeforeInsert.line,
ch: positionBeforeInsert.ch + 1,
});
},
});
// Command that opens the location search dialog and creates a new note from this location
this.addCommand({
id: 'new-geolocation-note',
name: 'New geolocation note',
callback: () => {
const dialog = new LocationSearchDialog(
this.app,
this.settings,
'newNote',
'New geolocation note'
);
dialog.open();
},
});
// Command that opens the location search dialog and adds the location to the current note
this.addCommand({
id: 'add-frontmatter-geolocation',
name: 'Add geolocation (front matter) to current note',
editorCallback: (editor, view) => {
const dialog = new LocationSearchDialog(
this.app,
this.settings,
'addToNote',
'Add geolocation to note',
editor
);
dialog.open();
},
});
this.addCommand({
id: 'open-map-search',
name: 'Search active map view',
checkCallback: (checking) => {
const currentView = this.app.workspace.activeLeaf.view;
if (
currentView &&
currentView.getViewType() == consts.MAP_VIEW_NAME
) {
if (!checking) (currentView as MapView).openSearch();
return true;
} else return false;
},
});
this.addSettingTab(new SettingsTab(this.app, this));
// Add items to the file context menu (run when the context menu is built)
// This is the context menu in the File Explorer and clicking "More options" (three dots) from within a file.
this.app.workspace.on(
'file-menu',
async (
menu: Menu,
file: TAbstractFile,
_source: string,
leaf?: WorkspaceLeaf
) => {
if (file instanceof TFile) {
let hasAnyLocation = false;
const location = getFrontMatterLocation(file, this.app);
if (location) {
// If there is a geolocation in the front matter of the file
// Add an option to open it in the map
menu.addItem((item: MenuItem) => {
item.setTitle('Show on map');
item.setIcon('globe');
item.onClick(
async (evt: MouseEvent) =>
await this.openMapWithLocation(
location,
evt.ctrlKey
)
);
});
// Add an option to open it in the default app
menu.addItem((item: MenuItem) => {
item.setTitle('Open with default app');
item.onClick((_ev) => {
open(`geo:${location.lat},${location.lng}`);
});
});
// Populate menu items from user defined "Open In" strings
utils.populateOpenInItems(
menu,
location,
this.settings
);
hasAnyLocation = true;
} else {
if (leaf && leaf.view instanceof MarkdownView) {
// If there is no valid geolocation in the front matter, add a menu item to populate it.
const editor = leaf.view.editor;
menu.addItem((item: MenuItem) => {
item.setTitle('Add geolocation (front matter)');
item.setIcon('globe');
item.onClick(async (evt: MouseEvent) => {
const dialog = new LocationSearchDialog(
this.app,
this.settings,
'addToNote',
'Add geolocation to note',
editor
);
dialog.open();
});
});
}
}
const contentMarkers = await getMarkersFromFileContent(
file,
this.settings,
this.app
);
if (contentMarkers.length > 0) {
hasAnyLocation = true;
}
if (hasAnyLocation) {
menu.addItem((item: MenuItem) => {
item.setTitle('Focus note in Map View');
item.setIcon('globe');
item.onClick(
async (evt: MouseEvent) =>
await this.openMapWithState(
{
query: `path:"${file.path}"`,
} as MapState,
evt.ctrlKey,
true
)
);
});
}
}
}
);
// Add items to the editor context menu (run when the context menu is built)
// This is the context menu when right clicking within an editor view.
this.app.workspace.on(
'editor-menu',
async (menu: Menu, editor: Editor, view: MarkdownView) => {
if (view instanceof FileView) {
const location = this.getLocationOnEditorLine(editor, view);
if (location) {
// If there is a geolocation on the line
// Add an option to open it in the map
menu.addItem((item: MenuItem) => {
item.setTitle('Show on map');
item.setIcon('globe');
item.onClick(
async (evt: MouseEvent) =>
await this.openMapWithLocation(
location,
evt.ctrlKey
)
);
});
// Add an option to open it in the default app
menu.addItem((item: MenuItem) => {
item.setTitle('Open with default app');
item.onClick((_ev) => {
open(`geo:${location.lat},${location.lng}`);
});
});
// Populate menu items from user defined "Open In" strings
utils.populateOpenInItems(
menu,
location,
this.settings
);
}
if (editor.getSelection()) {
// If there is text selected, add a menu item to convert it to coordinates using geosearch
menu.addItem((item: MenuItem) => {
item.setTitle('Convert to geolocation (geosearch)');
item.onClick(
async () =>
await this.suggestor.selectionToLink(editor)
);
});
}
if (this.urlConvertor.hasMatchInLine(editor))
// If the line contains a recognized geolocation that can be converted from a URL parsing rule
menu.addItem(async (item: MenuItem) => {
item.setTitle('Convert to geolocation');
item.onClick(async () => {
this.urlConvertor.convertUrlAtCursorToGeolocation(
editor
);
});
});
const clipboard = await navigator.clipboard.readText();
let clipboardLocation =
this.urlConvertor.parseLocationFromUrl(clipboard);
if (clipboardLocation) {
// If the clipboard contains a recognized geolocation that can be converted from a URL parsing rule
menu.addItem((item: MenuItem) => {
item.setTitle('Paste as geolocation');
item.onClick(async () => {
if (clipboardLocation instanceof Promise)
clipboardLocation = await clipboardLocation;
if (clipboardLocation)
this.urlConvertor.insertLocationToEditor(
clipboardLocation.location,
editor
);
});
});
}
}
}
);
}
Example #8
Source File: view.ts From obsidian-fantasy-calendar with MIT License | 4 votes |
build() {
this.contentEl.empty();
this._app = new CalendarUI({
target: this.contentEl,
props: {
calendar: this.helper,
fullView: this.full,
yearView: this.yearView,
moons: this.moons,
displayWeeks: this.helper.displayWeeks,
displayDayNumber: this.dayNumber
}
});
this._app.$on("day-click", (event: CustomEvent<DayHelper>) => {
const day = event.detail;
if (day.events.length) return;
this.createEventForDay(day.date);
});
this._app.$on("day-doubleclick", (event: CustomEvent<DayHelper>) => {
const day = event.detail;
if (!day.events.length) return;
this.helper.viewing.day = day.number;
this.helper.viewing.month = day.month.number;
this.helper.viewing.year = day.month.year;
this.yearView = false;
this._app.$set({ yearView: false });
this._app.$set({ dayView: true });
this.triggerHelperEvent("day-update", false);
});
this._app.$on(
"day-context-menu",
(event: CustomEvent<{ day: DayHelper; evt: MouseEvent }>) => {
const { day, evt } = event.detail;
const menu = new Menu(this.app);
menu.setNoIcon();
if (!this.full) {
menu.addItem((item) => {
item.setTitle("Open Day").onClick(() => {
this.openDay({
day: day.number,
month: this.helper.displayed.month,
year: this.helper.displayed.year
});
});
});
}
menu.addItem((item) => {
item.setTitle("Set as Today").onClick(() => {
this.calendar.current = day.date;
this.helper.current.day = day.number;
this.triggerHelperEvent("day-update");
this.saveCalendars();
});
});
menu.addItem((item) =>
item.setTitle("New Event").onClick(() => {
this.createEventForDay(day.date);
})
);
menu.showAtMouseEvent(evt);
}
);
this._app.$on("settings", (event: CustomEvent<MouseEvent>) => {
const evt = event.detail;
const menu = new Menu(this.app);
menu.setNoIcon();
menu.addItem((item) => {
item.setTitle(
`${this.calendar.displayWeeks ? "Hide" : "Show"} Weeks`
).onClick(() => {
this.calendar.displayWeeks = !this.calendar.displayWeeks;
this.helper.update(this.calendar);
this._app.$set({
displayWeeks: this.calendar.displayWeeks
});
this.saveCalendars();
});
});
menu.addItem((item) => {
item.setTitle(
`Open ${this.yearView ? "Month" : "Year"}`
).onClick(() => {
this.yearView = !this.yearView;
this._app.$set({ yearView: this.yearView });
});
});
menu.addItem((item) => {
item.setTitle(
this.moons ? "Hide Moons" : "Display Moons"
).onClick(() => {
this.toggleMoons();
});
});
menu.addItem((item) => {
item.setTitle(
this.dayNumber ? "Hide Day Number" : "Display Day Number"
).onClick(() => {
this.dayNumber = !this.dayNumber;
this.calendar.static.displayDayNumber = this.dayNumber;
this._app.$set({ displayDayNumber: this.dayNumber });
this.saveCalendars();
});
});
menu.addItem((item) => {
item.setTitle("View Day");
item.onClick(() => {
this.openDate();
});
});
menu.addItem((item) => {
item.setTitle("Switch Calendars");
item.setDisabled(this.plugin.data.calendars.length <= 1);
item.onClick(() => {
const modal = new SwitchModal(this.plugin, this.calendar);
modal.onClose = () => {
if (!modal.confirmed) return;
this.setCurrentCalendar(modal.calendar);
};
modal.open();
});
});
menu.showAtMouseEvent(evt);
});
this._app.$on(
"event-click",
(evt: CustomEvent<{ event: Event; modifier: boolean }>) => {
const { event, modifier } = evt.detail;
if (event.note) {
let leaves: WorkspaceLeaf[] = [];
this.app.workspace.iterateAllLeaves((leaf) => {
if (!(leaf.view instanceof MarkdownView)) return;
if (leaf.view.file.basename === event.note) {
leaves.push(leaf);
}
});
if (leaves.length) {
this.app.workspace.setActiveLeaf(leaves[0]);
} else {
this.app.workspace.openLinkText(
event.note,
"",
this.full || modifier
);
}
} else {
const modal = new ViewEventModal(event, this.plugin);
modal.open();
}
}
);
this._app.$on(
"event-mouseover",
(evt: CustomEvent<{ target: HTMLElement; event: Event }>) => {
if (!this.plugin.data.eventPreview) return;
const { target, event } = evt.detail;
if (event.note) {
this.app.workspace.trigger(
"link-hover",
this, //hover popover, but don't need
target, //targetEl
event.note, //linkText
"" //source
);
}
}
);
this._app.$on(
"event-context",
(custom: CustomEvent<{ evt: MouseEvent; event: Event }>) => {
const { evt, event } = custom.detail;
const menu = new Menu(this.app);
menu.setNoIcon();
if (!event.note) {
menu.addItem((item) => {
item.setTitle("Create Note").onClick(async () => {
const path =
this.app.workspace.getActiveFile()?.path;
const newFilePath = path
? this.app.fileManager.getNewFileParent(path)
?.parent ?? "/"
: "/";
const date = `${event.date.year}-${
event.date.month + 1
}-${event.date.day}`;
let end: string;
if (event.end) {
end = `${event.end.year}-${
event.end.month + 1
}-${event.end.day}`;
}
const content = {
"fc-calendar": this.calendar.name,
"fc-date": date,
...(event.end ? { "fc-end": end } : {}),
...(event.category
? {
"fc-category":
this.calendar.categories.find(
(cat) =>
cat.id == event.category
)?.name
}
: {}),
"fc-display-name": event.name
};
event.note = normalizePath(
`${newFilePath}/${event.name}.md`
);
let file = this.app.vault.getAbstractFileByPath(
event.note
);
if (!file) {
file = await this.app.vault.create(
event.note,
`---\n${stringifyYaml(content)}\n---`
);
}
this.saveCalendars();
if (file instanceof TFile) {
const fileViews =
this.app.workspace.getLeavesOfType(
"markdown"
);
const existing = fileViews.find((l) => {
l.view instanceof FileView &&
l.view.file.path == event.note;
});
if (existing) {
this.app.workspace.setActiveLeaf(existing);
} else {
await this.app.workspace
.getUnpinnedLeaf()
.openFile(file, {
active: true
});
}
}
});
});
}
menu.addItem((item) => {
item.setTitle("Edit Event").onClick(() => {
const modal = new CreateEventModal(
this.plugin,
this.calendar,
event
);
modal.onClose = () => {
if (!modal.saved) return;
const existing = this.calendar.events.find(
(e) => e.id == event.id
);
this.calendar.events.splice(
this.calendar.events.indexOf(existing),
1,
modal.event
);
this.helper.refreshMonth(
modal.event.date.month,
modal.event.date.year
);
if (
modal.event.date.month != existing.date.month ||
modal.event.date.year != existing.date.year
) {
this.helper.refreshMonth(
existing.date.month,
existing.date.year
);
}
this.saveCalendars();
this._app.$set({
calendar: this.helper
});
this.triggerHelperEvent("day-update");
};
modal.open();
});
});
menu.addItem((item) => {
item.setTitle("Delete Event").onClick(async () => {
if (
!this.plugin.data.exit.event &&
!(await confirmEventDeletion(this.plugin))
)
return;
const existing = this.calendar.events.find(
(e) => e.id == event.id
);
this.calendar.events.splice(
this.calendar.events.indexOf(existing),
1
);
this.helper.refreshMonth(
existing.date.month,
existing.date.year
);
this.saveCalendars();
this._app.$set({
calendar: this.helper
});
this.triggerHelperEvent("day-update");
});
});
menu.showAtMouseEvent(evt);
}
);
this._app.$on("event", (e: CustomEvent<CurrentCalendarData>) => {
const date = e.detail;
this.createEventForDay(date);
});
this._app.$on("reset", () => {
this.helper.reset();
this.yearView = false;
this._app.$set({ yearView: false });
this._app.$set({ dayView: true });
this.triggerHelperEvent("day-update", false);
});
}