mirror of
https://github.com/dotwee/obsidian-raindropio-plugin.git
synced 2026-07-22 06:50:29 +00:00
feat(renderer): add configurable result display fields
Introduce a display fields option so callers can toggle cover, domain, created date, excerpt, tags and collection per result row. Render an optional cover thumbnail and a collection label resolved from a collection title map. Defaults keep the previous appearance, so existing views render unchanged. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
a41574e926
commit
b643644fb0
2 changed files with 73 additions and 3 deletions
|
|
@ -2,10 +2,47 @@ import type { RaindropItem } from "./raindrop-api";
|
||||||
|
|
||||||
export type RaindropStatusKind = "loading" | "empty" | "error" | "info";
|
export type RaindropStatusKind = "loading" | "empty" | "error" | "info";
|
||||||
|
|
||||||
|
export interface RaindropDisplayFields {
|
||||||
|
cover: boolean;
|
||||||
|
domain: boolean;
|
||||||
|
created: boolean;
|
||||||
|
excerpt: boolean;
|
||||||
|
tags: boolean;
|
||||||
|
collection: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DEFAULT_DISPLAY_FIELDS: RaindropDisplayFields = {
|
||||||
|
cover: false,
|
||||||
|
domain: true,
|
||||||
|
created: true,
|
||||||
|
excerpt: true,
|
||||||
|
tags: true,
|
||||||
|
collection: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RAINDROP_FIELD_NAMES = Object.keys(DEFAULT_DISPLAY_FIELDS) as (keyof RaindropDisplayFields)[];
|
||||||
|
|
||||||
|
export function isRaindropFieldName(value: string): value is keyof RaindropDisplayFields {
|
||||||
|
return (RAINDROP_FIELD_NAMES as string[]).includes(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveRaindropDisplayFields(
|
||||||
|
defaults: RaindropDisplayFields,
|
||||||
|
show?: (keyof RaindropDisplayFields)[],
|
||||||
|
hide?: (keyof RaindropDisplayFields)[],
|
||||||
|
): RaindropDisplayFields {
|
||||||
|
const fields = { ...defaults };
|
||||||
|
for (const field of show ?? []) fields[field] = true;
|
||||||
|
for (const field of hide ?? []) fields[field] = false;
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
export interface RenderRaindropOptions {
|
export interface RenderRaindropOptions {
|
||||||
title?: string;
|
title?: string;
|
||||||
warnings?: string[];
|
warnings?: string[];
|
||||||
onTagClick?: (tag: string) => void;
|
onTagClick?: (tag: string) => void;
|
||||||
|
fields?: RaindropDisplayFields;
|
||||||
|
collectionTitles?: Map<number, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDisplayDate(value: string | undefined): string | undefined {
|
function getDisplayDate(value: string | undefined): string | undefined {
|
||||||
|
|
@ -17,6 +54,13 @@ function getDisplayDate(value: string | undefined): string | undefined {
|
||||||
return date.toLocaleDateString();
|
return date.toLocaleDateString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCollectionTitle(item: RaindropItem, collectionTitles: Map<number, string> | undefined): string | undefined {
|
||||||
|
const collectionId = item.collection?.$id;
|
||||||
|
if (typeof collectionId !== "number") return undefined;
|
||||||
|
|
||||||
|
return collectionTitles?.get(collectionId);
|
||||||
|
}
|
||||||
|
|
||||||
export function renderRaindropStatus(container: HTMLElement, message: string, kind: RaindropStatusKind): void {
|
export function renderRaindropStatus(container: HTMLElement, message: string, kind: RaindropStatusKind): void {
|
||||||
container.empty();
|
container.empty();
|
||||||
container.createDiv({
|
container.createDiv({
|
||||||
|
|
@ -32,6 +76,7 @@ export function renderRaindropItems(
|
||||||
): void {
|
): void {
|
||||||
container.empty();
|
container.empty();
|
||||||
|
|
||||||
|
const fields = options.fields ?? DEFAULT_DISPLAY_FIELDS;
|
||||||
const root = container.createDiv({ cls: "raindrop-results" });
|
const root = container.createDiv({ cls: "raindrop-results" });
|
||||||
if (options.title) {
|
if (options.title) {
|
||||||
root.createEl("h4", { cls: "raindrop-results-title", text: options.title });
|
root.createEl("h4", { cls: "raindrop-results-title", text: options.title });
|
||||||
|
|
@ -52,6 +97,18 @@ export function renderRaindropItems(
|
||||||
const list = root.createDiv({ cls: "raindrop-list" });
|
const list = root.createDiv({ cls: "raindrop-list" });
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
const row = list.createDiv({ cls: "raindrop-item" });
|
const row = list.createDiv({ cls: "raindrop-item" });
|
||||||
|
|
||||||
|
if (fields.cover && item.cover) {
|
||||||
|
row.createEl("img", {
|
||||||
|
cls: "raindrop-item-cover",
|
||||||
|
attr: {
|
||||||
|
src: item.cover,
|
||||||
|
alt: "",
|
||||||
|
loading: "lazy",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const title = row.createEl("a", {
|
const title = row.createEl("a", {
|
||||||
cls: "raindrop-item-title",
|
cls: "raindrop-item-title",
|
||||||
text: item.title || item.link,
|
text: item.title || item.link,
|
||||||
|
|
@ -63,16 +120,20 @@ export function renderRaindropItems(
|
||||||
});
|
});
|
||||||
title.setAttr("aria-label", `Open ${item.title || item.link}`);
|
title.setAttr("aria-label", `Open ${item.title || item.link}`);
|
||||||
|
|
||||||
const metaParts = [item.domain, getDisplayDate(item.created)].filter((part): part is string => Boolean(part));
|
const metaParts = [
|
||||||
|
fields.collection ? getCollectionTitle(item, options.collectionTitles) : undefined,
|
||||||
|
fields.domain ? item.domain : undefined,
|
||||||
|
fields.created ? getDisplayDate(item.created) : undefined,
|
||||||
|
].filter((part): part is string => Boolean(part));
|
||||||
if (metaParts.length > 0) {
|
if (metaParts.length > 0) {
|
||||||
row.createDiv({ cls: "raindrop-item-meta", text: metaParts.join(" - ") });
|
row.createDiv({ cls: "raindrop-item-meta", text: metaParts.join(" - ") });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.excerpt) {
|
if (fields.excerpt && item.excerpt) {
|
||||||
row.createDiv({ cls: "raindrop-item-excerpt", text: item.excerpt });
|
row.createDiv({ cls: "raindrop-item-excerpt", text: item.excerpt });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.tags && item.tags.length > 0) {
|
if (fields.tags && item.tags && item.tags.length > 0) {
|
||||||
const tags = row.createDiv({ cls: "raindrop-item-tags" });
|
const tags = row.createDiv({ cls: "raindrop-item-tags" });
|
||||||
for (const tag of item.tags) {
|
for (const tag of item.tags) {
|
||||||
const tagEl = tags.createEl("a", {
|
const tagEl = tags.createEl("a", {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,15 @@
|
||||||
background: var(--background-primary);
|
background: var(--background-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.raindrop-item-cover {
|
||||||
|
border-radius: var(--radius-s);
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
max-height: 8rem;
|
||||||
|
object-fit: cover;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.raindrop-item-title {
|
.raindrop-item-title {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-weight: var(--font-semibold);
|
font-weight: var(--font-semibold);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue