# blockenberg/2.0.7/blocks/countdown/index.js

Blockenberg — 600+ Advanced Gutenberg Blocks &amp; AI Agent for WordPress Block Editor, version 2.0.7. 535 lines.

- Page: https://pluginprobe.com/plugins/blockenberg/2.0.7/code/blocks/countdown/index.js
- Raw: https://pluginprobe.com/plugins/blockenberg/2.0.7/raw/blocks/countdown/index.js
- Modified: 2026-03-06T12:32:40+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/blockenberg/2.0.7/code/blocks/countdown/index.js#L10-L20`.

```javascript
( function () {
    var el = wp.element.createElement;
    var Fragment = wp.element.Fragment;
    var __ = wp.i18n.__;
    var useState = wp.element.useState;
    var useEffect = wp.element.useEffect;
    var registerBlockType = wp.blocks.registerBlockType;
    var InspectorControls = wp.blockEditor.InspectorControls;
    var useBlockProps = wp.blockEditor.useBlockProps;
    var PanelBody = wp.components.PanelBody;
    var PanelColorSettings = wp.blockEditor.PanelColorSettings;
    var ToggleControl = wp.components.ToggleControl;
    var RangeControl = wp.components.RangeControl;
    var SelectControl = wp.components.SelectControl;
    var TextControl = wp.components.TextControl;

    var _cdTC, _cdTV;
    function _tc() { return _cdTC || (_cdTC = window.bkbgTypographyControl); }
    function _tv(obj, prefix) { var fn = _cdTV || (_cdTV = window.bkbgTypoCssVars); return fn ? fn(obj, prefix) : {}; }

    // Calculate time remaining
    function getTimeRemaining(targetDate, targetTime) {
        if (!targetDate) return { days: 0, hours: 0, minutes: 0, seconds: 0, total: 0 };
        
        var target = new Date(targetDate + 'T' + (targetTime || '00:00') + ':00');
        var now = new Date();
        var total = target - now;

        if (total <= 0) {
            return { days: 0, hours: 0, minutes: 0, seconds: 0, total: 0 };
        }

        return {
            days: Math.floor(total / (1000 * 60 * 60 * 24)),
            hours: Math.floor((total / (1000 * 60 * 60)) % 24),
            minutes: Math.floor((total / (1000 * 60)) % 60),
            seconds: Math.floor((total / 1000) % 60),
            total: total
        };
    }

    // Pad number with leading zero
    function pad(num) {
        return num < 10 ? '0' + num : String(num);
    }

    // Get default date (7 days from now)
    function getDefaultDate() {
        var d = new Date();
        d.setDate(d.getDate() + 7);
        return d.toISOString().split('T')[0];
    }

    registerBlockType('blockenberg/countdown', {
        title: __('Countdown', 'blockenberg'),
        icon: 'clock',
        category: 'bkbg-effects',
        description: __('Display a countdown timer to a specific date and time.', 'blockenberg'),

        edit: function (props) {
            var attributes = props.attributes;
            var setAttributes = props.setAttributes;
            var a = attributes;

            // State for live countdown in editor
            var timeState = useState(function () {
                return getTimeRemaining(a.targetDate || getDefaultDate(), a.targetTime);
            });
            var time = timeState[0];
            var setTime = timeState[1];

            // State for editing
            var editingState = useState(null);
            var editing = editingState[0];
            var setEditing = editingState[1];

            // Ref for evergreen simulation start time
            var evergreenStartRef = wp.element.useRef(null);

            // Update countdown every second in editor
            useEffect(function () {
                var targetDate = a.targetDate || getDefaultDate();
                
                // Set default date if not set and not evergreen
                if (!a.targetDate && !a.evergreenMode) {
                    setAttributes({ targetDate: targetDate });
                }

                // For evergreen, set up simulation start time
                if (a.evergreenMode && !evergreenStartRef.current) {
                    evergreenStartRef.current = Date.now();
                }
                if (!a.evergreenMode) {
                    evergreenStartRef.current = null;
                }

                // Calculate time remaining
                function updateTime() {
                    if (a.evergreenMode) {
                        // Calculate total duration in ms
                        var totalDuration = (a.evergreenDays * 24 * 60 * 60 * 1000) + 
                                           (a.evergreenHours * 60 * 60 * 1000) + 
                                           (a.evergreenMinutes * 60 * 1000);
                        
                        // Calculate elapsed time since simulation started
                        var elapsed = Date.now() - evergreenStartRef.current;
                        var remaining = Math.max(0, totalDuration - elapsed);
                        
                        // If expired, restart simulation
                        if (remaining <= 0) {
                            evergreenStartRef.current = Date.now();
                            remaining = totalDuration;
                        }
                        
                        var days = Math.floor(remaining / (1000 * 60 * 60 * 24));
                        var hours = Math.floor((remaining / (1000 * 60 * 60)) % 24);
                        var minutes = Math.floor((remaining / (1000 * 60)) % 60);
                        var seconds = Math.floor((remaining / 1000) % 60);
                        setTime({ days: days, hours: hours, minutes: minutes, seconds: seconds, total: remaining });
                    } else {
                        setTime(getTimeRemaining(targetDate, a.targetTime));
                    }
                }

                var interval = setInterval(updateTime, 1000);
                updateTime();

                return function () { clearInterval(interval); };
            }, [a.targetDate, a.targetTime, a.evergreenMode, a.evergreenDays, a.evergreenHours, a.evergreenMinutes]);

            var layoutOptions = [
                { label: __('Cards', 'blockenberg'), value: 'cards' },
                { label: __('Inline', 'blockenberg'), value: 'inline' },
                { label: __('Minimal', 'blockenberg'), value: 'minimal' },
                { label: __('Circle', 'blockenberg'), value: 'circle' }
            ];

            var expiredOptions = [
                { label: __('Show Message', 'blockenberg'), value: 'message' },
                { label: __('Hide Block', 'blockenberg'), value: 'hide' },
                { label: __('Redirect', 'blockenberg'), value: 'redirect' },
                { label: __('Keep Last State', 'blockenberg'), value: 'keep' }
            ];

            var fontWeightOptions = [
                { label: '400', value: 400 },
                { label: '500', value: 500 },
                { label: '600', value: 600 },
                { label: '700', value: 700 },
                { label: '800', value: 800 }
            ];

            // Inspector controls
            var inspector = el(InspectorControls, {},
                el(PanelBody, { title: __('Date & Time', 'blockenberg'), initialOpen: true },
                    el(ToggleControl, {
                        label: __('Evergreen Mode', 'blockenberg'),
                        help: a.evergreenMode ? __('Countdown resets for each visitor', 'blockenberg') : __('Fixed date countdown', 'blockenberg'),
                        checked: a.evergreenMode,
                        __nextHasNoMarginBottom: true,
                        onChange: function (v) { 
                            setAttributes({ evergreenMode: v }); 
                            // Generate unique ID for evergreen
                            if (v && !a.evergreenId) {
                                setAttributes({ evergreenId: 'eg_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9) });
                            }
                        }
                    }),
                    !a.evergreenMode && el('div', { className: 'bkbg-cd-date-control' },
                        el('label', {}, __('Target Date', 'blockenberg')),
                        el('input', {
                            type: 'date',
                            value: a.targetDate,
                            onChange: function (e) { setAttributes({ targetDate: e.target.value }); },
                            className: 'components-text-control__input'
                        })
                    ),
                    !a.evergreenMode && el('div', { className: 'bkbg-cd-date-control', style: { marginTop: '12px' } },
                        el('label', {}, __('Target Time', 'blockenberg')),
                        el('input', {
                            type: 'time',
                            value: a.targetTime,
                            onChange: function (e) { setAttributes({ targetTime: e.target.value }); },
                            className: 'components-text-control__input'
                        })
                    ),
                    a.evergreenMode && el(RangeControl, {
                        label: __('Days', 'blockenberg'),
                        value: a.evergreenDays,
                        min: 0,
                        max: 30,
                        onChange: function (v) { setAttributes({ evergreenDays: v }); }
                    }),
                    a.evergreenMode && el(RangeControl, {
                        label: __('Hours', 'blockenberg'),
                        value: a.evergreenHours,
                        min: 0,
                        max: 23,
                        onChange: function (v) { setAttributes({ evergreenHours: v }); }
                    }),
                    a.evergreenMode && el(RangeControl, {
                        label: __('Minutes', 'blockenberg'),
                        value: a.evergreenMinutes,
                        min: 0,
                        max: 59,
                        onChange: function (v) { setAttributes({ evergreenMinutes: v }); }
                    })
                ),

                el(PanelBody, { title: __('Display Options', 'blockenberg'), initialOpen: true },
                    el(SelectControl, {
                        label: __('Layout Style', 'blockenberg'),
                        value: a.layoutStyle,
                        options: layoutOptions,
                        onChange: function (v) { setAttributes({ layoutStyle: v }); }
                    }),
                    el(ToggleControl, {
                        label: __('Show Title', 'blockenberg'),
                        checked: a.showTitle,
                        __nextHasNoMarginBottom: true,
                        onChange: function (v) { setAttributes({ showTitle: v }); }
                    }),
                    el(ToggleControl, {
                        label: __('Show Days', 'blockenberg'),
                        checked: a.showDays,
                        __nextHasNoMarginBottom: true,
                        onChange: function (v) { setAttributes({ showDays: v }); }
                    }),
                    el(ToggleControl, {
                        label: __('Show Hours', 'blockenberg'),
                        checked: a.showHours,
                        __nextHasNoMarginBottom: true,
                        onChange: function (v) { setAttributes({ showHours: v }); }
                    }),
                    el(ToggleControl, {
                        label: __('Show Minutes', 'blockenberg'),
                        checked: a.showMinutes,
                        __nextHasNoMarginBottom: true,
                        onChange: function (v) { setAttributes({ showMinutes: v }); }
                    }),
                    el(ToggleControl, {
                        label: __('Show Seconds', 'blockenberg'),
                        checked: a.showSeconds,
                        __nextHasNoMarginBottom: true,
                        onChange: function (v) { setAttributes({ showSeconds: v }); }
                    }),
                    el(ToggleControl, {
                        label: __('Show Labels', 'blockenberg'),
                        checked: a.showLabels,
                        __nextHasNoMarginBottom: true,
                        onChange: function (v) { setAttributes({ showLabels: v }); }
                    }),
                    el(ToggleControl, {
                        label: __('Show Separators', 'blockenberg'),
                        checked: a.showSeparators,
                        __nextHasNoMarginBottom: true,
                        onChange: function (v) { setAttributes({ showSeparators: v }); }
                    })
                ),

                el(PanelBody, { title: __('Labels', 'blockenberg'), initialOpen: false },
                    el(TextControl, {
                        label: __('Days Label', 'blockenberg'),
                        value: a.labelDays,
                        onChange: function (v) { setAttributes({ labelDays: v }); }
                    }),
                    el(TextControl, {
                        label: __('Hours Label', 'blockenberg'),
                        value: a.labelHours,
                        onChange: function (v) { setAttributes({ labelHours: v }); }
                    }),
                    el(TextControl, {
                        label: __('Minutes Label', 'blockenberg'),
                        value: a.labelMinutes,
                        onChange: function (v) { setAttributes({ labelMinutes: v }); }
                    }),
                    el(TextControl, {
                        label: __('Seconds Label', 'blockenberg'),
                        value: a.labelSeconds,
                        onChange: function (v) { setAttributes({ labelSeconds: v }); }
                    })
                ),

                el(PanelBody, { title: __('Expiration', 'blockenberg'), initialOpen: false },
                    el(SelectControl, {
                        label: __('When Expired', 'blockenberg'),
                        value: a.expiredAction,
                        options: expiredOptions,
                        onChange: function (v) { setAttributes({ expiredAction: v }); }
                    }),
                    a.expiredAction === 'message' && el(TextControl, {
                        label: __('Expired Message', 'blockenberg'),
                        value: a.expiredMessage,
                        onChange: function (v) { setAttributes({ expiredMessage: v }); }
                    }),
                    a.expiredAction === 'redirect' && el(TextControl, {
                        label: __('Redirect URL', 'blockenberg'),
                        value: a.expiredRedirectUrl,
                        onChange: function (v) { setAttributes({ expiredRedirectUrl: v }); }
                    })
                ),

                el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
                    _tc() && el(_tc(), { label: __('Digit', 'blockenberg'), value: a.typoDigit, onChange: function (v) { setAttributes({ typoDigit: v }); } }),
                    _tc() && el(_tc(), { label: __('Label', 'blockenberg'), value: a.typoLabel, onChange: function (v) { setAttributes({ typoLabel: v }); } }),
                    _tc() && el(_tc(), { label: __('Title', 'blockenberg'), value: a.typoTitle, onChange: function (v) { setAttributes({ typoTitle: v }); } })
                ),

                el(PanelBody, { title: __('Spacing', 'blockenberg'), initialOpen: false },
                    el(RangeControl, {
                        label: __('Container Padding', 'blockenberg'),
                        value: a.padding,
                        min: 0,
                        max: 60,
                        onChange: function (v) { setAttributes({ padding: v }); }
                    }),
                    el(RangeControl, {
                        label: __('Gap Between Units', 'blockenberg'),
                        value: a.gap,
                        min: 8,
                        max: 48,
                        onChange: function (v) { setAttributes({ gap: v }); }
                    }),
                    el(RangeControl, {
                        label: __('Digit Box Padding', 'blockenberg'),
                        value: a.digitPadding,
                        min: 8,
                        max: 40,
                        onChange: function (v) { setAttributes({ digitPadding: v }); }
                    }),
                    el(RangeControl, {
                        label: __('Container Radius', 'blockenberg'),
                        value: a.borderRadius,
                        min: 0,
                        max: 30,
                        onChange: function (v) { setAttributes({ borderRadius: v }); }
                    }),
                    el(RangeControl, {
                        label: __('Digit Box Radius', 'blockenberg'),
                        value: a.digitRadius,
                        min: 0,
                        max: 24,
                        onChange: function (v) { setAttributes({ digitRadius: v }); }
                    })
                ),

                el(PanelColorSettings, {
                    title: __('Colors', 'blockenberg'),
                    initialOpen: false,
                    colorSettings: [
                        { value: a.wrapBg, onChange: function (c) { setAttributes({ wrapBg: c }); }, label: __('Background', 'blockenberg') },
                        { value: a.digitBg, onChange: function (c) { setAttributes({ digitBg: c }); }, label: __('Digit Background', 'blockenberg') },
                        { value: a.digitColor, onChange: function (c) { setAttributes({ digitColor: c }); }, label: __('Digit Color', 'blockenberg') },
                        { value: a.labelColor, onChange: function (c) { setAttributes({ labelColor: c }); }, label: __('Label Color', 'blockenberg') },
                        { value: a.separatorColor, onChange: function (c) { setAttributes({ separatorColor: c }); }, label: __('Separator Color', 'blockenberg') },
                        { value: a.titleColor, onChange: function (c) { setAttributes({ titleColor: c }); }, label: __('Title Color', 'blockenberg') }
                    ]
                })
            );

            // CSS variables
            var wrapStyle = {
                '--bkbg-cd-wrap-bg': a.wrapBg,
                '--bkbg-cd-digit-bg': a.digitBg,
                '--bkbg-cd-digit-color': a.digitColor,
                '--bkbg-cd-label-color': a.labelColor,
                '--bkbg-cd-separator-color': a.separatorColor,
                '--bkbg-cd-title-color': a.titleColor,
                '--bkbg-cd-digit-size': a.digitFontSize + 'px',
                '--bkbg-cd-digit-weight': a.digitFontWeight,
                '--bkbg-cd-label-size': a.labelFontSize + 'px',
                '--bkbg-cd-label-weight': a.labelFontWeight,
                '--bkbg-cd-title-size': a.titleFontSize + 'px',
                '--bkbg-cd-padding': a.padding + 'px',
                '--bkbg-cd-gap': a.gap + 'px',
                '--bkbg-cd-radius': a.borderRadius + 'px',
                '--bkbg-cd-digit-padding': a.digitPadding + 'px',
                '--bkbg-cd-digit-radius': a.digitRadius + 'px'
            };

            // Build time units
            var units = [];

            if (a.showDays) {
                units.push({ key: 'days', value: time.days, label: a.labelDays });
            }
            if (a.showHours) {
                units.push({ key: 'hours', value: time.hours, label: a.labelHours });
            }
            if (a.showMinutes) {
                units.push({ key: 'minutes', value: time.minutes, label: a.labelMinutes });
            }
            if (a.showSeconds) {
                units.push({ key: 'seconds', value: time.seconds, label: a.labelSeconds });
            }

            // Title element
            var titleEl = null;
            if (a.showTitle) {
                if (editing === 'title') {
                    titleEl = el('input', {
                        type: 'text',
                        className: 'bkbg-cd-title bkbg-cd-input-active',
                        value: a.title,
                        autoFocus: true,
                        placeholder: __('Countdown Title', 'blockenberg'),
                        onChange: function (e) { setAttributes({ title: e.target.value }); },
                        onBlur: function () { setEditing(null); },
                        onKeyDown: function (e) { if (e.key === 'Enter') setEditing(null); }
                    });
                } else {
                    titleEl = el('div', {
                        className: 'bkbg-cd-title bkbg-cd-clickable',
                        onClick: function () { setEditing('title'); }
                    }, a.title || __('Countdown Title', 'blockenberg'));
                }
            }

            // Build countdown units
            var countdownUnits = units.map(function (unit, index) {
                var isLast = index === units.length - 1;
                var labelKey = 'label-' + unit.key;
                var valueStr = pad(unit.value);

                // Label element - click to edit
                var labelEl = null;
                if (a.showLabels) {
                    if (editing === labelKey) {
                        labelEl = el('input', {
                            type: 'text',
                            className: 'bkbg-cd-label bkbg-cd-input-active',
                            value: unit.label,
                            autoFocus: true,
                            onChange: function (e) {
                                var attr = 'label' + unit.key.charAt(0).toUpperCase() + unit.key.slice(1);
                                var obj = {};
                                obj[attr] = e.target.value;
                                setAttributes(obj);
                            },
                            onBlur: function () { setEditing(null); },
                            onKeyDown: function (e) { if (e.key === 'Enter') setEditing(null); }
                        });
                    } else {
                        labelEl = el('span', {
                            className: 'bkbg-cd-label bkbg-cd-clickable',
                            onClick: function () { setEditing(labelKey); }
                        }, unit.label);
                    }
                }

                return el(Fragment, { key: unit.key },
                    el('div', { className: 'bkbg-cd-unit' },
                        el('div', { className: 'bkbg-cd-digit' },
                            el('span', { className: 'bkbg-cd-number' }, valueStr)
                        ),
                        labelEl
                    ),
                    a.showSeparators && !isLast && el('span', { className: 'bkbg-cd-separator' }, ':')
                );
            });

            var blockProps = useBlockProps({
                className: 'bkbg-editor-wrap',
                'data-block-label': 'Countdown'
            });

            return el('div', blockProps,
                inspector,
                el('div', {
                    className: 'bkbg-cd-wrap',
                    style: wrapStyle,
                    'data-layout': a.layoutStyle
                },
                    titleEl,
                    el('div', { className: 'bkbg-cd-countdown' }, countdownUnits)
                )
            );
        },

        save: function (props) {
            var a = props.attributes;

            var wrapStyle = Object.assign({
                '--bkbg-cd-wrap-bg': a.wrapBg,
                '--bkbg-cd-digit-bg': a.digitBg,
                '--bkbg-cd-digit-color': a.digitColor,
                '--bkbg-cd-label-color': a.labelColor,
                '--bkbg-cd-separator-color': a.separatorColor,
                '--bkbg-cd-title-color': a.titleColor,
                '--bkbg-cd-digit-size': a.digitFontSize + 'px',
                '--bkbg-cd-digit-weight': a.digitFontWeight,
                '--bkbg-cd-label-size': a.labelFontSize + 'px',
                '--bkbg-cd-label-weight': a.labelFontWeight,
                '--bkbg-cd-title-size': a.titleFontSize + 'px',
                '--bkbg-cd-padding': a.padding + 'px',
                '--bkbg-cd-gap': a.gap + 'px',
                '--bkbg-cd-radius': a.borderRadius + 'px',
                '--bkbg-cd-digit-padding': a.digitPadding + 'px',
                '--bkbg-cd-digit-radius': a.digitRadius + 'px'
            }, _tv(a.typoDigit, '--bkbg-cd-dig-'), _tv(a.typoLabel, '--bkbg-cd-lbl-'), _tv(a.typoTitle, '--bkbg-cd-ttl-'));

            // Build units config as data attributes
            var unitsConfig = [];
            if (a.showDays) unitsConfig.push('days:' + a.labelDays);
            if (a.showHours) unitsConfig.push('hours:' + a.labelHours);
            if (a.showMinutes) unitsConfig.push('minutes:' + a.labelMinutes);
            if (a.showSeconds) unitsConfig.push('seconds:' + a.labelSeconds);

            // Calculate evergreen duration in milliseconds
            var evergreenDuration = (a.evergreenDays * 24 * 60 * 60 * 1000) + 
                                    (a.evergreenHours * 60 * 60 * 1000) + 
                                    (a.evergreenMinutes * 60 * 1000);

            return el('div', {
                className: 'bkbg-cd-wrap',
                style: wrapStyle,
                'data-layout': a.layoutStyle,
                'data-target': a.evergreenMode ? '' : (a.targetDate + 'T' + a.targetTime),
                'data-units': unitsConfig.join('|'),
                'data-show-labels': a.showLabels ? '1' : '0',
                'data-show-separators': a.showSeparators ? '1' : '0',
                'data-expired-action': a.expiredAction,
                'data-expired-message': a.expiredMessage,
                'data-expired-redirect': a.expiredRedirectUrl,
                'data-evergreen': a.evergreenMode ? '1' : '0',
                'data-evergreen-duration': String(evergreenDuration),
                'data-evergreen-id': a.evergreenId
            },
                a.showTitle && a.title && el('div', { className: 'bkbg-cd-title' }, a.title),
                el('div', { className: 'bkbg-cd-countdown' })
            );
        }
    });
}() );

```
