Add a toggle component

This commit is contained in:
Oliver 2026-03-22 23:55:55 -06:00
parent 92553cb1f1
commit 6e2dfa1cf1
5 changed files with 165 additions and 3 deletions

View file

@ -26,10 +26,13 @@ export function StyledShadowElement(Base) {
/** @type {ShadowRoot} */
_shadow;
constructor() {
constructor({focusable = false} = {}) {
super();
this._shadow = this.attachShadow({ mode: `open` });
this._shadow = this.attachShadow({
mode: `open`,
delegatesFocus: focusable,
});
this._style = document.createElement(`style`);
this._shadow.appendChild(this._style);
};

View file

@ -0,0 +1,97 @@
import { StyledShadowElement } from "./StyledShadowElement.mjs";
export class TafToggle extends StyledShadowElement(HTMLElement) {
static elementName = `taf-toggle`;
static _stylePath = `toggle.css`;
_mounted;
_internals;
constructor() {
super({ focusable: true });
this._internals = this.attachInternals();
this._internals.role = `checkbox`;
};
get type() {
return `checkbox`;
};
get name() {
return this.getAttribute(`name`);
};
set name(newName) {
this.setAttribute(`name`, newName);
};
get value() {
return this._input.value;
};
set value(newValue) {
this._input.value = newValue;
};
get checked() {
return this.hasAttribute(`checked`);
};
set checked(newValue) {
if (typeof newValue !== `boolean`) { return };
this.toggleAttribute(`checked`, newValue);
};
get disabled() {
return this.matches(`:disabled`);
};
set disabled(value) {
this.toggleAttribute(`disabled`, value);
};
get editable() {
return true;
};
connectedCallback() {
super.connectedCallback();
if (this._mounted) { return };
this._internals.checked = this.checked;
/*
This converts all of the double-dash prefixed properties on the
element to CSS variables so that they don't all need to be
provided by doing style=""
*/
for (const attrVar of this.attributes) {
if (attrVar.name?.startsWith(`var:`)) {
const prop = attrVar.name.replace(`var:`, ``);
this.style.setProperty(`--` + prop, attrVar.value);
};
};
const label = document.createElement(`label`);
label.dataset.type = `round`;
const input = document.createElement(`input`);
input.type = `checkbox`;
input.toggleAttribute(`switch`, true);
input.checked = this.checked;
label.appendChild(input);
const slider = document.createElement(`div`);
slider.classList = `slider`;
label.appendChild(slider);
this._shadow.appendChild(label);
this._mounted = true;
};
disconnectedCallback() {
super.disconnectedCallback();
if (!this._mounted) { return };
this._mounted = false;
};
};

View file

@ -1,10 +1,12 @@
import { Logger } from "../../utils/Logger.mjs";
import { TafIcon } from "./Icon.mjs";
import { TafSVGLoader } from "./svgLoader.mjs";
import { TafToggle } from "./Toggle.mjs";
const components = [
TafSVGLoader,
TafIcon,
TafToggle,
];
export function registerCustomComponents() {

View file

@ -0,0 +1,55 @@
:host {
display: block;
}
input {
width: 0;
height: 0;
margin: 0;
padding: 0;
}
.slider {
width: var(--size, 16px);
height: var(--size, 16px);
background: var(
--slider-colour,
var(--toggle-slider-unchecked-colour)
);
transition: all var(--speed, 150ms) ease-in-out;
border-radius: 9999px;
}
label {
display: flex;
padding: var(--padding, 4px);
height: calc(var(--size, 16px) + (var(--padding, 4px) * 2));
width: calc((var(--size, 16px) * 2) + (var(--padding, 4px) * 2));
border-radius: 9999px;
background: var(
--toggle-background,
var(--toggle-background-colour)
);
box-sizing: border-box;
cursor: pointer;
/* Non-checked, clicking */
&:active .slider {
width: calc(var(--size, 16px) * 1.5);
}
/* checked, non-clicking */
& > :checked + .slider {
transform: translateX(var(--size, 16px));
background: var(
--slider-checked-colour,
var(--toggle-slider-checked-colour)
);
}
/* checked, clicking */
&:active > :checked + .slider {
width: calc(var(--size, 16px) * 1.5);
transform: translateX(calc(var(--size, 16px) * 0.5));
}
}

View file

@ -4,6 +4,10 @@
--spinner-outer-colour: white;
--spinner-inner-colour: #FF3D00;
--toggle-background-colour: #171e26;
--toggle-slider-unchecked-colour: maroon;
--toggle-slider-checked-colour: green;
--tab-button-active-border: rebeccapurple;
--tab-button-hover-bg: var(--color-cool-3);
@ -19,7 +23,8 @@
--item-card-header-background: #242d38;
--item-card-header-color: white;
--item-card-header-input-background: #2b3642;
--item-card-header-input-color: white;
--item-card-header-input-colour: white;
--item-card-header-disabled-input-colour: gray;
/* Chip Variables */
--chip-color: #fff7ed;