Example - Countdown timer
Display a countdown timer to a date/time, optionally linking to a destination.
This example renders a simple countdown timer to a configured end time.
Code
import * as React from 'react';
import { Text, getTextStyle, getDestinationHandler } from '@tapcart/mobile-components';
function pad2(value) {
return String(value).padStart(2, '0');
}
function getTimeLeft(endDate) {
const diff = +new Date(endDate) - +new Date();
if (Number.isNaN(diff) || diff <= 0) {
return { days: 0, hours: 0, minutes: 0, seconds: 0 };
}
return {
days: Math.floor(diff / (1000 * 60 * 60 * 24)),
hours: Math.floor((diff / (1000 * 60 * 60)) % 24),
minutes: Math.floor((diff / 1000 / 60) % 60),
seconds: Math.floor((diff / 1000) % 60),
};
}
export default function CountdownTimerExample({ blockConfig, useActions }) {
const { openScreen, openProduct, openCollection } = useActions();
const { endTime, destination, title } = blockConfig;
const [timeLeft, setTimeLeft] = React.useState(() => getTimeLeft(endTime?.value));
React.useEffect(() => {
const timer = setInterval(() => {
setTimeLeft(getTimeLeft(endTime?.value));
}, 1000);
return () => clearInterval(timer);
}, [endTime?.value]);
const openDestination = getDestinationHandler(destination?.type);
const handlePress = (e) => {
e?.stopPropagation?.();
if (!destination || destination.type === 'none') return;
openDestination(destination.location, { openScreen, openProduct, openCollection });
};
const titleStyle = title?.enabled ? getTextStyle(title) : {};
return (
<div role="button" tabIndex={0} onClick={handlePress} style={{ padding: '16px' }}>
{title?.enabled && (
<Text type="h2" style={{ ...titleStyle, marginBottom: '12px' }}>
{title.text}
</Text>
)}
<div style={{ display: 'flex', gap: '12px' }}>
<div>
<Text type="h1">{pad2(timeLeft.days)}</Text>
<Text type="body">days</Text>
</div>
<div>
<Text type="h1">{pad2(timeLeft.hours)}</Text>
<Text type="body">hours</Text>
</div>
<div>
<Text type="h1">{pad2(timeLeft.minutes)}</Text>
<Text type="body">minutes</Text>
</div>
<div>
<Text type="h1">{pad2(timeLeft.seconds)}</Text>
<Text type="body">seconds</Text>
</div>
</div>
</div>
);
}Manifest
[
{
"id": "endTime",
"label": "End Time",
"type": "date-time",
"defaultValue": { "value": "2030-01-01T00:00:00.000Z" }
},
{
"id": "destination",
"label": "Destination (optional)",
"type": "destination",
"defaultValue": { "type": "none", "location": null }
},
{
"id": "title",
"label": "Title",
"type": "section",
"defaultValue": true,
"manifestOptions": [
{ "id": "enabled", "label": "Show title", "type": "toggle", "defaultValue": true },
{ "id": "text", "label": "Text", "type": "text", "defaultValue": "Sale ends soon" },
{ "id": "font", "label": "Font", "type": "font-select", "defaultValue": { "family": "unset", "weight": "unset" } },
{ "id": "size", "label": "Size", "type": "range", "defaultValue": 18, "min": 8, "max": 36, "unit": "px", "step": 1 },
{ "id": "color", "label": "Color", "type": "color-select", "defaultValue": { "type": "brand-kit", "value": "textColors-primaryColor" } }
]
}
]Notes
- If you don’t want the entire block to be clickable, remove
onClick={handlePress}and attach the handler to a button.
Updated 24 days ago