@themodcraft/addon-packs 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/dist/addons/animations/animated-number/AnimatedNumber.d.ts +2 -0
- package/dist/addons/animations/animated-number/AnimatedNumber.js +29 -0
- package/dist/addons/animations/animated-number/AnimatedNumber.types.d.ts +6 -0
- package/dist/addons/animations/animated-number/AnimatedNumber.types.js +1 -0
- package/dist/addons/animations/animated-number/index.d.ts +2 -0
- package/dist/addons/animations/animated-number/index.js +1 -0
- package/dist/addons/animations/index.d.ts +1 -0
- package/dist/addons/animations/index.js +1 -0
- package/dist/addons/artstyles/colors/color-utils.d.ts +23 -0
- package/dist/addons/artstyles/colors/color-utils.js +141 -0
- package/dist/addons/artstyles/colors/index.d.ts +1 -0
- package/dist/addons/artstyles/colors/index.js +1 -0
- package/dist/addons/artstyles/index.d.ts +1 -0
- package/dist/addons/artstyles/index.js +1 -0
- package/dist/addons/charts/index.d.ts +1 -0
- package/dist/addons/charts/index.js +1 -0
- package/dist/addons/charts/mini-bar-chart/MiniBarChart.d.ts +2 -0
- package/dist/addons/charts/mini-bar-chart/MiniBarChart.js +72 -0
- package/dist/addons/charts/mini-bar-chart/MiniBarChart.types.d.ts +16 -0
- package/dist/addons/charts/mini-bar-chart/MiniBarChart.types.js +1 -0
- package/dist/addons/charts/mini-bar-chart/index.d.ts +2 -0
- package/dist/addons/charts/mini-bar-chart/index.js +1 -0
- package/dist/addons/configs/index.d.ts +1 -0
- package/dist/addons/configs/index.js +1 -0
- package/dist/addons/index.d.ts +8 -0
- package/dist/addons/index.js +8 -0
- package/dist/addons/integrations/better-auth/better-auth.d.ts +16 -0
- package/dist/addons/integrations/better-auth/better-auth.js +11 -0
- package/dist/addons/integrations/better-auth/index.d.ts +1 -0
- package/dist/addons/integrations/better-auth/index.js +1 -0
- package/dist/addons/integrations/index.d.ts +1 -0
- package/dist/addons/integrations/index.js +1 -0
- package/dist/addons/paywall/Paywall.d.ts +5 -0
- package/dist/addons/paywall/Paywall.js +145 -0
- package/dist/addons/paywall/Paywall.types.d.ts +54 -0
- package/dist/addons/paywall/Paywall.types.js +1 -0
- package/dist/addons/paywall/index.d.ts +1 -0
- package/dist/addons/paywall/index.js +1 -0
- package/dist/addons/providers/auth/index.d.ts +1 -0
- package/dist/addons/providers/auth/index.js +1 -0
- package/dist/addons/providers/index.d.ts +1 -0
- package/dist/addons/providers/index.js +1 -0
- package/dist/addons/providers/keyboard/index.d.ts +1 -0
- package/dist/addons/providers/keyboard/index.js +1 -0
- package/dist/addons/providers/nexus/index.d.ts +1 -0
- package/dist/addons/providers/nexus/index.js +1 -0
- package/dist/addons/providers/runtime/auth-provider.d.ts +39 -0
- package/dist/addons/providers/runtime/auth-provider.js +172 -0
- package/dist/addons/providers/runtime/config-provider.d.ts +13 -0
- package/dist/addons/providers/runtime/config-provider.js +11 -0
- package/dist/addons/providers/runtime/index.d.ts +6 -0
- package/dist/addons/providers/runtime/index.js +6 -0
- package/dist/addons/providers/runtime/keyboard-provider.d.ts +29 -0
- package/dist/addons/providers/runtime/keyboard-provider.js +106 -0
- package/dist/addons/providers/runtime/nexus-provider.d.ts +15 -0
- package/dist/addons/providers/runtime/nexus-provider.js +10 -0
- package/dist/addons/providers/runtime/theme-provider.d.ts +28 -0
- package/dist/addons/providers/runtime/theme-provider.js +131 -0
- package/dist/addons/providers/runtime/ui-overlay-provider.d.ts +14 -0
- package/dist/addons/providers/runtime/ui-overlay-provider.js +36 -0
- package/dist/addons/providers/theme/index.d.ts +1 -0
- package/dist/addons/providers/theme/index.js +1 -0
- package/dist/addons/themes/index.d.ts +1 -0
- package/dist/addons/themes/index.js +1 -0
- package/dist/artstyles.d.ts +1 -0
- package/dist/artstyles.js +1 -0
- package/dist/configs/themodcraft-v3/index.d.ts +1 -0
- package/dist/configs/themodcraft-v3/index.js +1 -0
- package/dist/configs/themodcraft-v3/navigation.json +42 -0
- package/dist/configs/themodcraft-v3/servers.json +44 -0
- package/dist/configs/themodcraft-v3/sliderHome.json +31 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/paywall.d.ts +1 -0
- package/dist/paywall.js +1 -0
- package/dist/providers/auth.d.ts +1 -0
- package/dist/providers/auth.js +1 -0
- package/dist/providers/keyboard.d.ts +1 -0
- package/dist/providers/keyboard.js +1 -0
- package/dist/providers/nexus.d.ts +1 -0
- package/dist/providers/nexus.js +1 -0
- package/dist/providers/theme.d.ts +1 -0
- package/dist/providers/theme.js +1 -0
- package/dist/providers.d.ts +1 -0
- package/dist/providers.js +1 -0
- package/dist/theme-packs/default/default-theme-pack.json +24 -0
- package/dist/theme-packs/themodcraft-v3/color-scheme.css +106 -0
- package/dist/theme-packs/themodcraft-v3/index.d.ts +1 -0
- package/dist/theme-packs/themodcraft-v3/index.js +1 -0
- package/dist/theme-packs/themodcraft-v3/themodcraft-v3-theme-pack.json +32 -0
- package/package.json +105 -0
package/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# `@themodcraft/addon-packs`
|
|
2
|
+
|
|
3
|
+
Bundle package that re-exports the addon family.
|
|
4
|
+
|
|
5
|
+
Use this when you want the full set of addon packages instead of installing them individually.
|
|
6
|
+
|
|
7
|
+
Install:
|
|
8
|
+
```bash
|
|
9
|
+
npm install @themodcraft/addon-packs
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
This package publishes compiled output only.
|
|
13
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
export function AnimatedNumber({ className, durationMs = 800, format = (value) => String(Math.round(value)), value, }) {
|
|
5
|
+
const [displayValue, setDisplayValue] = useState(value);
|
|
6
|
+
const displayValueRef = useRef(value);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
displayValueRef.current = displayValue;
|
|
9
|
+
}, [displayValue]);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
const start = performance.now();
|
|
12
|
+
const from = displayValueRef.current;
|
|
13
|
+
const delta = value - from;
|
|
14
|
+
let frame = 0;
|
|
15
|
+
function tick(now) {
|
|
16
|
+
const progress = Math.min((now - start) / durationMs, 1);
|
|
17
|
+
const eased = 1 - Math.pow(1 - progress, 3);
|
|
18
|
+
const nextValue = from + delta * eased;
|
|
19
|
+
displayValueRef.current = nextValue;
|
|
20
|
+
setDisplayValue(nextValue);
|
|
21
|
+
if (progress < 1) {
|
|
22
|
+
frame = requestAnimationFrame(tick);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
frame = requestAnimationFrame(tick);
|
|
26
|
+
return () => cancelAnimationFrame(frame);
|
|
27
|
+
}, [durationMs, value]);
|
|
28
|
+
return _jsx("span", { className: className, children: format(displayValue) });
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { AnimatedNumber } from "./AnimatedNumber";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@themodcraft/addon-animations";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@themodcraft/addon-animations";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface RgbColor {
|
|
2
|
+
a?: number;
|
|
3
|
+
b: number;
|
|
4
|
+
g: number;
|
|
5
|
+
r: number;
|
|
6
|
+
}
|
|
7
|
+
export interface HslColor {
|
|
8
|
+
a?: number;
|
|
9
|
+
h: number;
|
|
10
|
+
l: number;
|
|
11
|
+
s: number;
|
|
12
|
+
}
|
|
13
|
+
export type ColorInput = HslColor | RgbColor | string;
|
|
14
|
+
export type TransformColorMode = "contrast" | "grayscale" | "invert";
|
|
15
|
+
export declare function isValidHex(hex: string): boolean;
|
|
16
|
+
export declare function rgbToHex(rgb: string): string | null;
|
|
17
|
+
export declare function rgbaToHex({ a, b, g, r }: RgbColor): string;
|
|
18
|
+
export declare function hexToRgb(hex: string): RgbColor | null;
|
|
19
|
+
export declare function convertToHex(color: ColorInput): string | null;
|
|
20
|
+
export declare function shiftColorLightness(hex: string, amount: number): string;
|
|
21
|
+
export declare function darken(hex: string, amount: number): string;
|
|
22
|
+
export declare function lighten(hex: string, amount: number): string;
|
|
23
|
+
export declare function transformColor(hex: string, mode: TransformColorMode): string;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
function clampChannel(value) {
|
|
2
|
+
return Math.max(0, Math.min(255, Math.round(value)));
|
|
3
|
+
}
|
|
4
|
+
function clampAlpha(value) {
|
|
5
|
+
return Math.max(0, Math.min(1, value));
|
|
6
|
+
}
|
|
7
|
+
function toHexChannel(value) {
|
|
8
|
+
return clampChannel(value).toString(16).padStart(2, "0").toUpperCase();
|
|
9
|
+
}
|
|
10
|
+
function normalizeHex(hex) {
|
|
11
|
+
const value = hex.trim();
|
|
12
|
+
if (/^#([0-9A-Fa-f]{3})$/.test(value)) {
|
|
13
|
+
const [, shortHex] = value.match(/^#([0-9A-Fa-f]{3})$/) ?? [];
|
|
14
|
+
return `#${shortHex.split("").map((channel) => `${channel}${channel}`).join("")}`.toUpperCase();
|
|
15
|
+
}
|
|
16
|
+
if (/^#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/.test(value)) {
|
|
17
|
+
return value.toUpperCase();
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
function hslToRgb({ h, l, s }) {
|
|
22
|
+
const hue = ((h % 360) + 360) % 360;
|
|
23
|
+
const saturation = Math.max(0, Math.min(100, s)) / 100;
|
|
24
|
+
const lightness = Math.max(0, Math.min(100, l)) / 100;
|
|
25
|
+
const chroma = (1 - Math.abs(2 * lightness - 1)) * saturation;
|
|
26
|
+
const x = chroma * (1 - Math.abs(((hue / 60) % 2) - 1));
|
|
27
|
+
const m = lightness - chroma / 2;
|
|
28
|
+
let r = 0;
|
|
29
|
+
let g = 0;
|
|
30
|
+
let b = 0;
|
|
31
|
+
if (hue < 60) {
|
|
32
|
+
r = chroma;
|
|
33
|
+
g = x;
|
|
34
|
+
}
|
|
35
|
+
else if (hue < 120) {
|
|
36
|
+
r = x;
|
|
37
|
+
g = chroma;
|
|
38
|
+
}
|
|
39
|
+
else if (hue < 180) {
|
|
40
|
+
g = chroma;
|
|
41
|
+
b = x;
|
|
42
|
+
}
|
|
43
|
+
else if (hue < 240) {
|
|
44
|
+
g = x;
|
|
45
|
+
b = chroma;
|
|
46
|
+
}
|
|
47
|
+
else if (hue < 300) {
|
|
48
|
+
r = x;
|
|
49
|
+
b = chroma;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
r = chroma;
|
|
53
|
+
b = x;
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
b: clampChannel((b + m) * 255),
|
|
57
|
+
g: clampChannel((g + m) * 255),
|
|
58
|
+
r: clampChannel((r + m) * 255),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export function isValidHex(hex) {
|
|
62
|
+
return Boolean(normalizeHex(hex));
|
|
63
|
+
}
|
|
64
|
+
export function rgbToHex(rgb) {
|
|
65
|
+
const channels = rgb.match(/-?\d+(\.\d+)?/g)?.map(Number);
|
|
66
|
+
if (!channels || channels.length < 3) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const [r, g, b] = channels;
|
|
70
|
+
return `#${toHexChannel(r)}${toHexChannel(g)}${toHexChannel(b)}`;
|
|
71
|
+
}
|
|
72
|
+
export function rgbaToHex({ a, b, g, r }) {
|
|
73
|
+
const alpha = typeof a === "number" ? toHexChannel(clampAlpha(a) * 255) : "";
|
|
74
|
+
return `#${toHexChannel(r)}${toHexChannel(g)}${toHexChannel(b)}${alpha}`;
|
|
75
|
+
}
|
|
76
|
+
export function hexToRgb(hex) {
|
|
77
|
+
const normalized = normalizeHex(hex);
|
|
78
|
+
if (!normalized) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
b: parseInt(normalized.slice(5, 7), 16),
|
|
83
|
+
g: parseInt(normalized.slice(3, 5), 16),
|
|
84
|
+
r: parseInt(normalized.slice(1, 3), 16),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
export function convertToHex(color) {
|
|
88
|
+
if (typeof color === "string") {
|
|
89
|
+
const normalizedHex = normalizeHex(color);
|
|
90
|
+
if (normalizedHex) {
|
|
91
|
+
return normalizedHex;
|
|
92
|
+
}
|
|
93
|
+
return rgbToHex(color);
|
|
94
|
+
}
|
|
95
|
+
if ("r" in color) {
|
|
96
|
+
return rgbaToHex(color);
|
|
97
|
+
}
|
|
98
|
+
const rgb = hslToRgb(color);
|
|
99
|
+
const alpha = typeof color.a === "number" ? toHexChannel(clampAlpha(color.a) * 255) : "";
|
|
100
|
+
return `${rgbaToHex(rgb)}${alpha}`;
|
|
101
|
+
}
|
|
102
|
+
export function shiftColorLightness(hex, amount) {
|
|
103
|
+
const rgb = hexToRgb(hex);
|
|
104
|
+
if (!rgb) {
|
|
105
|
+
return "#000000";
|
|
106
|
+
}
|
|
107
|
+
return rgbaToHex({
|
|
108
|
+
b: rgb.b + amount,
|
|
109
|
+
g: rgb.g + amount,
|
|
110
|
+
r: rgb.r + amount,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
export function darken(hex, amount) {
|
|
114
|
+
return shiftColorLightness(hex, -Math.abs(amount));
|
|
115
|
+
}
|
|
116
|
+
export function lighten(hex, amount) {
|
|
117
|
+
return shiftColorLightness(hex, Math.abs(amount));
|
|
118
|
+
}
|
|
119
|
+
export function transformColor(hex, mode) {
|
|
120
|
+
const rgb = hexToRgb(hex);
|
|
121
|
+
if (!rgb) {
|
|
122
|
+
return "#000000";
|
|
123
|
+
}
|
|
124
|
+
if (mode === "contrast") {
|
|
125
|
+
const yiq = (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;
|
|
126
|
+
return yiq >= 128 ? "#000000" : "#FFFFFF";
|
|
127
|
+
}
|
|
128
|
+
if (mode === "invert") {
|
|
129
|
+
return rgbaToHex({
|
|
130
|
+
b: 255 - rgb.b,
|
|
131
|
+
g: 255 - rgb.g,
|
|
132
|
+
r: 255 - rgb.r,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
const luma = 0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b;
|
|
136
|
+
return rgbaToHex({
|
|
137
|
+
b: luma,
|
|
138
|
+
g: luma,
|
|
139
|
+
r: luma,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { convertToHex, darken, hexToRgb, isValidHex, lighten, rgbaToHex, rgbToHex, shiftColorLightness, transformColor, type ColorInput, type HslColor, type RgbColor, type TransformColorMode, } from "./color-utils";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { convertToHex, darken, hexToRgb, isValidHex, lighten, rgbaToHex, rgbToHex, shiftColorLightness, transformColor, } from "./color-utils";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@themodcraft/addon-artstyles";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@themodcraft/addon-artstyles";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@themodcraft/addon-charts";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@themodcraft/addon-charts";
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { BarElement, CategoryScale, Chart as ChartJS, LinearScale, Tooltip, } from "chart.js";
|
|
4
|
+
import { useMemo } from "react";
|
|
5
|
+
import { Bar } from "react-chartjs-2";
|
|
6
|
+
ChartJS.register(CategoryScale, LinearScale, BarElement, Tooltip);
|
|
7
|
+
function mergeChartOptions(baseOptions, chartOptions) {
|
|
8
|
+
return {
|
|
9
|
+
...baseOptions,
|
|
10
|
+
...chartOptions,
|
|
11
|
+
plugins: {
|
|
12
|
+
...baseOptions.plugins,
|
|
13
|
+
...chartOptions?.plugins,
|
|
14
|
+
},
|
|
15
|
+
scales: {
|
|
16
|
+
...baseOptions.scales,
|
|
17
|
+
...chartOptions?.scales,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function MiniBarChart({ ariaLabel = "Mini bar chart", chartOptions, className, data, height, maxValue, showXAxis = false, showYAxis = true, }) {
|
|
22
|
+
const chartMax = maxValue ?? Math.max(...data.map((item) => item.value), 1);
|
|
23
|
+
const resolvedHeight = height ?? Math.max(data.length * 42, 120);
|
|
24
|
+
const chartData = useMemo(() => ({
|
|
25
|
+
labels: data.map((item) => item.label),
|
|
26
|
+
datasets: [
|
|
27
|
+
{
|
|
28
|
+
backgroundColor: data.map((item) => item.color ?? "#0b63ce"),
|
|
29
|
+
borderRadius: 6,
|
|
30
|
+
borderSkipped: false,
|
|
31
|
+
data: data.map((item) => item.value),
|
|
32
|
+
label: ariaLabel,
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
}), [ariaLabel, data]);
|
|
36
|
+
const options = useMemo(() => mergeChartOptions({
|
|
37
|
+
animation: {
|
|
38
|
+
duration: 220,
|
|
39
|
+
},
|
|
40
|
+
indexAxis: "y",
|
|
41
|
+
maintainAspectRatio: false,
|
|
42
|
+
plugins: {
|
|
43
|
+
legend: {
|
|
44
|
+
display: false,
|
|
45
|
+
},
|
|
46
|
+
tooltip: {
|
|
47
|
+
enabled: true,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
responsive: true,
|
|
51
|
+
scales: {
|
|
52
|
+
x: {
|
|
53
|
+
beginAtZero: true,
|
|
54
|
+
display: showXAxis,
|
|
55
|
+
grid: {
|
|
56
|
+
display: showXAxis,
|
|
57
|
+
},
|
|
58
|
+
max: chartMax,
|
|
59
|
+
ticks: {
|
|
60
|
+
precision: 0,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
y: {
|
|
64
|
+
display: showYAxis,
|
|
65
|
+
grid: {
|
|
66
|
+
display: false,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
}, chartOptions), [chartMax, chartOptions, showXAxis, showYAxis]);
|
|
71
|
+
return (_jsx("div", { "aria-label": ariaLabel, className: className, role: "img", style: { height: resolvedHeight }, children: _jsx(Bar, { data: chartData, options: options }) }));
|
|
72
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ChartOptions } from "chart.js";
|
|
2
|
+
export interface MiniBarChartDatum {
|
|
3
|
+
color?: string;
|
|
4
|
+
label: string;
|
|
5
|
+
value: number;
|
|
6
|
+
}
|
|
7
|
+
export interface MiniBarChartProps {
|
|
8
|
+
ariaLabel?: string;
|
|
9
|
+
chartOptions?: ChartOptions<"bar">;
|
|
10
|
+
className?: string;
|
|
11
|
+
data: MiniBarChartDatum[];
|
|
12
|
+
height?: number | string;
|
|
13
|
+
maxValue?: number;
|
|
14
|
+
showXAxis?: boolean;
|
|
15
|
+
showYAxis?: boolean;
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { MiniBarChart } from "./MiniBarChart";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@themodcraft/addon-configs";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@themodcraft/addon-configs";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface BetterAuthUserLike {
|
|
2
|
+
email?: string | null;
|
|
3
|
+
image?: string | null;
|
|
4
|
+
name?: string | null;
|
|
5
|
+
username?: string | null;
|
|
6
|
+
}
|
|
7
|
+
export interface BetterAuthSessionLike {
|
|
8
|
+
user?: BetterAuthUserLike | null;
|
|
9
|
+
}
|
|
10
|
+
export interface BetterAuthNavbarProfile {
|
|
11
|
+
email?: string | null;
|
|
12
|
+
image?: string | null;
|
|
13
|
+
name?: string | null;
|
|
14
|
+
username?: string | null;
|
|
15
|
+
}
|
|
16
|
+
export declare function profileFromBetterAuthSession(session: BetterAuthSessionLike | null | undefined): BetterAuthNavbarProfile | null;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function profileFromBetterAuthSession(session) {
|
|
2
|
+
if (!session?.user) {
|
|
3
|
+
return null;
|
|
4
|
+
}
|
|
5
|
+
return {
|
|
6
|
+
email: session.user.email ?? null,
|
|
7
|
+
image: session.user.image ?? null,
|
|
8
|
+
name: session.user.name ?? null,
|
|
9
|
+
username: session.user.username ?? null,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@themodcraft/addon-integrations/better-auth";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@themodcraft/addon-integrations/better-auth";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@themodcraft/addon-integrations";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@themodcraft/addon-integrations";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { PaywallContextValue, PaywallGateProps, PaywallProviderProps } from "./Paywall.types";
|
|
2
|
+
export declare function PaywallProvider({ checkoutUrl, children, defaultFallback, entitlements, onCheckout, plans, resolveEntitlements, status, }: PaywallProviderProps): import("react").JSX.Element;
|
|
3
|
+
export declare function usePaywall(): PaywallContextValue;
|
|
4
|
+
export declare function useOptionalPaywall(): PaywallContextValue | null;
|
|
5
|
+
export declare function PaywallGate({ children, fallback, loadingFallback, mode, planId, required, }: PaywallGateProps): string | number | bigint | boolean | import("react").JSX.Element | Iterable<import("react").ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<import("react").ReactNode> | null | undefined> | null | undefined;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, isValidElement, useCallback, useContext, useEffect, useMemo, useState, } from "react";
|
|
4
|
+
const PaywallContext = createContext(null);
|
|
5
|
+
function normalizeEntitlements(requiredEntitlements) {
|
|
6
|
+
if (!requiredEntitlements) {
|
|
7
|
+
return [];
|
|
8
|
+
}
|
|
9
|
+
return Array.isArray(requiredEntitlements)
|
|
10
|
+
? requiredEntitlements.filter(Boolean)
|
|
11
|
+
: [requiredEntitlements].filter(Boolean);
|
|
12
|
+
}
|
|
13
|
+
function checkEntitlements(entitlements, requiredEntitlements, mode = "all") {
|
|
14
|
+
const required = normalizeEntitlements(requiredEntitlements);
|
|
15
|
+
if (!required.length) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
if (mode === "any") {
|
|
19
|
+
return required.some((entitlement) => entitlements.includes(entitlement));
|
|
20
|
+
}
|
|
21
|
+
return required.every((entitlement) => entitlements.includes(entitlement));
|
|
22
|
+
}
|
|
23
|
+
function missingEntitlements(entitlements, requiredEntitlements, mode = "all") {
|
|
24
|
+
const required = normalizeEntitlements(requiredEntitlements);
|
|
25
|
+
if (mode === "any" && required.some((entitlement) => entitlements.includes(entitlement))) {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
return required.filter((entitlement) => !entitlements.includes(entitlement));
|
|
29
|
+
}
|
|
30
|
+
function renderFallback(fallback, decision, actions) {
|
|
31
|
+
if (typeof fallback === "function" && !isValidElement(fallback)) {
|
|
32
|
+
return fallback(decision, actions);
|
|
33
|
+
}
|
|
34
|
+
return fallback;
|
|
35
|
+
}
|
|
36
|
+
function DefaultPaywallFallback({ decision, startCheckout, }) {
|
|
37
|
+
const planLabel = decision.plan?.label ?? "Pro";
|
|
38
|
+
return (_jsxs("div", { role: "group", style: { border: "1px solid var(--tmc-color-border, #d7dde5)", borderRadius: "8px", padding: "16px" }, children: [_jsxs("p", { style: { color: "var(--tmc-color-text, #111827)", fontWeight: 600, margin: 0 }, children: [planLabel, " access required"] }), _jsx("p", { style: { color: "var(--tmc-color-text-muted, #5f6b7a)", margin: "8px 0 0" }, children: "This package area is locked until the required entitlement is active." }), _jsx("button", { onClick: () => void startCheckout(), style: { background: "var(--tmc-color-accent, #0b63ce)", border: 0, borderRadius: "6px", color: "var(--tmc-color-accent-text, #ffffff)", cursor: "pointer", marginTop: "12px", padding: "8px 12px" }, type: "button", children: "Unlock" })] }));
|
|
39
|
+
}
|
|
40
|
+
export function PaywallProvider({ checkoutUrl, children, defaultFallback, entitlements = [], onCheckout, plans = [], resolveEntitlements, status = "ready", }) {
|
|
41
|
+
const [resolvedEntitlements, setResolvedEntitlements] = useState(entitlements);
|
|
42
|
+
const [resolvedStatus, setResolvedStatus] = useState(status);
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
setResolvedEntitlements(entitlements);
|
|
45
|
+
setResolvedStatus(status);
|
|
46
|
+
}, [entitlements, status]);
|
|
47
|
+
const refreshEntitlements = useCallback(async () => {
|
|
48
|
+
if (!resolveEntitlements) {
|
|
49
|
+
return resolvedEntitlements;
|
|
50
|
+
}
|
|
51
|
+
setResolvedStatus("loading");
|
|
52
|
+
try {
|
|
53
|
+
const nextEntitlements = await resolveEntitlements();
|
|
54
|
+
setResolvedEntitlements(nextEntitlements);
|
|
55
|
+
setResolvedStatus("ready");
|
|
56
|
+
return nextEntitlements;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
setResolvedStatus("error");
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
}, [resolveEntitlements, resolvedEntitlements]);
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
if (!resolveEntitlements) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
void refreshEntitlements();
|
|
68
|
+
}, [refreshEntitlements, resolveEntitlements]);
|
|
69
|
+
const getDecision = useCallback((requiredEntitlements, mode = "all", planId) => {
|
|
70
|
+
const required = normalizeEntitlements(requiredEntitlements);
|
|
71
|
+
const plan = plans.find((currentPlan) => currentPlan.id === planId) ?? plans.find((currentPlan) => currentPlan.entitlements?.some((entitlement) => required.includes(entitlement)));
|
|
72
|
+
const allowed = resolvedStatus === "ready" && checkEntitlements(resolvedEntitlements, required, mode);
|
|
73
|
+
return {
|
|
74
|
+
allowed,
|
|
75
|
+
missingEntitlements: missingEntitlements(resolvedEntitlements, required, mode),
|
|
76
|
+
plan,
|
|
77
|
+
requiredEntitlements: required,
|
|
78
|
+
status: resolvedStatus,
|
|
79
|
+
};
|
|
80
|
+
}, [plans, resolvedEntitlements, resolvedStatus]);
|
|
81
|
+
const hasEntitlements = useCallback((requiredEntitlements, mode = "all") => (getDecision(requiredEntitlements, mode).allowed), [getDecision]);
|
|
82
|
+
const startCheckout = useCallback(async (planId, decision) => {
|
|
83
|
+
const plan = plans.find((currentPlan) => currentPlan.id === planId) ?? decision?.plan;
|
|
84
|
+
const nextDecision = decision ?? getDecision(plan?.entitlements, "all", plan?.id);
|
|
85
|
+
if (onCheckout) {
|
|
86
|
+
await onCheckout(plan, nextDecision);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const url = plan?.checkoutUrl ?? checkoutUrl;
|
|
90
|
+
if (url && typeof window !== "undefined") {
|
|
91
|
+
window.location.href = url;
|
|
92
|
+
}
|
|
93
|
+
}, [checkoutUrl, getDecision, onCheckout, plans]);
|
|
94
|
+
const value = useMemo(() => ({
|
|
95
|
+
checkoutUrl,
|
|
96
|
+
defaultFallback,
|
|
97
|
+
entitlements: resolvedEntitlements,
|
|
98
|
+
getDecision,
|
|
99
|
+
hasEntitlements,
|
|
100
|
+
plans,
|
|
101
|
+
refreshEntitlements,
|
|
102
|
+
startCheckout,
|
|
103
|
+
status: resolvedStatus,
|
|
104
|
+
}), [
|
|
105
|
+
checkoutUrl,
|
|
106
|
+
defaultFallback,
|
|
107
|
+
getDecision,
|
|
108
|
+
hasEntitlements,
|
|
109
|
+
plans,
|
|
110
|
+
refreshEntitlements,
|
|
111
|
+
resolvedEntitlements,
|
|
112
|
+
resolvedStatus,
|
|
113
|
+
startCheckout,
|
|
114
|
+
]);
|
|
115
|
+
return _jsx(PaywallContext.Provider, { value: value, children: children });
|
|
116
|
+
}
|
|
117
|
+
export function usePaywall() {
|
|
118
|
+
const context = useContext(PaywallContext);
|
|
119
|
+
if (!context) {
|
|
120
|
+
throw new Error("usePaywall must be used inside PaywallProvider.");
|
|
121
|
+
}
|
|
122
|
+
return context;
|
|
123
|
+
}
|
|
124
|
+
export function useOptionalPaywall() {
|
|
125
|
+
return useContext(PaywallContext);
|
|
126
|
+
}
|
|
127
|
+
export function PaywallGate({ children, fallback, loadingFallback = null, mode = "all", planId, required, }) {
|
|
128
|
+
const paywall = usePaywall();
|
|
129
|
+
const decision = paywall.getDecision(required, mode, planId);
|
|
130
|
+
const actions = {
|
|
131
|
+
refreshEntitlements: paywall.refreshEntitlements,
|
|
132
|
+
startCheckout: () => paywall.startCheckout(planId, decision),
|
|
133
|
+
};
|
|
134
|
+
if (decision.status === "loading") {
|
|
135
|
+
return loadingFallback;
|
|
136
|
+
}
|
|
137
|
+
if (decision.allowed) {
|
|
138
|
+
return children;
|
|
139
|
+
}
|
|
140
|
+
const renderedFallback = renderFallback(fallback ?? paywall.defaultFallback, decision, actions);
|
|
141
|
+
if (renderedFallback) {
|
|
142
|
+
return renderedFallback;
|
|
143
|
+
}
|
|
144
|
+
return _jsx(DefaultPaywallFallback, { decision: decision, startCheckout: actions.startCheckout });
|
|
145
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type PaywallAccessMode = "all" | "any";
|
|
3
|
+
export type PaywallStatus = "error" | "loading" | "ready";
|
|
4
|
+
export interface PaywallPlan {
|
|
5
|
+
checkoutUrl?: string;
|
|
6
|
+
entitlements?: string[];
|
|
7
|
+
id: string;
|
|
8
|
+
label: string;
|
|
9
|
+
priceLabel?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface PaywallDecision {
|
|
12
|
+
allowed: boolean;
|
|
13
|
+
missingEntitlements: string[];
|
|
14
|
+
plan?: PaywallPlan;
|
|
15
|
+
requiredEntitlements: string[];
|
|
16
|
+
status: PaywallStatus;
|
|
17
|
+
}
|
|
18
|
+
export interface PaywallActions {
|
|
19
|
+
refreshEntitlements: () => Promise<string[]>;
|
|
20
|
+
startCheckout: (planId?: string) => Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
export type PaywallFallbackRenderer = (decision: PaywallDecision, actions: PaywallActions) => ReactNode;
|
|
23
|
+
export type PaywallFallback = ReactNode | PaywallFallbackRenderer;
|
|
24
|
+
export type EntitlementResolver = () => Promise<string[]> | string[];
|
|
25
|
+
export type PaywallCheckoutHandler = (plan: PaywallPlan | undefined, decision: PaywallDecision) => Promise<void> | void;
|
|
26
|
+
export interface PaywallProviderProps {
|
|
27
|
+
checkoutUrl?: string;
|
|
28
|
+
children: ReactNode;
|
|
29
|
+
defaultFallback?: PaywallFallback;
|
|
30
|
+
entitlements?: string[];
|
|
31
|
+
onCheckout?: PaywallCheckoutHandler;
|
|
32
|
+
plans?: PaywallPlan[];
|
|
33
|
+
resolveEntitlements?: EntitlementResolver;
|
|
34
|
+
status?: PaywallStatus;
|
|
35
|
+
}
|
|
36
|
+
export interface PaywallContextValue {
|
|
37
|
+
checkoutUrl?: string;
|
|
38
|
+
defaultFallback?: PaywallFallback;
|
|
39
|
+
entitlements: string[];
|
|
40
|
+
getDecision: (requiredEntitlements?: string | string[], mode?: PaywallAccessMode, planId?: string) => PaywallDecision;
|
|
41
|
+
hasEntitlements: (requiredEntitlements?: string | string[], mode?: PaywallAccessMode) => boolean;
|
|
42
|
+
plans: PaywallPlan[];
|
|
43
|
+
refreshEntitlements: () => Promise<string[]>;
|
|
44
|
+
startCheckout: (planId?: string, decision?: PaywallDecision) => Promise<void>;
|
|
45
|
+
status: PaywallStatus;
|
|
46
|
+
}
|
|
47
|
+
export interface PaywallGateProps {
|
|
48
|
+
children: ReactNode;
|
|
49
|
+
fallback?: PaywallFallback;
|
|
50
|
+
loadingFallback?: ReactNode;
|
|
51
|
+
mode?: PaywallAccessMode;
|
|
52
|
+
planId?: string;
|
|
53
|
+
required?: string | string[];
|
|
54
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@themodcraft/addon-paywall";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@themodcraft/addon-paywall";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@themodcraft/addon-providers/auth";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@themodcraft/addon-providers/auth";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@themodcraft/addon-providers";
|