Created
November 5, 2021 01:41
-
-
Save renoirb/d0d92314b04927c8513a86810902a53a to your computer and use it in GitHub Desktop.
Lit.Dev with @lit-labs/context
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { LitElement, html, TemplateResult } from 'lit' | |
| import { property } from 'lit/decorators/property.js' | |
| import { state } from 'lit/decorators/state.js' | |
| import { assert } from '@esm-bundle/chai' | |
| import { ContextProvider, createContext } from '../context.js' | |
| import { ContextConsumer } from '../lib/controllers/context-consumer.js' | |
| /** | |
| * Say we have many components that has disperse data that would come from | |
| * many backends. | |
| * The "userId" field can be used to tell which component it is targeted for. | |
| * Sometimes, we already have list of "userId"s, but miss the rest of the data. | |
| */ | |
| interface IUserInfo { | |
| /** | |
| * The foreign key | |
| */ | |
| userId: string | |
| } | |
| /** | |
| * A slice of user information might come from one data source. | |
| */ | |
| interface IUserInfoBasics extends IUserInfo { | |
| firstName: string | |
| lastName: string | |
| } | |
| // const UserInfoBasicsDefault: Readonly<IUserInfoBasics> = Object.freeze({ | |
| // userId: '', | |
| // firstName: 'i18n:firstName', | |
| // lastName: 'i18n:lastName', | |
| // }) | |
| const PRESENCE_SYMBOLS = ['online', 'offline', 'away', 'unknown'] as const | |
| type IPresenceSymbol = typeof PRESENCE_SYMBOLS[number] | |
| /** | |
| * Displaying its presence status from another data source. | |
| */ | |
| interface IUserInfoPresence extends IUserInfo { | |
| presence: IPresenceSymbol | |
| } | |
| const UserInfoPresenceDefault: Readonly<IUserInfoPresence> = Object.freeze({ | |
| userId: '', | |
| presence: 'unknown', | |
| }) | |
| const extractPresence = ( | |
| maybe: { presence?: string } | unknown, | |
| ): IPresenceSymbol => { | |
| let outcome: IPresenceSymbol = UserInfoPresenceDefault.presence | |
| if (typeof maybe === 'object' && maybe && 'presence' in maybe) { | |
| const dto = maybe as IUserInfoPresence | |
| if (PRESENCE_SYMBOLS.includes(dto?.presence ?? '')) { | |
| outcome = dto.presence | |
| } | |
| } | |
| return outcome | |
| } | |
| const extractInitials = (dto: Omit<IUserInfoBasics, 'userId'>): string => { | |
| const { firstName = '', lastName = '' } = dto ?? {} | |
| return [firstName, lastName] | |
| .map((i) => i.trim().toLocaleUpperCase().charAt(0)) | |
| .join('') | |
| } | |
| const UserInfoPresenceContext = | |
| createContext<IUserInfoPresence>('user-info-presence') | |
| const UserInfoBasicsContext = createContext<IUserInfoBasics>('user-info-basics') | |
| class UserInfoPresenceContextProvider extends LitElement { | |
| private provider = new ContextProvider(this, UserInfoPresenceContext, { | |
| presence: 'unknown', | |
| userId: '', | |
| }) | |
| public setValue(value: IUserInfoPresence) { | |
| this.provider.setValue(value) | |
| } | |
| } | |
| class UserInfoBasicsContextProvider extends LitElement { | |
| private provider = new ContextProvider(this, UserInfoBasicsContext, { | |
| firstName: '', | |
| lastName: '', | |
| userId: '', | |
| }) | |
| public setValue(value: IUserInfoBasics) { | |
| this.provider.setValue(value) | |
| } | |
| } | |
| class UserPresenceIndicator extends LitElement { | |
| @property({ type: String, attribute: 'data-user-id' }) | |
| public userId = '' | |
| @state() | |
| private _presence: IPresenceSymbol | '' = '' | |
| public constructor() { | |
| super() | |
| // this.setAttribute('data-presence', this._presence) | |
| new ContextConsumer(this, UserInfoPresenceContext, this.#onCallback) | |
| } | |
| #onCallback = (value: IUserInfoPresence) => { | |
| const { userId = '' } = value ?? {} | |
| // Use in payload "userId" property (name not important) | |
| // to help re-use same context object, yet target a destination | |
| if (this.userId === userId) { | |
| const presence = extractPresence(value) | |
| this._presence = presence | |
| // this.setAttribute('data-presence', this._presence) | |
| } | |
| } | |
| render() { | |
| // Just mimicking an actual presence indicator | |
| const fillColor: Record<string, boolean> = { | |
| white: this._presence === 'unknown' || this._presence === 'away', | |
| green: this._presence === 'online', | |
| black: this._presence === 'offline', | |
| } | |
| const strokeColor: Record<string, boolean> = { | |
| white: fillColor.white === false, | |
| black: fillColor.white, | |
| } | |
| return html`<!-- --> | |
| <svg width="100" height="100"> | |
| <circle | |
| cx="50" | |
| cy="50" | |
| r="40" | |
| stroke=${strokeColor} | |
| stroke-width="4" | |
| fill=${fillColor} | |
| /> | |
| </svg> | |
| <!-- -->` | |
| } | |
| } | |
| class UserBadge extends LitElement implements IUserInfoBasics { | |
| @property({ type: String, attribute: 'data-user-id' }) | |
| public readonly userId: string = '' | |
| @state() | |
| public firstName = '' | |
| @state() | |
| public lastName = '' | |
| public constructor() { | |
| super() | |
| new ContextConsumer(this, UserInfoBasicsContext, this.#onCallback) | |
| } | |
| #onCallback = (value: IUserInfoBasics) => { | |
| const { userId = '', ...rest } = value ?? {} | |
| // Use in payload "userId" property (name not important) | |
| // to help re-use same context object, yet target a destination | |
| if (this.userId === userId) { | |
| const { firstName = '', lastName = '' } = rest ?? {} | |
| if (this.firstName !== firstName) { | |
| this.firstName = firstName | |
| } | |
| if (this.lastName !== lastName) { | |
| this.lastName = lastName | |
| } | |
| } | |
| } | |
| render(): TemplateResult { | |
| const dto: Omit<IUserInfoBasics, 'userId'> = { | |
| lastName: this.lastName, | |
| firstName: this.firstName, | |
| } | |
| const initials = extractInitials(dto) | |
| return html`<div title="${this.firstName} ${this.lastName}"> | |
| ${initials} | |
| <user-presence-indicator | |
| data-user-id=${this.userId} | |
| ></user-presence-indicator> | |
| </div>` | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment