@teambit/react.ui.component-highlighter 0.0.511 → 0.0.514

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.
Files changed (64) hide show
  1. package/children-highlighter/use-children-highlighter.tsx +5 -3
  2. package/component-highlighter.docs.md +3 -14
  3. package/dist/children-highlighter/use-children-highlighter.d.ts +7 -2
  4. package/dist/children-highlighter/use-children-highlighter.js.map +1 -1
  5. package/dist/component-highlighter.docs.md +3 -14
  6. package/dist/element-highlighter/element-highlighter.compositions.d.ts +2 -0
  7. package/dist/element-highlighter/element-highlighter.compositions.js +33 -17
  8. package/dist/element-highlighter/element-highlighter.compositions.js.map +1 -1
  9. package/dist/element-highlighter/element-highlighter.d.ts +6 -10
  10. package/dist/element-highlighter/element-highlighter.js +4 -4
  11. package/dist/element-highlighter/element-highlighter.js.map +1 -1
  12. package/dist/element-highlighter/index.d.ts +1 -1
  13. package/dist/frame/frame.d.ts +8 -4
  14. package/dist/frame/frame.js +69 -31
  15. package/dist/frame/frame.js.map +1 -1
  16. package/dist/frame/frame.module.scss +8 -1
  17. package/dist/hover-highlighter/bubble-to-component.d.ts +19 -0
  18. package/dist/hover-highlighter/bubble-to-component.js +43 -0
  19. package/dist/hover-highlighter/bubble-to-component.js.map +1 -0
  20. package/dist/hover-highlighter/bubble-to-component.spec.d.ts +1 -0
  21. package/dist/hover-highlighter/bubble-to-component.spec.js +38 -0
  22. package/dist/hover-highlighter/bubble-to-component.spec.js.map +1 -0
  23. package/dist/hover-highlighter/use-hover-highlighter.d.ts +6 -1
  24. package/dist/hover-highlighter/use-hover-highlighter.js +2 -23
  25. package/dist/hover-highlighter/use-hover-highlighter.js.map +1 -1
  26. package/dist/hybrid-highlighter/hybrid-highlighter.js +1 -1
  27. package/dist/hybrid-highlighter/hybrid-highlighter.js.map +1 -1
  28. package/dist/index.d.ts +1 -1
  29. package/dist/label/component-strip.d.ts +1 -1
  30. package/dist/label/component-strip.js +7 -1
  31. package/dist/label/component-strip.js.map +1 -1
  32. package/dist/label/label-container.d.ts +4 -5
  33. package/dist/label/label-container.js +29 -13
  34. package/dist/label/label-container.js.map +1 -1
  35. package/dist/label/label.d.ts +1 -1
  36. package/dist/label/label.module.scss +8 -0
  37. package/dist/label/other-components.d.ts +1 -1
  38. package/dist/mock-component.d.ts +2 -0
  39. package/dist/mock-component.js +30 -0
  40. package/dist/mock-component.js.map +1 -0
  41. package/element-highlighter/element-highlighter.compositions.tsx +53 -19
  42. package/element-highlighter/element-highlighter.tsx +13 -25
  43. package/element-highlighter/index.ts +1 -1
  44. package/frame/frame.module.scss +8 -1
  45. package/frame/frame.tsx +92 -44
  46. package/hover-highlighter/bubble-to-component.spec.tsx +57 -0
  47. package/hover-highlighter/bubble-to-component.tsx +69 -0
  48. package/hover-highlighter/use-hover-highlighter.tsx +5 -36
  49. package/hybrid-highlighter/hybrid-highlighter.tsx +7 -2
  50. package/index.ts +1 -1
  51. package/label/component-strip.tsx +11 -2
  52. package/label/label-container.tsx +42 -20
  53. package/label/label.module.scss +8 -0
  54. package/label/label.tsx +1 -1
  55. package/label/other-components.tsx +1 -1
  56. package/mock-component.tsx +9 -0
  57. package/package-tar/teambit-react.ui.component-highlighter-0.0.514.tgz +0 -0
  58. package/package.json +9 -10
  59. package/{preview-1647509820722.js → preview-1649066106369.js} +2 -2
  60. package/dist/use-animation-frame.d.ts +0 -1
  61. package/dist/use-animation-frame.js +0 -23
  62. package/dist/use-animation-frame.js.map +0 -1
  63. package/package-tar/teambit-react.ui.component-highlighter-0.0.511.tgz +0 -0
  64. package/use-animation-frame.tsx +0 -20
package/frame/frame.tsx CHANGED
@@ -1,64 +1,112 @@
1
- import React, { useState } from 'react';
2
- import { usePopper, Modifier } from 'react-popper';
1
+ import React, { useEffect, useLayoutEffect, useRef, RefObject } from 'react';
3
2
  import classnames from 'classnames';
4
- import '@popperjs/core';
3
+ import { useFloating, autoUpdate, offset, size, shift } from '@floating-ui/react-dom';
4
+ import type { Coords } from '@floating-ui/react-dom';
5
5
 
6
- import { ignorePopperSize } from '@teambit/base-ui.utils.popper-js.ignore-popper-size';
7
- import { resizeToMatchReference } from '@teambit/base-ui.utils.popper-js.resize-to-match-reference';
6
+ import styles from './frame.module.scss';
8
7
 
9
- import { useAnimationFrame } from '../use-animation-frame';
10
- import classStyles from './frame.module.scss';
11
-
12
- const BASE_OFFSET = +classStyles.offset;
13
-
14
- const popperModifiers: Modifier<any>[] = [
15
- ignorePopperSize,
16
- resizeToMatchReference,
17
- {
18
- name: 'flip',
19
- enabled: false,
20
- },
21
- {
22
- name: 'offset',
23
- options: {
24
- // move box from above the target ('top-start')
25
- // to directly cover the target.
26
- offset: ({ reference }: any) => [BASE_OFFSET, BASE_OFFSET - reference.height],
27
- },
28
- },
29
- ];
8
+ /** frame padding around the target */
9
+ const MARGIN_FROM_TARGET = +styles.offset || 6; // setting fallback 6, for tests
10
+ /** min. distance from the edge of the screen. */
11
+ const MARGIN_FROM_DOC_EDGE = 0;
30
12
 
31
13
  export interface FrameProps extends React.HTMLAttributes<HTMLDivElement> {
32
- targetRef: HTMLElement | null;
14
+ /** apply the frame to this element */
15
+ targetRef: RefObject<HTMLElement | null>;
16
+ /**
17
+ * the specific flavor of the frame.
18
+ * @default "redBorderClass"
19
+ */
33
20
  stylesClass?: string;
34
21
  /** continually update frame position to match moving elements */
35
22
  watchMotion?: boolean;
36
23
  }
37
24
 
38
- export function Frame({
39
- targetRef,
40
- watchMotion,
41
- className,
42
- stylesClass = classStyles.overlayBorder,
43
- style,
44
- }: FrameProps) {
45
- const [referenceElement, setReferenceElement] = useState<HTMLDivElement | null>(null);
25
+ // position - bottom start (bottom left corner)
26
+ // x - width - horizontal (cross axis)
27
+ // y - height - vertical (main axis)
28
+
29
+ export function Frame({ targetRef, watchMotion, className, stylesClass = styles.overlayBorder, style }: FrameProps) {
30
+ const dimensionRef = useRef({ width: 0, height: 0 });
31
+ const shiftRef = useRef<Coords | undefined>();
32
+
33
+ const { x, y, strategy, reference, floating, update, refs } = useFloating({
34
+ placement: 'bottom-start',
35
+ middleware: [
36
+ // replace dimensions from previous iterations with the target's size
37
+ // this is only the measured size, not yet the applied size
38
+ {
39
+ name: 'align-to-target',
40
+ fn({ rects }) {
41
+ rects.floating = {
42
+ ...rects.floating,
43
+ width: rects.reference.width + 2 * MARGIN_FROM_TARGET,
44
+ height: rects.reference.height + 2 * MARGIN_FROM_TARGET,
45
+ };
46
46
 
47
- const { styles, attributes, update } = usePopper(targetRef, referenceElement, {
48
- modifiers: popperModifiers,
49
- placement: 'top-start',
47
+ return {};
48
+ },
49
+ },
50
+ // reposition x,y, to the top of the reference
51
+ offset((options) => -options.reference.height),
52
+ // offset the frame by its extra padding
53
+ offset(() => ({ mainAxis: -MARGIN_FROM_TARGET, crossAxis: -MARGIN_FROM_TARGET })),
54
+ // push the frame inside the screen
55
+ shift({ rootBoundary: 'document', padding: MARGIN_FROM_DOC_EDGE, mainAxis: true, crossAxis: true }),
56
+ // read "shift" for the size-apply() method (because it doesn't forward middlewareData)
57
+ {
58
+ name: 'read-shift',
59
+ fn({ middlewareData }) {
60
+ shiftRef.current = middlewareData.shift;
61
+ return {};
62
+ },
63
+ },
64
+ // size also applies overflow detection via width and height
65
+ size({
66
+ // apply overflow detection in reference to the document
67
+ rootBoundary: 'document',
68
+ padding: MARGIN_FROM_DOC_EDGE,
69
+ apply({ reference: referenceRect, height, width }) {
70
+ const paddingX = 2 * MARGIN_FROM_TARGET - (shiftRef.current?.x || 0);
71
+ const paddingY = 2 * MARGIN_FROM_TARGET - (shiftRef.current?.y || 0);
72
+
73
+ dimensionRef.current = {
74
+ width: Math.min(referenceRect.width + paddingX, width),
75
+ height: Math.min(referenceRect.height + paddingY, height),
76
+ };
77
+ Object.assign(refs.floating.current?.style, dimensionRef.current);
78
+ },
79
+ }),
80
+ ],
50
81
  });
51
82
 
52
- useAnimationFrame(!!watchMotion && update);
83
+ // set target as floating reference
84
+ useLayoutEffect(() => {
85
+ reference(targetRef.current);
86
+ }, [targetRef.current]);
87
+
88
+ // automatically update on scroll, resize, etc.
89
+ // `watchMotion` will trigger continuous updates using animation frame
90
+ useEffect(() => {
91
+ if (!refs.reference.current || !refs.floating.current) return () => {};
92
+
93
+ return autoUpdate(refs.reference.current, refs.floating.current, update, { animationFrame: watchMotion });
94
+ }, [refs.reference.current, refs.floating.current, update, watchMotion]);
53
95
 
54
- if (!targetRef) return null;
96
+ const isReady = x !== null;
55
97
 
56
98
  return (
57
99
  <div
58
- ref={setReferenceElement}
59
- className={classnames(className, stylesClass)}
60
- style={{ ...styles.popper, ...style }}
61
- {...attributes.popper}
100
+ ref={floating}
101
+ className={classnames(className, stylesClass, !isReady && styles.hidden)}
102
+ style={{
103
+ ...style,
104
+ ...dimensionRef.current,
105
+ position: strategy,
106
+ // starting at pos [0,0] will ensure the label doesn't increase the document size.
107
+ top: y ?? 0,
108
+ left: x ?? 0,
109
+ }}
62
110
  />
63
111
  );
64
112
  }
@@ -0,0 +1,57 @@
1
+ import React from 'react';
2
+ import { render } from '@testing-library/react';
3
+ import { MockTarget } from '../mock-component';
4
+ import { bubbleToComponent } from './bubble-to-component';
5
+
6
+ it('should find component when starting from a div', () => {
7
+ const { getByText, getByTestId } = render(
8
+ <MockTarget data-testid="expected-result">
9
+ <div>hello world</div>
10
+ </MockTarget>
11
+ );
12
+
13
+ const rendered = getByText('hello world');
14
+
15
+ const result = bubbleToComponent(rendered);
16
+
17
+ expect(result?.element).toBe(getByTestId('expected-result'));
18
+ expect(result?.components).toEqual([MockTarget]);
19
+ });
20
+
21
+ it('should bubble to root component when it renders itself recursively', () => {
22
+ const { getByText, getByTestId } = render(
23
+ <MockTarget data-testid="expected-result">
24
+ <MockTarget>
25
+ <MockTarget>
26
+ <div>hello world</div>
27
+ </MockTarget>
28
+ </MockTarget>
29
+ </MockTarget>
30
+ );
31
+
32
+ const rendered = getByText('hello world');
33
+
34
+ const result = bubbleToComponent(rendered);
35
+
36
+ expect(result?.element).toBe(getByTestId('expected-result'));
37
+ expect(result?.components).toEqual([MockTarget]);
38
+ });
39
+
40
+ it('should find first component, when parent propagation is disabled', () => {
41
+ const { getByText, getByTestId } = render(
42
+ <MockTarget>
43
+ <MockTarget>
44
+ <MockTarget data-testid="expected-result">
45
+ <div>hello world</div>
46
+ </MockTarget>
47
+ </MockTarget>
48
+ </MockTarget>
49
+ );
50
+
51
+ const rendered = getByText('hello world');
52
+
53
+ const result = bubbleToComponent(rendered, { propagateSameParents: false });
54
+
55
+ expect(result?.element).toBe(getByTestId('expected-result'));
56
+ expect(result?.components).toEqual([MockTarget]);
57
+ });
@@ -0,0 +1,69 @@
1
+ import { domToReacts, toRootElement } from '@teambit/react.modules.dom-to-react';
2
+ import {
3
+ componentMetaField,
4
+ hasComponentMeta,
5
+ ReactComponentMetaHolder,
6
+ } from '@teambit/react.ui.highlighter.component-metadata.bit-component-meta';
7
+ import { ruleMatcher, MatchRule, ComponentMatchRule, componentRuleMatcher } from '../rule-matcher';
8
+
9
+ type BubblingOptions = {
10
+ /** filter elements by this rule */
11
+ elementRule?: MatchRule;
12
+ /** filter components by this rule */
13
+ componentRule?: ComponentMatchRule;
14
+ /**
15
+ * continue bubbling when encountering a parent of the same component
16
+ * @default true
17
+ */
18
+ propagateSameParents?: boolean;
19
+ };
20
+
21
+ /** go up the dom tree until reaching a react bit component */
22
+ export function bubbleToComponent(
23
+ element: HTMLElement | null,
24
+ { elementRule, componentRule, propagateSameParents = true }: BubblingOptions = {}
25
+ ) {
26
+ let current = bubbleToFirstComponent(element, elementRule, componentRule);
27
+ if (!propagateSameParents) return current;
28
+
29
+ while (current) {
30
+ const parentElement = current.element.parentElement;
31
+ const parent = bubbleToFirstComponent(parentElement, elementRule, componentRule);
32
+
33
+ const primeComponent = current?.components.slice(-1).pop();
34
+ const parentPrimeComponent = parent?.components.slice(-1).pop();
35
+
36
+ if (primeComponent?.[componentMetaField].id !== parentPrimeComponent?.[componentMetaField].id) return current;
37
+
38
+ current = parent;
39
+ }
40
+
41
+ return undefined;
42
+ }
43
+
44
+ /** go up the dom tree until reaching a react bit component */
45
+ function bubbleToFirstComponent(
46
+ element: HTMLElement | null,
47
+ elementRule?: MatchRule,
48
+ componentRule?: ComponentMatchRule
49
+ ) {
50
+ for (let current = element; current; current = current.parentElement) {
51
+ current = toRootElement(current);
52
+ if (!current) return undefined;
53
+ if (ruleMatcher(current, elementRule)) {
54
+ const components = domToReacts(current);
55
+
56
+ const relevantComponents = components.filter(
57
+ (x) => hasComponentMeta(x) && componentRuleMatcher({ meta: x[componentMetaField] }, componentRule)
58
+ ) as ReactComponentMetaHolder[];
59
+
60
+ if (relevantComponents.length < 1) return undefined;
61
+ return {
62
+ element: current,
63
+ components: relevantComponents,
64
+ };
65
+ }
66
+ }
67
+
68
+ return undefined;
69
+ }
@@ -1,17 +1,13 @@
1
1
  import React, { useEffect } from 'react';
2
2
  import { useDebouncedCallback } from 'use-debounce';
3
- import { domToReacts, toRootElement } from '@teambit/react.modules.dom-to-react';
4
3
  import { useHoverSelection } from '@teambit/react.ui.hover-selector';
5
- import {
6
- componentMetaField,
7
- hasComponentMeta,
8
- ReactComponentMetaHolder,
9
- } from '@teambit/react.ui.highlighter.component-metadata.bit-component-meta';
4
+ import { ComponentMetaHolder } from '@teambit/react.ui.highlighter.component-metadata.bit-component-meta';
10
5
 
11
6
  import { excludeHighlighterSelector } from '../ignore-highlighter';
12
- import { HighlightTarget } from '../element-highlighter';
13
- import { ruleMatcher, MatchRule, ComponentMatchRule, componentRuleMatcher } from '../rule-matcher';
7
+ import { MatchRule, ComponentMatchRule } from '../rule-matcher';
8
+ import { bubbleToComponent } from './bubble-to-component';
14
9
 
10
+ type HighlightTarget = { element: HTMLElement; components: ComponentMetaHolder[] };
15
11
  export type useHoverHighlighterOptions = {
16
12
  debounceDuration: number;
17
13
  scopeClass: string;
@@ -63,7 +59,7 @@ function useHoverHandler({
63
59
  // skip DOM trees having 'data-ignore-component-highlight'
64
60
  if (element.closest(`.${scopeClass} ${excludeHighlighterSelector}`)) return;
65
61
 
66
- const result = bubbleToBitComponent(element, rule, componentRule);
62
+ const result = bubbleToComponent(element, { elementRule: rule, componentRule });
67
63
  if (!result) return;
68
64
 
69
65
  onChange({
@@ -81,30 +77,3 @@ function useHoverHandler({
81
77
 
82
78
  return { handleElement };
83
79
  }
84
-
85
- /** go up the dom tree until reaching a react bit component */
86
- function bubbleToBitComponent(
87
- element: HTMLElement | null,
88
- elementRule?: MatchRule,
89
- componentRule?: ComponentMatchRule
90
- ) {
91
- for (let current = element; current; current = current.parentElement) {
92
- current = toRootElement(current);
93
- if (!current) return undefined;
94
- if (ruleMatcher(current, elementRule)) {
95
- const components = domToReacts(current);
96
-
97
- const relevantComponents = components.filter(
98
- (x) => hasComponentMeta(x) && componentRuleMatcher({ meta: x[componentMetaField] }, componentRule)
99
- ) as ReactComponentMetaHolder[];
100
-
101
- if (relevantComponents.length < 1) return undefined;
102
- return {
103
- element: current,
104
- components: relevantComponents,
105
- };
106
- }
107
- }
108
-
109
- return undefined;
110
- }
@@ -2,11 +2,15 @@ import React, { useState, useEffect, useMemo, useRef, createRef, CSSProperties }
2
2
  import classnames from 'classnames';
3
3
  import { v4 } from 'uuid';
4
4
 
5
+ import { ComponentMetaHolder } from '@teambit/react.ui.highlighter.component-metadata.bit-component-meta';
6
+
5
7
  import { useHoverHighlighter } from '../hover-highlighter';
6
- import { ElementHighlighter, HighlightTarget, Placement, HighlightClasses } from '../element-highlighter';
8
+ import { ElementHighlighter, Placement, HighlightClasses } from '../element-highlighter';
7
9
  import { useChildrenHighlighter } from '../children-highlighter';
8
10
  import type { MatchRule, ComponentMatchRule } from '../rule-matcher';
9
11
 
12
+ type HighlightTarget = { element: HTMLElement; components: ComponentMetaHolder[] };
13
+
10
14
  export interface HybridHighlighterProps extends React.HTMLAttributes<HTMLDivElement> {
11
15
  /** stop all highlighting and drop listeners */
12
16
  disabled?: boolean;
@@ -119,7 +123,8 @@ export function HybridHighlighter({
119
123
  {Object.entries(targets).map(([key, target]) => (
120
124
  <ElementHighlighter
121
125
  key={key}
122
- target={target}
126
+ targetRef={{ current: target.element }}
127
+ components={target.components}
123
128
  classes={classes}
124
129
  style={highlightStyle}
125
130
  placement={placement}
package/index.ts CHANGED
@@ -8,7 +8,7 @@ export { ChildrenHighlighter } from './children-highlighter';
8
8
  export type { ChildrenHighlighterProps } from './children-highlighter';
9
9
 
10
10
  export { ElementHighlighter } from './element-highlighter';
11
- export type { ElementHighlighterProps, HighlightTarget, Placement, HighlightClasses } from './element-highlighter';
11
+ export type { ElementHighlighterProps, Placement, HighlightClasses } from './element-highlighter';
12
12
 
13
13
  export {
14
14
  ExcludeHighlighter,
@@ -3,6 +3,7 @@ import { NativeLink } from '@teambit/base-ui.routing.native-link';
3
3
  import { ComponentID } from '@teambit/component-id';
4
4
  import { ScopeUrl } from '@teambit/component.modules.component-url';
5
5
  import {
6
+ ComponentMeta,
6
7
  componentMetaField,
7
8
  ComponentMetaHolder,
8
9
  } from '@teambit/react.ui.highlighter.component-metadata.bit-component-meta';
@@ -10,13 +11,13 @@ import styles from './component-strip.module.scss';
10
11
  import { calcComponentLink } from './links';
11
12
 
12
13
  interface ComponentStripProps extends React.HTMLAttributes<HTMLDivElement> {
13
- component: ComponentMetaHolder;
14
+ component: ComponentMetaHolder | string;
14
15
  }
15
16
  export const ComponentStrip = forwardRef(function ComponentStrip(
16
17
  { component, children }: ComponentStripProps,
17
18
  ref: ForwardedRef<HTMLDivElement>
18
19
  ) {
19
- const { id, homepage, exported } = component[componentMetaField];
20
+ const { id, homepage, exported } = extractMetadata(component);
20
21
 
21
22
  const parsedId = useMemo(() => ComponentID.tryFromString(id), [id]);
22
23
  const componentLink = homepage || calcComponentLink(parsedId, exported);
@@ -44,3 +45,11 @@ function LabelBlock({ link, children }: { link?: string; children: ReactNode })
44
45
  </Comp>
45
46
  );
46
47
  }
48
+
49
+ function extractMetadata(metadata: string | ComponentMetaHolder): ComponentMeta {
50
+ if (typeof metadata === 'string') {
51
+ return { id: metadata, exported: true };
52
+ }
53
+
54
+ return metadata[componentMetaField];
55
+ }
@@ -1,12 +1,18 @@
1
- import React, { useMemo, useState } from 'react';
2
- import { usePopper } from 'react-popper';
3
- import type { Placement, Modifier } from '@popperjs/core';
4
- import '@popperjs/core';
5
-
6
- import { useAnimationFrame } from '../use-animation-frame';
1
+ import React, { useLayoutEffect, useEffect, RefObject } from 'react';
2
+ import classnames from 'classnames';
3
+ import compact from 'lodash.compact';
4
+ import {
5
+ useFloating,
6
+ offset as offsetMiddleware,
7
+ flip as flipMiddleware,
8
+ shift,
9
+ autoUpdate,
10
+ } from '@floating-ui/react-dom';
11
+ import type { Placement } from '@floating-ui/react-dom';
12
+ import styles from './label.module.scss';
7
13
 
8
14
  export interface LabelContainerProps extends React.HTMLAttributes<HTMLDivElement> {
9
- targetRef: HTMLElement | null;
15
+ targetRef: RefObject<HTMLElement | null>;
10
16
  offset?: [number, number];
11
17
  placement?: Placement;
12
18
  flip?: boolean;
@@ -16,7 +22,6 @@ export interface LabelContainerProps extends React.HTMLAttributes<HTMLDivElement
16
22
 
17
23
  export type { Placement };
18
24
 
19
- // TODO - replace this with TippyJS, when it supports a `targetElement={targetRef.current}` prop
20
25
  export function LabelContainer({
21
26
  targetRef,
22
27
  offset,
@@ -24,23 +29,40 @@ export function LabelContainer({
24
29
  flip = true,
25
30
  watchMotion,
26
31
  className,
32
+ style,
27
33
  ...rest
28
34
  }: LabelContainerProps) {
29
- const [sourceRef, setSourceRef] = useState<HTMLDivElement | null>(null);
30
-
31
- const modifiers = useMemo<Partial<Modifier<any, any>>[]>(
32
- () => [{ name: 'offset', options: { offset } }],
33
- [flip, offset]
34
- );
35
-
36
- const { styles, attributes, update } = usePopper(targetRef, sourceRef, {
37
- modifiers,
35
+ const { x, y, strategy, floating, reference, refs, update } = useFloating({
38
36
  placement,
37
+ middleware: compact([
38
+ offset && offsetMiddleware({ mainAxis: offset[0], crossAxis: offset[1] }),
39
+ flip && flipMiddleware(),
40
+ // enabling 'shift' for 'crossAxis' will make floating-ui push the label _inside_, when it has nowhere to go
41
+ shift({ rootBoundary: 'document', mainAxis: true, crossAxis: true }),
42
+ ]),
39
43
  });
40
44
 
41
- useAnimationFrame(!!watchMotion && update);
45
+ useLayoutEffect(() => {
46
+ reference(targetRef.current);
47
+ }, [targetRef.current, reference]);
42
48
 
43
- if (!targetRef) return null;
49
+ // automatically update on scroll, resize, etc.
50
+ // `watchMotion` will trigger continuous updates using animation frame
51
+ useEffect(() => {
52
+ if (!refs.reference.current || !refs.floating.current) return () => {};
44
53
 
45
- return <div {...rest} ref={setSourceRef} className={className} style={styles.popper} {...attributes.popper} />;
54
+ return autoUpdate(refs.reference.current, refs.floating.current, update, { animationFrame: !!watchMotion });
55
+ }, [refs.reference.current, refs.floating.current, update, watchMotion]);
56
+
57
+ const isReady = x !== null;
58
+
59
+ return (
60
+ <div
61
+ {...rest}
62
+ ref={floating}
63
+ className={classnames(className, !isReady && styles.hidden)}
64
+ // starting at pos [0,0] will ensure the label doesn't increase the document size.
65
+ style={{ ...style, position: strategy, top: y ?? 0, left: x ?? 0 }}
66
+ />
67
+ );
46
68
  }
@@ -22,3 +22,11 @@
22
22
  transform: rotate(-180deg);
23
23
  }
24
24
  }
25
+
26
+ .hidden {
27
+ // label size is needed for position calculations,
28
+ // so it can't be removed by `display: none`
29
+ visibility: hidden;
30
+ pointer-events: none;
31
+ user-select: none;
32
+ }
package/label/label.tsx CHANGED
@@ -7,7 +7,7 @@ import { ComponentStrip } from './component-strip';
7
7
  import { OtherComponentsPopper } from './other-components';
8
8
 
9
9
  export interface LabelProps extends React.HTMLAttributes<HTMLDivElement> {
10
- components: ComponentMetaHolder[];
10
+ components: (ComponentMetaHolder | string)[];
11
11
  }
12
12
 
13
13
  export function Label({ components, ...props }: LabelProps) {
@@ -6,7 +6,7 @@ import { ComponentStrip } from './component-strip';
6
6
  import styles from './label.module.scss';
7
7
 
8
8
  export type OtherComponentsProps = {
9
- components: ComponentMetaHolder[];
9
+ components: (ComponentMetaHolder | string)[];
10
10
  start?: number;
11
11
  end?: number;
12
12
  } & TippyProps;
@@ -0,0 +1,9 @@
1
+ import React, { PropsWithChildren } from 'react';
2
+ import { componentMetaField } from '@teambit/react.ui.highlighter.component-metadata.bit-component-meta';
3
+
4
+ export function MockTarget({ children, ...rest }: PropsWithChildren<{}>) {
5
+ return <div {...rest}>mocked {children}</div>;
6
+ }
7
+ MockTarget[componentMetaField] = {
8
+ id: 'teambit.design/ui/icon-button@1.6.2',
9
+ };
package/package.json CHANGED
@@ -1,28 +1,26 @@
1
1
  {
2
2
  "name": "@teambit/react.ui.component-highlighter",
3
- "version": "0.0.511",
3
+ "version": "0.0.514",
4
4
  "homepage": "https://bit.dev/teambit/react/ui/component-highlighter",
5
5
  "main": "dist/index.js",
6
6
  "componentId": {
7
7
  "scope": "teambit.react",
8
8
  "name": "ui/component-highlighter",
9
- "version": "0.0.511"
9
+ "version": "0.0.514"
10
10
  },
11
11
  "dependencies": {
12
12
  "get-xpath": "3.0.1",
13
13
  "classnames": "2.2.6",
14
- "@popperjs/core": "2.6.0",
15
- "react-popper": "2.2.4",
16
- "uuid": "3.4.0",
17
14
  "use-debounce": "6.0.1",
15
+ "uuid": "3.4.0",
16
+ "@floating-ui/react-dom": "0.6.0",
17
+ "lodash.compact": "3.0.1",
18
18
  "url-join": "4.0.1",
19
19
  "@tippyjs/react": "4.2.0",
20
20
  "core-js": "^3.0.0",
21
- "@teambit/base-ui.utils.popper-js.ignore-popper-size": "1.0.0",
22
- "@teambit/base-ui.utils.popper-js.resize-to-match-reference": "1.0.0",
23
21
  "@teambit/base-ui.routing.native-link": "1.0.0",
24
- "@teambit/component-id": "0.0.402",
25
22
  "@teambit/react.ui.highlighter.component-metadata.bit-component-meta": "0.0.21",
23
+ "@teambit/component-id": "0.0.402",
26
24
  "@teambit/react.modules.dom-to-react": "0.0.489",
27
25
  "@teambit/ui-foundation.ui.constants.z-indexes": "0.0.487",
28
26
  "@teambit/react.ui.hover-selector": "0.0.165",
@@ -32,15 +30,16 @@
32
30
  "@types/react": "^17.0.8",
33
31
  "@testing-library/react": "11.2.6",
34
32
  "@types/classnames": "2.2.11",
35
- "@types/uuid": "3.4.9",
36
33
  "@types/react-dom": "^17.0.5",
34
+ "@types/uuid": "3.4.9",
35
+ "@types/lodash.compact": "3.0.6",
37
36
  "@types/url-join": "4.0.0",
38
37
  "@types/mocha": "9.1.0",
39
38
  "@types/testing-library__jest-dom": "5.9.5",
40
39
  "@babel/runtime": "7.12.18",
41
40
  "@types/jest": "^26.0.0",
42
41
  "@types/node": "12.20.4",
43
- "@teambit/design.ui.icon-button": "1.0.10"
42
+ "@teambit/design.ui.icon-button": "1.0.16"
44
43
  },
45
44
  "peerDependencies": {
46
45
  "react-dom": "^16.8.0 || ^17.0.0",
@@ -1,2 +1,2 @@
1
- export const compositions = [require('/home/circleci/Library/Caches/Bit/capsules/8891be5ad3d35bfc38b9cd90c0e05b598a5a55af/teambit.react_ui_component-highlighter@0.0.511/dist/children-highlighter/children-highlighter.composition.js'), require('/home/circleci/Library/Caches/Bit/capsules/8891be5ad3d35bfc38b9cd90c0e05b598a5a55af/teambit.react_ui_component-highlighter@0.0.511/dist/element-highlighter/element-highlighter.compositions.js'), require('/home/circleci/Library/Caches/Bit/capsules/8891be5ad3d35bfc38b9cd90c0e05b598a5a55af/teambit.react_ui_component-highlighter@0.0.511/dist/hover-highlighter/hover-highlighter.compositions.js')]
2
- export const overview = [require('/home/circleci/Library/Caches/Bit/capsules/8891be5ad3d35bfc38b9cd90c0e05b598a5a55af/teambit.react_ui_component-highlighter@0.0.511/dist/component-highlighter.docs.md')]
1
+ export const compositions = [require('/home/circleci/Library/Caches/Bit/capsules/8891be5ad3d35bfc38b9cd90c0e05b598a5a55af/teambit.react_ui_component-highlighter@0.0.514/dist/children-highlighter/children-highlighter.composition.js'), require('/home/circleci/Library/Caches/Bit/capsules/8891be5ad3d35bfc38b9cd90c0e05b598a5a55af/teambit.react_ui_component-highlighter@0.0.514/dist/element-highlighter/element-highlighter.compositions.js'), require('/home/circleci/Library/Caches/Bit/capsules/8891be5ad3d35bfc38b9cd90c0e05b598a5a55af/teambit.react_ui_component-highlighter@0.0.514/dist/hover-highlighter/hover-highlighter.compositions.js')]
2
+ export const overview = [require('/home/circleci/Library/Caches/Bit/capsules/8891be5ad3d35bfc38b9cd90c0e05b598a5a55af/teambit.react_ui_component-highlighter@0.0.514/dist/component-highlighter.docs.md')]
@@ -1 +0,0 @@
1
- export declare function useAnimationFrame(cb?: false | null | (() => any), deps?: any[]): void;
@@ -1,23 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useAnimationFrame = void 0;
4
- const react_1 = require("react");
5
- // TODO - extract to its own component
6
- function useAnimationFrame(cb, deps = []) {
7
- (0, react_1.useEffect)(() => {
8
- if (!cb)
9
- return () => { };
10
- let animationFrameId = -1;
11
- const animate = () => {
12
- cb();
13
- animationFrameId = window.requestAnimationFrame(animate);
14
- };
15
- animate();
16
- return () => {
17
- if (animationFrameId > -1)
18
- window.cancelAnimationFrame(animationFrameId);
19
- };
20
- }, [cb, ...deps]);
21
- }
22
- exports.useAnimationFrame = useAnimationFrame;
23
- //# sourceMappingURL=use-animation-frame.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"use-animation-frame.js","sourceRoot":"","sources":["../use-animation-frame.tsx"],"names":[],"mappings":";;;AAAA,iCAAkC;AAElC,sCAAsC;AAEtC,SAAgB,iBAAiB,CAAC,EAA+B,EAAE,OAAc,EAAE;IACjF,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,CAAC,EAAE;YAAE,OAAO,GAAG,EAAE,GAAE,CAAC,CAAC;QAEzB,IAAI,gBAAgB,GAAG,CAAC,CAAC,CAAC;QAC1B,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,EAAE,EAAE,CAAC;YACL,gBAAgB,GAAG,MAAM,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC;QAC3D,CAAC,CAAC;QACF,OAAO,EAAE,CAAC;QAEV,OAAO,GAAG,EAAE;YACV,IAAI,gBAAgB,GAAG,CAAC,CAAC;gBAAE,MAAM,CAAC,oBAAoB,CAAC,gBAAgB,CAAC,CAAC;QAC3E,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;AACpB,CAAC;AAfD,8CAeC"}