@xsolla/xui-tooltip 0.216.1 → 0.218.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/web/index.d.ts CHANGED
@@ -1,11 +1,14 @@
1
- import React, { ReactNode } from 'react';
1
+ import * as React from 'react';
2
+ import React__default, { ReactNode } from 'react';
2
3
  import { ThemeOverrideProps } from '@xsolla/xui-core';
3
4
 
4
5
  type TooltipSize = "sm" | "md" | "lg" | "xl";
5
6
  type TooltipPlacement = "top" | "top-left" | "top-right" | "bottom" | "bottom-left" | "bottom-right" | "left" | "right";
6
7
  interface TooltipProps {
7
8
  /**
8
- * The content of the tooltip.
9
+ * The content of the tooltip. Strings and numbers are rendered as themed
10
+ * text; any other node is rendered as-is and inherits the tooltip's
11
+ * typography (see `TooltipText` / `TooltipLink`).
9
12
  */
10
13
  content: ReactNode;
11
14
  /**
@@ -52,7 +55,125 @@ interface TooltipProps {
52
55
  className?: string;
53
56
  testID?: string;
54
57
  }
58
+ /**
59
+ * Typography and colour tokens the tooltip publishes to its content, so nested
60
+ * helpers do not have to re-derive them from the tooltip's `size`.
61
+ */
62
+ interface TooltipContentStyles {
63
+ /** Size of the surrounding tooltip. */
64
+ size: TooltipSize;
65
+ /** Font size for the surrounding tooltip's size, in pixels. */
66
+ fontSize: number;
67
+ /** Line height for the surrounding tooltip's size, as a CSS length. */
68
+ lineHeight: string;
69
+ /** `content/primary` — default colour for tooltip copy. */
70
+ color: string;
71
+ /** `content/brand-primary` — default colour for links inside a tooltip. */
72
+ linkColor: string;
73
+ }
74
+ interface TooltipTextProps {
75
+ children?: ReactNode;
76
+ /**
77
+ * Render with the *accent* (bold) weight of the body token matching the
78
+ * tooltip's size.
79
+ * @default false
80
+ */
81
+ accent?: boolean;
82
+ /**
83
+ * Overrides the size inherited from the surrounding tooltip. Defaults to the
84
+ * tooltip's own `size`, or `"md"` when rendered outside one.
85
+ */
86
+ size?: TooltipSize;
87
+ /** Overrides the inherited text colour. */
88
+ color?: string;
89
+ style?: any;
90
+ "data-testid"?: string;
91
+ testID?: string;
92
+ }
93
+ interface TooltipLinkProps extends TooltipTextProps {
94
+ /** Destination. Omit when driving navigation through `onPress`. */
95
+ href?: string;
96
+ /**
97
+ * Anchor target. `"_blank"` automatically adds `noopener noreferrer` to
98
+ * `rel`.
99
+ */
100
+ target?: string;
101
+ rel?: string;
102
+ /**
103
+ * Click handler. When supplied, the anchor's default navigation is
104
+ * prevented.
105
+ */
106
+ onPress?: () => void;
107
+ /**
108
+ * Underline the link text.
109
+ * @default false
110
+ */
111
+ underline?: boolean;
112
+ }
113
+
114
+ declare const Tooltip: React__default.ForwardRefExoticComponent<TooltipProps & ThemeOverrideProps & React__default.RefAttributes<HTMLDivElement>>;
55
115
 
56
- declare const Tooltip: React.ForwardRefExoticComponent<TooltipProps & ThemeOverrideProps & React.RefAttributes<HTMLDivElement>>;
116
+ /**
117
+ * Text inside a `Tooltip`'s `content`, styled with the tooltip's own size
118
+ * scale and `content/primary` colour.
119
+ *
120
+ * Pass `accent` for bold copy — the weight is read from the body accent token
121
+ * matching the tooltip's size, so it tracks the active product context instead
122
+ * of a hardcoded `700`.
123
+ */
124
+ declare const TooltipText: React__default.FC<TooltipTextProps & ThemeOverrideProps>;
125
+
126
+ /**
127
+ * A link inside a `Tooltip`'s `content`. Uses the `content/brand-primary`
128
+ * token and the tooltip's own size scale, so it sits inline with surrounding
129
+ * `TooltipText` without the caller re-deriving typography.
130
+ *
131
+ * The tooltip surface is hoverable, so the link is reachable with a pointer.
132
+ * It is **not** reachable by keyboard — tabbing away from the trigger blurs it
133
+ * and dismisses the tooltip. When a link must be keyboard-operable, use
134
+ * [Toggletip](../../toggletip) instead.
135
+ */
136
+ declare const TooltipLink: React__default.FC<TooltipLinkProps & ThemeOverrideProps>;
137
+
138
+ /**
139
+ * Publishes the surrounding tooltip's resolved typography and colour tokens to
140
+ * whatever is rendered inside its `content`, so `TooltipText` / `TooltipLink`
141
+ * inherit the tooltip's size without the caller repeating it.
142
+ *
143
+ * `null` when the consumer is rendered outside a `Tooltip` — the helpers then
144
+ * fall back to their own defaults.
145
+ */
146
+ declare const TooltipContentContext: React.Context<TooltipContentStyles | null>;
147
+ declare const useTooltipContentStyles: () => TooltipContentStyles | null;
148
+
149
+ /**
150
+ * Per-size font metrics for tooltip copy. Mirrors the tooltip size matrix in
151
+ * the Figma UI kit (node 5680-32928).
152
+ */
153
+ declare const getTooltipTypoStyles: (size: TooltipSize) => {
154
+ fontSize: number;
155
+ lineHeight: string;
156
+ };
157
+ /** Fallbacks for a resolved theme that predates `typographyTokens`. */
158
+ declare const DEFAULT_BODY_FONT_WEIGHT = 400;
159
+ declare const DEFAULT_ACCENT_FONT_WEIGHT = 500;
160
+ interface TooltipTypographyTokens {
161
+ basic?: Record<string, {
162
+ fontWeight?: number;
163
+ accent?: {
164
+ fontWeight?: number;
165
+ };
166
+ } | undefined>;
167
+ }
168
+ /** The slice of the resolved theme this module reads. */
169
+ interface TooltipThemeTypography {
170
+ typographyTokens?: TooltipTypographyTokens;
171
+ }
172
+ /**
173
+ * Resolves the accent font weight for `size` from the theme's body typography
174
+ * scale, so bold tooltip copy follows the active product context (b2b/b2c use
175
+ * 500, corpsite uses 600) instead of a hardcoded weight.
176
+ */
177
+ declare const getTooltipAccentFontWeight: (typographyTokens: TooltipTypographyTokens | undefined, size: TooltipSize) => number;
57
178
 
58
- export { Tooltip, type TooltipPlacement, type TooltipProps, type TooltipSize };
179
+ export { DEFAULT_ACCENT_FONT_WEIGHT, DEFAULT_BODY_FONT_WEIGHT, Tooltip, TooltipContentContext, type TooltipContentStyles, TooltipLink, type TooltipLinkProps, type TooltipPlacement, type TooltipProps, type TooltipSize, TooltipText, type TooltipTextProps, type TooltipThemeTypography, type TooltipTypographyTokens, getTooltipAccentFontWeight, getTooltipTypoStyles, useTooltipContentStyles };
package/web/index.js CHANGED
@@ -30,12 +30,20 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.tsx
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- Tooltip: () => Tooltip
33
+ DEFAULT_ACCENT_FONT_WEIGHT: () => DEFAULT_ACCENT_FONT_WEIGHT,
34
+ DEFAULT_BODY_FONT_WEIGHT: () => DEFAULT_BODY_FONT_WEIGHT,
35
+ Tooltip: () => Tooltip,
36
+ TooltipContentContext: () => TooltipContentContext,
37
+ TooltipLink: () => TooltipLink,
38
+ TooltipText: () => TooltipText,
39
+ getTooltipAccentFontWeight: () => getTooltipAccentFontWeight,
40
+ getTooltipTypoStyles: () => getTooltipTypoStyles,
41
+ useTooltipContentStyles: () => useTooltipContentStyles
34
42
  });
35
43
  module.exports = __toCommonJS(index_exports);
36
44
 
37
45
  // src/Tooltip.tsx
38
- var import_react4 = require("react");
46
+ var import_react5 = require("react");
39
47
 
40
48
  // ../../foundation/primitives-web/src/Box.tsx
41
49
  var import_react2 = __toESM(require("react"));
@@ -383,6 +391,41 @@ var Portal = ({ children }) => {
383
391
  return (0, import_react_dom.createPortal)(children, mountNode);
384
392
  };
385
393
 
394
+ // src/TooltipContentContext.tsx
395
+ var import_react4 = require("react");
396
+ var TooltipContentContext = (0, import_react4.createContext)(
397
+ null
398
+ );
399
+ var useTooltipContentStyles = () => (0, import_react4.useContext)(TooltipContentContext);
400
+
401
+ // src/contentTypography.ts
402
+ var getTooltipTypoStyles = (size) => {
403
+ switch (size) {
404
+ case "sm":
405
+ return { fontSize: 14, lineHeight: "16px" };
406
+ case "md":
407
+ return { fontSize: 16, lineHeight: "18px" };
408
+ case "lg":
409
+ return { fontSize: 18, lineHeight: "20px" };
410
+ case "xl":
411
+ return { fontSize: 20, lineHeight: "22px" };
412
+ default:
413
+ return { fontSize: 16, lineHeight: "18px" };
414
+ }
415
+ };
416
+ var TOOLTIP_BODY_VARIANT = {
417
+ sm: "body-sm",
418
+ md: "body-md",
419
+ lg: "body-lg",
420
+ xl: "body-lg"
421
+ };
422
+ var DEFAULT_BODY_FONT_WEIGHT = 400;
423
+ var DEFAULT_ACCENT_FONT_WEIGHT = 500;
424
+ var getTooltipAccentFontWeight = (typographyTokens, size) => {
425
+ const variant = TOOLTIP_BODY_VARIANT[size] ?? TOOLTIP_BODY_VARIANT.md;
426
+ return typographyTokens?.basic?.[variant]?.accent?.fontWeight ?? DEFAULT_ACCENT_FONT_WEIGHT;
427
+ };
428
+
386
429
  // src/Tooltip.tsx
387
430
  var import_jsx_runtime3 = require("react/jsx-runtime");
388
431
  var TOOLTIP_OPEN_EVENT = "xui-tooltip-open";
@@ -453,20 +496,6 @@ var getPositionStyles = (triggerRect, placement, offset, isVisible) => {
453
496
  };
454
497
  }
455
498
  };
456
- var getTypoStyles = (size) => {
457
- switch (size) {
458
- case "sm":
459
- return { fontSize: 14, lineHeight: "16px" };
460
- case "md":
461
- return { fontSize: 16, lineHeight: "18px" };
462
- case "lg":
463
- return { fontSize: 18, lineHeight: "20px" };
464
- case "xl":
465
- return { fontSize: 20, lineHeight: "22px" };
466
- default:
467
- return { fontSize: 16, lineHeight: "18px" };
468
- }
469
- };
470
499
  var getArrowStyles = (placement) => {
471
500
  const base = {
472
501
  width: ARROW_SIZE,
@@ -486,7 +515,7 @@ var getArrowStyles = (placement) => {
486
515
  };
487
516
  return { ...base, ...placementStyles[placement] };
488
517
  };
489
- var Tooltip = (0, import_react4.forwardRef)(
518
+ var Tooltip = (0, import_react5.forwardRef)(
490
519
  ({
491
520
  content,
492
521
  children,
@@ -504,17 +533,17 @@ var Tooltip = (0, import_react4.forwardRef)(
504
533
  themeMode,
505
534
  themeProductContext
506
535
  }, ref) => {
507
- const [isHovered, setIsHovered] = (0, import_react4.useState)(false);
508
- const [triggerRect, setTriggerRect] = (0, import_react4.useState)(null);
536
+ const [isHovered, setIsHovered] = (0, import_react5.useState)(false);
537
+ const [triggerRect, setTriggerRect] = (0, import_react5.useState)(null);
509
538
  const { theme: resolvedTheme } = (0, import_xui_core.useResolvedTheme)({
510
539
  themeMode,
511
540
  themeProductContext
512
541
  });
513
542
  const theme = resolvedTheme;
514
- const enterTimeoutRef = (0, import_react4.useRef)(null);
515
- const leaveTimeoutRef = (0, import_react4.useRef)(null);
516
- const rafRef = (0, import_react4.useRef)(null);
517
- const triggerRef = (0, import_react4.useRef)(null);
543
+ const enterTimeoutRef = (0, import_react5.useRef)(null);
544
+ const leaveTimeoutRef = (0, import_react5.useRef)(null);
545
+ const rafRef = (0, import_react5.useRef)(null);
546
+ const triggerRef = (0, import_react5.useRef)(null);
518
547
  const tooltipId = (0, import_xui_core.useId)();
519
548
  const modalId = (0, import_xui_core.useModalId)();
520
549
  const overlayLayer = (0, import_xui_core.useOverlayLayer)();
@@ -537,14 +566,29 @@ var Tooltip = (0, import_react4.forwardRef)(
537
566
  clearPendingVisibilityUpdates();
538
567
  setIsHovered(false);
539
568
  };
540
- const positionStyles = (0, import_react4.useMemo)(
569
+ const positionStyles = (0, import_react5.useMemo)(
541
570
  () => triggerRect ? getPositionStyles(triggerRect, placement, offset, isVisible) : { top: 0, left: 0 },
542
571
  [triggerRect, placement, offset, isVisible]
543
572
  );
544
- const typoStyles = (0, import_react4.useMemo)(() => getTypoStyles(size), [size]);
573
+ const typoStyles = (0, import_react5.useMemo)(() => getTooltipTypoStyles(size), [size]);
545
574
  const borderRadius = (theme.shape.tooltip[size] ?? theme.shape.tooltip.md).borderRadius;
546
- const arrowStyles = (0, import_react4.useMemo)(() => getArrowStyles(placement), [placement]);
547
- (0, import_react4.useEffect)(() => {
575
+ const arrowStyles = (0, import_react5.useMemo)(() => getArrowStyles(placement), [placement]);
576
+ const contentStyles = (0, import_react5.useMemo)(
577
+ () => ({
578
+ size,
579
+ fontSize: typoStyles.fontSize,
580
+ lineHeight: typoStyles.lineHeight,
581
+ color: theme.colors.content.primary,
582
+ linkColor: theme.colors.content.brand.primary
583
+ }),
584
+ [
585
+ size,
586
+ typoStyles,
587
+ theme.colors.content.primary,
588
+ theme.colors.content.brand.primary
589
+ ]
590
+ );
591
+ (0, import_react5.useEffect)(() => {
548
592
  if (!isVisible || import_xui_core.isNative) return;
549
593
  const updatePosition = () => {
550
594
  if (triggerRef.current) {
@@ -604,7 +648,7 @@ var Tooltip = (0, import_react4.forwardRef)(
604
648
  setIsHovered(false);
605
649
  }
606
650
  };
607
- (0, import_react4.useEffect)(() => {
651
+ (0, import_react5.useEffect)(() => {
608
652
  if (import_xui_core.isNative) return;
609
653
  const handleEscape = (event) => {
610
654
  if (event.key === "Escape" && isVisible) {
@@ -698,21 +742,28 @@ var Tooltip = (0, import_react4.forwardRef)(
698
742
  }
699
743
  }
700
744
  ),
701
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
745
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TooltipContentContext.Provider, { value: contentStyles, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
702
746
  Box,
703
747
  {
704
748
  style: {
705
749
  wordWrap: "break-word",
706
750
  overflowWrap: "break-word",
707
751
  maxWidth: "100%",
708
- whiteSpace: "normal"
752
+ whiteSpace: "normal",
753
+ // Custom `content` inherits the tooltip's own type scale,
754
+ // so plain markup (`<strong>`, `<a>`, bare strings) reads
755
+ // at the right size without re-deriving it per call site.
756
+ fontSize: typoStyles.fontSize,
757
+ lineHeight: typoStyles.lineHeight,
758
+ fontWeight: DEFAULT_BODY_FONT_WEIGHT,
759
+ color: theme.colors.content.primary
709
760
  },
710
761
  children: typeof content === "string" || typeof content === "number" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
711
762
  Text,
712
763
  {
713
764
  color: theme.colors.content.primary,
714
765
  fontSize: typoStyles.fontSize,
715
- fontWeight: "400",
766
+ fontWeight: String(DEFAULT_BODY_FONT_WEIGHT),
716
767
  style: {
717
768
  lineHeight: typoStyles.lineHeight,
718
769
  whiteSpace: "pre-line"
@@ -721,7 +772,7 @@ var Tooltip = (0, import_react4.forwardRef)(
721
772
  }
722
773
  ) : content
723
774
  }
724
- )
775
+ ) })
725
776
  ]
726
777
  }
727
778
  ) })
@@ -731,8 +782,136 @@ var Tooltip = (0, import_react4.forwardRef)(
731
782
  }
732
783
  );
733
784
  Tooltip.displayName = "Tooltip";
785
+
786
+ // src/TooltipText.tsx
787
+ var import_xui_core2 = require("@xsolla/xui-core");
788
+ var import_jsx_runtime4 = require("react/jsx-runtime");
789
+ var TooltipText = ({
790
+ children,
791
+ accent = false,
792
+ size,
793
+ color,
794
+ style,
795
+ testID,
796
+ "data-testid": dataTestId,
797
+ themeMode,
798
+ themeProductContext
799
+ }) => {
800
+ const inherited = useTooltipContentStyles();
801
+ const { theme } = (0, import_xui_core2.useResolvedTheme)({ themeMode, themeProductContext });
802
+ const resolvedSize = size ?? inherited?.size ?? "md";
803
+ const typo = getTooltipTypoStyles(resolvedSize);
804
+ const resolvedColor = color ?? inherited?.color ?? theme.colors.content.primary;
805
+ const fontWeight = accent ? getTooltipAccentFontWeight(
806
+ theme.typographyTokens,
807
+ resolvedSize
808
+ ) : DEFAULT_BODY_FONT_WEIGHT;
809
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
810
+ Text,
811
+ {
812
+ testID,
813
+ "data-testid": dataTestId,
814
+ color: resolvedColor,
815
+ fontSize: typo.fontSize,
816
+ fontWeight: String(fontWeight),
817
+ style: {
818
+ lineHeight: typo.lineHeight,
819
+ whiteSpace: "pre-line",
820
+ ...style
821
+ },
822
+ children
823
+ }
824
+ );
825
+ };
826
+ TooltipText.displayName = "TooltipText";
827
+
828
+ // src/TooltipLink.tsx
829
+ var import_xui_core3 = require("@xsolla/xui-core");
830
+ var import_jsx_runtime5 = require("react/jsx-runtime");
831
+ var TooltipLink = ({
832
+ children,
833
+ href,
834
+ target,
835
+ rel,
836
+ onPress,
837
+ accent = false,
838
+ underline = false,
839
+ size,
840
+ color,
841
+ style,
842
+ testID,
843
+ "data-testid": dataTestId,
844
+ themeMode,
845
+ themeProductContext
846
+ }) => {
847
+ const inherited = useTooltipContentStyles();
848
+ const { theme } = (0, import_xui_core3.useResolvedTheme)({ themeMode, themeProductContext });
849
+ const resolvedSize = size ?? inherited?.size ?? "md";
850
+ const typo = getTooltipTypoStyles(resolvedSize);
851
+ const resolvedColor = color ?? inherited?.linkColor ?? theme.colors.content.brand.primary;
852
+ const fontWeight = accent ? getTooltipAccentFontWeight(
853
+ theme.typographyTokens,
854
+ resolvedSize
855
+ ) : DEFAULT_BODY_FONT_WEIGHT;
856
+ const resolvedRel = target === "_blank" ? Array.from(
857
+ /* @__PURE__ */ new Set([
858
+ ...rel ? rel.split(/\s+/).filter(Boolean) : [],
859
+ "noopener",
860
+ "noreferrer"
861
+ ])
862
+ ).join(" ") : rel;
863
+ const handlePress = (event) => {
864
+ if (!onPress) return;
865
+ event?.preventDefault?.();
866
+ onPress();
867
+ };
868
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
869
+ Box,
870
+ {
871
+ as: "a",
872
+ href,
873
+ target,
874
+ rel: resolvedRel,
875
+ onPress: handlePress,
876
+ onClick: handlePress,
877
+ role: "link",
878
+ cursor: "pointer",
879
+ display: "inline-flex",
880
+ alignItems: "center",
881
+ testID,
882
+ "data-testid": dataTestId,
883
+ hoverStyle: { opacity: 0.8 },
884
+ focusStyle: {
885
+ outlineColor: theme.colors.border.brand,
886
+ outlineWidth: 2,
887
+ outlineStyle: "solid",
888
+ outlineOffset: 2
889
+ },
890
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
891
+ Text,
892
+ {
893
+ color: resolvedColor,
894
+ fontSize: typo.fontSize,
895
+ fontWeight: String(fontWeight),
896
+ textDecoration: underline ? "underline" : "none",
897
+ style: { lineHeight: typo.lineHeight, ...style },
898
+ children
899
+ }
900
+ )
901
+ }
902
+ );
903
+ };
904
+ TooltipLink.displayName = "TooltipLink";
734
905
  // Annotate the CommonJS export names for ESM import in node:
735
906
  0 && (module.exports = {
736
- Tooltip
907
+ DEFAULT_ACCENT_FONT_WEIGHT,
908
+ DEFAULT_BODY_FONT_WEIGHT,
909
+ Tooltip,
910
+ TooltipContentContext,
911
+ TooltipLink,
912
+ TooltipText,
913
+ getTooltipAccentFontWeight,
914
+ getTooltipTypoStyles,
915
+ useTooltipContentStyles
737
916
  });
738
917
  //# sourceMappingURL=index.js.map