create a link embed

- proof of concept of a separate web components package
- link embed has icon/title/subtitle
- web components are registered just before app mounts, making them available in the markdown as well as the app
This commit is contained in:
lightling 2024-05-15 01:32:33 -04:00
parent 6ffe5939a5
commit 75228e1a3a
9 changed files with 141 additions and 17 deletions

7
libs/embeds/package.json Normal file
View file

@ -0,0 +1,7 @@
{
"name": "@goldenwere/static-web-templates-embeds",
"private": true,
"version": "0.0.0",
"type": "module",
"main": "src/index.ts"
}

1
libs/embeds/src/index.ts Normal file
View file

@ -0,0 +1 @@
export * from './link'

101
libs/embeds/src/link.ts Normal file
View file

@ -0,0 +1,101 @@
import type { AttributeObserver } from './observer'
export type LinkEmbedAttributes = typeof LinkEmbed.observedAttributes[number]
/**
* A link embed is a link with a title and an optional icon + subtitle
*/
export class LinkEmbed extends HTMLElement {
static observedAttributes = [
'icon',
'title',
'subtitle',
'href',
'target',
] as const
_observers: AttributeObserver<LinkEmbedAttributes> = {} as any
constructor() {
super()
const { ownerDocument } = this
const _link = ownerDocument.createElement('a')
_link.setAttribute('rel', 'noreferrer noopener')
this._observers.href = {
element: _link,
observer: (oldVal, newVal) => {
const { element } = this._observers.href
element!.setAttribute('href', newVal)
},
}
this._observers.target = {
element: _link,
observer: (oldVal, newVal) => {
const { element } = this._observers.href
if (!!newVal) {
element!.setAttribute('target', newVal)
} else {
element!.removeAttribute('target')
}
},
}
this.append(_link)
const _icon = ownerDocument.createElement('img')
_icon.alt = '' // decorative
this._observers.icon = {
element: _icon,
observer: (oldVal, newVal) => {
const { element } = this._observers.icon
if (!!newVal) {
element!.setAttribute('src', newVal)
} else {
element!.style.display = 'hidden'
}
},
}
const _textWrapper = ownerDocument.createElement('div')
_textWrapper.classList.add('text')
_link.append(_textWrapper)
const _title = ownerDocument.createElement('p')
this._observers.title = {
element: _title,
observer: (oldVal, newVal) => {
const { element } = this._observers.title
element!.innerHTML = newVal
},
}
const _subtitle = ownerDocument.createElement('p')
this._observers.subtitle = {
element: _subtitle,
observer: (oldVal, newVal) => {
const { element } = this._observers.subtitle
if (!!newVal) {
element!.innerHTML = newVal
} else {
element!.style.display = 'hidden'
}
},
}
_textWrapper.append(
_title,
_subtitle,
)
}
attributeChangedCallback(name: LinkEmbedAttributes, oldVal: any, newVal: any) {
this._observers[name].observer(oldVal, newVal)
}
}
export const registerLinkEmbed = () => {
if (!customElements.get('link-embed')) {
customElements.define('link-embed', LinkEmbed)
}
}

7
libs/embeds/src/observer.d.ts vendored Normal file
View file

@ -0,0 +1,7 @@
export type AttributeObserver<T extends string> = Record<T, {
element?: HTMLElement,
observer: (
oldVal: any,
newVal: any,
) => void
}>