@sandlada/vue-mdc
Version:

143 lines (142 loc) • 3.79 kB
JavaScript
/**
* @license
* Copyright 2025 Sandlada & Kai Orion
* SPDX-License-Identifier: MIT
*/
import { ref, computed, onMounted, onBeforeUnmount, watch } from "vue";
import { isServer } from "../../utils/is-server.js";
const SAttachableController = Symbol("attachableController");
function useAttachable(hostRef, onControlChange) {
const currentControl = ref(null);
const forAttribute = ref(null);
const isAttached = ref(false);
let observer = null;
const htmlFor = computed({
get: () => forAttribute.value,
set: (value) => {
const host = hostRef.value;
if (!host) {
console.warn("useAttachable: Cannot set htmlFor, host element ref is null.");
return;
}
isAttached.value = false;
if (value === null) {
if (host.hasAttribute("for")) {
host.removeAttribute("for");
}
} else {
if (host.getAttribute("for") !== value) {
host.setAttribute("for", value);
}
}
}
});
const hostComputed = computed(() => {
const _host = hostRef.value;
if (!_host) {
return null;
}
return _host;
});
const control = computed(() => {
const host = hostRef.value;
if (!host) {
return null;
}
if (isAttached.value) {
return currentControl.value;
}
const htmlForValue = forAttribute.value;
if (htmlForValue !== null) {
if (htmlForValue === "" || !host.isConnected) {
return null;
}
return host.getRootNode().querySelector(`#${htmlForValue} `);
}
return host.parentElement;
});
const attach = (controlElement) => {
const host = hostRef.value;
if (!host) {
console.warn("useAttachable: Cannot attach, host element ref is null.");
return;
}
if (isAttached.value && currentControl.value === controlElement) {
if (host.hasAttribute("for")) {
host.removeAttribute("for");
}
return;
}
isAttached.value = true;
currentControl.value = controlElement;
if (host.hasAttribute("for")) {
host.removeAttribute("for");
}
};
const detach = () => {
const host = hostRef.value;
if (!host) {
console.warn("useAttachable: Cannot detach, host element ref is null.");
return;
}
isAttached.value = false;
currentControl.value = null;
if (host.getAttribute("for") !== "") {
host.setAttribute("for", "");
}
};
onMounted(() => {
if (isServer()) {
return;
}
const host = hostRef.value;
if (!host) {
console.error("useAttachable: Host element ref is null on mount. Attachable logic will not function.");
return;
}
forAttribute.value = host.getAttribute("for") ?? null;
observer = new MutationObserver((records) => {
for (const record of records) {
const targetElement = record.target;
const newHtmlFor = targetElement.getAttribute("for") ?? null;
forAttribute.value = newHtmlFor;
if (isAttached.value && newHtmlFor !== null) {
isAttached.value = false;
currentControl.value = null;
}
}
});
observer.observe(host, { attributeFilter: ["for"] });
host[SAttachableController] = {
detach,
attach,
htmlFor,
control,
host: hostComputed,
hostRef
};
});
onBeforeUnmount(() => {
observer?.disconnect();
observer = null;
detach();
});
watch(control, (newControl, prevControl) => {
if (prevControl !== newControl) {
onControlChange(prevControl, newControl);
}
}, { immediate: true });
return {
htmlFor,
control,
host: hostComputed,
hostRef,
attach,
detach
};
}
export {
SAttachableController,
useAttachable
};
//# sourceMappingURL=use-attachable.js.map