@eventlook/sdk 1.7.6 → 1.7.8
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/.gitlab-ci.yml +51 -0
- package/README.md +7 -0
- package/dist/cjs/index.js +25692 -16
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +25674 -12
- package/dist/esm/index.js.map +1 -1
- package/dist/types/components/RichText.d.ts +7 -0
- package/dist/types/context/SeatPickerContext.d.ts +13 -0
- package/dist/types/embed.d.ts +49 -0
- package/dist/types/locales/cs.d.ts +1 -0
- package/dist/types/locales/en.d.ts +1 -0
- package/dist/types/locales/es.d.ts +1 -0
- package/dist/types/locales/pl.d.ts +1 -0
- package/dist/types/locales/sk.d.ts +1 -0
- package/dist/types/locales/uk.d.ts +1 -0
- package/dist/types/utils/render-rich-text.d.ts +2 -0
- package/dist/types/utils/types/event.type.d.ts +1 -0
- package/package.json +4 -2
- package/rollup.embed.config.mjs +74 -0
- package/src/components/RichText.tsx +33 -0
- package/src/context/SeatPickerContext.tsx +35 -0
- package/src/embed.tsx +243 -0
- package/src/form/Payment.tsx +6 -1
- package/src/form/PaymentOverviewBox.tsx +35 -19
- package/src/form/PaymentPending.tsx +7 -24
- package/src/form/ReleaseWithMerchandise.tsx +8 -1
- package/src/form/TicketForm.tsx +204 -164
- package/src/form/services/index.tsx +1 -0
- package/src/form/tickets/TicketSelectionMap.tsx +240 -73
- package/src/index.ts +4 -0
- package/src/locales/cs.tsx +1 -0
- package/src/locales/en.tsx +1 -0
- package/src/locales/es.tsx +1 -0
- package/src/locales/pl.tsx +1 -0
- package/src/locales/sk.tsx +1 -0
- package/src/locales/uk.tsx +1 -0
- package/src/react-dom-client.d.ts +11 -0
- package/src/utils/axios.ts +3 -0
- package/src/utils/render-rich-text.ts +100 -0
- package/src/utils/types/event.type.ts +1 -0
- package/dist/cjs/index-Dh-sV_wk.js +0 -41946
- package/dist/cjs/index-Dh-sV_wk.js.map +0 -1
- package/dist/cjs/index.umd-CJ-ZDtw8.js +0 -13397
- package/dist/cjs/index.umd-CJ-ZDtw8.js.map +0 -1
- package/dist/esm/index-DPHu9Sfs.js +0 -41925
- package/dist/esm/index-DPHu9Sfs.js.map +0 -1
- package/dist/esm/index.umd-DPIwz_aJ.js +0 -13395
- package/dist/esm/index.umd-DPIwz_aJ.js.map +0 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
type DeselectFn = (locationId: string) => void;
|
|
3
|
+
interface SeatPickerContextValue {
|
|
4
|
+
/** The mounted picker registers its deselect handler here (null on unmount). */
|
|
5
|
+
registerDeselect: (fn: DeselectFn | null) => void;
|
|
6
|
+
/** Ask the picker to deselect one selection for a locationId (cart remove button). */
|
|
7
|
+
deselect: DeselectFn;
|
|
8
|
+
}
|
|
9
|
+
export declare const SeatPickerProvider: React.FC<{
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
}>;
|
|
12
|
+
export declare const useSeatPicker: () => SeatPickerContextValue;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Root } from 'react-dom/client';
|
|
2
|
+
import type { IContent } from '@utils/types/global.type';
|
|
3
|
+
interface ThemeOpts {
|
|
4
|
+
/** 'light' (default) or 'dark'. */
|
|
5
|
+
mode?: 'light' | 'dark';
|
|
6
|
+
/** Brand color — buttons, step markers, accents. */
|
|
7
|
+
primaryColor?: string;
|
|
8
|
+
/** Optional secondary accent color. */
|
|
9
|
+
secondaryColor?: string;
|
|
10
|
+
/** CSS font-family for the whole form, e.g. "Georgia, serif". */
|
|
11
|
+
fontFamily?: string;
|
|
12
|
+
/** Page background behind the form. */
|
|
13
|
+
background?: string;
|
|
14
|
+
/** Card / input surface background. */
|
|
15
|
+
paper?: string;
|
|
16
|
+
/** Corner radius in px for buttons, cards and inputs. */
|
|
17
|
+
radius?: number;
|
|
18
|
+
}
|
|
19
|
+
export interface MountOptions {
|
|
20
|
+
event: string;
|
|
21
|
+
apiUrl?: string;
|
|
22
|
+
seatingIframeUrl?: string;
|
|
23
|
+
lang?: string;
|
|
24
|
+
packetaApiKey?: string;
|
|
25
|
+
stickyHeaderTop?: number;
|
|
26
|
+
theme?: ThemeOpts;
|
|
27
|
+
callbacks?: {
|
|
28
|
+
homepage: () => void;
|
|
29
|
+
tickets: () => void;
|
|
30
|
+
detail: () => void;
|
|
31
|
+
};
|
|
32
|
+
links?: {
|
|
33
|
+
termsAndConditions: string;
|
|
34
|
+
gdpr: string;
|
|
35
|
+
};
|
|
36
|
+
/** Preselect a ticket release/type by id. */
|
|
37
|
+
selectedReleaseId?: number;
|
|
38
|
+
/** Content overrides — hide birth-date/gender fields, custom confirmation copy/image. */
|
|
39
|
+
content?: IContent;
|
|
40
|
+
/** Show combined T&C for these company ids (multi-organizer events). */
|
|
41
|
+
termsAndConditionsCompanies?: string[];
|
|
42
|
+
/** Auto-scroll to the top of the form after each step change. */
|
|
43
|
+
autoscrollAfterViewChange?: boolean;
|
|
44
|
+
/** Paydroid (Sobit / TapyGo) cashless API key. */
|
|
45
|
+
paydroidApiKey?: string;
|
|
46
|
+
}
|
|
47
|
+
export declare function mount(target: string | Element, userOpts?: Partial<MountOptions>): Root | undefined;
|
|
48
|
+
export declare function unmount(target: string | Element): void;
|
|
49
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eventlook/sdk",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.8",
|
|
4
4
|
"main": "dist/cjs/index.js",
|
|
5
5
|
"module": "dist/esm/index.js",
|
|
6
6
|
"types": "dist/types/index.d.ts",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
16
|
"build": "rollup -c && tsc -p tsconfig.build.json",
|
|
17
|
+
"build:embed": "rollup -c rollup.embed.config.mjs",
|
|
17
18
|
"prepare": "yarn run build",
|
|
18
19
|
"push": "yalc push --force",
|
|
19
20
|
"push:initial": "yarn build && yalc push --force",
|
|
@@ -54,8 +55,9 @@
|
|
|
54
55
|
"@rollup/plugin-json": "^6.1.0",
|
|
55
56
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
56
57
|
"@rollup/plugin-replace": "^6.0.3",
|
|
58
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
57
59
|
"@rollup/plugin-typescript": "^12.3.0",
|
|
58
|
-
"@seat-picker/seat-picker-sdk": "^
|
|
60
|
+
"@seat-picker/seat-picker-sdk": "^2.0.0",
|
|
59
61
|
"@stripe/react-stripe-js": "^6.6.0",
|
|
60
62
|
"@stripe/stripe-js": "^9.7.0",
|
|
61
63
|
"@types/js-cookie": "^3.0.6",
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Standalone browser build of the SDK — a single self-contained IIFE that bundles
|
|
3
|
+
* React, MUI, Emotion and the seat-picker SDK, exposing window.Eventlook.mount().
|
|
4
|
+
* Hosted unversioned by Eventlook so third-party sites never pin a version.
|
|
5
|
+
*
|
|
6
|
+
* yarn build:embed -> dist/embed/eventlook.js
|
|
7
|
+
*/
|
|
8
|
+
import typescript from '@rollup/plugin-typescript';
|
|
9
|
+
import resolve from '@rollup/plugin-node-resolve';
|
|
10
|
+
import commonjs from '@rollup/plugin-commonjs';
|
|
11
|
+
import json from '@rollup/plugin-json';
|
|
12
|
+
import replace from '@rollup/plugin-replace';
|
|
13
|
+
import alias from '@rollup/plugin-alias';
|
|
14
|
+
import terser from '@rollup/plugin-terser';
|
|
15
|
+
|
|
16
|
+
export default {
|
|
17
|
+
input: './src/embed.tsx',
|
|
18
|
+
output: {
|
|
19
|
+
file: 'dist/embed/eventlook.js',
|
|
20
|
+
format: 'iife',
|
|
21
|
+
name: 'EventlookEmbedBundle',
|
|
22
|
+
sourcemap: true,
|
|
23
|
+
inlineDynamicImports: true,
|
|
24
|
+
// Shim Node globals some deps reference, without a bundler define step.
|
|
25
|
+
intro:
|
|
26
|
+
'var process = (typeof process !== "undefined") ? process : { env: { NODE_ENV: "production" } };' +
|
|
27
|
+
'var global = (typeof global !== "undefined") ? global : (typeof globalThis !== "undefined" ? globalThis : window);',
|
|
28
|
+
},
|
|
29
|
+
onwarn(warning, warn) {
|
|
30
|
+
// Bundling a large app surfaces benign warnings — keep the log readable.
|
|
31
|
+
if (
|
|
32
|
+
warning.code === 'MODULE_LEVEL_DIRECTIVE' ||
|
|
33
|
+
warning.code === 'THIS_IS_UNDEFINED' ||
|
|
34
|
+
warning.code === 'CIRCULAR_DEPENDENCY' ||
|
|
35
|
+
warning.code === 'SOURCEMAP_ERROR'
|
|
36
|
+
) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
warn(warning);
|
|
40
|
+
},
|
|
41
|
+
plugins: [
|
|
42
|
+
alias({
|
|
43
|
+
entries: [
|
|
44
|
+
{ find: 'lottie-web', replacement: 'lottie-web/build/player/lottie_light.js' },
|
|
45
|
+
],
|
|
46
|
+
}),
|
|
47
|
+
replace({
|
|
48
|
+
preventAssignment: true,
|
|
49
|
+
values: {
|
|
50
|
+
'process.env.NODE_ENV': JSON.stringify('production'),
|
|
51
|
+
'require("form-data")': '{}',
|
|
52
|
+
"require('form-data')": '{}',
|
|
53
|
+
'require("zlib")': '{}',
|
|
54
|
+
"require('zlib')": '{}',
|
|
55
|
+
},
|
|
56
|
+
}),
|
|
57
|
+
json({ compact: true, preferConst: true, namedExports: true }),
|
|
58
|
+
resolve({
|
|
59
|
+
browser: true,
|
|
60
|
+
preferBuiltins: false,
|
|
61
|
+
moduleDirectories: ['node_modules'],
|
|
62
|
+
extensions: ['.mjs', '.js', '.jsx', '.ts', '.tsx', '.json'],
|
|
63
|
+
}),
|
|
64
|
+
commonjs(),
|
|
65
|
+
typescript({
|
|
66
|
+
tsconfig: './tsconfig.json',
|
|
67
|
+
noEmitOnError: false,
|
|
68
|
+
compilerOptions: { declaration: false, declarationMap: false, sourceMap: true },
|
|
69
|
+
}),
|
|
70
|
+
terser(),
|
|
71
|
+
],
|
|
72
|
+
// Bundle everything — the whole point of the hosted script is zero deps on the host.
|
|
73
|
+
external: [],
|
|
74
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { styled } from '@mui/material/styles';
|
|
3
|
+
import { renderRichTextToHtml } from '@utils/render-rich-text';
|
|
4
|
+
|
|
5
|
+
// Renders sanitized rich text (ProseMirror JSON or legacy HTML). Empty
|
|
6
|
+
// paragraphs are emitted as literal `<p></p>` and given height via CSS,
|
|
7
|
+
// matching frontend/src/components/RichText.tsx. See render-rich-text.ts.
|
|
8
|
+
const RootStyle = styled('div')(({ theme }) => ({
|
|
9
|
+
'& p': {
|
|
10
|
+
margin: 0,
|
|
11
|
+
},
|
|
12
|
+
'& p:empty': {
|
|
13
|
+
minHeight: '1em',
|
|
14
|
+
},
|
|
15
|
+
'& a': {
|
|
16
|
+
color: theme.palette.primary.main,
|
|
17
|
+
textDecoration: 'underline',
|
|
18
|
+
},
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
type Props = {
|
|
22
|
+
content: string | null | undefined;
|
|
23
|
+
className?: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const RichText: React.FC<Props> = ({ content, className }) => (
|
|
27
|
+
<RootStyle
|
|
28
|
+
className={className}
|
|
29
|
+
dangerouslySetInnerHTML={{ __html: renderRichTextToHtml(content) }}
|
|
30
|
+
/>
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
export default RichText;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React, { createContext, useCallback, useContext, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
type DeselectFn = (locationId: string) => void;
|
|
4
|
+
|
|
5
|
+
interface SeatPickerContextValue {
|
|
6
|
+
/** The mounted picker registers its deselect handler here (null on unmount). */
|
|
7
|
+
registerDeselect: (fn: DeselectFn | null) => void;
|
|
8
|
+
/** Ask the picker to deselect one selection for a locationId (cart remove button). */
|
|
9
|
+
deselect: DeselectFn;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const SeatPickerContext = createContext<SeatPickerContextValue>({
|
|
13
|
+
registerDeselect: () => {},
|
|
14
|
+
deselect: () => {},
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export const SeatPickerProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
18
|
+
const deselectRef = useRef<DeselectFn | null>(null);
|
|
19
|
+
|
|
20
|
+
const registerDeselect = useCallback((fn: DeselectFn | null) => {
|
|
21
|
+
deselectRef.current = fn;
|
|
22
|
+
}, []);
|
|
23
|
+
|
|
24
|
+
const deselect = useCallback((locationId: string) => {
|
|
25
|
+
deselectRef.current?.(locationId);
|
|
26
|
+
}, []);
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<SeatPickerContext.Provider value={{ registerDeselect, deselect }}>
|
|
30
|
+
{children}
|
|
31
|
+
</SeatPickerContext.Provider>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const useSeatPicker = () => useContext(SeatPickerContext);
|
package/src/embed.tsx
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Standalone browser entry for the Eventlook SDK.
|
|
3
|
+
*
|
|
4
|
+
* This is the same in-page OrderFormSdk, packaged as a self-contained <script>
|
|
5
|
+
* (React + MUI + everything bundled) so a third-party site can drop it in ONCE
|
|
6
|
+
* and never pin/upgrade a version — Eventlook redeploys the hosted file and every
|
|
7
|
+
* site gets it. It renders in-page (not in an iframe), so the seat picker overlays
|
|
8
|
+
* the real viewport and mobile seating works correctly.
|
|
9
|
+
*
|
|
10
|
+
* Declarative:
|
|
11
|
+
* <div data-eventlook-event="my-event-slug"></div>
|
|
12
|
+
* <script async src="https://cdn.eventlook.cz/eventlook.js"></script>
|
|
13
|
+
*
|
|
14
|
+
* Imperative:
|
|
15
|
+
* Eventlook.mount('#tickets', {
|
|
16
|
+
* event: 'my-event-slug',
|
|
17
|
+
* theme: { primaryColor: '#9b1d43' },
|
|
18
|
+
* callbacks: { detail: () => location.assign('/program') },
|
|
19
|
+
* });
|
|
20
|
+
*/
|
|
21
|
+
import React, { useMemo, useState } from 'react';
|
|
22
|
+
import { createRoot, Root } from 'react-dom/client';
|
|
23
|
+
import { ThemeProvider, createTheme, ScopedCssBaseline, Snackbar, Alert } from '@mui/material';
|
|
24
|
+
import { OrderFormSdk } from './index';
|
|
25
|
+
import { Languages } from '@utils/data/language';
|
|
26
|
+
import type { IContent } from '@utils/types/global.type';
|
|
27
|
+
|
|
28
|
+
type SnackVariant = 'success' | 'error';
|
|
29
|
+
|
|
30
|
+
interface ThemeOpts {
|
|
31
|
+
/** 'light' (default) or 'dark'. */
|
|
32
|
+
mode?: 'light' | 'dark';
|
|
33
|
+
/** Brand color — buttons, step markers, accents. */
|
|
34
|
+
primaryColor?: string;
|
|
35
|
+
/** Optional secondary accent color. */
|
|
36
|
+
secondaryColor?: string;
|
|
37
|
+
/** CSS font-family for the whole form, e.g. "Georgia, serif". */
|
|
38
|
+
fontFamily?: string;
|
|
39
|
+
/** Page background behind the form. */
|
|
40
|
+
background?: string;
|
|
41
|
+
/** Card / input surface background. */
|
|
42
|
+
paper?: string;
|
|
43
|
+
/** Corner radius in px for buttons, cards and inputs. */
|
|
44
|
+
radius?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface MountOptions {
|
|
48
|
+
event: string;
|
|
49
|
+
apiUrl?: string;
|
|
50
|
+
seatingIframeUrl?: string;
|
|
51
|
+
lang?: string;
|
|
52
|
+
packetaApiKey?: string;
|
|
53
|
+
stickyHeaderTop?: number;
|
|
54
|
+
theme?: ThemeOpts;
|
|
55
|
+
callbacks?: { homepage: () => void; tickets: () => void; detail: () => void };
|
|
56
|
+
links?: { termsAndConditions: string; gdpr: string };
|
|
57
|
+
/** Preselect a ticket release/type by id. */
|
|
58
|
+
selectedReleaseId?: number;
|
|
59
|
+
/** Content overrides — hide birth-date/gender fields, custom confirmation copy/image. */
|
|
60
|
+
content?: IContent;
|
|
61
|
+
/** Show combined T&C for these company ids (multi-organizer events). */
|
|
62
|
+
termsAndConditionsCompanies?: string[];
|
|
63
|
+
/** Auto-scroll to the top of the form after each step change. */
|
|
64
|
+
autoscrollAfterViewChange?: boolean;
|
|
65
|
+
/** Paydroid (Sobit / TapyGo) cashless API key. */
|
|
66
|
+
paydroidApiKey?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const DEFAULTS = {
|
|
70
|
+
apiUrl: 'https://api.eventlook.cz',
|
|
71
|
+
seatingIframeUrl: 'https://iframe.seatily.com',
|
|
72
|
+
lang: 'cs',
|
|
73
|
+
termsAndConditions: 'https://www.eventlook.cz/obchodni-podminky/',
|
|
74
|
+
gdpr: 'https://www.eventlook.cz/gdpr/',
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const noop = () => {};
|
|
78
|
+
|
|
79
|
+
const EmbedApp: React.FC<{ opts: MountOptions }> = ({ opts }) => {
|
|
80
|
+
const [snack, setSnack] = useState<{ text: string; variant: SnackVariant } | null>(null);
|
|
81
|
+
|
|
82
|
+
const theme = useMemo(() => {
|
|
83
|
+
const t = opts.theme || {};
|
|
84
|
+
return createTheme({
|
|
85
|
+
palette: {
|
|
86
|
+
mode: t.mode || 'light',
|
|
87
|
+
...(t.primaryColor ? { primary: { main: t.primaryColor } } : {}),
|
|
88
|
+
...(t.secondaryColor ? { secondary: { main: t.secondaryColor } } : {}),
|
|
89
|
+
...(t.background || t.paper
|
|
90
|
+
? {
|
|
91
|
+
background: {
|
|
92
|
+
...(t.background ? { default: t.background } : {}),
|
|
93
|
+
...(t.paper ? { paper: t.paper } : {}),
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
: {}),
|
|
97
|
+
},
|
|
98
|
+
...(t.fontFamily ? { typography: { fontFamily: t.fontFamily } } : {}),
|
|
99
|
+
...(t.radius != null ? { shape: { borderRadius: t.radius } } : {}),
|
|
100
|
+
});
|
|
101
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
102
|
+
}, []);
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<ThemeProvider theme={theme}>
|
|
106
|
+
<ScopedCssBaseline>
|
|
107
|
+
<OrderFormSdk
|
|
108
|
+
eventSlug={opts.event}
|
|
109
|
+
apiUrl={opts.apiUrl || DEFAULTS.apiUrl}
|
|
110
|
+
seatingIframeUrl={opts.seatingIframeUrl || DEFAULTS.seatingIframeUrl}
|
|
111
|
+
lang={(opts.lang || DEFAULTS.lang) as Languages}
|
|
112
|
+
selectedReleaseId={opts.selectedReleaseId}
|
|
113
|
+
content={opts.content}
|
|
114
|
+
callbacks={opts.callbacks || { homepage: noop, tickets: noop, detail: noop }}
|
|
115
|
+
links={
|
|
116
|
+
opts.links || {
|
|
117
|
+
termsAndConditions: DEFAULTS.termsAndConditions,
|
|
118
|
+
gdpr: DEFAULTS.gdpr,
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
options={{
|
|
122
|
+
packetaApiKey: opts.packetaApiKey,
|
|
123
|
+
stickyHeaderTop: opts.stickyHeaderTop ?? 0,
|
|
124
|
+
termsAndConditionsCompanies: opts.termsAndConditionsCompanies,
|
|
125
|
+
autoscrollAfterViewChange: opts.autoscrollAfterViewChange,
|
|
126
|
+
paydroidApiKey: opts.paydroidApiKey,
|
|
127
|
+
// In-page render (not an iframe) — the seat picker overlays the real
|
|
128
|
+
// viewport, so mobile seating works.
|
|
129
|
+
isIframe: false,
|
|
130
|
+
}}
|
|
131
|
+
slots={{
|
|
132
|
+
showSnackbar: (text, o) => setSnack({ text, variant: o?.variant || 'success' }),
|
|
133
|
+
}}
|
|
134
|
+
/>
|
|
135
|
+
<Snackbar
|
|
136
|
+
open={!!snack}
|
|
137
|
+
autoHideDuration={5000}
|
|
138
|
+
onClose={() => setSnack(null)}
|
|
139
|
+
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
|
|
140
|
+
>
|
|
141
|
+
{snack ? (
|
|
142
|
+
<Alert severity={snack.variant} variant="filled" onClose={() => setSnack(null)}>
|
|
143
|
+
{snack.text}
|
|
144
|
+
</Alert>
|
|
145
|
+
) : (
|
|
146
|
+
<span />
|
|
147
|
+
)}
|
|
148
|
+
</Snackbar>
|
|
149
|
+
</ScopedCssBaseline>
|
|
150
|
+
</ThemeProvider>
|
|
151
|
+
);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const roots = new WeakMap<Element, Root>();
|
|
155
|
+
|
|
156
|
+
function readAttrTheme(el: Element): ThemeOpts | undefined {
|
|
157
|
+
const mode = el.getAttribute('data-eventlook-mode');
|
|
158
|
+
const primary = el.getAttribute('data-eventlook-primary');
|
|
159
|
+
const secondary = el.getAttribute('data-eventlook-secondary');
|
|
160
|
+
const font = el.getAttribute('data-eventlook-font');
|
|
161
|
+
const background = el.getAttribute('data-eventlook-background');
|
|
162
|
+
const paper = el.getAttribute('data-eventlook-paper');
|
|
163
|
+
const radius = el.getAttribute('data-eventlook-radius');
|
|
164
|
+
if (!mode && !primary && !secondary && !font && !background && !paper && !radius) {
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
mode: mode === 'dark' ? 'dark' : mode === 'light' ? 'light' : undefined,
|
|
169
|
+
primaryColor: primary || undefined,
|
|
170
|
+
secondaryColor: secondary || undefined,
|
|
171
|
+
fontFamily: font || undefined,
|
|
172
|
+
background: background || undefined,
|
|
173
|
+
paper: paper || undefined,
|
|
174
|
+
radius: radius != null ? Number(radius) : undefined,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function mount(
|
|
179
|
+
target: string | Element,
|
|
180
|
+
userOpts: Partial<MountOptions> = {}
|
|
181
|
+
): Root | undefined {
|
|
182
|
+
const el = typeof target === 'string' ? document.querySelector(target) : target;
|
|
183
|
+
if (!el) {
|
|
184
|
+
console.error('[eventlook] mount target not found:', target);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const event = userOpts.event || el.getAttribute('data-eventlook-event') || '';
|
|
188
|
+
if (!event) {
|
|
189
|
+
console.error('[eventlook] missing event slug');
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (roots.has(el)) return roots.get(el);
|
|
193
|
+
|
|
194
|
+
const opts: MountOptions = {
|
|
195
|
+
event,
|
|
196
|
+
apiUrl: userOpts.apiUrl || el.getAttribute('data-eventlook-api') || DEFAULTS.apiUrl,
|
|
197
|
+
seatingIframeUrl:
|
|
198
|
+
userOpts.seatingIframeUrl ||
|
|
199
|
+
el.getAttribute('data-eventlook-seating') ||
|
|
200
|
+
DEFAULTS.seatingIframeUrl,
|
|
201
|
+
lang: userOpts.lang || el.getAttribute('data-eventlook-lang') || DEFAULTS.lang,
|
|
202
|
+
packetaApiKey: userOpts.packetaApiKey || el.getAttribute('data-eventlook-packeta') || undefined,
|
|
203
|
+
stickyHeaderTop: userOpts.stickyHeaderTop,
|
|
204
|
+
theme: userOpts.theme || readAttrTheme(el),
|
|
205
|
+
callbacks: userOpts.callbacks,
|
|
206
|
+
links: userOpts.links,
|
|
207
|
+
selectedReleaseId: userOpts.selectedReleaseId,
|
|
208
|
+
content: userOpts.content,
|
|
209
|
+
termsAndConditionsCompanies: userOpts.termsAndConditionsCompanies,
|
|
210
|
+
autoscrollAfterViewChange: userOpts.autoscrollAfterViewChange,
|
|
211
|
+
paydroidApiKey: userOpts.paydroidApiKey,
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const root = createRoot(el);
|
|
215
|
+
roots.set(el, root);
|
|
216
|
+
root.render(<EmbedApp opts={opts} />);
|
|
217
|
+
return root;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function unmount(target: string | Element): void {
|
|
221
|
+
const el = typeof target === 'string' ? document.querySelector(target) : target;
|
|
222
|
+
if (!el) return;
|
|
223
|
+
const root = roots.get(el);
|
|
224
|
+
if (root) {
|
|
225
|
+
root.unmount();
|
|
226
|
+
roots.delete(el);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function autoInit() {
|
|
231
|
+
document.querySelectorAll('[data-eventlook-event]').forEach((el) => mount(el));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Expose the global API used by the hosted snippet.
|
|
235
|
+
type EventlookGlobal = { mount: typeof mount; unmount: typeof unmount };
|
|
236
|
+
const w = window as unknown as { Eventlook?: Partial<EventlookGlobal> };
|
|
237
|
+
w.Eventlook = Object.assign(w.Eventlook || {}, { mount, unmount });
|
|
238
|
+
|
|
239
|
+
if (document.readyState === 'loading') {
|
|
240
|
+
document.addEventListener('DOMContentLoaded', autoInit);
|
|
241
|
+
} else {
|
|
242
|
+
autoInit();
|
|
243
|
+
}
|
package/src/form/Payment.tsx
CHANGED
|
@@ -28,7 +28,7 @@ import { ITicketForm, ITicketFormTicket } from '@utils/types/ticket.type';
|
|
|
28
28
|
import useGlobal from '@hooks/useGlobal';
|
|
29
29
|
import { IPromoCode } from '@utils/types/promo-code.type';
|
|
30
30
|
import { PromoCodeTypes } from '@utils/data/promo-code';
|
|
31
|
-
import
|
|
31
|
+
import throttle from 'lodash/throttle';
|
|
32
32
|
import { Iconify } from '@components';
|
|
33
33
|
import { IEventProductForm } from '@utils/types/product.type';
|
|
34
34
|
|
|
@@ -278,6 +278,11 @@ const Payment: React.FC<Props> = ({ event, stripeReady }) => {
|
|
|
278
278
|
<PaymentItem
|
|
279
279
|
key={payment.type}
|
|
280
280
|
active={!!paymentMethodId && Number(paymentMethodId) === payment.id}
|
|
281
|
+
className={`eventlook-payment-option${
|
|
282
|
+
!!paymentMethodId && Number(paymentMethodId) === payment.id
|
|
283
|
+
? ' eventlook-payment-option--active'
|
|
284
|
+
: ''
|
|
285
|
+
}`}
|
|
281
286
|
>
|
|
282
287
|
<FormControlLabel
|
|
283
288
|
value={payment.id}
|