pixel-react 1.21.99 → 1.22.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.
@@ -2,9 +2,7 @@
2
2
  * resizeObserverService.ts
3
3
  */
4
4
  const subscribers = new Map();
5
- // Created on first use, so importing this needs no ResizeObserver (e.g. SSR)
6
- let observer = null;
7
- const getObserver = () => (observer ??= new ResizeObserver(entries => {
5
+ const observer = new ResizeObserver(entries => {
8
6
  requestAnimationFrame(() => {
9
7
  for (const entry of entries) {
10
8
  const callback = subscribers.get(entry.target);
@@ -12,15 +10,15 @@ const getObserver = () => (observer ??= new ResizeObserver(entries => {
12
10
  callback();
13
11
  }
14
12
  });
15
- }));
13
+ });
16
14
  /**
17
15
  * Register an element to the SINGLE global observer.
18
16
  */
19
17
  export const subscribe = (el, callback) => {
20
18
  subscribers.set(el, callback);
21
- getObserver().observe(el);
19
+ observer.observe(el);
22
20
  return () => {
23
- getObserver().unobserve(el);
21
+ observer.unobserve(el);
24
22
  subscribers.delete(el);
25
23
  };
26
24
  };
@@ -1 +1 @@
1
- {"version":3,"file":"resizeObserverService.js","sourceRoot":"","sources":["../../../src/components/AutoTruncateText/resizeObserverService.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,GAAG,IAAI,GAAG,EAAuB,CAAC;AAEnD,6EAA6E;AAC7E,IAAI,QAAQ,GAA0B,IAAI,CAAC;AAE3C,MAAM,WAAW,GAAG,GAAG,EAAE,CACvB,CAAC,QAAQ,KAAK,IAAI,cAAc,CAAC,OAAO,CAAC,EAAE;IACzC,qBAAqB,CAAC,GAAG,EAAE;QACzB,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAC/C,IAAI,QAAQ;gBAAE,QAAQ,EAAE,CAAC;QAC3B,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC,CAAC;AAEN;;GAEG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,EAAW,EAAE,QAAoB,EAAE,EAAE;IAC7D,WAAW,CAAC,GAAG,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;IAC9B,WAAW,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAE1B,OAAO,GAAG,EAAE;QACV,WAAW,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAC5B,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC,CAAC;AACJ,CAAC,CAAC"}
1
+ {"version":3,"file":"resizeObserverService.js","sourceRoot":"","sources":["../../../src/components/AutoTruncateText/resizeObserverService.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,GAAG,IAAI,GAAG,EAAuB,CAAC;AAEnD,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC,EAAE;IAC5C,qBAAqB,CAAC,GAAG,EAAE;QACzB,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAC/C,IAAI,QAAQ;gBAAE,QAAQ,EAAE,CAAC;QAC3B,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,EAAW,EAAE,QAAoB,EAAE,EAAE;IAC7D,WAAW,CAAC,GAAG,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAErB,OAAO,GAAG,EAAE;QACV,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACvB,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC,CAAC;AACJ,CAAC,CAAC"}
@@ -1,4 +1,4 @@
1
1
  import { HighlightTextProps } from './types';
2
2
  import './HighlightText.scss';
3
- declare const MemoizedHighlightText: import("react").MemoExoticComponent<({ text, segments, highlight, onlyExactMatch, color, fontWeight, tooltip, truncate, }: HighlightTextProps) => import("react").JSX.Element>;
4
- export default MemoizedHighlightText;
3
+ declare const HighlightText: React.FC<HighlightTextProps>;
4
+ export default HighlightText;
@@ -1,146 +1,22 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import './HighlightText.scss';
3
- import { Fragment, isValidElement, memo, useId, useLayoutEffect, useRef, useState, } from 'react';
4
- import { subscribe } from '../AutoTruncateText/resizeObserverService';
5
- import useInteractiveAncestor from '../../hooks/useInteractiveAncestor';
3
+ import classNames from 'classnames';
4
+ import { Fragment } from 'react';
6
5
  // Function to escape special characters in the search term
7
6
  const escapeRegExp = (text) => {
8
7
  return text?.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
9
8
  };
10
- const getSegmentText = (segment) => typeof segment === 'string' ? segment : (segment.text ?? '');
11
- const getTextStyle = (color, fontWeight) => color || fontWeight ? { color, fontWeight } : undefined;
12
- // Text goes in `data-tooltip`. JSX cannot, so it is rendered into a hidden
13
- // element that `data-tooltip-id` points to, and TooltipService copies its HTML.
14
- // The pointer can move onto the tooltip (WCAG 1.4.13), and the element takes
15
- // focus so keyboard users get the tooltip too (WCAG 2.1.1).
16
- const getTooltipProps = (tooltip, templateId, focusable) => {
17
- if (!tooltip)
18
- return {};
19
- return {
20
- ...(isValidElement(tooltip)
21
- ? { 'data-tooltip-id': templateId }
22
- : { 'data-tooltip': tooltip }),
23
- 'data-tooltip-hoverable': '',
24
- tabIndex: focusable ? 0 : undefined,
25
- };
26
- };
27
- // One-line ellipsis cut-off, measured again when the text or its size changes
28
- const useIsClipped = (ref, enabled, text) => {
29
- const [isClipped, setIsClipped] = useState(false);
30
- useLayoutEffect(() => {
31
- const element = ref.current;
32
- if (!enabled || !element)
33
- return undefined;
34
- const measure = () => setIsClipped(element.scrollWidth > element.clientWidth + 0.5);
35
- measure();
36
- return subscribe(element, measure);
37
- }, [ref, enabled, text]);
38
- return enabled && isClipped;
39
- };
40
- // Where the search term occurs in the whole text, ignoring case
41
- const findMatches = (text, highlight) => {
42
- const ranges = [];
43
- const regex = new RegExp(escapeRegExp(highlight), 'gi');
44
- for (let match = regex.exec(text); match; match = regex.exec(text)) {
45
- ranges.push([match.index, match.index + match[0].length]);
46
- }
47
- return ranges;
48
- };
49
- // One segment's text, with the matches that overlap it marked. `start` is where
50
- // the segment begins in the whole text, so a match can run across segments.
51
- const markMatches = (text, start, ranges) => {
52
- const end = start + text.length;
53
- const nodes = [];
54
- let cursor = start;
55
- for (const [matchStart, matchEnd] of ranges) {
56
- if (matchStart >= end)
57
- break;
58
- if (matchEnd <= start)
59
- continue;
60
- const from = Math.max(matchStart, start);
61
- const to = Math.min(matchEnd, end);
62
- if (from > cursor)
63
- nodes.push(text.slice(cursor - start, from - start));
64
- nodes.push(_jsx("mark", { className: "ff-highlight-bg", children: text.slice(from - start, to - start) }, `highlight-${from}`));
65
- cursor = to;
9
+ const HighlightText = ({ text = '', highlight = '', onlyExactMatch = false, }) => {
10
+ if (!highlight) {
11
+ return (_jsx("span", { "aria-label": `Text: ${text}`, children: text }));
66
12
  }
67
- if (!nodes.length)
68
- return text;
69
- if (cursor < end)
70
- nodes.push(text.slice(cursor - start));
71
- return nodes;
72
- };
73
- const HighlightText = ({ text = '', segments, highlight = '', onlyExactMatch = false, color, fontWeight, tooltip, truncate = false, }) => {
74
- const rowRef = useRef(null);
75
- const tooltipId = `ff-highlight-tooltip-${useId().replace(/:/g, '')}`;
76
- const query = String(highlight ?? '');
77
- const fullText = segments ? segments.map(getSegmentText).join('') : text;
78
- const isExactMatch = !!query && onlyExactMatch && fullText.toLowerCase() === query.toLowerCase();
79
- const ranges = query && !onlyExactMatch ? findMatches(fullText, query) : [];
80
- // Without a tooltip, TooltipService shows the full text while it is cut off
81
- const showsOverflow = truncate && !tooltip;
82
- const hasTooltip = !!tooltip ||
83
- showsOverflow ||
84
- !!segments?.some((segment) => typeof segment !== 'string' && !!segment.tooltip);
85
- const isClipped = useIsClipped(rowRef, showsOverflow, fullText);
86
- // Inside a control (e.g. a button), that control takes focus instead
87
- const focusable = !useInteractiveAncestor(rowRef, hasTooltip);
88
- let offset = 0;
89
- const content = (segments ?? [text]).map((segment, index) => {
90
- const value = getSegmentText(segment);
91
- const marked = markMatches(value, offset, ranges);
92
- offset += value.length;
93
- if (typeof segment === 'string') {
94
- return _jsx(Fragment, { children: marked }, `segment-${index}`);
95
- }
96
- return (_jsx("span", { style: getTextStyle(segment.color, segment.fontWeight), ...getTooltipProps(segment.tooltip, `${tooltipId}-${index}`, focusable), children: marked }, `segment-${index}`));
97
- });
98
- const templates = [
99
- { id: tooltipId, tooltip },
100
- ...(segments ?? []).map((segment, index) => ({
101
- id: `${tooltipId}-${index}`,
102
- tooltip: typeof segment === 'string' ? undefined : segment.tooltip,
103
- })),
104
- ].filter((template) => isValidElement(template.tooltip));
105
- // The row takes focus only while its full text is hidden
106
- const rowTooltipProps = showsOverflow
107
- ? {
108
- 'data-tooltip-overflow': '',
109
- 'data-tooltip-hoverable': '',
110
- tabIndex: focusable && isClipped ? 0 : undefined,
111
- }
112
- : getTooltipProps(tooltip, tooltipId, focusable);
113
- return (_jsxs(_Fragment, { children: [_jsx("span", { ref: rowRef, className: truncate ? 'ff-highlight-text--truncate' : undefined, style: getTextStyle(color, fontWeight), ...rowTooltipProps, children: isExactMatch ? (_jsx("mark", { className: "ff-highlight-bg", children: content })) : (content) }), templates.map((template) => (_jsx("span", { id: template.id, hidden: true, children: template.tooltip }, template.id)))] }));
114
- };
115
- const isSameSegment = (a, b) => typeof a === 'string' || typeof b === 'string'
116
- ? a === b
117
- : a.text === b.text &&
118
- a.color === b.color &&
119
- a.fontWeight === b.fontWeight &&
120
- a.tooltip === b.tooltip;
121
- // Segments are compared by value (a JSX tooltip by reference), so rows that
122
- // rebuild the same segments on every render (e.g. in a virtualized table) do
123
- // not re-render
124
- const arePropsEqual = (prev, next) => {
125
- const keys = Object.keys({
126
- ...prev,
127
- ...next,
128
- });
129
- if (keys.some((key) => key !== 'segments' && !Object.is(prev[key], next[key]))) {
130
- return false;
13
+ const escapedHighlight = escapeRegExp(highlight); // Escape special characters
14
+ const parts = text?.split(new RegExp(`(${escapedHighlight})`, 'gi')); // Use escaped string in the regex
15
+ if (onlyExactMatch) {
16
+ const isTextEqualsHighlight = text.toLowerCase() === highlight.toLowerCase();
17
+ return (_jsx("span", { className: classNames(isTextEqualsHighlight ? 'ff-highlight-bg' : ''), "aria-label": `Highlighted text: ${text}`, children: text }));
131
18
  }
132
- const { segments: before } = prev;
133
- const { segments: after } = next;
134
- return (before === after ||
135
- (!!before &&
136
- !!after &&
137
- before.length === after.length &&
138
- before.every((segment, index) => {
139
- const other = after[index];
140
- return other !== undefined && isSameSegment(segment, other);
141
- })));
19
+ return (_jsx("span", { "aria-label": `Highlighted text: ${text}`, children: parts.map((part, index) => part?.toLowerCase() === highlight?.toLowerCase() ? (_jsx("span", { className: "ff-highlight-bg", children: part }, `highlight-${index}-${part}`)) : (_jsx(Fragment, { children: part }, `normal-${index}-${part}`))) }));
142
20
  };
143
- const MemoizedHighlightText = memo(HighlightText, arePropsEqual);
144
- MemoizedHighlightText.displayName = 'HighlightText';
145
- export default MemoizedHighlightText;
21
+ export default HighlightText;
146
22
  //# sourceMappingURL=HighlightText.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"HighlightText.js","sourceRoot":"","sources":["../../../src/components/HighlightText/HighlightText.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"HighlightText.js","sourceRoot":"","sources":["../../../src/components/HighlightText/HighlightText.tsx"],"names":[],"mappings":";AACA,OAAO,sBAAsB,CAAC;AAC9B,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEjC,2DAA2D;AAC3D,MAAM,YAAY,GAAG,CAAC,IAAY,EAAU,EAAE;IAC5C,OAAO,IAAI,EAAE,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC,CAAC,oCAAoC;AAC3F,CAAC,CAAC;AAEF,MAAM,aAAa,GAAiC,CAAC,EACnD,IAAI,GAAG,EAAE,EACT,SAAS,GAAG,EAAE,EACd,cAAc,GAAG,KAAK,GACvB,EAAE,EAAE;IACH,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,CACL,6BAAkB,SAAS,IAAI,EAAE,YAAG,IAAI,GAAQ,CACjD,CAAC;IACJ,CAAC;IAED,MAAM,gBAAgB,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC,CAAC,4BAA4B;IAC9E,MAAM,KAAK,GAAG,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,gBAAgB,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,kCAAkC;IAExG,IAAI,cAAc,EAAE,CAAC;QACnB,MAAM,qBAAqB,GACzB,IAAI,CAAC,WAAW,EAAE,KAAK,SAAS,CAAC,WAAW,EAAE,CAAC;QACjD,OAAO,CACL,eACE,SAAS,EAAE,UAAU,CAAC,qBAAqB,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC,gBACzD,qBAAqB,IAAI,EAAE,YAEtC,IAAI,GACA,CACR,CAAC;IACJ,CAAC;IAED,OAAO,CACL,6BAAkB,qBAAqB,IAAI,EAAE,YAC1C,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CACzB,IAAI,EAAE,WAAW,EAAE,KAAK,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,CACjD,eAAyC,SAAS,EAAC,iBAAiB,YACjE,IAAI,IADI,aAAa,KAAK,IAAI,IAAI,EAAE,CAEhC,CACR,CAAC,CAAC,CAAC,CACF,KAAC,QAAQ,cAAkC,IAAI,IAAhC,UAAU,KAAK,IAAI,IAAI,EAAE,CAAmB,CAC5D,CACF,GACI,CACR,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,aAAa,CAAC"}
@@ -1,2 +1 @@
1
1
  export { default } from './HighlightText';
2
- export type { HighlightTextProps, HighlightTextPart, HighlightTextSegment, } from './types';
@@ -1,46 +1,5 @@
1
- import type { CSSProperties, ReactElement } from 'react';
2
- /** A styled part of the text, e.g. a variable inside a step name */
3
- export interface HighlightTextPart {
4
- text: string;
5
- /**
6
- * Needs the same contrast as `color` on the row. Colour alone must not tell
7
- * kinds of parts apart (WCAG 1.4.1): a tooltip can say what the part is.
8
- */
9
- color?: string;
10
- fontWeight?: CSSProperties['fontWeight'];
11
- /** Shown on hover over this part only. Text or JSX, as for `tooltip` below */
12
- tooltip?: string | ReactElement;
13
- }
14
- /** Plain text, or a styled part */
15
- export type HighlightTextSegment = string | HighlightTextPart;
16
1
  export interface HighlightTextProps {
17
2
  text?: string;
18
- /**
19
- * Use instead of `text` when parts need their own colour or tooltip.
20
- * Search highlighting runs on the joined text, so a match can span parts.
21
- */
22
- segments?: HighlightTextSegment[];
23
3
  highlight?: String;
24
4
  onlyExactMatch?: boolean;
25
- /**
26
- * Text colour: any CSS colour value or variable, e.g. `var(--brand-color)`.
27
- * WCAG 1.4.6 (AAA) needs 7:1 contrast with the background (4.5:1 for large
28
- * text), also against the search highlight.
29
- */
30
- color?: string;
31
- fontWeight?: CSSProperties['fontWeight'];
32
- /**
33
- * Shown on hover or keyboard focus, through the global TooltipService. The
34
- * text takes focus for it, except inside another control (e.g. a button),
35
- * which should then carry the tooltip itself. Escape hides it. JSX is copied
36
- * into the tooltip as HTML, so it is static: event handlers inside it do not
37
- * run.
38
- */
39
- tooltip?: string | ReactElement;
40
- /**
41
- * Keep to one line and end with an ellipsis when it does not fit. The full
42
- * text shows on hover or focus, only while it is cut off (unless `tooltip`
43
- * is set); the row takes focus only then.
44
- */
45
- truncate?: boolean;
46
5
  }
@@ -1,8 +1,7 @@
1
1
  import { useEffect } from 'react';
2
- import tippy, { hideAll } from 'tippy.js';
2
+ import { delegate, hideAll } from 'tippy.js';
3
3
  import 'tippy.js/dist/tippy.css';
4
4
  import './TooltipService.scss';
5
- const TOOLTIP_TARGET = '[data-tooltip], [data-tooltip-id], [data-tooltip-overflow]';
6
5
  const VALID_PLACEMENTS = new Set([
7
6
  'auto',
8
7
  'auto-start',
@@ -20,30 +19,6 @@ const VALID_PLACEMENTS = new Set([
20
19
  'left-start',
21
20
  'left-end',
22
21
  ]);
23
- const TOOLTIP_PROPS = {
24
- arrow: false,
25
- theme: 'ff-global-tooltip',
26
- appendTo: () => document.body,
27
- allowHTML: true,
28
- popperOptions: {
29
- modifiers: [
30
- {
31
- name: 'preventOverflow',
32
- options: {
33
- altAxis: true,
34
- padding: 8,
35
- },
36
- },
37
- {
38
- name: 'flip',
39
- options: {
40
- padding: 8,
41
- fallbackPlacements: ['top', 'right', 'left'],
42
- },
43
- },
44
- ],
45
- },
46
- };
47
22
  const getTooltipContent = (reference) => {
48
23
  const contentNode = document.createElement('div');
49
24
  // Accept template content via id
@@ -70,12 +45,7 @@ const getTooltipContent = (reference) => {
70
45
  return contentNode;
71
46
  }
72
47
  }
73
- // An overflow-only element without its own text shows the text it cuts off
74
- const rawText = reference.getAttribute('data-tooltip') ||
75
- (reference.hasAttribute('data-tooltip-overflow')
76
- ? reference.textContent
77
- : '') ||
78
- '';
48
+ const rawText = reference.getAttribute('data-tooltip') || '';
79
49
  if (!rawText.trim())
80
50
  return '';
81
51
  const text = rawText
@@ -109,185 +79,118 @@ const getTooltipContent = (reference) => {
109
79
  }
110
80
  return contentNode;
111
81
  };
112
- // One-line ellipsis cut-off, read only when the element is hovered
113
- const isClipped = (element) => element.scrollWidth > element.clientWidth + 0.5;
114
- // The innermost element with something to show. A `data-tooltip-overflow`
115
- // element counts only while its text is cut off; otherwise the next one out is tried.
116
- const findTooltipTarget = (start) => {
117
- let element = start.closest(TOOLTIP_TARGET);
118
- while (element) {
119
- const overflowOnly = element.hasAttribute('data-tooltip-overflow');
120
- if ((!overflowOnly || isClipped(element)) &&
121
- getTooltipContent(element) !== '') {
122
- return element;
123
- }
124
- element = element.parentElement?.closest(TOOLTIP_TARGET) ?? null;
125
- }
126
- return null;
127
- };
128
- // Tippy's own ARIA, except where it would be wrong: a hoverable tooltip is not
129
- // interactive, so it still describes its element and sets no aria-expanded;
130
- // and text shown only because an ellipsis cuts it off repeats what the element
131
- // already says, so it is not added as a description
132
- const getAria = (reference) => {
133
- if (reference.hasAttribute('data-tooltip-interactive'))
134
- return {};
135
- const repeatsOwnText = reference.hasAttribute('data-tooltip-overflow') &&
136
- !reference.hasAttribute('data-tooltip') &&
137
- !reference.hasAttribute('data-tooltip-id');
138
- if (!repeatsOwnText && !reference.hasAttribute('data-tooltip-hoverable')) {
139
- return {};
140
- }
141
- return {
142
- aria: { expanded: false, content: repeatsOwnText ? null : 'describedby' },
143
- };
144
- };
145
- const getPlacement = (reference) => {
146
- // placement attribute for tooltip
147
- const normalizedPlacement = reference
148
- .getAttribute('data-tooltip-placement')
149
- ?.trim()
150
- .toLowerCase();
151
- return normalizedPlacement && VALID_PLACEMENTS.has(normalizedPlacement)
152
- ? normalizedPlacement
153
- : 'bottom';
154
- };
155
- /**
156
- * Shows at most one tooltip at a time. The instance is created when its element
157
- * is hovered or focused and destroyed when it hides, so nothing is kept for
158
- * elements that were hovered and later removed (e.g. virtualized rows).
159
- */
160
- const startTooltips = () => {
161
- let active = null;
162
- // Closed with Escape: stays hidden until the pointer or focus leaves it
163
- let dismissed = null;
164
- // While a tooltip shows: refresh it when its attributes change, and drop it
165
- // when its element leaves the page
166
- const activeObserver = new MutationObserver((records) => {
167
- if (!active)
168
- return;
169
- if (!active.reference.isConnected) {
170
- destroyActive();
171
- return;
172
- }
173
- if (records.some((record) => record.type === 'attributes')) {
174
- const content = getTooltipContent(active.reference);
175
- if (content === '') {
176
- destroyActive();
177
- }
178
- else {
179
- active.setContent(content);
82
+ const TooltipService = () => {
83
+ useEffect(() => {
84
+ const hideTooltipsOnScroll = (event) => {
85
+ const target = event.target;
86
+ if (target instanceof Element && target.closest('[data-tippy-root]')) {
87
+ return;
180
88
  }
181
- }
182
- });
183
- const destroyActive = () => {
184
- activeObserver.disconnect();
185
- active?.destroy();
186
- active = null;
187
- };
188
- const show = (reference) => {
189
- destroyActive();
190
- // Another copy of this service (e.g. from a second bundle) already handles it
191
- if (reference._tippy)
192
- return;
193
- active = tippy(reference, {
194
- ...TOOLTIP_PROPS,
195
- content: getTooltipContent(reference),
196
- placement: getPlacement(reference),
197
- // This is specific tooltip attributefor interactive. A hoverable
198
- // tooltip uses it only so the pointer can move onto it (WCAG 1.4.13).
199
- interactive: reference.hasAttribute('data-tooltip-interactive') ||
200
- reference.hasAttribute('data-tooltip-hoverable'),
201
- ...getAria(reference),
202
- showOnCreate: true,
203
- onShow(instance) {
204
- if (!instance.reference.isConnected)
89
+ hideAll({ duration: 0 });
90
+ };
91
+ window.addEventListener('scroll', hideTooltipsOnScroll, true);
92
+ const instance = delegate(document.body, {
93
+ target: '[data-tooltip], [data-tooltip-id]',
94
+ arrow: false,
95
+ placement: 'bottom',
96
+ theme: 'ff-global-tooltip',
97
+ appendTo: document.body,
98
+ allowHTML: true,
99
+ popperOptions: {
100
+ modifiers: [
101
+ {
102
+ name: 'preventOverflow',
103
+ options: {
104
+ altAxis: true,
105
+ padding: 8,
106
+ },
107
+ },
108
+ {
109
+ name: 'flip',
110
+ options: {
111
+ padding: 8,
112
+ fallbackPlacements: ['top', 'right', 'left'],
113
+ },
114
+ },
115
+ ],
116
+ },
117
+ content(reference) {
118
+ return getTooltipContent(reference);
119
+ },
120
+ onShow(tippyInstance) {
121
+ const { reference } = tippyInstance;
122
+ if (!document.body.contains(reference)) {
205
123
  return false;
124
+ }
125
+ const latestContent = getTooltipContent(reference);
126
+ // If no tooltip content is currently available, prevent showing.
127
+ if (latestContent === '') {
128
+ return false;
129
+ }
130
+ // This is specific tooltip attributefor interactive
131
+ const isInteractive = tippyInstance.reference.hasAttribute('data-tooltip-interactive');
132
+ // placement attribute for tooltip
133
+ const rawPlacement = tippyInstance.reference.getAttribute('data-tooltip-placement');
134
+ const normalizedPlacement = rawPlacement?.trim().toLowerCase();
135
+ const placement = normalizedPlacement && VALID_PLACEMENTS.has(normalizedPlacement)
136
+ ? normalizedPlacement
137
+ : 'bottom';
138
+ tippyInstance.setProps({
139
+ interactive: isInteractive,
140
+ placement,
141
+ content: latestContent,
142
+ });
206
143
  },
207
- onMount(instance) {
208
- const isExplicitlyInteractive = instance.reference.hasAttribute('data-tooltip-interactive');
209
- if (!isExplicitlyInteractive) {
210
- const tippyBox = instance.popper.querySelector('.tippy-box');
211
- if (tippyBox && tippyBox.scrollHeight > tippyBox.clientHeight) {
212
- instance.setProps({ interactive: true });
144
+ onCreate(tippyInstance) {
145
+ const attrObserver = new MutationObserver(() => {
146
+ if (tippyInstance.state.isVisible) {
147
+ const newContent = getTooltipContent(tippyInstance.reference);
148
+ if (newContent === '') {
149
+ tippyInstance.hide();
150
+ }
151
+ else {
152
+ tippyInstance.setContent(newContent);
153
+ }
213
154
  }
155
+ });
156
+ attrObserver.observe(tippyInstance.reference, {
157
+ attributes: true,
158
+ attributeFilter: ['data-tooltip', 'data-tooltip-id'],
159
+ });
160
+ tippyInstance._attrObserver = attrObserver;
161
+ const parentEl = tippyInstance.reference.parentElement;
162
+ if (parentEl) {
163
+ const domObserver = new MutationObserver(() => {
164
+ if (!document.body.contains(tippyInstance.reference)) {
165
+ tippyInstance.hide();
166
+ tippyInstance.destroy();
167
+ }
168
+ });
169
+ domObserver.observe(parentEl, { childList: true });
170
+ tippyInstance._domObserver = domObserver;
214
171
  }
215
172
  },
216
- onHidden(instance) {
217
- if (instance === active) {
218
- destroyActive();
219
- }
220
- else {
221
- instance.destroy();
173
+ onDestroy(tippyInstance) {
174
+ tippyInstance._attrObserver?.disconnect();
175
+ tippyInstance._domObserver?.disconnect();
176
+ },
177
+ onMount(tippyInstance) {
178
+ const isExplicitlyInteractive = tippyInstance.reference.hasAttribute('data-tooltip-interactive');
179
+ if (!isExplicitlyInteractive) {
180
+ const tippyBox = tippyInstance.popper.querySelector('.tippy-box');
181
+ if (tippyBox && tippyBox.scrollHeight > tippyBox.clientHeight) {
182
+ tippyInstance.setProps({ interactive: true });
183
+ }
222
184
  }
223
185
  },
224
186
  });
225
- activeObserver.observe(reference, {
226
- attributes: true,
227
- attributeFilter: ['data-tooltip', 'data-tooltip-id'],
228
- });
229
- activeObserver.observe(document.body, { childList: true, subtree: true });
230
- };
231
- const handlePointerOrFocus = (event) => {
232
- const { target } = event;
233
- if (!(target instanceof Element))
234
- return;
235
- // Moving into an interactive tooltip keeps it open
236
- if (active?.popper.contains(target))
237
- return;
238
- if (dismissed && !dismissed.contains(target))
239
- dismissed = null;
240
- // Nothing new under the pointer: the shown tooltip hides itself on mouseleave
241
- const closest = target.closest(TOOLTIP_TARGET);
242
- if (!closest || closest === active?.reference)
243
- return;
244
- const reference = findTooltipTarget(closest);
245
- if (reference &&
246
- reference !== active?.reference &&
247
- reference !== dismissed) {
248
- show(reference);
249
- }
250
- };
251
- // Escape hides the tooltip without moving the pointer or focus (WCAG 1.4.13)
252
- const hideOnEscape = (event) => {
253
- if (event.key !== 'Escape' || !active)
254
- return;
255
- dismissed = active.reference;
256
- destroyActive();
257
- };
258
- const hideTooltipsOnScroll = (event) => {
259
- const target = event.target;
260
- if (target instanceof Element && target.closest('[data-tippy-root]')) {
261
- return;
262
- }
263
- hideAll({ duration: 0 });
264
- destroyActive();
265
- };
266
- document.addEventListener('mouseover', handlePointerOrFocus);
267
- document.addEventListener('focusin', handlePointerOrFocus);
268
- document.addEventListener('keydown', hideOnEscape);
269
- window.addEventListener('scroll', hideTooltipsOnScroll, true);
270
- return () => {
271
- document.removeEventListener('mouseover', handlePointerOrFocus);
272
- document.removeEventListener('focusin', handlePointerOrFocus);
273
- document.removeEventListener('keydown', hideOnEscape);
274
- window.removeEventListener('scroll', hideTooltipsOnScroll, true);
275
- destroyActive();
276
- };
277
- };
278
- // Every mounted TooltipService shares one set of listeners
279
- let mountedServices = 0;
280
- let stopTooltips = null;
281
- const TooltipService = () => {
282
- useEffect(() => {
283
- mountedServices += 1;
284
- if (mountedServices === 1)
285
- stopTooltips = startTooltips();
286
187
  return () => {
287
- mountedServices -= 1;
288
- if (mountedServices === 0) {
289
- stopTooltips?.();
290
- stopTooltips = null;
188
+ window.removeEventListener('scroll', hideTooltipsOnScroll, true);
189
+ if (Array.isArray(instance)) {
190
+ instance.forEach((i) => i.destroy());
191
+ }
192
+ else {
193
+ instance.destroy();
291
194
  }
292
195
  };
293
196
  }, []);