vue3-mq
Version:
Build responsive design into your Vue 3 app
430 lines (429 loc) • 11.9 kB
JavaScript
import { ref as m, reactive as F, readonly as v, computed as u, h as w, TransitionGroup as G, Transition as J, inject as H } from "vue";
const E = m([]), L = m(null), _ = m(null), P = m(null), j = m(null), i = F({
current: ""
}), x = [], U = m(!1), p = v(E), W = v(L), X = v(_), ee = v(P), te = v(j), c = v(i), ne = (e) => {
E.value = e;
}, oe = (e) => {
L.value = e;
}, re = (e) => {
_.value = e;
}, se = (e) => {
P.value = e;
}, ae = (e) => {
j.value = e;
}, A = (e = W.value) => {
i.current = e;
const n = p.value.findIndex(
(t) => t.name === e
), o = p.value.map((t) => t.name);
for (let t = 0; t < o.length; t++) {
if (t > 0 && t < o.length - 1) {
const r = o[t] + "Minus", s = o[t] + "Plus";
i[r] = n <= t, i[s] = n >= t;
}
i[o[t]] = o[t] === e;
}
}, ie = () => {
const e = Object.keys(i);
for (let n of e)
delete i[n];
A(), D(), z(), K();
}, D = (e = X.value) => {
i.orientation = e, i.isLandscape = e === "landscape", i.isPortrait = e === "portrait";
}, z = (e = ee.value || "light") => {
i.theme = e, i.isDark = e === "dark", i.isLight = e === "light";
}, K = (e = te.value || "no-preference") => {
i.motionPreference = e, i.isMotion = e === "no-preference", i.isInert = e === "reduce";
};
function le() {
for (; x.length > 0; ) {
const e = x.shift();
if (e && typeof e == "object") {
const { mql: n, cb: o } = e;
n.addEventListener && typeof n.addEventListener == "function" ? n.removeEventListener("change", o) : n.removeListener(o);
}
}
}
function ue() {
return p.value.reduce(
(n, o, t, r) => {
const s = `(min-width: ${o.min}px)`, a = t < r.length - 1 ? `(max-width: ${r[t + 1].min - 1}px)` : null, l = s + (a ? " and " + a : "");
return Object.assign(n, {
[o.name]: l
});
},
{}
);
}
function k(e, n) {
if (typeof window > "u" || !window.matchMedia) return !1;
if (typeof window < "u" && !window.matchMedia)
return console.error(
"Vue3 Mq: No MatchMedia support detected in this browser. Responsive breakpoints not available."
), !1;
{
U.value = !0;
const o = window.matchMedia(e), t = ({ matches: s }) => {
s && n();
};
x.push({ mql: o, cb: t }), o.addEventListener && typeof o.addEventListener == "function" ? o.addEventListener("change", t) : o.addListener(t), t(o);
}
}
const f = (e) => p.value.some(
(n) => n.name === e
), S = (e, n) => {
const o = n.value.map((t) => t.name);
if (e) {
if (Array.isArray(e))
return e.filter((t) => f(t));
if (typeof e == "string" && /\w+\+$/.test(e)) {
if (e = e.replace(/\+$/, ""), f(e) === !1)
return console.error(
`Vue3 Mq: ${e} is not a valid breakpoint key. Invalid range.`
), o;
const t = n.value.findIndex((r) => r.name === e);
return n.value.slice(t).map((r) => r.name);
} else if (typeof e == "string" && /\w+-$/.test(e)) {
if (e = e.replace(/-$/, ""), f(e) === !1)
return console.error(
`Vue3 Mq: ${e} is not a valid breakpoint key. Invalid range.`
), o;
const t = n.value.findIndex((r) => r.name === e);
return n.value.slice(0, t + 1).map((r) => r.name);
} else if (typeof e == "string" && /^\w+-\w+$/.test(e)) {
const [t, r] = e.split("-");
if (f(t) === !1)
return console.error(
`Vue3 Mq: ${t} is not a valid breakpoint key. Invalid range.`
), o;
if (f(r) === !1)
return console.error(
`Vue3 Mq: ${r} is not a valid breakpoint key. Invalid range.`
), o;
const s = n.value.findIndex((l) => l.name === t), a = n.value.findIndex((l) => l.name === r);
return n.value.slice(s, a + 1).map((l) => l.name);
} else return typeof e == "string" && f(e) === !0 ? [e] : o;
} else return o;
}, $ = (e, n) => {
const o = [];
return !e && !n ? ["landscape", "portrait"] : (e && o.push("landscape"), n && o.push("portrait"), o);
}, R = (e, n) => {
const o = [];
return !n && !e ? ["light", "dark"] : (n && o.push("light"), e && o.push("dark"), o);
}, b = (e, n) => {
const o = [];
return !e && !n ? ["reduce", "no-preference"] : (e && o.push("reduce"), n && o.push("no-preference"), o);
}, ce = {
xs: 0,
sm: 576,
md: 768,
lg: 992,
xl: 1200,
xxl: 1400
}, de = {
xs: 0,
sm: 576,
md: 768,
lg: 992,
xl: 1200
}, fe = {
xs: 0,
sm: 768,
md: 992,
lg: 1200
}, pe = {
xs: 0,
sm: 600,
md: 960,
lg: 1280,
xl: 1920,
xxl: 2560
}, me = {
xs: 0,
sm: 600,
md: 960,
lg: 1264,
xl: 1904
}, ve = {
xs: 0,
sm: 640,
md: 768,
lg: 1024,
xl: 1280,
xxl: 1536
}, he = {
phone: 0,
tablet: 768,
laptop: 1370,
desktop: 1906
}, ge = {
mobile: 0,
small: 600,
medium: 782,
large: 960,
xlarge: 1080,
wide: 1280,
huge: 1440
}, q = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
__proto__: null,
bootstrap3: fe,
bootstrap4: de,
bootstrap5: ce,
devices: he,
tailwind: ve,
vuetify: me,
vuetify3: pe,
wordpress: ge
}, Symbol.toStringTag, { value: "Module" })), ye = (e) => {
if (typeof e == "string" && q[e]) return q[e];
{
const n = Object.keys(q);
return console.error(
`Vue3 Mq: "${e}" is not a valid preset. Available options are: ${n.join(
", "
)}`
), !1;
}
}, ke = (e) => ["landscape", "portrait"].includes(e) === !1 ? (console.error(
`Vue3 Mq: "${e}" is not a valid default orientation. Reverting to unset value.`
), null) : e, Me = (e = null) => ["dark", "light"].includes(e) === !1 && e !== null ? (console.error(
`Vue3 Mq: "${e}" is not a valid default theme. Reverting to unset value.`
), null) : e, we = (e = null) => ["no-preference", "reduce"].includes(e) === !1 && e !== null ? (console.error(
`Vue3 Mq: "${e}" is not a valid default motion preference. Reverting to unset value.`
), null) : e, O = (e) => {
if (!e || typeof e != "object") return !1;
const n = [];
for (let r in e) {
const s = parseFloat(e[r]);
if (!r || typeof r != "string") {
console.warn(
`Vue3 Mq: Invalid or missing breakpoint key (${JSON.stringify(
r
)}). Skipping.`
);
continue;
} else if (/^[^a-z]/i.test(r) || /[^a-zA-Z0-9_]/.test(r)) {
console.warn(
`Vue3 Mq: "${r}" is an invalid breakpoint key. Breakpoint keys must start with a letter and contain only alphanumeric characters and underscores. Skipping.`
);
continue;
} else if (!s && s !== 0 || isNaN(s) || s < 0) {
console.warn(
`Vue3 Mq: "${r}: ${e[r]}" is not a valid breakpoint. Breakpoints should be a number of zero or above. Skipping.`
);
continue;
}
n.push({
name: r,
min: s
});
}
return n.some(
(r) => r.min === 0
) || console.warn(
"Vue3 Mq: You have not declared a breakpoint with a minimum value of 0. There may be screen sizes to which Vue3Mq does not respond."
), new Set(
n.map((r) => r.min)
).size < n.length && console.warn(
"Vue3 Mq: Your breakpoint configuration contains duplicate values. Behaviour may be unpredictable."
), n.length === 0 ? !1 : n.sort((r, s) => r.min - s.min);
}, qe = (e) => {
const n = e.split(":"), o = {};
for (let t of n)
/\D/.test(t) !== !1 && (["landscape", "portrait"].includes(t) ? o.slotOrientation = t : ["light", "dark"].includes(t) ? o.slotTheme = t : ["inert", "motion"].includes(t) ? o.slotMotion = t : o.slotBp = t);
return o;
}, xe = {
name: "fade",
mode: "out-in"
}, Te = {
name: "MqResponsive",
props: {
/**
* Breakpoints to target when rendering component
*/
target: [String, Array],
/**
* Only render in a landscape view
*/
landscape: {
type: Boolean,
default: !1
},
/**
* Only render in a portrait view
*/
portrait: {
type: Boolean,
default: !1
},
/**
* Only render when dark mode is preferred
*/
dark: {
type: Boolean,
default: !1
},
/**
* Only render when light mode is preferred
*/
light: {
type: Boolean,
default: !1
},
/**
* Only render when reduced motion is preferred
*/
inert: {
type: Boolean,
default: !1
},
/**
* Only render when normal motion is preferred
*/
motion: {
type: Boolean,
default: !1
},
/**
* HTML tag to use when rendering
*/
tag: {
type: String,
default: "div"
},
/**
* When in group mode, the HTML tag to use on list items
*/
listTag: {
type: String,
default: "div"
},
/**
* Render items as part of a transition group
*/
group: {
type: Boolean,
default: !1
}
},
setup(e, { attrs: n, emit: o, slots: t }) {
const r = u(() => S(
e.target,
p
)), s = u(() => $(
e.landscape,
e.portrait
)), a = u(() => R(e.dark, e.light)), l = u(() => b(e.inert, e.motion)), h = u(() => r.value.includes(c.current) && s.value.includes(c.orientation) && a.value.includes(c.theme) && l.value.includes(c.motionPreference)), M = (d) => {
if (!e.group && t.length > 0) return t;
const g = [];
for (let y in t) {
const { slotBp: Q, slotOrientation: T, slotTheme: V, slotMotion: B } = qe(y), N = u(() => S(
Q,
p
)), C = u(() => $(
T === "landscape",
T === "portrait"
)), Y = u(() => R(
V === "dark",
V === "light"
)), Z = u(() => b(
B === "inert",
B === "motion"
));
u(() => N.value.includes(
c.current
) && C.value.includes(
c.orientation
) && Y.value.includes(c.theme) && Z.value.includes(
c.motionPreference
)).value === !0 && g.push(
w(
d || t[y],
{ key: y },
d ? t[y]() : void 0
)
);
}
return g.length > 0 ? g : void 0;
};
return t.default ? () => h.value ? w(e.tag, { ...n }, t.default()) : void 0 : () => {
const d = Object.assign(
{},
xe,
n,
{ tag: e.tag }
), g = e.group ? G : J;
return w(
g,
d,
() => M(e.listTag)
);
};
}
};
function I({ breakpoints: e, preset: n }) {
const o = n ? ye(n) : !1, t = e ? O(e) : !1;
if (o === !1 && !t)
throw new TypeError(
"Vue3 Mq: You must provide a valid preset, or valid breakpoint settings."
);
ne(
t || O(o)
), le(), ie();
const r = ue();
for (const s in r) {
const a = r[s];
k(a, () => {
A(s);
});
}
["portrait", "landscape"].forEach((s) => {
const a = () => {
D(s);
};
k(`(orientation: ${s})`, a);
}), ["light", "dark"].forEach((s) => {
const a = () => {
z(s);
};
k(`(prefers-color-scheme: ${s})`, a);
}), ["reduce", "no-preference"].forEach((s) => {
const a = () => {
K(s);
};
k(`(prefers-reduced-motion: ${s})`, a);
});
}
function $e() {
const e = H("mq");
if (e)
return e;
throw new Error(
"Vue3Mq is not installed in this app. Please follow the installation instructions and try again."
);
}
const Ve = (e, {
preset: n = "bootstrap5",
breakpoints: o,
defaultBreakpoint: t,
defaultOrientation: r = "landscape",
defaultMotion: s = "no-preference",
defaultTheme: a,
global: l = !1
} = {}) => {
try {
const h = ke(r), M = Me(a), d = we(s);
oe(t), re(h), se(M), ae(d), e.provide("mq", c), e.provide("updateBreakpoints", I), l === !0 && (e.component("MqResponsive", Te), e.config.globalProperties.$mq = c), I({ breakpoints: o, preset: n });
} catch (h) {
console.error(h);
}
}, Re = {
install: Ve
};
export {
Te as MqResponsive,
Re as Vue3Mq,
p as availableBreakpoints,
I as updateBreakpoints,
$e as useMq
};