Advertisement
Guest User

Untitled

a guest
Jul 18th, 2019
55
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.21 KB | None | 0 0
  1. <!--
  2. <h2>WC Wish List</h2>
  3.  
  4. <ul>
  5. <li><input type=checkbox> dynamic attributes</li>
  6. <li><input type=checkbox checked> if/else conditionals</li>
  7. </ul>
  8. -->
  9.  
  10. <template id="tpl-href-link">
  11. <style>
  12. a {
  13. color: red;
  14. }
  15. </style>
  16. <a href="{{href}}">
  17. <slot></slot>
  18. </a>
  19. </template>
  20.  
  21. <template id="tpl-show-once-loaded">
  22. <slot></slot>
  23. </template>
  24.  
  25. <template id="tpl-show-once-loading">
  26. <slot name="is-loading">
  27. <p>Loading...</p>
  28. </slot>
  29. </template>
  30.  
  31. <template id="tpl-news-preview">
  32. <article>
  33. <show-once>
  34. <slot slot="show-once" name="headline"></slot>
  35.  
  36. <h2><slot name="headline"></slot></h2>
  37.  
  38. <show-once>
  39. <slot slot="show-once" name="author.name"></slot>
  40. <div slot="is-loading"></div>
  41. <p>
  42. By <href-link>
  43. <slot name="author.@id" slot="href"></slot>
  44. <slot name="author.name"></slot>
  45. </href-link>
  46. </p>
  47. </show-once>
  48.  
  49. <show-once>
  50. <slot slot="show-once" name="dateModified"></slot>
  51. <div slot="is-loading"></div>
  52. Last modified <slot name="dateModified"></slot>
  53. </show-once>
  54.  
  55. <show-once>
  56. <slot slot="show-once" name="description"></slot>
  57.  
  58. <wrap-with>
  59. <p slot="wrap-with"></p>
  60. <slot name="description"></slot>
  61. </wrap-with>
  62. </show-once>
  63. </show-once>
  64. </article>
  65. </template>
  66.  
  67. <template id="tpl-event-preview">
  68. <style>
  69. article * {
  70. all: unset;
  71. display: flex;
  72. flex-basis: auto;
  73. flex-shrink: 0;
  74. flex-grow: 0;
  75. line-height: var(--line-height);
  76. --line-height: 28px;
  77. }
  78. h2 {
  79. font-size: 18px;
  80. }
  81. slot {
  82. padding-right: .25em;
  83. }
  84. </style>
  85. <fetch-data>
  86. <slot slot="by-id" name="agent.@id"></slot>
  87. <slot slot="by-id" name="object.@id"></slot>
  88. </fetch-data>
  89. <show-once>
  90. <slot slot="show-once" name="agent.name"></slot>
  91. <show-once>
  92. <slot slot="show-once" name="object.name"></slot>
  93. <article>
  94. <h2>
  95. <href-link>
  96. <slot slot="href" name="agent.@id"></slot>
  97. <slot name="agent.name"></slot>
  98. </href-link>
  99.  
  100. <slot name="@type"></slot>
  101.  
  102. <href-link>
  103. <slot slot="href" name="object.@id"></slot>
  104. <slot name="object.name"></slot>
  105. </href-link>
  106. </h2>
  107.  
  108. <show-once>
  109. <slot slot="show-once" name="body"></slot>
  110. <wrap-with>
  111. <p slot="wrap-with"></p>
  112. <slot name="body"></slot>
  113. </wrap-with>
  114. </show-once>
  115. </article>
  116. </show-once>
  117. </show-once>
  118. </template>
  119.  
  120. <script>
  121. (() => {
  122. // utility components
  123. window.customElements.define('wrap-with', class WrapWith extends window.HTMLElement {
  124. constructor() {
  125. super()
  126. this.attachShadow({
  127. mode: 'open'
  128. })
  129. const wrapWithSlotNode = [...this.children].find(node => node.matches('[slot="wrap-with"]'))
  130. if (wrapWithSlotNode) {
  131. const contentSlot = [...this.children].find(node => !node.matches('[slot="wrap-with"]'))
  132. contentSlot.addEventListener('slotchange', () => {
  133. this.slotChangedCallback()
  134. })
  135. }
  136. }
  137. slotChangedCallback() {
  138. ;[...this.shadowRoot.children].forEach(node => node.remove())
  139. const wrapWithSlotNode = [...this.children].find(node => node.matches('[slot="wrap-with"]'))
  140. const contentSlot = [...this.children].find(node => !node.matches('[slot="wrap-with"]'))
  141. contentSlot.assignedNodes().forEach((contentSlot) => {
  142. const clone = contentSlot.cloneNode(true)
  143. const wrapper = wrapWithSlotNode.cloneNode(true)
  144. clone.removeAttribute('slot')
  145. wrapper.removeAttribute('slot')
  146. wrapper.appendChild(clone)
  147. this.shadowRoot.appendChild(wrapper)
  148. })
  149. }
  150. })
  151. window.customElements.define('fetch-data', class FetchData extends window.HTMLElement {
  152. constructor() {
  153. super()
  154. this.attachShadow({
  155. mode: 'open'
  156. })
  157. const slots = [...this.querySelectorAll('[slot="by-id"]')]
  158. slots.forEach(async slot => {
  159. // fetch(slot.assignedNodes()[0].innerText.trim()).then(...)
  160. await new Promise(resolve => setTimeout(resolve))
  161. this.parentNode.host.appendChild(window.document.createTextNode(JSON.stringify({
  162. "agent": {
  163. "name": "Justin Amash"
  164. },
  165. "object": {
  166. "name": "Republican Party"
  167. }
  168. })))
  169. slot.remove()
  170. })
  171. }
  172. })
  173. window.customElements.define('show-once', class ShowOnce extends window.HTMLElement {
  174. constructor() {
  175. super()
  176. this.attachShadow({
  177. mode: 'open'
  178. })
  179. this.shadowRoot.appendChild(window.document.getElementById('tpl-show-once-loading').content.cloneNode(true))
  180. ;[...this.children].find(node => node.matches('[slot="show-once"]')).addEventListener('slotchange', () => {
  181. this.slotChangedCallback()
  182. })
  183. }
  184. slotChangedCallback() {
  185. const showIfSlot = [...this.children].find(node => node.matches('[slot~="show-once"]'))
  186. const showIfSlotText = !showIfSlot || showIfSlot.assignedNodes && showIfSlot.assignedNodes()[0] && showIfSlot.assignedNodes()[0].innerText.trim()
  187. ;[...this.shadowRoot.children].forEach(node => node.remove())
  188. if (showIfSlotText) {
  189. this.shadowRoot.appendChild(window.document.getElementById('tpl-show-once-loaded').content.cloneNode(true))
  190. if (showIfSlot) {
  191. showIfSlot.remove()
  192. }
  193. } else {
  194. this.shadowRoot.appendChild(window.document.getElementById('tpl-show-once-loading').content.cloneNode(true))
  195. }
  196. }
  197. })
  198. window.customElements.define('href-link', class HrefLink extends window.HTMLElement {
  199. constructor() {
  200. super()
  201. this.attachShadow({
  202. mode: 'open'
  203. })
  204. const hrefSlot = this.querySelector('[slot="href"]')
  205. hrefSlot.addEventListener('slotchange', () => {
  206. this.slotChangedCallback()
  207. })
  208. }
  209. slotChangedCallback() {
  210. const hrefSlot = this.querySelector('[slot="href"]')
  211. const href = hrefSlot.innerText.trim() || (hrefSlot.assignedNodes && hrefSlot.assignedNodes()[0] && hrefSlot.assignedNodes()[0].innerText.trim())
  212. if (href) {
  213. hrefSlot.assignedNodes()[0].remove()
  214. this.shadowRoot.innerHTML = [...window.document.getElementById('tpl-href-link').content.cloneNode(true).children].map(node => node.outerHTML).join(' ').replace(/{{href}}/, href)
  215. }
  216. }
  217. })
  218.  
  219. // ui components
  220. const BaseCustomElement = class BaseCustomElement extends window.HTMLElement {
  221. constructor() {
  222. super()
  223. this.attachShadow({
  224. mode: 'open'
  225. })
  226. this.shadowRoot.appendChild(window.document.getElementById(this.constructor.templateId).content.cloneNode(true))
  227. const hiddenDataNode = window.document.createElement('data')
  228. const hiddenDataSlot = window.document.createElement('slot')
  229. hiddenDataNode.setAttribute('hidden', 'hidden')
  230. hiddenDataNode.appendChild(hiddenDataSlot)
  231. this.shadowRoot.appendChild(hiddenDataNode)
  232. const defaultSlot = this.shadowRoot.querySelector('slot:not([name])')
  233. if (defaultSlot) {
  234. defaultSlot.addEventListener('slotchange', () => {
  235. this.slotChangedCallback()
  236. })
  237. }
  238. }
  239. slotChangedCallback() {
  240. const slotEl = this.shadowRoot.querySelector('slot:not([name])')
  241. const value = slotEl.assignedNodes().map(node => node.textContent.trim()).join('')
  242. if (value) {
  243. Object.entries(JSON.parse(value)).forEach(this.handleSlotCreation.bind(this))
  244. slotEl.assignedNodes().forEach(node => node.remove())
  245. }
  246. }
  247. handleSlotCreation([key, value]) {
  248. if (typeof value === 'string') {
  249. const slotEl = this.querySelector(`[slot="${key}"]`) || window.document.createElement('data')
  250. slotEl.setAttribute('slot', key)
  251. slotEl.innerText = value
  252. this.appendChild(slotEl)
  253. this.shadowRoot.firstElementChild.setAttribute(`data-${key}`.replace(/@/g, '').replace(/([A-Z])/g, '-$1').toLowerCase(), value)
  254. } else if (Array.isArray(value)) {
  255. ;[...this.querySelectorAll(`[slot="${key}"]`)].forEach(node => node.remove())
  256. const slotEls = value.forEach(v => {
  257. const slotEl = window.document.createElement('data')
  258. slotEl.setAttribute('slot', key)
  259. slotEl.innerText = v
  260. this.appendChild(slotEl)
  261. })
  262. } else if (value) {
  263. Object.entries(value).forEach(([innerKey, innerValue]) => {
  264. const slot = innerKey === '@value' ? key : `${key}.${innerKey}`
  265. if (typeof innerValue === 'string') {
  266. const slotEl = this.querySelector(`[slot="${slot}"]`) || window.document.createElement('data')
  267. slotEl.setAttribute('slot', `${slot}`)
  268. slotEl.innerText = innerValue
  269. this.appendChild(slotEl)
  270. } else {
  271. this.handleSlotCreation([slot, innerValue])
  272. }
  273. })
  274. }
  275. }
  276. }
  277. window.customElements.define('event-preview', class EventPreview extends BaseCustomElement {
  278. constructor() {
  279. super()
  280. }
  281. static get templateId() {
  282. return 'tpl-event-preview'
  283. }
  284. })
  285. window.customElements.define('news-preview', class NewsPreview extends BaseCustomElement {
  286. constructor() {
  287. super()
  288. }
  289. static get templateId() {
  290. return 'tpl-news-preview'
  291. }
  292. })
  293. })()
  294.  
  295. ;(async () => {
  296. await new Promise(resolve => setTimeout(resolve, 500))
  297. window.document.querySelector('#newsPreview').appendChild(window.document.createTextNode(JSON.stringify({
  298. "headline": "Justin Amash leaves Republican Party",
  299. "author": {
  300. "@type": "Person",
  301. "@id": "Person/Michael-Puckett"
  302. },
  303. "description": [
  304. `Justin Amash announced Thursday he was leaving the Republican Party to become an independent. He said he decided to exit the
  305. GOP "in this current Congress."`,
  306. `Amash in a July 4 Washington Post op-ed Amash wrote that he was departing the GOP after becoming "disenchanted
  307. with party politics and frightened by what I see from it."`
  308. ]
  309. })))
  310.  
  311. await new Promise(resolve => setTimeout(resolve, 500))
  312. window.document.querySelector('#newsPreview').appendChild(window.document.createTextNode(JSON.stringify({
  313. "dateModified": {
  314. "@type": "DateTime",
  315. "@value": "2019-07-04T11:30:00-07:00"
  316. },
  317. "author": {
  318. "name": "Michael Puckett"
  319. },
  320. "about": {
  321. "agent": {
  322. "name": "Justin Amash"
  323. },
  324. "object": {
  325. "name": "Republican Party"
  326. }
  327. }
  328. })))
  329. })()
  330. </script>
  331.  
  332. <news-preview id="newsPreview">
  333. {
  334. "@type": "NewsArticle",
  335. "@id": "NewsArticle/234567890123",
  336. "about": {
  337. "@type": "LeaveAction",
  338. "@id": "LeaveAction/234567890123",
  339. "agent": {
  340. "@id": "Person/Justin-Amash"
  341. },
  342. "object": {
  343. "@id": "Organization/Republican-Party"
  344. }
  345. }
  346. }
  347. </news-preview>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement