@xsolla/xui-gdpr-banner 0.206.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.
@@ -0,0 +1,918 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.tsx
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ DEFAULT_CATEGORIES: () => DEFAULT_CATEGORIES,
24
+ DEFAULT_LABELS: () => DEFAULT_LABELS,
25
+ GdprBanner: () => GdprBanner,
26
+ PrivacySettings: () => PrivacySettings
27
+ });
28
+ module.exports = __toCommonJS(index_exports);
29
+
30
+ // src/GdprBanner.tsx
31
+ var import_react = require("react");
32
+
33
+ // ../../foundation/primitives-native/src/Box.tsx
34
+ var import_react_native = require("react-native");
35
+ var import_jsx_runtime = require("react/jsx-runtime");
36
+ var roleToAccessibilityRole = {
37
+ button: "button",
38
+ link: "link",
39
+ img: "image",
40
+ image: "image",
41
+ summary: "summary",
42
+ switch: "switch",
43
+ checkbox: "checkbox",
44
+ radio: "radio",
45
+ heading: "header",
46
+ text: "text",
47
+ search: "search",
48
+ alert: "alert",
49
+ none: "none",
50
+ presentation: "none",
51
+ progressbar: "progressbar",
52
+ scrollbar: "scrollbar",
53
+ adjustable: "adjustable",
54
+ tab: "tab",
55
+ menu: "menu",
56
+ menuitem: "menuitem",
57
+ list: "list",
58
+ timer: "timer"
59
+ };
60
+ var toGapValue = (gap) => {
61
+ if (gap === void 0) return void 0;
62
+ if (typeof gap === "number") return gap;
63
+ const pxMatch = /^(\d+(?:\.\d+)?)px$/.exec(gap);
64
+ if (pxMatch) return Number(pxMatch[1]);
65
+ const asNumber = Number(gap);
66
+ return Number.isFinite(asNumber) ? asNumber : void 0;
67
+ };
68
+ var Box = ({
69
+ children,
70
+ onPress,
71
+ onLayout,
72
+ onMoveShouldSetResponder,
73
+ onResponderGrant,
74
+ onResponderMove,
75
+ onResponderRelease,
76
+ onResponderTerminate,
77
+ backgroundColor,
78
+ borderColor,
79
+ borderWidth,
80
+ borderBottomWidth,
81
+ borderBottomColor,
82
+ borderTopWidth,
83
+ borderTopColor,
84
+ borderLeftWidth,
85
+ borderLeftColor,
86
+ borderRightWidth,
87
+ borderRightColor,
88
+ borderRadius,
89
+ borderStyle,
90
+ height,
91
+ padding,
92
+ paddingHorizontal,
93
+ paddingVertical,
94
+ margin,
95
+ marginTop,
96
+ marginBottom,
97
+ marginLeft,
98
+ marginRight,
99
+ flexDirection,
100
+ flexWrap,
101
+ alignItems,
102
+ justifyContent,
103
+ alignSelf,
104
+ position,
105
+ top,
106
+ bottom,
107
+ left,
108
+ right,
109
+ width,
110
+ minWidth,
111
+ minHeight,
112
+ maxWidth,
113
+ maxHeight,
114
+ flex,
115
+ flexShrink,
116
+ gap,
117
+ overflow,
118
+ opacity,
119
+ zIndex,
120
+ hoverStyle,
121
+ pressStyle,
122
+ style,
123
+ disabled,
124
+ "data-testid": dataTestId,
125
+ testID,
126
+ as,
127
+ src,
128
+ alt,
129
+ ...rest
130
+ }) => {
131
+ const getContainerStyle = (pressed) => ({
132
+ backgroundColor: pressed && pressStyle?.backgroundColor ? pressStyle.backgroundColor : backgroundColor,
133
+ borderColor,
134
+ borderWidth,
135
+ borderBottomWidth,
136
+ borderBottomColor,
137
+ borderTopWidth,
138
+ borderTopColor,
139
+ borderLeftWidth,
140
+ borderLeftColor,
141
+ borderRightWidth,
142
+ borderRightColor,
143
+ borderRadius,
144
+ borderStyle,
145
+ overflow,
146
+ opacity: opacity ?? (disabled ? 0.5 : void 0),
147
+ zIndex,
148
+ height,
149
+ width,
150
+ minWidth,
151
+ minHeight,
152
+ maxWidth,
153
+ maxHeight,
154
+ padding,
155
+ paddingHorizontal,
156
+ paddingVertical,
157
+ margin,
158
+ marginTop,
159
+ marginBottom,
160
+ marginLeft,
161
+ marginRight,
162
+ flexDirection,
163
+ flexWrap,
164
+ alignItems,
165
+ justifyContent,
166
+ alignSelf,
167
+ position,
168
+ top,
169
+ bottom,
170
+ left,
171
+ right,
172
+ flex,
173
+ flexShrink,
174
+ gap: toGapValue(gap),
175
+ ...style
176
+ });
177
+ const finalTestID = dataTestId || testID;
178
+ const {
179
+ role,
180
+ tabIndex,
181
+ onKeyDown,
182
+ onKeyUp,
183
+ "aria-label": ariaLabel,
184
+ "aria-labelledby": _ariaLabelledBy,
185
+ "aria-current": _ariaCurrent,
186
+ "aria-disabled": ariaDisabled,
187
+ "aria-live": ariaLive,
188
+ "aria-busy": ariaBusy,
189
+ className,
190
+ "data-testid": _dataTestId,
191
+ cursor: _cursor,
192
+ type: _type,
193
+ ...nativeRest
194
+ } = rest;
195
+ const accessibilityRole = typeof role === "string" && roleToAccessibilityRole[role] || (as === "button" ? "button" : void 0);
196
+ const isDisabled = Boolean(disabled || ariaDisabled);
197
+ const accessibilityState = {
198
+ disabled: isDisabled || void 0,
199
+ busy: Boolean(ariaBusy) || void 0
200
+ };
201
+ const accessibilityLiveRegion = ariaLive === "polite" || ariaLive === "assertive" ? ariaLive : void 0;
202
+ if (as === "img" && src) {
203
+ const imageStyle = {
204
+ width,
205
+ height,
206
+ borderRadius,
207
+ position,
208
+ top,
209
+ bottom,
210
+ left,
211
+ right,
212
+ ...style
213
+ };
214
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
215
+ import_react_native.Image,
216
+ {
217
+ source: { uri: src },
218
+ style: imageStyle,
219
+ testID: finalTestID,
220
+ accessibilityLabel: typeof ariaLabel === "string" ? ariaLabel : alt,
221
+ resizeMode: "cover",
222
+ ...nativeRest
223
+ }
224
+ );
225
+ }
226
+ if (onPress || as === "button") {
227
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
228
+ import_react_native.Pressable,
229
+ {
230
+ onPress: !isDisabled && onPress ? onPress : void 0,
231
+ disabled: isDisabled,
232
+ onLayout,
233
+ onMoveShouldSetResponder,
234
+ onResponderGrant,
235
+ onResponderMove,
236
+ onResponderRelease,
237
+ onResponderTerminate,
238
+ style: ({ pressed }) => getContainerStyle(pressed),
239
+ testID: finalTestID,
240
+ accessibilityRole,
241
+ accessibilityLabel: typeof ariaLabel === "string" ? ariaLabel : void 0,
242
+ accessibilityState,
243
+ accessibilityLiveRegion,
244
+ ...nativeRest,
245
+ children
246
+ }
247
+ );
248
+ }
249
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
250
+ import_react_native.View,
251
+ {
252
+ style: getContainerStyle(),
253
+ testID: finalTestID,
254
+ onLayout,
255
+ onMoveShouldSetResponder,
256
+ onResponderGrant,
257
+ onResponderMove,
258
+ onResponderRelease,
259
+ onResponderTerminate,
260
+ accessibilityRole,
261
+ accessibilityLabel: typeof ariaLabel === "string" ? ariaLabel : void 0,
262
+ accessibilityState,
263
+ accessibilityLiveRegion,
264
+ ...nativeRest,
265
+ children
266
+ }
267
+ );
268
+ };
269
+
270
+ // ../../foundation/primitives-native/src/Text.tsx
271
+ var import_react_native2 = require("react-native");
272
+ var import_jsx_runtime2 = require("react/jsx-runtime");
273
+ var roleMap = {
274
+ alert: "alert",
275
+ heading: "header",
276
+ button: "button",
277
+ link: "link",
278
+ text: "text"
279
+ };
280
+ var parseNumericValue = (value) => {
281
+ if (value === void 0) return void 0;
282
+ if (typeof value === "number") return value;
283
+ const parsed = parseFloat(value);
284
+ return isNaN(parsed) ? void 0 : parsed;
285
+ };
286
+ var Text = ({
287
+ children,
288
+ color,
289
+ fontSize,
290
+ fontWeight,
291
+ fontFamily,
292
+ textAlign,
293
+ lineHeight,
294
+ numberOfLines,
295
+ id,
296
+ role,
297
+ testID,
298
+ "data-testid": dataTestId,
299
+ style: styleProp,
300
+ ...props
301
+ }) => {
302
+ let resolvedFontFamily = fontFamily ? fontFamily.split(",")[0].replace(/['"]/g, "").trim() : void 0;
303
+ if (resolvedFontFamily === "Pilat Wide" || resolvedFontFamily === "Pilat Wide Bold" || resolvedFontFamily === "Aktiv Grotesk") {
304
+ resolvedFontFamily = void 0;
305
+ }
306
+ const incomingStyle = import_react_native2.StyleSheet.flatten(styleProp);
307
+ const baseStyle = {
308
+ color: color ?? incomingStyle?.color,
309
+ fontSize: typeof fontSize === "number" ? fontSize : void 0,
310
+ fontWeight,
311
+ fontFamily: resolvedFontFamily,
312
+ textDecorationLine: props.textDecoration,
313
+ textAlign: textAlign ?? incomingStyle?.textAlign,
314
+ lineHeight: parseNumericValue(lineHeight ?? incomingStyle?.lineHeight),
315
+ marginTop: parseNumericValue(
316
+ incomingStyle?.marginTop
317
+ ),
318
+ marginBottom: parseNumericValue(
319
+ incomingStyle?.marginBottom
320
+ )
321
+ };
322
+ const accessibilityRole = role ? roleMap[role] : void 0;
323
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
324
+ import_react_native2.Text,
325
+ {
326
+ style: baseStyle,
327
+ numberOfLines,
328
+ testID: dataTestId || testID || id,
329
+ accessibilityRole,
330
+ children
331
+ }
332
+ );
333
+ };
334
+
335
+ // src/GdprBanner.tsx
336
+ var import_xui_core2 = require("@xsolla/xui-core");
337
+ var import_xui_button = require("@xsolla/xui-button");
338
+ var import_xui_icons_base2 = require("@xsolla/xui-icons-base");
339
+
340
+ // src/PrivacySettings.tsx
341
+ var import_xui_core = require("@xsolla/xui-core");
342
+ var import_xui_checkbox = require("@xsolla/xui-checkbox");
343
+ var import_xui_link = require("@xsolla/xui-link");
344
+ var import_xui_icons_base = require("@xsolla/xui-icons-base");
345
+ var import_jsx_runtime3 = require("react/jsx-runtime");
346
+ var PANE_RADIUS = 8;
347
+ var LIST_WIDTH = 280;
348
+ var PrivacySettings = ({
349
+ categories,
350
+ selectedCategoryIds,
351
+ onToggleCategory,
352
+ activeCategoryId,
353
+ onActiveCategoryChange,
354
+ viewport,
355
+ labels,
356
+ cookiePolicyUrl,
357
+ privacyPolicyUrl,
358
+ testID = "gdpr-privacy-settings",
359
+ themeMode,
360
+ themeProductContext
361
+ }) => {
362
+ const { theme } = (0, import_xui_core.useResolvedTheme)({ themeMode, themeProductContext });
363
+ const isMobile = viewport === "mobile";
364
+ const body = theme.typographyTokens.basic["body-sm"];
365
+ const activeDescription = activeCategoryId === null ? labels.generalInformationDescription : categories.find((category) => category.id === activeCategoryId)?.description ?? "";
366
+ const descriptionPane = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
367
+ Box,
368
+ {
369
+ testID: `${testID}__description`,
370
+ backgroundColor: theme.colors.control.faint.bg,
371
+ borderRadius: PANE_RADIUS,
372
+ padding: 16,
373
+ flex: isMobile ? void 0 : 1,
374
+ width: isMobile ? "100%" : void 0,
375
+ "aria-live": "polite",
376
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
377
+ Text,
378
+ {
379
+ color: theme.colors.content.secondary,
380
+ fontSize: body.fontSize,
381
+ lineHeight: body.lineHeight,
382
+ fontFamily: theme.fonts.body,
383
+ children: activeDescription
384
+ }
385
+ )
386
+ }
387
+ );
388
+ const focusStyle = {
389
+ outlineColor: theme.colors.border.brand,
390
+ outlineWidth: 2,
391
+ outlineOffset: 2,
392
+ outlineStyle: "solid"
393
+ };
394
+ const list = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
395
+ Box,
396
+ {
397
+ gap: 12,
398
+ width: isMobile ? "100%" : LIST_WIDTH,
399
+ flexShrink: 0,
400
+ testID: `${testID}__categories`,
401
+ children: [
402
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box, { gap: 8, children: [
403
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
404
+ Box,
405
+ {
406
+ flexDirection: "row",
407
+ alignItems: "center",
408
+ gap: 8,
409
+ role: "button",
410
+ tabIndex: 0,
411
+ cursor: "pointer",
412
+ "aria-pressed": activeCategoryId === null,
413
+ onPress: () => onActiveCategoryChange(null),
414
+ onKeyDown: (event) => {
415
+ if (event.key === "Enter" || event.key === " ") {
416
+ event.preventDefault();
417
+ onActiveCategoryChange(null);
418
+ }
419
+ },
420
+ focusStyle,
421
+ testID: `${testID}__general-information`,
422
+ children: [
423
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
424
+ import_xui_icons_base.InfoCr,
425
+ {
426
+ variant: "solid",
427
+ size: 16,
428
+ color: theme.colors.control.link.primary
429
+ }
430
+ ),
431
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
432
+ Text,
433
+ {
434
+ color: theme.colors.content.primary,
435
+ fontSize: body.fontSize,
436
+ lineHeight: body.lineHeight,
437
+ fontFamily: theme.fonts.body,
438
+ children: labels.generalInformation
439
+ }
440
+ )
441
+ ]
442
+ }
443
+ ),
444
+ isMobile && activeCategoryId === null && descriptionPane
445
+ ] }),
446
+ categories.map((category) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box, { gap: 8, children: [
447
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box, { onPress: () => onActiveCategoryChange(category.id), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
448
+ import_xui_checkbox.Checkbox,
449
+ {
450
+ size: "md",
451
+ checked: selectedCategoryIds.includes(category.id),
452
+ disabled: category.locked,
453
+ onChange: () => onToggleCategory(category.id),
454
+ testID: `${testID}__category-${category.id}`,
455
+ themeMode,
456
+ themeProductContext,
457
+ children: category.label
458
+ }
459
+ ) }),
460
+ isMobile && activeCategoryId === category.id && descriptionPane
461
+ ] }, category.id))
462
+ ]
463
+ }
464
+ );
465
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box, { testID, gap: 16, width: "100%", children: [
466
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
467
+ Box,
468
+ {
469
+ flexDirection: isMobile ? "column" : "row",
470
+ gap: isMobile ? 12 : 24,
471
+ alignItems: "flex-start",
472
+ width: "100%",
473
+ children: [
474
+ list,
475
+ !isMobile && descriptionPane
476
+ ]
477
+ }
478
+ ),
479
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
480
+ Box,
481
+ {
482
+ flexDirection: "row",
483
+ alignItems: "center",
484
+ gap: 8,
485
+ testID: `${testID}__policies`,
486
+ children: [
487
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
488
+ import_xui_link.Link,
489
+ {
490
+ size: "sm",
491
+ href: cookiePolicyUrl,
492
+ target: "_blank",
493
+ themeMode,
494
+ themeProductContext,
495
+ testID: `${testID}__cookie-policy`,
496
+ children: labels.cookiePolicy
497
+ }
498
+ ),
499
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
500
+ Text,
501
+ {
502
+ color: theme.colors.content.tertiary,
503
+ fontSize: body.fontSize,
504
+ lineHeight: body.lineHeight,
505
+ fontFamily: theme.fonts.body,
506
+ "aria-hidden": true,
507
+ children: "\u2022"
508
+ }
509
+ ),
510
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
511
+ import_xui_link.Link,
512
+ {
513
+ size: "sm",
514
+ href: privacyPolicyUrl,
515
+ target: "_blank",
516
+ themeMode,
517
+ themeProductContext,
518
+ testID: `${testID}__privacy-policy`,
519
+ children: labels.privacyPolicy
520
+ }
521
+ )
522
+ ]
523
+ }
524
+ )
525
+ ] });
526
+ };
527
+ PrivacySettings.displayName = "PrivacySettings";
528
+
529
+ // src/defaults.ts
530
+ var DEFAULT_CATEGORIES = [
531
+ {
532
+ id: "essential",
533
+ label: "Essential cookies",
534
+ locked: true,
535
+ description: "These cookies are required for the Xsolla Ecosystem to function and cannot be disabled in our systems. They are often only set in response to activities taken by you that constitute a request for services, such as changing your privacy settings, logging in, contacting customer support, or filling out forms. You can set your browser to block or inform you about these cookies, but some aspects of the Xsolla Ecosystem will not function properly. These cookies do not contain personally identifying information."
536
+ },
537
+ {
538
+ id: "analytical",
539
+ label: "Analytical and performance cookies",
540
+ description: "These cookies let us count visits and traffic sources so we can measure and improve the performance of the Xsolla Ecosystem. They help us understand which pages are the most and least popular and see how visitors move around the service. All information these cookies collect is aggregated."
541
+ },
542
+ {
543
+ id: "functional",
544
+ label: "Functional cookies",
545
+ description: "These cookies enable the Xsolla Ecosystem to provide enhanced functionality and personalization, such as remembering your preferences and the region you are in. If you do not allow these cookies, some or all of these services may not function properly."
546
+ },
547
+ {
548
+ id: "targeting",
549
+ label: "Targeting cookies",
550
+ description: "These cookies may be set through the Xsolla Ecosystem by our advertising partners to build a profile of your interests and show you relevant offers on other sites. They do not store directly personal information but are based on uniquely identifying your browser and device."
551
+ }
552
+ ];
553
+ var DEFAULT_LABELS = {
554
+ title: "How we use cookies",
555
+ description: "We use cookies to personalize your experience. If you reject the use of cookies, your experience will not be tailored to your preferences.",
556
+ simpleDescription: "We only use essential cookies that allow us to run this service. Read our cookie policy to learn more.",
557
+ privacySettingsTitle: "Privacy settings",
558
+ generalInformation: "General information",
559
+ generalInformationDescription: "We use cookies to personalize your experience, to run and secure the Xsolla Ecosystem, and to measure how it is used. Choose which categories you want to allow below. You can change your choice at any time.",
560
+ customize: "Customize",
561
+ rejectAll: "Reject All",
562
+ acceptAll: "Accept All",
563
+ confirmSelection: "Confirm Selection",
564
+ dismiss: "Dismiss",
565
+ close: "Close",
566
+ cookiePolicy: "Cookie Policy",
567
+ privacyPolicy: "Privacy Policy",
568
+ closeButtonAriaLabel: "Close cookie banner"
569
+ };
570
+ var CARD_WIDTH = 400;
571
+ var EDGE_OFFSET = 20;
572
+ var CONTENT_GAP = 8;
573
+
574
+ // src/GdprBanner.tsx
575
+ var import_jsx_runtime4 = require("react/jsx-runtime");
576
+ var FOCUSABLE_SELECTORS = 'button:not([disabled]), [href], input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
577
+ var isElementVisible = (element) => {
578
+ if (typeof window === "undefined") return true;
579
+ const style = window.getComputedStyle(element);
580
+ return style.visibility !== "hidden" && style.display !== "none";
581
+ };
582
+ var getFocusableElements = (container) => Array.from(
583
+ container.querySelectorAll(FOCUSABLE_SELECTORS)
584
+ ).filter(isElementVisible);
585
+ var GdprBanner = ({
586
+ type = "complex",
587
+ viewport = "desktop",
588
+ buttonsRow = "single",
589
+ anchor = "bottom-left",
590
+ expanded,
591
+ defaultExpanded = false,
592
+ onExpandedChange,
593
+ categories = DEFAULT_CATEGORIES,
594
+ selectedCategoryIds,
595
+ defaultSelectedCategoryIds,
596
+ onSelectionChange,
597
+ labels,
598
+ cookiePolicyUrl,
599
+ privacyPolicyUrl,
600
+ onAcceptAll,
601
+ onRejectAll,
602
+ onCustomize,
603
+ onConfirmSelection,
604
+ onDismiss,
605
+ onClose,
606
+ showCloseButton = true,
607
+ position = "fixed",
608
+ offset = EDGE_OFFSET,
609
+ testID = "gdpr-banner",
610
+ themeMode,
611
+ themeProductContext
612
+ }) => {
613
+ const { theme } = (0, import_xui_core2.useResolvedTheme)({ themeMode, themeProductContext });
614
+ const containerRef = (0, import_react.useRef)(null);
615
+ const rawId = (0, import_xui_core2.useId)();
616
+ const titleId = `gdpr-banner-title-${rawId.replace(/:/g, "")}`;
617
+ const t = (0, import_react.useMemo)(() => ({ ...DEFAULT_LABELS, ...labels }), [labels]);
618
+ const isMobile = viewport === "mobile";
619
+ const isSimple = type === "simple";
620
+ const allIds = (0, import_react.useMemo)(
621
+ () => categories.map((category) => category.id),
622
+ [categories]
623
+ );
624
+ const lockedIds = (0, import_react.useMemo)(
625
+ () => categories.filter((category) => category.locked).map((c) => c.id),
626
+ [categories]
627
+ );
628
+ const isExpandedControlled = expanded !== void 0;
629
+ const [internalExpanded, setInternalExpanded] = (0, import_react.useState)(defaultExpanded);
630
+ const isExpanded = !isSimple && (isExpandedControlled ? expanded : internalExpanded);
631
+ const isSelectionControlled = selectedCategoryIds !== void 0;
632
+ const [internalSelection, setInternalSelection] = (0, import_react.useState)(
633
+ defaultSelectedCategoryIds ?? allIds
634
+ );
635
+ const selection = isSelectionControlled ? selectedCategoryIds : internalSelection;
636
+ const [activeCategoryId, setActiveCategoryId] = (0, import_react.useState)(null);
637
+ const setExpanded = (0, import_react.useCallback)(
638
+ (next) => {
639
+ if (!isExpandedControlled) setInternalExpanded(next);
640
+ onExpandedChange?.(next);
641
+ },
642
+ [isExpandedControlled, onExpandedChange]
643
+ );
644
+ const setSelection = (0, import_react.useCallback)(
645
+ (next) => {
646
+ if (!isSelectionControlled) setInternalSelection(next);
647
+ onSelectionChange?.(next);
648
+ },
649
+ [isSelectionControlled, onSelectionChange]
650
+ );
651
+ (0, import_react.useEffect)(() => {
652
+ if (!onClose || typeof document === "undefined") return;
653
+ const handleKeyDown = (event) => {
654
+ if (event.key === "Escape") onClose();
655
+ };
656
+ document.addEventListener("keydown", handleKeyDown);
657
+ return () => document.removeEventListener("keydown", handleKeyDown);
658
+ }, [onClose]);
659
+ (0, import_react.useEffect)(() => {
660
+ const container = containerRef.current;
661
+ if (!container) return;
662
+ const [first] = getFocusableElements(container);
663
+ first?.focus();
664
+ }, [isExpanded]);
665
+ const handleTabKey = (0, import_react.useCallback)((event) => {
666
+ if (event.key !== "Tab" || !containerRef.current) return;
667
+ const focusable = getFocusableElements(containerRef.current);
668
+ const first = focusable[0];
669
+ const last = focusable[focusable.length - 1];
670
+ if (!first) {
671
+ event.preventDefault();
672
+ return;
673
+ }
674
+ if (event.shiftKey && document.activeElement === first) {
675
+ event.preventDefault();
676
+ last.focus();
677
+ } else if (!event.shiftKey && document.activeElement === last) {
678
+ event.preventDefault();
679
+ first.focus();
680
+ }
681
+ }, []);
682
+ const handleToggleCategory = (0, import_react.useCallback)(
683
+ (id) => {
684
+ const category = categories.find((item) => item.id === id);
685
+ if (!category || category.locked) return;
686
+ const next = selection.includes(id) ? selection.filter((item) => item !== id) : [...selection, id];
687
+ setSelection(next);
688
+ },
689
+ [categories, selection, setSelection]
690
+ );
691
+ const handleAcceptAll = (0, import_react.useCallback)(() => {
692
+ setSelection(allIds);
693
+ onAcceptAll?.();
694
+ }, [allIds, onAcceptAll, setSelection]);
695
+ const handleRejectAll = (0, import_react.useCallback)(() => {
696
+ setSelection(lockedIds);
697
+ onRejectAll?.();
698
+ }, [lockedIds, onRejectAll, setSelection]);
699
+ const handleCustomize = (0, import_react.useCallback)(() => {
700
+ setExpanded(true);
701
+ onCustomize?.();
702
+ }, [onCustomize, setExpanded]);
703
+ const handleCollapse = (0, import_react.useCallback)(() => {
704
+ setExpanded(false);
705
+ }, [setExpanded]);
706
+ const handleConfirmSelection = (0, import_react.useCallback)(() => {
707
+ onConfirmSelection?.(selection);
708
+ }, [onConfirmSelection, selection]);
709
+ const sizing = theme.sizing.modal();
710
+ const titleToken = theme.typographyTokens.basic["body-lg"];
711
+ const bodyToken = theme.typographyTokens.basic["body-sm"];
712
+ const solidButton = (key, label, onPress, fullWidth) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
713
+ import_xui_button.Button,
714
+ {
715
+ variant: "secondary",
716
+ size: "md",
717
+ fullWidth,
718
+ onPress,
719
+ testID: `${testID}__${key}`,
720
+ themeMode,
721
+ themeProductContext,
722
+ children: label
723
+ },
724
+ key
725
+ );
726
+ const textButton = (key, label, onPress) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
727
+ import_xui_button.FlexButton,
728
+ {
729
+ variant: "brand",
730
+ size: "lg",
731
+ onPress,
732
+ testID: `${testID}__${key}`,
733
+ themeMode,
734
+ themeProductContext,
735
+ children: label
736
+ },
737
+ key
738
+ );
739
+ const renderActions = () => {
740
+ if (isSimple) {
741
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
742
+ Box,
743
+ {
744
+ width: "100%",
745
+ flexDirection: "row",
746
+ justifyContent: isMobile ? "center" : "flex-end",
747
+ children: solidButton("dismiss", t.dismiss, () => onDismiss?.(), isMobile)
748
+ }
749
+ );
750
+ }
751
+ if (isExpanded) {
752
+ const close = textButton("close", t.close, handleCollapse);
753
+ const reject = solidButton(
754
+ "reject-all",
755
+ t.rejectAll,
756
+ handleRejectAll,
757
+ isMobile
758
+ );
759
+ const confirm = solidButton(
760
+ "confirm-selection",
761
+ t.confirmSelection,
762
+ handleConfirmSelection,
763
+ isMobile
764
+ );
765
+ return isMobile ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box, { width: "100%", gap: CONTENT_GAP, alignItems: "center", children: [confirm, reject, close] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
766
+ Box,
767
+ {
768
+ width: "100%",
769
+ flexDirection: "row",
770
+ alignItems: "center",
771
+ justifyContent: "flex-end",
772
+ gap: CONTENT_GAP,
773
+ children: [close, reject, confirm]
774
+ }
775
+ );
776
+ }
777
+ const customize = textButton("customize", t.customize, handleCustomize);
778
+ if (isMobile) {
779
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box, { width: "100%", gap: CONTENT_GAP, alignItems: "center", children: [
780
+ solidButton("accept-all", t.acceptAll, handleAcceptAll, true),
781
+ solidButton("reject-all", t.rejectAll, handleRejectAll, true),
782
+ customize
783
+ ] });
784
+ }
785
+ if (buttonsRow === "double") {
786
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box, { width: "100%", gap: CONTENT_GAP, children: [
787
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box, { flexDirection: "row", justifyContent: "center", width: "100%", children: customize }),
788
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box, { flexDirection: "row", gap: CONTENT_GAP, width: "100%", children: [
789
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box, { flex: 1, children: solidButton("reject-all", t.rejectAll, handleRejectAll, true) }),
790
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box, { flex: 1, children: solidButton("accept-all", t.acceptAll, handleAcceptAll, true) })
791
+ ] })
792
+ ] });
793
+ }
794
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
795
+ Box,
796
+ {
797
+ width: "100%",
798
+ flexDirection: "row",
799
+ alignItems: "center",
800
+ justifyContent: "flex-end",
801
+ gap: CONTENT_GAP,
802
+ children: [
803
+ customize,
804
+ solidButton("reject-all", t.rejectAll, handleRejectAll, false),
805
+ solidButton("accept-all", t.acceptAll, handleAcceptAll, false)
806
+ ]
807
+ }
808
+ );
809
+ };
810
+ const edge = isExpanded ? 0 : offset;
811
+ const isFixed = position === "fixed";
812
+ const spansFullWidth = isExpanded || isMobile;
813
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
814
+ Box,
815
+ {
816
+ ref: containerRef,
817
+ testID,
818
+ role: "dialog",
819
+ "aria-labelledby": titleId,
820
+ onKeyDown: handleTabKey,
821
+ "data-gdpr-banner-type": type,
822
+ "data-gdpr-banner-viewport": viewport,
823
+ "data-gdpr-banner-expanded": isExpanded ? "true" : "false",
824
+ position: isFixed ? "fixed" : "relative",
825
+ bottom: isFixed ? edge : void 0,
826
+ left: isFixed && (spansFullWidth || anchor === "bottom-left") ? edge : void 0,
827
+ right: isFixed && (spansFullWidth || anchor === "bottom-right") ? edge : void 0,
828
+ zIndex: isFixed ? 1e3 : void 0,
829
+ width: spansFullWidth ? "100%" : CARD_WIDTH,
830
+ maxWidth: "100%",
831
+ backgroundColor: theme.colors.background.primary,
832
+ borderRadius: isExpanded ? 0 : sizing.borderRadius,
833
+ padding: isMobile ? 16 : 20,
834
+ gap: CONTENT_GAP,
835
+ children: [
836
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
837
+ Box,
838
+ {
839
+ flexDirection: "row",
840
+ alignItems: "flex-start",
841
+ justifyContent: "space-between",
842
+ gap: 12,
843
+ width: "100%",
844
+ children: [
845
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
846
+ Text,
847
+ {
848
+ id: titleId,
849
+ color: theme.colors.content.primary,
850
+ fontSize: titleToken.fontSize,
851
+ lineHeight: titleToken.lineHeight,
852
+ fontWeight: titleToken.accent?.fontWeight ?? 500,
853
+ fontFamily: theme.fonts.body,
854
+ children: isExpanded ? t.privacySettingsTitle : t.title
855
+ }
856
+ ),
857
+ showCloseButton && onClose && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
858
+ import_xui_button.FlexButton,
859
+ {
860
+ variant: "tertiary",
861
+ size: "xs",
862
+ onPress: onClose,
863
+ "aria-label": t.closeButtonAriaLabel,
864
+ testID: `${testID}__close-icon`,
865
+ themeMode,
866
+ themeProductContext,
867
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_xui_icons_base2.Remove, { variant: "line" })
868
+ }
869
+ )
870
+ ]
871
+ }
872
+ ),
873
+ isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
874
+ PrivacySettings,
875
+ {
876
+ categories,
877
+ selectedCategoryIds: selection,
878
+ onToggleCategory: handleToggleCategory,
879
+ activeCategoryId,
880
+ onActiveCategoryChange: setActiveCategoryId,
881
+ viewport,
882
+ labels: {
883
+ generalInformation: t.generalInformation,
884
+ generalInformationDescription: t.generalInformationDescription,
885
+ cookiePolicy: t.cookiePolicy,
886
+ privacyPolicy: t.privacyPolicy
887
+ },
888
+ cookiePolicyUrl,
889
+ privacyPolicyUrl,
890
+ themeMode,
891
+ themeProductContext,
892
+ testID: `${testID}__privacy-settings`
893
+ }
894
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
895
+ Text,
896
+ {
897
+ color: theme.colors.content.secondary,
898
+ fontSize: bodyToken.fontSize,
899
+ lineHeight: bodyToken.lineHeight,
900
+ fontFamily: theme.fonts.body,
901
+ testID: `${testID}__description`,
902
+ children: isSimple ? t.simpleDescription : t.description
903
+ }
904
+ ),
905
+ renderActions()
906
+ ]
907
+ }
908
+ );
909
+ };
910
+ GdprBanner.displayName = "GdprBanner";
911
+ // Annotate the CommonJS export names for ESM import in node:
912
+ 0 && (module.exports = {
913
+ DEFAULT_CATEGORIES,
914
+ DEFAULT_LABELS,
915
+ GdprBanner,
916
+ PrivacySettings
917
+ });
918
+ //# sourceMappingURL=index.js.map