forked from mirrors/homebox
127 lines
2.7 KiB
TypeScript
127 lines
2.7 KiB
TypeScript
|
type ColorType = "hsla";
|
||
|
|
||
|
export type VarOptions = {
|
||
|
type: ColorType;
|
||
|
transparency?: number;
|
||
|
apply?: (value: string) => string;
|
||
|
};
|
||
|
|
||
|
export type Breakpoints = {
|
||
|
sm: boolean;
|
||
|
md: boolean;
|
||
|
lg: boolean;
|
||
|
xl: boolean;
|
||
|
xxl: boolean;
|
||
|
};
|
||
|
|
||
|
export function useBreakpoints(): Breakpoints {
|
||
|
const breakpoints: Breakpoints = reactive({
|
||
|
sm: false,
|
||
|
md: false,
|
||
|
lg: false,
|
||
|
xl: false,
|
||
|
xxl: false,
|
||
|
});
|
||
|
|
||
|
const updateBreakpoints = () => {
|
||
|
breakpoints.sm = window.innerWidth < 640;
|
||
|
breakpoints.md = window.innerWidth >= 640;
|
||
|
breakpoints.lg = window.innerWidth >= 768;
|
||
|
breakpoints.xl = window.innerWidth >= 1024;
|
||
|
breakpoints.xxl = window.innerWidth >= 1280;
|
||
|
};
|
||
|
|
||
|
onMounted(() => {
|
||
|
updateBreakpoints();
|
||
|
window.addEventListener("resize", updateBreakpoints);
|
||
|
});
|
||
|
|
||
|
onUnmounted(() => {
|
||
|
window.removeEventListener("resize", updateBreakpoints);
|
||
|
});
|
||
|
|
||
|
return breakpoints;
|
||
|
}
|
||
|
|
||
|
class ThemeObserver {
|
||
|
// eslint-disable-next-line no-use-before-define
|
||
|
private static instance?: ThemeObserver;
|
||
|
private readonly observer: MutationObserver;
|
||
|
|
||
|
private fns: (() => void)[] = [];
|
||
|
|
||
|
private constructor() {
|
||
|
this.observer = new MutationObserver(mutations => {
|
||
|
mutations.forEach(mutation => {
|
||
|
if (mutation.attributeName === "data-theme") {
|
||
|
this.fire();
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
|
||
|
const html = document.querySelector("html");
|
||
|
if (!html) {
|
||
|
throw new Error("No html element found");
|
||
|
}
|
||
|
|
||
|
this.observer.observe(html, { attributes: true });
|
||
|
}
|
||
|
|
||
|
public static getInstance() {
|
||
|
if (!ThemeObserver.instance) {
|
||
|
ThemeObserver.instance = new ThemeObserver();
|
||
|
}
|
||
|
|
||
|
return ThemeObserver.instance;
|
||
|
}
|
||
|
|
||
|
private fire() {
|
||
|
this.fns.forEach(fn => fn());
|
||
|
}
|
||
|
|
||
|
public add(fn: () => void) {
|
||
|
this.fns.push(fn);
|
||
|
}
|
||
|
|
||
|
public remove(fn: () => void) {
|
||
|
this.fns = this.fns.filter(f => f !== fn);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export function useCssVar(name: string, options?: VarOptions) {
|
||
|
if (!options) {
|
||
|
options = {
|
||
|
type: "hsla",
|
||
|
transparency: 1,
|
||
|
apply: undefined,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
const cssVal = ref(getComputedStyle(document.documentElement).getPropertyValue(name).trim());
|
||
|
const update = () => {
|
||
|
cssVal.value = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
||
|
};
|
||
|
|
||
|
ThemeObserver.getInstance().add(update);
|
||
|
onUnmounted(() => {
|
||
|
ThemeObserver.getInstance().remove(update);
|
||
|
});
|
||
|
|
||
|
switch (options.type) {
|
||
|
case "hsla": {
|
||
|
return computed(() => {
|
||
|
if (!document) {
|
||
|
return "";
|
||
|
}
|
||
|
|
||
|
let val = cssVal.value.trim().split(" ").join(", ");
|
||
|
if (options?.transparency) {
|
||
|
val += `, ${options.transparency}`;
|
||
|
}
|
||
|
|
||
|
return `hsla(${val})`;
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
}
|