Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 2 additions & 30 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 23 additions & 7 deletions packages/note-boards/src/board-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { PopupMenu, useDraggable, usePreventChildDrag } from "@notes/ui";
import { DragEvent, useEffect, useRef, useState } from "react";
import type { IBoardColumn, RichCard } from "./board-format";
import { cardFilePath } from "./board-format";

const LABEL_COLORS = ["#e2f0fb", "#fde8d8", "#d9f2e8", "#f5e6fb", "#fef9c3"];

Expand All @@ -16,6 +17,7 @@ export function BoardCard({
updateCardState,
column,
onOpenModal,
boardPath,
isColumnDragActive = false,
}: {
card: RichCard;
Expand All @@ -28,6 +30,7 @@ export function BoardCard({
updateCardState: (card: RichCard) => void;
column: IBoardColumn;
onOpenModal: (card: RichCard) => void;
boardPath: string;
isColumnDragActive?: boolean;
}) {
const [expanded, setExpanded] = useState(false);
Expand Down Expand Up @@ -212,13 +215,26 @@ export function BoardCard({
open={menuIsOpen}
onClose={() => setMenuIsOpen(false)}
menu={
<button
className="board-card-del"
aria-label="Delete card"
onClick={() => void handleDeleteCard(card.id)}
>
🗑 Delete
</button>
<>
<button
className="board-card-menu-item"
aria-label="Copy link to card"
onClick={() => {
const link = `![[${cardFilePath(boardPath, card.id)}]]`;
void navigator.clipboard?.writeText(link);
setMenuIsOpen(false);
}}
>
🔗 Copy link
</button>
<button
className="board-card-del"
aria-label="Delete card"
onClick={() => void handleDeleteCard(card.id)}
>
🗑 Delete
</button>
</>
}
>
<button
Expand Down
3 changes: 3 additions & 0 deletions packages/note-boards/src/board-column.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export function BoardColumn({
updateCardState,
handleDeleteCard,
setModalCard,
boardPath,
dragRef,
}: {
column: IBoardColumn;
Expand All @@ -47,6 +48,7 @@ export function BoardColumn({
updateCardState: (updated: RichCard) => void;
handleDeleteCard: (cardId: string) => void;
setModalCard: (card: RichCard | null) => void;
boardPath: string;
dragRef: React.RefObject<{ cardId: string; fromColumn: string } | null>;
}) {
const dragHandle = useDraggable(
Expand Down Expand Up @@ -159,6 +161,7 @@ export function BoardColumn({
handleDeleteCard={handleDeleteCard}
column={column}
onOpenModal={setModalCard}
boardPath={boardPath}
isColumnDragActive={Boolean(draggingColumnName)}
/>
</Fragment>
Expand Down
14 changes: 14 additions & 0 deletions packages/note-boards/src/board-format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ import {
parseFrontmatter,
} from "@notes/web/src/lib/frontmatter";

/** Returns the companion dot-folder for a board's card files (relative to tome root). */
export function cardDotFolder(boardPath: string): string {
const lastSlash = boardPath.lastIndexOf("/");
const dir = lastSlash !== -1 ? boardPath.slice(0, lastSlash + 1) : "";
const filename = lastSlash !== -1 ? boardPath.slice(lastSlash + 1) : boardPath;
const base = filename.endsWith(".md") ? filename.slice(0, -3) : filename;
return `${dir}.${base}.cards`;
}

/** Returns the file path for a single card within a board (relative to tome root). */
export function cardFilePath(boardPath: string, cardId: string): string {
return `${cardDotFolder(boardPath)}/${cardId}.md`;
}

export interface RichCard {
id: string;
title: string;
Expand Down
11 changes: 10 additions & 1 deletion packages/note-boards/src/board-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { BoardCardModal } from "./board-card-modal";
import {
type IBoardColumn,
BoardModel,
cardFilePath,
parseBoard,
type RichCard,
serializeBoard,
Expand Down Expand Up @@ -338,6 +339,13 @@ export function BoardView({ value, onChange, path, onRegisterContextMenu }: Rend
const card = cardsRef.current.get(cardId);
if (!card) return [];
return [
{
label: "Copy link",
run: () => {
const link = `![[${cardFilePath(path, cardId)}]]`;
void navigator.clipboard?.writeText(link);
},
},
{
label: "Duplicate card",
run: () => void duplicateCard(card),
Expand All @@ -354,7 +362,7 @@ export function BoardView({ value, onChange, path, onRegisterContextMenu }: Rend
};
onRegisterContextMenu(builder);
return () => onRegisterContextMenu(null);
}, [duplicateCard, handleDeleteCard, onRegisterContextMenu]);
}, [duplicateCard, handleDeleteCard, onRegisterContextMenu, path]);

const addColumn = async () => {
const values = await openPrompt({
Expand Down Expand Up @@ -470,6 +478,7 @@ export function BoardView({ value, onChange, path, onRegisterContextMenu }: Rend
updateCardState={updateCardState}
handleDeleteCard={handleDeleteCard}
setModalCard={setModalCard}
boardPath={path}
dragRef={dragRef}
/>
</Fragment>
Expand Down
6 changes: 6 additions & 0 deletions packages/note-boards/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@
color: var(--fg-muted);
}

.board-card-menu-item {
background: transparent;
border: none;
border-radius: 4px;
}

.board-card-del {
background: transparent;
border: none;
Expand Down
14 changes: 14 additions & 0 deletions packages/note-calendar/src/calendar-format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ import {
parseFrontmatter,
} from "@notes/web/src/lib/frontmatter";

/** Returns the companion dot-folder for a calendar's event files (relative to tome root). */
export function eventDotFolder(calendarPath: string): string {
const lastSlash = calendarPath.lastIndexOf("/");
const dir = lastSlash !== -1 ? calendarPath.slice(0, lastSlash + 1) : "";
const filename = lastSlash !== -1 ? calendarPath.slice(lastSlash + 1) : calendarPath;
const base = filename.endsWith(".md") ? filename.slice(0, -3) : filename;
return `${dir}.${base}.events`;
}

/** Returns the file path for a single event within a calendar (relative to tome root). */
export function eventFilePath(calendarPath: string, eventId: string): string {
return `${eventDotFolder(calendarPath)}/${eventId}.md`;
}

export interface RichEvent {
id: string;
title: string;
Expand Down
1 change: 1 addition & 0 deletions packages/note-calendar/src/calendar-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ export function CalendarView({ value, path }: RendererProps) {
setSelectedEvent={setSelectedEvent}
onUpdateEvent={updateEventState}
onDeleteEvent={handleDeleteEvent}
calendarPath={path}
/>
)}
</div>
Expand Down
14 changes: 14 additions & 0 deletions packages/note-calendar/src/event-details.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { RenderedEditor } from "@notes/editor";
import type { RichEvent } from "./calendar-format";
import { eventFilePath } from "./calendar-format";

const DURATION_OPTIONS = [
{ value: 15, label: "15 min" },
Expand All @@ -15,16 +16,29 @@ export function EventDetails({
setSelectedEvent,
onUpdateEvent,
onDeleteEvent,
calendarPath,
}: {
selectedEvent: RichEvent;
setSelectedEvent: (event: RichEvent | null) => void;
onUpdateEvent: (event: RichEvent) => void;
onDeleteEvent: (eventId: string) => void;
calendarPath: string;
}) {
return (
<div className="calendar-event-panel">
<div className="calendar-event-panel-header">
<span className="calendar-event-panel-title">Edit Event</span>
<button
className="calendar-event-panel-copy-link"
aria-label="Copy link to event"
title="Copy link"
onClick={() => {
const link = `![[${eventFilePath(calendarPath, selectedEvent.id)}]]`;
void navigator.clipboard?.writeText(link);
}}
>
🔗
</button>
<button
className="calendar-event-panel-close"
aria-label="Close event editor"
Expand Down