feat: extract shop from mp/shop — initial libreshop/shop
Some checks failed
Build and publish / build (push) Failing after 19s
Some checks failed
Build and publish / build (push) Failing after 19s
Source moved verbatim from mp/shop/ on 2026-04-29; mp was the first concrete adapter consuming the libreshop toolkit. Builds and publishes git.librete.ch/libreshop/shop on every main / v* push via the standard .gitea/workflows/build.yml shared across libreshop components.
This commit is contained in:
8
utils/numberFormatter.ts
Normal file
8
utils/numberFormatter.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export const numberFormatter = (num: number, symbol?: string): string => {
|
||||
const formatter = new Intl.NumberFormat("de-DE", {
|
||||
style: "decimal",
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
});
|
||||
return formatter.format(num) + (symbol ? ` ${symbol}` : "");
|
||||
};
|
||||
29
utils/randomTailwindColor.ts
Normal file
29
utils/randomTailwindColor.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
type TailwindColorPrefix = "text" | "bg" | "shadow" | "from" | "to" | "via" | "group-hover:shadow" | "group-active:shadow";
|
||||
|
||||
type TailwindColorShade = 50 | 100 | 150 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;
|
||||
|
||||
export const randomTailwindColor = (rand: number, prefix: TailwindColorPrefix = "bg", shade: TailwindColorShade = 100, suffix = "") => {
|
||||
const tailwindColors = [
|
||||
"red",
|
||||
"orange",
|
||||
"amber",
|
||||
"yellow",
|
||||
"lime",
|
||||
"green",
|
||||
"emerald",
|
||||
"teal",
|
||||
"cyan",
|
||||
"sky",
|
||||
"blue",
|
||||
"indigo",
|
||||
"violet",
|
||||
"purple",
|
||||
"fuchsia",
|
||||
"pink",
|
||||
"rose"
|
||||
];
|
||||
|
||||
const randomColorIndex = rand % tailwindColors.length;
|
||||
|
||||
return `${prefix}-${tailwindColors[randomColorIndex]}-${shade}${suffix}`;
|
||||
};
|
||||
8
utils/scrollProgress.ts
Normal file
8
utils/scrollProgress.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export const scrollProgress = () => {
|
||||
if (!import.meta.client) return 0;
|
||||
|
||||
const { documentElement, body } = document;
|
||||
const windowScroll = body.scrollTop || documentElement.scrollTop;
|
||||
const height = documentElement.scrollHeight - documentElement.clientHeight;
|
||||
return height > 0 ? Math.round((windowScroll / height) * 100) : 0;
|
||||
};
|
||||
9
utils/slugify.ts
Normal file
9
utils/slugify.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export function slugify(text: string): string {
|
||||
return text
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, "-")
|
||||
.replace(/ü/g, "ue")
|
||||
.replace(/ä/g, "ae")
|
||||
.replace(/ö/g, "oe")
|
||||
.replace(/ß/g, "ss");
|
||||
}
|
||||
13
utils/trackEvent.ts
Normal file
13
utils/trackEvent.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
declare global {
|
||||
interface Window {
|
||||
umami?: {
|
||||
track: (event: string, data?: Record<string, unknown>) => void;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const trackEvent = (event: string, data?: Record<string, unknown>) => {
|
||||
if (import.meta.client && window.umami) {
|
||||
window.umami.track(event, data);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user