169 lines
4.7 KiB
JavaScript
169 lines
4.7 KiB
JavaScript
// Eryn Wells <eryn@erynwells.me>
|
|
|
|
class RubySwitch extends HTMLElement {
|
|
static controlSizeInPixels = 32;
|
|
static thumbTransitionDuration = 0.1;
|
|
|
|
static settings = [
|
|
{
|
|
id: "ruby-switch-none",
|
|
value: "none",
|
|
label: "あ"
|
|
},
|
|
{
|
|
id: "ruby-switch-both",
|
|
value: "both",
|
|
label: "<ruby>あ<rt>a</rt></ruby>",
|
|
default: true
|
|
},
|
|
{
|
|
id: "ruby-switch-hidden",
|
|
value: "hidden",
|
|
label: "ab"
|
|
},
|
|
];
|
|
|
|
#root;
|
|
#thumb;
|
|
|
|
constructor() {
|
|
super();
|
|
|
|
this.#updateValue(RubySwitch.settings.find(obj => obj.default).value);
|
|
this.addEventListener("RubyStyleChanged", event => {
|
|
this.#updateValue(event.detail.style);
|
|
});
|
|
|
|
this.#root = this.attachShadow({ mode: "closed" });
|
|
this.#buildShadowDOM();
|
|
|
|
this.#updateThumbPosition(this.#root.querySelector(".control[data-default]"));
|
|
}
|
|
|
|
#updateValue(style) {
|
|
this.setAttribute("value", style);
|
|
}
|
|
|
|
get #stylesheet() {
|
|
const controlSize = RubySwitch.controlSizeInPixels;
|
|
const halfControlSize = controlSize / 2;
|
|
|
|
return `
|
|
#ruby-controls {
|
|
box-sizing: border-box;
|
|
display: inline-block;
|
|
position: relative;
|
|
}
|
|
|
|
#controls {
|
|
border: none;
|
|
box-sizing: border-box;
|
|
display: inline grid;
|
|
grid-template-columns: repeat(3, ${controlSize}px);
|
|
margin: 0;
|
|
overflow: none;
|
|
padding: 0;
|
|
}
|
|
|
|
#thumb {
|
|
box-sizing: border-box;
|
|
box-shadow: 2px 2px 6px #ccc;
|
|
border: 0.5px solid #aaa;
|
|
border-radius: ${halfControlSize}px;
|
|
position: absolute;
|
|
top: 0;
|
|
height: ${controlSize}px;
|
|
width: ${controlSize}px;
|
|
z-index: 50;
|
|
transition: left ${RubySwitch.thumbTransitionDuration}s;
|
|
}
|
|
|
|
.control {
|
|
aspect-ratio: 1;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
height: ${controlSize}px;
|
|
width: ${controlSize}px;
|
|
}
|
|
|
|
b {
|
|
font-weight: normal;
|
|
cursor: pointer;
|
|
}
|
|
|
|
label {
|
|
z-index: 2;
|
|
text-align: center;
|
|
}
|
|
|
|
#ruby-switch-both {
|
|
}
|
|
`;
|
|
}
|
|
|
|
#buildShadowDOM() {
|
|
const root = this.#root;
|
|
|
|
const style = document.createElement("style");
|
|
style.textContent = this.#stylesheet;
|
|
root.appendChild(style);
|
|
|
|
let container = document.createElement("div");
|
|
container.id = "ruby-controls";
|
|
root.appendChild(container);
|
|
|
|
let controls = document.createElement("div");
|
|
controls.id = "controls";
|
|
container.appendChild(controls);
|
|
|
|
for (const desc of RubySwitch.settings) {
|
|
let control = document.createElement("div");
|
|
control.classList.add("control")
|
|
control.id = desc.id;
|
|
|
|
control.dataset.value = desc.value;
|
|
if (desc.default) {
|
|
control.dataset.default = "";
|
|
}
|
|
|
|
controls.appendChild(control);
|
|
|
|
control.addEventListener("click", event => {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
|
|
this.#updateThumbPosition(event.currentTarget);
|
|
|
|
this.#root.dispatchEvent(new CustomEvent("RubyStyleChanged", {
|
|
bubbles: true,
|
|
composed: true,
|
|
detail: {
|
|
style: control.dataset.value,
|
|
},
|
|
}));
|
|
}, { capture: true });
|
|
|
|
const label = document.createElement("b");
|
|
label.innerHTML = desc.label;
|
|
control.appendChild(label);
|
|
}
|
|
|
|
const thumb = document.createElement("div");
|
|
this.#thumb = thumb;
|
|
thumb.id = "thumb";
|
|
container.appendChild(thumb);
|
|
}
|
|
|
|
#updateThumbPosition(selectedControl) {
|
|
const controls = this.#root.querySelector("#controls");
|
|
|
|
const trackBoundingRect = controls.getBoundingClientRect();
|
|
const controlBoundingRect = selectedControl.getBoundingClientRect();
|
|
|
|
const offset = controlBoundingRect.left - trackBoundingRect.left;
|
|
this.#thumb.style.left = `${offset}px`;
|
|
}
|
|
}
|
|
|
|
customElements.define("ruby-switch", RubySwitch);
|