Last active
October 17, 2020 22:24
-
-
Save peerreynders/3a346c618de5ae0cf5694bca5d56b912 to your computer and use it in GitHub Desktop.
"Slots" in Preact
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
<!doctype html> | |
<html lang="eng"> | |
<!-- | |
Based on: Getting Your Head Around Vue.js Scoped Slots - Anthony Gore | |
https://vuejsdevelopers.com/2017/10/02/vue-js-scoped-slots/ | |
https://codepen.io/anthonygore/pen/zExPZX | |
--> | |
<head> | |
<meta charset="utf-8"/> | |
<title>"Slots" in Preact using children prop</title> | |
<style> | |
section h1, | |
ul { | |
margin-block-start: 0; | |
margin-block-end: 0; | |
} | |
#app { | |
display: flex; | |
background: linear-gradient(270deg, #0E6251, #28B463); | |
height: 100%; | |
} | |
.swatch { | |
display: inline-block; | |
width: 15px; | |
height: 10px; | |
margin-right: 8px; | |
} | |
.my-list { | |
flex: 1 1 50%; | |
font-family: Arial; | |
color: white; | |
margin: 20px; | |
} | |
.my-list h1 { | |
background: #A93226; | |
padding: 20px; | |
font-weight: bold; | |
font-size: 22px; | |
border-top-left-radius: 4px; | |
border-top-right-radius: 4px; | |
} | |
.my-list > ul { | |
list-style-type: none; | |
background: #34495E; | |
padding: 20px; | |
font-size: 16px; | |
border-bottom-left-radius: 4px; | |
border-bottom-right-radius: 4px; | |
} | |
.my-list .my-list__item + .my-list__item { | |
padding-top: 20px; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="app"></div> | |
<script type="module"> | |
import { createElement as h, render, Component } from 'https://unpkg.com/preact?module'; | |
// 1. In order to emulate a 'scoped slot' | |
// the `children` prop has to be a | |
// render prop so that the `MyList` | |
// component can pass props to its `children`. | |
// | |
function MyList({ title, items, children }) { | |
return ( | |
h('section', {class: 'my-list'}, | |
h('h1', null, title), | |
h('ul', null, | |
...items.map(item => | |
h('li', {class: 'my-list__item'}, | |
children(item) // (1.) | |
)) | |
) | |
) | |
); | |
} | |
// Given that the slot content | |
// needs to receive props | |
// the content needs to be | |
// implemented as a component | |
// (`Shape` and `Color`) | |
function Shape({ shape }) { | |
return ( | |
h('div', null, | |
`${shape.name} `, | |
h('small', null, `(${shape.sides} sides)`) | |
) | |
); | |
} | |
function Color({ color }){ | |
return ( | |
h('div', null, | |
h('div', {class: 'swatch', style: `background: ${color.hex}`}), | |
color.name | |
) | |
); | |
} | |
// 2. Here `MyList` is parent to `Shape` | |
// but as `MyList` needs pass props to `Shape` | |
// `children` to `MyList` needs to be function. | |
// 3. Similarly the `Color``children` to `MyList` | |
// has to be a function. | |
// | |
function App({ data }) { | |
return [ | |
h(MyList, {title: 'Shapes', items: data.shapes }, | |
shape => h(Shape, { shape }) // (2.) | |
), | |
h(MyList, {title: 'Colors', items: data.colors }, | |
color => h(Color, { color }) // (3.) | |
), | |
]; | |
} | |
const data = { | |
shapes: [ | |
{ name: 'Square', sides: 4 }, | |
{ name: 'Hexagon', sides: 6 }, | |
{ name: 'Triangle', sides: 3 }, | |
], | |
colors: [ | |
{ name: 'Yellow', hex: '#F4D03F' }, | |
{ name: 'Green', hex: '#229954' }, | |
{ name: 'Purple', hex: '#9B59B6' }, | |
], | |
}; | |
render( | |
h(App, { data }), | |
document.getElementById('app') | |
); | |
</script> | |
</body> | |
</html> |
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
<!doctype html> | |
<html lang="eng"> | |
<!-- | |
Based on: Getting Your Head Around Vue.js Scoped Slots - Anthony Gore | |
https://vuejsdevelopers.com/2017/10/02/vue-js-scoped-slots/ | |
https://codepen.io/anthonygore/pen/zExPZX | |
--> | |
<head> | |
<meta charset="utf-8"/> | |
<title>"Slots" in Preact using component injection</title> | |
<style> | |
section h1, | |
ul { | |
margin-block-start: 0; | |
margin-block-end: 0; | |
} | |
#app { | |
display: flex; | |
background: linear-gradient(270deg, #0E6251, #28B463); | |
height: 100%; | |
.. } | |
.swatch { | |
display: inline-block; | |
width: 15px; | |
height: 10px; | |
margin-right: 8px; | |
} | |
.my-list { | |
flex: 1 1 50%; | |
font-family: Arial; | |
color: white; | |
margin: 20px; | |
} | |
.my-list h1 { | |
background: #A93226; | |
padding: 20px; | |
font-weight: bold; | |
font-size: 22px; | |
border-top-left-radius: 4px; | |
border-top-right-radius: 4px; | |
} | |
.my-list > ul { | |
list-style-type: none; | |
background: #34495E; | |
padding: 20px; | |
font-size: 16px; | |
border-bottom-left-radius: 4px; | |
border-bottom-right-radius: 4px; | |
} | |
.my-list .my-list__item + .my-list__item { | |
padding-top: 20px; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="app"></div> | |
<script type="module"> | |
import { createElement as h, render, Component } from 'https://unpkg.com/preact?module'; | |
// 1. Here the 'scoped slot' is emulated with component injection: | |
// - the `render` prop references the component to use | |
// - the `name` prop identifies the prop name the | |
// item should be passed by. | |
// | |
function MyList({ title, items, name, render: View }) { | |
return ( | |
h('section', {class: 'my-list'}, | |
h('h1', null, title), | |
h('ul', null, | |
...items.map(item => | |
h('li', {class: 'my-list__item'}, | |
h(View, { [name]: item }) // (1.) | |
)) | |
) | |
) | |
); | |
} | |
// Given that the slot content | |
// needs to receive props | |
// the content needs to be | |
// implemented as a component | |
// (`Shape` and `Color`) | |
function Shape({ shape }) { | |
return ( | |
h('div', null, | |
`${shape.name} `, | |
h('small', null, `(${shape.sides} sides)`) | |
) | |
); | |
} | |
function Color({ color }){ | |
return ( | |
h('div', null, | |
h('div', {class: 'swatch', style: `background: ${color.hex}`}), | |
color.name | |
) | |
); | |
} | |
// The `App` component using component injection instead | |
// | |
// 2. The `Shape` component is injected for rendering | |
// while the `item` should be passed via the `shape` prop. | |
// 3. The `Colors` component is injected for rendering | |
// while the `item` should be passed via the `color` prop. | |
// | |
// In effect the "slot content" is no longer passed as "children" | |
// | |
function App({ data }) { | |
return [ | |
h(MyList, {title: 'Shapes', items: data.shapes, name: 'shape', render: Shape}), // (2.) | |
h(MyList, {title: 'Colors', items: data.colors, name: 'color', render: Color}) // (3.) | |
]; | |
} | |
const data = { | |
shapes: [ | |
{ name: 'Square', sides: 4 }, | |
{ name: 'Hexagon', sides: 6 }, | |
{ name: 'Triangle', sides: 3 }, | |
], | |
colors: [ | |
{ name: 'Yellow', hex: '#F4D03F' }, | |
{ name: 'Green', hex: '#229954' }, | |
{ name: 'Purple', hex: '#9B59B6' }, | |
], | |
}; | |
render( | |
h(App, { data }), | |
document.getElementById('app') | |
); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment