Skip to content

Instantly share code, notes, and snippets.

@peerreynders
Last active October 17, 2020 22:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save peerreynders/3a346c618de5ae0cf5694bca5d56b912 to your computer and use it in GitHub Desktop.
Save peerreynders/3a346c618de5ae0cf5694bca5d56b912 to your computer and use it in GitHub Desktop.
"Slots" in Preact
<!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>
<!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