-
Notifications
You must be signed in to change notification settings - Fork 0
Reusable Components
It's very easy to create reusable components in Cardboard. They're plain functions, that return a Tag. We capitalize them to keep with common practices in other frameworks.
Counter component
const Counter = () => {
let count = state(0);
return button(
text(`Clicked $count times`, { count })
).clicked((_) => count.value++);
};
They can be used as any other tag:
div(Counter());
Todo component
export default function TodoItem(
content: State<TodoItem>,
remove: (self: CTag, content: State<TodoItem>) => void
) {
const isComplete = grab(content, 'complete', false);
const todoItem = grab(content, 'item', 'Empty TODO');
return div(
input()
.setAttrs({ type: 'checkbox' })
.on('change', (self, evt) => {
content.value.complete = self.checked;
}),
h4(todoItem)
.stylesIf(isComplete, { textDecoration: 'line-through' }),
button('-')
.clicked((self) => {
if (remove) remove(self, content);
}), // self.parent will be div
);
}
To style a component you can either use inline styles with CTag.addStyle
and .setStyle
,
or adding a class to the component and defining a style for it.
But Note that using inline style will duplicate the style for each instance of the component.
// Your component's file
tag('(head)').append(style({
'.styled_thing': {
backgroundColor: 'lightblue',
padding: '10px',
borderRadius: '5px',
color: 'darkslategray',
textAlign: 'center',
margin: '10px 0',
}
}));
const StyledThing = () => {
return div(
p('This is a styled component'),
).addClass('styled_thing');
}
// In main file
init().append(
StyledThing(),
StyledThing(),
);
In some cases, you might want to do stuff when the element is added to the page, or when it's removed. For that, Cardboard offers the function `withLifecycle(tag, { mounted, unmounted, beforeUnmounted }).
const Clock = () => {
const seconds = state('00');
const minutes = state('00');
const hours = state('00');
const setTime = () => {
const date = new Date();
seconds.value = date.getSeconds();
minutes.value = date.getMinutes();
hours.value = date.getHours();
};
let interval: any;
return withLifecycle(
div(hours, ':', minutes, ':', seconds),
{
mounted() {
// Add the interval when the element is added
setTime();
clearInterval(interval);
interval = setInterval(setTime, 500);
},
beforeUnmounted() {
// You can return a Promise<boolean>, that if false, it will cancel the remove operation
// Or if it's true or not return anything, the element will be removed
},
unmounted() {
// Clear the interval when the element is removed
clear interval(interval);
},
},
);
};
It's handy to have this control, as you don't want the interval to keep going if the element is not on the page.
It fires whenever the element is added to the page. This can be handy when you want to make an action when the element is added to the page. For example, running an animation, Tween, etc...
It fires before the element is removed from the page. If you return a promise that's false, the element will not be removed. If the promise returns true, or you don't return anything, the element will be removed as normal.
This can be very useful when you want to perform an action before the element is removed. For example, if you want to animate an element being removed from the page. You can return a promise that resolves when the animation or Tween ends. This way, the animation ends and then the element is removed.
It fires whenever the element is removed from the page. A good place to remove side effects that might stay running even if the element is removed. Like in the example above, where we need to stop the interval when the element is removed. Otherwise, it stays running infinitely.
If you miss any event, please let me know by dropping an issue here with your suggestion.
Cardboard provides a Component
helper to simplify the creation of reusable, stateful UI components. This function allows you to encapsulate state and logic, and returns a tag that updates automatically when its state changes.
Usage example:
import { Component } from '../../dist/ext/components.js';
const Counter = Component(({ state }) => {
return button(
text(`Clicked $count times`, { count: state })
).clicked(() => state.value++);
});
You can use this component just like any other tag:
div(Counter());
You can style components created with Component
in several ways:
Use .addStyle('property', value)
or .setStyle({ ... })
on the returned tag.
Use .addClass('your-class')
and define the class in a stylesheet.
The Component
function provides a .styled
property for convenient styling.
Example:
const BlueCounter = Counter.styled({
color: 'blue',
fontWeight: 'bold',
padding: '8px',
});
This returns a new component with the given styles applied to the root tag, the root element will get a class applied to it to identify those styles.
If you want your component to always be styled you can run styled within the component definition:
const Counter = Component(({ state }) => {
return button(
text(`Clicked $count times`, { count: state })
).clicked(() => state.value++);
}).styled(styles.CounterStyles);
Note: Inline styles are duplicated for each instance, while classes and
.styled
styles are more efficient for many components.
See more examples in the examples/components folder.
The implementation can be found in src/ext/components.js
.
This Wiki is a work in progress, and your help is greatly appreciated!
If you have some free time and are interested in contributing to the wiki or the project in general, please don't hesitate to reach out. All contributions are welcome!