Skip to content

Commit b1a085f

Browse files
committed
moved footer and header code to ui
1 parent c4ad302 commit b1a085f

6 files changed

Lines changed: 203 additions & 53 deletions

File tree

src/footer/index.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
This file was copied from mashlib/src/global/footer.ts file. It is modified to
3+
work in solid-ui by adjusting where imported functions are found.
4+
*/
5+
import { IndexedFormula, NamedNode } from 'rdflib'
6+
import { authSession, currentUser } from '../authn/authn'
7+
import { addStyleClassToElement, getName, getPod, getPodOwner } from '../utils/headerFooterHelpers'
8+
9+
const DEFAULT_SOLID_PROJECT_URL = 'https://solidproject.org'
10+
const DEFAULT_SOLID_PROJECT_NAME = 'solidproject.org'
11+
12+
/*
13+
FooterOptions allow for customizing the link and name of the link part of the footer.
14+
*/
15+
export type FooterOptions = {
16+
solidProjectUrl?: string,
17+
solidProjectName?: string
18+
}
19+
20+
/**
21+
* Initialize footer component, the footer object returned depends on whether the user is authenticated.
22+
* @param store the data store
23+
* @returns the footer
24+
*/
25+
export async function initFooter (store: IndexedFormula) {
26+
const footer = document.getElementById('PageFooter')
27+
if (!footer) {
28+
return
29+
}
30+
const pod = getPod()
31+
const podOwner = await getPodOwner(pod, store)
32+
rebuildFooter(footer, store, pod, podOwner)()
33+
authSession.onLogin(rebuildFooter(footer, store, pod, podOwner))
34+
authSession.onLogout(rebuildFooter(footer, store, pod, podOwner))
35+
}
36+
/**
37+
* @ignore exporting this only for the unit test
38+
*/
39+
function rebuildFooter (footer: HTMLElement, store: IndexedFormula, pod: NamedNode | null, podOwner: NamedNode | null) {
40+
return async () => {
41+
const user = currentUser()
42+
footer.innerHTML = ''
43+
footer.appendChild(await createControllerInfoBlock(store, user, pod, podOwner))
44+
}
45+
}
46+
/**
47+
* @ignore exporting this only for the unit test
48+
*/
49+
function createControllerInfoBlock (store: IndexedFormula, user: NamedNode | null, pod: NamedNode | null, podOwner: NamedNode | null): HTMLElement {
50+
const profileLinkContainer = document.createElement('div')
51+
if (!pod || !podOwner || (user && user.equals(podOwner))) {
52+
return profileLinkContainer
53+
}
54+
55+
addStyleClassToElement(profileLinkContainer, ['footer-pod-info', 'footer'], 'footer')
56+
57+
const podLinkPre = document.createElement('span')
58+
podLinkPre.innerText = "You're visiting "
59+
60+
const podLink = document.createElement('a')
61+
podLink.href = pod.uri
62+
podLink.innerText = 'the Pod'
63+
64+
const profileLinkPre = document.createElement('span')
65+
profileLinkPre.innerText = ' controlled by '
66+
67+
const profileLink = document.createElement('a')
68+
profileLink.href = podOwner.uri
69+
profileLink.innerText = getName(store, podOwner)
70+
71+
const solidProjectLinkPre = document.createElement('span')
72+
solidProjectLinkPre.innerText = '. For more info, check out '
73+
74+
const solidProjectLink = document.createElement('a')
75+
solidProjectLink.href = DEFAULT_SOLID_PROJECT_URL
76+
solidProjectLink.innerText = DEFAULT_SOLID_PROJECT_NAME
77+
78+
const solidProjectLinkPost = document.createElement('span')
79+
solidProjectLinkPost.innerText = '.'
80+
81+
profileLinkContainer.appendChild(podLinkPre)
82+
profileLinkContainer.appendChild(podLink)
83+
profileLinkContainer.appendChild(profileLinkPre)
84+
profileLinkContainer.appendChild(profileLink)
85+
profileLinkContainer.appendChild(solidProjectLinkPre)
86+
profileLinkContainer.appendChild(solidProjectLink)
87+
profileLinkContainer.appendChild(solidProjectLinkPost)
88+
89+
return profileLinkContainer
90+
}

src/footer/styleMap.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export const styleMap = {
2+
'footer': {
3+
borderTop: 'solid 1px $divider-color',
4+
fontSize: '0.9em',
5+
padding: '0.5em 1.5em'
6+
}
7+
}

src/header/index.ts

Lines changed: 52 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,22 @@
44
work in solid-ui by adjusting where imported functions are found.
55
*/
66
import { IndexedFormula, NamedNode } from 'rdflib'
7-
import { icons } from '..'
7+
import { icons } from '../iconBase'
88
import { loginStatusBox, authSession, currentUser } from '../authn/authn'
99
import * as widgets from '../widgets'
1010
import { emptyProfile } from './empty-profile'
11-
import { addStyleClassToElement, getPod, throttle } from './headerHelpers'
11+
import { addStyleClassToElement, getPod, throttle } from '../utils/headerFooterHelpers'
12+
13+
/**
14+
* menu icons
15+
*/
16+
const DEFAULT_HELP_MENU_ICON = icons.iconBase + 'noun_help.svg'
17+
const DEFAUL_SOLID_ICON_URL = 'https://solidproject.org/assets/img/solid-emblem.svg'
1218

1319
export type MenuItemLink = {
1420
label: string,
15-
url: string
21+
url: string,
22+
target?: string
1623
}
1724

1825
export type MenuItemButton = {
@@ -23,69 +30,73 @@ export type MenuItemButton = {
2330
export type MenuItems = MenuItemLink | MenuItemButton
2431

2532
/*
26-
HeaderOptions allow for customizing the logo and menu list. If a logo is not provided the default
27-
is solid. Menulist will always show a link to logout and to the users profile.
33+
HeaderOptions allow for customizing the logo and menu list. If a logo is not provided the default
34+
is solid. Menulist will always show a link to logout and to the users profile.
2835
*/
2936
export type HeaderOptions = {
3037
logo?: string,
31-
menuList?: MenuItems[]
38+
helpIcon?: string,
39+
helpMenuList?: MenuItems[]
3240
}
3341

3442
/**
3543
* Initialize header component, the header object returned depends on whether the user is authenticated.
3644
* @param store the data store
37-
* @param options allow the header to be customized with a personalized logo and a menu list of links or buttons.
45+
* @param userMenuList a list of menu items when the user is logged in
46+
* @param options allow the header to be customized with a personalized logo, help icon and a help menu list of links or buttons.
3847
* @returns a header for an authenticated user with menu items given or a login screen
3948
*/
40-
export async function initHeader (store: IndexedFormula, options?: HeaderOptions) {
49+
export async function initHeader (store: IndexedFormula, userMenuList: MenuItems[], options?: HeaderOptions) {
4150
const header = document.getElementById('PageHeader')
4251
if (!header) {
4352
return
4453
}
4554

4655
const pod = getPod()
47-
rebuildHeader(header, store, pod, options)()
48-
authSession.onLogout(rebuildHeader(header, store, pod, options))
49-
authSession.onLogin(rebuildHeader(header, store, pod, options))
56+
rebuildHeader(header, store, pod, userMenuList, options)()
57+
authSession.onLogout(rebuildHeader(header, store, pod, userMenuList, options))
58+
authSession.onLogin(rebuildHeader(header, store, pod, userMenuList, options))
5059
}
5160
/**
5261
* @ignore exporting this only for the unit test
5362
*/
54-
export function rebuildHeader (header: HTMLElement, store: IndexedFormula, pod: NamedNode, options?: HeaderOptions) {
63+
export function rebuildHeader (header: HTMLElement, store: IndexedFormula, pod: NamedNode, userMenuList: MenuItems[], options?: HeaderOptions) {
5564
return async () => {
5665
const user = currentUser()
5766
header.innerHTML = ''
58-
header.appendChild(await createBanner(store, pod, user, options))
67+
header.appendChild(await createBanner(store, pod, user, userMenuList, options))
5968
}
6069
}
6170
/**
6271
* @ignore exporting this only for the unit test
6372
*/
64-
export async function createBanner (store: IndexedFormula, pod: NamedNode, user: NamedNode | null, options?: HeaderOptions): Promise<HTMLElement> {
73+
export async function createBanner (store: IndexedFormula, pod: NamedNode, user: NamedNode | null, userMenuList: MenuItems[], options?: HeaderOptions): Promise<HTMLElement> {
6574
const podLink = document.createElement('a')
6675
podLink.href = pod.uri
6776
addStyleClassToElement(podLink, ['header-banner__link'])
6877
const image = document.createElement('img')
6978
if (options) {
70-
image.src = options.logo ? options.logo : 'https://solidproject.org/assets/img/solid-emblem.svg'
79+
image.src = options.logo ? options.logo : DEFAUL_SOLID_ICON_URL
7180
}
7281
addStyleClassToElement(image, ['header-banner__icon'])
7382
podLink.appendChild(image)
7483

7584
const userMenu = user
76-
? await createUserMenu(store, user, options)
85+
? await createUserMenu(store, user, userMenuList, options)
7786
: createLoginSignUpButtons()
7887

79-
const helpMenu = createHelpMenu()
80-
8188
const banner = document.createElement('div')
8289
addStyleClassToElement(banner, ['header-banner'])
8390
banner.appendChild(podLink)
8491

8592
const leftSideOfHeader = document.createElement('div')
8693
addStyleClassToElement(leftSideOfHeader, ['header-banner__right-menu'])
8794
leftSideOfHeader.appendChild(userMenu)
88-
leftSideOfHeader.appendChild(helpMenu)
95+
96+
if (options && options.helpMenuList) {
97+
const helpMenu = createHelpMenu(options, options.helpMenuList)
98+
leftSideOfHeader.appendChild(helpMenu)
99+
}
89100

90101
banner.appendChild(leftSideOfHeader)
91102

@@ -94,20 +105,24 @@ export async function createBanner (store: IndexedFormula, pod: NamedNode, user:
94105
/**
95106
* @ignore exporting this only for the unit test
96107
*/
97-
export function createHelpMenu () {
108+
export function createHelpMenu (options: HeaderOptions, helpMenuItems: MenuItems[]) {
98109
const helpMenuList = document.createElement('ul')
99110
addStyleClassToElement(helpMenuList, ['header-user-menu__list'])
100-
helpMenuList.appendChild(createUserMenuItem(createUserMenuLink('User guide', 'https://github.com/solid/userguide', '_blank')))
101-
helpMenuList.appendChild(createUserMenuItem(createUserMenuLink('Report a problem', 'https://github.com/solid/solidos/issues', '_blank')))
111+
helpMenuItems.forEach(function (menuItem) {
112+
const menuItemType: string = (menuItem as MenuItemLink).url ? 'url' : 'onclick'
113+
if (menuItemType === 'url') {
114+
helpMenuList.appendChild(createUserMenuItem(createUserMenuLink(menuItem.label, (menuItem as MenuItemLink).url, (menuItem as MenuItemLink).target)))
115+
} else {
116+
helpMenuList.appendChild(createUserMenuItem(createUserMenuButton(menuItem.label, (menuItem as MenuItemButton).onclick)))
117+
}
118+
})
102119

103120
const helpMenu = document.createElement('nav')
104121

105122
addStyleClassToElement(helpMenu, ['header-user-menu__navigation-menu'])
106123
helpMenu.setAttribute('aria-hidden', 'true')
107124
helpMenu.appendChild(helpMenuList)
108125

109-
const helpIcon = icons.iconBase + 'noun_144.svg'
110-
111126
const helpMenuContainer = document.createElement('div')
112127
addStyleClassToElement(helpMenuContainer, ['header-banner__user-menu'])
113128
addStyleClassToElement(helpMenuContainer, ['header-user-menu'])
@@ -118,7 +133,7 @@ export function createHelpMenu () {
118133
helpMenuTrigger.type = 'button'
119134

120135
const helpMenuIcon = document.createElement('img')
121-
helpMenuIcon.src = helpIcon
136+
helpMenuIcon.src = (options && options.helpIcon) ? options.helpIcon : icons.iconBase + DEFAULT_HELP_MENU_ICON
122137
addStyleClassToElement(helpMenuIcon, ['header-banner__help-icon'])
123138
helpMenuContainer.appendChild(helpMenuTrigger)
124139
helpMenuTrigger.appendChild(helpMenuIcon)
@@ -158,7 +173,7 @@ export function createUserMenuButton (label: string, onClick: EventListenerOrEve
158173
/**
159174
* @ignore exporting this only for the unit test
160175
*/
161-
export function createUserMenuLink (label: string, href: string, target?:string): HTMLElement {
176+
export function createUserMenuLink (label: string, href: string, target?: string): HTMLElement {
162177
const link = document.createElement('a')
163178
addStyleClassToElement(link, ['header-user-menu__link'])
164179
link.href = href
@@ -170,30 +185,25 @@ export function createUserMenuLink (label: string, href: string, target?:string)
170185
/**
171186
* @ignore exporting this only for the unit test
172187
*/
173-
export async function createUserMenu (store: IndexedFormula, user: NamedNode, options?: HeaderOptions): Promise<HTMLElement> {
188+
export async function createUserMenu (store: IndexedFormula, user: NamedNode, userMenuList: MenuItems[], options?: HeaderOptions): Promise<HTMLElement> {
174189
const fetcher = (<any>store).fetcher
175190
if (fetcher) {
176191
// Making sure that Profile is loaded before building menu
177192
await fetcher.load(user)
178193
}
194+
179195
const loggedInMenuList = document.createElement('ul')
180196
addStyleClassToElement(loggedInMenuList, ['header-user-menu__list'])
181-
loggedInMenuList.appendChild(createUserMenuItem(createUserMenuLink('Show your profile', user.uri)))
182-
if (options) {
183-
if (options.menuList) {
184-
options.menuList.forEach(function (menuItem) {
185-
const menuItemType: string = (menuItem as MenuItemLink).url ? 'url' : 'onclick'
186-
if (menuItemType === 'url') {
187-
loggedInMenuList.appendChild(createUserMenuItem(createUserMenuLink(menuItem.label, menuItem[menuItemType])))
188-
} else {
189-
loggedInMenuList.appendChild(createUserMenuItem(createUserMenuButton(menuItem.label, menuItem[menuItemType])))
190-
}
191-
})
192-
}
197+
if (userMenuList) {
198+
userMenuList.forEach(function (menuItem) {
199+
const menuItemType: string = (menuItem as MenuItemLink).url ? 'url' : 'onclick'
200+
if (menuItemType === 'url') {
201+
loggedInMenuList.appendChild(createUserMenuItem(createUserMenuLink(menuItem.label, (menuItem as MenuItemLink).url, (menuItem as MenuItemLink).target)))
202+
} else {
203+
loggedInMenuList.appendChild(createUserMenuItem(createUserMenuButton(menuItem.label, (menuItem as MenuItemButton).onclick)))
204+
}
205+
})
193206
}
194-
195-
loggedInMenuList.appendChild(createUserMenuItem(createUserMenuButton('Log out', () => authSession.logout())))
196-
197207
const loggedInMenu = document.createElement('nav')
198208

199209
addStyleClassToElement(loggedInMenu, ['header-user-menu__navigation-menu'])

src/header/styleMap.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,6 @@ export const styleMap = {
114114
width: '65px !important' // may just be 65px round($icon-size * 352 / 322);
115115
},
116116
'header-banner__help-icon': {
117-
backgroundSize: '55px 50px',
118-
height: '50px !important', // this is the icon size
119-
width: '55px !important' // may just be 65px round($icon-size * 352 / 430);
117+
width: '28px !important'
120118
}
121119
}

src/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ import * as utils from './utils'
7373
import * as widgets from './widgets/index'
7474
import versionInfo from './versionInfo'
7575
import { initHeader } from './header'
76+
import { initFooter } from './footer'
7677

7778
const dom = window ? window.document : null // Idea that UI.dom can be adapted in non-browser environments
7879
const store = solidLogicSingleton.store
@@ -104,7 +105,8 @@ if (typeof window !== 'undefined') {
104105
utils,
105106
widgets,
106107
versionInfo,
107-
initHeader
108+
initHeader,
109+
initFooter
108110
} // Simpler access by non-node scripts
109111
}
110112

@@ -134,5 +136,6 @@ export {
134136
utils,
135137
widgets,
136138
versionInfo,
137-
initHeader
139+
initHeader,
140+
initFooter
138141
}

0 commit comments

Comments
 (0)