feat: color-coded visibility badges and contextual space display names
- Visibility badges now color-coded: green (public), yellow (permissioned), red (private) with matching card border tints - Public spaces show eye icon instead of lock - Private spaces display as "username's (you)rSpace" instead of raw name - Applied consistently across space switcher dropdown and My Spaces modal Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
aeb9247f96
commit
c7674ac526
|
|
@ -926,23 +926,36 @@ export class RStackIdentity extends HTMLElement {
|
||||||
overlay.addEventListener("click", (e) => { if (e.target === overlay) close(); });
|
overlay.addEventListener("click", (e) => { if (e.target === overlay) close(); });
|
||||||
};
|
};
|
||||||
|
|
||||||
const visIcon = (v: string) =>
|
const visInfo = (v: string) =>
|
||||||
v === "members_only" ? "🔒" : v === "authenticated" ? "🔑" : "🔓";
|
v === "members_only" ? { icon: "🔒", cls: "vis-private", label: "private" }
|
||||||
|
: v === "authenticated" ? { icon: "🔑", cls: "vis-permissioned", label: "permissioned" }
|
||||||
|
: { icon: "👁", cls: "vis-public", label: "public" };
|
||||||
|
|
||||||
|
const displayName = (s: any) => {
|
||||||
|
const v = s.visibility || "public_read";
|
||||||
|
if (v === "members_only") {
|
||||||
|
const username = getUsername();
|
||||||
|
return username ? `${username}'s (you)rSpace` : "(you)rSpace";
|
||||||
|
}
|
||||||
|
return `${(s.name || s.slug).replace(/</g, "<")} rSpace`;
|
||||||
|
};
|
||||||
|
|
||||||
const renderSpaces = (spaces: any[]) => {
|
const renderSpaces = (spaces: any[]) => {
|
||||||
const yourSpaces = spaces.filter((s) => s.role);
|
const yourSpaces = spaces.filter((s) => s.role);
|
||||||
const publicSpaces = spaces.filter((s) => !s.role && s.accessible);
|
const publicSpaces = spaces.filter((s) => !s.role && s.accessible);
|
||||||
|
|
||||||
const cardHTML = (s: any) => `
|
const cardHTML = (s: any) => {
|
||||||
<button class="space-card" data-slug="${s.slug}">
|
const vis = visInfo(s.visibility || "public_read");
|
||||||
|
return `
|
||||||
|
<button class="space-card ${vis.cls}" data-slug="${s.slug}">
|
||||||
<div class="space-card-initial">${(s.name || s.slug).charAt(0).toUpperCase()}</div>
|
<div class="space-card-initial">${(s.name || s.slug).charAt(0).toUpperCase()}</div>
|
||||||
<div class="space-card-name">${(s.name || s.slug).replace(/</g, "<")}</div>
|
<div class="space-card-name">${displayName(s)}</div>
|
||||||
<div class="space-card-meta">
|
<div class="space-card-meta">
|
||||||
<span class="space-vis">${visIcon(s.visibility)} ${s.visibility.replace(/_/g, " ")}</span>
|
<span class="space-vis ${vis.cls}">${vis.icon} ${vis.label}</span>
|
||||||
${s.role ? `<span class="space-role">${s.role}</span>` : ""}
|
${s.role ? `<span class="space-role">${s.role}</span>` : ""}
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
`;
|
`;};
|
||||||
|
|
||||||
const yourSection = yourSpaces.length
|
const yourSection = yourSpaces.length
|
||||||
? `<div class="spaces-section-label">Your Spaces</div>
|
? `<div class="spaces-section-label">Your Spaces</div>
|
||||||
|
|
@ -1290,6 +1303,12 @@ const SPACES_STYLES = `
|
||||||
font-size: 0.7rem; color: #94a3b8; background: rgba(255,255,255,0.06);
|
font-size: 0.7rem; color: #94a3b8; background: rgba(255,255,255,0.06);
|
||||||
padding: 2px 8px; border-radius: 10px;
|
padding: 2px 8px; border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
.space-vis.vis-public { background: rgba(52,211,153,0.15); color: #34d399; }
|
||||||
|
.space-vis.vis-private { background: rgba(248,113,113,0.15); color: #f87171; }
|
||||||
|
.space-vis.vis-permissioned { background: rgba(251,191,36,0.15); color: #fbbf24; }
|
||||||
|
.space-card.vis-public { border-color: rgba(52,211,153,0.3); }
|
||||||
|
.space-card.vis-private { border-color: rgba(248,113,113,0.3); }
|
||||||
|
.space-card.vis-permissioned { border-color: rgba(251,191,36,0.3); }
|
||||||
.space-role {
|
.space-role {
|
||||||
font-size: 0.7rem; color: #06b6d4; background: rgba(6,182,212,0.1);
|
font-size: 0.7rem; color: #06b6d4; background: rgba(6,182,212,0.1);
|
||||||
padding: 2px 8px; border-radius: 10px; font-weight: 600;
|
padding: 2px 8px; border-radius: 10px; font-weight: 600;
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,17 @@ export class RStackSpaceSwitcher extends HTMLElement {
|
||||||
const v = s.visibility || "public_read";
|
const v = s.visibility || "public_read";
|
||||||
if (v === "members_only") return { cls: "vis-private", label: "🔒" };
|
if (v === "members_only") return { cls: "vis-private", label: "🔒" };
|
||||||
if (v === "authenticated") return { cls: "vis-permissioned", label: "🔑" };
|
if (v === "authenticated") return { cls: "vis-permissioned", label: "🔑" };
|
||||||
return { cls: "vis-public", label: "🔓" };
|
return { cls: "vis-public", label: "👁" };
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Format display name based on visibility type */
|
||||||
|
#displayName(s: SpaceInfo): string {
|
||||||
|
const v = s.visibility || "public_read";
|
||||||
|
if (v === "members_only") {
|
||||||
|
const username = getUsername();
|
||||||
|
return username ? `${username}'s (you)rSpace` : "(you)rSpace";
|
||||||
|
}
|
||||||
|
return `${s.name} rSpace`;
|
||||||
}
|
}
|
||||||
|
|
||||||
#renderMenu(menu: HTMLElement, current: string) {
|
#renderMenu(menu: HTMLElement, current: string) {
|
||||||
|
|
@ -178,7 +188,7 @@ export class RStackSpaceSwitcher extends HTMLElement {
|
||||||
<div class="item-row ${vis.cls} ${s.slug === current ? "active" : ""}">
|
<div class="item-row ${vis.cls} ${s.slug === current ? "active" : ""}">
|
||||||
<a class="item" href="${rspaceNavUrl(s.slug, moduleId)}">
|
<a class="item" href="${rspaceNavUrl(s.slug, moduleId)}">
|
||||||
<span class="item-icon">${s.icon || "🌐"}</span>
|
<span class="item-icon">${s.icon || "🌐"}</span>
|
||||||
<span class="item-name">${s.name}</span>
|
<span class="item-name">${this.#displayName(s)}</span>
|
||||||
<span class="item-vis ${vis.cls}">${vis.label}</span>
|
<span class="item-vis ${vis.cls}">${vis.label}</span>
|
||||||
</a>${canEdit ? `<button class="item-gear" data-edit-slug="${s.slug}" data-edit-name="${s.name.replace(/"/g, """)}" title="Edit Space">⚙</button>` : ""}
|
</a>${canEdit ? `<button class="item-gear" data-edit-slug="${s.slug}" data-edit-name="${s.name.replace(/"/g, """)}" title="Edit Space">⚙</button>` : ""}
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
@ -197,7 +207,7 @@ export class RStackSpaceSwitcher extends HTMLElement {
|
||||||
<a class="item ${vis.cls} ${s.slug === current ? "active" : ""}"
|
<a class="item ${vis.cls} ${s.slug === current ? "active" : ""}"
|
||||||
href="${rspaceNavUrl(s.slug, moduleId)}">
|
href="${rspaceNavUrl(s.slug, moduleId)}">
|
||||||
<span class="item-icon">${s.icon || "🌐"}</span>
|
<span class="item-icon">${s.icon || "🌐"}</span>
|
||||||
<span class="item-name">${s.name}</span>
|
<span class="item-name">${this.#displayName(s)}</span>
|
||||||
<span class="item-vis ${vis.cls}">${vis.label}</span>
|
<span class="item-vis ${vis.cls}">${vis.label}</span>
|
||||||
</a>`;
|
</a>`;
|
||||||
})
|
})
|
||||||
|
|
@ -215,7 +225,7 @@ export class RStackSpaceSwitcher extends HTMLElement {
|
||||||
return `
|
return `
|
||||||
<div class="item item--discover ${vis.cls}">
|
<div class="item item--discover ${vis.cls}">
|
||||||
<span class="item-icon">${s.icon || "🌐"}</span>
|
<span class="item-icon">${s.icon || "🌐"}</span>
|
||||||
<span class="item-name">${s.name}</span>
|
<span class="item-name">${this.#displayName(s)}</span>
|
||||||
<span class="item-vis ${vis.cls}">${vis.label}</span>
|
<span class="item-vis ${vis.cls}">${vis.label}</span>
|
||||||
${pending
|
${pending
|
||||||
? `<span class="item-badge item-badge--pending">Requested</span>`
|
? `<span class="item-badge item-badge--pending">Requested</span>`
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue