UNPKG

@sandlada/vue-mdc

Version:

![Vue MDC Logo](https://raw.githubusercontent.com/sandlada/vue-mdc/refs/heads/main/docs/vue-mdc-cover.png)

143 lines (142 loc) 3.79 kB
/** * @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