Skip to content

Commit b8d2e96

Browse files
committed
close to final design and calling components
1 parent eeb27e6 commit b8d2e96

8 files changed

Lines changed: 167 additions & 63 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,5 @@ We have added comments in the code to make it explicit which parts are 100% writ
8888
* Model Claude Opus 4.6: Initially solid-panes is loaded into a HTML shell form mashlib that looks like ... Also, an iFrame is rendered inside the <div class="TabulatorOutline" id="DummyUUID"> for “isolated pane rendering”. Analize the solid-panes code for what it uses from this HTML and suggest a new HTML structure which is mobile and accessibility friendly. Lets go ahead and make chnages in this code as suggested to accommodate the new databwroser HTML.
8989

9090
* Raptor mini: take a look how I wired the environement from mashlib into solid-panes. It is not quite right, can you suggest fixes?
91+
92+
* Raptor mini: Update the code to use the new solid-ui-header component. Keep in mind the log in and sign up are wired in specific ways.

src/home/homePane.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
**
99
*/
1010

11-
import { PaneDefinition } from 'pane-registry'
11+
import { DataBrowserContext, PaneDefinition } from 'pane-registry'
1212
import { NamedNode } from 'rdflib'
1313
import { authn } from 'solid-logic'
1414
import { create, icons, login } from 'solid-ui'
@@ -29,7 +29,7 @@ const HomePaneSource: PaneDefinition = {
2929
return 'home'
3030
},
3131

32-
render: function (subject, context) {
32+
render: function (subject, context: DataBrowserContext) {
3333
const dom = context.dom
3434
const showContent = async function () {
3535
const homePaneContext = { div, dom, statusArea: div, me }
@@ -54,6 +54,7 @@ const HomePaneSource: PaneDefinition = {
5454
const relevantPanes = await login.filterAvailablePanes(
5555
context.session.paneRegistry.list
5656
)
57+
// @ts-ignore
5758
create.newThingUI(creationContext, context, relevantPanes) // newUI Have to pass panes down
5859

5960
login.registrationList(homePaneContext, {}).then(function () {})

src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
RenderEnvironment
2626
} from 'pane-registry'
2727
import { createContext } from './outline/context'
28-
import { initMainPage, updateMenuLayout } from './mainPage'
28+
import { initMainPage, refreshUI } from './mainPage'
2929

3030
function getOutliner (dom, environment?: RenderEnvironment): OutlineManager {
3131
if (!dom.outlineManager) {
@@ -68,7 +68,7 @@ export {
6868
UI,
6969
versionInfo,
7070
initMainPage,
71-
updateMenuLayout,
71+
refreshUI,
7272
list, // from paneRegistry
7373
paneForIcon, // from paneRegistry
7474
paneForPredicate, // from paneRegistry

src/mainPage/footer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const SOLID_PROJECT_URL = 'https://solidproject.org'
88
const SOLID_PROJECT_NAME = 'solidproject.org'
99

1010
export function createFooter (store: LiveStore) {
11+
// @ts-ignore
1112
initFooter(store, setFooterOptions())
1213
}
1314

src/mainPage/header.ts

Lines changed: 145 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,176 @@
1-
import { authSession, authn } from 'solid-logic'
2-
import { icons, initHeader } from 'solid-ui'
1+
import { authSession, authn, store } from 'solid-logic'
2+
import { icons, widgets, utils } from 'solid-ui'
3+
import { Header } from 'solid-ui/dist/components/header/index'
4+
import type {
5+
HeaderAccountMenuItem,
6+
HeaderAuthState,
7+
HeaderMenuItem
8+
} from 'solid-ui/dist/components/header/index'
39
import { OutlineManager } from '../outline/manager'
410
import { LiveStore } from 'rdflib'
511
/**
612
* menu icons
713
*/
814
const HELP_MENU_ICON = icons.iconBase + 'noun_help.svg'
915
const SOLID_ICON_URL = 'https://solidproject.org/assets/img/solid-emblem.svg'
10-
1116
/**
1217
* menu elements
1318
*/
14-
const USER_GUIDE_MENU_ITEM = 'User guide'
15-
const REPORT_A_PROBLEM_MENU_ITEM = 'Report a problem'
16-
const SHOW_YOUR_PROFILE_MENU_ITEM = 'Show your profile'
17-
const LOG_OUT_MENU_ITEM = 'Log out'
19+
const SIGN_IN_MENU_ITEM = 'Log In'
20+
const SIGN_OUT_MENU_ITEM = 'Log Out'
21+
const SIGN_UP_MENU_ITEM = 'Sign Up'
22+
const ACCOUNT_MENU_LABEL = '▼'
23+
const SIGN_UP__MENU_LINK = 'https://solidproject.org/get_a_pod'
1824

19-
type UserMenuItem = { label: string; onclick: () => void }
20-
/**
21-
* URLS
22-
*/
23-
const USER_GUIDE_MENU_URL = 'https://solidos.github.io/userguide/'
24-
const REPORT_A_PROBLEM_MENU_URL = 'https://github.com/solidos/solidos/issues'
25+
// data structure extracted for solid-ui-header binding
26+
export const DEFAULT_HELP_MENU_LIST = [
27+
{ label: 'User guide', url: 'https://solidos.github.io/userguide/', target: '_blank' },
28+
{ label: 'Report a problem', url: 'https://github.com/solidos/solidos/issues', target: '_blank' }
29+
]
30+
31+
const HEADER_MOBILE_STYLE_ID = 'solid-ui-header-mobile-style'
32+
33+
function ensureMobileHeaderStyles () {
34+
if (document.getElementById(HEADER_MOBILE_STYLE_ID)) return
35+
const style = document.createElement('style')
36+
style.id = HEADER_MOBILE_STYLE_ID
37+
style.textContent = `
38+
solid-ui-header[layout="mobile"]::part(logo) {
39+
display: none !important;
40+
}
41+
`
42+
document.head.appendChild(style)
43+
}
2544

2645
export async function createHeader (store: LiveStore, outliner: OutlineManager) {
27-
initHeader(store, await setUserMenu(outliner), setHeaderOptions())
46+
ensureMobileHeaderStyles()
47+
48+
const header = (document.querySelector('solid-ui-header') || document.createElement('solid-ui-header')) as Header
49+
const isNewHeader = !header.isConnected
50+
51+
// ensure it is in DOM (before MainContent for consistency)
52+
const main = document.getElementById('MainContent')
53+
if (!header.isConnected) {
54+
if (main && main.parentNode) {
55+
main.parentNode.insertBefore(header, main)
56+
} else {
57+
document.body.prepend(header)
58+
}
59+
}
60+
61+
await refreshHeader(outliner, header)
62+
63+
if (isNewHeader) {
64+
const refreshCurrentHeader = async () => {
65+
await refreshHeader(outliner, header)
66+
}
67+
68+
authSession.events.on('login', refreshCurrentHeader)
69+
authSession.events.on('logout', refreshCurrentHeader)
70+
authSession.events.on('sessionRestore', refreshCurrentHeader)
71+
72+
header.addEventListener('login-success', async () => {
73+
await refreshCurrentHeader()
74+
})
75+
76+
header.addEventListener('signup-success', async () => {
77+
await refreshCurrentHeader()
78+
})
79+
80+
header.addEventListener('account-menu-select', (e: Event) => {
81+
const detail = (e as CustomEvent).detail
82+
if (detail?.action === 'logout') {
83+
authSession.logout()
84+
} else if (detail?.action === 'show-profile') {
85+
openUserProfile(outliner)
86+
}
87+
})
88+
}
89+
90+
return header
2891
}
2992

30-
function setHeaderOptions () {
31-
const helpMenuList = [
32-
{ label: USER_GUIDE_MENU_ITEM, url: USER_GUIDE_MENU_URL, target: '_blank' },
33-
{ label: REPORT_A_PROBLEM_MENU_ITEM, url: REPORT_A_PROBLEM_MENU_URL, target: '_blank' }
34-
]
35-
const headerOptions = { logo: SOLID_ICON_URL, helpIcon: HELP_MENU_ICON, helpMenuList }
93+
export async function refreshHeader (outliner: OutlineManager, headerElement?: Header) {
94+
ensureMobileHeaderStyles()
95+
const headerOptions = setHeaderOptions(outliner)
96+
const header = headerElement || document.querySelector('solid-ui-header') as Header | null
97+
if (!header) return null
98+
99+
header.theme = headerOptions.theme
100+
header.layout = headerOptions.layout
101+
header.brandLink = headerOptions.brandLink
102+
header.logo = headerOptions.layout === 'desktop' ? headerOptions.logo : ''
103+
header.helpIcon = headerOptions.helpIcon
104+
header.helpMenuList = headerOptions.helpMenuList
105+
header.authState = headerOptions.authState
106+
header.loginAction = headerOptions.loginAction
107+
header.signUpAction = headerOptions.signUpAction
108+
header.accountMenu = await setUserMenu()
109+
header.logoutLabel = headerOptions.logoutLabel
110+
header.accountLabel = headerOptions.accountLabel
111+
header.accountAvatar = headerOptions.accountAvatar
112+
113+
return header
114+
}
115+
116+
function setHeaderOptions (outliner: OutlineManager) {
117+
const currentUser = authn.currentUser()
118+
const isAuthenticated = !!currentUser
119+
const authState: HeaderAuthState = isAuthenticated ? 'logged-in' : 'logged-out'
120+
const layout: Header['layout'] = outliner.context?.environment?.layout === 'mobile' ? 'mobile' : 'desktop'
121+
const theme: Header['theme'] = outliner.context?.environment?.theme === 'dark' ? 'dark' : 'light'
122+
123+
const headerOptions = {
124+
logo: SOLID_ICON_URL,
125+
helpIcon: HELP_MENU_ICON,
126+
helpMenuList: DEFAULT_HELP_MENU_LIST,
127+
layout,
128+
theme,
129+
brandLink: '/',
130+
authState,
131+
loginAction: {
132+
label: SIGN_IN_MENU_ITEM
133+
} as HeaderMenuItem,
134+
signUpAction: {
135+
label: SIGN_UP_MENU_ITEM,
136+
url: SIGN_UP__MENU_LINK,
137+
} as HeaderMenuItem,
138+
logoutLabel: SIGN_OUT_MENU_ITEM,
139+
accountLabel: isAuthenticated ? ACCOUNT_MENU_LABEL : '',
140+
accountAvatar: isAuthenticated ? widgets.findImage(currentUser) : undefined
141+
}
36142

37143
return headerOptions
38144
}
39145

40-
async function setUserMenu (outliner: OutlineManager) {
41-
// @ts-ignore: showProfile is used conditionally
42-
const showProfile = {
43-
label: SHOW_YOUR_PROFILE_MENU_ITEM,
44-
onclick: () => openUserProfile(outliner)
146+
async function setUserMenu () {
147+
const me = authn.currentUser()
148+
if (!me) {
149+
return []
45150
}
46151

47-
const logOut: UserMenuItem = {
48-
label: LOG_OUT_MENU_ITEM,
49-
onclick: () => {
50-
authSession.logout()
51-
}
152+
try {
153+
await store.fetcher.load(me.doc())
154+
} catch (err) {
155+
console.error('Unable to load user profile', err)
52156
}
53157

54-
// the order of the menu is important here, show profile first and logout last
55-
let userMenuList:UserMenuItem[] = [] // was [showProfile]
56-
userMenuList = userMenuList.concat(await getMenuItems(outliner))
57-
userMenuList.push(logOut)
158+
const accountMenu: HeaderAccountMenuItem[] = [
159+
{
160+
label: utils.label(me),
161+
avatar: widgets.findImage(me),
162+
webid: me.value,
163+
action: 'show-profile',
164+
pane: 'profile-pane'
165+
},
166+
// TODO add all my available accounts
167+
]
58168

59-
return userMenuList
169+
return accountMenu
60170
}
61171

62172
// Does not work to jump to user profile,
63173
function openUserProfile (outliner: OutlineManager) {
64174
outliner.GotoSubject(authn.currentUser(), true, undefined, true, undefined)
65175
location.reload()
66176
}
67-
68-
async function getMenuItems (outliner: OutlineManager) {
69-
const items = await outliner.getDashboardItems()
70-
return items.map((element) => {
71-
return {
72-
label: element.label,
73-
onclick: () => openDashboardPane(outliner, element.tabName || element.paneName)
74-
}
75-
})
76-
}
77-
78-
async function openDashboardPane (outliner: OutlineManager, pane: string): Promise<void> {
79-
outliner.showDashboard({
80-
pane
81-
})
82-
}

src/mainPage/index.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
*/
55

66
import { LiveStore, NamedNode } from 'rdflib'
7-
import { getOutliner } from '../index'
8-
import { createHeader } from './header'
7+
import { getOutliner, OutlineManager } from '../index'
8+
import { createHeader, refreshHeader } from './header'
99
import { createFooter } from './footer'
10-
import { initResponsiveMenu } from './menu'
10+
import { createLeftSideMenu, refreshMenu } from './menu'
1111

12-
export { updateMenuLayout } from './menu'
12+
export { refreshMenu as updateMenuLayout } from './menu'
13+
export { refreshHeader } from './header'
1314

1415
function ensureMainContent () {
1516
let main = document.getElementById('MainContent') as HTMLElement | null
@@ -37,7 +38,12 @@ export async function initMainPage (
3738
outliner.GotoSubject(subject, true, undefined, true, undefined)
3839

3940
const header = await createHeader(store, outliner)
40-
const menu = initResponsiveMenu(outliner)
41+
const menu = createLeftSideMenu(outliner)
4142
const footer = createFooter(store)
4243
return Promise.all([header, menu, footer])
4344
}
45+
46+
export async function refreshUI (outliner: OutlineManager) {
47+
await refreshHeader(outliner)
48+
refreshMenu(outliner.context.environment?.layout === 'mobile' ? 'mobile' : 'desktop')
49+
}

src/mainPage/menu.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
.menu-toggle {
2323
position: fixed;
24-
top: 1.75rem;
24+
top: 0.5rem;
2525
left: 2.75rem;
2626
z-index: 300;
2727
display: inline-flex;

src/mainPage/menu.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export const buildMenuHtml = (isAuthenticated: boolean) => {
6262
`
6363
}
6464

65-
export const updateMenuLayout = (layout: 'mobile' | 'desktop') => {
65+
export const refreshMenu = (layout: 'mobile' | 'desktop') => {
6666
const navMenu = document.getElementById('NavMenu') as HTMLElement | null
6767
const toggle = document.getElementById('MenuToggleBtn') as HTMLButtonElement | null
6868
const overlay = document.getElementById('MenuOverlay') as HTMLElement | null
@@ -85,7 +85,7 @@ export const updateMenuLayout = (layout: 'mobile' | 'desktop') => {
8585
}
8686
}
8787

88-
export const initResponsiveMenu = (outliner: OutlineManager) => {
88+
export const createLeftSideMenu = (outliner: OutlineManager) => {
8989
ensureMenuSkeleton()
9090
const navMenu = document.getElementById('NavMenu') as HTMLElement | null
9191
const menuToggle = document.getElementById('MenuToggleBtn') as HTMLElement | null

0 commit comments

Comments
 (0)