diff --git a/README.md b/README.md index 2152b8bab..42276d2f3 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,8 @@ You can also use [import maps](https://developer.mozilla.org/en-US/docs/Web/HTML ``` +PS: If you're using the `` component to wrap your application, make sure to register it first. Otherwise, you may face errors or unexpected behaviour regarding global state. + ## Development When developing a component in solid-ui you can test it in isolation using Storybook: diff --git a/package-lock.json b/package-lock.json index 13d678b23..050ba8261 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "solid-ui", - "version": "3.1.3-11", + "version": "3.1.3-12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "solid-ui", - "version": "3.1.3-11", + "version": "3.1.3-12", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index b1c1c5518..d8d2f5340 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solid-ui", - "version": "3.1.3-11", + "version": "3.1.3-12", "description": "UI library for Solid applications", "main": "dist/index.cjs.js", "types": "dist/index.d.ts", diff --git a/src/components/account/Account.ts b/src/components/account/Account.ts index 4e6c82611..3b470957b 100644 --- a/src/components/account/Account.ts +++ b/src/components/account/Account.ts @@ -22,6 +22,7 @@ import styles from './Account.styles.css' export interface AccountMenuItem { label: string | TemplateResult href?: string + selected?: boolean onSelected?(): void } @@ -78,14 +79,14 @@ export default class Account extends WebComponent { } return html` - + ${this.menuItems.map(menuItem => html` - menuItem.onSelected?.()}> + menuItem.onSelected?.()}> ${menuItem.label} `)} diff --git a/src/components/all.ts b/src/components/all.ts new file mode 100644 index 000000000..1ad523c61 --- /dev/null +++ b/src/components/all.ts @@ -0,0 +1 @@ +import.meta.glob(['./*/index.ts', '!./provider/index.ts'], { eager: true }) diff --git a/src/components/avatar/Avatar.ts b/src/components/avatar/Avatar.ts index 317d44000..196d0fb87 100644 --- a/src/components/avatar/Avatar.ts +++ b/src/components/avatar/Avatar.ts @@ -17,6 +17,25 @@ export default class Avatar extends WebComponent { @consume({ context: authContext, subscribe: true }) private accessor auth: AuthContext = DEFAULT_AUTH_CONTEXT + private unsubscribeSessionUpdated?: () => void + + connectedCallback () { + super.connectedCallback() + + this.auth.loadProfile?.() + + this.unsubscribeSessionUpdated = this.auth.onSessionUpdated(() => { + this.auth.loadProfile?.() + this.requestUpdate() + }) + } + + disconnectedCallback () { + super.disconnectedCallback() + + this.unsubscribeSessionUpdated?.() + } + protected render () { if (!this.auth.account?.avatarUrl) { return html` diff --git a/src/components/button/Button.styles.css b/src/components/button/Button.styles.css index 38ce4648a..051f833ad 100644 --- a/src/components/button/Button.styles.css +++ b/src/components/button/Button.styles.css @@ -98,5 +98,5 @@ --background-color: transparent; --hover-text-color: currentColor; --hover-border-color: transparent; - --hover-background-color: color-mix(in srgb, currentColor 25%, transparent); + --hover-background-color: var(--solid-ui-color-primary-hover); } diff --git a/src/components/combobox/Combobox.styles.css b/src/components/combobox/Combobox.styles.css index 098120e59..02cdc3bd5 100644 --- a/src/components/combobox/Combobox.styles.css +++ b/src/components/combobox/Combobox.styles.css @@ -21,6 +21,7 @@ border-radius: 5px; box-shadow: 0px 4px 16px 0px rgba(0, 0, 0, 0.35); overflow: hidden; + padding: 4px; } .input-wrapper { @@ -50,21 +51,26 @@ .listbox { display: flex; flex-direction: column; + gap: 2px; max-height: inherit; overflow: auto; [role="option"], .non-selectable-option { color: var(--solid-ui-color-gray-700); padding: 12px 8px; + border-radius: 5px; } [role="option"] { - border-bottom: 1px solid var(--solid-ui-color-gray-100); cursor: pointer; &:hover, &[data-active] { - background: rgba(0, 0, 0, 0.05); + background: var(--solid-ui-color-primary-hover); + } + + &[aria-selected="true"] { + background: var(--solid-ui-color-primary-selected); } } diff --git a/src/components/combobox/Combobox.ts b/src/components/combobox/Combobox.ts index ac9d0a880..7c84a5fee 100644 --- a/src/components/combobox/Combobox.ts +++ b/src/components/combobox/Combobox.ts @@ -199,9 +199,7 @@ export default class Combobox extends WebComponent { ? html`
this.setActiveIndex(index)} > diff --git a/src/components/guard/Guard.stories.ts b/src/components/guard/Guard.stories.ts index e09d3e9b0..3ee42c2a8 100644 --- a/src/components/guard/Guard.stories.ts +++ b/src/components/guard/Guard.stories.ts @@ -15,11 +15,11 @@ const meta = { } as const const render = defineAuthStoryRender(() => html` - + Initializing content Guest content Logged in content - + `) export const Primary = { render } diff --git a/src/components/guard/Guard.ts b/src/components/guard/Guard.ts index 416dcb8e7..92d961b88 100644 --- a/src/components/guard/Guard.ts +++ b/src/components/guard/Guard.ts @@ -3,8 +3,13 @@ import { consume } from '@lit/context' import { html } from 'lit' import { authContext, AuthContext, DEFAULT_AUTH_CONTEXT } from '@/lib/auth' -@customElement('solid-guard') +@customElement('solid-ui-guard') export default class Guard extends WebComponent { + static states = { + initializing: (component: Guard) => !component.auth.initialized, + loggedIn: (component: Guard) => !!component.auth.account, + } + @consume({ context: authContext, subscribe: true }) private accessor auth: AuthContext = DEFAULT_AUTH_CONTEXT diff --git a/src/components/index.ts b/src/components/index.ts index 0d70de55f..916e49f9f 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1 +1,4 @@ -import.meta.glob('./*/index.ts', { eager: true }) +// Make sure that is registered first, so that contexts +// are available to nested components when they mount. +import './provider' +import './all' diff --git a/src/components/menu-item/MenuItem.styles.css b/src/components/menu-item/MenuItem.styles.css index d4b256023..594b8b28e 100644 --- a/src/components/menu-item/MenuItem.styles.css +++ b/src/components/menu-item/MenuItem.styles.css @@ -12,10 +12,10 @@ color: var(--solid-ui-color-gray-600); font-weight: 500; font-size: var(--solid-ui-font-size-md); - } - &:hover { - background-color: var(--solid-ui-color-slate-200); + &[data-selected] { + background: var(--solid-ui-color-primary-selected); + } } ::slotted([slot="left-icon"]), ::slotted([slot="right-icon"]) { diff --git a/src/components/menu-item/MenuItem.ts b/src/components/menu-item/MenuItem.ts index 824c14298..bd5d42a8a 100644 --- a/src/components/menu-item/MenuItem.ts +++ b/src/components/menu-item/MenuItem.ts @@ -11,13 +11,16 @@ export default class MenuItem extends WebComponent { @property({ type: String, reflect: true }) accessor href: string | undefined + @property({ type: Boolean, reflect: true }) + accessor selected: boolean = false + @query('a') private accessor anchor: HTMLAnchorElement | null = null render () { if (this.href) { return html` - + @@ -26,7 +29,7 @@ export default class MenuItem extends WebComponent { } return html` -
+
diff --git a/src/components/menu/Menu.stories.ts b/src/components/menu/Menu.stories.ts index 04a136da1..2f5ebee39 100644 --- a/src/components/menu/Menu.stories.ts +++ b/src/components/menu/Menu.stories.ts @@ -1,5 +1,7 @@ import { html } from 'lit' import { defineStoryRender } from '@/storybook' +import type Menu from '@/components/menu/Menu' +import type MenuItem from '@/components/menu-item/MenuItem' import '@/components/button' import '@/components/menu-item' @@ -10,14 +12,28 @@ const meta = { title: 'Menu', } as const +function select (event: Event, message: string) { + const selectedItem = event.target as MenuItem + const menu = selectedItem.parentElement as Menu + const items = menu.querySelectorAll('solid-ui-menu-item') + + for (const item of items) { + item.selected = false + } + + selectedItem.selected = true + + alert(message) +} + const render = defineStoryRender(() => html` Open Menu - alert('Selected One!')}>One - alert('Selected Two!')}>Two + select(event, 'Selected One!')}>One + select(event, 'Selected Two!')}>Two External Link `) diff --git a/src/components/menu/Menu.styles.css b/src/components/menu/Menu.styles.css index 3a6ec70aa..34dde081b 100644 --- a/src/components/menu/Menu.styles.css +++ b/src/components/menu/Menu.styles.css @@ -1,8 +1,14 @@ wa-dropdown::part(menu) { min-width: 300px; box-shadow: 0px 4px 16px 0px rgba(0, 0, 0, 0.35); + gap: 2px; } wa-dropdown-item { + border-radius: 5px; padding: 0px; + + &:hover { + background-color: var(--solid-ui-color-primary-hover); + } } diff --git a/src/components/menu/Menu.ts b/src/components/menu/Menu.ts index c8657a466..b946aeb31 100644 --- a/src/components/menu/Menu.ts +++ b/src/components/menu/Menu.ts @@ -18,7 +18,7 @@ export default class Menu extends WebComponent { accessor placement: WaDropdown['placement'] = 'bottom-start'; @property({ type: Number, reflect: true }) - accessor distance: number = 0; + accessor distance: number = 5; @query('wa-dropdown') private accessor dropdown: WaDropdown | null = null; diff --git a/src/lib/auth/SolidAuth.ts b/src/lib/auth/SolidAuth.ts index d69b32e1e..d96a34865 100644 --- a/src/lib/auth/SolidAuth.ts +++ b/src/lib/auth/SolidAuth.ts @@ -25,6 +25,7 @@ function findAccountImage (webId: string): string | undefined { export default class SolidAuth implements AuthContext { private _initialized = false + private profileLoaded = false private listeners: (() => unknown)[] = [] constructor (public signupUrl: string = DEFAULT_SIGNUP_URL) {} @@ -36,6 +37,18 @@ export default class SolidAuth implements AuthContext { this.listeners.forEach(listener => listener()) } + async loadProfile () { + if (this.profileLoaded || !this.account) { + return + } + + this.profileLoaded = true + + await solidLogicSingleton.profile.loadMe() + + this.listeners.forEach(listener => listener()) + } + get initialized (): boolean { return this._initialized } diff --git a/src/lib/auth/context.ts b/src/lib/auth/context.ts index 14fc82a7e..977570f75 100644 --- a/src/lib/auth/context.ts +++ b/src/lib/auth/context.ts @@ -6,6 +6,7 @@ export interface AuthContext { initialized: boolean; account: Account | null; login(loginUrl?: string): Promise; + loadProfile?(): Promise; signup(): Promise; logout(): Promise; onSessionUpdated(callback: () => unknown): () => void; diff --git a/src/styles/theme.css b/src/styles/theme.css index 2b8b00ea7..7a8fe5a17 100644 --- a/src/styles/theme.css +++ b/src/styles/theme.css @@ -1,4 +1,4 @@ -@import "@awesome.me/webawesome/dist/styles/webawesome.css"; +@import "@awesome.me/webawesome/dist/styles/themes/default.css"; @import url('https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible+Next:wght@400;700&display=swap'); :root { @@ -6,6 +6,8 @@ --font-family-ui: "Atkinson Hyperlegible Next", "Inter", "Noto Sans", sans-serif; --wa-font-family-body: "Atkinson Hyperlegible Next", "Inter", "Noto Sans", sans-serif; --solid-ui-color-primary: #7c4dff; + --solid-ui-color-primary-hover: var(--solid-ui-color-lavender-300); + --solid-ui-color-primary-selected: var(--solid-ui-color-lavender-400); --solid-ui-color-tertiary: #083575; --solid-ui-color-white: #ffffff; --solid-ui-color-error: #b00020; @@ -23,6 +25,9 @@ --solid-ui-color-slate-400: #90a1b9; --solid-ui-color-slate-800: #1d293d; + --solid-ui-color-lavender-300: #e6dcff; + --solid-ui-color-lavender-400: #cbb9ff; + --solid-ui-color-red-500: #fb2c36; --solid-ui-color-body-grey: #666;