@sandlada/vue-mdc
Version:

179 lines (178 loc) • 6.3 kB
JavaScript
/**
* @license
* Copyright 2025 Sandlada & Kai Orion
* SPDX-License-Identifier: MIT
*/
import { defineComponent, ref, computed, onMounted, onBeforeUnmount, watch, createVNode } from "vue";
import { useReflectAttribute } from "../../node_modules/@glare-labs/vue-reflect-attribute/build/vue-reflect-attribute.js";
import { isServer } from "../../utils/is-server.js";
import { generateUuid } from "../../utils/uuid.js";
import { setupDispatchHooks, afterDispatch } from "../../internals/events/dispatch-hooks.js";
import { isActivationClick, dispatchActivationClick } from "../../internals/events/form-label-activation.js";
import { redispatchEvent } from "../../internals/events/redispatch-event.js";
import "../../internals/navigation/render-navigation-destination.js";
import { componentNamePrefix } from "../../internals/component-name-prefix/component-name-prefix.js";
import { FocusRing } from "../focus-ring/focus-ring.js";
import { Ripple } from "../ripple/ripple.js";
import css from "./styles/switch.module.scss.js";
import { props } from "./switch.definition.js";
const SwitchOnIcon = () => createVNode("div", {
"class": [css.icon, css["icon--on"]]
}, [createVNode("svg", {
"viewBox": "0 0 24 24"
}, [createVNode("path", {
"d": "M9.55 18.2 3.65 12.3 5.275 10.675 9.55 14.95 18.725 5.775 20.35 7.4Z"
}, null)])]);
const SwitchOffIcon = () => createVNode("div", {
"class": [css.icon, css["icon--off"]]
}, [createVNode("svg", {
"viewBox": "0 0 24 24"
}, [createVNode("path", {
"d": "M6.4 19.2 4.8 17.6 10.4 12 4.8 6.4 6.4 4.8 12 10.4 17.6 4.8 19.2 6.4 13.6 12 19.2 17.6 17.6 19.2 12 13.6Z"
}, null)])]);
const Switch = /* @__PURE__ */ defineComponent({
name: `${componentNamePrefix}-switch`,
props,
slots: {},
emits: ["update:modelValue", "change"],
setup(props2, {
emit,
slots
}) {
const root = ref(null);
const input = ref(null);
const ripple = ref(null);
const focusRing = ref(null);
const _selected = ref(props2.defaultSelected);
const selected = computed({
get: () => _selected.value,
set: (value) => {
_selected.value = value;
emit("update:modelValue", value);
}
});
const _disabled = ref(props2.disabled);
const _withIcon = ref(props2.withIcon);
const _withIconSelectedOnly = ref(props2.withIconSelectedOnly);
const _value = computed(() => selected.value ? "on" : "off");
useReflectAttribute(root, {
attributes: [{
attribute: "disabled",
ref: _disabled,
reflect: true,
type: "boolean"
}, {
attribute: "selected",
ref: _selected,
reflect: true,
type: "boolean"
}, {
attribute: "with-icon",
ref: _withIcon,
reflect: true,
type: "boolean"
}, {
attribute: "with-icon-selected-only",
ref: _withIconSelectedOnly,
reflect: true,
type: "boolean"
}, {
attribute: "value",
ref: _value,
reflect: false,
type: "string"
}]
});
const handleClick = (event) => {
if (!isActivationClick(event) || !input.value) {
return;
}
root.value?.focus();
if (input.value) {
dispatchActivationClick(input.value);
}
};
const handleKeydown = (event) => {
afterDispatch(event, () => {
const ignoreEvent = event.defaultPrevented || event.key !== "Enter";
if (ignoreEvent || _disabled.value || !input.value) {
return;
}
input.value.click();
});
};
const handleInput = (event) => {
const target = event.target;
selected.value = target.checked;
};
const handleChange = (event) => {
redispatchEvent(root.value, event);
};
onMounted(() => {
if (isServer() || !root.value) {
return;
}
const innerId = `switch-${generateUuid()}`;
input.value?.setAttribute("id", innerId);
focusRing.value?.$el.setAttribute("for", innerId);
ripple.value?.$el.setAttribute("for", innerId);
setupDispatchHooks(root.value, "keydown");
root.value.addEventListener("click", handleClick);
root.value.addEventListener("keydown", handleKeydown);
});
onBeforeUnmount(() => {
root.value?.removeEventListener("click", handleClick);
root.value?.removeEventListener("keydown", handleKeydown);
});
watch(() => props2.modelValue, (newValue, oldValue) => {
selected.value = newValue;
}, {
immediate: false
});
return () => {
const renderIcon = createVNode("span", {
"class": [css.handle, (_withIconSelectedOnly.value || _withIcon.value) && css["with-icon"]]
}, [(_withIconSelectedOnly.value || _withIcon.value) && createVNode("div", {
"class": css.icons
}, [_selected.value && (slots["on-icon"] ? slots["on-icon"]() : createVNode(SwitchOnIcon, null, null)), !_withIconSelectedOnly.value && !_selected.value && (slots["off-icon"] ? slots["off-icon"]() : createVNode(SwitchOffIcon, null, null))])]);
return createVNode("div", {
"class": [css.switch, _disabled.value && css.disabled, _selected.value ? css.selected : css.unselected],
"role": "switch",
"aria-disabled": _disabled.value,
"data-component": "switch",
"ref": root
}, [createVNode(FocusRing, {
"ref": focusRing,
"shapeInherit": false
}, null), createVNode("input", {
"type": "checkbox",
"role": "switch",
"checked": _selected.value,
"disabled": _disabled.value,
"aria-disabled": _disabled.value,
"class": "need-inner-id",
"onInput": handleInput,
"onChange": handleChange,
"ref": input
}, null), createVNode("span", {
"aria-hidden": "true",
"class": css.background
}, null), createVNode("span", {
"aria-hidden": "true",
"class": css.outline
}, null), createVNode("span", {
"class": css.track,
"aria-hidden": "true"
}, [createVNode("span", {
"class": css["handle-container"]
}, [createVNode(Ripple, {
"ref": ripple
}, null), renderIcon])])]);
};
},
inheritAttrs: true
});
export {
Switch
};
//# sourceMappingURL=switch.js.map