@stimulus-library/mixins
Version:
A library of useful controllers for Stimulus
181 lines (180 loc) • 6.39 kB
JavaScript
import { useMixin } from "./use_mixin";
import { getOtherRadiosInGroup, isElementCheckable, isHTMLInputElement, isHTMLSelectElement } from "@stimulus-library/utilities";
import { useEventListener } from "./use_event_listener";
const CACHE_ATTR_NAME = "data-detect-dirty-load-value";
export function useDirtyInputTracking(controller, element) {
const setup = () => {
cacheLoadValues(element);
checkDirty(element);
useEventListener(controller, element, ["input", "change"], () => checkDirty(element), { debounce: 10 });
};
const teardown = () => {
};
useMixin(controller, setup, teardown);
return {
restore: () => restoreElementFromLoadValue(element),
teardown,
};
}
export function useDirtyFormTracking(controller, form) {
const teardowns = [];
const restores = [];
const setup = () => {
form.querySelectorAll("input, select, textarea").forEach((element) => {
const functions = useDirtyInputTracking(controller, element);
teardowns.push(functions.teardown);
restores.push(functions.restore);
});
useEventListener(controller, form, "input-dirtied", () => {
form.setAttribute("data-dirty", "true");
form.dispatchEvent(new CustomEvent("form-dirtied", {
bubbles: true,
cancelable: true,
detail: {
target: form,
},
}));
});
useEventListener(controller, form, "input-cleaned", () => {
if (form.querySelectorAll("[data-dirty=\"true\"]").length === 0) {
form.removeAttribute("data-dirty");
form.dispatchEvent(new CustomEvent("form-cleaned", {
bubbles: true,
cancelable: true,
detail: {
target: form,
},
}));
}
});
};
const teardown = () => {
teardowns.forEach((teardown) => teardown());
};
const restore = () => {
restores.forEach((restore) => restore());
};
useMixin(controller, setup, teardown);
return {
restore,
teardown,
};
}
function checkDirty(element) {
var _a;
if (isHTMLInputElement(element) && element.type == "radio") {
getOtherRadiosInGroup(element).forEach((radio) => radio.removeAttribute("data-dirty"));
}
if (isElementDirty(element)) {
element.setAttribute("data-dirty", "true");
(_a = element.form) === null || _a === void 0 ? void 0 : _a.setAttribute("data-dirty", "true");
element.dispatchEvent(new CustomEvent("input-dirtied", {
bubbles: true,
cancelable: true,
detail: {
target: element,
},
}));
}
else {
element.removeAttribute("data-dirty");
element.dispatchEvent(new CustomEvent("input-cleaned", {
bubbles: true,
cancelable: true,
detail: {
target: element,
},
}));
}
}
export function getElementValue(element) {
if (isElementCheckable(element)) {
return element.checked;
}
else if (isHTMLSelectElement(element) && element.multiple) {
return JSON.stringify(getMultiSelectValues(element));
}
else {
return element.value;
}
}
export function getMultiSelectLoadValues(element) {
let options = Array.from(element.options);
options = options.filter(option => option.defaultSelected);
return options.map(option => option.value);
}
export function getMultiSelectValues(element) {
let options = Array.from(element.selectedOptions);
return options.map(option => option.value);
}
export function getElementLoadValue(element) {
const value = element.getAttribute(CACHE_ATTR_NAME);
if (isElementCheckable(element)) {
return value == null ? element.defaultChecked : value == "true";
}
else if (isHTMLSelectElement(element) && value == null) {
const options = Array.from(element.options);
if (element.multiple) {
return JSON.stringify(getMultiSelectLoadValues(element));
}
options.forEach((option) => {
if (option.defaultSelected) {
return option.value;
}
});
}
else if (value !== null) {
return value;
}
return value;
}
export function elementHasCachedLoadValue(element) {
return element.hasAttribute(CACHE_ATTR_NAME);
}
export function isElementDirty(element) {
return getElementValue(element) !== getElementLoadValue(element);
}
export function restoreElementFromLoadValue(element) {
const cacheValue = element.getAttribute(CACHE_ATTR_NAME);
if (isElementCheckable(element)) {
element.setAttribute(CACHE_ATTR_NAME, element.checked.toString());
element.checked = cacheValue == null ? element.defaultChecked : cacheValue == "true";
}
else if (isHTMLSelectElement(element)) {
if (cacheValue == null) {
const options = Array.from(element.options);
options.forEach((option) => {
if (option.defaultSelected) {
option.selected = true;
}
});
}
else {
element.multiple ? restoreMultiSelect(element, cacheValue) : element.value = cacheValue;
}
}
else {
element.value = cacheValue == null ? element.defaultValue : cacheValue;
}
checkDirty(element);
}
export function restoreMultiSelect(element, cacheValue) {
let selectedOptions = JSON.parse(cacheValue);
Array.from(element.options).forEach((option) => option.selected = selectedOptions.includes(option.value));
}
export function cacheLoadValues(element) {
if (isElementCheckable(element)) {
if (!elementHasCachedLoadValue(element)) {
element.setAttribute(CACHE_ATTR_NAME, element.checked.toString());
}
}
else if (isHTMLSelectElement(element) && element.multiple) {
element.setAttribute(CACHE_ATTR_NAME, JSON.stringify(getMultiSelectLoadValues(element)));
}
else {
element.setAttribute(CACHE_ATTR_NAME, element.value.toString());
}
}
export function isDirty(element) {
return element.hasAttribute("data-dirty");
}