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
256 changes: 182 additions & 74 deletions package-lock.json

Large diffs are not rendered by default.

14 changes: 4 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "solid-panes",
"version": "4.4.2-4",
"version": "4.4.2-5",
"description": "Solid-compatible Panes: applets and views for the mashlib and databrowser",
"main": "dist/index.cjs.js",
"types": "dist/index.d.ts",
Expand Down Expand Up @@ -58,7 +58,7 @@
"dependencies": {
"@solid/better-simple-slideshow": "^0.1.0",
"activitystreams-pane": "1.0.3-4",
"chat-pane": "3.0.4-2",
"chat-pane": "3.0.4-3",
"contacts-pane": "3.2.1-5",
"dompurify": "^3.4.4",
"folder-pane": "3.1.1-2",
Expand All @@ -68,18 +68,12 @@
"meeting-pane": "3.0.3-1",
"mime-types": "^3.0.2",
"pane-registry": "3.1.2-2",
"profile-pane": "3.2.3-3",
"profile-pane": "3.2.3-4",
"rdflib": "2.4.0",
"solid-logic": "4.0.8-1",
"solid-namespace": "^0.5.4",
"solid-ui": "3.1.3-13",
"source-pane": "3.1.1-4"
},
"overrides": {
"rdflib": "$rdflib",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because rdflib is not in my solidos setup I didn't have to remove this one. Thought I'd just mention.

"solid-logic": "$solid-logic",
"solid-ui": "$solid-ui",
"pane-registry": "$pane-registry"
"source-pane": "3.1.1-5"
},
"devDependencies": {
"@iconify/json": "2.2.494",
Expand Down
26 changes: 26 additions & 0 deletions src/components/navbar/Navbar.styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
:host {
.navbar {
border: 1px solid var(--color-border,#444);
border-radius: 5px;
display: flex;
background: var(--solid-ui-color-white, #fff);
overflow: hidden;
}

button {
padding: 10px 25px;
border-bottom: 2px solid transparent;
font-weight: 500;
font-size: 90%;
}

button:active,
button:hover,
button:focus {
border-bottom-color: var(--solid-ui-color-primary, #7C4DFF);
border-radius: 0;
color: var(--solid-ui-color-primary, #7C4DFF);
text-transform: capitalize;
background: color-mix(in srgb, currentColor 25%, transparent);
}
}
44 changes: 44 additions & 0 deletions src/components/navbar/Navbar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { customElement, WebComponent } from 'solid-ui'
import { html, TemplateResult } from 'lit'
import { property } from 'lit/decorators.js'

import 'solid-ui/components/account'
import 'solid-ui/components/button'
import 'solid-ui/components/menu-item'
import 'solid-ui/components/menu'
import 'solid-ui/components/solid-emblem'
import '~icons/lucide/help-circle'

import styles from './Navbar.styles.css'

export interface NavbarMenuItem {
label: string | TemplateResult
href?: string
selected?: boolean
onSelected?(): void
}

@customElement('solid-panes-navbar')
export default class Navbar extends WebComponent {
static styles = styles

@property({ type: Array })
accessor navbarItems: NavbarMenuItem[] = []

render () {
return html`
<nav class="navbar">
${this.navbarItems.map(menuItem => html`
<button

@SharonStrats SharonStrats Jul 29, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a feeling you are planning to use them later, but just in case you have not used
solid-ui-button or the menu etc..

Also, just a note that I'm going to need to add configuration to the menu-item so that the size of the icon and font can be changed in order to use it for the 3 dot menu. Perhaps you'll need this too.

type='button'
@click=${() => menuItem.onSelected?.()}
title="${menuItem.label}"
text="${menuItem.label}"
aria-expanded="false">
${menuItem.label}
Comment on lines +32 to +38
</button>
`)}
</nav>
`
}
}
4 changes: 4 additions & 0 deletions src/components/navbar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import Navbar from './Navbar'

export { Navbar }
export default Navbar
4 changes: 3 additions & 1 deletion src/mainPage/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { LiveStore, NamedNode } from 'rdflib'
import type { RenderEnvironment } from 'pane-registry'
import { getOutliner, OutlineManager } from '../index'
import { createHeader } from './header'
import { createNavbar } from './navbar'

// Symbol used to stash the last render-relevant env snapshot on the outliner
// so refreshUI can skip a full GotoSubject re-render when nothing changed.
Expand Down Expand Up @@ -43,7 +44,8 @@ export async function initMainPage (
outliner.GotoSubject(subject, true, undefined, true, undefined)

const header = await createHeader(outliner)
return Promise.all([header])
const navbar = await createNavbar(outliner)
return Promise.all([header, navbar])
}

export async function refreshUI (outliner: OutlineManager) {
Expand Down
83 changes: 83 additions & 0 deletions src/mainPage/navbar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { store } from 'solid-logic'
import { html, render } from 'lit-html'
import type { OutlineManager } from '../outline/manager'
import { getPodStorages } from '../outline/podUtils'
import { createFolderPaneItem } from '../outline/folderPaneUtils'

import '~icons/lucide/user'
import '~icons/lucide/users'
import '~icons/lucide/folder-open'
import '../components/navbar'
import { NamedNode } from 'rdflib'
import { NavbarMenuItem } from 'src/components/navbar/Navbar'

function createNavItem (label: string, onSelected: () => void): NavbarMenuItem {
return { label, onSelected }
}

async function createNavbarMenuItems (
outliner: OutlineManager,
subject: NamedNode,
OutlineView: HTMLElement | null
): Promise<NavbarMenuItem[]> {
const baseItems: NavbarMenuItem[] = [
createNavItem('Profile', () => {
const profilePane = outliner?.context?.session?.paneRegistry?.byName('profile')
outliner.GotoSubject(subject, true, profilePane, true, undefined, OutlineView)
}),
createNavItem('Friends', () => {
const socialPane = outliner?.context?.session?.paneRegistry?.byName('social')
outliner.GotoSubject(subject, true, socialPane, true, undefined, OutlineView)
})
]

const podStorages = await getPodStorages(subject.uri)
if (podStorages.length === 0) return baseItems

return [
...baseItems,
createNavItem('Storage', () => {
const folderPanes = podStorages.map((pod, index) => createFolderPaneItem(outliner?.context?.session?.paneRegistry?.byName('folder'), pod, index))
if (folderPanes.length === 0) {
console.warn('Folder pane is not registered')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe you should also add this message for profile and social.

return
}
// TODO make storage work for more storage spaces, not just the first one
outliner.GotoSubject(subject, true, folderPanes[0], true, undefined, OutlineView)
})
Comment on lines +39 to +47
]
}

export async function createNavbar (outliner: OutlineManager) {
const existingNavbar = document.querySelector('solid-panes-navbar')

if (existingNavbar) {
return existingNavbar
}

const OutlineView = document.getElementById('OutlineView')
const mainContent = document.getElementById('MainContent')
const tmpContainer = document.createElement('div')
const uri = window.location.href
const subject: NamedNode = typeof uri === 'string' ? store.sym(uri) : uri
const menuItems = await createNavbarMenuItems(outliner, subject, OutlineView)

render(
html`<solid-panes-navbar .navbarItems=${menuItems}></solid-panes-navbar>`,
tmpContainer
)

const navbar = tmpContainer.firstElementChild as HTMLElement | null

if (!navbar) {
throw new Error('Failed to create nav bar')
}

if (mainContent) {
mainContent.insertBefore(navbar, mainContent.firstChild)
} else {
document.body.prepend(navbar)
}

return navbar
}
39 changes: 39 additions & 0 deletions src/outline/folderPaneUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as paneRegistry from 'pane-registry'
import { getPodStorages } from './podUtils'
import folderIcon from '../icons/folder.svg'
import { NamedNode } from 'rdflib'

const FOLDER_ICON = folderIcon

// returns the folder pane of the pods of the given subject (or the current URL subject if none is provided), if any. If not, returns an empty array.
export async function getFolderPaneforStorage (subject?: NamedNode) {
const uri = (new URL(window.location.href)).searchParams.get('uri')
const podUrl = subject?.uri || uri || window.location.href
const podStorages = await getPodStorages(podUrl)
if (!podStorages.length) return []

const folderPane = paneRegistry.byName('folder')
if (!folderPane) return []

return podStorages.map((pod, index) => createFolderPaneItem(folderPane, pod, index))
}

export function createFolderPaneItem (folderPane, pod: NamedNode, index: number) {
return {
name: folderPane.name,
paneName: 'folder',
tabName: `folder-${index}`,
label: function () {
return 'Storage'
},
render: function (_subject, context, options) {
return folderPane.render(pod, context, options)
},
shouldGetFocus: function () {
return false
},
requireQueryButton: !!folderPane.requireQueryButton,
subject: pod,
icon: FOLDER_ICON
}
}
7 changes: 4 additions & 3 deletions src/outline/manager.css
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,12 @@
}

.tdFlex {
margin: var(--spacing-xxxs, 0.2rem);
border: none;
padding: 0;
border: 1px solid var(--color-border, #444);
border-radius: 5px;
vertical-align: top;
display: flex;
justify-content: space-between;
flex-direction: row;
background: var(--solid-ui-color-white, #fff);
overflow: hidden;
}
Loading
Loading