@useaward/embed-predictions 0.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/dist/web.js +870 -0
- package/package.json +49 -0
- package/src/api/client.ts +107 -0
- package/src/assets/index.css +15 -0
- package/src/components/ButtonBar.tsx +52 -0
- package/src/components/CorrectValueStep.tsx +80 -0
- package/src/components/EmailInput.tsx +38 -0
- package/src/components/Game.tsx +365 -0
- package/src/components/GameLayout.tsx +29 -0
- package/src/components/GamePlay.tsx +77 -0
- package/src/components/LanguageSwitcher.tsx +40 -0
- package/src/components/ScoreGameStep.tsx +113 -0
- package/src/components/StepRenderer.tsx +41 -0
- package/src/components/SuccessScreen.tsx +24 -0
- package/src/components/WelcomeScreen.tsx +43 -0
- package/src/components/ui/button.tsx +64 -0
- package/src/components/ui/spinner.tsx +18 -0
- package/src/constants.ts +27 -0
- package/src/context/GameContext.tsx +43 -0
- package/src/context/locale-context.tsx +32 -0
- package/src/features/standard/StandardGameEmbed.tsx +57 -0
- package/src/global.d.ts +1 -0
- package/src/locale/en.ts +24 -0
- package/src/locale/lv.ts +24 -0
- package/src/register.ts +13 -0
- package/src/stores/gameStore.ts +101 -0
- package/src/types/style.ts +9 -0
- package/src/types/translations.ts +8 -0
- package/src/utils/cn.ts +7 -0
- package/src/utils/fetch-locale.ts +8 -0
- package/src/utils/inject-font.ts +23 -0
- package/src/utils/set-css-variables.ts +83 -0
- package/src/web.ts +12 -0
- package/src/window.ts +33 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { createStore } from "solid-js/store";
|
|
2
|
+
|
|
3
|
+
import type { GameData, Submission } from "../api/client";
|
|
4
|
+
|
|
5
|
+
export interface GameStoreState {
|
|
6
|
+
// Game data
|
|
7
|
+
gameData: GameData | null;
|
|
8
|
+
loading: boolean;
|
|
9
|
+
error: string | null;
|
|
10
|
+
customCss: string;
|
|
11
|
+
|
|
12
|
+
// Content state
|
|
13
|
+
hasStarted: boolean;
|
|
14
|
+
submitted: boolean;
|
|
15
|
+
|
|
16
|
+
// Email state
|
|
17
|
+
email: string;
|
|
18
|
+
emailError: string;
|
|
19
|
+
|
|
20
|
+
// Step navigation
|
|
21
|
+
currentStepIndex: number;
|
|
22
|
+
|
|
23
|
+
// Predictions
|
|
24
|
+
predictions: Record<string, any>;
|
|
25
|
+
|
|
26
|
+
// Submission state
|
|
27
|
+
submitting: boolean;
|
|
28
|
+
submitError: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const initialState: GameStoreState = {
|
|
32
|
+
gameData: null,
|
|
33
|
+
loading: true,
|
|
34
|
+
error: null,
|
|
35
|
+
customCss: "",
|
|
36
|
+
hasStarted: false,
|
|
37
|
+
submitted: false,
|
|
38
|
+
email: "",
|
|
39
|
+
emailError: "",
|
|
40
|
+
currentStepIndex: 0,
|
|
41
|
+
predictions: {},
|
|
42
|
+
submitting: false,
|
|
43
|
+
submitError: "",
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export function createGameStore(initialEmail?: string) {
|
|
47
|
+
const [state, setState] = createStore<GameStoreState>({
|
|
48
|
+
...initialState,
|
|
49
|
+
email: initialEmail || "",
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const setGameData = (data: GameData) => setState("gameData", data);
|
|
53
|
+
const setLoading = (value: boolean) => setState("loading", value);
|
|
54
|
+
const setError = (value: string | null) => setState("error", value);
|
|
55
|
+
const setCustomCss = (value: string) => setState("customCss", value);
|
|
56
|
+
|
|
57
|
+
const setHasStarted = (value: boolean) => setState("hasStarted", value);
|
|
58
|
+
const setSubmitted = (value: boolean) => setState("submitted", value);
|
|
59
|
+
|
|
60
|
+
const setEmail = (value: string) => setState("email", value);
|
|
61
|
+
const setEmailError = (value: string) => setState("emailError", value);
|
|
62
|
+
|
|
63
|
+
const setCurrentStepIndex = (value: number) =>
|
|
64
|
+
setState("currentStepIndex", value);
|
|
65
|
+
|
|
66
|
+
const setPredictions = (value: Record<string, any>) =>
|
|
67
|
+
setState("predictions", value);
|
|
68
|
+
|
|
69
|
+
const updatePrediction = (stepId: string, value: any) => {
|
|
70
|
+
setState("predictions", stepId, value);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const setSubmitting = (value: boolean) => setState("submitting", value);
|
|
74
|
+
const setSubmitError = (value: string) => setState("submitError", value);
|
|
75
|
+
|
|
76
|
+
// Reset for new game
|
|
77
|
+
const reset = () => {
|
|
78
|
+
setState(initialState);
|
|
79
|
+
setState("email", initialEmail || "");
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
state,
|
|
84
|
+
setGameData,
|
|
85
|
+
setLoading,
|
|
86
|
+
setError,
|
|
87
|
+
setCustomCss,
|
|
88
|
+
setHasStarted,
|
|
89
|
+
setSubmitted,
|
|
90
|
+
setEmail,
|
|
91
|
+
setEmailError,
|
|
92
|
+
setCurrentStepIndex,
|
|
93
|
+
setPredictions,
|
|
94
|
+
updatePrediction,
|
|
95
|
+
setSubmitting,
|
|
96
|
+
setSubmitError,
|
|
97
|
+
reset,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export type GameStore = ReturnType<typeof createGameStore>;
|
package/src/utils/cn.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { locales } from "@/constants";
|
|
2
|
+
import { Dictionary, Locale, RawDictionary } from "@/types/translations";
|
|
3
|
+
import * as i18n from "@solid-primitives/i18n";
|
|
4
|
+
|
|
5
|
+
export async function fetchLocale(locale: Locale): Promise<Dictionary> {
|
|
6
|
+
const dict: RawDictionary = locales[locale].dict;
|
|
7
|
+
return i18n.flatten(dict);
|
|
8
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { defaultFontFamily } from "@/constants";
|
|
2
|
+
|
|
3
|
+
const googleFontCdnBaseUrl = "https://fonts.bunny.net/css2";
|
|
4
|
+
const elementId = "useaward-font";
|
|
5
|
+
|
|
6
|
+
export const injectFont = (font?: string | undefined) => {
|
|
7
|
+
const existingFont = document.getElementById(elementId);
|
|
8
|
+
const fontFamily = font || defaultFontFamily;
|
|
9
|
+
|
|
10
|
+
if (existingFont?.getAttribute("href")?.includes(fontFamily)) return;
|
|
11
|
+
|
|
12
|
+
existingFont?.remove();
|
|
13
|
+
|
|
14
|
+
const fontElement = document.createElement("link");
|
|
15
|
+
|
|
16
|
+
fontElement.href = `${googleFontCdnBaseUrl}?family=${fontFamily}:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700&display=swap`;
|
|
17
|
+
fontElement.rel = "stylesheet";
|
|
18
|
+
fontElement.id = elementId;
|
|
19
|
+
|
|
20
|
+
document.head.appendChild(fontElement);
|
|
21
|
+
|
|
22
|
+
return;
|
|
23
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { defaultFontFamily, embedCssVariableNames } from "@/constants";
|
|
2
|
+
import { Theme } from "@/types/style";
|
|
3
|
+
|
|
4
|
+
type CommonProps = {
|
|
5
|
+
documentStyle: CSSStyleDeclaration;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const setCssVariablesValue = ({
|
|
9
|
+
theme,
|
|
10
|
+
container,
|
|
11
|
+
}: {
|
|
12
|
+
theme: Theme | undefined;
|
|
13
|
+
container: HTMLDivElement;
|
|
14
|
+
} & Omit<CommonProps, "documentStyle">) => {
|
|
15
|
+
if (!theme) return;
|
|
16
|
+
const documentStyle = container?.style;
|
|
17
|
+
if (!documentStyle) return;
|
|
18
|
+
setFontFamily({
|
|
19
|
+
fontFamily: theme.fontFamily ?? defaultFontFamily,
|
|
20
|
+
documentStyle,
|
|
21
|
+
});
|
|
22
|
+
setGeneralTheme({
|
|
23
|
+
theme,
|
|
24
|
+
documentStyle,
|
|
25
|
+
});
|
|
26
|
+
setEmbedTheme({
|
|
27
|
+
theme,
|
|
28
|
+
documentStyle,
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const setFontFamily = ({
|
|
33
|
+
fontFamily,
|
|
34
|
+
documentStyle,
|
|
35
|
+
}: {
|
|
36
|
+
fontFamily: string;
|
|
37
|
+
documentStyle: CSSStyleDeclaration;
|
|
38
|
+
}) => {
|
|
39
|
+
documentStyle.setProperty(
|
|
40
|
+
embedCssVariableNames.general.fontFamily,
|
|
41
|
+
fontFamily,
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const setGeneralTheme = ({
|
|
46
|
+
theme,
|
|
47
|
+
documentStyle,
|
|
48
|
+
}: {
|
|
49
|
+
theme: Theme | undefined;
|
|
50
|
+
} & CommonProps) => {
|
|
51
|
+
documentStyle.setProperty(
|
|
52
|
+
embedCssVariableNames.general.backgroundColor,
|
|
53
|
+
theme?.backgroundColor ?? "#fff",
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const setEmbedTheme = ({
|
|
58
|
+
theme,
|
|
59
|
+
documentStyle,
|
|
60
|
+
}: {
|
|
61
|
+
theme: Theme | undefined;
|
|
62
|
+
} & CommonProps) => {
|
|
63
|
+
documentStyle.setProperty(
|
|
64
|
+
embedCssVariableNames.embed.questionTextColor,
|
|
65
|
+
theme?.questionTextColor ?? "#000",
|
|
66
|
+
);
|
|
67
|
+
documentStyle.setProperty(
|
|
68
|
+
embedCssVariableNames.embed.buttonBackground,
|
|
69
|
+
theme?.buttonBackground ?? "#000",
|
|
70
|
+
);
|
|
71
|
+
documentStyle.setProperty(
|
|
72
|
+
embedCssVariableNames.embed.buttonTextColor,
|
|
73
|
+
theme?.buttonTextColor ?? "#fff",
|
|
74
|
+
);
|
|
75
|
+
documentStyle.setProperty(
|
|
76
|
+
embedCssVariableNames.embed.selectionBackground,
|
|
77
|
+
theme?.selectionBackground ?? "#000",
|
|
78
|
+
);
|
|
79
|
+
documentStyle.setProperty(
|
|
80
|
+
embedCssVariableNames.embed.selectionTextColor,
|
|
81
|
+
theme?.selectionTextColor ?? "#fff",
|
|
82
|
+
);
|
|
83
|
+
};
|
package/src/web.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { registerWebComponents } from "./register";
|
|
2
|
+
import {
|
|
3
|
+
injectPredictionGameEmbedInWindow,
|
|
4
|
+
parsePredictionGameEmbed,
|
|
5
|
+
} from "./window";
|
|
6
|
+
|
|
7
|
+
// Register web components on load
|
|
8
|
+
registerWebComponents();
|
|
9
|
+
|
|
10
|
+
// Expose global API for programmatic initialization (optional)
|
|
11
|
+
const useawardPredictionGame = parsePredictionGameEmbed();
|
|
12
|
+
injectPredictionGameEmbedInWindow(useawardPredictionGame);
|
package/src/window.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { GameProps } from "./components/Game";
|
|
2
|
+
|
|
3
|
+
// Optional: Programmatic initialization API
|
|
4
|
+
// Allows customers to initialize elements via JavaScript if needed
|
|
5
|
+
export const initStandard = (props: GameProps & { id?: string }) => {
|
|
6
|
+
const standardElement = props.id
|
|
7
|
+
? document.getElementById(props.id)
|
|
8
|
+
: document.querySelector("useaward-prediction-game");
|
|
9
|
+
|
|
10
|
+
if (!standardElement)
|
|
11
|
+
throw new Error("<useaward-prediction-game> element not found.");
|
|
12
|
+
|
|
13
|
+
Object.assign(standardElement, props);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const parsePredictionGameEmbed = () => ({
|
|
17
|
+
initStandard,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
type PredictionGameEmbed = ReturnType<typeof parsePredictionGameEmbed>;
|
|
21
|
+
|
|
22
|
+
declare const window:
|
|
23
|
+
| {
|
|
24
|
+
UseawardPredictionGame: PredictionGameEmbed;
|
|
25
|
+
}
|
|
26
|
+
| undefined;
|
|
27
|
+
|
|
28
|
+
export const injectPredictionGameEmbedInWindow = (
|
|
29
|
+
predictionGameEmbed: PredictionGameEmbed,
|
|
30
|
+
) => {
|
|
31
|
+
if (typeof window === "undefined") return;
|
|
32
|
+
window.UseawardPredictionGame = { ...predictionGameEmbed };
|
|
33
|
+
};
|