Created
September 29, 2020 21:58
-
-
Save furf/a54c05511e386261e218cb3880b02cd1 to your computer and use it in GitHub Desktop.
This file contains 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
export type Callback<T> = (value: T) => void; | |
export type Unsubscribe = () => void; | |
export default class Callbacks<T> { | |
private callbacks = new Set<Callback<T>>(); | |
subscribe(callback: Callback<T>): Unsubscribe { | |
this.callbacks.add(callback); | |
return () => this.unsubscribe(callback); | |
} | |
publish(value: T) { | |
for (const callback of this.callbacks) { | |
callback(value); | |
} | |
} | |
unsubscribe(callback: Callback<T>): boolean { | |
return this.callbacks.delete(callback); | |
} | |
unsubscribeAll() { | |
this.callbacks.clear(); | |
} | |
} |
This file contains 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 } from 'lit-element'; | |
import pick from 'lodash-es/pick'; | |
import Callbacks, { Callback } from '../lib/callbacks'; | |
/** | |
* withContext | |
*/ | |
export default function withContext<T>() { | |
/** | |
* ContextProvider | |
*/ | |
class ContextProvider extends LitElement { | |
callbacks = new Callbacks<T>(); | |
disconnectedCallback() { | |
super.disconnectedCallback(); | |
this.callbacks.unsubscribeAll(); | |
} | |
subscribeContext(callback: Callback<T>) { | |
return this.callbacks.subscribe(callback); | |
} | |
publishContext(context: T) { | |
this.callbacks.publish(context); | |
} | |
} | |
/** | |
* isContextProvider | |
* @param element | |
*/ | |
function isContextProvider(element: HTMLElement): element is ContextProvider { | |
return element instanceof ContextProvider; | |
} | |
/** | |
* findContextProvider | |
* @param child | |
*/ | |
function findContextProvider(child: HTMLElement) { | |
let parent = child.parentElement; | |
while (parent instanceof HTMLElement && !isContextProvider(parent)) { | |
parent = parent.parentElement; | |
} | |
return parent; | |
} | |
function withContextConsumer(contextProps?: string[]) { | |
// Prefer lodash/fp/pick when you figure out how to import correctly | |
const filterProps = | |
Array.isArray(contextProps) && contextProps.length > 0 | |
? (context: T) => pick(context, ...contextProps) | |
: (context: T) => context; | |
/** | |
* ContextConsumer | |
*/ | |
class ContextConsumer extends LitElement { | |
connectedCallback() { | |
super.connectedCallback(); | |
const provider = findContextProvider(this); | |
if (provider) { | |
this.unsubscribeContext = provider.subscribeContext( | |
this.updateContext | |
); | |
} | |
} | |
disconnectedCallback() { | |
super.disconnectedCallback(); | |
this.unsubscribeContext(); | |
} | |
unsubscribeContext() {} | |
updateContext = (context: T) => { | |
Object.assign(this, filterProps(context)); | |
}; | |
} | |
return ContextConsumer; | |
} | |
return { | |
Provider: ContextProvider, | |
withContextConsumer, | |
}; | |
} |
This file contains 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 { html, css, property, query } from 'lit-element'; | |
import VideoContext from '../VideoContext'; | |
export class RmpVideo extends VideoContext.Provider { | |
static styles = css` | |
:host video { | |
display: block; | |
} | |
`; | |
@property({ type: Boolean }) | |
controls = false; | |
@property({ type: String }) | |
src = ''; | |
@property({ type: String }) | |
height = ''; | |
@property({ type: String }) | |
width = ''; | |
@query('video') | |
video: HTMLVideoElement | undefined; | |
render() { | |
return html` | |
<div | |
@click-pause=${this.handleClickPause} | |
@click-play=${this.handleClickPlay} | |
> | |
<video | |
?controls=${this.controls} | |
height=${this.height} | |
src=${this.src} | |
width=${this.width} | |
@pause=${this.handleVideoPause} | |
@play=${this.handleVideoPlay} | |
@timeupdate=${this.handleVideoTimeUpdate} | |
></video> | |
<slot></slot> | |
</div> | |
`; | |
} | |
private updateContext() { | |
if (!this.video) return; | |
const { currentTime, paused } = this.video; | |
const context = { | |
currentTime, | |
paused, | |
}; | |
this.publishContext(context); | |
} | |
/** | |
* UI Events | |
*/ | |
private handleClickPause() { | |
this.handleUserRequestedPause(); | |
} | |
private handleClickPlay() { | |
this.handleUserRequestedPlay(); | |
} | |
/** | |
* User intents | |
*/ | |
private handleUserRequestedPause() { | |
this.pause(); | |
} | |
private handleUserRequestedPlay() { | |
this.play(); | |
} | |
/** | |
* Video events | |
*/ | |
private handleVideoPause() { | |
this.updateContext(); | |
} | |
private handleVideoPlay() { | |
this.updateContext(); | |
} | |
private handleVideoTimeUpdate() { | |
this.updateContext(); | |
} | |
/** | |
* Actions | |
*/ | |
private pause() { | |
this.video?.pause(); | |
} | |
private play() { | |
this.video?.play(); | |
} | |
} |
This file contains 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 withContext from '../withContext'; | |
export interface IVideoContext { | |
paused: boolean; | |
} | |
const VideoContext = withContext<IVideoContext>(); | |
export default VideoContext; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment