working draft
This commit is contained in:
		
							parent
							
								
									0b684bc42e
								
							
						
					
					
						commit
						40e07e7419
					
				
							
								
								
									
										52
									
								
								components/generic/Consumer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								components/generic/Consumer.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | ||||
| import { createParentsIterator } from './ParentsIterator.js' | ||||
| import { StateProvider } from './Provider.js' | ||||
| 
 | ||||
| class StateConsumer extends HTMLElement { | ||||
|   constructor() { | ||||
|     super() | ||||
|     this.provider = null | ||||
|     this.appendChild(document.createElement('slot')) | ||||
|   } | ||||
| 
 | ||||
|   get name() { | ||||
|     return this.getAttribute('name') | ||||
|   } | ||||
| 
 | ||||
|   /** Returns the value of the corresponding provider */ | ||||
|   get value() { | ||||
|     return this.provider ? this.provider.value : null | ||||
|   } | ||||
| 
 | ||||
|   connectedCallback() { | ||||
|     this.provider = this.findProvider() | ||||
|     if (this.provider) { | ||||
|       this.provider.addObserver(this) | ||||
|     } | ||||
|     this.update(this.provider) | ||||
|   } | ||||
| 
 | ||||
|   disconnectedCallback() { | ||||
|     this.provider.removeObserver(this) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Traverses tree upwards until a Provider with the same name is found | ||||
|    * Returns the closest provider or null if none is existent | ||||
|    */ | ||||
|   findProvider() { | ||||
|     for (let node of createParentsIterator(this)) { | ||||
|       if (node instanceof StateProvider && node.name === this.name) { | ||||
|         return node | ||||
|       } | ||||
|     } | ||||
|     return null | ||||
|   } | ||||
| 
 | ||||
|   update({ value }) { | ||||
|     if (typeof this.onChange === 'function') { | ||||
|       this.onChange(value) | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| window.customElements.define('state-consumer', StateConsumer) | ||||
							
								
								
									
										28
									
								
								components/generic/ParentsIterator.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								components/generic/ParentsIterator.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| /** | ||||
|  * Returns the parent of a node. | ||||
|  * Breaks through shadow roots. | ||||
|  * Returns null if node has no parent. | ||||
|  * @param {Node} node | ||||
|  */ | ||||
| function getParent(node) { | ||||
|   let parent = node.parentNode | ||||
|   if (!parent) return null | ||||
|   return parent.host ? parent.host : parent | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Returns an Iterator that walks the DOM tree upwards. | ||||
|  * @param {Node} node | ||||
|  */ | ||||
| export function createParentsIterator(node) { | ||||
|   let curNode = node | ||||
|   return { | ||||
|     next: function() { | ||||
|       curNode = getParent(curNode) | ||||
|       return curNode ? { value: curNode } : { done: true } | ||||
|     }, | ||||
|     [Symbol.iterator]: function() { | ||||
|       return this | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										53
									
								
								components/generic/Provider.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								components/generic/Provider.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | ||||
| export class StateProvider extends HTMLElement { | ||||
|   constructor() { | ||||
|     super() | ||||
|     this.appendChild(document.createElement('slot')) | ||||
|     this.observers = [] | ||||
|   } | ||||
| 
 | ||||
|   connectedCallback() { | ||||
|     this.value = this.getAttribute('value') | ||||
|   } | ||||
| 
 | ||||
|   get name() { | ||||
|     return this.getAttribute('name') | ||||
|   } | ||||
| 
 | ||||
|   get value() { | ||||
|     return this._val | ||||
|   } | ||||
| 
 | ||||
|   set value(newValue) { | ||||
|     // if (this.value !== newValue) {
 | ||||
|     this._val = newValue | ||||
|     this.notifyObservers() | ||||
|     // }
 | ||||
|   } | ||||
| 
 | ||||
|   static get observedAttributes() { | ||||
|     return ['value'] | ||||
|   } | ||||
| 
 | ||||
|   attributeChangedCallback(name, oldValue, newValue) { | ||||
|     // console.log('attrChange', name, oldValue, newValue)
 | ||||
|     if (name === 'value') { | ||||
|       this.value = newValue | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   notifyObservers() { | ||||
|     for (let obs of this.observers) { | ||||
|       obs.update(this) | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   addObserver(obs) { | ||||
|     this.observers.push(obs) | ||||
|   } | ||||
| 
 | ||||
|   removeObserver(obs) { | ||||
|     this.observers = this.observers.filter(el => el !== obs) | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| window.customElements.define('state-provider', StateProvider) | ||||
							
								
								
									
										43
									
								
								components/rsvp-component.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								components/rsvp-component.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | ||||
| import { RSVPProvider } from './rsvp-provider.js' | ||||
| 
 | ||||
| export class RSVPComponent extends HTMLElement { | ||||
|   constructor() { | ||||
|     super() | ||||
|     this._root = this.attachShadow({ mode: 'open' }) | ||||
|     const provider = this.findProvider() | ||||
|     provider.addObserver(this) | ||||
|   } | ||||
| 
 | ||||
|   connectedCallback() {} | ||||
| 
 | ||||
|   render() {} | ||||
| 
 | ||||
|   update(state) {} | ||||
| 
 | ||||
|   fireEvent(action, payload) { | ||||
|     let evt = new CustomEvent('rsvp-event', { | ||||
|       bubbles: true, | ||||
|       composed: true, | ||||
|       detail: { action, payload } | ||||
|     }) | ||||
|     this.dispatchEvent(evt) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Traverses tree upwards until a RSVPProvider node is found | ||||
|    * Returns the closest provider or null if none is existent | ||||
|    */ | ||||
|   findProvider() { | ||||
|     let curNode = this | ||||
|     while (curNode.parentNode) { | ||||
|       curNode = curNode.parentNode | ||||
|       if (curNode.host) { | ||||
|         curNode = curNode.host | ||||
|       } | ||||
|       if (curNode instanceof RSVPProvider) { | ||||
|         break | ||||
|       } | ||||
|     } | ||||
|     return curNode instanceof RSVPProvider ? curNode : null | ||||
|   } | ||||
| } | ||||
| @ -1,15 +1,35 @@ | ||||
| import { RSVPComponent } from './rsvp-component.js' | ||||
| 
 | ||||
| class RSVPButton extends RSVPComponent { | ||||
|   constructor() { | ||||
|     super() | ||||
|     const btn = document.createElement('button') | ||||
|     const slot = document.createElement('slot') | ||||
| 
 | ||||
|     btn.addEventListener('click', this.handleClick.bind(this)) | ||||
|     btn.appendChild(slot) | ||||
|     this._root.appendChild(btn) | ||||
|   } | ||||
| 
 | ||||
|   handleClick(e) { | ||||
|     this.fireEvent(this.getAttribute('action')) | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| window.customElements.define('rsvp-button', RSVPButton) | ||||
| 
 | ||||
| class RSVPControls extends HTMLElement { | ||||
|   constructor() { | ||||
|     super() | ||||
|     const shadow = this.attachShadow({ mode: 'open' }) | ||||
|     // const shadow = document.createElement('div')
 | ||||
|     shadow.innerHTML = ` | ||||
|       <button action="prevSentence"><<</button> | ||||
|       <button action="prevWord"><</button> | ||||
|       <button action="stop">stop</button> | ||||
|       <button action="playpause">start</button> | ||||
|       <button action="nextWord">></button> | ||||
|       <button action="nextSentence">>></button> | ||||
|       <rsvp-button action="prevSentence"><<</rsvp-button> | ||||
|       <rsvp-button action="prevWord"><</rsvp-button> | ||||
|       <rsvp-button action="stop">stop</rsvp-button> | ||||
|       <rsvp-button action="playpause">start</rsvp-button> | ||||
|       <rsvp-button action="nextWord">></rsvp-button> | ||||
|       <rsvp-button action="nextSentence">>></rsvp-button> | ||||
|     ` | ||||
|     this._root = shadow | ||||
|     // this.appendChild(shadow)
 | ||||
| @ -19,18 +39,22 @@ class RSVPControls extends HTMLElement { | ||||
|     for (let button of this._root.querySelectorAll('button')) { | ||||
|       button.addEventListener('click', e => { | ||||
|         let action = e.target.getAttribute('action') | ||||
|         let evt = new CustomEvent('control-event', { detail: { action } }) | ||||
|         let evt = new CustomEvent('control-event', { | ||||
|           bubbles: true, | ||||
|           composed: true, | ||||
|           detail: { action } | ||||
|         }) | ||||
|         this.dispatchEvent(evt) | ||||
|       }) | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   updateUI(chapter, player) { | ||||
|     this.getButton('prevSentence').disabled = !chapter.hasPrevSentence() | ||||
|     this.getButton('prevWord').disabled = !chapter.hasPrevWord() | ||||
|     this.getButton('nextWord').disabled = !chapter.hasNextWord() | ||||
|     this.getButton('nextSentence').disabled = !chapter.hasNextSentence() | ||||
|     this.getButton('playpause').innerText = player.playing ? 'pause' : 'start' | ||||
|     // this.getButton('prevSentence').disabled = !chapter.hasPrevSentence()
 | ||||
|     // this.getButton('prevWord').disabled = !chapter.hasPrevWord()
 | ||||
|     // this.getButton('nextWord').disabled = !chapter.hasNextWord()
 | ||||
|     // this.getButton('nextSentence').disabled = !chapter.hasNextSentence()
 | ||||
|     // this.getButton('playpause').innerText = player.playing ? 'pause' : 'start'
 | ||||
|   } | ||||
| 
 | ||||
|   getButton(name) { | ||||
|  | ||||
							
								
								
									
										23
									
								
								components/rsvp-provider.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								components/rsvp-provider.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | ||||
| import { RSVPController } from '../src/RSVPController.js' | ||||
| 
 | ||||
| export class RSVPProvider extends HTMLElement { | ||||
|   constructor() { | ||||
|     super() | ||||
|     const shadow = this.attachShadow({ mode: 'open' }) | ||||
|     const provider = document.createElement('state-provider') | ||||
|     const slot = document.createElement('slot') | ||||
|     provider.setAttribute('name', 'rsvp-controller') | ||||
|     provider.appendChild(slot) | ||||
|     shadow.appendChild(provider) | ||||
| 
 | ||||
|     const controller = new RSVPController() | ||||
|     controller.onChange = this.provider.notifyObservers() | ||||
| 
 | ||||
|     this.addEventListener('rsvp-event', function(e) { | ||||
|       let { action, payload } = e.detail | ||||
|       controller.handleAction(action, payload) | ||||
|     }) | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| window.customElements.define('rsvp-provider', RSVPProvider) | ||||
| @ -1,78 +1,19 @@ | ||||
| import './rsvp-word.js' | ||||
| import './rsvp-controls.js' | ||||
| import './rsvp-word-markers.js' | ||||
| 
 | ||||
| import { Chapter } from '../src/Chapter.js' | ||||
| import { Player } from '../src/Player.js' | ||||
| import './rsvp-provider.js' | ||||
| 
 | ||||
| class RSVPReader extends HTMLElement { | ||||
|   constructor() { | ||||
|     super() | ||||
|     const shadow = this.attachShadow({ mode: 'open' }) | ||||
|     const marker = document.createElement('rsvp-border-marker') | ||||
|     const word = document.createElement('rsvp-word') | ||||
|     const controls = document.createElement('rsvp-controls') | ||||
| 
 | ||||
|     shadow.appendChild(marker) | ||||
|     marker.appendChild(word) | ||||
|     shadow.appendChild(controls) | ||||
| 
 | ||||
|     this._root = shadow | ||||
|     this._rsvpWord = word | ||||
|     this._rsvpControls = controls | ||||
| 
 | ||||
|     this.player = new Player() | ||||
|     this.player.subscribe('main', this.tick.bind(this)) | ||||
|     this.setText('Sample Text. Easy there.') | ||||
| 
 | ||||
|     this._rsvpControls.addEventListener( | ||||
|       'control-event', | ||||
|       this.handleControlEvent.bind(this) | ||||
|     ) | ||||
|   } | ||||
| 
 | ||||
|   setText(text) { | ||||
|     this.chapter = new Chapter(text, 10) | ||||
|     this.updateChildrenUI() | ||||
|   } | ||||
| 
 | ||||
|   tick() { | ||||
|     if (!this.chapter.hasNext()) { | ||||
|       this.player.stop() | ||||
|     } else { | ||||
|       this.chapter.next() | ||||
|     } | ||||
|     this.updateChildrenUI() | ||||
|   } | ||||
| 
 | ||||
|   updateChildrenUI() { | ||||
|     this._rsvpWord.setAttribute('word', this.chapter.currentSegment) | ||||
|     this._rsvpControls.updateUI(this.chapter, this.player) | ||||
|   } | ||||
| 
 | ||||
|   handleControlEvent(e) { | ||||
|     switch (e.detail.action) { | ||||
|       case 'prevSentence': | ||||
|         this.chapter.prevSentence() | ||||
|         break | ||||
|       case 'nextSentence': | ||||
|         this.chapter.nextSentence() | ||||
|         break | ||||
|       case 'prevWord': | ||||
|         this.chapter.prevWord() | ||||
|         break | ||||
|       case 'nextWord': | ||||
|         this.chapter.nextWord() | ||||
|         break | ||||
|       case 'playpause': | ||||
|         this.player.toggle() | ||||
|         break | ||||
|       case 'stop': | ||||
|         this.player.stop() | ||||
|         this.chapter.reset() | ||||
|         break | ||||
|     } | ||||
|     this.updateChildrenUI() | ||||
|     shadow.innerHTML = ` | ||||
|       <rsvp-provider> | ||||
|         <rsvp-word></rsvp-word> | ||||
|         <rsvp-controls></rsvp-controls> | ||||
|       </rsvp-provider> | ||||
|     ` | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,9 +1,9 @@ | ||||
| import { pivotize } from '../src/textProcessing/pivotize.js' | ||||
| import { RSVPComponent } from './rsvp-component.js' | ||||
| 
 | ||||
| class RSVPWord extends HTMLElement { | ||||
| class RSVPWord extends RSVPComponent { | ||||
|   constructor() { | ||||
|     super() | ||||
|     const shadow = this.attachShadow({ mode: 'open' }) | ||||
|     const style = document.createElement('style') | ||||
|     const word = document.createElement('div') | ||||
|     const prefix = document.createElement('span') | ||||
| @ -21,15 +21,17 @@ class RSVPWord extends HTMLElement { | ||||
|     word.appendChild(prefix) | ||||
|     word.appendChild(pivot) | ||||
|     word.appendChild(suffix) | ||||
|     shadow.appendChild(style) | ||||
|     shadow.appendChild(word) | ||||
|     this._root.appendChild(style) | ||||
|     this._root.appendChild(word) | ||||
| 
 | ||||
|     this._root = shadow | ||||
|     this.wordParts = { prefix, pivot, suffix } | ||||
|   } | ||||
| 
 | ||||
|   connectedCallback() { | ||||
|     this.updateDisplay() | ||||
|   update({ chapter }) { | ||||
|     const [prefix, pivot, suffix] = pivotize(chapter.currentSegment) | ||||
|     this.wordParts.prefix.innerText = prefix | ||||
|     this.wordParts.pivot.innerText = pivot | ||||
|     this.wordParts.suffix.innerText = suffix | ||||
|   } | ||||
| 
 | ||||
|   static get observedAttributes() { | ||||
|  | ||||
							
								
								
									
										2
									
								
								index.js
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								index.js
									
									
									
									
									
								
							| @ -9,4 +9,4 @@ function loadText() { | ||||
| } | ||||
| 
 | ||||
| loadBtn.addEventListener('click', loadText) | ||||
| loadText() | ||||
| // loadText()
 | ||||
|  | ||||
							
								
								
									
										25
									
								
								src/Book.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/Book.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | ||||
| import { Chapter } from './Chapter.js' | ||||
| import { Player } from './Player.js' | ||||
| 
 | ||||
| export class Book { | ||||
|   constructor() { | ||||
|     this.chapters = [new Chapter('')] | ||||
|     this.curIdx = 0 | ||||
|     this.player = new Player() | ||||
|   } | ||||
| 
 | ||||
|   get currentChapter() { | ||||
|     return this.chapters(curIdx) | ||||
|   } | ||||
| 
 | ||||
|   addChapter(chapter) { | ||||
|     this.chapters.push(chapter) | ||||
|   } | ||||
| 
 | ||||
|   removeChapter(chapter) { | ||||
|     this.chapters.filter(el => el !== chapter) | ||||
|     if (this.curIdx > 0) { | ||||
|       this.curIdx-- | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -2,17 +2,33 @@ import { parseText } from './textProcessing/parseText.js' | ||||
| 
 | ||||
| export class Chapter { | ||||
|   constructor(text, maxLength = -1) { | ||||
|     let { segments, words, sentences } = parseText(text, maxLength) | ||||
|     this._originalText = text | ||||
|     this._maxLength = maxLength | ||||
|     this.initSegments() | ||||
|   } | ||||
| 
 | ||||
|   initSegments() { | ||||
|     let { segments, words, sentences } = parseText(this._text, this._maxLength) | ||||
|     this.segments = segments | ||||
|     this.words = words | ||||
|     this.sentences = sentences | ||||
|     this.reset() | ||||
|     this.resetIndex() | ||||
|   } | ||||
| 
 | ||||
|   reset() { | ||||
|   resetIndex() { | ||||
|     this.currentIdx = 0 | ||||
|   } | ||||
| 
 | ||||
|   setText(text) { | ||||
|     this._originalText = text | ||||
|     this.initSegments() | ||||
|   } | ||||
| 
 | ||||
|   setMaxLength(maxLength) { | ||||
|     this._maxLength = maxLength | ||||
|     this.initSegments() | ||||
|   } | ||||
| 
 | ||||
|   get currentSegment() { | ||||
|     return this.segments[this.currentIdx] | ||||
|   } | ||||
|  | ||||
							
								
								
									
										70
									
								
								src/RSVPController.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/RSVPController.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | ||||
| import { RSVPPlayer } from './RSVPPlayer.js' | ||||
| 
 | ||||
| const initialOptions = { | ||||
|   maxLength: 10, | ||||
|   autoPlayNextChapter: false, | ||||
|   wordsPerMinute: 200, | ||||
|   pivotMethod: 'default', | ||||
|   pauseOnPunctuation: true | ||||
| } | ||||
| 
 | ||||
| export class RSVPController { | ||||
|   constructor(options = {}) { | ||||
|     this.options = initialOptions | ||||
|     this.setOptions(options) | ||||
| 
 | ||||
|     this.rsvpPlayer = new RSVPPlayer() | ||||
|     this.rsvpPlayer.onTick = this.onChange | ||||
| 
 | ||||
|     this.applyOptions() | ||||
|   } | ||||
| 
 | ||||
|   setOptions(options) { | ||||
|     this.options = { ...this.options, ...options } | ||||
|   } | ||||
| 
 | ||||
|   applyOptions() { | ||||
|     let { options } = this | ||||
|     let { chapter, player } = this.rsvpPlayer | ||||
|     chapter.setMaxLength = options.maxLength | ||||
|     player.updateInterval(1000 / options.wordsPerMinute) | ||||
|   } | ||||
| 
 | ||||
|   updateOptions(options) { | ||||
|     this.setOptions(options) | ||||
|     this.applyOptions() | ||||
|     this.onChange() | ||||
|   } | ||||
| 
 | ||||
|   processAction(action, payload) { | ||||
|     let { chapter, player } = this.rsvpPlayer | ||||
|     switch (action) { | ||||
|       case 'prevSentence': | ||||
|         chapter.prevSentence() | ||||
|         break | ||||
|       case 'nextSentence': | ||||
|         chapter.nextSentence() | ||||
|         break | ||||
|       case 'prevWord': | ||||
|         chapter.prevWord() | ||||
|         break | ||||
|       case 'nextWord': | ||||
|         chapter.nextWord() | ||||
|         break | ||||
|       case 'playpause': | ||||
|         player.toggle() | ||||
|         break | ||||
|       case 'stop': | ||||
|         player.stop() | ||||
|         chapter.reset() | ||||
|         break | ||||
|       case 'load': | ||||
|         chapter = new Chapter(payload, 10) | ||||
|     } | ||||
|     this.onChange() | ||||
|   } | ||||
| 
 | ||||
|   onChange() { | ||||
|     console.warn('RSVPController: onChange not set') | ||||
|   } | ||||
| } | ||||
							
								
								
									
										25
									
								
								src/RSVPPlayer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/RSVPPlayer.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | ||||
| import { Player } from './Player.js' | ||||
| import { Book } from './Book.js' | ||||
| import { Chapter } from './Chapter.js' | ||||
| 
 | ||||
| export class RSVPPlayer { | ||||
|   constructor(maxLength) { | ||||
|     this.chapter = new Chapter(maxLength) | ||||
|     this.player = new Player() | ||||
| 
 | ||||
|     this.player.subscribe('main', this.tick.bind(this)) | ||||
|   } | ||||
| 
 | ||||
|   tick() { | ||||
|     if (!this.chapter.hasNext()) { | ||||
|       this.player.stop() | ||||
|     } else { | ||||
|       this.chapter.next() | ||||
|     } | ||||
|     this.onTick() | ||||
|   } | ||||
| 
 | ||||
|   onTick() { | ||||
|     console.warn('RSVPPlayer: onTick not set') | ||||
|   } | ||||
| } | ||||
							
								
								
									
										56
									
								
								test-provider.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								test-provider.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | ||||
| <html lang="en"> | ||||
|   <head> | ||||
|     <meta charset="UTF-8" /> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||||
|     <meta http-equiv="X-UA-Compatible" content="ie=edge" /> | ||||
|     <title>Document</title> | ||||
|   </head> | ||||
| 
 | ||||
|   <body> | ||||
|     <state-provider value="1" id="prov1" name="state1"> | ||||
|       <b>lalal</b> | ||||
|       <div id="1"> | ||||
|         Hello World | ||||
|         <state-provider value="2" id="prov2" name="state2"> | ||||
|           <span id="2"> | ||||
|             <state-consumer id="cons1" name="state1"></state-consumer> | ||||
|           </span> | ||||
|         </state-provider> | ||||
|       </div> | ||||
|     </state-provider> | ||||
|     <div id="root"></div> | ||||
|     <script type="module"> | ||||
|       import './components/generic/Provider.js' | ||||
|       import './components/generic/Consumer.js' | ||||
| 
 | ||||
|       const prov1 = document.getElementById('prov1') | ||||
|       const cons1 = document.getElementById('cons1') | ||||
| 
 | ||||
|       cons1.onChange = console.log | ||||
| 
 | ||||
|       prov1.setAttribute('value', 'change1') | ||||
|       prov1.value = 'change2' | ||||
|       prov1.value = { val: 'change3', test: 'testing objects' } | ||||
| 
 | ||||
|       console.log(prov1.observers.length, 'should be 1') | ||||
|       cons1.remove() | ||||
|       console.log(prov1.observers.length, 'should be 0') | ||||
|     </script> | ||||
| 
 | ||||
|     <script> | ||||
|       const root = document.getElementById('root') | ||||
| 
 | ||||
|       root.innerHTML = ` | ||||
|         <state-provider id="prov3" value="initial"> | ||||
|           <state-consumer id="cons3" onChange=${console.log}></state-consumer> | ||||
|         </state-provider> | ||||
|       ` | ||||
| 
 | ||||
|       const prov3 = document.getElementById('prov3') | ||||
|       const cons3 = document.getElementById('cons3') | ||||
| 
 | ||||
|       console.log(prov3) | ||||
|       console.log(typeof cons3.getAttribute('onChange')) | ||||
|     </script> | ||||
|   </body> | ||||
| </html> | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user