Check out BugBytes overview of labb

Swap

<c-lb.swap /> Markdown

Swap component for Django: toggle between two states with smooth transitions. Built with django-cotton, Tailwind CSS, and daisyUI 5.

Swap toggles between two child elements using CSS checkbox state — no JavaScript needed. Common uses include icon toggles, show/hide text, and theme icon switching. Rotate and flip transition effects are available via the effect prop.

Basic Swap

<c-lb.swap>
    <c-lb.swap.on>ON</c-lb.swap.on>
    <c-lb.swap.off>OFF</c-lb.swap.off>
</c-lb.swap>

With Icons

<c-lb.swap>
    <c-lb.swap.on icon="rmx.volume-up" />
    <c-lb.swap.off icon="rmx.volume-mute" />
</c-lb.swap>

Rotate Effect

<c-lb.swap effect="rotate">
    <c-lb.swap.on icon="rmx.sun" />
    <c-lb.swap.off icon="rmx.moon" />
</c-lb.swap>

Flip Effect

<c-lb.swap effect="flip" class="text-6xl">
    <c-lb.swap.on>🥵</c-lb.swap.on>
    <c-lb.swap.off>🥶</c-lb.swap.off>
</c-lb.swap>

Hamburger Menu

<c-lb.swap effect="rotate" class="btn btn-circle">
    <c-lb.swap.on icon="rmx.close" />
    <c-lb.swap.off icon="rmx.menu" />
</c-lb.swap>

Checked State

<c-lb.swap :checked="False">
    <c-lb.swap.on>
        <c-lbi n="rmx.heart" w="2em" h="2em" class="text-error" />
    </c-lb.swap.on>
    <c-lb.swap.off>
        <c-lbi n="rmx.heart" w="2em" h="2em" />
    </c-lb.swap.off>
</c-lb.swap>

Disabled State

<c-lb.swap disabled>
    <c-lb.swap.on>
        <c-lbi n="rmx.play" w="2em" h="2em" />
    </c-lb.swap.on>
    <c-lb.swap.off>
        <c-lbi n="rmx.pause" w="2em" h="2em" />
    </c-lb.swap.off>
</c-lb.swap>

Indeterminate State

💡 Click the child (document) checkboxes to see the parent's indeterminate state.

Select All Files
Document 1
Document 2
Document 3

Status: No files selected

Parent state: Unchecked

<!-- Interactive example showing indeterminate state -->
<div class="space-y-4 sm:w-50">

    <!-- Parent checkbox -->
    <div class="flex items-center gap-2 p-3 bg-base-200 rounded-lg w-full">
        <c-lb.swap id="parent-swap">
            <c-lb.swap.on>
                <c-lbi n="rmx.check" w="1.5em" h="1.5em" class="text-success" />
            </c-lb.swap.on>
            <c-lb.swap.off>
                <c-lbi n="rmx.close" w="1.5em" h="1.5em" class="text-error" />
            </c-lb.swap.off>
            <c-lb.swap.indeterminate>
                <c-lbi n="rmx.subtract" w="1.5em" h="1.5em" class="text-warning" />
            </c-lb.swap.indeterminate>
        </c-lb.swap>
        <span class="font-medium">Select All Files</span>
    </div>

    <!-- Child checkboxes -->
    <div class="ml-6 space-y-2">
        <div class="flex items-center gap-2">
            <c-lb.swap class="child-checkbox">
                <c-lb.swap.on>
                    <c-lbi n="rmx.check" w="1em" h="1em" class="text-success" />
                </c-lb.swap.on>
                <c-lb.swap.off>
                    <c-lbi n="rmx.close" w="1em" h="1em" class="text-error" />
                </c-lb.swap.off>
            </c-lb.swap>
            <span class="text-sm">Document 1</span>
        </div>
        <div class="flex items-center gap-2">
            <c-lb.swap class="child-checkbox">
                <c-lb.swap.on>
                    <c-lbi n="rmx.check" w="1em" h="1em" class="text-success" />
                </c-lb.swap.on>
                <c-lb.swap.off>
                    <c-lbi n="rmx.close" w="1em" h="1em" class="text-error" />
                </c-lb.swap.off>
            </c-lb.swap>
            <span class="text-sm">Document 2</span>
        </div>
        <div class="flex items-center gap-2">
            <c-lb.swap class="child-checkbox">
                <c-lb.swap.on>
                    <c-lbi n="rmx.check" w="1em" h="1em" class="text-success" />
                </c-lb.swap.on>
                <c-lb.swap.off>
                    <c-lbi n="rmx.close" w="1em" h="1em" class="text-error" />
                </c-lb.swap.off>
            </c-lb.swap>
            <span class="text-sm">Document 3</span>
        </div>
    </div>

    <!-- Status display -->
    <div class="text-xs text-base-content/70 mt-4 p-3 bg-base-100 rounded border">
        <p><strong>Status:</strong> <span id="status-text">No files selected</span></p>
        <p><strong>Parent state:</strong> <span id="parent-state">Unchecked</span></p>
    </div>

    <!-- JavaScript to handle the logic -->
    <script>
        document.addEventListener('DOMContentLoaded', function() {
            const parentCheckbox = document.querySelector('#parent-swap input[type="checkbox"]');
            const childCheckboxes = document.querySelectorAll('.child-checkbox input[type="checkbox"]');
            const statusText = document.getElementById('status-text');
            const parentState = document.getElementById('parent-state');

            // Function to update parent checkbox state
            function updateParentState() {
                const checkedCount = Array.from(childCheckboxes).filter(cb => cb.checked).length;
                const totalCount = childCheckboxes.length;

                // Update status text
                if (checkedCount === 0) {
                    statusText.textContent = 'No files selected';
                    parentState.textContent = 'Unchecked';
                    parentCheckbox.checked = false;
                    parentCheckbox.indeterminate = false;
                } else if (checkedCount === totalCount) {
                    statusText.textContent = 'All files selected';
                    parentState.textContent = 'Checked';
                    parentCheckbox.checked = true;
                    parentCheckbox.indeterminate = false;
                } else {
                    statusText.textContent = `${checkedCount} of ${totalCount} files selected`;
                    parentState.textContent = 'Indeterminate';
                    parentCheckbox.checked = false;
                    parentCheckbox.indeterminate = true;
                }
            }

            // Add event listeners to child checkboxes
            childCheckboxes.forEach(checkbox => {
                checkbox.addEventListener('change', updateParentState);
            });

            // Add event listener to parent checkbox
            parentCheckbox.addEventListener('change', function() {
                const isChecked = this.checked;
                childCheckboxes.forEach(checkbox => {
                    checkbox.checked = isChecked;
                });
                updateParentState();
            });

            // Initialize state
            updateParentState();
        });
    </script>
</div>

Note: JavaScript is required to set the checkbox's indeterminate property. Example:

document.querySelector('input[type="checkbox"]').indeterminate = true;

API Reference

c-lb.swap

Property Type Default Required Description
class string - No Additional CSS classes to apply
effect
rotate flip
- No Animation effect for the swap transition
checked boolean False No Whether the checkbox is initially checked
disabled boolean False No Whether the swap is disabled
inputClass string - No CSS classes to apply to the checkbox input element
name string - No Name attribute for the checkbox input
value string - No Value attribute for the checkbox input

c-lb.swap.on

Property Type Default Required Description
class string - No Additional CSS classes to apply
icon string - No Icon name to display
↳ .fill modifier - No Render in filled (solid) style
icon.class string - No Additional CSS classes for the icon

c-lb.swap.off

Property Type Default Required Description
class string - No Additional CSS classes to apply
icon string - No Icon name to display
↳ .fill modifier - No Render in filled (solid) style
icon.class string - No Additional CSS classes for the icon

c-lb.swap.indeterminate

Property Type Default Required Description
class string - No Additional CSS classes to apply
icon string - No Icon name to display
↳ .fill modifier - No Render in filled (solid) style
icon.class string - No Additional CSS classes for the icon