@session-services/web-elements 0.1.17 → 0.1.18
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 +4 -2
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/session-services-elements.cjs.js +1 -1
- package/dist/cjs/session-services-formatted-text.cjs.entry.js +1759 -0
- package/dist/cjs/session-services-ticketing.cjs.entry.js +2071 -834
- package/dist/collection/collection-manifest.json +1 -0
- package/dist/collection/components/session-services-formatted-text/session-services-formatted-text.css +135 -0
- package/dist/collection/components/session-services-formatted-text/session-services-formatted-text.js +162 -0
- package/dist/collection/components/session-services-ticketing/session-services-ticketing.css +175 -3
- package/dist/collection/components/session-services-ticketing/session-services-ticketing.js +50 -9
- package/dist/collection/index.js +1 -11
- package/dist/components/session-services-formatted-text.d.ts +11 -0
- package/dist/components/session-services-formatted-text.js +1786 -0
- package/dist/components/session-services-ticketing.js +2073 -835
- package/dist/esm/loader.js +1 -1
- package/dist/esm/session-services-elements.js +1 -1
- package/dist/esm/session-services-formatted-text.entry.js +1757 -0
- package/dist/esm/session-services-ticketing.entry.js +2071 -834
- package/dist/session-services-elements/p-62a8e2cc.entry.js +1 -0
- package/dist/session-services-elements/p-a2404c46.entry.js +1 -0
- package/dist/session-services-elements/session-services-elements.esm.js +1 -1
- package/dist/types/components/session-services-formatted-text/session-services-formatted-text.d.ts +19 -0
- package/dist/types/components/session-services-ticketing/session-services-ticketing.d.ts +6 -0
- package/dist/types/components.d.ts +29 -0
- package/dist/types/index.d.ts +1 -0
- package/package.json +10 -5
- package/dist/session-services-elements/p-448028d4.entry.js +0 -1
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
/**
|
|
3
|
+
* Shadcn-compatible design tokens for formatted text component.
|
|
4
|
+
* Override these CSS custom properties to customize the appearance.
|
|
5
|
+
*
|
|
6
|
+
* @prop --foreground: Primary text color.
|
|
7
|
+
* @prop --muted-foreground: Secondary/muted text color.
|
|
8
|
+
* @prop --primary: Link color.
|
|
9
|
+
* @prop --border: Divider color.
|
|
10
|
+
* @prop --ring: Focus ring color.
|
|
11
|
+
* @prop --radius: Border radius.
|
|
12
|
+
* @prop --spacing-md: Medium spacing (1rem).
|
|
13
|
+
* @prop --spacing-lg: Large spacing (1.5rem).
|
|
14
|
+
* @prop --font-family: Base font family.
|
|
15
|
+
* @prop --transition-fast: Fast transition duration.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/* Light theme defaults (shadcn zinc) */
|
|
19
|
+
--foreground: oklch(0.145 0 0);
|
|
20
|
+
--muted-foreground: oklch(0.556 0 0);
|
|
21
|
+
--primary: oklch(0.42 0.24 264.376);
|
|
22
|
+
--border: oklch(0.922 0 0);
|
|
23
|
+
--ring: oklch(0.708 0 0);
|
|
24
|
+
--radius: 0.5rem;
|
|
25
|
+
--spacing-md: 1rem;
|
|
26
|
+
--spacing-lg: 1.5rem;
|
|
27
|
+
--font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
|
28
|
+
"Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
|
|
29
|
+
--transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
30
|
+
|
|
31
|
+
/* Internal tokens with external override fallbacks */
|
|
32
|
+
--internal-foreground: var(--foreground, oklch(0.145 0 0));
|
|
33
|
+
--internal-muted-foreground: var(--muted-foreground, oklch(0.556 0 0));
|
|
34
|
+
--internal-primary: var(--primary, oklch(0.42 0.24 264.376));
|
|
35
|
+
--internal-border: var(--border, oklch(0.922 0 0));
|
|
36
|
+
--internal-ring: var(--ring, oklch(0.708 0 0));
|
|
37
|
+
--internal-radius: var(--radius, 0.5rem);
|
|
38
|
+
--internal-spacing-md: var(--spacing-md, 1rem);
|
|
39
|
+
--internal-spacing-lg: var(--spacing-lg, 1.5rem);
|
|
40
|
+
--internal-font-family: var(
|
|
41
|
+
--font-family,
|
|
42
|
+
ui-sans-serif,
|
|
43
|
+
system-ui,
|
|
44
|
+
-apple-system,
|
|
45
|
+
BlinkMacSystemFont,
|
|
46
|
+
"Segoe UI",
|
|
47
|
+
Roboto,
|
|
48
|
+
"Helvetica Neue",
|
|
49
|
+
Arial,
|
|
50
|
+
"Noto Sans",
|
|
51
|
+
sans-serif
|
|
52
|
+
);
|
|
53
|
+
--internal-transition-fast: var(
|
|
54
|
+
--transition-fast,
|
|
55
|
+
150ms cubic-bezier(0.4, 0, 0.2, 1)
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
display: block;
|
|
59
|
+
font-family: var(--internal-font-family);
|
|
60
|
+
line-height: 1.6;
|
|
61
|
+
color: var(--internal-foreground);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
:host([data-theme="dark"]) {
|
|
65
|
+
--foreground: oklch(0.96 0 0);
|
|
66
|
+
--muted-foreground: oklch(0.78 0 0);
|
|
67
|
+
--primary: oklch(0.623 0.214 259.815);
|
|
68
|
+
--border: oklch(0.29 0 0);
|
|
69
|
+
--ring: oklch(0.6 0 0);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* Reset box model */
|
|
73
|
+
* {
|
|
74
|
+
box-sizing: border-box;
|
|
75
|
+
margin: 0;
|
|
76
|
+
padding: 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
*:focus:not(:focus-visible) {
|
|
80
|
+
outline: none;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
*:focus-visible {
|
|
84
|
+
outline: 2px solid var(--internal-ring);
|
|
85
|
+
outline-offset: 2px;
|
|
86
|
+
border-radius: calc(var(--internal-radius) - 4px);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.formatted-text {
|
|
90
|
+
color: var(--internal-foreground);
|
|
91
|
+
white-space: pre-wrap;
|
|
92
|
+
word-wrap: break-word;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.formatted-link {
|
|
96
|
+
color: var(--internal-primary);
|
|
97
|
+
text-decoration: none;
|
|
98
|
+
transition: color var(--internal-transition-fast);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.formatted-link:hover {
|
|
102
|
+
color: oklch(from var(--internal-primary) calc(l + 0.1) c h);
|
|
103
|
+
text-decoration: underline;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.formatted-divider {
|
|
107
|
+
height: 0;
|
|
108
|
+
border: none;
|
|
109
|
+
border-top: 1px solid var(--internal-border);
|
|
110
|
+
margin: var(--internal-spacing-md) 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.soundcloud-embed {
|
|
114
|
+
margin: var(--internal-spacing-md) 0;
|
|
115
|
+
border-radius: var(--internal-radius);
|
|
116
|
+
overflow: hidden;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.soundcloud-embed iframe {
|
|
120
|
+
display: block;
|
|
121
|
+
border: none;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* Screen reader only - visually hidden but accessible */
|
|
125
|
+
.sr-only {
|
|
126
|
+
position: absolute;
|
|
127
|
+
width: 1px;
|
|
128
|
+
height: 1px;
|
|
129
|
+
padding: 0;
|
|
130
|
+
margin: -1px;
|
|
131
|
+
overflow: hidden;
|
|
132
|
+
clip: rect(0, 0, 0, 0);
|
|
133
|
+
white-space: nowrap;
|
|
134
|
+
border: 0;
|
|
135
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import {
|
|
2
|
+
// biome-ignore lint/correctness/noUnusedImports: h is required by Stencil JSX transform
|
|
3
|
+
h, Host, } from "@stencil/core";
|
|
4
|
+
import { getDisplayUrl, isSoundCloudUrl, parseFormattedText, } from "@session-services/utils/formatting";
|
|
5
|
+
export class SessionServicesFormattedText {
|
|
6
|
+
text = "";
|
|
7
|
+
theme = "system";
|
|
8
|
+
currentTheme = "light";
|
|
9
|
+
segments = [];
|
|
10
|
+
mediaQueryList;
|
|
11
|
+
componentWillLoad() {
|
|
12
|
+
this.setupTheme();
|
|
13
|
+
this.parseText();
|
|
14
|
+
}
|
|
15
|
+
textChanged() {
|
|
16
|
+
this.parseText();
|
|
17
|
+
}
|
|
18
|
+
themeChanged() {
|
|
19
|
+
this.setupTheme();
|
|
20
|
+
}
|
|
21
|
+
disconnectedCallback() {
|
|
22
|
+
if (this.mediaQueryList) {
|
|
23
|
+
this.mediaQueryList.removeEventListener("change", this.handleSystemThemeChange);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
setupTheme() {
|
|
27
|
+
if (this.mediaQueryList) {
|
|
28
|
+
this.mediaQueryList.removeEventListener("change", this.handleSystemThemeChange);
|
|
29
|
+
}
|
|
30
|
+
if (this.theme === "system") {
|
|
31
|
+
// Guard for testing environments (JSDOM) where matchMedia may not exist
|
|
32
|
+
if (typeof globalThis.matchMedia === "function") {
|
|
33
|
+
this.mediaQueryList = globalThis.matchMedia("(prefers-color-scheme: dark)");
|
|
34
|
+
this.currentTheme = this.mediaQueryList.matches ? "dark" : "light";
|
|
35
|
+
this.mediaQueryList.addEventListener("change", this.handleSystemThemeChange);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
this.currentTheme = "light";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
this.currentTheme = this.theme;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
handleSystemThemeChange = (e) => {
|
|
46
|
+
if (this.theme === "system") {
|
|
47
|
+
this.currentTheme = e.matches ? "dark" : "light";
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
parseText() {
|
|
51
|
+
this.segments = parseFormattedText(this.text);
|
|
52
|
+
}
|
|
53
|
+
renderLink(segment, key) {
|
|
54
|
+
const displayText = segment.linkType === "url"
|
|
55
|
+
? getDisplayUrl(segment.href || "")
|
|
56
|
+
: segment.content;
|
|
57
|
+
return (h("a", { key: key, href: segment.href, target: "_blank", rel: "noopener noreferrer", class: "formatted-link" }, displayText, h("span", { class: "sr-only" }, " (opens in new tab)")));
|
|
58
|
+
}
|
|
59
|
+
renderSoundCloudEmbed(url, key, label) {
|
|
60
|
+
if (!isSoundCloudUrl(url)) {
|
|
61
|
+
return this.renderLink({
|
|
62
|
+
type: "link",
|
|
63
|
+
content: url,
|
|
64
|
+
href: url,
|
|
65
|
+
linkType: "url",
|
|
66
|
+
}, key);
|
|
67
|
+
}
|
|
68
|
+
const embedUrl = `https://w.soundcloud.com/player/?url=${encodeURIComponent(url)}&color=%23ff5500&auto_play=false&hide_related=true&show_comments=false&show_user=true&show_reposts=false&show_teaser=false`;
|
|
69
|
+
// Extract path for accessible title (e.g., "artist/track-name" from URL)
|
|
70
|
+
const pathLabel = new URL(url).pathname.slice(1).replace(/-/g, " ");
|
|
71
|
+
const iframeTitle = `SoundCloud: ${label || pathLabel || url}`;
|
|
72
|
+
return (h("div", { key: key, class: "soundcloud-embed" }, h("iframe", { width: "100%", height: "166", scrolling: "no", allow: "autoplay", sandbox: "allow-scripts allow-same-origin allow-popups", src: embedUrl, title: iframeTitle, loading: "lazy" })));
|
|
73
|
+
}
|
|
74
|
+
renderSegment(segment, index) {
|
|
75
|
+
switch (segment.type) {
|
|
76
|
+
case "text":
|
|
77
|
+
return h("span", { key: index }, segment.content);
|
|
78
|
+
case "link":
|
|
79
|
+
return this.renderLink(segment, index);
|
|
80
|
+
case "divider":
|
|
81
|
+
return h("hr", { key: index, class: "formatted-divider" });
|
|
82
|
+
case "soundcloud":
|
|
83
|
+
return this.renderSoundCloudEmbed(segment.href || "", index, segment.content);
|
|
84
|
+
default:
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
render() {
|
|
89
|
+
return (h(Host, { key: '1a7f36efb8cdbd1efdaa97c36b60593a13c99c4e', "data-theme": this.currentTheme }, h("div", { key: 'fce1176ff0ef2ea5c3f1b282ab9a52554491f9bf', class: "formatted-text" }, this.segments.map((segment, index) => this.renderSegment(segment, index)))));
|
|
90
|
+
}
|
|
91
|
+
static get is() { return "session-services-formatted-text"; }
|
|
92
|
+
static get encapsulation() { return "shadow"; }
|
|
93
|
+
static get originalStyleUrls() {
|
|
94
|
+
return {
|
|
95
|
+
"$": ["session-services-formatted-text.css"]
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
static get styleUrls() {
|
|
99
|
+
return {
|
|
100
|
+
"$": ["session-services-formatted-text.css"]
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
static get properties() {
|
|
104
|
+
return {
|
|
105
|
+
"text": {
|
|
106
|
+
"type": "string",
|
|
107
|
+
"mutable": false,
|
|
108
|
+
"complexType": {
|
|
109
|
+
"original": "string",
|
|
110
|
+
"resolved": "string",
|
|
111
|
+
"references": {}
|
|
112
|
+
},
|
|
113
|
+
"required": false,
|
|
114
|
+
"optional": false,
|
|
115
|
+
"docs": {
|
|
116
|
+
"tags": [],
|
|
117
|
+
"text": ""
|
|
118
|
+
},
|
|
119
|
+
"getter": false,
|
|
120
|
+
"setter": false,
|
|
121
|
+
"reflect": false,
|
|
122
|
+
"attribute": "text",
|
|
123
|
+
"defaultValue": "\"\""
|
|
124
|
+
},
|
|
125
|
+
"theme": {
|
|
126
|
+
"type": "string",
|
|
127
|
+
"mutable": false,
|
|
128
|
+
"complexType": {
|
|
129
|
+
"original": "\"light\" | \"dark\" | \"system\"",
|
|
130
|
+
"resolved": "\"dark\" | \"light\" | \"system\"",
|
|
131
|
+
"references": {}
|
|
132
|
+
},
|
|
133
|
+
"required": false,
|
|
134
|
+
"optional": false,
|
|
135
|
+
"docs": {
|
|
136
|
+
"tags": [],
|
|
137
|
+
"text": ""
|
|
138
|
+
},
|
|
139
|
+
"getter": false,
|
|
140
|
+
"setter": false,
|
|
141
|
+
"reflect": false,
|
|
142
|
+
"attribute": "theme",
|
|
143
|
+
"defaultValue": "\"system\""
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
static get states() {
|
|
148
|
+
return {
|
|
149
|
+
"currentTheme": {},
|
|
150
|
+
"segments": {}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
static get watchers() {
|
|
154
|
+
return [{
|
|
155
|
+
"propName": "text",
|
|
156
|
+
"methodName": "textChanged"
|
|
157
|
+
}, {
|
|
158
|
+
"propName": "theme",
|
|
159
|
+
"methodName": "themeChanged"
|
|
160
|
+
}];
|
|
161
|
+
}
|
|
162
|
+
}
|
package/dist/collection/components/session-services-ticketing/session-services-ticketing.css
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
* @prop --border: Border color for cards/controls.
|
|
20
20
|
* @prop --input: Input border color (reserved).
|
|
21
21
|
* @prop --ring: Focus ring color.
|
|
22
|
+
* @prop --backdrop: Modal backdrop overlay color.
|
|
22
23
|
* @prop --spacing-xs: Spacing scale (xs).
|
|
23
24
|
* @prop --spacing-sm: Spacing scale (sm).
|
|
24
25
|
* @prop --spacing-md: Spacing scale (md).
|
|
@@ -67,6 +68,9 @@
|
|
|
67
68
|
/* Additional colors for component */
|
|
68
69
|
--success: oklch(0.66 0.154 149.48);
|
|
69
70
|
--warning: oklch(0.72 0.153 66.34);
|
|
71
|
+
--info: oklch(0.623 0.214 259.815);
|
|
72
|
+
--info-foreground: oklch(0.488 0.243 264.376);
|
|
73
|
+
--backdrop: oklch(0 0 0 / 0.32);
|
|
70
74
|
|
|
71
75
|
/* Radius */
|
|
72
76
|
--radius: 0.625rem;
|
|
@@ -133,6 +137,9 @@
|
|
|
133
137
|
|
|
134
138
|
--internal-success: var(--success, oklch(0.66 0.154 149.48));
|
|
135
139
|
--internal-warning: var(--warning, oklch(0.72 0.153 66.34));
|
|
140
|
+
--internal-info: var(--info, oklch(0.623 0.214 259.815));
|
|
141
|
+
--internal-info-foreground: var(--info-foreground, oklch(0.488 0.243 264.376));
|
|
142
|
+
--internal-backdrop: var(--backdrop, oklch(0 0 0 / 0.32));
|
|
136
143
|
|
|
137
144
|
--internal-radius: var(--radius, 0.625rem);
|
|
138
145
|
|
|
@@ -220,6 +227,8 @@
|
|
|
220
227
|
--border: oklch(0.29 0 0);
|
|
221
228
|
--input: oklch(0.29 0 0);
|
|
222
229
|
--ring: oklch(0.60 0 0);
|
|
230
|
+
--info: oklch(0.623 0.214 259.815);
|
|
231
|
+
--info-foreground: oklch(0.707 0.165 254.624);
|
|
223
232
|
}
|
|
224
233
|
|
|
225
234
|
/* Tickets Section */
|
|
@@ -236,7 +245,7 @@
|
|
|
236
245
|
background: var(--card);
|
|
237
246
|
border: 1px solid var(--border);
|
|
238
247
|
border-radius: var(--radius);
|
|
239
|
-
margin: 0 0 var(--spacing-
|
|
248
|
+
margin: 0 0 var(--spacing-md) 0;
|
|
240
249
|
overflow: hidden;
|
|
241
250
|
width: 100%;
|
|
242
251
|
box-shadow: var(--shadow-sm);
|
|
@@ -274,6 +283,11 @@
|
|
|
274
283
|
transition: all var(--internal-transition-base);
|
|
275
284
|
}
|
|
276
285
|
|
|
286
|
+
.tickets-wrapper > .ticket-section:last-child,
|
|
287
|
+
.tickets-wrapper > .unsectioned-admissions:last-child .ticket-section:last-child {
|
|
288
|
+
margin-bottom: 0;
|
|
289
|
+
}
|
|
290
|
+
|
|
277
291
|
.ticket-section:focus-within {
|
|
278
292
|
box-shadow: var(--internal-shadow-md);
|
|
279
293
|
}
|
|
@@ -413,16 +427,67 @@
|
|
|
413
427
|
flex: 1;
|
|
414
428
|
}
|
|
415
429
|
|
|
430
|
+
.admission-header {
|
|
431
|
+
display: flex;
|
|
432
|
+
justify-content: space-between;
|
|
433
|
+
align-items: flex-start;
|
|
434
|
+
gap: var(--internal-spacing-md);
|
|
435
|
+
margin-bottom: var(--internal-spacing-sm);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.admission-header-text {
|
|
439
|
+
flex: 1;
|
|
440
|
+
min-width: 0;
|
|
441
|
+
}
|
|
442
|
+
|
|
416
443
|
.admission-title {
|
|
417
444
|
color: var(--internal-foreground);
|
|
418
|
-
font-size: var(--internal-font-size-lg);
|
|
445
|
+
font-size: var(--internal-font-size-lg);
|
|
419
446
|
font-weight: var(--internal-font-weight-semibold);
|
|
420
|
-
margin-bottom:
|
|
447
|
+
margin-bottom: 0;
|
|
421
448
|
display: flex;
|
|
422
449
|
align-items: center;
|
|
423
450
|
gap: var(--internal-spacing-sm);
|
|
424
451
|
}
|
|
425
452
|
|
|
453
|
+
.admission-subtitle {
|
|
454
|
+
color: var(--internal-muted-foreground);
|
|
455
|
+
font-size: var(--internal-font-size-sm);
|
|
456
|
+
margin-top: var(--internal-spacing-xs);
|
|
457
|
+
line-height: 1.4;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/* More Info Badge */
|
|
461
|
+
.more-info-badge {
|
|
462
|
+
display: inline-flex;
|
|
463
|
+
align-items: center;
|
|
464
|
+
gap: calc(var(--internal-spacing-xs) * 1.5);
|
|
465
|
+
padding: calc(var(--internal-spacing-xs) * 1.5) var(--internal-spacing-sm);
|
|
466
|
+
background: oklch(from var(--internal-info) l c h / 0.08);
|
|
467
|
+
color: var(--internal-info-foreground);
|
|
468
|
+
border: none;
|
|
469
|
+
border-radius: calc(var(--internal-radius) - 2px);
|
|
470
|
+
font-size: var(--internal-font-size-xs);
|
|
471
|
+
font-weight: var(--internal-font-weight-medium);
|
|
472
|
+
cursor: pointer;
|
|
473
|
+
transition: all var(--internal-transition-fast);
|
|
474
|
+
white-space: nowrap;
|
|
475
|
+
flex-shrink: 0;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
.more-info-badge:hover {
|
|
479
|
+
background: oklch(from var(--internal-info) l c h / 0.16);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.more-info-badge:focus-visible {
|
|
483
|
+
outline: 2px solid var(--internal-ring);
|
|
484
|
+
outline-offset: 2px;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
.more-info-badge svg {
|
|
488
|
+
flex-shrink: 0;
|
|
489
|
+
}
|
|
490
|
+
|
|
426
491
|
.admission-time {
|
|
427
492
|
color: var(--muted-foreground);
|
|
428
493
|
font-size: var(--font-size-sm);
|
|
@@ -877,3 +942,110 @@
|
|
|
877
942
|
margin: var(--spacing-md) 0;
|
|
878
943
|
}
|
|
879
944
|
}
|
|
945
|
+
|
|
946
|
+
/* Modal Styles */
|
|
947
|
+
.modal-backdrop {
|
|
948
|
+
position: fixed;
|
|
949
|
+
inset: 0;
|
|
950
|
+
z-index: 50;
|
|
951
|
+
display: flex;
|
|
952
|
+
align-items: center;
|
|
953
|
+
justify-content: center;
|
|
954
|
+
padding: var(--internal-spacing-md);
|
|
955
|
+
background: var(--internal-backdrop);
|
|
956
|
+
backdrop-filter: blur(4px);
|
|
957
|
+
animation: modal-backdrop-fade-in var(--internal-transition-base) ease-out;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
@keyframes modal-backdrop-fade-in {
|
|
961
|
+
from {
|
|
962
|
+
opacity: 0;
|
|
963
|
+
}
|
|
964
|
+
to {
|
|
965
|
+
opacity: 1;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
.modal-content {
|
|
970
|
+
position: relative;
|
|
971
|
+
width: 100%;
|
|
972
|
+
max-width: 32rem;
|
|
973
|
+
max-height: calc(100vh - var(--internal-spacing-xl) * 2);
|
|
974
|
+
overflow-y: auto;
|
|
975
|
+
background: var(--internal-popover);
|
|
976
|
+
color: var(--internal-popover-foreground);
|
|
977
|
+
border: 1px solid var(--internal-border);
|
|
978
|
+
border-radius: var(--internal-radius);
|
|
979
|
+
padding: var(--internal-spacing-lg);
|
|
980
|
+
box-shadow: var(--internal-shadow-xl);
|
|
981
|
+
animation: modal-content-scale-in var(--internal-transition-base) ease-out;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
@keyframes modal-content-scale-in {
|
|
985
|
+
from {
|
|
986
|
+
opacity: 0;
|
|
987
|
+
transform: scale(0.98);
|
|
988
|
+
}
|
|
989
|
+
to {
|
|
990
|
+
opacity: 1;
|
|
991
|
+
transform: scale(1);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
.modal-close-btn {
|
|
996
|
+
position: absolute;
|
|
997
|
+
top: var(--internal-spacing-sm);
|
|
998
|
+
right: var(--internal-spacing-sm);
|
|
999
|
+
display: flex;
|
|
1000
|
+
align-items: center;
|
|
1001
|
+
justify-content: center;
|
|
1002
|
+
width: 1.75rem;
|
|
1003
|
+
height: 1.75rem;
|
|
1004
|
+
padding: 0;
|
|
1005
|
+
background: transparent;
|
|
1006
|
+
border: 1px solid transparent;
|
|
1007
|
+
border-radius: calc(var(--internal-radius) - 4px);
|
|
1008
|
+
color: var(--internal-muted-foreground);
|
|
1009
|
+
cursor: pointer;
|
|
1010
|
+
opacity: 0.72;
|
|
1011
|
+
transition: all var(--internal-transition-fast);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
.modal-close-btn:hover {
|
|
1015
|
+
opacity: 1;
|
|
1016
|
+
background: var(--internal-accent);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
.modal-close-btn:focus-visible {
|
|
1020
|
+
outline: 2px solid var(--internal-ring);
|
|
1021
|
+
outline-offset: 1px;
|
|
1022
|
+
opacity: 1;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
.modal-header {
|
|
1026
|
+
margin-bottom: var(--internal-spacing-md);
|
|
1027
|
+
padding-right: var(--internal-spacing-xl);
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
.modal-title {
|
|
1031
|
+
font-size: var(--internal-font-size-xl);
|
|
1032
|
+
font-weight: var(--internal-font-weight-semibold);
|
|
1033
|
+
color: var(--internal-foreground);
|
|
1034
|
+
margin: 0;
|
|
1035
|
+
line-height: 1.2;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
.modal-subtitle {
|
|
1039
|
+
color: var(--internal-muted-foreground);
|
|
1040
|
+
font-size: var(--internal-font-size-sm);
|
|
1041
|
+
margin-top: var(--internal-spacing-xs);
|
|
1042
|
+
line-height: 1.4;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
.modal-description {
|
|
1046
|
+
color: var(--internal-foreground);
|
|
1047
|
+
font-size: var(--internal-font-size-base);
|
|
1048
|
+
line-height: 1.6;
|
|
1049
|
+
white-space: pre-wrap;
|
|
1050
|
+
margin: 0;
|
|
1051
|
+
}
|
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
h, Host, } from "@stencil/core";
|
|
6
6
|
const CalendarIcon = () => (h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M8 2v4" }), h("path", { d: "M16 2v4" }), h("rect", { width: "18", height: "18", x: "3", y: "4", rx: "2" }), h("path", { d: "M3 10h18" })));
|
|
7
7
|
const LocationIcon = () => (h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0" }), h("circle", { cx: "12", cy: "10", r: "3" })));
|
|
8
|
+
const InfoIcon = () => (h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("circle", { cx: "12", cy: "12", r: "10" }), h("path", { d: "M12 16v-4" }), h("path", { d: "M12 8h.01" })));
|
|
9
|
+
const CloseIcon = () => (h("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true" }, h("path", { d: "M18 6 6 18" }), h("path", { d: "M6 6l12 12" })));
|
|
8
10
|
export class SessionsTicketing {
|
|
9
11
|
tenantId;
|
|
10
12
|
eventId;
|
|
@@ -23,6 +25,7 @@ export class SessionsTicketing {
|
|
|
23
25
|
quoteError = null;
|
|
24
26
|
checkoutLoading = false;
|
|
25
27
|
currentTheme = "light";
|
|
28
|
+
openModalAdmissionId = null;
|
|
26
29
|
checkoutInit;
|
|
27
30
|
cartUpdate;
|
|
28
31
|
errorOccurred;
|
|
@@ -43,6 +46,10 @@ export class SessionsTicketing {
|
|
|
43
46
|
if (this.mediaQueryList) {
|
|
44
47
|
this.mediaQueryList.removeEventListener("change", this.handleSystemThemeChange);
|
|
45
48
|
}
|
|
49
|
+
// Clean up modal listener if component unmounts while modal is open
|
|
50
|
+
if (this.openModalAdmissionId) {
|
|
51
|
+
document.removeEventListener("keydown", this.handleModalKeyDown);
|
|
52
|
+
}
|
|
46
53
|
}
|
|
47
54
|
async initializeComponent() {
|
|
48
55
|
this.error = null;
|
|
@@ -82,13 +89,16 @@ export class SessionsTicketing {
|
|
|
82
89
|
trackEvent(eventType, properties = {}) {
|
|
83
90
|
if (!this.analytics || !this.enableAnalytics)
|
|
84
91
|
return;
|
|
92
|
+
// Ensure required fields exist before tracking
|
|
93
|
+
if (!this.event?.teamId || !this.event?.promoterId)
|
|
94
|
+
return;
|
|
85
95
|
this.analytics.track(eventType, {
|
|
86
96
|
event_id: this.eventId,
|
|
87
|
-
team_id: this.event
|
|
88
|
-
promoter_id: this.event
|
|
89
|
-
event_name: this.event
|
|
90
|
-
event_slug: this.event
|
|
91
|
-
currency: this.event
|
|
97
|
+
team_id: this.event.teamId,
|
|
98
|
+
promoter_id: this.event.promoterId,
|
|
99
|
+
event_name: this.event.name,
|
|
100
|
+
event_slug: this.event.slug,
|
|
101
|
+
currency: this.event.currency,
|
|
92
102
|
...properties,
|
|
93
103
|
});
|
|
94
104
|
}
|
|
@@ -370,6 +380,26 @@ export class SessionsTicketing {
|
|
|
370
380
|
handleQuantityIncrease = async (admissionId, sectionId) => {
|
|
371
381
|
await this.updateQuantity(admissionId, sectionId, 1);
|
|
372
382
|
};
|
|
383
|
+
openModal = (admissionId, e) => {
|
|
384
|
+
e.stopPropagation();
|
|
385
|
+
this.openModalAdmissionId = admissionId;
|
|
386
|
+
document.addEventListener("keydown", this.handleModalKeyDown);
|
|
387
|
+
};
|
|
388
|
+
closeModal = () => {
|
|
389
|
+
this.openModalAdmissionId = null;
|
|
390
|
+
document.removeEventListener("keydown", this.handleModalKeyDown);
|
|
391
|
+
};
|
|
392
|
+
handleModalKeyDown = (e) => {
|
|
393
|
+
if (e.key === "Escape") {
|
|
394
|
+
this.closeModal();
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
handleBackdropClick = (e) => {
|
|
398
|
+
if (e.target instanceof HTMLElement &&
|
|
399
|
+
e.target.classList.contains("modal-backdrop")) {
|
|
400
|
+
this.closeModal();
|
|
401
|
+
}
|
|
402
|
+
};
|
|
373
403
|
renderQuantityControls(admission, sectionId) {
|
|
374
404
|
const key = `${admission.id}::${sectionId || "none"}`;
|
|
375
405
|
const quantity = this.selectedItems.get(key) || 0;
|
|
@@ -395,7 +425,7 @@ export class SessionsTicketing {
|
|
|
395
425
|
const ariaLabel = section
|
|
396
426
|
? `${admission.name} in ${section.name} - ${isSoldOut ? "Sold Out" : this.formatPriceValue(basePrice)}`
|
|
397
427
|
: `${admission.name} - ${isSoldOut ? "Sold Out" : this.formatPriceValue(basePrice)}`;
|
|
398
|
-
return (h("section", { class: `admission-card ${isSoldOut ? "sold-out" : ""} ${isStandaloneAdmission ? "solo-admission" : ""}`, key: key, tabindex: isSoldOut ? -1 : 0, "aria-label": ariaLabel }, h("div", { class: "admission-content" }, h("div", { class: "admission-details" }, h("h3", { class: "admission-title" }, admission.name), isStandaloneAdmission && (h(h.Fragment, null, this.renderEventTimeAndLocation(), this.renderPriceAndControls(admission, sectionId, isSoldOut))), section &&
|
|
428
|
+
return (h("section", { class: `admission-card ${isSoldOut ? "sold-out" : ""} ${isStandaloneAdmission ? "solo-admission" : ""}`, key: key, tabindex: isSoldOut ? -1 : 0, "aria-label": ariaLabel }, h("div", { class: "admission-content" }, h("div", { class: "admission-details" }, h("div", { class: "admission-header" }, h("div", { class: "admission-header-text" }, h("h3", { class: "admission-title" }, admission.name), admission.subtitle && (h("p", { class: "admission-subtitle" }, admission.subtitle))), admission.description && (h("button", { type: "button", class: "more-info-badge", onClick: (e) => this.openModal(admission.id, e), "aria-label": `More info about ${admission.name}` }, InfoIcon(), h("span", null, "More info")))), isStandaloneAdmission && (h(h.Fragment, null, this.renderEventTimeAndLocation(), this.renderPriceAndControls(admission, sectionId, isSoldOut))), section &&
|
|
399
429
|
this.renderPriceAndControls(admission, sectionId, isSoldOut)))));
|
|
400
430
|
}
|
|
401
431
|
renderSectionHeader(section, isSectionSoldOut) {
|
|
@@ -456,6 +486,16 @@ export class SessionsTicketing {
|
|
|
456
486
|
? "Free"
|
|
457
487
|
: this.formatPriceValue(this.quote.price?.total || 0))), h("button", { type: "button", class: "checkout-btn", onClick: () => this.initiateCheckout(), disabled: this.isCheckoutDisabled() }, this.getCheckoutButtonText())));
|
|
458
488
|
}
|
|
489
|
+
renderModal() {
|
|
490
|
+
if (!this.openModalAdmissionId)
|
|
491
|
+
return null;
|
|
492
|
+
const admission = this.event?.admissions.find((a) => a.id === this.openModalAdmissionId);
|
|
493
|
+
if (!admission)
|
|
494
|
+
return null;
|
|
495
|
+
return (
|
|
496
|
+
// biome-ignore lint/a11y/noStaticElementInteractions lint/a11y/useKeyWithClickEvents: backdrop click-to-close is a common modal pattern, keyboard handled via Escape key
|
|
497
|
+
h("div", { class: "modal-backdrop", onClick: this.handleBackdropClick }, h("div", { class: "modal-content", role: "dialog", "aria-modal": "true", "aria-labelledby": "modal-title" }, h("button", { type: "button", class: "modal-close-btn", onClick: () => this.closeModal(), "aria-label": "Close" }, CloseIcon()), h("div", { class: "modal-header" }, h("h2", { id: "modal-title", class: "modal-title" }, admission.name), admission.subtitle && (h("p", { class: "modal-subtitle" }, admission.subtitle))), h("p", { class: "modal-description" }, admission.description))));
|
|
498
|
+
}
|
|
459
499
|
renderTicketSections() {
|
|
460
500
|
const generalAdmissions = this.event?.admissions.filter((adm) => !adm.sectionId) || [];
|
|
461
501
|
return (h("div", { class: "tickets-wrapper" }, generalAdmissions.length > 0 && (h("div", { class: "unsectioned-admissions" }, generalAdmissions
|
|
@@ -471,7 +511,7 @@ export class SessionsTicketing {
|
|
|
471
511
|
return this.renderErrorState();
|
|
472
512
|
if (!this.event)
|
|
473
513
|
return this.renderNotFoundState();
|
|
474
|
-
return (h(Host, { "data-theme": this.currentTheme }, h("div", { class: "session-services-ticketing-container" }, this.renderTicketSections(), this.renderOrderSummary())));
|
|
514
|
+
return (h(Host, { "data-theme": this.currentTheme }, h("div", { class: "session-services-ticketing-container" }, this.renderTicketSections(), this.renderOrderSummary()), this.renderModal()));
|
|
475
515
|
}
|
|
476
516
|
static get is() { return "session-services-ticketing"; }
|
|
477
517
|
static get encapsulation() { return "shadow"; }
|
|
@@ -656,7 +696,8 @@ export class SessionsTicketing {
|
|
|
656
696
|
"quoteLoading": {},
|
|
657
697
|
"quoteError": {},
|
|
658
698
|
"checkoutLoading": {},
|
|
659
|
-
"currentTheme": {}
|
|
699
|
+
"currentTheme": {},
|
|
700
|
+
"openModalAdmissionId": {}
|
|
660
701
|
};
|
|
661
702
|
}
|
|
662
703
|
static get events() {
|
|
@@ -687,7 +728,7 @@ export class SessionsTicketing {
|
|
|
687
728
|
},
|
|
688
729
|
"complexType": {
|
|
689
730
|
"original": "OrderQuote",
|
|
690
|
-
"resolved": "{ items: { id: string; kind: \"ADMISSION\" | \"ADDON\"; quantity: number; price: { net: number; total: number; gross: number; base: number; booking: number; refunded: number; discount: number; currency: string; }; admissionId?: string; sectionId?: string; admission?: { name: string; capacity: number; limit: number; order: number; entryStartsAt: string; entryEndsAt: string; saleStartsAt: string; saleEndsAt: string; price: { base: number; booking: number; currency: string; }; teamId: string; promoterId: string; eventId: string; eventSlug: string; id?: string; available?: number; sold?: number; reserved?: number; createdAt?: string; updatedAt?: string; sectionId?: string; }; section?: { name: string; capacity: number; limit: number; order: number; location: { createdAt: string; updatedAt: string; name: string; address: string; country: string; latitude: number; longitude: number; timeZone: string; id?: string; googlePlaceId?: string; city?: string; state?: string; postcode?: string; types?: string[]; description?: string; image?: { src: string; alt: string; width: number; height: number; }; venueId?: string; }; teamId: string; promoterId: string; eventId: string; eventSlug: string; id?: string; createdAt?: string; updatedAt?: string; available?: number; sold?: number; reserved?: number; }; }[]; price: { net: number; total: number; gross: number; base: number; booking: number; shipping: number; refunded: number; discount: number; gateway: number; dispute: number; currency: string; }; couponCode?: { name: string; active: boolean; couponId: string; teamId: string; id?: string; createdAt?: string; updatedAt?: string; uses?: number; }; }",
|
|
731
|
+
"resolved": "{ items: { id: string; kind: \"ADMISSION\" | \"ADDON\"; quantity: number; price: { net: number; total: number; gross: number; base: number; booking: number; refunded: number; discount: number; currency: string; }; admissionId?: string; sectionId?: string; admission?: { name: string; capacity: number; limit: number; order: number; entryStartsAt: string; entryEndsAt: string; saleStartsAt: string; saleEndsAt: string; price: { base: number; booking: number; currency: string; }; visibility: \"ALWAYS\" | \"STACKED\" | \"HIDDEN\"; teamId: string; promoterId: string; eventId: string; eventSlug: string; id?: string; subtitle?: string; description?: string; available?: number; sold?: number; reserved?: number; createdAt?: string; updatedAt?: string; passcode?: string; sectionId?: string; }; section?: { name: string; capacity: number; limit: number; order: number; location: { createdAt: string; updatedAt: string; name: string; address: string; country: string; latitude: number; longitude: number; timeZone: string; id?: string; googlePlaceId?: string; city?: string; state?: string; postcode?: string; types?: string[]; description?: string; image?: { src: string; alt: string; width: number; height: number; }; venueId?: string; }; teamId: string; promoterId: string; eventId: string; eventSlug: string; id?: string; createdAt?: string; updatedAt?: string; available?: number; sold?: number; reserved?: number; passcode?: string; }; }[]; price: { net: number; total: number; gross: number; base: number; booking: number; shipping: number; refunded: number; discount: number; gateway: number; dispute: number; currency: string; }; couponCode?: { name: string; active: boolean; couponId: string; teamId: string; id?: string; createdAt?: string; updatedAt?: string; uses?: number; }; }",
|
|
691
732
|
"references": {
|
|
692
733
|
"OrderQuote": {
|
|
693
734
|
"location": "import",
|
package/dist/collection/index.js
CHANGED
|
@@ -1,11 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* @fileoverview entry point for your component library
|
|
3
|
-
*
|
|
4
|
-
* This is the entry point for your component library. Use this file to export utilities,
|
|
5
|
-
* constants or data structure that accompany your components.
|
|
6
|
-
*
|
|
7
|
-
* DO NOT use this file to export your components. Instead, use the recommended approaches
|
|
8
|
-
* to consume components of this package as outlined in the `README.md`.
|
|
9
|
-
*/
|
|
10
|
-
// Export components through Stencil's loader mechanism
|
|
11
|
-
// Types are exported through the generated dist/types folder
|
|
1
|
+
export {};
|