Update
This commit is contained in:
40
main.ts
40
main.ts
@@ -1,30 +1,30 @@
|
|||||||
import { MarkdownView, Platform, Plugin, WorkspaceLeaf } from "obsidian";
|
import { MarkdownView, Platform, Plugin, WorkspaceLeaf } from "obsidian";
|
||||||
import {
|
import {
|
||||||
VIEW_TYPE_PROJECTS,
|
VIEW_TYPE_PROJECT_VIEW,
|
||||||
VIEW_TYPE_OVERVIEW,
|
VIEW_TYPE_PROJECT_DETAILS_VIEW,
|
||||||
VIEW_TYPE_DETAILS,
|
VIEW_TYPE_COLLECTION_VIEW,
|
||||||
RIBBON_ICON,
|
RIBBON_ICON,
|
||||||
} from "./src/const";
|
} from "./src/const";
|
||||||
import { ProjectsView } from "./src/views/ProjectsView";
|
import { ProjectView } from "./src/views/ProjectView";
|
||||||
import { OverviewView } from "./src/views/OverviewView";
|
import { ProjectDetailsView } from "./src/views/ProjectDetailsView";
|
||||||
import { DetailsView } from "./src/views/DetailsView";
|
import { CollectionView } from "./src/views/CollectionView";
|
||||||
import { parseProjectFilePath } from "./src/fs";
|
import { parseProjectFilePath } from "./src/fs";
|
||||||
import { BreadcrumbSegment, injectMobileBreadcrumb } from "./src/ui";
|
import { BreadcrumbSegment, injectMobileBreadcrumb } from "./src/ui";
|
||||||
|
|
||||||
export default class ProjektkontextPlugin extends Plugin {
|
export default class ProjektkontextPlugin extends Plugin {
|
||||||
async onload(): Promise<void> {
|
async onload(): Promise<void> {
|
||||||
this.registerView(VIEW_TYPE_PROJECTS, (leaf) => new ProjectsView(leaf));
|
this.registerView(VIEW_TYPE_PROJECT_VIEW, (leaf) => new ProjectView(leaf));
|
||||||
this.registerView(VIEW_TYPE_OVERVIEW, (leaf) => new OverviewView(leaf));
|
this.registerView(VIEW_TYPE_PROJECT_DETAILS_VIEW, (leaf) => new ProjectDetailsView(leaf));
|
||||||
this.registerView(VIEW_TYPE_DETAILS, (leaf) => new DetailsView(leaf));
|
this.registerView(VIEW_TYPE_COLLECTION_VIEW, (leaf) => new CollectionView(leaf));
|
||||||
|
|
||||||
this.addRibbonIcon(RIBBON_ICON, "Projekte", () => {
|
this.addRibbonIcon(RIBBON_ICON, "Projekte", () => {
|
||||||
void this.activateProjectsView();
|
void this.activateProjectView();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: "open-projects",
|
id: "open-projects",
|
||||||
name: "Projekte öffnen",
|
name: "Projekte öffnen",
|
||||||
callback: () => void this.activateProjectsView(),
|
callback: () => void this.activateProjectView(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Platform.isMobile) {
|
if (Platform.isMobile) {
|
||||||
@@ -41,12 +41,12 @@ export default class ProjektkontextPlugin extends Plugin {
|
|||||||
|
|
||||||
async onunload(): Promise<void> {}
|
async onunload(): Promise<void> {}
|
||||||
|
|
||||||
async activateProjectsView(): Promise<void> {
|
async activateProjectView(): Promise<void> {
|
||||||
const { workspace } = this.app;
|
const { workspace } = this.app;
|
||||||
let leaf: WorkspaceLeaf | null = workspace.getLeavesOfType(VIEW_TYPE_PROJECTS)[0] ?? null;
|
let leaf: WorkspaceLeaf | null = workspace.getLeavesOfType(VIEW_TYPE_PROJECT_VIEW)[0] ?? null;
|
||||||
if (!leaf) {
|
if (!leaf) {
|
||||||
leaf = workspace.getLeaf(false);
|
leaf = workspace.getLeaf(false);
|
||||||
await leaf.setViewState({ type: VIEW_TYPE_PROJECTS, active: true });
|
await leaf.setViewState({ type: VIEW_TYPE_PROJECT_VIEW, active: true });
|
||||||
}
|
}
|
||||||
workspace.revealLeaf(leaf);
|
workspace.revealLeaf(leaf);
|
||||||
}
|
}
|
||||||
@@ -71,26 +71,26 @@ export default class ProjektkontextPlugin extends Plugin {
|
|||||||
const segments: BreadcrumbSegment[] = [
|
const segments: BreadcrumbSegment[] = [
|
||||||
{
|
{
|
||||||
label: "Projekte",
|
label: "Projekte",
|
||||||
onClick: () => void leaf.setViewState({ type: VIEW_TYPE_PROJECTS, active: true }),
|
onClick: () => void leaf.setViewState({ type: VIEW_TYPE_PROJECT_VIEW, active: true }),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: loc.project,
|
label: loc.project,
|
||||||
onClick: () =>
|
onClick: () =>
|
||||||
void leaf.setViewState({
|
void leaf.setViewState({
|
||||||
type: VIEW_TYPE_OVERVIEW,
|
type: VIEW_TYPE_PROJECT_DETAILS_VIEW,
|
||||||
active: true,
|
active: true,
|
||||||
state: { project: loc.project },
|
state: { project: loc.project },
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
if (loc.area) {
|
if (loc.collection) {
|
||||||
segments.push({
|
segments.push({
|
||||||
label: loc.area,
|
label: loc.collection,
|
||||||
onClick: () =>
|
onClick: () =>
|
||||||
void leaf.setViewState({
|
void leaf.setViewState({
|
||||||
type: VIEW_TYPE_DETAILS,
|
type: VIEW_TYPE_COLLECTION_VIEW,
|
||||||
active: true,
|
active: true,
|
||||||
state: { project: loc.project, area: loc.area },
|
state: { project: loc.project, collection: loc.collection, zone: loc.zone },
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
export const PROJECTS_ROOT = "projects";
|
export const PROJECTS_ROOT = "projects";
|
||||||
export const TO_UPDATE_DIR = "_to-update";
|
export const TO_UPDATE_DIR = "_to-update";
|
||||||
|
|
||||||
export const VIEW_TYPE_PROJECTS = "projektkontext-projects";
|
export const VIEW_TYPE_PROJECT_VIEW = "projektkontext-projects";
|
||||||
export const VIEW_TYPE_OVERVIEW = "projektkontext-overview";
|
export const VIEW_TYPE_PROJECT_DETAILS_VIEW = "projektkontext-overview";
|
||||||
export const VIEW_TYPE_DETAILS = "projektkontext-details";
|
export const VIEW_TYPE_COLLECTION_VIEW = "projektkontext-details";
|
||||||
|
|
||||||
export const RIBBON_ICON = "layout-grid";
|
export const RIBBON_ICON = "layout-grid";
|
||||||
|
|
||||||
|
|||||||
30
src/fs.ts
30
src/fs.ts
@@ -19,18 +19,18 @@ export function zoneRootPath(project: string, zone: Zone): string {
|
|||||||
return zone === "ready" ? projectPath(project) : toUpdatePath(project);
|
return zone === "ready" ? projectPath(project) : toUpdatePath(project);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function areaPath(project: string, area: string, zone: Zone = "ready"): string {
|
export function collectionPath(project: string, collection: string, zone: Zone = "ready"): string {
|
||||||
return normalizePath(`${zoneRootPath(project, zone)}/${area}`);
|
return normalizePath(`${zoneRootPath(project, zone)}/${collection}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function featurePath(
|
export function featurePath(
|
||||||
project: string,
|
project: string,
|
||||||
area: string,
|
collection: string,
|
||||||
feature: string,
|
feature: string,
|
||||||
zone: Zone = "ready",
|
zone: Zone = "ready",
|
||||||
): string {
|
): string {
|
||||||
const file = feature.endsWith(".md") ? feature : `${feature}.md`;
|
const file = feature.endsWith(".md") ? feature : `${feature}.md`;
|
||||||
return normalizePath(`${zoneRootPath(project, zone)}/${area}/${file}`);
|
return normalizePath(`${zoneRootPath(project, zone)}/${collection}/${file}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function ensureFolder(app: App, path: string): Promise<void> {
|
export async function ensureFolder(app: App, path: string): Promise<void> {
|
||||||
@@ -54,6 +54,7 @@ export function listFolders(app: App, path: string): TFolder[] {
|
|||||||
if (!(folder instanceof TFolder)) return [];
|
if (!(folder instanceof TFolder)) return [];
|
||||||
return folder.children
|
return folder.children
|
||||||
.filter((c): c is TFolder => c instanceof TFolder)
|
.filter((c): c is TFolder => c instanceof TFolder)
|
||||||
|
.filter((c) => !c.name.startsWith("__"))
|
||||||
.sort((a, b) => a.name.localeCompare(b.name));
|
.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,6 +65,7 @@ export function listMarkdownFiles(app: App, path: string, exclude: string[] = []
|
|||||||
return folder.children
|
return folder.children
|
||||||
.filter((c): c is TFile => c instanceof TFile && c.extension === "md")
|
.filter((c): c is TFile => c instanceof TFile && c.extension === "md")
|
||||||
.filter((f) => !exclude.includes(f.name))
|
.filter((f) => !exclude.includes(f.name))
|
||||||
|
.filter((f) => !f.basename.startsWith("__"))
|
||||||
.sort((a, b) => a.basename.localeCompare(b.basename));
|
.sort((a, b) => a.basename.localeCompare(b.basename));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,27 +96,27 @@ export async function createProject(app: App, name: string): Promise<void> {
|
|||||||
for (const file of PROJECT_FILES) {
|
for (const file of PROJECT_FILES) {
|
||||||
await ensureFile(app, normalizePath(`${root}/${file}`), "");
|
await ensureFile(app, normalizePath(`${root}/${file}`), "");
|
||||||
}
|
}
|
||||||
await createArea(app, name, "story");
|
await createCollection(app, name, "story");
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createArea(
|
export async function createCollection(
|
||||||
app: App,
|
app: App,
|
||||||
project: string,
|
project: string,
|
||||||
area: string,
|
collection: string,
|
||||||
zone: Zone = "to-update",
|
zone: Zone = "to-update",
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (zone === "to-update") await ensureFolder(app, toUpdatePath(project));
|
if (zone === "to-update") await ensureFolder(app, toUpdatePath(project));
|
||||||
await ensureFolder(app, areaPath(project, area, zone));
|
await ensureFolder(app, collectionPath(project, collection, zone));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createFeature(
|
export async function createFeature(
|
||||||
app: App,
|
app: App,
|
||||||
project: string,
|
project: string,
|
||||||
area: string,
|
collection: string,
|
||||||
feature: string,
|
feature: string,
|
||||||
zone: Zone = "ready",
|
zone: Zone = "ready",
|
||||||
): Promise<TFile> {
|
): Promise<TFile> {
|
||||||
return await ensureFile(app, featurePath(project, area, feature, zone), "");
|
return await ensureFile(app, featurePath(project, collection, feature, zone), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function projectFeaturePath(
|
export function projectFeaturePath(
|
||||||
@@ -136,13 +138,9 @@ export async function createProjectFeature(
|
|||||||
return await ensureFile(app, projectFeaturePath(project, feature, zone), "");
|
return await ensureFile(app, projectFeaturePath(project, feature, zone), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isProjectRootFile(name: string): boolean {
|
|
||||||
return (PROJECT_FILES as readonly string[]).includes(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ProjectFileLocation {
|
export interface ProjectFileLocation {
|
||||||
project: string;
|
project: string;
|
||||||
area?: string;
|
collection?: string;
|
||||||
zone: Zone;
|
zone: Zone;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,6 +157,6 @@ export function parseProjectFilePath(path: string): ProjectFileLocation | null {
|
|||||||
rest.shift();
|
rest.shift();
|
||||||
}
|
}
|
||||||
if (rest.length === 1) return { project, zone };
|
if (rest.length === 1) return { project, zone };
|
||||||
if (rest.length === 2) return { project, area: rest[0], zone };
|
if (rest.length === 2) return { project, collection: rest[0], zone };
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { ItemView, MarkdownRenderer, WorkspaceLeaf, ViewStateResult } from "obsidian";
|
import { ItemView, MarkdownRenderer, WorkspaceLeaf, ViewStateResult } from "obsidian";
|
||||||
import {
|
import {
|
||||||
VIEW_TYPE_DETAILS,
|
VIEW_TYPE_COLLECTION_VIEW,
|
||||||
VIEW_TYPE_OVERVIEW,
|
VIEW_TYPE_PROJECT_DETAILS_VIEW,
|
||||||
VIEW_TYPE_PROJECTS,
|
VIEW_TYPE_PROJECT_VIEW,
|
||||||
RIBBON_ICON,
|
RIBBON_ICON,
|
||||||
} from "../const";
|
} from "../const";
|
||||||
import {
|
import {
|
||||||
Zone,
|
Zone,
|
||||||
areaPath,
|
collectionPath,
|
||||||
featurePath,
|
featurePath,
|
||||||
listMarkdownFiles,
|
listMarkdownFiles,
|
||||||
readFile,
|
readFile,
|
||||||
@@ -17,9 +17,9 @@ import {
|
|||||||
import { menu, breadcrumb, emptyState, openMarkdown } from "../ui";
|
import { menu, breadcrumb, emptyState, openMarkdown } from "../ui";
|
||||||
import { NameModal } from "../modals/NameModal";
|
import { NameModal } from "../modals/NameModal";
|
||||||
|
|
||||||
export interface DetailsState extends Record<string, unknown> {
|
export interface CollectionViewState extends Record<string, unknown> {
|
||||||
project: string;
|
project: string;
|
||||||
area: string;
|
collection: string;
|
||||||
zone?: Zone;
|
zone?: Zone;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,9 +32,9 @@ function validateName(name: string, taken: string[]): string | null {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DetailsView extends ItemView {
|
export class CollectionView extends ItemView {
|
||||||
project = "";
|
project = "";
|
||||||
area = "";
|
collection = "";
|
||||||
zone: Zone = "ready";
|
zone: Zone = "ready";
|
||||||
private renderToken = 0;
|
private renderToken = 0;
|
||||||
|
|
||||||
@@ -44,27 +44,27 @@ export class DetailsView extends ItemView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getViewType(): string {
|
getViewType(): string {
|
||||||
return VIEW_TYPE_DETAILS;
|
return VIEW_TYPE_COLLECTION_VIEW;
|
||||||
}
|
}
|
||||||
|
|
||||||
getDisplayText(): string {
|
getDisplayText(): string {
|
||||||
return this.area ? `Area: ${this.area}` : "Area";
|
return this.collection ? `Collection: ${this.collection}` : "Collection";
|
||||||
}
|
}
|
||||||
|
|
||||||
getIcon(): string {
|
getIcon(): string {
|
||||||
return RIBBON_ICON;
|
return RIBBON_ICON;
|
||||||
}
|
}
|
||||||
|
|
||||||
async setState(state: DetailsState, result: ViewStateResult): Promise<void> {
|
async setState(state: CollectionViewState, result: ViewStateResult): Promise<void> {
|
||||||
this.project = state?.project ?? "";
|
this.project = state?.project ?? "";
|
||||||
this.area = state?.area ?? "";
|
this.collection = state?.collection ?? (state as { area?: string })?.area ?? "";
|
||||||
this.zone = state?.zone ?? "ready";
|
this.zone = state?.zone ?? "ready";
|
||||||
await super.setState(state, result);
|
await super.setState(state, result);
|
||||||
await this.render();
|
await this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
getState(): DetailsState {
|
getState(): CollectionViewState {
|
||||||
return { project: this.project, area: this.area, zone: this.zone };
|
return { project: this.project, collection: this.collection, zone: this.zone };
|
||||||
}
|
}
|
||||||
|
|
||||||
async onOpen(): Promise<void> {
|
async onOpen(): Promise<void> {
|
||||||
@@ -87,14 +87,18 @@ export class DetailsView extends ItemView {
|
|||||||
const token = ++this.renderToken;
|
const token = ++this.renderToken;
|
||||||
const root = this.containerEl.children[1] as HTMLElement;
|
const root = this.containerEl.children[1] as HTMLElement;
|
||||||
|
|
||||||
if (!this.project || !this.area) {
|
if (!this.project || !this.collection) {
|
||||||
root.empty();
|
root.empty();
|
||||||
root.addClass("pk-root");
|
root.addClass("pk-root");
|
||||||
emptyState(root, "Keine Area ausgewählt.");
|
emptyState(root, "Keine Collection ausgewählt.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const features = listMarkdownFiles(this.app, areaPath(this.project, this.area, this.zone), []);
|
const features = listMarkdownFiles(
|
||||||
|
this.app,
|
||||||
|
collectionPath(this.project, this.collection, this.zone),
|
||||||
|
[],
|
||||||
|
);
|
||||||
const contents = await Promise.all(features.map((f) => readFile(this.app, f.path)));
|
const contents = await Promise.all(features.map((f) => readFile(this.app, f.path)));
|
||||||
if (token !== this.renderToken) return;
|
if (token !== this.renderToken) return;
|
||||||
|
|
||||||
@@ -102,13 +106,13 @@ export class DetailsView extends ItemView {
|
|||||||
root.addClass("pk-root");
|
root.addClass("pk-root");
|
||||||
|
|
||||||
breadcrumb(root, [
|
breadcrumb(root, [
|
||||||
{ label: "Projekte", onClick: () => this.openProjectsView() },
|
{ label: "Projekte", onClick: () => this.openProjectView() },
|
||||||
{ label: this.project, onClick: () => this.openOverview() },
|
{ label: this.project, onClick: () => this.openProjectDetails() },
|
||||||
{ label: this.area },
|
{ label: this.collection },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (features.length === 0) {
|
if (features.length === 0) {
|
||||||
emptyState(root, "Noch keine Features. Rechtsklick → Neues Feature.");
|
emptyState(root, "Keine Features");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,6 +134,7 @@ export class DetailsView extends ItemView {
|
|||||||
btn.addEventListener("contextmenu", (ev) => {
|
btn.addEventListener("contextmenu", (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
menu(ev, [
|
menu(ev, [
|
||||||
|
{ title: "Neues Feature", icon: "plus", onClick: () => this.openCreate() },
|
||||||
{ title: "Feature löschen", icon: "trash", onClick: () => this.openDelete(f.basename) },
|
{ title: "Feature löschen", icon: "trash", onClick: () => this.openDelete(f.basename) },
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@@ -139,7 +144,7 @@ export class DetailsView extends ItemView {
|
|||||||
private openCreate(): void {
|
private openCreate(): void {
|
||||||
const taken = listMarkdownFiles(
|
const taken = listMarkdownFiles(
|
||||||
this.app,
|
this.app,
|
||||||
areaPath(this.project, this.area, this.zone),
|
collectionPath(this.project, this.collection, this.zone),
|
||||||
[],
|
[],
|
||||||
).map((f) => f.basename);
|
).map((f) => f.basename);
|
||||||
new NameModal(this.app, {
|
new NameModal(this.app, {
|
||||||
@@ -148,26 +153,29 @@ export class DetailsView extends ItemView {
|
|||||||
cta: "Erstellen",
|
cta: "Erstellen",
|
||||||
validate: (n) => validateName(n, taken),
|
validate: (n) => validateName(n, taken),
|
||||||
onSubmit: async (name) => {
|
onSubmit: async (name) => {
|
||||||
await createFeature(this.app, this.project, this.area, name, this.zone);
|
await createFeature(this.app, this.project, this.collection, name, this.zone);
|
||||||
await this.render();
|
await this.render();
|
||||||
},
|
},
|
||||||
}).open();
|
}).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async openDelete(feature: string): Promise<void> {
|
private async openDelete(feature: string): Promise<void> {
|
||||||
await deleteRecursive(this.app, featurePath(this.project, this.area, feature, this.zone));
|
await deleteRecursive(
|
||||||
|
this.app,
|
||||||
|
featurePath(this.project, this.collection, feature, this.zone),
|
||||||
|
);
|
||||||
await this.render();
|
await this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async openOverview(): Promise<void> {
|
private async openProjectDetails(): Promise<void> {
|
||||||
await this.leaf.setViewState({
|
await this.leaf.setViewState({
|
||||||
type: VIEW_TYPE_OVERVIEW,
|
type: VIEW_TYPE_PROJECT_DETAILS_VIEW,
|
||||||
active: true,
|
active: true,
|
||||||
state: { project: this.project },
|
state: { project: this.project },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async openProjectsView(): Promise<void> {
|
private async openProjectView(): Promise<void> {
|
||||||
await this.leaf.setViewState({ type: VIEW_TYPE_PROJECTS, active: true });
|
await this.leaf.setViewState({ type: VIEW_TYPE_PROJECT_VIEW, active: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,17 @@
|
|||||||
import { ItemView, MarkdownRenderer, Notice, TFile, TFolder, WorkspaceLeaf, ViewStateResult, normalizePath } from "obsidian";
|
|
||||||
import {
|
import {
|
||||||
VIEW_TYPE_OVERVIEW,
|
ItemView,
|
||||||
VIEW_TYPE_DETAILS,
|
MarkdownRenderer,
|
||||||
VIEW_TYPE_PROJECTS,
|
Notice,
|
||||||
|
TFile,
|
||||||
|
TFolder,
|
||||||
|
WorkspaceLeaf,
|
||||||
|
ViewStateResult,
|
||||||
|
normalizePath,
|
||||||
|
} from "obsidian";
|
||||||
|
import {
|
||||||
|
VIEW_TYPE_PROJECT_DETAILS_VIEW,
|
||||||
|
VIEW_TYPE_COLLECTION_VIEW,
|
||||||
|
VIEW_TYPE_PROJECT_VIEW,
|
||||||
RIBBON_ICON,
|
RIBBON_ICON,
|
||||||
PROJECT_FILES,
|
PROJECT_FILES,
|
||||||
CORE_FILE,
|
CORE_FILE,
|
||||||
@@ -20,14 +29,13 @@ import {
|
|||||||
rename,
|
rename,
|
||||||
deleteRecursive,
|
deleteRecursive,
|
||||||
ensureFolder,
|
ensureFolder,
|
||||||
createArea,
|
createCollection,
|
||||||
createFeature,
|
|
||||||
createProjectFeature,
|
createProjectFeature,
|
||||||
} from "../fs";
|
} from "../fs";
|
||||||
import { menu, breadcrumb, emptyState, openMarkdown } from "../ui";
|
import { menu, breadcrumb, emptyState, openMarkdown } from "../ui";
|
||||||
import { NameModal } from "../modals/NameModal";
|
import { NameModal } from "../modals/NameModal";
|
||||||
|
|
||||||
export interface OverviewState extends Record<string, unknown> {
|
export interface ProjectDetailsState extends Record<string, unknown> {
|
||||||
project: string;
|
project: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +55,7 @@ function validateName(name: string, taken: string[], current?: string): string |
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class OverviewView extends ItemView {
|
export class ProjectDetailsView extends ItemView {
|
||||||
project = "";
|
project = "";
|
||||||
private renderToken = 0;
|
private renderToken = 0;
|
||||||
|
|
||||||
@@ -57,7 +65,7 @@ export class OverviewView extends ItemView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getViewType(): string {
|
getViewType(): string {
|
||||||
return VIEW_TYPE_OVERVIEW;
|
return VIEW_TYPE_PROJECT_DETAILS_VIEW;
|
||||||
}
|
}
|
||||||
|
|
||||||
getDisplayText(): string {
|
getDisplayText(): string {
|
||||||
@@ -68,13 +76,13 @@ export class OverviewView extends ItemView {
|
|||||||
return RIBBON_ICON;
|
return RIBBON_ICON;
|
||||||
}
|
}
|
||||||
|
|
||||||
async setState(state: OverviewState, result: ViewStateResult): Promise<void> {
|
async setState(state: ProjectDetailsState, result: ViewStateResult): Promise<void> {
|
||||||
this.project = state?.project ?? "";
|
this.project = state?.project ?? "";
|
||||||
await super.setState(state, result);
|
await super.setState(state, result);
|
||||||
await this.render();
|
await this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
getState(): OverviewState {
|
getState(): ProjectDetailsState {
|
||||||
return { project: this.project };
|
return { project: this.project };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,14 +122,14 @@ export class OverviewView extends ItemView {
|
|||||||
root.addClass("pk-root");
|
root.addClass("pk-root");
|
||||||
|
|
||||||
breadcrumb(root, [
|
breadcrumb(root, [
|
||||||
{ label: "Projekte", onClick: () => this.openProjectsView() },
|
{ label: "Projekte", onClick: () => this.openProjectView() },
|
||||||
{ label: this.project },
|
{ label: this.project },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const info = root.createDiv({ cls: "pk-info-grid" });
|
const info = root.createDiv({ cls: "pk-info-grid" });
|
||||||
this.renderInfoCard(info, core, corePath);
|
this.renderInfoCard(info, core, corePath);
|
||||||
this.renderInfoCard(info, target, targetPath);
|
this.renderInfoCard(info, target, targetPath);
|
||||||
this.renderAreas(root);
|
this.renderCollections(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderInfoCard(parent: HTMLElement, content: string, path: string): void {
|
private renderInfoCard(parent: HTMLElement, content: string, path: string): void {
|
||||||
@@ -137,13 +145,13 @@ export class OverviewView extends ItemView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderAreas(parent: HTMLElement): void {
|
private renderCollections(parent: HTMLElement): void {
|
||||||
const section = parent.createDiv({ cls: "pk-areas-section" });
|
const section = parent.createDiv({ cls: "pk-areas-section" });
|
||||||
section.addEventListener("contextmenu", (ev) => {
|
section.addEventListener("contextmenu", (ev) => {
|
||||||
if (ev.defaultPrevented) return;
|
if (ev.defaultPrevented) return;
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
menu(ev, [
|
menu(ev, [
|
||||||
{ title: "Neue Collection", icon: "plus", onClick: () => this.openCreateArea() },
|
{ title: "Neue Collection", icon: "plus", onClick: () => this.openCreateCollection() },
|
||||||
{ title: "Neues Feature", icon: "plus", onClick: () => this.openCreateProjectFeature() },
|
{ title: "Neues Feature", icon: "plus", onClick: () => this.openCreateProjectFeature() },
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@@ -156,10 +164,10 @@ export class OverviewView extends ItemView {
|
|||||||
this.renderZone(section, "to-update", toUpdate);
|
this.renderZone(section, "to-update", toUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private collectZoneItems(zone: Zone): { areas: TFolder[]; features: TFile[] } {
|
private collectZoneItems(zone: Zone): { collections: TFolder[]; features: TFile[] } {
|
||||||
const root = zoneRootPath(this.project, zone);
|
const root = zoneRootPath(this.project, zone);
|
||||||
const folders = listFolders(this.app, root);
|
const folders = listFolders(this.app, root);
|
||||||
const areas = zone === "ready"
|
const collections = zone === "ready"
|
||||||
? folders.filter((f) => f.name !== TO_UPDATE_DIR)
|
? folders.filter((f) => f.name !== TO_UPDATE_DIR)
|
||||||
: folders;
|
: folders;
|
||||||
const features = listMarkdownFiles(
|
const features = listMarkdownFiles(
|
||||||
@@ -167,13 +175,13 @@ export class OverviewView extends ItemView {
|
|||||||
root,
|
root,
|
||||||
zone === "ready" ? [...PROJECT_FILES] : [],
|
zone === "ready" ? [...PROJECT_FILES] : [],
|
||||||
);
|
);
|
||||||
return { areas, features };
|
return { collections, features };
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderZone(
|
private renderZone(
|
||||||
parent: HTMLElement,
|
parent: HTMLElement,
|
||||||
zone: Zone,
|
zone: Zone,
|
||||||
items: { areas: TFolder[]; features: TFile[] },
|
items: { collections: TFolder[]; features: TFile[] },
|
||||||
): void {
|
): void {
|
||||||
const flex = parent.createDiv({
|
const flex = parent.createDiv({
|
||||||
cls: `pk-areas-flex pk-zone-${zone}`,
|
cls: `pk-areas-flex pk-zone-${zone}`,
|
||||||
@@ -198,42 +206,39 @@ export class OverviewView extends ItemView {
|
|||||||
await this.handleDropOnZone(raw, zone);
|
await this.handleDropOnZone(raw, zone);
|
||||||
});
|
});
|
||||||
|
|
||||||
const takenAreas = items.areas.map((a) => a.name);
|
const takenCollections = items.collections.map((a) => a.name);
|
||||||
for (const area of items.areas) {
|
for (const collection of items.collections) {
|
||||||
this.renderAreaCard(flex, zone, area, takenAreas);
|
this.renderCollectionCard(flex, zone, collection, takenCollections);
|
||||||
}
|
}
|
||||||
for (const f of items.features) {
|
for (const f of items.features) {
|
||||||
this.renderProjectFeatureCard(flex, f);
|
this.renderProjectFeatureCard(flex, zone, f);
|
||||||
}
|
}
|
||||||
if (items.areas.length === 0 && items.features.length === 0) {
|
if (items.collections.length === 0 && items.features.length === 0) {
|
||||||
flex.createDiv({
|
flex.createDiv({ cls: "pk-zone-placeholder", text: "Keine Features" });
|
||||||
cls: "pk-zone-placeholder",
|
|
||||||
text: zone === "to-update" ? "Nothing to update" : "Empty",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderAreaCard(
|
private renderCollectionCard(
|
||||||
parent: HTMLElement,
|
parent: HTMLElement,
|
||||||
zone: Zone,
|
zone: Zone,
|
||||||
area: TFolder,
|
collection: TFolder,
|
||||||
takenAreas: string[],
|
takenCollections: string[],
|
||||||
): void {
|
): void {
|
||||||
const folderPath = area.path;
|
const folderPath = collection.path;
|
||||||
const features = listMarkdownFiles(this.app, folderPath, []);
|
const features = listMarkdownFiles(this.app, folderPath, []);
|
||||||
|
|
||||||
const card = parent.createDiv({
|
const card = parent.createDiv({
|
||||||
cls: "pk-btn-card pk-area-card",
|
cls: "pk-btn-card pk-area-card",
|
||||||
attr: { role: "button", tabindex: "0", draggable: "true" },
|
attr: { role: "button", tabindex: "0", draggable: "true" },
|
||||||
});
|
});
|
||||||
card.createEl("strong", { text: area.name });
|
card.createEl("strong", { text: collection.name });
|
||||||
card.addEventListener("click", () => this.openDetails(area.name, zone));
|
card.addEventListener("click", () => this.openCollectionDetails(collection.name, zone));
|
||||||
card.addEventListener("contextmenu", (ev) => {
|
card.addEventListener("contextmenu", (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
menu(ev, [
|
menu(ev, [
|
||||||
{ title: "Neues Feature", icon: "plus", onClick: () => this.openCreateFeatureIn(folderPath) },
|
{ title: "Neue Collection", icon: "plus", onClick: () => this.openCreateCollection() },
|
||||||
{ title: "Collection umbenennen", icon: "pencil", onClick: () => this.openRenameAreaAt(folderPath, area.name, takenAreas) },
|
{ title: "Collection umbenennen", icon: "pencil", onClick: () => this.openRenameCollectionAt(folderPath, collection.name, takenCollections) },
|
||||||
{ title: "Collection löschen", icon: "trash", onClick: () => this.openDeletePath(folderPath) },
|
{ title: "Collection löschen", icon: "trash", onClick: () => this.openDeletePath(folderPath) },
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@@ -242,7 +247,7 @@ export class OverviewView extends ItemView {
|
|||||||
ev.dataTransfer.setData(PK_DND_MIME, JSON.stringify({
|
ev.dataTransfer.setData(PK_DND_MIME, JSON.stringify({
|
||||||
kind: "collection",
|
kind: "collection",
|
||||||
sourcePath: folderPath,
|
sourcePath: folderPath,
|
||||||
name: area.name,
|
name: collection.name,
|
||||||
} satisfies DndPayload));
|
} satisfies DndPayload));
|
||||||
ev.dataTransfer.effectAllowed = "move";
|
ev.dataTransfer.effectAllowed = "move";
|
||||||
card.addClass("pk-feature-chip-dragging");
|
card.addClass("pk-feature-chip-dragging");
|
||||||
@@ -265,17 +270,18 @@ export class OverviewView extends ItemView {
|
|||||||
if (!raw) return;
|
if (!raw) return;
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
await this.handleDropOnArea(raw, folderPath);
|
await this.handleDropOnCollection(raw, folderPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (features.length === 0) {
|
if (features.length === 0) {
|
||||||
card.createDiv({ cls: "pk-empty", text: "Keine Features." });
|
card.createDiv({ cls: "pk-empty", text: "Keine Features" });
|
||||||
} else {
|
} else {
|
||||||
const list = card.createDiv({ cls: "pk-features" });
|
const list = card.createDiv({ cls: "pk-features" });
|
||||||
for (const f of features) {
|
for (const f of features) {
|
||||||
const chip = list.createDiv({ cls: "pk-feature-chip", text: f.basename });
|
const chip = list.createDiv({ cls: "pk-feature-chip", text: f.basename });
|
||||||
chip.draggable = true;
|
chip.draggable = true;
|
||||||
chip.addEventListener("dragstart", (ev) => {
|
chip.addEventListener("dragstart", (ev) => {
|
||||||
|
ev.stopPropagation();
|
||||||
if (!ev.dataTransfer) return;
|
if (!ev.dataTransfer) return;
|
||||||
ev.dataTransfer.setData(PK_DND_MIME, JSON.stringify({
|
ev.dataTransfer.setData(PK_DND_MIME, JSON.stringify({
|
||||||
kind: "feature",
|
kind: "feature",
|
||||||
@@ -294,6 +300,7 @@ export class OverviewView extends ItemView {
|
|||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
menu(ev, [
|
menu(ev, [
|
||||||
|
{ title: "Neues Feature", icon: "plus", onClick: () => this.openCreateFeatureIn(folderPath) },
|
||||||
{ title: "Feature löschen", icon: "trash", onClick: () => this.openDeletePath(f.path) },
|
{ title: "Feature löschen", icon: "trash", onClick: () => this.openDeletePath(f.path) },
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@@ -301,7 +308,7 @@ export class OverviewView extends ItemView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderProjectFeatureCard(parent: HTMLElement, file: TFile): void {
|
private renderProjectFeatureCard(parent: HTMLElement, zone: Zone, file: TFile): void {
|
||||||
const card = parent.createDiv({
|
const card = parent.createDiv({
|
||||||
cls: "pk-btn-card pk-area-card",
|
cls: "pk-btn-card pk-area-card",
|
||||||
attr: { role: "button", tabindex: "0", draggable: "true" },
|
attr: { role: "button", tabindex: "0", draggable: "true" },
|
||||||
@@ -323,6 +330,7 @@ export class OverviewView extends ItemView {
|
|||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
menu(ev, [
|
menu(ev, [
|
||||||
|
{ title: "Neues Feature", icon: "plus", onClick: () => this.openCreateProjectFeatureInZone(zone) },
|
||||||
{ title: "Feature löschen", icon: "trash", onClick: () => this.openDeletePath(file.path) },
|
{ title: "Feature löschen", icon: "trash", onClick: () => this.openDeletePath(file.path) },
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@@ -352,12 +360,12 @@ export class OverviewView extends ItemView {
|
|||||||
await this.movePath(data.sourcePath, newPath);
|
await this.movePath(data.sourcePath, newPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleDropOnArea(raw: string, areaFolderPath: string): Promise<void> {
|
private async handleDropOnCollection(raw: string, collectionFolderPath: string): Promise<void> {
|
||||||
const data = this.parseDnd(raw);
|
const data = this.parseDnd(raw);
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
if (data.kind !== "feature") return;
|
if (data.kind !== "feature") return;
|
||||||
const file = data.name.endsWith(".md") ? data.name : `${data.name}.md`;
|
const file = data.name.endsWith(".md") ? data.name : `${data.name}.md`;
|
||||||
const newPath = normalizePath(`${areaFolderPath}/${file}`);
|
const newPath = normalizePath(`${collectionFolderPath}/${file}`);
|
||||||
if (data.sourcePath === newPath) return;
|
if (data.sourcePath === newPath) return;
|
||||||
await this.movePath(data.sourcePath, newPath);
|
await this.movePath(data.sourcePath, newPath);
|
||||||
}
|
}
|
||||||
@@ -377,8 +385,16 @@ export class OverviewView extends ItemView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private openCreateProjectFeature(): void {
|
private openCreateProjectFeature(): void {
|
||||||
const root = toUpdatePath(this.project);
|
this.openCreateProjectFeatureInZone("to-update");
|
||||||
const existing = listMarkdownFiles(this.app, root, []);
|
}
|
||||||
|
|
||||||
|
private openCreateProjectFeatureInZone(zone: Zone): void {
|
||||||
|
const root = zoneRootPath(this.project, zone);
|
||||||
|
const existing = listMarkdownFiles(
|
||||||
|
this.app,
|
||||||
|
root,
|
||||||
|
zone === "ready" ? [...PROJECT_FILES] : [],
|
||||||
|
);
|
||||||
const taken = existing.map((f) => f.basename);
|
const taken = existing.map((f) => f.basename);
|
||||||
new NameModal(this.app, {
|
new NameModal(this.app, {
|
||||||
title: "Neues Feature",
|
title: "Neues Feature",
|
||||||
@@ -386,13 +402,13 @@ export class OverviewView extends ItemView {
|
|||||||
cta: "Erstellen",
|
cta: "Erstellen",
|
||||||
validate: (n) => validateName(n, taken),
|
validate: (n) => validateName(n, taken),
|
||||||
onSubmit: async (name) => {
|
onSubmit: async (name) => {
|
||||||
await createProjectFeature(this.app, this.project, name);
|
await createProjectFeature(this.app, this.project, name, zone);
|
||||||
await this.render();
|
await this.render();
|
||||||
},
|
},
|
||||||
}).open();
|
}).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
private openCreateArea(): void {
|
private openCreateCollection(): void {
|
||||||
const ready = listFolders(this.app, projectPath(this.project))
|
const ready = listFolders(this.app, projectPath(this.project))
|
||||||
.filter((f) => f.name !== TO_UPDATE_DIR).map((a) => a.name);
|
.filter((f) => f.name !== TO_UPDATE_DIR).map((a) => a.name);
|
||||||
const inToUpdate = listFolders(this.app, toUpdatePath(this.project)).map((a) => a.name);
|
const inToUpdate = listFolders(this.app, toUpdatePath(this.project)).map((a) => a.name);
|
||||||
@@ -403,7 +419,7 @@ export class OverviewView extends ItemView {
|
|||||||
cta: "Erstellen",
|
cta: "Erstellen",
|
||||||
validate: (n) => validateName(n, taken),
|
validate: (n) => validateName(n, taken),
|
||||||
onSubmit: async (name) => {
|
onSubmit: async (name) => {
|
||||||
await createArea(this.app, this.project, name);
|
await createCollection(this.app, this.project, name);
|
||||||
await this.render();
|
await this.render();
|
||||||
},
|
},
|
||||||
}).open();
|
}).open();
|
||||||
@@ -426,7 +442,7 @@ export class OverviewView extends ItemView {
|
|||||||
}).open();
|
}).open();
|
||||||
}
|
}
|
||||||
|
|
||||||
private openRenameAreaAt(folderPath: string, current: string, taken: string[]): void {
|
private openRenameCollectionAt(folderPath: string, current: string, taken: string[]): void {
|
||||||
new NameModal(this.app, {
|
new NameModal(this.app, {
|
||||||
title: "Collection umbenennen",
|
title: "Collection umbenennen",
|
||||||
label: "Collection-Name",
|
label: "Collection-Name",
|
||||||
@@ -447,15 +463,15 @@ export class OverviewView extends ItemView {
|
|||||||
await this.render();
|
await this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async openDetails(area: string, zone: Zone): Promise<void> {
|
private async openCollectionDetails(collection: string, zone: Zone): Promise<void> {
|
||||||
await this.leaf.setViewState({
|
await this.leaf.setViewState({
|
||||||
type: VIEW_TYPE_DETAILS,
|
type: VIEW_TYPE_COLLECTION_VIEW,
|
||||||
active: true,
|
active: true,
|
||||||
state: { project: this.project, area, zone },
|
state: { project: this.project, collection, zone },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async openProjectsView(): Promise<void> {
|
private async openProjectView(): Promise<void> {
|
||||||
await this.leaf.setViewState({ type: VIEW_TYPE_PROJECTS, active: true });
|
await this.leaf.setViewState({ type: VIEW_TYPE_PROJECT_VIEW, active: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,16 @@
|
|||||||
import { ItemView, Notice, WorkspaceLeaf, normalizePath } from "obsidian";
|
import {
|
||||||
import { VIEW_TYPE_PROJECTS, VIEW_TYPE_OVERVIEW, RIBBON_ICON, CORE_FILE } from "../const";
|
ItemView,
|
||||||
|
MarkdownRenderer,
|
||||||
|
Notice,
|
||||||
|
WorkspaceLeaf,
|
||||||
|
normalizePath,
|
||||||
|
} from "obsidian";
|
||||||
|
import {
|
||||||
|
VIEW_TYPE_PROJECT_VIEW,
|
||||||
|
VIEW_TYPE_PROJECT_DETAILS_VIEW,
|
||||||
|
RIBBON_ICON,
|
||||||
|
CORE_FILE,
|
||||||
|
} from "../const";
|
||||||
import {
|
import {
|
||||||
projectsPath,
|
projectsPath,
|
||||||
projectPath,
|
projectPath,
|
||||||
@@ -22,7 +33,7 @@ function validateName(name: string, taken: string[], current?: string): string |
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ProjectsView extends ItemView {
|
export class ProjectView extends ItemView {
|
||||||
private renderToken = 0;
|
private renderToken = 0;
|
||||||
|
|
||||||
constructor(leaf: WorkspaceLeaf) {
|
constructor(leaf: WorkspaceLeaf) {
|
||||||
@@ -31,7 +42,7 @@ export class ProjectsView extends ItemView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getViewType(): string {
|
getViewType(): string {
|
||||||
return VIEW_TYPE_PROJECTS;
|
return VIEW_TYPE_PROJECT_VIEW;
|
||||||
}
|
}
|
||||||
|
|
||||||
getDisplayText(): string {
|
getDisplayText(): string {
|
||||||
@@ -79,11 +90,8 @@ export class ProjectsView extends ItemView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const taken = projects.map((p) => p.name);
|
const taken = projects.map((p) => p.name);
|
||||||
const cores = await Promise.all(
|
const corePaths = projects.map((p) => normalizePath(`${projectPath(p.name)}/${CORE_FILE}`));
|
||||||
projects.map((p) =>
|
const cores = await Promise.all(corePaths.map((p) => readFile(this.app, p)));
|
||||||
readFile(this.app, normalizePath(`${projectPath(p.name)}/${CORE_FILE}`)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if (token !== this.renderToken) return;
|
if (token !== this.renderToken) return;
|
||||||
|
|
||||||
root.empty();
|
root.empty();
|
||||||
@@ -94,13 +102,17 @@ export class ProjectsView extends ItemView {
|
|||||||
for (let i = 0; i < projects.length; i++) {
|
for (let i = 0; i < projects.length; i++) {
|
||||||
const proj = projects[i];
|
const proj = projects[i];
|
||||||
const core = cores[i].trim();
|
const core = cores[i].trim();
|
||||||
|
const corePath = corePaths[i];
|
||||||
const btn = grid.createDiv({
|
const btn = grid.createDiv({
|
||||||
cls: "pk-btn-card",
|
cls: "pk-btn-card",
|
||||||
attr: { role: "button", tabindex: "0" },
|
attr: { role: "button", tabindex: "0" },
|
||||||
});
|
});
|
||||||
btn.createEl("strong", { text: proj.name });
|
btn.createEl("strong", { text: proj.name });
|
||||||
if (core) btn.createDiv({ text: core });
|
if (core) {
|
||||||
btn.addEventListener("click", () => this.openOverview(proj.name));
|
const body = btn.createDiv({ cls: "pk-project-core" });
|
||||||
|
void MarkdownRenderer.render(this.app, core, body, corePath, this);
|
||||||
|
}
|
||||||
|
btn.addEventListener("click", () => this.openProjectDetails(proj.name));
|
||||||
btn.addEventListener("contextmenu", (ev) => {
|
btn.addEventListener("contextmenu", (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
menu(ev, [
|
menu(ev, [
|
||||||
@@ -158,9 +170,9 @@ export class ProjectsView extends ItemView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async openOverview(name: string): Promise<void> {
|
private async openProjectDetails(name: string): Promise<void> {
|
||||||
await this.leaf.setViewState({
|
await this.leaf.setViewState({
|
||||||
type: VIEW_TYPE_OVERVIEW,
|
type: VIEW_TYPE_PROJECT_DETAILS_VIEW,
|
||||||
active: true,
|
active: true,
|
||||||
state: { project: name },
|
state: { project: name },
|
||||||
});
|
});
|
||||||
11
styles.css
11
styles.css
@@ -192,6 +192,17 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pk-project-core {
|
||||||
|
font-size: 0.85em;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
.pk-project-core > p:first-child { margin-top: 0; }
|
||||||
|
.pk-project-core > p:last-child { margin-bottom: 0; }
|
||||||
|
.pk-project-core > ul:first-child,
|
||||||
|
.pk-project-core > ol:first-child { margin-top: 0; }
|
||||||
|
.pk-project-core > ul:last-child,
|
||||||
|
.pk-project-core > ol:last-child { margin-bottom: 0; }
|
||||||
|
|
||||||
.pk-info-card > p:first-child { margin-top: 0; }
|
.pk-info-card > p:first-child { margin-top: 0; }
|
||||||
.pk-info-card > p:last-child { margin-bottom: 0; }
|
.pk-info-card > p:last-child { margin-bottom: 0; }
|
||||||
.pk-info-card > ul:first-child,
|
.pk-info-card > ul:first-child,
|
||||||
|
|||||||
Reference in New Issue
Block a user