204 lines
6.7 KiB
JavaScript
204 lines
6.7 KiB
JavaScript
const { CSS_CLASSES } = window;
|
|
|
|
const ICON_PATHS = {
|
|
checked: [
|
|
"M7.89561 14.8538L6.30462 13.2629L14.3099 5.25755L15.9009 6.84854L7.89561 14.8538Z",
|
|
"M4.08643 11.0903L5.67742 9.49929L9.4485 13.2704L7.85751 14.8614L4.08643 11.0903Z"
|
|
],
|
|
unchecked: [
|
|
"M5.13231 6.72963L6.7233 5.13864L14.855 13.2704L13.264 14.8614L5.13231 6.72963Z",
|
|
"M13.2704 5.13864L14.8614 6.72963L6.72963 14.8614L5.13864 13.2704L13.2704 5.13864Z"
|
|
]
|
|
};
|
|
|
|
const CHECKED_COLOR = "hsl(139, 47.3%, 43.9%)";
|
|
const UNCHECKED_COLOR = "hsl(218, 4.6%, 46.9%)";
|
|
|
|
function createSwitch(
|
|
labelText,
|
|
descriptionText,
|
|
inputId,
|
|
defaultChecked = false,
|
|
onToggle = null
|
|
) {
|
|
let isToggling = false;
|
|
|
|
const container = document.createElement("div");
|
|
container.className = CSS_CLASSES.container;
|
|
|
|
const checkbox = document.createElement("input");
|
|
checkbox.id = inputId;
|
|
checkbox.type = "checkbox";
|
|
checkbox.className = CSS_CLASSES.input;
|
|
checkbox.tabIndex = 0;
|
|
checkbox.checked = defaultChecked;
|
|
|
|
const sliderSVG = createSliderSVG(defaultChecked);
|
|
|
|
const switchControl = document.createElement("div");
|
|
switchControl.className = `${CSS_CLASSES.switchControl} ${defaultChecked ? CSS_CLASSES.switchControlChecked : ""}`;
|
|
switchControl.style.opacity = "1";
|
|
switchControl.style.backgroundColor = defaultChecked ? CHECKED_COLOR : UNCHECKED_COLOR;
|
|
switchControl.style.cursor = "pointer";
|
|
switchControl.tabIndex = -1;
|
|
switchControl.appendChild(sliderSVG);
|
|
switchControl.appendChild(checkbox);
|
|
|
|
const controlWrapper = document.createElement("div");
|
|
controlWrapper.className = CSS_CLASSES.control;
|
|
controlWrapper.appendChild(switchControl);
|
|
|
|
const label = document.createElement("label");
|
|
label.htmlFor = inputId;
|
|
label.className = CSS_CLASSES.label;
|
|
label.textContent = labelText;
|
|
|
|
const labelRow = document.createElement("div");
|
|
labelRow.className = CSS_CLASSES.labelRow;
|
|
labelRow.appendChild(label);
|
|
labelRow.appendChild(controlWrapper);
|
|
|
|
container.appendChild(labelRow);
|
|
|
|
if (descriptionText) {
|
|
const description = document.createElement("div");
|
|
description.className = CSS_CLASSES.description;
|
|
description.textContent = descriptionText;
|
|
|
|
const noteWrapper = document.createElement("div");
|
|
noteWrapper.className = CSS_CLASSES.note;
|
|
noteWrapper.appendChild(description);
|
|
container.appendChild(noteWrapper);
|
|
}
|
|
|
|
const divider = document.createElement("div");
|
|
divider.className = CSS_CLASSES.divider;
|
|
container.appendChild(divider);
|
|
|
|
const updateSwitch = (checked) => {
|
|
switchControl.classList.toggle(CSS_CLASSES.switchControlChecked, checked);
|
|
switchControl.style.backgroundColor = checked ? CHECKED_COLOR : UNCHECKED_COLOR;
|
|
sliderSVG.style.left = checked ? "12px" : "-3px";
|
|
updateSVGIcon(sliderSVG, checked);
|
|
};
|
|
|
|
const handleToggle = async () => {
|
|
if (isToggling) return;
|
|
isToggling = true;
|
|
|
|
const prev = !checkbox.checked;
|
|
|
|
if (typeof onToggle === "function") {
|
|
try {
|
|
const ret = await onToggle(checkbox.checked);
|
|
if (ret === false) checkbox.checked = prev;
|
|
} finally {
|
|
isToggling = false;
|
|
}
|
|
} else {
|
|
isToggling = false;
|
|
}
|
|
|
|
updateSwitch(checkbox.checked);
|
|
};
|
|
|
|
switchControl.addEventListener("click", (e) => {
|
|
if (isToggling) return;
|
|
if (e.target === checkbox) return;
|
|
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
checkbox.checked = !checkbox.checked;
|
|
checkbox.dispatchEvent(new Event("change", { bubbles: true }));
|
|
});
|
|
|
|
checkbox.addEventListener("click", (e) => {
|
|
if (isToggling) {
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
});
|
|
|
|
checkbox.addEventListener("change", handleToggle);
|
|
|
|
return container;
|
|
}
|
|
|
|
function createSliderSVG(isChecked) {
|
|
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
svg.setAttribute("class", CSS_CLASSES.slider);
|
|
svg.setAttribute("viewBox", "0 0 28 20");
|
|
svg.setAttribute("preserveAspectRatio", "xMinYMid meet");
|
|
svg.style.left = isChecked ? "12px" : "-3px";
|
|
svg.style.position = "absolute";
|
|
svg.style.transition = "left 0.2s";
|
|
|
|
const circle = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
|
circle.setAttribute("fill", "white");
|
|
circle.setAttribute("x", "4");
|
|
circle.setAttribute("y", "0");
|
|
circle.setAttribute("height", "20");
|
|
circle.setAttribute("width", "20");
|
|
circle.setAttribute("rx", "10");
|
|
svg.appendChild(circle);
|
|
|
|
const icon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
icon.setAttribute("viewBox", "0 0 20 20");
|
|
icon.setAttribute("fill", "none");
|
|
|
|
const color = isChecked ? CHECKED_COLOR : UNCHECKED_COLOR;
|
|
|
|
if (isChecked) {
|
|
const checkPaths = [
|
|
"M7.89561 14.8538L6.30462 13.2629L14.3099 5.25755L15.9009 6.84854L7.89561 14.8538Z",
|
|
"M4.08643 11.0903L5.67742 9.49929L9.4485 13.2704L7.85751 14.8614L4.08643 11.0903Z"
|
|
];
|
|
checkPaths.forEach(d => {
|
|
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
path.setAttribute("fill", color);
|
|
path.setAttribute("d", d);
|
|
icon.appendChild(path);
|
|
});
|
|
} else {
|
|
const xPaths = [
|
|
"M5.13231 6.72963L6.7233 5.13864L14.855 13.2704L13.264 14.8614L5.13231 6.72963Z",
|
|
"M13.2704 5.13864L14.8614 6.72963L6.72963 14.8614L5.13864 13.2704L13.2704 5.13864Z"
|
|
];
|
|
xPaths.forEach(d => {
|
|
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
path.setAttribute("fill", color);
|
|
path.setAttribute("d", d);
|
|
icon.appendChild(path);
|
|
});
|
|
}
|
|
|
|
svg.appendChild(icon);
|
|
return svg;
|
|
}
|
|
|
|
function createSVGIcon(parentSVG, isChecked) {
|
|
const color = isChecked ? CHECKED_COLOR : UNCHECKED_COLOR;
|
|
const paths = ICON_PATHS[isChecked ? "checked" : "unchecked"];
|
|
|
|
const icon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
icon.setAttribute("viewBox", "0 0 20 20");
|
|
icon.setAttribute("fill", "none");
|
|
|
|
paths.forEach(d => {
|
|
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
path.setAttribute("fill", color);
|
|
path.setAttribute("d", d);
|
|
icon.appendChild(path);
|
|
});
|
|
|
|
parentSVG.appendChild(icon);
|
|
}
|
|
|
|
function updateSVGIcon(sliderSVG, isChecked) {
|
|
const oldIcon = sliderSVG.querySelector("svg svg");
|
|
if (oldIcon) {
|
|
oldIcon.remove();
|
|
}
|
|
createSVGIcon(sliderSVG, isChecked);
|
|
} |